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