• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 14140028801

29 Mar 2025 12:08AM UTC coverage: 70.533% (+0.01%) from 70.522%
14140028801

Pull #12043

github

web-flow
Merge fed4c9c54 into 1f1b80b31
Pull Request #12043: Add gdal geom vector make-valid, segmentize, simplify, buffer, swap-xy

321 of 323 new or added lines in 17 files covered. (99.38%)

62 existing lines in 28 files now uncovered.

556103 of 788426 relevant lines covered (70.53%)

221377.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.77
/gcore/gdalalgorithm.cpp
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_minixml.h"
18

19
#include "gdalalgorithm.h"
20
#include "gdal_priv.h"
21

22
#include <algorithm>
23
#include <cassert>
24
#include <cerrno>
25
#include <cstdlib>
26
#include <map>
27

28
#ifndef _
29
#define _(x) (x)
30
#endif
31

32
constexpr const char *GDAL_ARG_NAME_INPUT_FORMAT = "input-format";
33

34
constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
35

36
constexpr const char *GDAL_ARG_NAME_OPEN_OPTION = "open-option";
37

38
//! @cond Doxygen_Suppress
39
struct GDALAlgorithmArgHS
40
{
41
    GDALAlgorithmArg *ptr = nullptr;
42

43
    explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
862✔
44
    {
45
    }
862✔
46
};
47

48
//! @endcond
49

50
//! @cond Doxygen_Suppress
51
struct GDALArgDatasetValueHS
52
{
53
    GDALArgDatasetValue val{};
54
    GDALArgDatasetValue *ptr = nullptr;
55

56
    GDALArgDatasetValueHS() : ptr(&val)
1✔
57
    {
58
    }
1✔
59

60
    explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
458✔
61
    {
62
    }
458✔
63

64
    GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
65
    GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
66
};
67

68
//! @endcond
69

70
/************************************************************************/
71
/*                     GDALAlgorithmArgTypeIsList()                     */
72
/************************************************************************/
73

74
bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
34,040✔
75
{
76
    switch (type)
34,040✔
77
    {
78
        case GAAT_BOOLEAN:
25,002✔
79
        case GAAT_STRING:
80
        case GAAT_INTEGER:
81
        case GAAT_REAL:
82
        case GAAT_DATASET:
83
            break;
25,002✔
84

85
        case GAAT_STRING_LIST:
9,038✔
86
        case GAAT_INTEGER_LIST:
87
        case GAAT_REAL_LIST:
88
        case GAAT_DATASET_LIST:
89
            return true;
9,038✔
90
    }
91

92
    return false;
25,002✔
93
}
94

95
/************************************************************************/
96
/*                     GDALAlgorithmArgTypeName()                       */
97
/************************************************************************/
98

99
const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
1,206✔
100
{
101
    switch (type)
1,206✔
102
    {
103
        case GAAT_BOOLEAN:
276✔
104
            break;
276✔
105
        case GAAT_STRING:
340✔
106
            return "string";
340✔
107
        case GAAT_INTEGER:
26✔
108
            return "integer";
26✔
109
        case GAAT_REAL:
63✔
110
            return "real";
63✔
111
        case GAAT_DATASET:
127✔
112
            return "dataset";
127✔
113
        case GAAT_STRING_LIST:
294✔
114
            return "string_list";
294✔
115
        case GAAT_INTEGER_LIST:
23✔
116
            return "integer_list";
23✔
117
        case GAAT_REAL_LIST:
51✔
118
            return "real_list";
51✔
119
        case GAAT_DATASET_LIST:
6✔
120
            return "dataset_list";
6✔
121
    }
122

123
    return "boolean";
276✔
124
}
125

126
/************************************************************************/
127
/*                     GDALArgDatasetValueTypeName()                    */
128
/************************************************************************/
129

130
std::string GDALArgDatasetValueTypeName(GDALArgDatasetValueType type)
1,720✔
131
{
132
    std::string ret;
1,720✔
133
    if ((type & GDAL_OF_RASTER) != 0)
1,720✔
134
        ret = "raster";
809✔
135
    if ((type & GDAL_OF_VECTOR) != 0)
1,720✔
136
    {
137
        if (!ret.empty())
976✔
138
        {
139
            if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
86✔
140
                ret += ", ";
79✔
141
            else
142
                ret += " or ";
7✔
143
        }
144
        ret += "vector";
976✔
145
    }
146
    if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
1,720✔
147
    {
148
        if (!ret.empty())
115✔
149
        {
150
            ret += " or ";
94✔
151
        }
152
        ret += "multidimensional raster";
115✔
153
    }
154
    return ret;
1,720✔
155
}
156

157
/************************************************************************/
158
/*                     GDALAlgorithmArgDecl()                           */
159
/************************************************************************/
160

161
// cppcheck-suppress uninitMemberVar
162
GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
28,610✔
163
                                           char chShortName,
164
                                           const std::string &description,
165
                                           GDALAlgorithmArgType type)
28,610✔
166
    : m_longName(longName),
167
      m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
28,610✔
168
      m_description(description), m_type(type),
169
      m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
57,220✔
170
                    .toupper()),
28,610✔
171
      m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
85,830✔
172
{
173
    if (m_type == GAAT_BOOLEAN)
28,610✔
174
    {
175
        m_defaultValue = false;
16,230✔
176
    }
177
}
28,610✔
178

179
/************************************************************************/
180
/*               GDALAlgorithmArgDecl::SetMinCount()                    */
181
/************************************************************************/
182

183
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
652✔
184
{
185
    if (!GDALAlgorithmArgTypeIsList(m_type))
652✔
186
    {
187
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
188
                 "SetMinCount() illegal on scalar argument '%s'",
189
                 GetName().c_str());
1✔
190
    }
191
    else
192
    {
193
        m_minCount = count;
651✔
194
    }
195
    return *this;
652✔
196
}
197

198
/************************************************************************/
199
/*               GDALAlgorithmArgDecl::SetMaxCount()                    */
200
/************************************************************************/
201

202
GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
382✔
203
{
204
    if (!GDALAlgorithmArgTypeIsList(m_type))
382✔
205
    {
206
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
207
                 "SetMaxCount() illegal on scalar argument '%s'",
208
                 GetName().c_str());
1✔
209
    }
210
    else
211
    {
212
        m_maxCount = count;
381✔
213
    }
214
    return *this;
382✔
215
}
216

217
/************************************************************************/
218
/*                         GDALAlgorithmArg::Set()                      */
219
/************************************************************************/
220

221
bool GDALAlgorithmArg::Set(bool value)
207✔
222
{
223
    if (m_decl.GetType() != GAAT_BOOLEAN)
207✔
224
    {
225
        CPLError(
14✔
226
            CE_Failure, CPLE_AppDefined,
227
            "Calling Set(bool) on argument '%s' of type %s is not supported",
228
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
7✔
229
        return false;
7✔
230
    }
231
    return SetInternal(value);
200✔
232
}
233

234
bool GDALAlgorithmArg::ProcessString(std::string &value) const
450✔
235
{
236
    if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
472✔
237
        value.front() == '@')
22✔
238
    {
239
        GByte *pabyData = nullptr;
2✔
240
        if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
2✔
241
                          1024 * 1024))
2✔
242
        {
243
            // Remove UTF-8 BOM
244
            size_t offset = 0;
1✔
245
            if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
1✔
246
                pabyData[2] == 0xBF)
1✔
247
            {
248
                offset = 3;
1✔
249
            }
250
            value = reinterpret_cast<const char *>(pabyData + offset);
1✔
251
            VSIFree(pabyData);
1✔
252
        }
253
        else
254
        {
255
            return false;
1✔
256
        }
257
    }
258

259
    if (m_decl.IsRemoveSQLCommentsEnabled())
449✔
260
        value = CPLRemoveSQLComments(value);
21✔
261

262
    return true;
449✔
263
}
264

265
bool GDALAlgorithmArg::Set(const std::string &value)
440✔
266
{
267
    if (m_decl.GetType() != GAAT_STRING)
440✔
268
    {
269
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
270
                 "Calling Set(std::string) on argument '%s' of type %s is not "
271
                 "supported",
272
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
273
        return false;
1✔
274
    }
275

276
    std::string newValue(value);
439✔
277
    return ProcessString(newValue) && SetInternal(newValue);
439✔
278
}
279

280
bool GDALAlgorithmArg::Set(int value)
26✔
281
{
282
    if (m_decl.GetType() == GAAT_REAL)
26✔
283
    {
284
        return Set(static_cast<double>(value));
2✔
285
    }
286
    if (m_decl.GetType() != GAAT_INTEGER)
24✔
287
    {
288
        CPLError(
2✔
289
            CE_Failure, CPLE_AppDefined,
290
            "Calling Set(int) on argument '%s' of type %s is not supported",
291
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
292
        return false;
1✔
293
    }
294
    return SetInternal(value);
23✔
295
}
296

297
bool GDALAlgorithmArg::Set(double value)
70✔
298
{
299
    if (m_decl.GetType() != GAAT_REAL)
70✔
300
    {
301
        CPLError(
2✔
302
            CE_Failure, CPLE_AppDefined,
303
            "Calling Set(double) on argument '%s' of type %s is not supported",
304
            GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
305
        return false;
1✔
306
    }
307
    return SetInternal(value);
69✔
308
}
309

310
bool GDALAlgorithmArg::Set(GDALDataset *ds)
16✔
311
{
312
    if (m_decl.GetType() != GAAT_DATASET)
16✔
313
    {
314
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
315
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
316
                 "is not supported",
317
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
318
        return false;
1✔
319
    }
320
    auto &val = *std::get<GDALArgDatasetValue *>(m_value);
15✔
321
    if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
15✔
322
    {
323
        CPLError(
2✔
324
            CE_Failure, CPLE_AppDefined,
325
            "Dataset object '%s' is created by algorithm and cannot be set "
326
            "as an input.",
327
            GetName().c_str());
2✔
328
        return false;
2✔
329
    }
330
    m_explicitlySet = true;
13✔
331
    val.Set(ds);
13✔
332
    return RunAllActions();
13✔
333
}
334

335
bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
2✔
336
{
337
    if (m_decl.GetType() != GAAT_DATASET)
2✔
338
    {
339
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
340
                 "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
341
                 "is not supported",
342
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
343
        return false;
1✔
344
    }
345
    auto &val = *std::get<GDALArgDatasetValue *>(m_value);
1✔
346
    if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
1✔
347
    {
348
        CPLError(
×
349
            CE_Failure, CPLE_AppDefined,
350
            "Dataset object '%s' is created by algorithm and cannot be set "
351
            "as an input.",
352
            GetName().c_str());
×
353
        return false;
×
354
    }
355
    m_explicitlySet = true;
1✔
356
    val.Set(std::move(ds));
1✔
357
    return RunAllActions();
1✔
358
}
359

360
bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
496✔
361
{
362
    if (m_decl.GetType() != GAAT_DATASET)
496✔
363
    {
364
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
365
                 "Calling SetDatasetName() on argument '%s' of type %s is "
366
                 "not supported",
367
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
368
        return false;
1✔
369
    }
370
    m_explicitlySet = true;
495✔
371
    std::get<GDALArgDatasetValue *>(m_value)->Set(name);
495✔
372
    return RunAllActions();
495✔
373
}
374

375
bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
345✔
376
{
377
    if (m_decl.GetType() != GAAT_DATASET)
345✔
378
    {
379
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
380
                 "Calling SetFrom() on argument '%s' of type %s is "
381
                 "not supported",
382
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
383
        return false;
1✔
384
    }
385
    m_explicitlySet = true;
344✔
386
    std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
344✔
387
    return RunAllActions();
344✔
388
}
389

390
bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
173✔
391
{
392
    if (m_decl.GetType() != GAAT_STRING_LIST)
173✔
393
    {
394
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
395
                 "Calling Set(const std::vector<std::string> &) on argument "
396
                 "'%s' of type %s is not supported",
397
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
398
        return false;
1✔
399
    }
400

401
    if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
336✔
402
        m_decl.IsRemoveSQLCommentsEnabled())
164✔
403
    {
404
        std::vector<std::string> newValue(value);
16✔
405
        for (auto &s : newValue)
19✔
406
        {
407
            if (!ProcessString(s))
11✔
408
                return false;
×
409
        }
410
        return SetInternal(newValue);
8✔
411
    }
412
    else
413
    {
414
        return SetInternal(value);
164✔
415
    }
416
}
417

418
bool GDALAlgorithmArg::Set(const std::vector<int> &value)
38✔
419
{
420
    if (m_decl.GetType() != GAAT_INTEGER_LIST)
38✔
421
    {
422
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
423
                 "Calling Set(const std::vector<int> &) on argument '%s' of "
424
                 "type %s is not supported",
425
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
426
        return false;
1✔
427
    }
428
    return SetInternal(value);
37✔
429
}
430

431
bool GDALAlgorithmArg::Set(const std::vector<double> &value)
75✔
432
{
433
    if (m_decl.GetType() != GAAT_REAL_LIST)
75✔
434
    {
435
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
436
                 "Calling Set(const std::vector<double> &) on argument '%s' of "
437
                 "type %s is not supported",
438
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
439
        return false;
1✔
440
    }
441
    return SetInternal(value);
74✔
442
}
443

444
bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
38✔
445
{
446
    if (m_decl.GetType() != GAAT_DATASET_LIST)
38✔
447
    {
448
        CPLError(CE_Failure, CPLE_AppDefined,
2✔
449
                 "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
450
                 "argument '%s' of type %s is not supported",
451
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
1✔
452
        return false;
1✔
453
    }
454
    m_explicitlySet = true;
37✔
455
    *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
37✔
456
    return RunAllActions();
37✔
457
}
458

459
bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
491✔
460
{
461
    if (m_decl.GetType() != other.GetType())
491✔
462
    {
463
        CPLError(CE_Failure, CPLE_AppDefined,
8✔
464
                 "Calling SetFrom() on argument '%s' of type %s whereas "
465
                 "other argument type is %s is not supported",
466
                 GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
4✔
467
                 GDALAlgorithmArgTypeName(other.GetType()));
468
        return false;
4✔
469
    }
470

471
    switch (m_decl.GetType())
487✔
472
    {
473
        case GAAT_BOOLEAN:
5✔
474
            *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
5✔
475
            break;
5✔
476
        case GAAT_STRING:
136✔
477
            *std::get<std::string *>(m_value) =
272✔
478
                *std::get<std::string *>(other.m_value);
136✔
479
            break;
136✔
480
        case GAAT_INTEGER:
1✔
481
            *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
1✔
482
            break;
1✔
483
        case GAAT_REAL:
1✔
484
            *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
1✔
485
            break;
1✔
486
        case GAAT_DATASET:
340✔
487
            return SetFrom(other.Get<GDALArgDatasetValue>());
340✔
488
        case GAAT_STRING_LIST:
1✔
489
            *std::get<std::vector<std::string> *>(m_value) =
2✔
490
                *std::get<std::vector<std::string> *>(other.m_value);
1✔
491
            break;
1✔
492
        case GAAT_INTEGER_LIST:
1✔
493
            *std::get<std::vector<int> *>(m_value) =
2✔
494
                *std::get<std::vector<int> *>(other.m_value);
1✔
495
            break;
1✔
496
        case GAAT_REAL_LIST:
1✔
497
            *std::get<std::vector<double> *>(m_value) =
2✔
498
                *std::get<std::vector<double> *>(other.m_value);
1✔
499
            break;
1✔
500
        case GAAT_DATASET_LIST:
1✔
501
        {
502
            std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
1✔
503
            for (const auto &val :
2✔
504
                 *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
5✔
505
            {
506
                GDALArgDatasetValue v;
4✔
507
                v.SetFrom(val);
2✔
508
                std::get<std::vector<GDALArgDatasetValue> *>(m_value)
2✔
509
                    ->push_back(std::move(v));
2✔
510
            }
511
            break;
1✔
512
        }
513
    }
514
    m_explicitlySet = true;
147✔
515
    return RunAllActions();
147✔
516
}
517

518
/************************************************************************/
519
/*                  GDALAlgorithmArg::RunAllActions()                   */
520
/************************************************************************/
521

522
bool GDALAlgorithmArg::RunAllActions()
2,050✔
523
{
524
    if (!RunValidationActions())
2,050✔
525
        return false;
36✔
526
    RunActions();
2,014✔
527
    return true;
2,014✔
528
}
529

530
/************************************************************************/
531
/*                      GDALAlgorithmArg::RunActions()                  */
532
/************************************************************************/
533

534
void GDALAlgorithmArg::RunActions()
2,015✔
535
{
536
    for (const auto &f : m_actions)
2,034✔
537
        f();
19✔
538
}
2,015✔
539

540
/************************************************************************/
541
/*                    GDALAlgorithmArg::ValidateChoice()                */
542
/************************************************************************/
543

544
bool GDALAlgorithmArg::ValidateChoice(const std::string &value) const
53✔
545
{
546
    for (const std::string &choice : GetChoices())
120✔
547
    {
548
        if (EQUAL(value.c_str(), choice.c_str()))
113✔
549
        {
550
            return true;
46✔
551
        }
552
    }
553

554
    for (const std::string &choice : GetHiddenChoices())
8✔
555
    {
556
        if (EQUAL(value.c_str(), choice.c_str()))
2✔
557
        {
558
            return true;
1✔
559
        }
560
    }
561

562
    std::string expected;
6✔
563
    for (const auto &choice : GetChoices())
17✔
564
    {
565
        if (!expected.empty())
11✔
566
            expected += ", ";
5✔
567
        expected += '\'';
11✔
568
        expected += choice;
11✔
569
        expected += '\'';
11✔
570
    }
571
    CPLError(CE_Failure, CPLE_IllegalArg,
12✔
572
             "Invalid value '%s' for string argument '%s'. Should be "
573
             "one among %s.",
574
             value.c_str(), GetName().c_str(), expected.c_str());
6✔
575
    return false;
6✔
576
}
577

578
/************************************************************************/
579
/*                    GDALAlgorithmArg::RunValidationActions()          */
580
/************************************************************************/
581

582
bool GDALAlgorithmArg::RunValidationActions()
2,050✔
583
{
584
    if (GetType() == GAAT_STRING && !GetChoices().empty())
2,050✔
585
    {
586
        if (!ValidateChoice(Get<std::string>()))
43✔
587
            return false;
2✔
588
    }
589
    else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
2,007✔
590
    {
591
        const auto &values = Get<std::vector<std::string>>();
5✔
592
        for (const std::string &value : values)
11✔
593
        {
594
            if (!ValidateChoice(value))
10✔
595
                return false;
4✔
596
        }
597
    }
598

599
    for (const auto &f : m_validationActions)
2,470✔
600
    {
601
        if (!f())
456✔
602
            return false;
30✔
603
    }
604
    return true;
2,014✔
605
}
606

607
/************************************************************************/
608
/*                    GDALAlgorithmArg::Serialize()                     */
609
/************************************************************************/
610

611
bool GDALAlgorithmArg::Serialize(std::string &serializedArg) const
9✔
612
{
613
    serializedArg.clear();
9✔
614

615
    if (!IsExplicitlySet())
9✔
616
    {
617
        return false;
×
618
    }
619

620
    std::string ret = "--";
18✔
621
    ret += GetName();
9✔
622
    if (GetType() == GAAT_BOOLEAN)
9✔
623
    {
624
        serializedArg = std::move(ret);
×
625
        return true;
×
626
    }
627

628
    const auto AppendString = [&ret](const std::string &str)
18✔
629
    {
630
        if (str.find('"') != std::string::npos ||
18✔
631
            str.find(' ') != std::string::npos ||
18✔
632
            str.find('\\') != std::string::npos ||
27✔
633
            str.find(',') != std::string::npos)
9✔
634
        {
635
            ret += '"';
×
636
            ret +=
637
                CPLString(str).replaceAll('\\', "\\\\").replaceAll('"', "\\\"");
×
638
            ret += '"';
×
639
        }
640
        else
641
        {
642
            ret += str;
9✔
643
        }
644
    };
9✔
645

646
    ret += ' ';
9✔
647
    switch (GetType())
9✔
648
    {
649
        case GAAT_BOOLEAN:
×
650
            break;
×
651
        case GAAT_STRING:
4✔
652
        {
653
            const auto &val = Get<std::string>();
4✔
654
            AppendString(val);
4✔
655
            break;
4✔
656
        }
657
        case GAAT_INTEGER:
×
658
        {
659
            ret += CPLSPrintf("%d", Get<int>());
×
660
            break;
×
661
        }
662
        case GAAT_REAL:
×
663
        {
664
            ret += CPLSPrintf("%.17g", Get<double>());
×
665
            break;
×
666
        }
667
        case GAAT_DATASET:
4✔
668
        {
669
            const auto &val = Get<GDALArgDatasetValue>();
4✔
670
            const auto &str = val.GetName();
4✔
671
            if (str.empty())
4✔
672
            {
673
                return false;
×
674
            }
675
            AppendString(str);
4✔
676
            break;
4✔
677
        }
678
        case GAAT_STRING_LIST:
×
679
        {
680
            const auto &vals = Get<std::vector<std::string>>();
×
681
            for (size_t i = 0; i < vals.size(); ++i)
×
682
            {
683
                if (i > 0)
×
684
                    ret += ',';
×
685
                AppendString(vals[i]);
×
686
            }
687
            break;
×
688
        }
689
        case GAAT_INTEGER_LIST:
×
690
        {
691
            const auto &vals = Get<std::vector<int>>();
×
692
            for (size_t i = 0; i < vals.size(); ++i)
×
693
            {
694
                if (i > 0)
×
695
                    ret += ',';
×
696
                ret += CPLSPrintf("%d", vals[i]);
×
697
            }
698
            break;
×
699
        }
700
        case GAAT_REAL_LIST:
×
701
        {
702
            const auto &vals = Get<std::vector<double>>();
×
703
            for (size_t i = 0; i < vals.size(); ++i)
×
704
            {
705
                if (i > 0)
×
706
                    ret += ',';
×
707
                ret += CPLSPrintf("%.17g", vals[i]);
×
708
            }
709
            break;
×
710
        }
711
        case GAAT_DATASET_LIST:
1✔
712
        {
713
            const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1✔
714
            for (size_t i = 0; i < vals.size(); ++i)
2✔
715
            {
716
                if (i > 0)
1✔
717
                    ret += ',';
×
718
                const auto &val = vals[i];
1✔
719
                const auto &str = val.GetName();
1✔
720
                if (str.empty())
1✔
721
                {
722
                    return false;
×
723
                }
724
                AppendString(str);
1✔
725
            }
726
            break;
1✔
727
        }
728
    }
729

730
    serializedArg = std::move(ret);
9✔
731
    return true;
9✔
732
}
733

734
/************************************************************************/
735
/*              GDALInConstructionAlgorithmArg::AddAlias()              */
736
/************************************************************************/
737

738
GDALInConstructionAlgorithmArg &
739
GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
5,669✔
740
{
741
    m_decl.AddAlias(alias);
5,669✔
742
    if (m_owner)
5,669✔
743
        m_owner->AddAliasFor(this, alias);
5,669✔
744
    return *this;
5,669✔
745
}
746

747
/************************************************************************/
748
/*            GDALInConstructionAlgorithmArg::AddHiddenAlias()          */
749
/************************************************************************/
750

751
GDALInConstructionAlgorithmArg &
752
GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
906✔
753
{
754
    m_decl.AddHiddenAlias(alias);
906✔
755
    if (m_owner)
906✔
756
        m_owner->AddAliasFor(this, alias);
906✔
757
    return *this;
906✔
758
}
759

760
/************************************************************************/
761
/*             GDALInConstructionAlgorithmArg::SetPositional()          */
762
/************************************************************************/
763

764
GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1,768✔
765
{
766
    m_decl.SetPositional();
1,768✔
767
    if (m_owner)
1,768✔
768
        m_owner->SetPositional(this);
1,768✔
769
    return *this;
1,768✔
770
}
771

772
/************************************************************************/
773
/*              GDALArgDatasetValue::GDALArgDatasetValue()              */
774
/************************************************************************/
775

776
GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
23✔
777
    : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
46✔
778
      m_nameSet(true)
23✔
779
{
780
    if (m_poDS)
23✔
781
        m_poDS->Reference();
23✔
782
}
23✔
783

784
/************************************************************************/
785
/*              GDALArgDatasetValue::Set()                              */
786
/************************************************************************/
787

788
void GDALArgDatasetValue::Set(const std::string &name)
660✔
789
{
790
    Close();
660✔
791
    m_name = name;
660✔
792
    m_nameSet = true;
660✔
793
    if (m_ownerArg)
660✔
794
        m_ownerArg->NotifyValueSet();
656✔
795
}
660✔
796

797
/************************************************************************/
798
/*              GDALArgDatasetValue::Set()                              */
799
/************************************************************************/
800

801
void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
384✔
802
{
803
    Close();
384✔
804
    m_poDS = poDS.release();
384✔
805
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
384✔
806
    m_nameSet = true;
384✔
807
    if (m_ownerArg)
384✔
808
        m_ownerArg->NotifyValueSet();
363✔
809
}
384✔
810

811
/************************************************************************/
812
/*              GDALArgDatasetValue::Set()                              */
813
/************************************************************************/
814

815
void GDALArgDatasetValue::Set(GDALDataset *poDS)
1,307✔
816
{
817
    Close();
1,307✔
818
    m_poDS = poDS;
1,307✔
819
    if (m_poDS)
1,307✔
820
        m_poDS->Reference();
1,140✔
821
    m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1,307✔
822
    m_nameSet = true;
1,307✔
823
    if (m_ownerArg)
1,307✔
824
        m_ownerArg->NotifyValueSet();
912✔
825
}
1,307✔
826

827
/************************************************************************/
828
/*                   GDALArgDatasetValue::SetFrom()                     */
829
/************************************************************************/
830

831
void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
346✔
832
{
833
    Close();
346✔
834
    m_name = other.m_name;
346✔
835
    m_nameSet = other.m_nameSet;
346✔
836
    m_poDS = other.m_poDS;
346✔
837
    if (m_poDS)
346✔
838
        m_poDS->Reference();
173✔
839
}
346✔
840

841
/************************************************************************/
842
/*              GDALArgDatasetValue::~GDALArgDatasetValue()             */
843
/************************************************************************/
844

845
GDALArgDatasetValue::~GDALArgDatasetValue()
3,433✔
846
{
847
    Close();
3,433✔
848
}
3,433✔
849

850
/************************************************************************/
851
/*                     GDALArgDatasetValue::Close()                     */
852
/************************************************************************/
853

854
bool GDALArgDatasetValue::Close()
6,579✔
855
{
856
    bool ret = true;
6,579✔
857
    if (m_poDS && m_poDS->Dereference() == 0)
6,579✔
858
    {
859
        ret = m_poDS->Close() == CE_None;
669✔
860
        delete m_poDS;
669✔
861
    }
862
    m_poDS = nullptr;
6,579✔
863
    return ret;
6,579✔
864
}
865

866
/************************************************************************/
867
/*                      GDALArgDatasetValue::operator=()                */
868
/************************************************************************/
869

870
GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
2✔
871
{
872
    Close();
2✔
873
    m_poDS = other.m_poDS;
2✔
874
    m_name = other.m_name;
2✔
875
    m_nameSet = other.m_nameSet;
2✔
876
    m_type = other.m_type;
2✔
877
    m_inputFlags = other.m_inputFlags;
2✔
878
    m_outputFlags = other.m_outputFlags;
2✔
879
    other.m_poDS = nullptr;
2✔
880
    other.m_name.clear();
2✔
881
    other.m_nameSet = false;
2✔
882
    return *this;
2✔
883
}
884

885
/************************************************************************/
886
/*                   GDALArgDatasetValue::GetDataset()                  */
887
/************************************************************************/
888

889
GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
131✔
890
{
891
    if (m_poDS)
131✔
892
        m_poDS->Reference();
130✔
893
    return m_poDS;
131✔
894
}
895

896
/************************************************************************/
897
/*               GDALArgDatasetValue(GDALArgDatasetValue &&other)       */
898
/************************************************************************/
899

900
GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
34✔
901
    : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet),
68✔
902
      m_type(other.m_type), m_inputFlags(other.m_inputFlags),
34✔
903
      m_outputFlags(other.m_outputFlags)
34✔
904
{
905
    other.m_poDS = nullptr;
34✔
906
    other.m_name.clear();
34✔
907
}
34✔
908

909
/************************************************************************/
910
/*              GDALInConstructionAlgorithmArg::SetIsCRSArg()           */
911
/************************************************************************/
912

913
GDALInConstructionAlgorithmArg &
914
GDALInConstructionAlgorithmArg::SetIsCRSArg(bool noneAllowed)
320✔
915
{
916
    if (GetType() != GAAT_STRING)
320✔
917
    {
918
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
919
                 "SetIsCRSArg() can only be called on a String argument");
920
        return *this;
1✔
921
    }
922
    AddValidationAction(
923
        [this, noneAllowed]()
91✔
924
        {
925
            const std::string &osVal =
926
                static_cast<const GDALInConstructionAlgorithmArg *>(this)
927
                    ->Get<std::string>();
43✔
928
            if (!noneAllowed || (osVal != "none" && osVal != "null"))
43✔
929
            {
930
                OGRSpatialReference oSRS;
39✔
931
                if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
39✔
932
                {
933
                    m_owner->ReportError(CE_Failure, CPLE_AppDefined,
5✔
934
                                         "Invalid value for '%s' argument",
935
                                         GetName().c_str());
5✔
936
                    return false;
5✔
937
                }
938
            }
939
            return true;
38✔
940
        });
319✔
941

942
    SetAutoCompleteFunction(
943
        [](const std::string &currentValue)
6✔
944
        {
945
            std::vector<std::string> oRet;
6✔
946
            if (!currentValue.empty())
6✔
947
            {
948
                const CPLStringList aosTokens(
949
                    CSLTokenizeString2(currentValue.c_str(), ":", 0));
10✔
950
                int nCount = 0;
5✔
951
                OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
5✔
952
                    aosTokens[0], nullptr, &nCount);
953
                std::string osCode;
10✔
954
                for (int i = 0; i < nCount; ++i)
33,050✔
955
                {
956
                    if (aosTokens.size() == 1 ||
52,872✔
957
                        STARTS_WITH(pCRSList[i]->pszCode, aosTokens[1]))
19,827✔
958
                    {
959
                        if (oRet.empty())
13,409✔
960
                            osCode = pCRSList[i]->pszCode;
5✔
961
                        oRet.push_back(std::string(pCRSList[i]->pszCode)
26,818✔
962
                                           .append(" -- ")
13,409✔
963
                                           .append(pCRSList[i]->pszName));
13,409✔
964
                    }
965
                }
966
                if (oRet.size() == 1)
5✔
967
                {
968
                    // If there is a single match, remove the name from the suggestion.
969
                    oRet.clear();
1✔
970
                    oRet.push_back(osCode);
1✔
971
                }
972
                OSRDestroyCRSInfoList(pCRSList);
5✔
973
            }
974
            if (oRet.empty())
6✔
975
            {
976
                const CPLStringList aosAuthorities(
977
                    OSRGetAuthorityListFromDatabase());
2✔
978
                for (const char *pszAuth : cpl::Iterate(aosAuthorities))
6✔
979
                {
980
                    int nCount = 0;
5✔
981
                    OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
5✔
982
                        pszAuth, nullptr, &nCount);
983
                    OSRDestroyCRSInfoList(pCRSList);
5✔
984
                    if (nCount)
5✔
985
                        oRet.push_back(std::string(pszAuth).append(":"));
4✔
986
                }
987
            }
988
            return oRet;
6✔
989
        });
319✔
990

991
    return *this;
319✔
992
}
993

994
/************************************************************************/
995
/*                     GDALAlgorithm::GDALAlgorithm()                  */
996
/************************************************************************/
997

998
GDALAlgorithm::GDALAlgorithm(const std::string &name,
2,343✔
999
                             const std::string &description,
1000
                             const std::string &helpURL)
2,343✔
1001
    : m_name(name), m_description(description), m_helpURL(helpURL),
1002
      m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
4,686✔
1003
                        ? "https://gdal.org" + m_helpURL
2,343✔
1004
                        : m_helpURL)
6,882✔
1005
{
1006
    AddArg("help", 'h', _("Display help message and exit"), &m_helpRequested)
4,686✔
1007
        .SetOnlyForCLI()
2,343✔
1008
        .SetCategory(GAAC_COMMON)
4,686✔
1009
        .AddAction([this]() { m_specialActionRequested = true; });
2,343✔
1010
    AddArg("help-doc", 0, _("Display help message for use by documentation"),
1011
           &m_helpDocRequested)
4,686✔
1012
        .SetHidden()
2,343✔
1013
        .AddAction([this]() { m_specialActionRequested = true; });
2,343✔
1014
    AddArg("version", 0, _("Display GDAL version and exit"), &m_dummyBoolean)
4,686✔
1015
        .SetOnlyForCLI()
2,343✔
1016
        .SetCategory(GAAC_COMMON);
2,343✔
1017
    AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1018
           &m_JSONUsageRequested)
4,686✔
1019
        .SetOnlyForCLI()
2,343✔
1020
        .SetCategory(GAAC_COMMON)
4,686✔
1021
        .AddAction([this]() { m_specialActionRequested = true; });
2,343✔
1022
    AddArg("drivers", 0, _("Display driver list as JSON document and exit"),
1023
           &m_dummyBoolean)
4,686✔
1024
        .SetOnlyForCLI()
2,343✔
1025
        .SetCategory(GAAC_COMMON);
2,343✔
1026
    AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
4,686✔
1027
        .SetMetaVar("<KEY>=<VALUE>")
4,686✔
1028
        .SetOnlyForCLI()
2,343✔
1029
        .SetCategory(GAAC_COMMON);
2,343✔
1030
}
2,343✔
1031

1032
/************************************************************************/
1033
/*                     GDALAlgorithm::~GDALAlgorithm()                  */
1034
/************************************************************************/
1035

1036
GDALAlgorithm::~GDALAlgorithm() = default;
1037

1038
/************************************************************************/
1039
/*                    GDALAlgorithm::ParseArgument()                    */
1040
/************************************************************************/
1041

1042
bool GDALAlgorithm::ParseArgument(
1,177✔
1043
    GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1044
    std::map<
1045
        GDALAlgorithmArg *,
1046
        std::variant<std::vector<std::string>, std::vector<int>,
1047
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
1048
        &inConstructionValues)
1049
{
1050
    const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1,177✔
1051
    if (arg->IsExplicitlySet() && !isListArg)
1,177✔
1052
    {
1053
        // Hack for "gdal info" to be able to pass an opened raster dataset
1054
        // by "gdal raster info" to the "gdal vector info" algorithm.
1055
        if (arg->SkipIfAlreadySet())
3✔
1056
        {
1057
            arg->SetSkipIfAlreadySet(false);
1✔
1058
            return true;
1✔
1059
        }
1060

1061
        ReportError(CE_Failure, CPLE_IllegalArg,
2✔
1062
                    "Argument '%s' has already been specified.", name.c_str());
1063
        return false;
2✔
1064
    }
1065

1066
    if (!arg->GetRepeatedArgAllowed() &&
1,217✔
1067
        cpl::contains(inConstructionValues, arg))
43✔
1068
    {
1069
        ReportError(CE_Failure, CPLE_IllegalArg,
1✔
1070
                    "Argument '%s' has already been specified.", name.c_str());
1071
        return false;
1✔
1072
    }
1073

1074
    switch (arg->GetType())
1,173✔
1075
    {
1076
        case GAAT_BOOLEAN:
149✔
1077
        {
1078
            if (value.empty() || value == "true")
149✔
1079
                return arg->Set(true);
147✔
1080
            else if (value == "false")
2✔
1081
                return arg->Set(false);
1✔
1082
            else
1083
            {
1084
                ReportError(
1✔
1085
                    CE_Failure, CPLE_IllegalArg,
1086
                    "Invalid value '%s' for boolean argument '%s'. Should be "
1087
                    "'true' or 'false'.",
1088
                    value.c_str(), name.c_str());
1089
                return false;
1✔
1090
            }
1091
        }
1092

1093
        case GAAT_STRING:
269✔
1094
        {
1095
            return arg->Set(value);
269✔
1096
        }
1097

1098
        case GAAT_INTEGER:
12✔
1099
        {
1100
            errno = 0;
12✔
1101
            char *endptr = nullptr;
12✔
1102
            const auto val = std::strtol(value.c_str(), &endptr, 10);
12✔
1103
            if (errno == 0 && endptr &&
11✔
1104
                endptr == value.c_str() + value.size() && val >= INT_MIN &&
23✔
1105
                val <= INT_MAX)
1106
            {
1107
                return arg->Set(static_cast<int>(val));
9✔
1108
            }
1109
            else
1110
            {
1111
                ReportError(CE_Failure, CPLE_IllegalArg,
3✔
1112
                            "Expected integer value for argument '%s', "
1113
                            "but got '%s'.",
1114
                            name.c_str(), value.c_str());
1115
                return false;
3✔
1116
            }
1117
        }
1118

1119
        case GAAT_REAL:
14✔
1120
        {
1121
            char *endptr = nullptr;
14✔
1122
            double dfValue = CPLStrtod(value.c_str(), &endptr);
14✔
1123
            if (endptr != value.c_str() + value.size())
14✔
1124
            {
1125
                ReportError(
1✔
1126
                    CE_Failure, CPLE_IllegalArg,
1127
                    "Expected real value for argument '%s', but got '%s'.",
1128
                    name.c_str(), value.c_str());
1129
                return false;
1✔
1130
            }
1131
            return arg->Set(dfValue);
13✔
1132
        }
1133

1134
        case GAAT_DATASET:
494✔
1135
        {
1136
            return arg->SetDatasetName(value);
494✔
1137
        }
1138

1139
        case GAAT_STRING_LIST:
110✔
1140
        {
1141
            const CPLStringList aosTokens(
1142
                arg->GetPackedValuesAllowed()
110✔
1143
                    ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
96✔
1144
                    : CSLAddString(nullptr, value.c_str()));
316✔
1145
            if (!cpl::contains(inConstructionValues, arg))
110✔
1146
            {
1147
                inConstructionValues[arg] = std::vector<std::string>();
86✔
1148
            }
1149
            auto &valueVector =
1150
                std::get<std::vector<std::string>>(inConstructionValues[arg]);
110✔
1151
            for (const char *v : aosTokens)
244✔
1152
            {
1153
                valueVector.push_back(v);
134✔
1154
            }
1155
            break;
110✔
1156
        }
1157

1158
        case GAAT_INTEGER_LIST:
39✔
1159
        {
1160
            const CPLStringList aosTokens(
1161
                arg->GetPackedValuesAllowed()
39✔
1162
                    ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
39✔
1163
                    : CSLAddString(nullptr, value.c_str()));
78✔
1164
            if (!cpl::contains(inConstructionValues, arg))
39✔
1165
            {
1166
                inConstructionValues[arg] = std::vector<int>();
35✔
1167
            }
1168
            auto &valueVector =
1169
                std::get<std::vector<int>>(inConstructionValues[arg]);
39✔
1170
            for (const char *v : aosTokens)
123✔
1171
            {
1172
                errno = 0;
88✔
1173
                char *endptr = nullptr;
88✔
1174
                const auto val = std::strtol(v, &endptr, 10);
88✔
1175
                if (errno == 0 && endptr && endptr == v + strlen(v) &&
88✔
1176
                    val >= INT_MIN && val <= INT_MAX)
84✔
1177
                {
1178
                    valueVector.push_back(static_cast<int>(val));
84✔
1179
                }
1180
                else
1181
                {
1182
                    ReportError(
4✔
1183
                        CE_Failure, CPLE_IllegalArg,
1184
                        "Expected list of integer value for argument '%s', "
1185
                        "but got '%s'.",
1186
                        name.c_str(), value.c_str());
1187
                    return false;
4✔
1188
                }
1189
            }
1190
            break;
35✔
1191
        }
1192

1193
        case GAAT_REAL_LIST:
69✔
1194
        {
1195
            const CPLStringList aosTokens(
1196
                arg->GetPackedValuesAllowed()
69✔
1197
                    ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
69✔
1198
                    : CSLAddString(nullptr, value.c_str()));
138✔
1199
            if (!cpl::contains(inConstructionValues, arg))
69✔
1200
            {
1201
                inConstructionValues[arg] = std::vector<double>();
67✔
1202
            }
1203
            auto &valueVector =
1204
                std::get<std::vector<double>>(inConstructionValues[arg]);
69✔
1205
            for (const char *v : aosTokens)
280✔
1206
            {
1207
                char *endptr = nullptr;
213✔
1208
                double dfValue = CPLStrtod(v, &endptr);
213✔
1209
                if (endptr != v + strlen(v))
213✔
1210
                {
1211
                    ReportError(
2✔
1212
                        CE_Failure, CPLE_IllegalArg,
1213
                        "Expected list of real value for argument '%s', "
1214
                        "but got '%s'.",
1215
                        name.c_str(), value.c_str());
1216
                    return false;
2✔
1217
                }
1218
                valueVector.push_back(dfValue);
211✔
1219
            }
1220
            break;
67✔
1221
        }
1222

1223
        case GAAT_DATASET_LIST:
17✔
1224
        {
1225
            const CPLStringList aosTokens(
1226
                CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS));
34✔
1227
            if (!cpl::contains(inConstructionValues, arg))
17✔
1228
            {
1229
                inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
16✔
1230
            }
1231
            auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1232
                inConstructionValues[arg]);
17✔
1233
            for (const char *v : aosTokens)
34✔
1234
            {
1235
                valueVector.push_back(GDALArgDatasetValue(v));
17✔
1236
            }
1237
            break;
17✔
1238
        }
1239
    }
1240

1241
    return true;
229✔
1242
}
1243

1244
/************************************************************************/
1245
/*               GDALAlgorithm::ParseCommandLineArguments()             */
1246
/************************************************************************/
1247

1248
bool GDALAlgorithm::ParseCommandLineArguments(
682✔
1249
    const std::vector<std::string> &args)
1250
{
1251
    if (m_parsedSubStringAlreadyCalled)
682✔
1252
    {
1253
        ReportError(CE_Failure, CPLE_AppDefined,
1✔
1254
                    "ParseCommandLineArguments() can only be called once per "
1255
                    "instance.");
1256
        return false;
1✔
1257
    }
1258
    m_parsedSubStringAlreadyCalled = true;
681✔
1259

1260
    // AWS like syntax supported too (not advertized)
1261
    if (args.size() == 1 && args[0] == "help")
681✔
1262
    {
1263
        auto arg = GetArg("help");
1✔
1264
        assert(arg);
1✔
1265
        arg->Set(true);
1✔
1266
        arg->RunActions();
1✔
1267
        return true;
1✔
1268
    }
1269

1270
    if (HasSubAlgorithms())
680✔
1271
    {
1272
        if (args.empty())
86✔
1273
        {
1274
            ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2✔
1275
                        m_callPath.size() == 1 ? "command" : "subcommand");
2✔
1276
            return false;
2✔
1277
        }
1278
        if (!args[0].empty() && args[0][0] == '-')
84✔
1279
        {
1280
            // go on argument parsing
1281
        }
1282
        else
1283
        {
1284
            m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
81✔
1285
            if (m_selectedSubAlgHolder)
81✔
1286
            {
1287
                m_selectedSubAlg = m_selectedSubAlgHolder.get();
80✔
1288
                m_selectedSubAlg->SetReferencePathForRelativePaths(
80✔
1289
                    m_referencePath);
80✔
1290
                m_selectedSubAlg->m_executionForStreamOutput =
80✔
1291
                    m_executionForStreamOutput;
80✔
1292
                bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
80✔
1293
                    std::vector<std::string>(args.begin() + 1, args.end()));
160✔
1294
                m_selectedSubAlg->PropagateSpecialActionTo(this);
80✔
1295
                return bRet;
80✔
1296
            }
1297
            else
1298
            {
1299
                ReportError(CE_Failure, CPLE_AppDefined,
1✔
1300
                            "Unknown command: '%s'", args[0].c_str());
1✔
1301
                return false;
1✔
1302
            }
1303
        }
1304
    }
1305

1306
    std::map<
1307
        GDALAlgorithmArg *,
1308
        std::variant<std::vector<std::string>, std::vector<int>,
1309
                     std::vector<double>, std::vector<GDALArgDatasetValue>>>
1310
        inConstructionValues;
1,194✔
1311

1312
    std::vector<std::string> lArgs(args);
1,194✔
1313
    for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
1,768✔
1314
    {
1315
        const auto &strArg = lArgs[i];
1,225✔
1316
        GDALAlgorithmArg *arg = nullptr;
1,225✔
1317
        std::string name;
1,225✔
1318
        std::string value;
1,225✔
1319
        bool hasValue = false;
1,225✔
1320
        if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
1,225✔
1321
        {
1322
            const auto equalPos = strArg.find('=');
646✔
1323
            name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
1,292✔
1324
                                                   : strArg;
646✔
1325
            auto iterArg = m_mapLongNameToArg.find(name.substr(2));
646✔
1326
            if (iterArg == m_mapLongNameToArg.end())
646✔
1327
            {
1328
                ReportError(CE_Failure, CPLE_IllegalArg,
16✔
1329
                            "Long name option '%s' is unknown.", name.c_str());
1330
                return false;
16✔
1331
            }
1332
            arg = iterArg->second;
630✔
1333
            if (equalPos != std::string::npos)
630✔
1334
            {
1335
                hasValue = true;
204✔
1336
                value = strArg.substr(equalPos + 1);
204✔
1337
            }
1338
        }
1339
        else if (strArg.size() >= 2 && strArg[0] == '-')
579✔
1340
        {
1341
            if (strArg.size() != 2)
59✔
1342
            {
1343
                ReportError(
1✔
1344
                    CE_Failure, CPLE_IllegalArg,
1345
                    "Option '%s' not recognized. Should be either a long "
1346
                    "option or a one-letter short option.",
1347
                    strArg.c_str());
1348
                return false;
2✔
1349
            }
1350
            name = strArg;
58✔
1351
            auto iterArg = m_mapShortNameToArg.find(name.substr(1));
58✔
1352
            if (iterArg == m_mapShortNameToArg.end())
58✔
1353
            {
1354
                ReportError(CE_Failure, CPLE_IllegalArg,
1✔
1355
                            "Short name option '%s' is unknown.", name.c_str());
1356
                return false;
1✔
1357
            }
1358
            arg = iterArg->second;
57✔
1359
        }
1360
        else
1361
        {
1362
            ++i;
520✔
1363
            continue;
520✔
1364
        }
1365
        assert(arg);
687✔
1366

1367
        if (arg->GetType() == GAAT_BOOLEAN)
687✔
1368
        {
1369
            if (!hasValue)
150✔
1370
            {
1371
                hasValue = true;
147✔
1372
                value = "true";
147✔
1373
            }
1374
        }
1375

1376
        if (!hasValue)
687✔
1377
        {
1378
            if (i + 1 == lArgs.size())
336✔
1379
            {
1380
                if (m_parseForAutoCompletion)
15✔
1381
                {
1382
                    lArgs.erase(lArgs.begin() + i);
14✔
1383
                    break;
14✔
1384
                }
1385
                ReportError(
1✔
1386
                    CE_Failure, CPLE_IllegalArg,
1387
                    "Expected value for argument '%s', but ran short of tokens",
1388
                    name.c_str());
1389
                return false;
1✔
1390
            }
1391
            value = lArgs[i + 1];
321✔
1392
            lArgs.erase(lArgs.begin() + i + 1);
321✔
1393
        }
1394

1395
        if (!ParseArgument(arg, name, value, inConstructionValues))
672✔
1396
            return false;
21✔
1397

1398
        lArgs.erase(lArgs.begin() + i);
651✔
1399
    }
1400

1401
    if (m_specialActionRequested)
557✔
1402
    {
1403
        return true;
14✔
1404
    }
1405

1406
    const auto ProcessInConstructionValues = [&inConstructionValues]()
734✔
1407
    {
1408
        for (auto &[arg, value] : inConstructionValues)
715✔
1409
        {
1410
            if (arg->GetType() == GAAT_STRING_LIST)
196✔
1411
            {
1412
                if (!arg->Set(std::get<std::vector<std::string>>(
83✔
1413
                        inConstructionValues[arg])))
83✔
1414
                {
1415
                    return false;
19✔
1416
                }
1417
            }
1418
            else if (arg->GetType() == GAAT_INTEGER_LIST)
113✔
1419
            {
1420
                if (!arg->Set(
32✔
1421
                        std::get<std::vector<int>>(inConstructionValues[arg])))
32✔
1422
                {
1423
                    return false;
1✔
1424
                }
1425
            }
1426
            else if (arg->GetType() == GAAT_REAL_LIST)
81✔
1427
            {
1428
                if (!arg->Set(std::get<std::vector<double>>(
65✔
1429
                        inConstructionValues[arg])))
65✔
1430
                {
1431
                    return false;
6✔
1432
                }
1433
            }
1434
            else if (arg->GetType() == GAAT_DATASET_LIST)
16✔
1435
            {
1436
                if (!arg->Set(
16✔
1437
                        std::move(std::get<std::vector<GDALArgDatasetValue>>(
1438
                            inConstructionValues[arg]))))
16✔
1439
                {
1440
                    return false;
1✔
1441
                }
1442
            }
1443
        }
1444
        return true;
519✔
1445
    };
543✔
1446

1447
    // Process positional arguments that have not been set through their
1448
    // option name.
1449
    size_t i = 0;
543✔
1450
    size_t iCurPosArg = 0;
543✔
1451
    while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
1,032✔
1452
    {
1453
        GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
496✔
1454
        while (arg->IsExplicitlySet())
499✔
1455
        {
1456
            ++iCurPosArg;
4✔
1457
            if (iCurPosArg == m_positionalArgs.size())
4✔
1458
                break;
1✔
1459
            arg = m_positionalArgs[iCurPosArg];
3✔
1460
        }
1461
        if (iCurPosArg == m_positionalArgs.size())
496✔
1462
        {
1463
            break;
1✔
1464
        }
1465
        if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
531✔
1466
            arg->GetMinCount() != arg->GetMaxCount())
36✔
1467
        {
1468
            if (iCurPosArg == 0)
31✔
1469
            {
1470
                size_t nCountAtEnd = 0;
20✔
1471
                for (size_t j = 1; j < m_positionalArgs.size(); j++)
37✔
1472
                {
1473
                    const auto *otherArg = m_positionalArgs[j];
19✔
1474
                    if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
19✔
1475
                    {
1476
                        if (otherArg->GetMinCount() != otherArg->GetMaxCount())
4✔
1477
                        {
1478
                            ReportError(
2✔
1479
                                CE_Failure, CPLE_AppDefined,
1480
                                "Ambiguity in definition of positional "
1481
                                "argument "
1482
                                "'%s' given it has a varying number of values, "
1483
                                "but follows argument '%s' which also has a "
1484
                                "varying number of values",
1485
                                otherArg->GetName().c_str(),
1✔
1486
                                arg->GetName().c_str());
1✔
1487
                            ProcessInConstructionValues();
1✔
1488
                            return false;
1✔
1489
                        }
1490
                        nCountAtEnd += otherArg->GetMinCount();
3✔
1491
                    }
1492
                    else
1493
                    {
1494
                        if (!otherArg->IsRequired())
15✔
1495
                        {
1496
                            ReportError(
2✔
1497
                                CE_Failure, CPLE_AppDefined,
1498
                                "Ambiguity in definition of positional "
1499
                                "argument "
1500
                                "'%s', given it is not required but follows "
1501
                                "argument '%s' which has a varying number of "
1502
                                "values",
1503
                                otherArg->GetName().c_str(),
1✔
1504
                                arg->GetName().c_str());
1✔
1505
                            ProcessInConstructionValues();
1✔
1506
                            return false;
1✔
1507
                        }
1508
                        nCountAtEnd++;
14✔
1509
                    }
1510
                }
1511
                if (lArgs.size() < nCountAtEnd)
18✔
1512
                {
1513
                    ReportError(CE_Failure, CPLE_AppDefined,
1✔
1514
                                "Not enough positional values.");
1515
                    ProcessInConstructionValues();
1✔
1516
                    return false;
1✔
1517
                }
1518
                for (; i < lArgs.size() - nCountAtEnd; ++i)
40✔
1519
                {
1520
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
23✔
1521
                                       inConstructionValues))
1522
                    {
UNCOV
1523
                        ProcessInConstructionValues();
×
UNCOV
1524
                        return false;
×
1525
                    }
1526
                }
1527
            }
1528
            else if (iCurPosArg == m_positionalArgs.size() - 1)
11✔
1529
            {
1530
                for (; i < lArgs.size(); ++i)
25✔
1531
                {
1532
                    if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
15✔
1533
                                       inConstructionValues))
1534
                    {
UNCOV
1535
                        ProcessInConstructionValues();
×
UNCOV
1536
                        return false;
×
1537
                    }
1538
                }
1539
            }
1540
            else
1541
            {
1542
                ReportError(CE_Failure, CPLE_AppDefined,
1✔
1543
                            "Ambiguity in definition of positional arguments: "
1544
                            "arguments with varying number of values must be "
1545
                            "first or last one.");
1546
                return false;
1✔
1547
            }
1548
        }
1549
        else
1550
        {
1551
            if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
464✔
1552
            {
1553
                ReportError(CE_Failure, CPLE_AppDefined,
1✔
1554
                            "Not enough positional values.");
1555
                return false;
1✔
1556
            }
1557
            const size_t iMax = i + arg->GetMaxCount();
463✔
1558
            for (; i < iMax; ++i)
929✔
1559
            {
1560
                if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
467✔
1561
                                   inConstructionValues))
1562
                {
1563
                    ProcessInConstructionValues();
1✔
1564
                    return false;
1✔
1565
                }
1566
            }
1567
        }
1568
        ++iCurPosArg;
489✔
1569
    }
1570

1571
    if (i < lArgs.size())
537✔
1572
    {
1573
        ReportError(CE_Failure, CPLE_AppDefined,
3✔
1574
                    "Positional values starting at '%s' are not expected.",
1575
                    lArgs[i].c_str());
3✔
1576
        return false;
3✔
1577
    }
1578

1579
    if (!ProcessInConstructionValues())
534✔
1580
    {
1581
        return false;
19✔
1582
    }
1583

1584
    // Skip to first unset positional argument.
1585
    while (iCurPosArg < m_positionalArgs.size() &&
814✔
1586
           m_positionalArgs[iCurPosArg]->IsExplicitlySet())
166✔
1587
    {
1588
        ++iCurPosArg;
133✔
1589
    }
1590
    // Check if this positional argument is required.
1591
    if (iCurPosArg < m_positionalArgs.size() &&
548✔
1592
        (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
33✔
1593
             ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
1✔
1594
             : m_positionalArgs[iCurPosArg]->IsRequired()))
32✔
1595
    {
1596
        ReportError(CE_Failure, CPLE_AppDefined,
28✔
1597
                    "Positional arguments starting at '%s' have not been "
1598
                    "specified.",
1599
                    m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
28✔
1600
        return false;
28✔
1601
    }
1602

1603
    return m_skipValidationInParseCommandLine || ValidateArguments();
487✔
1604
}
1605

1606
/************************************************************************/
1607
/*                     GDALAlgorithm::ReportError()                     */
1608
/************************************************************************/
1609

1610
//! @cond Doxygen_Suppress
1611
void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
184✔
1612
                                const char *fmt, ...) const
1613
{
1614
    va_list args;
1615
    va_start(args, fmt);
184✔
1616
    CPLError(eErrClass, err_no, "%s",
184✔
1617
             std::string(m_name)
184✔
1618
                 .append(": ")
184✔
1619
                 .append(CPLString().vPrintf(fmt, args))
368✔
1620
                 .c_str());
1621
    va_end(args);
184✔
1622
}
184✔
1623

1624
//! @endcond
1625

1626
/************************************************************************/
1627
/*                   GDALAlgorithm::ProcessDatasetArg()                 */
1628
/************************************************************************/
1629

1630
bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
1,637✔
1631
                                      GDALAlgorithm *algForOutput)
1632
{
1633
    bool ret = true;
1,637✔
1634

1635
    const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
1,637✔
1636
    const bool update = (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
2,393✔
1637
                         updateArg->Get<bool>());
756✔
1638
    const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
1,637✔
1639
    const bool overwrite =
1640
        (arg->IsOutput() && overwriteArg &&
3,040✔
1641
         overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
3,040✔
1642
    auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
1,637✔
1643
    auto &val = arg->Get<GDALArgDatasetValue>();
1,637✔
1644
    if (!val.GetDatasetRef() && !val.IsNameSet())
1,637✔
1645
    {
1646
        ReportError(CE_Failure, CPLE_AppDefined,
2✔
1647
                    "Argument '%s' has no dataset object or dataset name.",
1648
                    arg->GetName().c_str());
2✔
1649
        ret = false;
2✔
1650
    }
1651
    else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2,563✔
1652
             (!arg->IsOutput() || (arg == outputArg && update && !overwrite)))
928✔
1653
    {
1654
        int flags = val.GetType();
269✔
1655
        bool assignToOutputArg = false;
269✔
1656

1657
        // Check if input and output parameters point to the same
1658
        // filename (for vector datasets)
1659
        if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
507✔
1660
            outputArg && outputArg->GetType() == GAAT_DATASET)
507✔
1661
        {
1662
            auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
19✔
1663
            if (!outputVal.GetDatasetRef() &&
38✔
1664
                outputVal.GetName() == val.GetName() &&
38✔
1665
                (outputVal.GetInputFlags() & GADV_OBJECT) != 0)
1✔
1666
            {
1667
                assignToOutputArg = true;
1✔
1668
                flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
1✔
1669
            }
1670
        }
1671

1672
        if (!arg->IsOutput() || val.GetInputFlags() == GADV_NAME)
269✔
1673
            flags |= GDAL_OF_VERBOSE_ERROR;
251✔
1674
        if ((arg == outputArg || !outputArg) && update)
269✔
1675
            flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
19✔
1676

1677
        const auto readOnlyArg = algForOutput->GetArg(GDAL_ARG_NAME_READ_ONLY);
269✔
1678
        const bool readOnly =
1679
            (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
277✔
1680
             readOnlyArg->Get<bool>());
8✔
1681
        if (readOnly)
269✔
1682
            flags &= ~GDAL_OF_UPDATE;
2✔
1683

1684
        CPLStringList aosOpenOptions;
538✔
1685
        CPLStringList aosAllowedDrivers;
538✔
1686
        if (arg->GetName() == GDAL_ARG_NAME_INPUT)
269✔
1687
        {
1688
            const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
238✔
1689
            if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
238✔
1690
                aosOpenOptions =
1691
                    CPLStringList(ooArg->Get<std::vector<std::string>>());
235✔
1692

1693
            const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
238✔
1694
            if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
238✔
1695
                aosAllowedDrivers =
1696
                    CPLStringList(ifArg->Get<std::vector<std::string>>());
235✔
1697
        }
1698

1699
        std::string osDatasetName = val.GetName();
538✔
1700
        if (!m_referencePath.empty())
269✔
1701
        {
1702
            osDatasetName = GDALDataset::BuildFilename(
8✔
1703
                osDatasetName.c_str(), m_referencePath.c_str(), true);
4✔
1704
        }
1705

1706
        auto poDS =
1707
            GDALDataset::Open(osDatasetName.c_str(), flags,
269✔
1708
                              aosAllowedDrivers.List(), aosOpenOptions.List());
269✔
1709
        if (poDS)
269✔
1710
        {
1711
            if (assignToOutputArg)
250✔
1712
            {
1713
                // Avoid opening twice the same datasource if it is both
1714
                // the input and output.
1715
                // Known to cause problems with at least FGdb, SQLite
1716
                // and GPKG drivers. See #4270
1717
                // Restrict to those 3 drivers. For example it is known
1718
                // to break with the PG driver due to the way it
1719
                // manages transactions.
1720
                auto poDriver = poDS->GetDriver();
1✔
1721
                if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2✔
1722
                                 EQUAL(poDriver->GetDescription(), "SQLite") ||
1✔
1723
                                 EQUAL(poDriver->GetDescription(), "GPKG")))
1✔
1724
                {
1725
                    outputArg->Get<GDALArgDatasetValue>().Set(poDS);
1✔
1726
                }
1727
            }
1728
            val.Set(poDS);
250✔
1729
            poDS->ReleaseRef();
250✔
1730
        }
1731
        else
1732
        {
1733
            ret = false;
19✔
1734
        }
1735
    }
1736
    return ret;
1,637✔
1737
}
1738

1739
/************************************************************************/
1740
/*                   GDALAlgorithm::ValidateArguments()                 */
1741
/************************************************************************/
1742

1743
bool GDALAlgorithm::ValidateArguments()
1,333✔
1744
{
1745
    if (m_selectedSubAlg)
1,333✔
1746
        return m_selectedSubAlg->ValidateArguments();
3✔
1747

1748
    if (m_specialActionRequested)
1,330✔
1749
        return true;
1✔
1750

1751
    bool ret = true;
1,329✔
1752
    std::map<std::string, std::string> mutualExclusionGroupUsed;
1,329✔
1753
    for (auto &arg : m_args)
23,753✔
1754
    {
1755
        // Check mutually exclusive arguments
1756
        if (arg->IsExplicitlySet())
22,424✔
1757
        {
1758
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3,066✔
1759
            if (!mutualExclusionGroup.empty())
3,066✔
1760
            {
1761
                auto oIter =
1762
                    mutualExclusionGroupUsed.find(mutualExclusionGroup);
242✔
1763
                if (oIter != mutualExclusionGroupUsed.end())
242✔
1764
                {
1765
                    ret = false;
5✔
1766
                    ReportError(
10✔
1767
                        CE_Failure, CPLE_AppDefined,
1768
                        "Argument '%s' is mutually exclusive with '%s'.",
1769
                        arg->GetName().c_str(), oIter->second.c_str());
10✔
1770
                }
1771
                else
1772
                {
1773
                    mutualExclusionGroupUsed[mutualExclusionGroup] =
237✔
1774
                        arg->GetName();
474✔
1775
                }
1776
            }
1777
        }
1778

1779
        if (arg->IsRequired() && !arg->IsExplicitlySet() &&
22,430✔
1780
            !arg->HasDefaultValue())
6✔
1781
        {
1782
            ReportError(CE_Failure, CPLE_AppDefined,
6✔
1783
                        "Required argument '%s' has not been specified.",
1784
                        arg->GetName().c_str());
6✔
1785
            ret = false;
6✔
1786
        }
1787
        else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
22,418✔
1788
        {
1789
            if (!ProcessDatasetArg(arg.get(), this))
1,565✔
1790
                ret = false;
19✔
1791
        }
1792
        else if (arg->IsExplicitlySet() &&
22,354✔
1793
                 GDALAlgorithmArgTypeIsList(arg->GetType()))
1,501✔
1794
        {
1795
            int valueCount = 0;
443✔
1796
            if (arg->GetType() == GAAT_STRING_LIST)
443✔
1797
            {
1798
                valueCount = static_cast<int>(
217✔
1799
                    arg->Get<std::vector<std::string>>().size());
217✔
1800
            }
1801
            else if (arg->GetType() == GAAT_INTEGER_LIST)
226✔
1802
            {
1803
                valueCount =
58✔
1804
                    static_cast<int>(arg->Get<std::vector<int>>().size());
58✔
1805
            }
1806
            else if (arg->GetType() == GAAT_REAL_LIST)
168✔
1807
            {
1808
                valueCount =
121✔
1809
                    static_cast<int>(arg->Get<std::vector<double>>().size());
121✔
1810
            }
1811
            else if (arg->GetType() == GAAT_DATASET_LIST)
47✔
1812
            {
1813
                valueCount = static_cast<int>(
47✔
1814
                    arg->Get<std::vector<GDALArgDatasetValue>>().size());
47✔
1815
            }
1816

1817
            if (valueCount != arg->GetMinCount() &&
706✔
1818
                arg->GetMinCount() == arg->GetMaxCount())
263✔
1819
            {
1820
                ReportError(
2✔
1821
                    CE_Failure, CPLE_AppDefined,
1822
                    "%d value(s) have been specified for argument '%s', "
1823
                    "whereas exactly %d were expected.",
1824
                    valueCount, arg->GetName().c_str(), arg->GetMinCount());
1✔
1825
                ret = false;
1✔
1826
            }
1827
            else if (valueCount < arg->GetMinCount())
442✔
1828
            {
1829
                ReportError(
4✔
1830
                    CE_Failure, CPLE_AppDefined,
1831
                    "Only %d value(s) have been specified for argument '%s', "
1832
                    "whereas at least %d were expected.",
1833
                    valueCount, arg->GetName().c_str(), arg->GetMinCount());
2✔
1834
                ret = false;
2✔
1835
            }
1836
            else if (valueCount > arg->GetMaxCount())
440✔
1837
            {
1838
                ReportError(CE_Failure, CPLE_AppDefined,
2✔
1839
                            "%d values have been specified for argument '%s', "
1840
                            "whereas at most %d were expected.",
1841
                            valueCount, arg->GetName().c_str(),
1✔
1842
                            arg->GetMaxCount());
1843
                ret = false;
1✔
1844
            }
1845
        }
1846

1847
        if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
22,471✔
1848
            arg->AutoOpenDataset())
47✔
1849
        {
1850
            auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
4✔
1851
            for (auto &val : listVal)
8✔
1852
            {
1853
                if (!val.GetDatasetRef() && val.GetName().empty())
4✔
1854
                {
1855
                    ReportError(
1✔
1856
                        CE_Failure, CPLE_AppDefined,
1857
                        "Argument '%s' has no dataset object or dataset name.",
1858
                        arg->GetName().c_str());
1✔
1859
                    ret = false;
1✔
1860
                }
1861
                else if (!val.GetDatasetRef())
3✔
1862
                {
1863
                    int flags = val.GetType() | GDAL_OF_VERBOSE_ERROR;
3✔
1864

1865
                    CPLStringList aosOpenOptions;
6✔
1866
                    CPLStringList aosAllowedDrivers;
6✔
1867
                    if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3✔
1868
                    {
1869
                        const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
1✔
1870
                        if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
1✔
1871
                        {
1872
                            aosOpenOptions = CPLStringList(
1✔
1873
                                ooArg->Get<std::vector<std::string>>());
1✔
1874
                        }
1875

1876
                        const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
1✔
1877
                        if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
1✔
1878
                        {
1879
                            aosAllowedDrivers = CPLStringList(
1✔
1880
                                ifArg->Get<std::vector<std::string>>());
1✔
1881
                        }
1882

1883
                        const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
1✔
1884
                        if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
2✔
1885
                            updateArg->Get<bool>())
1✔
1886
                        {
1887
                            flags |= GDAL_OF_UPDATE;
1✔
1888
                        }
1889
                    }
1890

1891
                    auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1892
                        val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3✔
1893
                        aosOpenOptions.List()));
9✔
1894
                    if (poDS)
3✔
1895
                    {
1896
                        val.Set(std::move(poDS));
2✔
1897
                    }
1898
                    else
1899
                    {
1900
                        ret = false;
1✔
1901
                    }
1902
                }
1903
            }
1904
        }
1905
    }
1906
    return ret;
1,329✔
1907
}
1908

1909
/************************************************************************/
1910
/*                      GDALAlgorithm::GetArg()                         */
1911
/************************************************************************/
1912

1913
const GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName) const
13,223✔
1914
{
1915
    const auto nPos = osName.find_first_not_of('-');
13,223✔
1916
    if (nPos == std::string::npos)
13,223✔
1917
        return nullptr;
8✔
1918
    const std::string osKey = osName.substr(nPos);
26,430✔
1919
    {
1920
        const auto oIter = m_mapLongNameToArg.find(osKey);
13,215✔
1921
        if (oIter != m_mapLongNameToArg.end())
13,215✔
1922
            return oIter->second;
11,172✔
1923
    }
1924
    {
1925
        const auto oIter = m_mapShortNameToArg.find(osKey);
2,043✔
1926
        if (oIter != m_mapShortNameToArg.end())
2,043✔
1927
            return oIter->second;
2✔
1928
    }
1929
    return nullptr;
2,041✔
1930
}
1931

1932
/************************************************************************/
1933
/*                   GDALAlgorithm::AddAliasFor()                       */
1934
/************************************************************************/
1935

1936
//! @cond Doxygen_Suppress
1937
void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
6,575✔
1938
                                const std::string &alias)
1939
{
1940
    if (cpl::contains(m_mapLongNameToArg, alias))
6,575✔
1941
    {
1942
        ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
1✔
1943
                    alias.c_str());
1944
    }
1945
    else
1946
    {
1947
        m_mapLongNameToArg[alias] = arg;
6,574✔
1948
    }
1949
}
6,575✔
1950

1951
//! @endcond
1952

1953
/************************************************************************/
1954
/*                   GDALAlgorithm::SetPositional()                     */
1955
/************************************************************************/
1956

1957
//! @cond Doxygen_Suppress
1958
void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
1,768✔
1959
{
1960
    CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
1,768✔
1961
                        arg) == m_positionalArgs.end());
1962
    m_positionalArgs.push_back(arg);
1,768✔
1963
}
1,768✔
1964

1965
//! @endcond
1966

1967
/************************************************************************/
1968
/*                     GDALAlgorithm::AddArg()                          */
1969
/************************************************************************/
1970

1971
GDALInConstructionAlgorithmArg &
1972
GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
28,582✔
1973
{
1974
    auto argRaw = arg.get();
28,582✔
1975
    const auto &longName = argRaw->GetName();
28,582✔
1976
    if (!longName.empty())
28,582✔
1977
    {
1978
        if (longName[0] == '-')
28,579✔
1979
        {
1980
            ReportError(CE_Failure, CPLE_AppDefined,
1✔
1981
                        "Long name '%s' should not start with '-'",
1982
                        longName.c_str());
1983
        }
1984
        if (longName.find('=') != std::string::npos)
28,579✔
1985
        {
1986
            ReportError(CE_Failure, CPLE_AppDefined,
1✔
1987
                        "Long name '%s' should not contain a '=' character",
1988
                        longName.c_str());
1989
        }
1990
        if (cpl::contains(m_mapLongNameToArg, longName))
28,579✔
1991
        {
1992
            ReportError(CE_Failure, CPLE_AppDefined,
1✔
1993
                        "Long name '%s' already declared", longName.c_str());
1994
        }
1995
        m_mapLongNameToArg[longName] = argRaw;
28,579✔
1996
    }
1997
    const auto &shortName = argRaw->GetShortName();
28,582✔
1998
    if (!shortName.empty())
28,582✔
1999
    {
2000
        if (shortName.size() != 1 ||
12,358✔
2001
            !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
6,179✔
2002
              (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3✔
2003
              (shortName[0] >= '0' && shortName[0] <= '9')))
2✔
2004
        {
2005
            ReportError(CE_Failure, CPLE_AppDefined,
1✔
2006
                        "Short name '%s' should be a single letter or digit",
2007
                        shortName.c_str());
2008
        }
2009
        if (cpl::contains(m_mapShortNameToArg, shortName))
6,179✔
2010
        {
2011
            ReportError(CE_Failure, CPLE_AppDefined,
1✔
2012
                        "Short name '%s' already declared", shortName.c_str());
2013
        }
2014
        m_mapShortNameToArg[shortName] = argRaw;
6,179✔
2015
    }
2016
    m_args.emplace_back(std::move(arg));
28,582✔
2017
    return *(
2018
        static_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
28,582✔
2019
}
2020

2021
GDALInConstructionAlgorithmArg &
2022
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
16,226✔
2023
                      const std::string &helpMessage, bool *pValue)
2024
{
2025
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
16,226✔
2026
        this,
2027
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
32,452✔
2028
        pValue));
32,452✔
2029
}
2030

2031
GDALInConstructionAlgorithmArg &
2032
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3,159✔
2033
                      const std::string &helpMessage, std::string *pValue)
2034
{
2035
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3,159✔
2036
        this,
2037
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
6,318✔
2038
        pValue));
6,318✔
2039
}
2040

2041
GDALInConstructionAlgorithmArg &
2042
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
168✔
2043
                      const std::string &helpMessage, int *pValue)
2044
{
2045
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
168✔
2046
        this,
2047
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
336✔
2048
        pValue));
336✔
2049
}
2050

2051
GDALInConstructionAlgorithmArg &
2052
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
312✔
2053
                      const std::string &helpMessage, double *pValue)
2054
{
2055
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
312✔
2056
        this,
2057
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
624✔
2058
        pValue));
624✔
2059
}
2060

2061
GDALInConstructionAlgorithmArg &
2062
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1,804✔
2063
                      const std::string &helpMessage,
2064
                      GDALArgDatasetValue *pValue, GDALArgDatasetValueType type)
2065
{
2066
    pValue->SetType(type);
1,804✔
2067
    auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1,804✔
2068
        this,
2069
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_DATASET),
3,608✔
2070
        pValue));
1,804✔
2071
    pValue->SetOwnerArgument(&arg);
1,804✔
2072
    return arg;
1,804✔
2073
}
2074

2075
GDALInConstructionAlgorithmArg &
2076
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
6,286✔
2077
                      const std::string &helpMessage,
2078
                      std::vector<std::string> *pValue)
2079
{
2080
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
6,286✔
2081
        this,
2082
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
12,572✔
2083
                             GAAT_STRING_LIST),
2084
        pValue));
12,572✔
2085
}
2086

2087
GDALInConstructionAlgorithmArg &
2088
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
168✔
2089
                      const std::string &helpMessage, std::vector<int> *pValue)
2090
{
2091
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
168✔
2092
        this,
2093
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
336✔
2094
                             GAAT_INTEGER_LIST),
2095
        pValue));
336✔
2096
}
2097

2098
GDALInConstructionAlgorithmArg &
2099
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
418✔
2100
                      const std::string &helpMessage,
2101
                      std::vector<double> *pValue)
2102
{
2103
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
418✔
2104
        this,
2105
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
836✔
2106
                             GAAT_REAL_LIST),
2107
        pValue));
836✔
2108
}
2109

2110
GDALInConstructionAlgorithmArg &
2111
GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
41✔
2112
                      const std::string &helpMessage,
2113
                      std::vector<GDALArgDatasetValue> *pValue,
2114
                      GDALArgDatasetValueType)
2115
{
2116
    // FIXME
2117
    // pValue->SetType(type);
2118
    return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
41✔
2119
        this,
2120
        GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
82✔
2121
                             GAAT_DATASET_LIST),
2122
        pValue));
82✔
2123
}
2124

2125
/************************************************************************/
2126
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
2127
/************************************************************************/
2128

2129
GDALInConstructionAlgorithmArg &
2130
GDALAlgorithm::AddInputDatasetArg(GDALArgDatasetValue *pValue,
882✔
2131
                                  GDALArgDatasetValueType type,
2132
                                  bool positionalAndRequired)
2133
{
2134
    auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
2135
                       CPLSPrintf("Input %s dataset",
2136
                                  GDALArgDatasetValueTypeName(type).c_str()),
882✔
2137
                       pValue, type);
1,764✔
2138
    if (positionalAndRequired)
882✔
2139
        arg.SetPositional().SetRequired();
737✔
2140

2141
    arg.SetAutoCompleteFunction(
2142
        [type](const std::string &currentValue)
1,083✔
2143
        {
2144
            std::vector<std::string> oRet;
3✔
2145

2146
            auto poDM = GetGDALDriverManager();
3✔
2147
            std::set<std::string> oExtensions;
6✔
2148
            for (int i = 0; i < poDM->GetDriverCount(); ++i)
663✔
2149
            {
2150
                auto poDriver = poDM->GetDriver(i);
660✔
2151
                if (((type & GDAL_OF_RASTER) != 0 &&
1,980✔
2152
                     poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
660✔
2153
                    ((type & GDAL_OF_VECTOR) != 0 &&
210✔
2154
                     poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
1,320✔
2155
                    ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
210✔
2156
                     poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
×
2157
                {
2158
                    const char *pszExtensions =
2159
                        poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
450✔
2160
                    if (pszExtensions)
450✔
2161
                    {
2162
                        const CPLStringList aosExts(
2163
                            CSLTokenizeString2(pszExtensions, " ", 0));
582✔
2164
                        for (const char *pszExt : cpl::Iterate(aosExts))
648✔
2165
                            oExtensions.insert(CPLString(pszExt).tolower());
357✔
2166
                    }
2167
                }
2168
            }
2169

2170
            std::string osDir = CPLGetDirnameSafe(currentValue.c_str());
6✔
2171
            auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3✔
2172
            const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
6✔
2173
            if (currentValue.empty())
3✔
2174
                osDir.clear();
1✔
2175
            const std::string currentFilename =
2176
                CPLGetFilename(currentValue.c_str());
6✔
2177
            if (psDir)
3✔
2178
            {
2179
                while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
140✔
2180
                {
2181
                    if ((currentFilename.empty() ||
137✔
2182
                         STARTS_WITH(psEntry->pszName,
×
2183
                                     currentFilename.c_str())) &&
137✔
2184
                        strcmp(psEntry->pszName, ".") != 0 &&
137✔
2185
                        strcmp(psEntry->pszName, "..") != 0 &&
411✔
2186
                        !strstr(psEntry->pszName, ".aux.xml"))
137✔
2187
                    {
2188
                        if (cpl::contains(
408✔
2189
                                oExtensions,
2190
                                CPLString(CPLGetExtensionSafe(psEntry->pszName))
272✔
2191
                                    .tolower()) ||
385✔
2192
                            VSI_ISDIR(psEntry->nMode))
113✔
2193
                        {
2194
                            std::string osVal;
54✔
2195
                            if (osDir.empty())
27✔
2196
                                osVal = psEntry->pszName;
4✔
2197
                            else
2198
                                osVal = CPLFormFilenameSafe(
46✔
2199
                                    osDir.c_str(), psEntry->pszName, nullptr);
46✔
2200
                            if (VSI_ISDIR(psEntry->nMode))
27✔
2201
                                osVal += osSep;
4✔
2202
                            oRet.push_back(osVal);
27✔
2203
                        }
2204
                    }
2205
                }
137✔
2206
                VSICloseDir(psDir);
3✔
2207
            }
2208
            return oRet;
6✔
2209
        });
882✔
2210

2211
    return arg;
882✔
2212
}
2213

2214
/************************************************************************/
2215
/*                 GDALAlgorithm::AddInputDatasetArg()                  */
2216
/************************************************************************/
2217

2218
GDALInConstructionAlgorithmArg &
2219
GDALAlgorithm::AddInputDatasetArg(std::vector<GDALArgDatasetValue> *pValue,
1✔
2220
                                  GDALArgDatasetValueType type,
2221
                                  bool positionalAndRequired)
2222
{
2223
    auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
2224
                       CPLSPrintf("Input %s datasets",
2225
                                  GDALArgDatasetValueTypeName(type).c_str()),
1✔
2226
                       pValue, type);
2✔
2227
    if (positionalAndRequired)
1✔
2228
        arg.SetPositional().SetRequired();
1✔
2229
    return arg;
1✔
2230
}
2231

2232
/************************************************************************/
2233
/*                 GDALAlgorithm::AddOutputDatasetArg()                 */
2234
/************************************************************************/
2235

2236
GDALInConstructionAlgorithmArg &
2237
GDALAlgorithm::AddOutputDatasetArg(GDALArgDatasetValue *pValue,
830✔
2238
                                   GDALArgDatasetValueType type,
2239
                                   bool positionalAndRequired)
2240
{
2241
    pValue->SetInputFlags(GADV_NAME);
830✔
2242
    pValue->SetOutputFlags(GADV_OBJECT);
830✔
2243
    auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
2244
                       CPLSPrintf("Output %s dataset",
2245
                                  GDALArgDatasetValueTypeName(type).c_str()),
830✔
2246
                       pValue, type)
2,490✔
2247
                    .SetIsInput(true)
830✔
2248
                    .SetIsOutput(true);
830✔
2249
    if (positionalAndRequired)
830✔
2250
        arg.SetPositional().SetRequired();
685✔
2251
    return arg;
830✔
2252
}
2253

2254
/************************************************************************/
2255
/*                 GDALAlgorithm::AddOverwriteArg()                     */
2256
/************************************************************************/
2257

2258
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOverwriteArg(bool *pValue)
821✔
2259
{
2260
    return AddArg(GDAL_ARG_NAME_OVERWRITE, 0,
2261
                  _("Whether overwriting existing output is allowed"), pValue)
1,642✔
2262
        .SetDefault(false);
1,642✔
2263
}
2264

2265
/************************************************************************/
2266
/*                 GDALAlgorithm::AddUpdateArg()                        */
2267
/************************************************************************/
2268

2269
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddUpdateArg(bool *pValue)
443✔
2270
{
2271
    return AddArg(GDAL_ARG_NAME_UPDATE, 0,
2272
                  _("Whether to open existing dataset in update mode"), pValue)
886✔
2273
        .SetDefault(false);
886✔
2274
}
2275

2276
/************************************************************************/
2277
/*                 GDALAlgorithm::AddOptionsSuggestions()               */
2278
/************************************************************************/
2279

2280
/* static */
2281
bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
21✔
2282
                                          const std::string &currentValue,
2283
                                          std::vector<std::string> &oRet)
2284
{
2285
    if (!pszXML)
21✔
2286
        return false;
×
2287
    CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
42✔
2288
    if (!poTree)
21✔
2289
        return false;
×
2290
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
319✔
2291
         psChild = psChild->psNext)
298✔
2292
    {
2293
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
308✔
2294
        if (pszName && currentValue == pszName &&
318✔
2295
            EQUAL(psChild->pszValue, "Option"))
10✔
2296
        {
2297
            const char *pszType = CPLGetXMLValue(psChild, "type", "");
10✔
2298
            const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
10✔
2299
            const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
10✔
2300
            if (EQUAL(pszType, "string-select"))
10✔
2301
            {
2302
                for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
72✔
2303
                     psChild2 = psChild2->psNext)
68✔
2304
                {
2305
                    if (EQUAL(psChild2->pszValue, "Value"))
68✔
2306
                    {
2307
                        oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
60✔
2308
                    }
2309
                }
2310
            }
2311
            else if (EQUAL(pszType, "boolean"))
6✔
2312
            {
2313
                oRet.push_back("NO");
1✔
2314
                oRet.push_back("YES");
1✔
2315
            }
2316
            else if (EQUAL(pszType, "int"))
5✔
2317
            {
2318
                if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
5✔
2319
                    atoi(pszMax) - atoi(pszMin) < 25)
2✔
2320
                {
2321
                    const int nMax = atoi(pszMax);
1✔
2322
                    for (int i = atoi(pszMin); i <= nMax; ++i)
13✔
2323
                        oRet.push_back(std::to_string(i));
12✔
2324
                }
2325
            }
2326

2327
            if (oRet.empty())
10✔
2328
            {
2329
                if (pszMin && pszMax)
4✔
2330
                {
2331
                    oRet.push_back(std::string("##"));
1✔
2332
                    oRet.push_back(std::string("validity range: [")
2✔
2333
                                       .append(pszMin)
1✔
2334
                                       .append(",")
1✔
2335
                                       .append(pszMax)
1✔
2336
                                       .append("]"));
1✔
2337
                }
2338
                else if (pszMin)
3✔
2339
                {
2340
                    oRet.push_back(std::string("##"));
1✔
2341
                    oRet.push_back(
1✔
2342
                        std::string("validity range: >= ").append(pszMin));
1✔
2343
                }
2344
                else if (pszMax)
2✔
2345
                {
2346
                    oRet.push_back(std::string("##"));
1✔
2347
                    oRet.push_back(
1✔
2348
                        std::string("validity range: <= ").append(pszMax));
1✔
2349
                }
2350
                else if (const char *pszDescription =
1✔
2351
                             CPLGetXMLValue(psChild, "description", nullptr))
1✔
2352
                {
2353
                    oRet.push_back(std::string("##"));
1✔
2354
                    oRet.push_back(std::string("type: ")
2✔
2355
                                       .append(pszType)
1✔
2356
                                       .append(", description: ")
1✔
2357
                                       .append(pszDescription));
1✔
2358
                }
2359
            }
2360

2361
            return true;
10✔
2362
        }
2363
    }
2364

2365
    for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
239✔
2366
         psChild = psChild->psNext)
228✔
2367
    {
2368
        const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
228✔
2369
        if (pszName && EQUAL(psChild->pszValue, "Option"))
228✔
2370
        {
2371
            const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
228✔
2372
            if (!pszScope ||
228✔
2373
                (EQUAL(pszScope, "raster") &&
40✔
2374
                 (datasetType & GDAL_OF_RASTER) != 0) ||
40✔
2375
                (EQUAL(pszScope, "vector") &&
20✔
2376
                 (datasetType & GDAL_OF_VECTOR) != 0))
×
2377
            {
2378
                oRet.push_back(std::string(pszName).append("="));
208✔
2379
            }
2380
        }
2381
    }
2382

2383
    return false;
11✔
2384
}
2385

2386
/************************************************************************/
2387
/*                 GDALAlgorithm::AddOpenOptionsArg()                   */
2388
/************************************************************************/
2389

2390
GDALInConstructionAlgorithmArg &
2391
GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue)
860✔
2392
{
2393
    auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0, _("Open options"), pValue)
1,720✔
2394
                    .AddAlias("oo")
1,720✔
2395
                    .SetMetaVar("KEY=VALUE")
1,720✔
2396
                    .SetCategory(GAAC_ADVANCED);
860✔
2397

2398
    arg.SetAutoCompleteFunction(
2399
        [this](const std::string &currentValue)
6✔
2400
        {
2401
            std::vector<std::string> oRet;
2✔
2402

2403
            int datasetType =
2✔
2404
                GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
2405
            auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
2✔
2406
            if (inputArg && inputArg->GetType() == GAAT_DATASET)
2✔
2407
            {
2408
                auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
2✔
2409
                datasetType = datasetValue.GetType();
2✔
2410
            }
2411

2412
            auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2✔
2413
            if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4✔
2414
                inputFormat->IsExplicitlySet())
2✔
2415
            {
2416
                const auto &aosAllowedDrivers =
2417
                    inputFormat->Get<std::vector<std::string>>();
1✔
2418
                if (aosAllowedDrivers.size() == 1)
1✔
2419
                {
2420
                    auto poDriver = GetGDALDriverManager()->GetDriverByName(
2✔
2421
                        aosAllowedDrivers[0].c_str());
1✔
2422
                    if (poDriver)
1✔
2423
                    {
2424
                        AddOptionsSuggestions(
1✔
2425
                            poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
1✔
2426
                            datasetType, currentValue, oRet);
2427
                    }
2428
                    return oRet;
1✔
2429
                }
2430
            }
2431

2432
            if (inputArg && inputArg->GetType() == GAAT_DATASET)
1✔
2433
            {
2434
                auto poDM = GetGDALDriverManager();
1✔
2435
                auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
1✔
2436
                const auto &osDSName = datasetValue.GetName();
1✔
2437
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
1✔
2438
                if (!osExt.empty())
1✔
2439
                {
2440
                    std::set<std::string> oVisitedExtensions;
1✔
2441
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
221✔
2442
                    {
2443
                        auto poDriver = poDM->GetDriver(i);
220✔
2444
                        if (((datasetType & GDAL_OF_RASTER) != 0 &&
660✔
2445
                             poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
220✔
2446
                            ((datasetType & GDAL_OF_VECTOR) != 0 &&
70✔
2447
                             poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
440✔
2448
                            ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
70✔
2449
                             poDriver->GetMetadataItem(
×
2450
                                 GDAL_DCAP_MULTIDIM_RASTER)))
×
2451
                        {
2452
                            const char *pszExtensions =
2453
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
150✔
2454
                            if (pszExtensions)
150✔
2455
                            {
2456
                                const CPLStringList aosExts(
2457
                                    CSLTokenizeString2(pszExtensions, " ", 0));
97✔
2458
                                for (const char *pszExt : cpl::Iterate(aosExts))
214✔
2459
                                {
2460
                                    if (EQUAL(pszExt, osExt.c_str()) &&
121✔
2461
                                        !cpl::contains(oVisitedExtensions,
3✔
2462
                                                       pszExt))
2463
                                    {
2464
                                        oVisitedExtensions.insert(pszExt);
1✔
2465
                                        if (AddOptionsSuggestions(
1✔
2466
                                                poDriver->GetMetadataItem(
2467
                                                    GDAL_DMD_OPENOPTIONLIST),
1✔
2468
                                                datasetType, currentValue,
2469
                                                oRet))
2470
                                        {
2471
                                            return oRet;
×
2472
                                        }
2473
                                        break;
1✔
2474
                                    }
2475
                                }
2476
                            }
2477
                        }
2478
                    }
2479
                }
2480
            }
2481

2482
            return oRet;
1✔
2483
        });
860✔
2484

2485
    return arg;
860✔
2486
}
2487

2488
/************************************************************************/
2489
/*                            ValidateFormat()                          */
2490
/************************************************************************/
2491

2492
bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
325✔
2493
                                   bool bStreamAllowed,
2494
                                   bool bGDALGAllowed) const
2495
{
2496
    if (arg.GetChoices().empty())
325✔
2497
    {
2498
        const auto Validate =
2499
            [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
643✔
2500
        {
2501
            if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
302✔
2502
                return true;
134✔
2503

2504
            if (bGDALGAllowed && EQUAL(val.c_str(), "GDALG"))
168✔
2505
                return true;
×
2506

2507
            auto hDriver = GDALGetDriverByName(val.c_str());
168✔
2508
            if (!hDriver)
168✔
2509
            {
2510
                ReportError(CE_Failure, CPLE_AppDefined,
2✔
2511
                            "Invalid value for argument '%s'. Driver '%s' does "
2512
                            "not exist",
2513
                            arg.GetName().c_str(), val.c_str());
1✔
2514
                return false;
1✔
2515
            }
2516

2517
            const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
167✔
2518
            if (caps)
167✔
2519
            {
2520
                for (const std::string &cap : *caps)
483✔
2521
                {
2522
                    if (!GDALGetMetadataItem(hDriver, cap.c_str(), nullptr))
322✔
2523
                    {
2524
                        if (cap == GDAL_DCAP_CREATECOPY &&
148✔
2525
                            std::find(caps->begin(), caps->end(),
×
2526
                                      GDAL_DCAP_RASTER) != caps->end() &&
73✔
2527
                            GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
73✔
2528
                                                nullptr) &&
148✔
2529
                            GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
73✔
2530
                                                nullptr))
2531
                        {
2532
                            // if it supports Create, it supports CreateCopy
2533
                        }
2534
                        else
2535
                        {
2536
                            ReportError(
4✔
2537
                                CE_Failure, CPLE_AppDefined,
2538
                                "Invalid value for argument '%s'. Driver '%s' "
2539
                                "does "
2540
                                "not expose the required '%s' capability.",
2541
                                arg.GetName().c_str(), val.c_str(),
2✔
2542
                                cap.c_str());
2543
                            return false;
2✔
2544
                        }
2545
                    }
2546
                }
2547
            }
2548
            return true;
165✔
2549
        };
302✔
2550

2551
        if (arg.GetType() == GAAT_STRING)
302✔
2552
        {
2553
            return Validate(arg.Get<std::string>());
300✔
2554
        }
2555
        else if (arg.GetType() == GAAT_STRING_LIST)
4✔
2556
        {
2557
            for (const auto &val : arg.Get<std::vector<std::string>>())
6✔
2558
            {
2559
                if (!Validate(val))
4✔
2560
                    return false;
2✔
2561
            }
2562
        }
2563
    }
2564

2565
    return true;
25✔
2566
}
2567

2568
/************************************************************************/
2569
/*                    FormatAutoCompleteFunction()                      */
2570
/************************************************************************/
2571

2572
static std::vector<std::string>
2573
FormatAutoCompleteFunction(const GDALAlgorithmArg &arg,
1✔
2574
                           bool /* bStreamAllowed */, bool bGDALGAllowed)
2575
{
2576
    std::vector<std::string> res;
1✔
2577
    auto poDM = GetGDALDriverManager();
1✔
2578
    for (int i = 0; i < poDM->GetDriverCount(); ++i)
221✔
2579
    {
2580
        auto poDriver = poDM->GetDriver(i);
220✔
2581

2582
        const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
220✔
2583
        if (caps)
220✔
2584
        {
2585
            bool ok = true;
220✔
2586
            for (const std::string &cap : *caps)
439✔
2587
            {
2588
                if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
370✔
2589
                {
2590
                    if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
×
2591
                        !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
×
2592
                    {
2593
                        ok = false;
×
2594
                        break;
×
2595
                    }
2596
                }
2597
                else if (poDriver->GetMetadataItem(cap.c_str()))
370✔
2598
                {
2599
                }
2600
                else if (cap == GDAL_DCAP_CREATECOPY &&
268✔
2601
                         (std::find(caps->begin(), caps->end(),
×
2602
                                    GDAL_DCAP_RASTER) != caps->end() &&
99✔
2603
                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
367✔
2604
                         poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
99✔
2605
                {
2606
                    // if it supports Create, it supports CreateCopy
2607
                }
2608
                else
2609
                {
2610
                    ok = false;
151✔
2611
                    break;
151✔
2612
                }
2613
            }
2614
            if (ok)
220✔
2615
            {
2616
                res.push_back(poDriver->GetDescription());
69✔
2617
            }
2618
        }
2619
    }
2620
    if (bGDALGAllowed)
1✔
2621
        res.push_back("GDALG");
×
2622
    return res;
1✔
2623
}
2624

2625
/************************************************************************/
2626
/*                 GDALAlgorithm::AddInputFormatsArg()                  */
2627
/************************************************************************/
2628

2629
GDALInConstructionAlgorithmArg &
2630
GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue)
847✔
2631
{
2632
    auto &arg =
2633
        AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0, _("Input formats"), pValue)
1,694✔
2634
            .AddAlias("if")
1,694✔
2635
            .SetCategory(GAAC_ADVANCED);
847✔
2636
    arg.AddValidationAction([this, &arg]()
4✔
2637
                            { return ValidateFormat(arg, false, false); });
851✔
2638
    arg.SetAutoCompleteFunction(
2639
        [&arg](const std::string &)
×
2640
        { return FormatAutoCompleteFunction(arg, false, false); });
847✔
2641
    return arg;
847✔
2642
}
2643

2644
/************************************************************************/
2645
/*                 GDALAlgorithm::AddOutputFormatArg()                  */
2646
/************************************************************************/
2647

2648
GDALInConstructionAlgorithmArg &
2649
GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
944✔
2650
                                  bool bGDALGAllowed)
2651
{
2652
    auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
2653
                       bGDALGAllowed ? _("Output format (\"GDALG\" allowed)")
2654
                                     : _("Output format"),
2655
                       pValue)
1,888✔
2656
                    .AddAlias("of")
1,888✔
2657
                    .AddAlias("format");
944✔
2658
    arg.AddValidationAction(
2659
        [this, &arg, bStreamAllowed, bGDALGAllowed]()
321✔
2660
        { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
1,265✔
2661
    arg.SetAutoCompleteFunction(
2662
        [&arg, bStreamAllowed, bGDALGAllowed](const std::string &) {
1✔
2663
            return FormatAutoCompleteFunction(arg, bStreamAllowed,
2664
                                              bGDALGAllowed);
1✔
2665
        });
944✔
2666
    return arg;
944✔
2667
}
2668

2669
/************************************************************************/
2670
/*                 GDALAlgorithm::AddOutputDataTypeArg()                */
2671
/************************************************************************/
2672
GDALInConstructionAlgorithmArg &
2673
GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue)
85✔
2674
{
2675
    auto &arg =
2676
        AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0, _("Output data type"), pValue)
170✔
2677
            .AddAlias("ot")
170✔
2678
            .AddAlias("datatype")
170✔
2679
            .SetChoices("Byte", "Int8", "UInt16", "Int16", "UInt32", "Int32",
2680
                        "UInt64", "Int64", "CInt16", "CInt32", "Float32",
2681
                        "Float64", "CFloat32", "CFloat64");
85✔
2682
    return arg;
85✔
2683
}
2684

2685
/************************************************************************/
2686
/*                 GDALAlgorithm::AddOutputStringArg()                  */
2687
/************************************************************************/
2688

2689
GDALInConstructionAlgorithmArg &
2690
GDALAlgorithm::AddOutputStringArg(std::string *pValue)
87✔
2691
{
2692
    return AddArg("output-string", 0,
2693
                  _("Output string, in which the result is placed"), pValue)
174✔
2694
        .SetHiddenForCLI()
87✔
2695
        .SetIsInput(false)
87✔
2696
        .SetIsOutput(true);
174✔
2697
}
2698

2699
/************************************************************************/
2700
/*                    GDALAlgorithm::AddLayerNameArg()                  */
2701
/************************************************************************/
2702

2703
GDALInConstructionAlgorithmArg &
2704
GDALAlgorithm::AddLayerNameArg(std::string *pValue)
19✔
2705
{
2706
    return AddArg("layer", 'l', _("Layer name"), pValue);
19✔
2707
}
2708

2709
/************************************************************************/
2710
/*                    GDALAlgorithm::AddLayerNameArg()                  */
2711
/************************************************************************/
2712

2713
GDALInConstructionAlgorithmArg &
2714
GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue)
36✔
2715
{
2716
    return AddArg("layer", 'l', _("Layer name"), pValue);
36✔
2717
}
2718

2719
/************************************************************************/
2720
/*                          ValidateKeyValue()                          */
2721
/************************************************************************/
2722

2723
bool GDALAlgorithm::ValidateKeyValue(const GDALAlgorithmArg &arg) const
22✔
2724
{
2725
    const auto Validate = [this, &arg](const std::string &val)
29✔
2726
    {
2727
        if (val.find('=') == std::string::npos)
27✔
2728
        {
2729
            ReportError(
2✔
2730
                CE_Failure, CPLE_AppDefined,
2731
                "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
2732
                arg.GetName().c_str());
2✔
2733
            return false;
2✔
2734
        }
2735

2736
        return true;
25✔
2737
    };
22✔
2738

2739
    if (arg.GetType() == GAAT_STRING)
22✔
2740
    {
2741
        return Validate(arg.Get<std::string>());
×
2742
    }
2743
    else if (arg.GetType() == GAAT_STRING_LIST)
22✔
2744
    {
2745
        for (const auto &val : arg.Get<std::vector<std::string>>())
47✔
2746
        {
2747
            if (!Validate(val))
27✔
2748
                return false;
2✔
2749
        }
2750
    }
2751

2752
    return true;
20✔
2753
}
2754

2755
/************************************************************************/
2756
/*                             IsGDALGOutput()                          */
2757
/************************************************************************/
2758

2759
bool GDALAlgorithm::IsGDALGOutput() const
270✔
2760
{
2761
    bool isGDALGOutput = false;
270✔
2762
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
270✔
2763
    const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
270✔
2764
    if (outputArg && outputArg->GetType() == GAAT_DATASET &&
540✔
2765
        outputArg->IsExplicitlySet())
270✔
2766
    {
2767
        if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
540✔
2768
            outputFormatArg->IsExplicitlySet())
270✔
2769
        {
2770
            const auto &val =
2771
                outputFormatArg->GDALAlgorithmArg::Get<std::string>();
148✔
2772
            isGDALGOutput = EQUAL(val.c_str(), "GDALG");
148✔
2773
        }
2774
        else
2775
        {
2776
            const auto &filename =
2777
                outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
122✔
2778
            isGDALGOutput =
122✔
2779
                filename.GetName().size() > strlen(".gdalg.json") &&
224✔
2780
                EQUAL(filename.GetName().c_str() + filename.GetName().size() -
102✔
2781
                          strlen(".gdalg.json"),
2782
                      ".gdalg.json");
2783
        }
2784
    }
2785
    return isGDALGOutput;
270✔
2786
}
2787

2788
/************************************************************************/
2789
/*                          ProcessGDALGOutput()                        */
2790
/************************************************************************/
2791

2792
GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
376✔
2793
{
2794
    if (!SupportsStreamedOutput())
376✔
2795
        return ProcessGDALGOutputRet::NOT_GDALG;
177✔
2796

2797
    if (IsGDALGOutput())
199✔
2798
    {
2799
        const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2✔
2800
        const auto &filename =
2801
            outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
2✔
2802
        VSIStatBufL sStat;
2803
        if (VSIStatL(filename.c_str(), &sStat) == 0)
2✔
2804
        {
2805
            const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
×
2806
            if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
×
2807
            {
2808
                if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
×
2809
                {
2810
                    CPLError(CE_Failure, CPLE_AppDefined,
×
2811
                             "File '%s' already exists. Specify the "
2812
                             "--overwrite option to overwrite it.",
2813
                             filename.c_str());
2814
                    return ProcessGDALGOutputRet::GDALG_ERROR;
×
2815
                }
2816
            }
2817
        }
2818

2819
        std::string osCommandLine;
4✔
2820

2821
        for (const auto &path : GDALAlgorithm::m_callPath)
8✔
2822
        {
2823
            if (!osCommandLine.empty())
6✔
2824
                osCommandLine += ' ';
4✔
2825
            osCommandLine += path;
6✔
2826
        }
2827

2828
        for (const auto &arg : GetArgs())
50✔
2829
        {
2830
            if (arg->IsExplicitlySet() &&
48✔
2831
                arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
6✔
2832
                arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
4✔
2833
                arg->GetName() != GDAL_ARG_NAME_UPDATE &&
58✔
2834
                arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
4✔
2835
            {
2836
                osCommandLine += ' ';
3✔
2837
                std::string strArg;
3✔
2838
                if (!arg->Serialize(strArg))
3✔
2839
                {
2840
                    CPLError(CE_Failure, CPLE_AppDefined,
×
2841
                             "Cannot serialize argument %s",
2842
                             arg->GetName().c_str());
×
2843
                    return ProcessGDALGOutputRet::GDALG_ERROR;
×
2844
                }
2845
                osCommandLine += strArg;
3✔
2846
            }
2847
        }
2848

2849
        osCommandLine += " --output-format stream --output streamed_dataset";
2✔
2850

2851
        CPLJSONDocument oDoc;
2✔
2852
        oDoc.GetRoot().Add("type", "gdal_streamed_alg");
2✔
2853
        oDoc.GetRoot().Add("command_line", osCommandLine);
2✔
2854

2855
        return oDoc.Save(filename) ? ProcessGDALGOutputRet::GDALG_OK
2✔
2856
                                   : ProcessGDALGOutputRet::GDALG_ERROR;
2✔
2857
    }
2858

2859
    return ProcessGDALGOutputRet::NOT_GDALG;
197✔
2860
}
2861

2862
/************************************************************************/
2863
/*                 GDALAlgorithm::AddCreationOptionsArg()               */
2864
/************************************************************************/
2865

2866
GDALInConstructionAlgorithmArg &
2867
GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue)
823✔
2868
{
2869
    auto &arg = AddArg("creation-option", 0, _("Creation option"), pValue)
1,646✔
2870
                    .AddAlias("co")
1,646✔
2871
                    .SetMetaVar("<KEY>=<VALUE>");
823✔
2872
    arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
834✔
2873

2874
    arg.SetAutoCompleteFunction(
2875
        [this](const std::string &currentValue)
45✔
2876
        {
2877
            std::vector<std::string> oRet;
15✔
2878

2879
            int datasetType =
15✔
2880
                GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
2881
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
15✔
2882
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
15✔
2883
            {
2884
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
15✔
2885
                datasetType = datasetValue.GetType();
15✔
2886
            }
2887

2888
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
15✔
2889
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
30✔
2890
                outputFormat->IsExplicitlySet())
15✔
2891
            {
2892
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
12✔
2893
                    outputFormat->Get<std::string>().c_str());
6✔
2894
                if (poDriver)
6✔
2895
                {
2896
                    AddOptionsSuggestions(
6✔
2897
                        poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
6✔
2898
                        datasetType, currentValue, oRet);
2899
                }
2900
                return oRet;
6✔
2901
            }
2902

2903
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
9✔
2904
            {
2905
                auto poDM = GetGDALDriverManager();
9✔
2906
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
9✔
2907
                const auto &osDSName = datasetValue.GetName();
9✔
2908
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
9✔
2909
                if (!osExt.empty())
9✔
2910
                {
2911
                    std::set<std::string> oVisitedExtensions;
9✔
2912
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
477✔
2913
                    {
2914
                        auto poDriver = poDM->GetDriver(i);
475✔
2915
                        if (((datasetType & GDAL_OF_RASTER) != 0 &&
1,425✔
2916
                             poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
475✔
2917
                            ((datasetType & GDAL_OF_VECTOR) != 0 &&
140✔
2918
                             poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
950✔
2919
                            ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
140✔
2920
                             poDriver->GetMetadataItem(
×
2921
                                 GDAL_DCAP_MULTIDIM_RASTER)))
×
2922
                        {
2923
                            const char *pszExtensions =
2924
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
335✔
2925
                            if (pszExtensions)
335✔
2926
                            {
2927
                                const CPLStringList aosExts(
2928
                                    CSLTokenizeString2(pszExtensions, " ", 0));
215✔
2929
                                for (const char *pszExt : cpl::Iterate(aosExts))
477✔
2930
                                {
2931
                                    if (EQUAL(pszExt, osExt.c_str()) &&
284✔
2932
                                        !cpl::contains(oVisitedExtensions,
13✔
2933
                                                       pszExt))
2934
                                    {
2935
                                        oVisitedExtensions.insert(pszExt);
9✔
2936
                                        if (AddOptionsSuggestions(
9✔
2937
                                                poDriver->GetMetadataItem(
2938
                                                    GDAL_DMD_CREATIONOPTIONLIST),
9✔
2939
                                                datasetType, currentValue,
2940
                                                oRet))
2941
                                        {
2942
                                            return oRet;
7✔
2943
                                        }
2944
                                        break;
2✔
2945
                                    }
2946
                                }
2947
                            }
2948
                        }
2949
                    }
2950
                }
2951
            }
2952

2953
            return oRet;
2✔
2954
        });
823✔
2955

2956
    return arg;
823✔
2957
}
2958

2959
/************************************************************************/
2960
/*                GDALAlgorithm::AddLayerCreationOptionsArg()           */
2961
/************************************************************************/
2962

2963
GDALInConstructionAlgorithmArg &
2964
GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue)
411✔
2965
{
2966
    auto &arg =
2967
        AddArg("layer-creation-option", 0, _("Layer creation option"), pValue)
822✔
2968
            .AddAlias("lco")
822✔
2969
            .SetMetaVar("<KEY>=<VALUE>");
411✔
2970
    arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
415✔
2971

2972
    arg.SetAutoCompleteFunction(
2973
        [this](const std::string &currentValue)
5✔
2974
        {
2975
            std::vector<std::string> oRet;
2✔
2976

2977
            auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2✔
2978
            if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
4✔
2979
                outputFormat->IsExplicitlySet())
2✔
2980
            {
2981
                auto poDriver = GetGDALDriverManager()->GetDriverByName(
2✔
2982
                    outputFormat->Get<std::string>().c_str());
1✔
2983
                if (poDriver)
1✔
2984
                {
2985
                    AddOptionsSuggestions(poDriver->GetMetadataItem(
1✔
2986
                                              GDAL_DS_LAYER_CREATIONOPTIONLIST),
1✔
2987
                                          GDAL_OF_VECTOR, currentValue, oRet);
2988
                }
2989
                return oRet;
1✔
2990
            }
2991

2992
            auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
1✔
2993
            if (outputArg && outputArg->GetType() == GAAT_DATASET)
1✔
2994
            {
2995
                auto poDM = GetGDALDriverManager();
1✔
2996
                auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
1✔
2997
                const auto &osDSName = datasetValue.GetName();
1✔
2998
                const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
1✔
2999
                if (!osExt.empty())
1✔
3000
                {
3001
                    std::set<std::string> oVisitedExtensions;
1✔
3002
                    for (int i = 0; i < poDM->GetDriverCount(); ++i)
221✔
3003
                    {
3004
                        auto poDriver = poDM->GetDriver(i);
220✔
3005
                        if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
220✔
3006
                        {
3007
                            const char *pszExtensions =
3008
                                poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
88✔
3009
                            if (pszExtensions)
88✔
3010
                            {
3011
                                const CPLStringList aosExts(
3012
                                    CSLTokenizeString2(pszExtensions, " ", 0));
61✔
3013
                                for (const char *pszExt : cpl::Iterate(aosExts))
154✔
3014
                                {
3015
                                    if (EQUAL(pszExt, osExt.c_str()) &&
95✔
3016
                                        !cpl::contains(oVisitedExtensions,
1✔
3017
                                                       pszExt))
3018
                                    {
3019
                                        oVisitedExtensions.insert(pszExt);
1✔
3020
                                        if (AddOptionsSuggestions(
1✔
3021
                                                poDriver->GetMetadataItem(
3022
                                                    GDAL_DS_LAYER_CREATIONOPTIONLIST),
1✔
3023
                                                GDAL_OF_VECTOR, currentValue,
3024
                                                oRet))
3025
                                        {
3026
                                            return oRet;
×
3027
                                        }
3028
                                        break;
1✔
3029
                                    }
3030
                                }
3031
                            }
3032
                        }
3033
                    }
3034
                }
3035
            }
3036

3037
            return oRet;
1✔
3038
        });
411✔
3039

3040
    return arg;
411✔
3041
}
3042

3043
/************************************************************************/
3044
/*                        GDALAlgorithm::AddBBOXArg()                   */
3045
/************************************************************************/
3046

3047
/** Add bbox=xmin,ymin,xmax,ymax argument. */
3048
GDALInConstructionAlgorithmArg &
3049
GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
177✔
3050
{
3051
    auto &arg = AddArg("bbox", 0,
3052
                       helpMessage ? helpMessage
3053
                                   : _("Bounding box as xmin,ymin,xmax,ymax"),
3054
                       pValue)
354✔
3055
                    .SetRepeatedArgAllowed(false)
177✔
3056
                    .SetMinCount(4)
177✔
3057
                    .SetMaxCount(4)
177✔
3058
                    .SetDisplayHintAboutRepetition(false);
177✔
3059
    arg.AddValidationAction(
3060
        [&arg]()
27✔
3061
        {
3062
            const auto &val = arg.Get<std::vector<double>>();
27✔
3063
            CPLAssert(val.size() == 4);
27✔
3064
            if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
27✔
3065
            {
3066
                CPLError(CE_Failure, CPLE_AppDefined,
4✔
3067
                         "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
3068
                         "xmin <= xmax and ymin <= ymax");
3069
                return false;
4✔
3070
            }
3071
            return true;
23✔
3072
        });
177✔
3073
    return arg;
177✔
3074
}
3075

3076
/************************************************************************/
3077
/*                  GDALAlgorithm::AddActiveLayerArg()                  */
3078
/************************************************************************/
3079

3080
GDALInConstructionAlgorithmArg &
3081
GDALAlgorithm::AddActiveLayerArg(std::string *pValue)
201✔
3082
{
3083
    return AddArg("active-layer", 0,
3084
                  _("Set active layer (if not specified, all)"), pValue);
201✔
3085
}
3086

3087
/************************************************************************/
3088
/*                  GDALAlgorithm::AddProgressArg()                     */
3089
/************************************************************************/
3090

3091
GDALInConstructionAlgorithmArg &GDALAlgorithm::AddProgressArg()
599✔
3092
{
3093
    return AddArg("progress", 0, _("Display progress bar"),
3094
                  &m_progressBarRequested)
1,198✔
3095
        .SetOnlyForCLI()
599✔
3096
        .SetCategory(GAAC_COMMON);
1,198✔
3097
}
3098

3099
/************************************************************************/
3100
/*                       GDALAlgorithm::Run()                           */
3101
/************************************************************************/
3102

3103
bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
941✔
3104
{
3105
    if (m_selectedSubAlg)
941✔
3106
        return m_selectedSubAlg->Run(pfnProgress, pProgressData);
82✔
3107

3108
    if (m_helpRequested || m_helpDocRequested)
859✔
3109
    {
3110
        printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
7✔
3111
        return true;
7✔
3112
    }
3113

3114
    if (m_JSONUsageRequested)
852✔
3115
    {
3116
        printf("%s", GetUsageAsJSON().c_str()); /*ok*/
3✔
3117
        return true;
3✔
3118
    }
3119

3120
    if (!ValidateArguments())
849✔
3121
        return false;
4✔
3122

3123
    switch (ProcessGDALGOutput())
845✔
3124
    {
3125
        case ProcessGDALGOutputRet::GDALG_ERROR:
×
3126
            return false;
×
3127

3128
        case ProcessGDALGOutputRet::GDALG_OK:
2✔
3129
            return true;
2✔
3130

3131
        case ProcessGDALGOutputRet::NOT_GDALG:
843✔
3132
            break;
843✔
3133
    }
3134

3135
    if (m_executionForStreamOutput)
843✔
3136
    {
3137
        if (!CheckSafeForStreamOutput())
18✔
3138
        {
3139
            return false;
4✔
3140
        }
3141
    }
3142

3143
    return RunImpl(pfnProgress, pProgressData);
839✔
3144
}
3145

3146
/************************************************************************/
3147
/*              GDALAlgorithm::CheckSafeForStreamOutput()               */
3148
/************************************************************************/
3149

3150
bool GDALAlgorithm::CheckSafeForStreamOutput()
10✔
3151
{
3152
    const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
10✔
3153
    if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
10✔
3154
    {
3155
        const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
10✔
3156
        if (!EQUAL(val.c_str(), "stream"))
10✔
3157
        {
3158
            // For security reasons, to avoid that reading a .gdalg.json file
3159
            // writes a file on the file system.
3160
            ReportError(
4✔
3161
                CE_Failure, CPLE_NotSupported,
3162
                "in streamed execution, --format stream should be used");
3163
            return false;
4✔
3164
        }
3165
    }
3166
    return true;
6✔
3167
}
3168

3169
/************************************************************************/
3170
/*                     GDALAlgorithm::Finalize()                        */
3171
/************************************************************************/
3172

3173
bool GDALAlgorithm::Finalize()
347✔
3174
{
3175
    bool ret = true;
347✔
3176
    if (m_selectedSubAlg)
347✔
3177
        ret = m_selectedSubAlg->Finalize();
41✔
3178

3179
    for (auto &arg : m_args)
5,965✔
3180
    {
3181
        if (arg->GetType() == GAAT_DATASET)
5,618✔
3182
        {
3183
            ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
440✔
3184
        }
3185
        else if (arg->GetType() == GAAT_DATASET_LIST)
5,178✔
3186
        {
3187
            for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
14✔
3188
            {
3189
                ret = ds.Close() && ret;
7✔
3190
            }
3191
        }
3192
    }
3193
    return ret;
347✔
3194
}
3195

3196
/************************************************************************/
3197
/*                   GDALAlgorithm::GetArgNamesForCLI()                 */
3198
/************************************************************************/
3199

3200
std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
3201
GDALAlgorithm::GetArgNamesForCLI() const
86✔
3202
{
3203
    std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
172✔
3204

3205
    size_t maxOptLen = 0;
86✔
3206
    for (const auto &arg : m_args)
898✔
3207
    {
3208
        if (arg->IsHidden() || arg->IsHiddenForCLI())
812✔
3209
            continue;
133✔
3210
        std::string opt;
679✔
3211
        bool addComma = false;
679✔
3212
        if (!arg->GetShortName().empty())
679✔
3213
        {
3214
            opt += '-';
145✔
3215
            opt += arg->GetShortName();
145✔
3216
            addComma = true;
145✔
3217
        }
3218
        for (const std::string &alias : arg->GetAliases())
732✔
3219
        {
3220
            if (addComma)
53✔
3221
                opt += ", ";
21✔
3222
            opt += "--";
53✔
3223
            opt += alias;
53✔
3224
            addComma = true;
53✔
3225
        }
3226
        if (!arg->GetName().empty())
679✔
3227
        {
3228
            if (addComma)
679✔
3229
                opt += ", ";
177✔
3230
            opt += "--";
679✔
3231
            opt += arg->GetName();
679✔
3232
        }
3233
        const auto &metaVar = arg->GetMetaVar();
679✔
3234
        if (!metaVar.empty())
679✔
3235
        {
3236
            opt += ' ';
288✔
3237
            if (metaVar.front() != '<')
288✔
3238
                opt += '<';
162✔
3239
            opt += metaVar;
288✔
3240
            if (metaVar.back() != '>')
288✔
3241
                opt += '>';
162✔
3242
        }
3243
        maxOptLen = std::max(maxOptLen, opt.size());
679✔
3244
        options.emplace_back(arg.get(), opt);
679✔
3245
    }
3246

3247
    return std::make_pair(std::move(options), maxOptLen);
172✔
3248
}
3249

3250
/************************************************************************/
3251
/*                    GDALAlgorithm::GetUsageForCLI()                   */
3252
/************************************************************************/
3253

3254
std::string
3255
GDALAlgorithm::GetUsageForCLI(bool shortUsage,
81✔
3256
                              const UsageOptions &usageOptions) const
3257
{
3258
    if (m_selectedSubAlg)
81✔
3259
        return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6✔
3260

3261
    std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
150✔
3262
    std::string osPath;
150✔
3263
    for (const std::string &s : m_callPath)
135✔
3264
    {
3265
        if (!osPath.empty())
60✔
3266
            osPath += ' ';
22✔
3267
        osPath += s;
60✔
3268
    }
3269
    osRet += ' ';
75✔
3270
    osRet += osPath;
75✔
3271

3272
    bool hasNonPositionals = false;
75✔
3273
    for (const auto &arg : m_args)
806✔
3274
    {
3275
        if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
731✔
3276
            hasNonPositionals = true;
533✔
3277
    }
3278

3279
    if (HasSubAlgorithms())
75✔
3280
    {
3281
        if (m_callPath.size() == 1)
5✔
3282
        {
3283
            osRet += " <COMMAND>";
4✔
3284
            if (hasNonPositionals)
4✔
3285
                osRet += " [OPTIONS]";
4✔
3286
            osRet += "\nwhere <COMMAND> is one of:\n";
4✔
3287
        }
3288
        else
3289
        {
3290
            osRet += " <SUBCOMMAND>";
1✔
3291
            if (hasNonPositionals)
1✔
3292
                osRet += " [OPTIONS]";
1✔
3293
            osRet += "\nwhere <SUBCOMMAND> is one of:\n";
1✔
3294
        }
3295
        size_t maxNameLen = 0;
5✔
3296
        for (const auto &subAlgName : GetSubAlgorithmNames())
31✔
3297
        {
3298
            maxNameLen = std::max(maxNameLen, subAlgName.size());
26✔
3299
        }
3300
        for (const auto &subAlgName : GetSubAlgorithmNames())
31✔
3301
        {
3302
            auto subAlg = InstantiateSubAlgorithm(subAlgName);
52✔
3303
            assert(subAlg);
26✔
3304
            const std::string &name(subAlg->GetName());
26✔
3305
            osRet += "  - ";
26✔
3306
            osRet += name;
26✔
3307
            osRet += ": ";
26✔
3308
            osRet.append(maxNameLen - name.size(), ' ');
26✔
3309
            osRet += subAlg->GetDescription();
26✔
3310
            if (!subAlg->m_aliases.empty())
26✔
3311
            {
3312
                bool first = true;
×
3313
                for (const auto &alias : subAlg->GetAliases())
×
3314
                {
3315
                    if (alias == GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
×
3316
                        break;
×
3317
                    if (first)
×
3318
                        osRet += " (alias: ";
×
3319
                    else
3320
                        osRet += ", ";
×
3321
                    osRet += alias;
×
3322
                    first = false;
×
3323
                }
3324
                if (!first)
×
3325
                {
3326
                    osRet += ')';
×
3327
                }
3328
            }
3329
            osRet += '\n';
26✔
3330
        }
3331

3332
        if (shortUsage && hasNonPositionals)
5✔
3333
        {
3334
            osRet += "\nTry '";
2✔
3335
            osRet += osPath;
2✔
3336
            osRet += " --help' for help.\n";
2✔
3337
        }
3338
    }
3339
    else
3340
    {
3341
        if (!m_args.empty())
70✔
3342
        {
3343
            if (hasNonPositionals)
70✔
3344
                osRet += " [OPTIONS]";
70✔
3345
            for (const auto *arg : m_positionalArgs)
108✔
3346
            {
3347
                const std::string &metavar = arg->GetMetaVar();
38✔
3348
                if (!metavar.empty() && metavar[0] == '<')
38✔
3349
                {
3350
                    osRet += metavar;
1✔
3351
                }
3352
                else
3353
                {
3354
                    osRet += " <";
37✔
3355
                    osRet += metavar;
37✔
3356
                    osRet += '>';
37✔
3357
                }
3358
            }
3359
        }
3360

3361
        const size_t nLenFirstLine = osRet.size();
70✔
3362
        osRet += '\n';
70✔
3363
        if (usageOptions.isPipelineStep)
70✔
3364
        {
3365
            osRet.append(nLenFirstLine, '-');
20✔
3366
            osRet += '\n';
20✔
3367
        }
3368

3369
        if (shortUsage)
70✔
3370
        {
3371
            osRet += "Try '";
6✔
3372
            osRet += osPath;
6✔
3373
            osRet += " --help' for help.\n";
6✔
3374
            return osRet;
6✔
3375
        }
3376

3377
        osRet += '\n';
64✔
3378
        osRet += m_description;
64✔
3379
        osRet += '\n';
64✔
3380
    }
3381

3382
    if (!m_args.empty() && !shortUsage)
69✔
3383
    {
3384
        std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
134✔
3385
        size_t maxOptLen;
3386
        std::tie(options, maxOptLen) = GetArgNamesForCLI();
67✔
3387
        if (usageOptions.maxOptLen)
67✔
3388
            maxOptLen = usageOptions.maxOptLen;
19✔
3389

3390
        const auto OutputArg =
3391
            [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
395✔
3392
                                      const std::string &opt)
2,776✔
3393
        {
3394
            osRet += "  ";
395✔
3395
            osRet += opt;
395✔
3396
            osRet += "  ";
395✔
3397
            osRet.append(maxOptLen - opt.size(), ' ');
395✔
3398
            osRet += arg->GetDescription();
395✔
3399

3400
            const auto &choices = arg->GetChoices();
395✔
3401
            if (!choices.empty())
395✔
3402
            {
3403
                osRet += ". ";
9✔
3404
                osRet += arg->GetMetaVar();
9✔
3405
                osRet += '=';
9✔
3406
                bool firstChoice = true;
9✔
3407
                for (const auto &choice : choices)
80✔
3408
                {
3409
                    if (!firstChoice)
71✔
3410
                        osRet += '|';
62✔
3411
                    osRet += choice;
71✔
3412
                    firstChoice = false;
71✔
3413
                }
3414
            }
3415

3416
            if (arg->GetType() == GAAT_DATASET)
395✔
3417
            {
3418
                auto &val = arg->Get<GDALArgDatasetValue>();
9✔
3419
                if (val.GetInputFlags() == GADV_NAME &&
10✔
3420
                    val.GetOutputFlags() == GADV_OBJECT)
1✔
3421
                {
3422
                    osRet += " (created by algorithm)";
1✔
3423
                }
3424
            }
3425

3426
            if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
395✔
3427
            {
3428
                osRet += " (default: ";
5✔
3429
                osRet += arg->GetDefault<std::string>();
5✔
3430
                osRet += ')';
5✔
3431
            }
3432
            else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
390✔
3433
            {
3434
                if (arg->GetDefault<bool>())
5✔
3435
                    osRet += " (default: true)";
×
3436
            }
3437
            else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
385✔
3438
            {
3439
                osRet += " (default: ";
1✔
3440
                osRet += CPLSPrintf("%d", arg->GetDefault<int>());
1✔
3441
                osRet += ')';
1✔
3442
            }
3443
            else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
384✔
3444
            {
3445
                osRet += " (default: ";
1✔
3446
                osRet += CPLSPrintf("%g", arg->GetDefault<double>());
1✔
3447
                osRet += ')';
1✔
3448
            }
3449

3450
            if (arg->GetDisplayHintAboutRepetition())
395✔
3451
            {
3452
                if (arg->GetMinCount() > 0 &&
394✔
3453
                    arg->GetMinCount() == arg->GetMaxCount())
9✔
3454
                {
3455
                    osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
2✔
3456
                }
3457
                else if (arg->GetMinCount() > 0 &&
390✔
3458
                         arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
7✔
3459
                {
3460
                    osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
3461
                                        arg->GetMaxCount());
4✔
3462
                }
3463
                else if (arg->GetMinCount() > 0)
379✔
3464
                {
3465
                    osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
3✔
3466
                }
3467
                else if (arg->GetMaxCount() > 1)
376✔
3468
                {
3469
                    osRet += " [may be repeated]";
83✔
3470
                }
3471
            }
3472

3473
            if (arg->IsRequired())
395✔
3474
            {
3475
                osRet += " [required]";
19✔
3476
            }
3477

3478
            osRet += '\n';
395✔
3479

3480
            const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
395✔
3481
            if (!mutualExclusionGroup.empty())
395✔
3482
            {
3483
                std::string otherArgs;
34✔
3484
                for (const auto &otherArg : m_args)
257✔
3485
                {
3486
                    if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
459✔
3487
                        otherArg.get() == arg)
219✔
3488
                        continue;
38✔
3489
                    if (otherArg->GetMutualExclusionGroup() ==
202✔
3490
                        mutualExclusionGroup)
3491
                    {
3492
                        if (!otherArgs.empty())
22✔
3493
                            otherArgs += ", ";
6✔
3494
                        otherArgs += "--";
22✔
3495
                        otherArgs += otherArg->GetName();
22✔
3496
                    }
3497
                }
3498
                if (!otherArgs.empty())
17✔
3499
                {
3500
                    osRet += "  ";
16✔
3501
                    osRet += "  ";
16✔
3502
                    osRet.append(maxOptLen, ' ');
16✔
3503
                    osRet += "Mutually exclusive with ";
16✔
3504
                    osRet += otherArgs;
16✔
3505
                    osRet += '\n';
16✔
3506
                }
3507
            }
3508
        };
395✔
3509

3510
        if (!m_positionalArgs.empty())
67✔
3511
        {
3512
            osRet += "\nPositional arguments:\n";
24✔
3513
            for (const auto &[arg, opt] : options)
217✔
3514
            {
3515
                if (arg->IsPositional())
193✔
3516
                    OutputArg(arg, opt);
29✔
3517
            }
3518
        }
3519

3520
        if (hasNonPositionals)
67✔
3521
        {
3522
            bool hasCommon = false;
67✔
3523
            bool hasBase = false;
67✔
3524
            bool hasAdvanced = false;
67✔
3525
            bool hasEsoteric = false;
67✔
3526
            std::vector<std::string> categories;
134✔
3527
            for (const auto &iter : options)
567✔
3528
            {
3529
                const auto &arg = iter.first;
500✔
3530
                if (!arg->IsPositional())
500✔
3531
                {
3532
                    const auto &category = arg->GetCategory();
471✔
3533
                    if (category == GAAC_COMMON)
471✔
3534
                    {
3535
                        hasCommon = true;
343✔
3536
                    }
3537
                    else if (category == GAAC_BASE)
128✔
3538
                    {
3539
                        hasBase = true;
103✔
3540
                    }
3541
                    else if (category == GAAC_ADVANCED)
25✔
3542
                    {
3543
                        hasAdvanced = true;
21✔
3544
                    }
3545
                    else if (category == GAAC_ESOTERIC)
4✔
3546
                    {
3547
                        hasEsoteric = true;
3✔
3548
                    }
3549
                    else if (std::find(categories.begin(), categories.end(),
1✔
3550
                                       category) == categories.end())
1✔
3551
                    {
3552
                        categories.push_back(category);
1✔
3553
                    }
3554
                }
3555
            }
3556
            if (hasAdvanced)
67✔
3557
                categories.insert(categories.begin(), GAAC_ADVANCED);
5✔
3558
            if (hasBase)
67✔
3559
                categories.insert(categories.begin(), GAAC_BASE);
39✔
3560
            if (hasCommon && !usageOptions.isPipelineStep)
67✔
3561
                categories.insert(categories.begin(), GAAC_COMMON);
46✔
3562
            if (hasEsoteric)
67✔
3563
                categories.push_back(GAAC_ESOTERIC);
1✔
3564

3565
            for (const auto &category : categories)
159✔
3566
            {
3567
                osRet += "\n";
92✔
3568
                if (category != GAAC_BASE)
92✔
3569
                {
3570
                    osRet += category;
53✔
3571
                    osRet += ' ';
53✔
3572
                }
3573
                osRet += "Options:\n";
92✔
3574
                for (const auto &[arg, opt] : options)
813✔
3575
                {
3576
                    if (!arg->IsPositional() && arg->GetCategory() == category)
721✔
3577
                        OutputArg(arg, opt);
366✔
3578
                }
3579
            }
3580
        }
3581
    }
3582

3583
    if (!m_longDescription.empty())
69✔
3584
    {
3585
        osRet += '\n';
5✔
3586
        osRet += m_longDescription;
5✔
3587
        osRet += '\n';
5✔
3588
    }
3589

3590
    if (!m_helpDocRequested)
69✔
3591
    {
3592
        if (!m_helpURL.empty())
65✔
3593
        {
3594
            osRet += "\nFor more details, consult ";
65✔
3595
            osRet += GetHelpFullURL();
65✔
3596
            osRet += '\n';
65✔
3597
        }
3598

3599
        if (!m_callPath.empty() && m_callPath[0] == "gdal")
65✔
3600
        {
3601
            osRet +=
3602
                "\nWARNING: the gdal command is provisionally provided as an "
3603
                "alternative interface to GDAL and OGR command line "
3604
                "utilities.\nThe project reserves the right to modify, "
3605
                "rename, reorganize, and change the behavior of the utility\n"
3606
                "until it is officially frozen in a future feature release of "
3607
                "GDAL.\n";
7✔
3608
        }
3609
    }
3610

3611
    return osRet;
69✔
3612
}
3613

3614
/************************************************************************/
3615
/*                    GDALAlgorithm::GetUsageAsJSON()                   */
3616
/************************************************************************/
3617

3618
std::string GDALAlgorithm::GetUsageAsJSON() const
160✔
3619
{
3620
    CPLJSONDocument oDoc;
320✔
3621
    auto oRoot = oDoc.GetRoot();
320✔
3622

3623
    if (m_displayInJSONUsage)
160✔
3624
    {
3625
        oRoot.Add("name", m_name);
158✔
3626
        CPLJSONArray jFullPath;
158✔
3627
        for (const std::string &s : m_callPath)
381✔
3628
        {
3629
            jFullPath.Add(s);
223✔
3630
        }
3631
        oRoot.Add("full_path", jFullPath);
158✔
3632
    }
3633

3634
    oRoot.Add("description", m_description);
160✔
3635
    if (!m_helpURL.empty())
160✔
3636
    {
3637
        oRoot.Add("short_url", m_helpURL);
160✔
3638
        oRoot.Add("url", GetHelpFullURL());
160✔
3639
    }
3640

3641
    CPLJSONArray jSubAlgorithms;
320✔
3642
    for (const auto &subAlgName : GetSubAlgorithmNames())
243✔
3643
    {
3644
        auto subAlg = InstantiateSubAlgorithm(subAlgName);
166✔
3645
        assert(subAlg);
83✔
3646
        if (subAlg->m_displayInJSONUsage)
83✔
3647
        {
3648
            CPLJSONDocument oSubDoc;
80✔
3649
            CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
80✔
3650
            jSubAlgorithms.Add(oSubDoc.GetRoot());
80✔
3651
        }
3652
    }
3653
    oRoot.Add("sub_algorithms", jSubAlgorithms);
160✔
3654

3655
    const auto ProcessArg = [](const GDALAlgorithmArg *arg)
1,171✔
3656
    {
3657
        CPLJSONObject jArg;
1,171✔
3658
        jArg.Add("name", arg->GetName());
1,171✔
3659
        jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
1,171✔
3660
        jArg.Add("description", arg->GetDescription());
1,171✔
3661
        const auto &choices = arg->GetChoices();
1,171✔
3662
        if (!choices.empty())
1,171✔
3663
        {
3664
            CPLJSONArray jChoices;
53✔
3665
            for (const auto &choice : choices)
518✔
3666
                jChoices.Add(choice);
465✔
3667
            jArg.Add("choices", jChoices);
53✔
3668
        }
3669
        if (arg->HasDefaultValue())
1,171✔
3670
        {
3671
            switch (arg->GetType())
171✔
3672
            {
3673
                case GAAT_BOOLEAN:
122✔
3674
                    jArg.Add("default", arg->GetDefault<bool>());
122✔
3675
                    break;
122✔
3676
                case GAAT_STRING:
37✔
3677
                    jArg.Add("default", arg->GetDefault<std::string>());
37✔
3678
                    break;
37✔
3679
                case GAAT_INTEGER:
7✔
3680
                    jArg.Add("default", arg->GetDefault<int>());
7✔
3681
                    break;
7✔
3682
                case GAAT_REAL:
5✔
3683
                    jArg.Add("default", arg->GetDefault<double>());
5✔
3684
                    break;
5✔
3685
                case GAAT_DATASET:
×
3686
                case GAAT_STRING_LIST:
3687
                case GAAT_INTEGER_LIST:
3688
                case GAAT_REAL_LIST:
3689
                case GAAT_DATASET_LIST:
3690
                    CPLError(CE_Warning, CPLE_AppDefined,
×
3691
                             "Unhandled default value for arg %s",
3692
                             arg->GetName().c_str());
×
3693
                    break;
×
3694
            }
3695
        }
3696
        jArg.Add("required", arg->IsRequired());
1,171✔
3697
        if (GDALAlgorithmArgTypeIsList(arg->GetType()))
1,171✔
3698
        {
3699
            jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
363✔
3700
            jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
363✔
3701
            jArg.Add("min_count", arg->GetMinCount());
363✔
3702
            jArg.Add("max_count", arg->GetMaxCount());
363✔
3703
        }
3704
        jArg.Add("category", arg->GetCategory());
1,171✔
3705

3706
        if (arg->GetType() == GAAT_DATASET)
1,171✔
3707
        {
3708
            const auto &val = arg->Get<GDALArgDatasetValue>();
126✔
3709
            {
3710
                CPLJSONArray jAr;
126✔
3711
                if (val.GetType() & GDAL_OF_RASTER)
126✔
3712
                    jAr.Add("raster");
82✔
3713
                if (val.GetType() & GDAL_OF_VECTOR)
126✔
3714
                    jAr.Add("vector");
48✔
3715
                if (val.GetType() & GDAL_OF_MULTIDIM_RASTER)
126✔
3716
                    jAr.Add("multidim_raster");
5✔
3717
                jArg.Add("dataset_type", jAr);
126✔
3718
            }
3719

3720
            const auto GetFlags = [](int flags)
181✔
3721
            {
3722
                CPLJSONArray jAr;
181✔
3723
                if (flags & GADV_NAME)
181✔
3724
                    jAr.Add("name");
126✔
3725
                if (flags & GADV_OBJECT)
181✔
3726
                    jAr.Add("dataset");
169✔
3727
                return jAr;
181✔
3728
            };
3729

3730
            if (arg->IsInput())
126✔
3731
            {
3732
                jArg.Add("input_flags", GetFlags(val.GetInputFlags()));
126✔
3733
            }
3734
            if (arg->IsOutput())
126✔
3735
            {
3736
                jArg.Add("output_flags", GetFlags(val.GetOutputFlags()));
55✔
3737
            }
3738
        }
3739

3740
        const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
1,171✔
3741
        if (!mutualExclusionGroup.empty())
1,171✔
3742
        {
3743
            jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
124✔
3744
        }
3745

3746
        const auto &metadata = arg->GetMetadata();
2,342✔
3747
        if (!metadata.empty())
1,171✔
3748
        {
3749
            CPLJSONObject jMetadata;
102✔
3750
            for (const auto &[key, values] : metadata)
204✔
3751
            {
3752
                CPLJSONArray jValue;
204✔
3753
                for (const auto &value : values)
252✔
3754
                    jValue.Add(value);
150✔
3755
                jMetadata.Add(key, jValue);
102✔
3756
            }
3757
            jArg.Add("metadata", jMetadata);
102✔
3758
        }
3759

3760
        return jArg;
2,342✔
3761
    };
3762

3763
    {
3764
        CPLJSONArray jArgs;
160✔
3765
        for (const auto &arg : m_args)
2,346✔
3766
        {
3767
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
3,353✔
3768
                !arg->IsOutput())
1,167✔
3769
                jArgs.Add(ProcessArg(arg.get()));
1,112✔
3770
        }
3771
        oRoot.Add("input_arguments", jArgs);
160✔
3772
    }
3773

3774
    {
3775
        CPLJSONArray jArgs;
160✔
3776
        for (const auto &arg : m_args)
2,346✔
3777
        {
3778
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && !arg->IsInput() &&
2,190✔
3779
                arg->IsOutput())
4✔
3780
                jArgs.Add(ProcessArg(arg.get()));
4✔
3781
        }
3782
        oRoot.Add("output_arguments", jArgs);
160✔
3783
    }
3784

3785
    {
3786
        CPLJSONArray jArgs;
160✔
3787
        for (const auto &arg : m_args)
2,346✔
3788
        {
3789
            if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
3,353✔
3790
                arg->IsOutput())
1,167✔
3791
                jArgs.Add(ProcessArg(arg.get()));
55✔
3792
        }
3793
        oRoot.Add("input_output_arguments", jArgs);
160✔
3794
    }
3795

3796
    if (m_supportsStreamedOutput)
160✔
3797
    {
3798
        oRoot.Add("supports_streamed_output", true);
38✔
3799
    }
3800

3801
    return oDoc.SaveAsString();
320✔
3802
}
3803

3804
/************************************************************************/
3805
/*                    GDALAlgorithm::GetAutoComplete()                  */
3806
/************************************************************************/
3807

3808
std::vector<std::string>
3809
GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
116✔
3810
                               bool showAllOptions)
3811
{
3812
    // Get inner-most algorithm
3813
    std::unique_ptr<GDALAlgorithm> curAlgHolder;
116✔
3814
    GDALAlgorithm *curAlg = this;
116✔
3815
    while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
243✔
3816
    {
3817
        auto subAlg = curAlg->InstantiateSubAlgorithm(args.front());
178✔
3818
        if (!subAlg)
178✔
3819
            break;
51✔
3820
        showAllOptions = false;
127✔
3821
        args.erase(args.begin());
127✔
3822
        curAlgHolder = std::move(subAlg);
127✔
3823
        curAlg = curAlgHolder.get();
127✔
3824
    }
3825
    if (curAlg != this)
116✔
3826
    {
3827
        return curAlg->GetAutoComplete(args, /* showAllOptions = */ false);
62✔
3828
    }
3829

3830
    std::vector<std::string> ret;
108✔
3831

3832
    std::string option;
108✔
3833
    std::string value;
108✔
3834
    ExtractLastOptionAndValue(args, option, value);
54✔
3835

3836
    if (option.empty() && !args.empty() && !args.back().empty() &&
62✔
3837
        args.back()[0] == '-')
8✔
3838
    {
3839
        // List available options
3840
        for (const auto &arg : GetArgs())
103✔
3841
        {
3842
            if (arg->IsHidden() || arg->IsHiddenForCLI() ||
183✔
3843
                (!showAllOptions &&
169✔
3844
                 (arg->GetName() == "help" || arg->GetName() == "drivers" ||
228✔
3845
                  arg->GetName() == "config" || arg->GetName() == "version" ||
192✔
3846
                  arg->GetName() == "json-usage")))
58✔
3847
            {
3848
                continue;
39✔
3849
            }
3850
            if (!arg->GetShortName().empty())
57✔
3851
            {
3852
                ret.push_back(std::string("-").append(arg->GetShortName()));
14✔
3853
            }
3854
            for (const std::string &alias : arg->GetAliases())
77✔
3855
            {
3856
                ret.push_back(std::string("--").append(alias));
20✔
3857
            }
3858
            if (!arg->GetName().empty())
57✔
3859
            {
3860
                ret.push_back(std::string("--").append(arg->GetName()));
57✔
3861
            }
3862
        }
3863
    }
3864
    else if (!option.empty())
47✔
3865
    {
3866
        // List possible choices for current option
3867
        auto arg = GetArg(option);
43✔
3868
        if (arg && arg->GetType() != GAAT_BOOLEAN)
43✔
3869
        {
3870
            ret = arg->GetChoices();
43✔
3871
            if (ret.empty())
43✔
3872
            {
3873
                {
3874
                    CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
39✔
3875
                    SetParseForAutoCompletion();
39✔
3876
                    CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
39✔
3877
                }
3878
                ret = arg->GetAutoCompleteChoices(value);
39✔
3879
            }
3880
            if (ret.empty())
43✔
3881
            {
3882
                ret.push_back("**");
3✔
3883
                ret.push_back(
3✔
3884
                    std::string("description: ").append(arg->GetDescription()));
3✔
3885
            }
3886
        }
3887
    }
3888
    else if (!args.empty() && STARTS_WITH(args.back().c_str(), "/vsi"))
4✔
3889
    {
3890
        auto arg = GetArg(GDAL_ARG_NAME_INPUT);
1✔
3891
        if (arg)
1✔
3892
        {
3893
            ret = arg->GetAutoCompleteChoices(args.back());
1✔
3894
        }
3895
    }
3896
    else
3897
    {
3898
        // List possible sub-algorithms
3899
        ret = GetSubAlgorithmNames();
3✔
3900
    }
3901

3902
    return ret;
54✔
3903
}
3904

3905
/************************************************************************/
3906
/*             GDALAlgorithm::ExtractLastOptionAndValue()               */
3907
/************************************************************************/
3908

3909
void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
54✔
3910
                                              std::string &option,
3911
                                              std::string &value) const
3912
{
3913
    if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
54✔
3914
    {
3915
        const auto nPosEqual = args.back().find('=');
36✔
3916
        if (nPosEqual == std::string::npos)
36✔
3917
        {
3918
            // Deal with "gdal ... --option"
3919
            if (GetArg(args.back()))
25✔
3920
            {
3921
                option = args.back();
18✔
3922
                args.pop_back();
18✔
3923
            }
3924
        }
3925
        else
3926
        {
3927
            // Deal with "gdal ... --option=<value>"
3928
            if (GetArg(args.back().substr(0, nPosEqual)))
11✔
3929
            {
3930
                option = args.back().substr(0, nPosEqual);
11✔
3931
                value = args.back().substr(nPosEqual + 1);
11✔
3932
                args.pop_back();
11✔
3933
            }
3934
        }
3935
    }
3936
    else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
32✔
3937
             args[args.size() - 2][0] == '-')
14✔
3938
    {
3939
        // Deal with "gdal ... --option <value>"
3940
        auto arg = GetArg(args[args.size() - 2]);
14✔
3941
        if (arg && arg->GetType() != GAAT_BOOLEAN)
14✔
3942
        {
3943
            option = args[args.size() - 2];
14✔
3944
            value = args.back();
14✔
3945
            args.pop_back();
14✔
3946
        }
3947
    }
3948

3949
    const auto IsKeyValueOption = [](const std::string &osStr)
54✔
3950
    {
3951
        return osStr == "--co" || osStr == "--creation-option" ||
133✔
3952
               osStr == "--lco" || osStr == "--layer-creation-option" ||
114✔
3953
               osStr == "--oo" || osStr == "--open-option";
131✔
3954
    };
3955

3956
    if (IsKeyValueOption(option))
54✔
3957
    {
3958
        const auto nPosEqual = value.find('=');
19✔
3959
        if (nPosEqual != std::string::npos)
19✔
3960
        {
3961
            value.resize(nPosEqual);
10✔
3962
        }
3963
    }
3964
}
54✔
3965

3966
/************************************************************************/
3967
/*                        GDALAlgorithmRelease()                        */
3968
/************************************************************************/
3969

3970
/** Release a handle to an algorithm.
3971
 *
3972
 * @since 3.11
3973
 */
3974
void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
1,113✔
3975
{
3976
    delete hAlg;
1,113✔
3977
}
1,113✔
3978

3979
/************************************************************************/
3980
/*                        GDALAlgorithmGetName()                        */
3981
/************************************************************************/
3982

3983
/** Return the algorithm name.
3984
 *
3985
 * @param hAlg Handle to an algorithm. Must NOT be null.
3986
 * @return algorithm name whose lifetime is bound to hAlg and which must not
3987
 * be freed.
3988
 * @since 3.11
3989
 */
3990
const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
2✔
3991
{
3992
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
3993
    return hAlg->ptr->GetName().c_str();
2✔
3994
}
3995

3996
/************************************************************************/
3997
/*                     GDALAlgorithmGetDescription()                    */
3998
/************************************************************************/
3999

4000
/** Return the algorithm (short) description.
4001
 *
4002
 * @param hAlg Handle to an algorithm. Must NOT be null.
4003
 * @return algorithm description whose lifetime is bound to hAlg and which must
4004
 * not be freed.
4005
 * @since 3.11
4006
 */
4007
const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
2✔
4008
{
4009
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
4010
    return hAlg->ptr->GetDescription().c_str();
2✔
4011
}
4012

4013
/************************************************************************/
4014
/*                     GDALAlgorithmGetLongDescription()                */
4015
/************************************************************************/
4016

4017
/** Return the algorithm (longer) description.
4018
 *
4019
 * @param hAlg Handle to an algorithm. Must NOT be null.
4020
 * @return algorithm description whose lifetime is bound to hAlg and which must
4021
 * not be freed.
4022
 * @since 3.11
4023
 */
4024
const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
2✔
4025
{
4026
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
4027
    return hAlg->ptr->GetLongDescription().c_str();
2✔
4028
}
4029

4030
/************************************************************************/
4031
/*                     GDALAlgorithmGetHelpFullURL()                    */
4032
/************************************************************************/
4033

4034
/** Return the algorithm full URL.
4035
 *
4036
 * @param hAlg Handle to an algorithm. Must NOT be null.
4037
 * @return algorithm URL whose lifetime is bound to hAlg and which must
4038
 * not be freed.
4039
 * @since 3.11
4040
 */
4041
const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
2✔
4042
{
4043
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
4044
    return hAlg->ptr->GetHelpFullURL().c_str();
2✔
4045
}
4046

4047
/************************************************************************/
4048
/*                     GDALAlgorithmHasSubAlgorithms()                  */
4049
/************************************************************************/
4050

4051
/** Return whether the algorithm has sub-algorithms.
4052
 *
4053
 * @param hAlg Handle to an algorithm. Must NOT be null.
4054
 * @since 3.11
4055
 */
4056
bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
541✔
4057
{
4058
    VALIDATE_POINTER1(hAlg, __func__, false);
541✔
4059
    return hAlg->ptr->HasSubAlgorithms();
541✔
4060
}
4061

4062
/************************************************************************/
4063
/*                 GDALAlgorithmGetSubAlgorithmNames()                  */
4064
/************************************************************************/
4065

4066
/** Get the names of registered algorithms.
4067
 *
4068
 * @param hAlg Handle to an algorithm. Must NOT be null.
4069
 * @return a NULL terminated list of names, which must be destroyed with
4070
 * CSLDestroy()
4071
 * @since 3.11
4072
 */
4073
char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
2✔
4074
{
4075
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
4076
    return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
2✔
4077
}
4078

4079
/************************************************************************/
4080
/*                GDALAlgorithmInstantiateSubAlgorithm()                */
4081
/************************************************************************/
4082

4083
/** Instantiate an algorithm by its name (or its alias).
4084
 *
4085
 * @param hAlg Handle to an algorithm. Must NOT be null.
4086
 * @param pszSubAlgName Algorithm name. Must NOT be null.
4087
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
4088
 * or NULL if the algorithm does not exist or another error occurred.
4089
 * @since 3.11
4090
 */
4091
GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
504✔
4092
                                                    const char *pszSubAlgName)
4093
{
4094
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
504✔
4095
    VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
504✔
4096
    auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
1,008✔
4097
    return subAlg
4098
               ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
1,008✔
4099
               : nullptr;
1,008✔
4100
}
4101

4102
/************************************************************************/
4103
/*                GDALAlgorithmParseCommandLineArguments()              */
4104
/************************************************************************/
4105

4106
/** Parse a command line argument, which does not include the algorithm
4107
 * name, to set the value of corresponding arguments.
4108
 *
4109
 * @param hAlg Handle to an algorithm. Must NOT be null.
4110
 * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
4111
 * @return true if successful, false otherwise
4112
 * @since 3.11
4113
 */
4114

4115
bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
277✔
4116
                                            CSLConstList papszArgs)
4117
{
4118
    VALIDATE_POINTER1(hAlg, __func__, false);
277✔
4119
    return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
277✔
4120
}
4121

4122
/************************************************************************/
4123
/*                  GDALAlgorithmGetActualAlgorithm()                   */
4124
/************************************************************************/
4125

4126
/** Return the actual algorithm that is going to be invoked, when the
4127
 * current algorithm has sub-algorithms.
4128
 *
4129
 * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
4130
 *
4131
 * Note that the lifetime of the returned algorithm does not exceed the one of
4132
 * the hAlg instance that owns it.
4133
 *
4134
 * @param hAlg Handle to an algorithm. Must NOT be null.
4135
 * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
4136
 * @since 3.11
4137
 */
4138
GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
153✔
4139
{
4140
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
153✔
4141
    return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
153✔
4142
}
4143

4144
/************************************************************************/
4145
/*                          GDALAlgorithmRun()                          */
4146
/************************************************************************/
4147

4148
/** Execute the algorithm, starting with ValidateArguments() and then
4149
 * calling RunImpl().
4150
 *
4151
 * @param hAlg Handle to an algorithm. Must NOT be null.
4152
 * @param pfnProgress Progress callback. May be null.
4153
 * @param pProgressData Progress callback user data. May be null.
4154
 * @return true if successful, false otherwise
4155
 * @since 3.11
4156
 */
4157

4158
bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
419✔
4159
                      void *pProgressData)
4160
{
4161
    VALIDATE_POINTER1(hAlg, __func__, false);
419✔
4162
    return hAlg->ptr->Run(pfnProgress, pProgressData);
419✔
4163
}
4164

4165
/************************************************************************/
4166
/*                       GDALAlgorithmFinalize()                        */
4167
/************************************************************************/
4168

4169
/** Complete any pending actions, and return the final status.
4170
 * This is typically useful for algorithm that generate an output dataset.
4171
 *
4172
 * Note that this function does *NOT* release memory associated with the
4173
 * algorithm. GDALAlgorithmRelease() must still be called afterwards.
4174
 *
4175
 * @param hAlg Handle to an algorithm. Must NOT be null.
4176
 * @return true if successful, false otherwise
4177
 * @since 3.11
4178
 */
4179

4180
bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
168✔
4181
{
4182
    VALIDATE_POINTER1(hAlg, __func__, false);
168✔
4183
    return hAlg->ptr->Finalize();
168✔
4184
}
4185

4186
/************************************************************************/
4187
/*                    GDALAlgorithmGetUsageAsJSON()                     */
4188
/************************************************************************/
4189

4190
/** Return the usage of the algorithm as a JSON-serialized string.
4191
 *
4192
 * This can be used to dynamically generate interfaces to algorithms.
4193
 *
4194
 * @param hAlg Handle to an algorithm. Must NOT be null.
4195
 * @return a string that must be freed with CPLFree()
4196
 * @since 3.11
4197
 */
4198
char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
4✔
4199
{
4200
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
4✔
4201
    return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
4✔
4202
}
4203

4204
/************************************************************************/
4205
/*                      GDALAlgorithmGetArgNames()                      */
4206
/************************************************************************/
4207

4208
/** Return the list of available argument names.
4209
 *
4210
 * @param hAlg Handle to an algorithm. Must NOT be null.
4211
 * @return a NULL terminated list of names, which must be destroyed with
4212
 * CSLDestroy()
4213
 * @since 3.11
4214
 */
4215
char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
2✔
4216
{
4217
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
2✔
4218
    CPLStringList list;
4✔
4219
    for (const auto &arg : hAlg->ptr->GetArgs())
31✔
4220
        list.AddString(arg->GetName().c_str());
29✔
4221
    return list.StealList();
2✔
4222
}
4223

4224
/************************************************************************/
4225
/*                        GDALAlgorithmGetArg()                         */
4226
/************************************************************************/
4227

4228
/** Return an argument from its name.
4229
 *
4230
 * The lifetime of the returned object does not exceed the one of hAlg.
4231
 *
4232
 * @param hAlg Handle to an algorithm. Must NOT be null.
4233
 * @param pszArgName Argument name. Must NOT be null.
4234
 * @return an argument that must be released with GDALAlgorithmArgRelease(),
4235
 * or nullptr in case of error
4236
 * @since 3.11
4237
 */
4238
GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
864✔
4239
                                      const char *pszArgName)
4240
{
4241
    VALIDATE_POINTER1(hAlg, __func__, nullptr);
864✔
4242
    VALIDATE_POINTER1(pszArgName, __func__, nullptr);
864✔
4243
    auto arg = hAlg->ptr->GetArg(pszArgName);
864✔
4244
    if (!arg)
864✔
4245
        return nullptr;
2✔
4246
    return std::make_unique<GDALAlgorithmArgHS>(arg).release();
862✔
4247
}
4248

4249
/************************************************************************/
4250
/*                       GDALAlgorithmArgRelease()                      */
4251
/************************************************************************/
4252

4253
/** Release a handle to an argument.
4254
 *
4255
 * @since 3.11
4256
 */
4257
void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
862✔
4258
{
4259
    delete hArg;
862✔
4260
}
862✔
4261

4262
/************************************************************************/
4263
/*                      GDALAlgorithmArgGetName()                       */
4264
/************************************************************************/
4265

4266
/** Return the name of an argument.
4267
 *
4268
 * @param hArg Handle to an argument. Must NOT be null.
4269
 * @return argument name whose lifetime is bound to hArg and which must not
4270
 * be freed.
4271
 * @since 3.11
4272
 */
4273
const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
1✔
4274
{
4275
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4276
    return hArg->ptr->GetName().c_str();
1✔
4277
}
4278

4279
/************************************************************************/
4280
/*                       GDALAlgorithmArgGetType()                      */
4281
/************************************************************************/
4282

4283
/** Get the type of an argument
4284
 *
4285
 * @param hArg Handle to an argument. Must NOT be null.
4286
 * @since 3.11
4287
 */
4288
GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
854✔
4289
{
4290
    VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
854✔
4291
    return hArg->ptr->GetType();
854✔
4292
}
4293

4294
/************************************************************************/
4295
/*                   GDALAlgorithmArgGetDescription()                   */
4296
/************************************************************************/
4297

4298
/** Return the description of an argument.
4299
 *
4300
 * @param hArg Handle to an argument. Must NOT be null.
4301
 * @return argument descriptioin whose lifetime is bound to hArg and which must not
4302
 * be freed.
4303
 * @since 3.11
4304
 */
4305
const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
1✔
4306
{
4307
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4308
    return hArg->ptr->GetDescription().c_str();
1✔
4309
}
4310

4311
/************************************************************************/
4312
/*                   GDALAlgorithmArgGetShortName()                     */
4313
/************************************************************************/
4314

4315
/** Return the short name, or empty string if there is none
4316
 *
4317
 * @param hArg Handle to an argument. Must NOT be null.
4318
 * @return short name whose lifetime is bound to hArg and which must not
4319
 * be freed.
4320
 * @since 3.11
4321
 */
4322
const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
1✔
4323
{
4324
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4325
    return hArg->ptr->GetShortName().c_str();
1✔
4326
}
4327

4328
/************************************************************************/
4329
/*                    GDALAlgorithmArgGetAliases()                      */
4330
/************************************************************************/
4331

4332
/** Return the aliases (potentially none)
4333
 *
4334
 * @param hArg Handle to an argument. Must NOT be null.
4335
 * @return a NULL terminated list of names, which must be destroyed with
4336
 * CSLDestroy()
4337

4338
 * @since 3.11
4339
 */
4340
char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
1✔
4341
{
4342
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4343
    return CPLStringList(hArg->ptr->GetAliases()).StealList();
1✔
4344
}
4345

4346
/************************************************************************/
4347
/*                    GDALAlgorithmArgGetMetaVar()                      */
4348
/************************************************************************/
4349

4350
/** Return the "meta-var" hint.
4351
 *
4352
 * By default, the meta-var value is the long name of the argument in
4353
 * upper case.
4354
 *
4355
 * @param hArg Handle to an argument. Must NOT be null.
4356
 * @return meta-var hint whose lifetime is bound to hArg and which must not
4357
 * be freed.
4358
 * @since 3.11
4359
 */
4360
const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
1✔
4361
{
4362
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4363
    return hArg->ptr->GetMetaVar().c_str();
1✔
4364
}
4365

4366
/************************************************************************/
4367
/*                   GDALAlgorithmArgGetCategory()                      */
4368
/************************************************************************/
4369

4370
/** Return the argument category
4371
 *
4372
 * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
4373
 *
4374
 * @param hArg Handle to an argument. Must NOT be null.
4375
 * @return category whose lifetime is bound to hArg and which must not
4376
 * be freed.
4377
 * @since 3.11
4378
 */
4379
const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
1✔
4380
{
4381
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4382
    return hArg->ptr->GetCategory().c_str();
1✔
4383
}
4384

4385
/************************************************************************/
4386
/*                   GDALAlgorithmArgIsPositional()                     */
4387
/************************************************************************/
4388

4389
/** Return if the argument is a positional one.
4390
 *
4391
 * @param hArg Handle to an argument. Must NOT be null.
4392
 * @since 3.11
4393
 */
4394
bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
1✔
4395
{
4396
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4397
    return hArg->ptr->IsPositional();
1✔
4398
}
4399

4400
/************************************************************************/
4401
/*                   GDALAlgorithmArgIsRequired()                       */
4402
/************************************************************************/
4403

4404
/** Return whether the argument is required. Defaults to false.
4405
 *
4406
 * @param hArg Handle to an argument. Must NOT be null.
4407
 * @since 3.11
4408
 */
4409
bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
1✔
4410
{
4411
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4412
    return hArg->ptr->IsRequired();
1✔
4413
}
4414

4415
/************************************************************************/
4416
/*                   GDALAlgorithmArgGetMinCount()                      */
4417
/************************************************************************/
4418

4419
/** Return the minimum number of values for the argument.
4420
 *
4421
 * Defaults to 0.
4422
 * Only applies to list type of arguments.
4423
 *
4424
 * @param hArg Handle to an argument. Must NOT be null.
4425
 * @since 3.11
4426
 */
4427
int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
1✔
4428
{
4429
    VALIDATE_POINTER1(hArg, __func__, 0);
1✔
4430
    return hArg->ptr->GetMinCount();
1✔
4431
}
4432

4433
/************************************************************************/
4434
/*                   GDALAlgorithmArgGetMaxCount()                      */
4435
/************************************************************************/
4436

4437
/** Return the maximum number of values for the argument.
4438
 *
4439
 * Defaults to 1 for scalar types, and INT_MAX for list types.
4440
 * Only applies to list type of arguments.
4441
 *
4442
 * @param hArg Handle to an argument. Must NOT be null.
4443
 * @since 3.11
4444
 */
4445
int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
1✔
4446
{
4447
    VALIDATE_POINTER1(hArg, __func__, 0);
1✔
4448
    return hArg->ptr->GetMaxCount();
1✔
4449
}
4450

4451
/************************************************************************/
4452
/*                GDALAlgorithmArgGetPackedValuesAllowed()              */
4453
/************************************************************************/
4454

4455
/** Return whether, for list type of arguments, several values, space
4456
 * separated, may be specified. That is "--foo=bar,baz".
4457
 * The default is true.
4458
 *
4459
 * @param hArg Handle to an argument. Must NOT be null.
4460
 * @since 3.11
4461
 */
4462
bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
1✔
4463
{
4464
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4465
    return hArg->ptr->GetPackedValuesAllowed();
1✔
4466
}
4467

4468
/************************************************************************/
4469
/*                GDALAlgorithmArgGetRepeatedArgAllowed()               */
4470
/************************************************************************/
4471

4472
/** Return whether, for list type of arguments, the argument may be
4473
 * repeated. That is "--foo=bar --foo=baz".
4474
 * The default is true.
4475
 *
4476
 * @param hArg Handle to an argument. Must NOT be null.
4477
 * @since 3.11
4478
 */
4479
bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
1✔
4480
{
4481
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4482
    return hArg->ptr->GetRepeatedArgAllowed();
1✔
4483
}
4484

4485
/************************************************************************/
4486
/*                    GDALAlgorithmArgGetChoices()                      */
4487
/************************************************************************/
4488

4489
/** Return the allowed values (as strings) for the argument.
4490
 *
4491
 * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
4492
 *
4493
 * @param hArg Handle to an argument. Must NOT be null.
4494
 * @return a NULL terminated list of names, which must be destroyed with
4495
 * CSLDestroy()
4496

4497
 * @since 3.11
4498
 */
4499
char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
1✔
4500
{
4501
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4502
    return CPLStringList(hArg->ptr->GetChoices()).StealList();
1✔
4503
}
4504

4505
/************************************************************************/
4506
/*                   GDALAlgorithmArgIsExplicitlySet()                  */
4507
/************************************************************************/
4508

4509
/** Return whether the argument value has been explicitly set with Set()
4510
 *
4511
 * @param hArg Handle to an argument. Must NOT be null.
4512
 * @since 3.11
4513
 */
4514
bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
1✔
4515
{
4516
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4517
    return hArg->ptr->IsExplicitlySet();
1✔
4518
}
4519

4520
/************************************************************************/
4521
/*                   GDALAlgorithmArgHasDefaultValue()                  */
4522
/************************************************************************/
4523

4524
/** Return if the argument has a declared default value.
4525
 *
4526
 * @param hArg Handle to an argument. Must NOT be null.
4527
 * @since 3.11
4528
 */
4529
bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
1✔
4530
{
4531
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4532
    return hArg->ptr->HasDefaultValue();
1✔
4533
}
4534

4535
/************************************************************************/
4536
/*                   GDALAlgorithmArgIsHiddenForCLI()                   */
4537
/************************************************************************/
4538

4539
/** Return whether the argument must not be mentioned in CLI usage.
4540
 *
4541
 * For example, "output-value" for "gdal raster info", which is only
4542
 * meant when the algorithm is used from a non-CLI context.
4543
 *
4544
 * @param hArg Handle to an argument. Must NOT be null.
4545
 * @since 3.11
4546
 */
4547
bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
1✔
4548
{
4549
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4550
    return hArg->ptr->IsHiddenForCLI();
1✔
4551
}
4552

4553
/************************************************************************/
4554
/*                   GDALAlgorithmArgIsOnlyForCLI()                     */
4555
/************************************************************************/
4556

4557
/** Return whether the argument is only for CLI usage.
4558
 *
4559
 * For example "--help"
4560
 *
4561
 * @param hArg Handle to an argument. Must NOT be null.
4562
 * @since 3.11
4563
 */
4564
bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
1✔
4565
{
4566
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4567
    return hArg->ptr->IsOnlyForCLI();
1✔
4568
}
4569

4570
/************************************************************************/
4571
/*                     GDALAlgorithmArgIsInput()                        */
4572
/************************************************************************/
4573

4574
/** Indicate whether the value of the argument is read-only during the
4575
 * execution of the algorithm.
4576
 *
4577
 * Default is true.
4578
 *
4579
 * @param hArg Handle to an argument. Must NOT be null.
4580
 * @since 3.11
4581
 */
4582
bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
1✔
4583
{
4584
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4585
    return hArg->ptr->IsInput();
1✔
4586
}
4587

4588
/************************************************************************/
4589
/*                     GDALAlgorithmArgIsOutput()                       */
4590
/************************************************************************/
4591

4592
/** Return whether (at least part of) the value of the argument is set
4593
 * during the execution of the algorithm.
4594
 *
4595
 * For example, "output-value" for "gdal raster info"
4596
 * Default is false.
4597
 * An argument may return both IsInput() and IsOutput() as true.
4598
 * For example the "gdal raster convert" algorithm consumes the dataset
4599
 * name of its "output" argument, and sets the dataset object during its
4600
 * execution.
4601
 *
4602
 * @param hArg Handle to an argument. Must NOT be null.
4603
 * @since 3.11
4604
 */
4605
bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
1✔
4606
{
4607
    VALIDATE_POINTER1(hArg, __func__, false);
1✔
4608
    return hArg->ptr->IsOutput();
1✔
4609
}
4610

4611
/************************************************************************/
4612
/*               GDALAlgorithmArgGetMutualExclusionGroup()              */
4613
/************************************************************************/
4614

4615
/** Return the name of the mutual exclusion group to which this argument
4616
 * belongs to.
4617
 *
4618
 * Or empty string if it does not belong to any exclusion group.
4619
 *
4620
 * @param hArg Handle to an argument. Must NOT be null.
4621
 * @return string whose lifetime is bound to hArg and which must not
4622
 * be freed.
4623
 * @since 3.11
4624
 */
4625
const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
1✔
4626
{
4627
    VALIDATE_POINTER1(hArg, __func__, nullptr);
1✔
4628
    return hArg->ptr->GetMutualExclusionGroup().c_str();
1✔
4629
}
4630

4631
/************************************************************************/
4632
/*                    GDALAlgorithmArgGetAsBoolean()                    */
4633
/************************************************************************/
4634

4635
/** Return the argument value as a boolean.
4636
 *
4637
 * Must only be called on arguments whose type is GAAT_BOOLEAN.
4638
 *
4639
 * @param hArg Handle to an argument. Must NOT be null.
4640
 * @since 3.11
4641
 */
4642
bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
4✔
4643
{
4644
    VALIDATE_POINTER1(hArg, __func__, false);
4✔
4645
    if (hArg->ptr->GetType() != GAAT_BOOLEAN)
4✔
4646
    {
4647
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4648
                 "%s must only be called on arguments of type GAAT_BOOLEAN",
4649
                 __func__);
4650
        return false;
1✔
4651
    }
4652
    return hArg->ptr->Get<bool>();
3✔
4653
}
4654

4655
/************************************************************************/
4656
/*                    GDALAlgorithmArgGetAsBoolean()                    */
4657
/************************************************************************/
4658

4659
/** Return the argument value as a string.
4660
 *
4661
 * Must only be called on arguments whose type is GAAT_STRING.
4662
 *
4663
 * @param hArg Handle to an argument. Must NOT be null.
4664
 * @return string whose lifetime is bound to hArg and which must not
4665
 * be freed.
4666
 * @since 3.11
4667
 */
4668
const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
28✔
4669
{
4670
    VALIDATE_POINTER1(hArg, __func__, nullptr);
28✔
4671
    if (hArg->ptr->GetType() != GAAT_STRING)
28✔
4672
    {
4673
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4674
                 "%s must only be called on arguments of type GAAT_STRING",
4675
                 __func__);
4676
        return nullptr;
1✔
4677
    }
4678
    return hArg->ptr->Get<std::string>().c_str();
27✔
4679
}
4680

4681
/************************************************************************/
4682
/*                 GDALAlgorithmArgGetAsDatasetValue()                  */
4683
/************************************************************************/
4684

4685
/** Return the argument value as a GDALArgDatasetValueH.
4686
 *
4687
 * Must only be called on arguments whose type is GAAT_DATASET
4688
 *
4689
 * @param hArg Handle to an argument. Must NOT be null.
4690
 * @return handle to a GDALArgDatasetValue that must be released with
4691
 * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
4692
 * the one of hArg.
4693
 * @since 3.11
4694
 */
4695
GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
459✔
4696
{
4697
    VALIDATE_POINTER1(hArg, __func__, nullptr);
459✔
4698
    if (hArg->ptr->GetType() != GAAT_DATASET)
459✔
4699
    {
4700
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4701
                 "%s must only be called on arguments of type GAAT_DATASET",
4702
                 __func__);
4703
        return nullptr;
1✔
4704
    }
4705
    return std::make_unique<GDALArgDatasetValueHS>(
458✔
4706
               &(hArg->ptr->Get<GDALArgDatasetValue>()))
916✔
4707
        .release();
458✔
4708
}
4709

4710
/************************************************************************/
4711
/*                    GDALAlgorithmArgGetAsInteger()                    */
4712
/************************************************************************/
4713

4714
/** Return the argument value as a integer.
4715
 *
4716
 * Must only be called on arguments whose type is GAAT_INTEGER
4717
 *
4718
 * @param hArg Handle to an argument. Must NOT be null.
4719
 * @since 3.11
4720
 */
4721
int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
2✔
4722
{
4723
    VALIDATE_POINTER1(hArg, __func__, 0);
2✔
4724
    if (hArg->ptr->GetType() != GAAT_INTEGER)
2✔
4725
    {
4726
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4727
                 "%s must only be called on arguments of type GAAT_INTEGER",
4728
                 __func__);
4729
        return 0;
1✔
4730
    }
4731
    return hArg->ptr->Get<int>();
1✔
4732
}
4733

4734
/************************************************************************/
4735
/*                    GDALAlgorithmArgGetAsDouble()                     */
4736
/************************************************************************/
4737

4738
/** Return the argument value as a double.
4739
 *
4740
 * Must only be called on arguments whose type is GAAT_REAL
4741
 *
4742
 * @param hArg Handle to an argument. Must NOT be null.
4743
 * @since 3.11
4744
 */
4745
double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
2✔
4746
{
4747
    VALIDATE_POINTER1(hArg, __func__, 0);
2✔
4748
    if (hArg->ptr->GetType() != GAAT_REAL)
2✔
4749
    {
4750
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4751
                 "%s must only be called on arguments of type GAAT_REAL",
4752
                 __func__);
4753
        return 0;
1✔
4754
    }
4755
    return hArg->ptr->Get<double>();
1✔
4756
}
4757

4758
/************************************************************************/
4759
/*                   GDALAlgorithmArgGetAsStringList()                  */
4760
/************************************************************************/
4761

4762
/** Return the argument value as a double.
4763
 *
4764
 * Must only be called on arguments whose type is GAAT_STRING_LIST.
4765
 *
4766
 * @param hArg Handle to an argument. Must NOT be null.
4767
 * @return a NULL terminated list of names, which must be destroyed with
4768
 * CSLDestroy()
4769

4770
 * @since 3.11
4771
 */
4772
char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
2✔
4773
{
4774
    VALIDATE_POINTER1(hArg, __func__, nullptr);
2✔
4775
    if (hArg->ptr->GetType() != GAAT_STRING_LIST)
2✔
4776
    {
4777
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4778
                 "%s must only be called on arguments of type GAAT_STRING_LIST",
4779
                 __func__);
4780
        return nullptr;
1✔
4781
    }
4782
    return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
2✔
4783
        .StealList();
1✔
4784
}
4785

4786
/************************************************************************/
4787
/*                  GDALAlgorithmArgGetAsIntegerList()                  */
4788
/************************************************************************/
4789

4790
/** Return the argument value as a integer.
4791
 *
4792
 * Must only be called on arguments whose type is GAAT_INTEGER
4793
 *
4794
 * @param hArg Handle to an argument. Must NOT be null.
4795
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
4796
 * @since 3.11
4797
 */
4798
const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
2✔
4799
                                            size_t *pnCount)
4800
{
4801
    VALIDATE_POINTER1(hArg, __func__, nullptr);
2✔
4802
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
2✔
4803
    if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
2✔
4804
    {
4805
        CPLError(
1✔
4806
            CE_Failure, CPLE_AppDefined,
4807
            "%s must only be called on arguments of type GAAT_INTEGER_LIST",
4808
            __func__);
4809
        *pnCount = 0;
1✔
4810
        return nullptr;
1✔
4811
    }
4812
    const auto &val = hArg->ptr->Get<std::vector<int>>();
1✔
4813
    *pnCount = val.size();
1✔
4814
    return val.data();
1✔
4815
}
4816

4817
/************************************************************************/
4818
/*                  GDALAlgorithmArgGetAsDoubleList()                   */
4819
/************************************************************************/
4820

4821
/** Return the argument value as a integer.
4822
 *
4823
 * Must only be called on arguments whose type is GAAT_INTEGER
4824
 *
4825
 * @param hArg Handle to an argument. Must NOT be null.
4826
 * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
4827
 * @since 3.11
4828
 */
4829
const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
2✔
4830
                                              size_t *pnCount)
4831
{
4832
    VALIDATE_POINTER1(hArg, __func__, nullptr);
2✔
4833
    VALIDATE_POINTER1(pnCount, __func__, nullptr);
2✔
4834
    if (hArg->ptr->GetType() != GAAT_REAL_LIST)
2✔
4835
    {
4836
        CPLError(CE_Failure, CPLE_AppDefined,
1✔
4837
                 "%s must only be called on arguments of type GAAT_REAL_LIST",
4838
                 __func__);
4839
        *pnCount = 0;
1✔
4840
        return nullptr;
1✔
4841
    }
4842
    const auto &val = hArg->ptr->Get<std::vector<double>>();
1✔
4843
    *pnCount = val.size();
1✔
4844
    return val.data();
1✔
4845
}
4846

4847
/************************************************************************/
4848
/*                    GDALAlgorithmArgSetAsBoolean()                    */
4849
/************************************************************************/
4850

4851
/** Set the value for a GAAT_BOOLEAN argument.
4852
 *
4853
 * It cannot be called several times for a given argument.
4854
 * Validation checks and other actions are run.
4855
 *
4856
 * @param hArg Handle to an argument. Must NOT be null.
4857
 * @param value value.
4858
 * @return true if success.
4859
 * @since 3.11
4860
 */
4861

4862
bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
44✔
4863
{
4864
    VALIDATE_POINTER1(hArg, __func__, false);
44✔
4865
    return hArg->ptr->Set(value);
44✔
4866
}
4867

4868
/************************************************************************/
4869
/*                    GDALAlgorithmArgSetAsString()                     */
4870
/************************************************************************/
4871

4872
/** Set the value for a GAAT_STRING argument.
4873
 *
4874
 * It cannot be called several times for a given argument.
4875
 * Validation checks and other actions are run.
4876
 *
4877
 * @param hArg Handle to an argument. Must NOT be null.
4878
 * @param value value (may be null)
4879
 * @return true if success.
4880
 * @since 3.11
4881
 */
4882

4883
bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
158✔
4884
{
4885
    VALIDATE_POINTER1(hArg, __func__, false);
158✔
4886
    return hArg->ptr->Set(value ? value : "");
158✔
4887
}
4888

4889
/************************************************************************/
4890
/*                    GDALAlgorithmArgSetAsInteger()                    */
4891
/************************************************************************/
4892

4893
/** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
4894
 *
4895
 * It cannot be called several times for a given argument.
4896
 * Validation checks and other actions are run.
4897
 *
4898
 * @param hArg Handle to an argument. Must NOT be null.
4899
 * @param value value.
4900
 * @return true if success.
4901
 * @since 3.11
4902
 */
4903

4904
bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8✔
4905
{
4906
    VALIDATE_POINTER1(hArg, __func__, false);
8✔
4907
    return hArg->ptr->Set(value);
8✔
4908
}
4909

4910
/************************************************************************/
4911
/*                    GDALAlgorithmArgSetAsDouble()                     */
4912
/************************************************************************/
4913

4914
/** Set the value for a GAAT_REAL argument.
4915
 *
4916
 * It cannot be called several times for a given argument.
4917
 * Validation checks and other actions are run.
4918
 *
4919
 * @param hArg Handle to an argument. Must NOT be null.
4920
 * @param value value.
4921
 * @return true if success.
4922
 * @since 3.11
4923
 */
4924

4925
bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
53✔
4926
{
4927
    VALIDATE_POINTER1(hArg, __func__, false);
53✔
4928
    return hArg->ptr->Set(value);
53✔
4929
}
4930

4931
/************************************************************************/
4932
/*                 GDALAlgorithmArgSetAsDatasetValue()                  */
4933
/************************************************************************/
4934

4935
/** Set the value for a GAAT_DATASET argument.
4936
 *
4937
 * It cannot be called several times for a given argument.
4938
 * Validation checks and other actions are run.
4939
 *
4940
 * @param hArg Handle to an argument. Must NOT be null.
4941
 * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
4942
 * @return true if success.
4943
 * @since 3.11
4944
 */
4945
bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
2✔
4946
                                       GDALArgDatasetValueH value)
4947
{
4948
    VALIDATE_POINTER1(hArg, __func__, false);
2✔
4949
    VALIDATE_POINTER1(value, __func__, false);
2✔
4950
    return hArg->ptr->SetFrom(*(value->ptr));
2✔
4951
}
4952

4953
/************************************************************************/
4954
/*                     GDALAlgorithmArgSetDataset()                     */
4955
/************************************************************************/
4956

4957
/** Set dataset object, increasing its reference counter.
4958
 *
4959
 * @param hArg Handle to an argument. Must NOT be null.
4960
 * @param hDS Dataset object. May be null.
4961
 * @return true if success.
4962
 * @since 3.11
4963
 */
4964

4965
bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
3✔
4966
{
4967
    VALIDATE_POINTER1(hArg, __func__, false);
3✔
4968
    return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
3✔
4969
}
4970

4971
/************************************************************************/
4972
/*                  GDALAlgorithmArgSetAsStringList()                   */
4973
/************************************************************************/
4974

4975
/** Set the value for a GAAT_STRING_LIST argument.
4976
 *
4977
 * It cannot be called several times for a given argument.
4978
 * Validation checks and other actions are run.
4979
 *
4980
 * @param hArg Handle to an argument. Must NOT be null.
4981
 * @param value value as a NULL terminated list (may be null)
4982
 * @return true if success.
4983
 * @since 3.11
4984
 */
4985

4986
bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
85✔
4987
{
4988
    VALIDATE_POINTER1(hArg, __func__, false);
85✔
4989
    return hArg->ptr->Set(
85✔
4990
        static_cast<std::vector<std::string>>(CPLStringList(value)));
170✔
4991
}
4992

4993
/************************************************************************/
4994
/*                  GDALAlgorithmArgSetAsIntegerList()                  */
4995
/************************************************************************/
4996

4997
/** Set the value for a GAAT_INTEGER_LIST argument.
4998
 *
4999
 * It cannot be called several times for a given argument.
5000
 * Validation checks and other actions are run.
5001
 *
5002
 * @param hArg Handle to an argument. Must NOT be null.
5003
 * @param nCount Number of values in pnValues.
5004
 * @param pnValues Pointer to an array of integer values of size nCount.
5005
 * @return true if success.
5006
 * @since 3.11
5007
 */
5008
bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
4✔
5009
                                      const int *pnValues)
5010
{
5011
    VALIDATE_POINTER1(hArg, __func__, false);
4✔
5012
    return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
4✔
5013
}
5014

5015
/************************************************************************/
5016
/*                   GDALAlgorithmArgSetAsDoubleList()                  */
5017
/************************************************************************/
5018

5019
/** Set the value for a GAAT_REAL_LIST argument.
5020
 *
5021
 * It cannot be called several times for a given argument.
5022
 * Validation checks and other actions are run.
5023
 *
5024
 * @param hArg Handle to an argument. Must NOT be null.
5025
 * @param nCount Number of values in pnValues.
5026
 * @param pnValues Pointer to an array of double values of size nCount.
5027
 * @return true if success.
5028
 * @since 3.11
5029
 */
5030
bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
6✔
5031
                                     const double *pnValues)
5032
{
5033
    VALIDATE_POINTER1(hArg, __func__, false);
6✔
5034
    return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
6✔
5035
}
5036

5037
/************************************************************************/
5038
/*                     GDALAlgorithmArgSetDatasets()                    */
5039
/************************************************************************/
5040

5041
/** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
5042
 *
5043
 * @param hArg Handle to an argument. Must NOT be null.
5044
 * @param nCount Number of values in pnValues.
5045
 * @param pahDS Pointer to an array of dataset of size nCount.
5046
 * @return true if success.
5047
 * @since 3.11
5048
 */
5049

5050
bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
12✔
5051
                                 GDALDatasetH *pahDS)
5052
{
5053
    VALIDATE_POINTER1(hArg, __func__, false);
12✔
5054
    std::vector<GDALArgDatasetValue> values;
24✔
5055
    for (size_t i = 0; i < nCount; ++i)
34✔
5056
    {
5057
        values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
22✔
5058
    }
5059
    return hArg->ptr->Set(std::move(values));
12✔
5060
}
5061

5062
/************************************************************************/
5063
/*                    GDALAlgorithmArgSetDatasetNames()                 */
5064
/************************************************************************/
5065

5066
/** Set dataset names to a GAAT_DATASET_LIST argument.
5067
 *
5068
 * @param hArg Handle to an argument. Must NOT be null.
5069
 * @param names Dataset names as a NULL terminated list (may be null)
5070
 * @return true if success.
5071
 * @since 3.11
5072
 */
5073

5074
bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
7✔
5075
{
5076
    VALIDATE_POINTER1(hArg, __func__, false);
7✔
5077
    std::vector<GDALArgDatasetValue> values;
14✔
5078
    for (size_t i = 0; names[i]; ++i)
14✔
5079
    {
5080
        values.emplace_back(names[i]);
7✔
5081
    }
5082
    return hArg->ptr->Set(std::move(values));
7✔
5083
}
5084

5085
/************************************************************************/
5086
/*                      GDALArgDatasetValueCreate()                     */
5087
/************************************************************************/
5088

5089
/** Instantiate an empty GDALArgDatasetValue
5090
 *
5091
 * @return new handle to free with GDALArgDatasetValueRelease()
5092
 * @since 3.11
5093
 */
5094
GDALArgDatasetValueH GDALArgDatasetValueCreate()
1✔
5095
{
5096
    return std::make_unique<GDALArgDatasetValueHS>().release();
1✔
5097
}
5098

5099
/************************************************************************/
5100
/*                      GDALArgDatasetValueRelease()                    */
5101
/************************************************************************/
5102

5103
/** Release a handle to a GDALArgDatasetValue
5104
 *
5105
 * @since 3.11
5106
 */
5107
void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
459✔
5108
{
5109
    delete hValue;
459✔
5110
}
459✔
5111

5112
/************************************************************************/
5113
/*                    GDALArgDatasetValueGetName()                      */
5114
/************************************************************************/
5115

5116
/** Return the name component of the GDALArgDatasetValue
5117
 *
5118
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5119
 * @return string whose lifetime is bound to hAlg and which must not
5120
 * be freed.
5121
 * @since 3.11
5122
 */
5123
const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
1✔
5124
{
5125
    VALIDATE_POINTER1(hValue, __func__, nullptr);
1✔
5126
    return hValue->ptr->GetName().c_str();
1✔
5127
}
5128

5129
/************************************************************************/
5130
/*               GDALArgDatasetValueGetDatasetRef()                     */
5131
/************************************************************************/
5132

5133
/** Return the dataset component of the GDALArgDatasetValue.
5134
 *
5135
 * This does not modify the reference counter, hence the lifetime of the
5136
 * returned object is not guaranteed to exceed the one of hValue.
5137
 *
5138
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5139
 * @since 3.11
5140
 */
5141
GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
3✔
5142
{
5143
    VALIDATE_POINTER1(hValue, __func__, nullptr);
3✔
5144
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
3✔
5145
}
5146

5147
/************************************************************************/
5148
/*               GDALArgDatasetValueGetDatasetIncreaseRefCount()        */
5149
/************************************************************************/
5150

5151
/** Return the dataset component of the GDALArgDatasetValue, and increase its
5152
 * reference count if not null. Once done with the dataset, the caller should
5153
 * call GDALReleaseDataset().
5154
 *
5155
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5156
 * @since 3.11
5157
 */
5158
GDALDatasetH
5159
GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
131✔
5160
{
5161
    VALIDATE_POINTER1(hValue, __func__, nullptr);
131✔
5162
    return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
131✔
5163
}
5164

5165
/************************************************************************/
5166
/*                    GDALArgDatasetValueGetType()                      */
5167
/************************************************************************/
5168

5169
/** Get which type of dataset is allowed / generated.
5170
 *
5171
 * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
5172
 * GDAL_OF_MULTIDIM_RASTER.
5173
 *
5174
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5175
 * @since 3.11
5176
 */
5177
GDALArgDatasetValueType GDALArgDatasetValueGetType(GDALArgDatasetValueH hValue)
1✔
5178
{
5179
    VALIDATE_POINTER1(hValue, __func__, 0);
1✔
5180
    return hValue->ptr->GetType();
1✔
5181
}
5182

5183
/************************************************************************/
5184
/*                   GDALArgDatasetValueGetInputFlags()                 */
5185
/************************************************************************/
5186

5187
/** Indicates which components among name and dataset are accepted as
5188
 * input, when this argument serves as an input.
5189
 *
5190
 * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
5191
 * input.
5192
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
5193
 * accepted as input.
5194
 * If both bits are set, the algorithm can accept either a name or a dataset
5195
 * object.
5196
 *
5197
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5198
 * @return string whose lifetime is bound to hAlg and which must not
5199
 * be freed.
5200
 * @since 3.11
5201
 */
5202
int GDALArgDatasetValueGetInputFlags(GDALArgDatasetValueH hValue)
1✔
5203
{
5204
    VALIDATE_POINTER1(hValue, __func__, 0);
1✔
5205
    return hValue->ptr->GetInputFlags();
1✔
5206
}
5207

5208
/************************************************************************/
5209
/*                  GDALArgDatasetValueGetOutputFlags()                 */
5210
/************************************************************************/
5211

5212
/** Indicates which components among name and dataset are modified,
5213
 * when this argument serves as an output.
5214
 *
5215
 * If the GADV_NAME bit is set, it indicates a dataset name is generated as
5216
 * output (that is the algorithm will generate the name. Rarely used).
5217
 * If the GADV_OBJECT bit is set, it indicates a dataset object is
5218
 * generated as output, and available for use after the algorithm has
5219
 * completed.
5220
 *
5221
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5222
 * @return string whose lifetime is bound to hAlg and which must not
5223
 * be freed.
5224
 * @since 3.11
5225
 */
5226
int GDALArgDatasetValueGetOutputFlags(GDALArgDatasetValueH hValue)
1✔
5227
{
5228
    VALIDATE_POINTER1(hValue, __func__, 0);
1✔
5229
    return hValue->ptr->GetOutputFlags();
1✔
5230
}
5231

5232
/************************************************************************/
5233
/*                    GDALArgDatasetValueSetName()                      */
5234
/************************************************************************/
5235

5236
/** Set dataset name
5237
 *
5238
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5239
 * @param pszName Dataset name. May be null.
5240
 * @since 3.11
5241
 */
5242

5243
void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
163✔
5244
                                const char *pszName)
5245
{
5246
    VALIDATE_POINTER0(hValue, __func__);
163✔
5247
    hValue->ptr->Set(pszName ? pszName : "");
163✔
5248
}
5249

5250
/************************************************************************/
5251
/*                  GDALArgDatasetValueSetDataset()                     */
5252
/************************************************************************/
5253

5254
/** Set dataset object, increasing its reference counter.
5255
 *
5256
 * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
5257
 * @param hDS Dataset object. May be null.
5258
 * @since 3.11
5259
 */
5260

5261
void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
167✔
5262
                                   GDALDatasetH hDS)
5263
{
5264
    VALIDATE_POINTER0(hValue, __func__);
167✔
5265
    hValue->ptr->Set(GDALDataset::FromHandle(hDS));
167✔
5266
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc