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 6783 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
54 : {
55 6783 : }
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 1524 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
71 : {
72 1524 : }
73 :
74 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
75 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
76 : };
77 :
78 : //! @endcond
79 :
80 : /************************************************************************/
81 : /* GDALAlgorithmArgTypeIsList() */
82 : /************************************************************************/
83 :
84 103679 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
85 : {
86 103679 : switch (type)
87 : {
88 70893 : case GAAT_BOOLEAN:
89 : case GAAT_STRING:
90 : case GAAT_INTEGER:
91 : case GAAT_REAL:
92 : case GAAT_DATASET:
93 70893 : break;
94 :
95 32786 : case GAAT_STRING_LIST:
96 : case GAAT_INTEGER_LIST:
97 : case GAAT_REAL_LIST:
98 : case GAAT_DATASET_LIST:
99 32786 : return true;
100 : }
101 :
102 70893 : return false;
103 : }
104 :
105 : /************************************************************************/
106 : /* GDALAlgorithmArgTypeName() */
107 : /************************************************************************/
108 :
109 3508 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
110 : {
111 3508 : switch (type)
112 : {
113 634 : case GAAT_BOOLEAN:
114 634 : break;
115 967 : case GAAT_STRING:
116 967 : return "string";
117 245 : case GAAT_INTEGER:
118 245 : return "integer";
119 425 : case GAAT_REAL:
120 425 : return "real";
121 186 : case GAAT_DATASET:
122 186 : return "dataset";
123 646 : case GAAT_STRING_LIST:
124 646 : return "string_list";
125 68 : case GAAT_INTEGER_LIST:
126 68 : return "integer_list";
127 201 : case GAAT_REAL_LIST:
128 201 : return "real_list";
129 136 : case GAAT_DATASET_LIST:
130 136 : return "dataset_list";
131 : }
132 :
133 634 : return "boolean";
134 : }
135 :
136 : /************************************************************************/
137 : /* GDALAlgorithmArgDatasetTypeName() */
138 : /************************************************************************/
139 :
140 5443 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
141 : {
142 5443 : std::string ret;
143 5443 : if ((type & GDAL_OF_RASTER) != 0)
144 3877 : ret = "raster";
145 5443 : if ((type & GDAL_OF_VECTOR) != 0)
146 : {
147 1767 : if (!ret.empty())
148 : {
149 223 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
150 50 : ret += ", ";
151 : else
152 173 : ret += " or ";
153 : }
154 1767 : ret += "vector";
155 : }
156 5443 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
157 : {
158 88 : if (!ret.empty())
159 : {
160 66 : ret += " or ";
161 : }
162 88 : ret += "multidimensional raster";
163 : }
164 5443 : return ret;
165 : }
166 :
167 : /************************************************************************/
168 : /* GDALAlgorithmArgDecl() */
169 : /************************************************************************/
170 :
171 : // cppcheck-suppress uninitMemberVar
172 84957 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
173 : char chShortName,
174 : const std::string &description,
175 84957 : GDALAlgorithmArgType type)
176 : : m_longName(longName),
177 84957 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
178 : m_description(description), m_type(type),
179 169914 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
180 84957 : .toupper()),
181 254871 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
182 : {
183 84957 : if (m_type == GAAT_BOOLEAN)
184 : {
185 34270 : m_defaultValue = false;
186 : }
187 84957 : }
188 :
189 : /************************************************************************/
190 : /* GDALAlgorithmArgDecl::SetMinCount() */
191 : /************************************************************************/
192 :
193 3563 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
194 : {
195 3563 : 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 3562 : m_minCount = count;
204 : }
205 3563 : return *this;
206 : }
207 :
208 : /************************************************************************/
209 : /* GDALAlgorithmArgDecl::SetMaxCount() */
210 : /************************************************************************/
211 :
212 3029 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
213 : {
214 3029 : 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 3028 : m_maxCount = count;
223 : }
224 3029 : return *this;
225 : }
226 :
227 : /************************************************************************/
228 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
229 : /************************************************************************/
230 :
231 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
232 :
233 : /************************************************************************/
234 : /* GDALAlgorithmArg::Set() */
235 : /************************************************************************/
236 :
237 599 : bool GDALAlgorithmArg::Set(bool value)
238 : {
239 599 : 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 592 : return SetInternal(value);
248 : }
249 :
250 1764 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
251 : {
252 1793 : 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 1763 : if (m_decl.IsRemoveSQLCommentsEnabled())
276 28 : value = CPLRemoveSQLComments(value);
277 :
278 1763 : return true;
279 : }
280 :
281 1780 : bool GDALAlgorithmArg::Set(const std::string &value)
282 : {
283 1780 : 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 1749 : case GAAT_STRING:
332 1749 : 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 1757 : 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 1749 : std::string newValue(value);
359 1749 : return ProcessString(newValue) && SetInternal(newValue);
360 : }
361 :
362 328 : bool GDALAlgorithmArg::Set(int value)
363 : {
364 328 : 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 325 : else if (m_decl.GetType() == GAAT_REAL)
372 : {
373 3 : return Set(static_cast<double>(value));
374 : }
375 322 : else if (m_decl.GetType() == GAAT_STRING)
376 : {
377 2 : return Set(std::to_string(value));
378 : }
379 320 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
380 : {
381 1 : return Set(std::vector<int>{value});
382 : }
383 319 : else if (m_decl.GetType() == GAAT_REAL_LIST)
384 : {
385 1 : return Set(std::vector<double>{static_cast<double>(value)});
386 : }
387 318 : else if (m_decl.GetType() == GAAT_STRING_LIST)
388 : {
389 2 : return Set(std::vector<std::string>{std::to_string(value)});
390 : }
391 :
392 318 : 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 317 : return SetInternal(value);
401 : }
402 :
403 253 : bool GDALAlgorithmArg::Set(double value)
404 : {
405 256 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
406 256 : value <= INT_MAX && static_cast<int>(value) == value)
407 : {
408 2 : return Set(static_cast<int>(value));
409 : }
410 251 : else if (m_decl.GetType() == GAAT_STRING)
411 : {
412 2 : return Set(std::to_string(value));
413 : }
414 251 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
415 251 : value <= INT_MAX && static_cast<int>(value) == value)
416 : {
417 1 : return Set(std::vector<int>{static_cast<int>(value)});
418 : }
419 248 : else if (m_decl.GetType() == GAAT_REAL_LIST)
420 : {
421 0 : return Set(std::vector<double>{value});
422 : }
423 248 : else if (m_decl.GetType() == GAAT_STRING_LIST)
424 : {
425 2 : return Set(std::vector<std::string>{std::to_string(value)});
426 : }
427 247 : 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 244 : return SetInternal(value);
436 : }
437 :
438 2085 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
439 : {
440 2089 : 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 2081 : 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 2079 : return true;
459 : }
460 :
461 19 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
462 : {
463 19 : 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 18 : if (!CheckCanSetDatasetObject(this))
472 3 : return false;
473 15 : m_explicitlySet = true;
474 15 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
475 15 : val.Set(ds);
476 15 : 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 458 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
498 : {
499 458 : 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 457 : m_explicitlySet = true;
508 457 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
509 457 : return RunAllActions();
510 : }
511 :
512 471 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
513 : {
514 471 : 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 470 : if (!CheckCanSetDatasetObject(this))
523 1 : return false;
524 469 : m_explicitlySet = true;
525 469 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
526 469 : return RunAllActions();
527 : }
528 :
529 435 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
530 : {
531 435 : 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 432 : 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 858 : else if ((m_decl.GetType() == GAAT_INTEGER ||
572 855 : m_decl.GetType() == GAAT_REAL ||
573 1287 : m_decl.GetType() == GAAT_STRING) &&
574 5 : value.size() == 1)
575 : {
576 4 : return Set(value[0]);
577 : }
578 426 : 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 421 : 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 820 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
596 404 : 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 404 : return SetInternal(value);
609 : }
610 : }
611 :
612 108 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
613 : {
614 108 : 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 107 : 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 210 : else if ((m_decl.GetType() == GAAT_INTEGER ||
629 206 : m_decl.GetType() == GAAT_REAL ||
630 314 : m_decl.GetType() == GAAT_STRING) &&
631 5 : value.size() == 1)
632 : {
633 3 : return Set(value[0]);
634 : }
635 :
636 103 : 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 100 : 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 978 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
693 : {
694 978 : 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 977 : m_explicitlySet = true;
703 977 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
704 977 : 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 1559 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
721 : {
722 1559 : 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 1555 : switch (m_decl.GetType())
733 : {
734 17 : case GAAT_BOOLEAN:
735 17 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
736 17 : break;
737 357 : case GAAT_STRING:
738 714 : *std::get<std::string *>(m_value) =
739 357 : *std::get<std::string *>(other.m_value);
740 357 : break;
741 1 : case GAAT_INTEGER:
742 1 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
743 1 : break;
744 1 : case GAAT_REAL:
745 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
746 1 : break;
747 465 : case GAAT_DATASET:
748 465 : return SetFrom(other.Get<GDALArgDatasetValue>());
749 18 : case GAAT_STRING_LIST:
750 36 : *std::get<std::vector<std::string> *>(m_value) =
751 18 : *std::get<std::vector<std::string> *>(other.m_value);
752 18 : 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 694 : case GAAT_DATASET_LIST:
762 : {
763 694 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
764 711 : for (const auto &val :
765 2116 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
766 : {
767 1422 : GDALArgDatasetValue v;
768 711 : v.SetFrom(val);
769 711 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
770 711 : ->push_back(std::move(v));
771 : }
772 694 : break;
773 : }
774 : }
775 1090 : m_explicitlySet = true;
776 1090 : return RunAllActions();
777 : }
778 :
779 : /************************************************************************/
780 : /* GDALAlgorithmArg::RunAllActions() */
781 : /************************************************************************/
782 :
783 6627 : bool GDALAlgorithmArg::RunAllActions()
784 : {
785 6627 : if (!RunValidationActions())
786 84 : return false;
787 6543 : RunActions();
788 6543 : return true;
789 : }
790 :
791 : /************************************************************************/
792 : /* GDALAlgorithmArg::RunActions() */
793 : /************************************************************************/
794 :
795 6544 : void GDALAlgorithmArg::RunActions()
796 : {
797 6611 : for (const auto &f : m_actions)
798 67 : f();
799 6544 : }
800 :
801 : /************************************************************************/
802 : /* GDALAlgorithmArg::ValidateChoice() */
803 : /************************************************************************/
804 :
805 : // Returns the canonical value if matching a valid choice, or empty string
806 : // otherwise.
807 368 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
808 : {
809 1255 : for (const std::string &choice : GetChoices())
810 : {
811 1237 : if (EQUAL(value.c_str(), choice.c_str()))
812 : {
813 350 : 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 519 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
850 : {
851 519 : bool ret = true;
852 :
853 519 : const auto [minVal, minValIsIncluded] = GetMinValue();
854 519 : if (!std::isnan(minVal))
855 : {
856 332 : 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 329 : 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 519 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
873 519 : 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 519 : return ret;
893 : }
894 :
895 : /************************************************************************/
896 : /* GDALAlgorithmArg::ValidateRealRange() */
897 : /************************************************************************/
898 :
899 795 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
900 : {
901 795 : bool ret = true;
902 :
903 795 : const auto [minVal, minValIsIncluded] = GetMinValue();
904 795 : if (!std::isnan(minVal))
905 : {
906 77 : if (minValIsIncluded && !(val >= minVal))
907 : {
908 6 : CPLError(CE_Failure, CPLE_IllegalArg,
909 : "Value of argument '%s' is %g, but should be >= %g",
910 6 : GetName().c_str(), val, minVal);
911 6 : ret = false;
912 : }
913 71 : 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 795 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
923 795 : 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 795 : return ret;
943 : }
944 :
945 : /************************************************************************/
946 : /* GDALAlgorithmArg::RunValidationActions() */
947 : /************************************************************************/
948 :
949 6627 : bool GDALAlgorithmArg::RunValidationActions()
950 : {
951 6627 : bool ret = true;
952 :
953 6627 : if (GetType() == GAAT_STRING && !GetChoices().empty())
954 : {
955 299 : auto &val = Get<std::string>();
956 598 : std::string validVal = ValidateChoice(val);
957 299 : if (validVal.empty())
958 5 : ret = false;
959 : else
960 294 : val = std::move(validVal);
961 : }
962 6328 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
963 : {
964 59 : auto &values = Get<std::vector<std::string>>();
965 128 : for (std::string &val : values)
966 : {
967 138 : std::string validVal = ValidateChoice(val);
968 69 : if (validVal.empty())
969 4 : ret = false;
970 : else
971 65 : val = std::move(validVal);
972 : }
973 : }
974 :
975 6627 : if (GetType() == GAAT_STRING)
976 : {
977 2105 : const int nMinCharCount = GetMinCharCount();
978 2105 : if (nMinCharCount > 0)
979 : {
980 226 : const auto &val = Get<std::string>();
981 226 : 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 4522 : else if (GetType() == GAAT_STRING_LIST)
993 : {
994 434 : const int nMinCharCount = GetMinCharCount();
995 434 : 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 4088 : else if (GetType() == GAAT_INTEGER)
1012 : {
1013 318 : ret = ValidateIntRange(Get<int>()) && ret;
1014 : }
1015 3770 : else if (GetType() == GAAT_INTEGER_LIST)
1016 : {
1017 302 : for (int v : Get<std::vector<int>>())
1018 201 : ret = ValidateIntRange(v) && ret;
1019 : }
1020 3669 : else if (GetType() == GAAT_REAL)
1021 : {
1022 245 : ret = ValidateRealRange(Get<double>()) && ret;
1023 : }
1024 3424 : 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 8180 : for (const auto &f : m_validationActions)
1031 : {
1032 1553 : if (!f())
1033 52 : ret = false;
1034 : }
1035 :
1036 6627 : 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 14868 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1192 : {
1193 14868 : m_decl.AddAlias(alias);
1194 14868 : if (m_owner)
1195 14868 : m_owner->AddAliasFor(this, alias);
1196 14868 : return *this;
1197 : }
1198 :
1199 : /************************************************************************/
1200 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1201 : /************************************************************************/
1202 :
1203 : GDALInConstructionAlgorithmArg &
1204 2733 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1205 : {
1206 2733 : m_decl.AddHiddenAlias(alias);
1207 2733 : if (m_owner)
1208 2733 : m_owner->AddAliasFor(this, alias);
1209 2733 : 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 5057 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1230 : {
1231 5057 : m_decl.SetPositional();
1232 5057 : if (m_owner)
1233 5057 : m_owner->SetPositional(this);
1234 5057 : return *this;
1235 : }
1236 :
1237 : /************************************************************************/
1238 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1239 : /************************************************************************/
1240 :
1241 385 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1242 770 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1243 385 : m_nameSet(true)
1244 : {
1245 385 : if (m_poDS)
1246 385 : m_poDS->Reference();
1247 385 : }
1248 :
1249 : /************************************************************************/
1250 : /* GDALArgDatasetValue::Set() */
1251 : /************************************************************************/
1252 :
1253 1372 : void GDALArgDatasetValue::Set(const std::string &name)
1254 : {
1255 1372 : Close();
1256 1372 : m_name = name;
1257 1372 : m_nameSet = true;
1258 1372 : if (m_ownerArg)
1259 1367 : m_ownerArg->NotifyValueSet();
1260 1372 : }
1261 :
1262 : /************************************************************************/
1263 : /* GDALArgDatasetValue::Set() */
1264 : /************************************************************************/
1265 :
1266 1001 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1267 : {
1268 1001 : Close();
1269 1001 : m_poDS = poDS.release();
1270 1001 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1271 1001 : m_nameSet = true;
1272 1001 : if (m_ownerArg)
1273 955 : m_ownerArg->NotifyValueSet();
1274 1001 : }
1275 :
1276 : /************************************************************************/
1277 : /* GDALArgDatasetValue::Set() */
1278 : /************************************************************************/
1279 :
1280 2892 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1281 : {
1282 2892 : Close();
1283 2892 : m_poDS = poDS;
1284 2892 : if (m_poDS)
1285 2505 : m_poDS->Reference();
1286 2892 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1287 2892 : m_nameSet = true;
1288 2892 : if (m_ownerArg)
1289 1275 : m_ownerArg->NotifyValueSet();
1290 2892 : }
1291 :
1292 : /************************************************************************/
1293 : /* GDALArgDatasetValue::SetFrom() */
1294 : /************************************************************************/
1295 :
1296 1180 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1297 : {
1298 1180 : Close();
1299 1180 : m_name = other.m_name;
1300 1180 : m_nameSet = other.m_nameSet;
1301 1180 : m_poDS = other.m_poDS;
1302 1180 : if (m_poDS)
1303 696 : m_poDS->Reference();
1304 1180 : }
1305 :
1306 : /************************************************************************/
1307 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1308 : /************************************************************************/
1309 :
1310 9800 : GDALArgDatasetValue::~GDALArgDatasetValue()
1311 : {
1312 9800 : Close();
1313 9800 : }
1314 :
1315 : /************************************************************************/
1316 : /* GDALArgDatasetValue::Close() */
1317 : /************************************************************************/
1318 :
1319 17229 : bool GDALArgDatasetValue::Close()
1320 : {
1321 17229 : bool ret = true;
1322 17229 : if (m_poDS && m_poDS->Dereference() == 0)
1323 : {
1324 1691 : ret = m_poDS->Close() == CE_None;
1325 1691 : delete m_poDS;
1326 : }
1327 17229 : m_poDS = nullptr;
1328 17229 : 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 493 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1352 : {
1353 493 : if (m_poDS)
1354 492 : m_poDS->Reference();
1355 493 : return m_poDS;
1356 : }
1357 :
1358 : /************************************************************************/
1359 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1360 : /************************************************************************/
1361 :
1362 1050 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1363 1050 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1364 : {
1365 1050 : other.m_poDS = nullptr;
1366 1050 : other.m_name.clear();
1367 1050 : }
1368 :
1369 : /************************************************************************/
1370 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1371 : /************************************************************************/
1372 :
1373 1040 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1374 : bool noneAllowed, const std::vector<std::string> &specialValues)
1375 : {
1376 1040 : 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 1039 : });
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 1039 : });
1609 :
1610 1039 : return *this;
1611 : }
1612 :
1613 : /************************************************************************/
1614 : /* GDALAlgorithm::GDALAlgorithm() */
1615 : /************************************************************************/
1616 :
1617 6693 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1618 : const std::string &description,
1619 6693 : const std::string &helpURL)
1620 : : m_name(name), m_description(description), m_helpURL(helpURL),
1621 13337 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1622 6693 : ? "https://gdal.org" + m_helpURL
1623 19817 : : m_helpURL)
1624 : {
1625 13386 : AddArg("help", 'h', _("Display help message and exit"), &m_helpRequested)
1626 6693 : .SetOnlyForCLI()
1627 13386 : .SetCategory(GAAC_COMMON)
1628 6693 : .AddAction([this]() { m_specialActionRequested = true; });
1629 : AddArg("help-doc", 0, _("Display help message for use by documentation"),
1630 13386 : &m_helpDocRequested)
1631 6693 : .SetHidden()
1632 6693 : .AddAction([this]() { m_specialActionRequested = true; });
1633 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1634 13386 : &m_JSONUsageRequested)
1635 6693 : .SetOnlyForCLI()
1636 13386 : .SetCategory(GAAC_COMMON)
1637 6693 : .AddAction([this]() { m_specialActionRequested = true; });
1638 13386 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1639 13386 : .SetMetaVar("<KEY>=<VALUE>")
1640 6693 : .SetOnlyForCLI()
1641 13386 : .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 6693 : });
1650 6693 : }
1651 :
1652 : /************************************************************************/
1653 : /* GDALAlgorithm::~GDALAlgorithm() */
1654 : /************************************************************************/
1655 :
1656 : GDALAlgorithm::~GDALAlgorithm() = default;
1657 :
1658 : /************************************************************************/
1659 : /* GDALAlgorithm::ParseArgument() */
1660 : /************************************************************************/
1661 :
1662 1738 : 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 1738 : const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1671 1738 : 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 1779 : 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 1734 : switch (arg->GetType())
1695 : {
1696 200 : case GAAT_BOOLEAN:
1697 : {
1698 200 : if (value.empty() || value == "true")
1699 198 : 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 409 : case GAAT_STRING:
1714 : {
1715 409 : return arg->Set(value);
1716 : }
1717 :
1718 112 : case GAAT_INTEGER:
1719 : {
1720 112 : errno = 0;
1721 112 : char *endptr = nullptr;
1722 112 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1723 111 : if (errno == 0 && endptr &&
1724 223 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1725 : val <= INT_MAX)
1726 : {
1727 109 : 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 448 : case GAAT_DATASET:
1755 : {
1756 448 : return arg->SetDatasetName(value);
1757 : }
1758 :
1759 176 : case GAAT_STRING_LIST:
1760 : {
1761 : const CPLStringList aosTokens(
1762 176 : arg->GetPackedValuesAllowed()
1763 113 : ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
1764 465 : : CSLAddString(nullptr, value.c_str()));
1765 176 : if (!cpl::contains(inConstructionValues, arg))
1766 : {
1767 152 : inConstructionValues[arg] = std::vector<std::string>();
1768 : }
1769 : auto &valueVector =
1770 176 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
1771 379 : for (const char *v : aosTokens)
1772 : {
1773 203 : valueVector.push_back(v);
1774 : }
1775 176 : 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 245 : case GAAT_DATASET_LIST:
1850 : {
1851 : const CPLStringList aosTokens(
1852 490 : CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS));
1853 245 : if (!cpl::contains(inConstructionValues, arg))
1854 : {
1855 244 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
1856 : }
1857 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1858 245 : inConstructionValues[arg]);
1859 490 : for (const char *v : aosTokens)
1860 : {
1861 245 : valueVector.push_back(GDALArgDatasetValue(v));
1862 : }
1863 245 : break;
1864 : }
1865 : }
1866 :
1867 529 : return true;
1868 : }
1869 :
1870 : /************************************************************************/
1871 : /* GDALAlgorithm::ParseCommandLineArguments() */
1872 : /************************************************************************/
1873 :
1874 1159 : bool GDALAlgorithm::ParseCommandLineArguments(
1875 : const std::vector<std::string> &args)
1876 : {
1877 1159 : 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 1158 : m_parsedSubStringAlreadyCalled = true;
1885 :
1886 : // AWS like syntax supported too (not advertized)
1887 1158 : 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 1157 : if (HasSubAlgorithms())
1897 : {
1898 242 : 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 240 : if (!args[0].empty() && args[0][0] == '-')
1905 : {
1906 : // go on argument parsing
1907 : }
1908 : else
1909 : {
1910 237 : const auto nCounter = CPLGetErrorCounter();
1911 237 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
1912 237 : if (m_selectedSubAlgHolder)
1913 : {
1914 234 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
1915 234 : m_selectedSubAlg->SetReferencePathForRelativePaths(
1916 234 : m_referencePath);
1917 234 : m_selectedSubAlg->m_executionForStreamOutput =
1918 234 : m_executionForStreamOutput;
1919 234 : m_selectedSubAlg->m_calledFromCommandLine =
1920 234 : m_calledFromCommandLine;
1921 234 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
1922 468 : std::vector<std::string>(args.begin() + 1, args.end()));
1923 234 : m_selectedSubAlg->PropagateSpecialActionTo(this);
1924 234 : 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 1836 : inConstructionValues;
1944 :
1945 1836 : std::vector<std::string> lArgs(args);
1946 918 : bool helpValueRequested = false;
1947 2649 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
1948 : {
1949 1817 : const auto &strArg = lArgs[i];
1950 1817 : GDALAlgorithmArg *arg = nullptr;
1951 1817 : std::string name;
1952 1817 : std::string value;
1953 1817 : bool hasValue = false;
1954 1817 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
1955 5 : helpValueRequested = true;
1956 1817 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
1957 : {
1958 1056 : const auto equalPos = strArg.find('=');
1959 2112 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
1960 1056 : : strArg;
1961 1056 : const std::string nameWithoutDash = name.substr(2);
1962 1056 : const auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
1963 1056 : 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 1033 : arg = iterArg->second;
1981 1033 : if (equalPos != std::string::npos)
1982 : {
1983 320 : hasValue = true;
1984 320 : value = strArg.substr(equalPos + 1);
1985 : }
1986 : }
1987 825 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
1988 64 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
1989 : {
1990 123 : for (size_t j = 1; j < strArg.size(); ++j)
1991 : {
1992 64 : name.clear();
1993 64 : name += strArg[j];
1994 64 : const auto iterArg = m_mapShortNameToArg.find(name);
1995 64 : 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 59 : arg = iterArg->second;
2028 59 : 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 59 : if (strArg.size() > 2)
2044 : {
2045 0 : lArgs.erase(lArgs.begin() + i);
2046 0 : continue;
2047 : }
2048 : }
2049 : else
2050 : {
2051 697 : ++i;
2052 697 : continue;
2053 : }
2054 1092 : CPLAssert(arg);
2055 :
2056 1092 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2057 : {
2058 201 : if (!hasValue)
2059 : {
2060 198 : hasValue = true;
2061 198 : value = "true";
2062 : }
2063 : }
2064 :
2065 1092 : if (!hasValue)
2066 : {
2067 574 : if (i + 1 == lArgs.size())
2068 : {
2069 21 : if (m_parseForAutoCompletion)
2070 : {
2071 20 : lArgs.erase(lArgs.begin() + i);
2072 20 : 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 553 : value = lArgs[i + 1];
2081 553 : lArgs.erase(lArgs.begin() + i + 1);
2082 : }
2083 :
2084 1071 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2085 37 : return false;
2086 :
2087 1034 : lArgs.erase(lArgs.begin() + i);
2088 : }
2089 :
2090 852 : if (m_specialActionRequested)
2091 : {
2092 22 : return true;
2093 : }
2094 :
2095 1309 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2096 : {
2097 1287 : for (auto &[arg, value] : inConstructionValues)
2098 : {
2099 492 : if (arg->GetType() == GAAT_STRING_LIST)
2100 : {
2101 148 : if (!arg->Set(std::get<std::vector<std::string>>(
2102 148 : inConstructionValues[arg])))
2103 : {
2104 22 : return false;
2105 : }
2106 : }
2107 344 : 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 306 : 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 241 : else if (arg->GetType() == GAAT_DATASET_LIST)
2124 : {
2125 241 : if (!arg->Set(
2126 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2127 241 : inConstructionValues[arg]))))
2128 : {
2129 1 : return false;
2130 : }
2131 : }
2132 : }
2133 795 : return true;
2134 830 : };
2135 :
2136 : // Process positional arguments that have not been set through their
2137 : // option name.
2138 830 : size_t i = 0;
2139 830 : size_t iCurPosArg = 0;
2140 :
2141 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2142 848 : 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 854 : !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 1467 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2191 : {
2192 647 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2193 650 : 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 647 : if (iCurPosArg == m_positionalArgs.size())
2201 : {
2202 1 : break;
2203 : }
2204 893 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2205 247 : arg->GetMinCount() != arg->GetMaxCount())
2206 : {
2207 52 : if (iCurPosArg == 0)
2208 : {
2209 49 : size_t nCountAtEnd = 0;
2210 89 : 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 47 : 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 98 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2258 : {
2259 52 : 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 594 : 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 593 : const size_t iMax = i + arg->GetMaxCount();
2297 1189 : for (; i < iMax; ++i)
2298 : {
2299 597 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2300 : inConstructionValues))
2301 : {
2302 1 : ProcessInConstructionValues();
2303 1 : return false;
2304 : }
2305 : }
2306 : }
2307 640 : ++iCurPosArg;
2308 : }
2309 :
2310 821 : if (i < lArgs.size())
2311 : {
2312 10 : ReportError(CE_Failure, CPLE_AppDefined,
2313 : "Positional values starting at '%s' are not expected.",
2314 10 : lArgs[i].c_str());
2315 10 : return false;
2316 : }
2317 :
2318 811 : if (!ProcessInConstructionValues())
2319 : {
2320 22 : return false;
2321 : }
2322 :
2323 : // Skip to first unset positional argument.
2324 1285 : while (iCurPosArg < m_positionalArgs.size() &&
2325 274 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2326 : {
2327 222 : ++iCurPosArg;
2328 : }
2329 : // Check if this positional argument is required.
2330 840 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2331 51 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2332 24 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2333 27 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2334 : {
2335 43 : ReportError(CE_Failure, CPLE_AppDefined,
2336 : "Positional arguments starting at '%s' have not been "
2337 : "specified.",
2338 43 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2339 43 : return false;
2340 : }
2341 :
2342 746 : if (m_calledFromCommandLine)
2343 : {
2344 1079 : for (auto &arg : m_args)
2345 : {
2346 1481 : if (arg->IsExplicitlySet() &&
2347 255 : ((arg->GetType() == GAAT_STRING &&
2348 252 : arg->Get<std::string>() == "?") ||
2349 247 : (arg->GetType() == GAAT_STRING_LIST &&
2350 58 : arg->Get<std::vector<std::string>>().size() == 1 &&
2351 29 : 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 741 : return m_skipValidationInParseCommandLine || ValidateArguments();
2390 : }
2391 :
2392 : /************************************************************************/
2393 : /* GDALAlgorithm::ReportError() */
2394 : /************************************************************************/
2395 :
2396 : //! @cond Doxygen_Suppress
2397 505 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2398 : const char *fmt, ...) const
2399 : {
2400 : va_list args;
2401 505 : va_start(args, fmt);
2402 505 : CPLError(eErrClass, err_no, "%s",
2403 505 : std::string(m_name)
2404 505 : .append(": ")
2405 1010 : .append(CPLString().vPrintf(fmt, args))
2406 : .c_str());
2407 505 : va_end(args);
2408 505 : }
2409 :
2410 : //! @endcond
2411 :
2412 : /************************************************************************/
2413 : /* GDALAlgorithm::ProcessDatasetArg() */
2414 : /************************************************************************/
2415 :
2416 3885 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2417 : GDALAlgorithm *algForOutput)
2418 : {
2419 3885 : bool ret = true;
2420 :
2421 3885 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2422 3885 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2423 3885 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2424 3885 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2425 : const bool overwrite =
2426 7127 : (arg->IsOutput() && overwriteArg &&
2427 7127 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2428 3885 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2429 7770 : auto &val = [arg]() -> GDALArgDatasetValue &
2430 : {
2431 3885 : if (arg->GetType() == GAAT_DATASET_LIST)
2432 1648 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2433 : else
2434 2237 : return arg->Get<GDALArgDatasetValue>();
2435 3885 : }();
2436 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2437 5940 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2438 5948 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2439 8 : !overwrite;
2440 3885 : 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 3882 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2448 : {
2449 1 : return false;
2450 : }
2451 6165 : else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2452 2284 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2453 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2454 : {
2455 763 : int flags = arg->GetDatasetType();
2456 763 : bool assignToOutputArg = false;
2457 :
2458 : // Check if input and output parameters point to the same
2459 : // filename (for vector datasets)
2460 1390 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2461 1390 : 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 763 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2485 706 : flags |= GDAL_OF_VERBOSE_ERROR;
2486 763 : if ((arg == outputArg || !outputArg) && update)
2487 60 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2488 :
2489 763 : const auto readOnlyArg = algForOutput->GetArg(GDAL_ARG_NAME_READ_ONLY);
2490 : const bool readOnly =
2491 797 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2492 34 : readOnlyArg->Get<bool>());
2493 763 : if (readOnly)
2494 10 : flags &= ~GDAL_OF_UPDATE;
2495 :
2496 1526 : CPLStringList aosOpenOptions;
2497 1526 : CPLStringList aosAllowedDrivers;
2498 763 : if (arg->IsInput())
2499 : {
2500 763 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2501 763 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2502 : aosOpenOptions =
2503 719 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2504 :
2505 763 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2506 763 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2507 : aosAllowedDrivers =
2508 692 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2509 : }
2510 :
2511 1526 : std::string osDatasetName = val.GetName();
2512 763 : if (!m_referencePath.empty())
2513 : {
2514 40 : osDatasetName = GDALDataset::BuildFilename(
2515 20 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2516 : }
2517 763 : 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 870 : 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 763 : GDALDataset::Open(osDatasetName.c_str(), flags,
2538 763 : aosAllowedDrivers.List(), aosOpenOptions.List());
2539 763 : if (poDS)
2540 : {
2541 733 : 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 733 : val.SetDatasetOpenedByAlgorithm();
2563 733 : val.Set(poDS);
2564 733 : poDS->ReleaseRef();
2565 : }
2566 : else
2567 : {
2568 30 : ret = false;
2569 : }
2570 : }
2571 3124 : 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 3884 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2585 : {
2586 1521 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2587 : const bool hasAppendArg =
2588 1521 : appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2589 1521 : const bool append = (hasAppendArg && appendArg->Get<bool>());
2590 1521 : 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 1515 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2595 4543 : if (!(outputFormatArg &&
2596 1514 : outputFormatArg->GetType() == GAAT_STRING &&
2597 1514 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2598 888 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2599 638 : "stream") ||
2600 638 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2601 : "Memory"))))
2602 : {
2603 639 : const char *pszType = "";
2604 639 : GDALDriver *poDriver = nullptr;
2605 1255 : if (!val.GetName().empty() &&
2606 616 : 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 3859 : return ret;
2651 : }
2652 :
2653 : /************************************************************************/
2654 : /* GDALAlgorithm::ValidateArguments() */
2655 : /************************************************************************/
2656 :
2657 3035 : bool GDALAlgorithm::ValidateArguments()
2658 : {
2659 3035 : if (m_selectedSubAlg)
2660 3 : return m_selectedSubAlg->ValidateArguments();
2661 :
2662 3032 : 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 3031 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2668 3031 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2669 1925 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
2670 1063 : !outputArg->IsExplicitlySet() &&
2671 97 : outputFormatArg->GetType() == GAAT_STRING &&
2672 97 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2673 140 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
2674 5053 : outputArg->GetType() == GAAT_DATASET &&
2675 97 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
2676 : {
2677 97 : outputArg->Get<GDALArgDatasetValue>().Set("");
2678 : }
2679 :
2680 : // The method may emit several errors if several constraints are not met.
2681 3031 : bool ret = true;
2682 3031 : std::map<std::string, std::string> mutualExclusionGroupUsed;
2683 54781 : for (auto &arg : m_args)
2684 : {
2685 : // Check mutually exclusive arguments
2686 51750 : if (arg->IsExplicitlySet())
2687 : {
2688 8433 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
2689 8433 : if (!mutualExclusionGroup.empty())
2690 : {
2691 : auto oIter =
2692 432 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
2693 432 : 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 424 : mutualExclusionGroupUsed[mutualExclusionGroup] =
2704 848 : arg->GetName();
2705 : }
2706 : }
2707 : }
2708 :
2709 51761 : 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 51739 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
2718 : {
2719 2237 : if (!ProcessDatasetArg(arg.get(), this))
2720 52 : ret = false;
2721 : }
2722 55698 : else if (arg->IsExplicitlySet() &&
2723 6196 : GDALAlgorithmArgTypeIsList(arg->GetType()))
2724 : {
2725 2655 : int valueCount = 0;
2726 2655 : if (arg->GetType() == GAAT_STRING_LIST)
2727 : {
2728 497 : valueCount = static_cast<int>(
2729 497 : arg->Get<std::vector<std::string>>().size());
2730 : }
2731 2158 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2732 : {
2733 108 : valueCount =
2734 108 : static_cast<int>(arg->Get<std::vector<int>>().size());
2735 : }
2736 2050 : else if (arg->GetType() == GAAT_REAL_LIST)
2737 : {
2738 239 : valueCount =
2739 239 : static_cast<int>(arg->Get<std::vector<double>>().size());
2740 : }
2741 1811 : else if (arg->GetType() == GAAT_DATASET_LIST)
2742 : {
2743 1811 : valueCount = static_cast<int>(
2744 1811 : arg->Get<std::vector<GDALArgDatasetValue>>().size());
2745 : }
2746 :
2747 3723 : if (valueCount != arg->GetMinCount() &&
2748 1068 : 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 2653 : 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 2651 : 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 53561 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
2781 1811 : arg->AutoOpenDataset())
2782 : {
2783 1566 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
2784 1566 : if (listVal.size() == 1)
2785 : {
2786 1531 : if (!ProcessDatasetArg(arg.get(), this))
2787 5 : ret = false;
2788 : }
2789 : else
2790 : {
2791 105 : for (auto &val : listVal)
2792 : {
2793 70 : 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 70 : else if (!val.GetDatasetRef())
2802 : {
2803 : int flags =
2804 8 : arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
2805 :
2806 16 : CPLStringList aosOpenOptions;
2807 16 : CPLStringList aosAllowedDrivers;
2808 8 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
2809 : {
2810 : const auto ooArg =
2811 8 : GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2812 8 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2813 : {
2814 8 : aosOpenOptions = CPLStringList(
2815 8 : ooArg->Get<std::vector<std::string>>());
2816 : }
2817 :
2818 : const auto ifArg =
2819 8 : GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2820 8 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2821 : {
2822 8 : aosAllowedDrivers = CPLStringList(
2823 8 : ifArg->Get<std::vector<std::string>>());
2824 : }
2825 :
2826 8 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
2827 8 : if (updateArg &&
2828 16 : updateArg->GetType() == GAAT_BOOLEAN &&
2829 8 : updateArg->Get<bool>())
2830 : {
2831 0 : flags |= GDAL_OF_UPDATE;
2832 : }
2833 : }
2834 :
2835 : auto poDS = std::unique_ptr<GDALDataset>(
2836 8 : GDALDataset::Open(val.GetName().c_str(), flags,
2837 8 : aosAllowedDrivers.List(),
2838 24 : aosOpenOptions.List()));
2839 8 : if (poDS)
2840 : {
2841 8 : val.Set(std::move(poDS));
2842 : }
2843 : else
2844 : {
2845 0 : ret = false;
2846 : }
2847 : }
2848 : }
2849 : }
2850 : }
2851 : }
2852 :
2853 9472 : for (const auto &f : m_validationActions)
2854 : {
2855 6441 : if (!f())
2856 34 : ret = false;
2857 : }
2858 :
2859 3031 : return ret;
2860 : }
2861 :
2862 : /************************************************************************/
2863 : /* GDALAlgorithm::InstantiateSubAlgorithm */
2864 : /************************************************************************/
2865 :
2866 : std::unique_ptr<GDALAlgorithm>
2867 2463 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
2868 : bool suggestionAllowed) const
2869 : {
2870 2463 : auto ret = m_subAlgRegistry.Instantiate(name);
2871 4926 : auto childCallPath = m_callPath;
2872 2463 : childCallPath.push_back(name);
2873 2463 : if (!ret)
2874 : {
2875 142 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
2876 142 : .InstantiateDeclaredSubAlgorithm(childCallPath);
2877 : }
2878 2463 : if (ret)
2879 : {
2880 2374 : 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 4926 : return ret;
2909 : }
2910 :
2911 : /************************************************************************/
2912 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
2913 : /************************************************************************/
2914 :
2915 : std::string
2916 9622 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
2917 : {
2918 9622 : if (osName.size() >= 3)
2919 : {
2920 9619 : std::string bestCandidate;
2921 9619 : size_t bestDistance = std::numeric_limits<size_t>::max();
2922 189060 : for (const auto &[key, value] : m_mapLongNameToArg)
2923 : {
2924 179441 : CPL_IGNORE_RET_VAL(value);
2925 179441 : const size_t distance = CPLLevenshteinDistance(
2926 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
2927 179441 : if (distance < bestDistance)
2928 : {
2929 22338 : bestCandidate = key;
2930 22338 : bestDistance = distance;
2931 : }
2932 157103 : else if (distance == bestDistance)
2933 : {
2934 26788 : bestCandidate.clear();
2935 : }
2936 : }
2937 14281 : if (!bestCandidate.empty() &&
2938 4662 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
2939 : {
2940 5 : return bestCandidate;
2941 : }
2942 : }
2943 9617 : return std::string();
2944 : }
2945 :
2946 : /************************************************************************/
2947 : /* GDALAlgorithm::GetArg() */
2948 : /************************************************************************/
2949 :
2950 48973 : const GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
2951 : bool suggestionAllowed) const
2952 : {
2953 48973 : const auto nPos = osName.find_first_not_of('-');
2954 48973 : if (nPos == std::string::npos)
2955 9 : return nullptr;
2956 97928 : const std::string osKey = osName.substr(nPos);
2957 : {
2958 48964 : const auto oIter = m_mapLongNameToArg.find(osKey);
2959 48964 : if (oIter != m_mapLongNameToArg.end())
2960 39304 : return oIter->second;
2961 : }
2962 : {
2963 9660 : const auto oIter = m_mapShortNameToArg.find(osKey);
2964 9660 : if (oIter != m_mapShortNameToArg.end())
2965 6 : return oIter->second;
2966 : }
2967 :
2968 9654 : if (suggestionAllowed)
2969 : {
2970 19190 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
2971 : ;
2972 9595 : 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 9654 : return nullptr;
2981 : }
2982 :
2983 : /************************************************************************/
2984 : /* GDALAlgorithm::AddAliasFor() */
2985 : /************************************************************************/
2986 :
2987 : //! @cond Doxygen_Suppress
2988 17601 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
2989 : const std::string &alias)
2990 : {
2991 17601 : 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 17600 : m_mapLongNameToArg[alias] = arg;
2999 : }
3000 17601 : }
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 5057 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3033 : {
3034 5057 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3035 : arg) == m_positionalArgs.end());
3036 5057 : m_positionalArgs.push_back(arg);
3037 5057 : }
3038 :
3039 : //! @endcond
3040 :
3041 : /************************************************************************/
3042 : /* GDALAlgorithm::HasSubAlgorithms() */
3043 : /************************************************************************/
3044 :
3045 3736 : bool GDALAlgorithm::HasSubAlgorithms() const
3046 : {
3047 3736 : if (!m_subAlgRegistry.empty())
3048 1597 : return true;
3049 2139 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3050 4278 : .GetDeclaredSubAlgorithmNames(m_callPath)
3051 2139 : .empty();
3052 : }
3053 :
3054 : /************************************************************************/
3055 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3056 : /************************************************************************/
3057 :
3058 444 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3059 : {
3060 444 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3061 444 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3062 888 : .GetDeclaredSubAlgorithmNames(m_callPath);
3063 444 : ret.insert(ret.end(), other.begin(), other.end());
3064 444 : if (!other.empty())
3065 32 : std::sort(ret.begin(), ret.end());
3066 888 : return ret;
3067 : }
3068 :
3069 : /************************************************************************/
3070 : /* GDALAlgorithm::AddArg() */
3071 : /************************************************************************/
3072 :
3073 : GDALInConstructionAlgorithmArg &
3074 78234 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3075 : {
3076 78234 : auto argRaw = arg.get();
3077 78234 : const auto &longName = argRaw->GetName();
3078 78234 : if (!longName.empty())
3079 : {
3080 78221 : 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 78221 : 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 78221 : 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 78221 : m_mapLongNameToArg[longName] = argRaw;
3098 : }
3099 78234 : const auto &shortName = argRaw->GetShortName();
3100 78234 : if (!shortName.empty())
3101 : {
3102 36466 : if (shortName.size() != 1 ||
3103 18233 : !((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 18233 : 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 18233 : m_mapShortNameToArg[shortName] = argRaw;
3117 : }
3118 78234 : m_args.emplace_back(std::move(arg));
3119 : return *(
3120 78234 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3121 : }
3122 :
3123 : GDALInConstructionAlgorithmArg &
3124 34266 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3125 : const std::string &helpMessage, bool *pValue)
3126 : {
3127 34266 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3128 : this,
3129 68532 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3130 68532 : pValue));
3131 : }
3132 :
3133 : GDALInConstructionAlgorithmArg &
3134 13228 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3135 : const std::string &helpMessage, std::string *pValue)
3136 : {
3137 13228 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3138 : this,
3139 26456 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3140 26456 : pValue));
3141 : }
3142 :
3143 : GDALInConstructionAlgorithmArg &
3144 2972 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3145 : const std::string &helpMessage, int *pValue)
3146 : {
3147 2972 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3148 : this,
3149 5944 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3150 5944 : pValue));
3151 : }
3152 :
3153 : GDALInConstructionAlgorithmArg &
3154 3085 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3155 : const std::string &helpMessage, double *pValue)
3156 : {
3157 3085 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3158 : this,
3159 6170 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3160 6170 : pValue));
3161 : }
3162 :
3163 : GDALInConstructionAlgorithmArg &
3164 2999 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3165 : const std::string &helpMessage,
3166 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3167 : {
3168 5998 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3169 : this,
3170 5998 : GDALAlgorithmArgDecl(longName, chShortName,
3171 : helpMessage, GAAT_DATASET),
3172 2999 : pValue))
3173 2999 : .SetDatasetType(type);
3174 2999 : pValue->SetOwnerArgument(&arg);
3175 2999 : return arg;
3176 : }
3177 :
3178 : GDALInConstructionAlgorithmArg &
3179 16643 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3180 : const std::string &helpMessage,
3181 : std::vector<std::string> *pValue)
3182 : {
3183 16643 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3184 : this,
3185 33286 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3186 : GAAT_STRING_LIST),
3187 33286 : pValue));
3188 : }
3189 :
3190 : GDALInConstructionAlgorithmArg &
3191 609 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3192 : const std::string &helpMessage, std::vector<int> *pValue)
3193 : {
3194 609 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3195 : this,
3196 1218 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3197 : GAAT_INTEGER_LIST),
3198 1218 : pValue));
3199 : }
3200 :
3201 : GDALInConstructionAlgorithmArg &
3202 1564 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3203 : const std::string &helpMessage,
3204 : std::vector<double> *pValue)
3205 : {
3206 1564 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3207 : this,
3208 3128 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3209 : GAAT_REAL_LIST),
3210 3128 : pValue));
3211 : }
3212 :
3213 : GDALInConstructionAlgorithmArg &
3214 2868 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3215 : const std::string &helpMessage,
3216 : std::vector<GDALArgDatasetValue> *pValue,
3217 : GDALArgDatasetType type)
3218 : {
3219 5736 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3220 : this,
3221 5736 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3222 : GAAT_DATASET_LIST),
3223 2868 : pValue))
3224 5736 : .SetDatasetType(type);
3225 : }
3226 :
3227 : /************************************************************************/
3228 : /* MsgOrDefault() */
3229 : /************************************************************************/
3230 :
3231 25006 : inline const char *MsgOrDefault(const char *helpMessage,
3232 : const char *defaultMessage)
3233 : {
3234 25006 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3235 : }
3236 :
3237 : /************************************************************************/
3238 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3239 : /************************************************************************/
3240 :
3241 : /* static */
3242 877 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3243 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3244 : {
3245 : arg.SetAutoCompleteFunction(
3246 2376 : [type](const std::string ¤tValue) -> std::vector<std::string>
3247 : {
3248 14 : std::vector<std::string> oRet;
3249 :
3250 : {
3251 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3252 : VSIStatBufL sStat;
3253 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3254 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3255 : {
3256 0 : return oRet;
3257 : }
3258 : }
3259 :
3260 7 : auto poDM = GetGDALDriverManager();
3261 14 : std::set<std::string> oExtensions;
3262 7 : if (type)
3263 : {
3264 1326 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3265 : {
3266 1320 : auto poDriver = poDM->GetDriver(i);
3267 3740 : if (((type & GDAL_OF_RASTER) != 0 &&
3268 1100 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3269 565 : ((type & GDAL_OF_VECTOR) != 0 &&
3270 2772 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3271 477 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3272 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3273 : {
3274 : const char *pszExtensions =
3275 843 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3276 843 : if (pszExtensions)
3277 : {
3278 : const CPLStringList aosExts(
3279 1102 : CSLTokenizeString2(pszExtensions, " ", 0));
3280 1246 : for (const char *pszExt : cpl::Iterate(aosExts))
3281 695 : oExtensions.insert(CPLString(pszExt).tolower());
3282 : }
3283 : }
3284 : }
3285 : }
3286 :
3287 14 : std::string osDir;
3288 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3289 14 : std::string osPrefix;
3290 7 : 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 6 : if (osDir.empty())
3306 : {
3307 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3308 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3309 0 : osDir = std::move(osPrefix);
3310 : }
3311 :
3312 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3313 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3314 6 : if (currentValue.empty())
3315 1 : osDir.clear();
3316 : const std::string currentFilename =
3317 12 : CPLGetFilename(currentValue.c_str());
3318 6 : if (psDir)
3319 : {
3320 392 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3321 : {
3322 387 : if ((currentFilename.empty() ||
3323 193 : STARTS_WITH(psEntry->pszName,
3324 195 : currentFilename.c_str())) &&
3325 195 : strcmp(psEntry->pszName, ".") != 0 &&
3326 1163 : strcmp(psEntry->pszName, "..") != 0 &&
3327 195 : (oExtensions.empty() ||
3328 194 : !strstr(psEntry->pszName, ".aux.xml")))
3329 : {
3330 774 : if (oExtensions.empty() ||
3331 193 : cpl::contains(
3332 : oExtensions,
3333 387 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3334 580 : .tolower()) ||
3335 164 : VSI_ISDIR(psEntry->nMode))
3336 : {
3337 68 : std::string osVal;
3338 34 : if (osDir.empty() || osDir == ".")
3339 4 : osVal = psEntry->pszName;
3340 : else
3341 60 : osVal = CPLFormFilenameSafe(
3342 60 : osDir.c_str(), psEntry->pszName, nullptr);
3343 34 : if (VSI_ISDIR(psEntry->nMode))
3344 4 : osVal += osSep;
3345 34 : oRet.push_back(std::move(osVal));
3346 : }
3347 : }
3348 387 : }
3349 5 : VSICloseDir(psDir);
3350 : }
3351 6 : return oRet;
3352 877 : });
3353 877 : }
3354 :
3355 : /************************************************************************/
3356 : /* GDALAlgorithm::AddInputDatasetArg() */
3357 : /************************************************************************/
3358 :
3359 532 : 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 532 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3368 1064 : pValue, type);
3369 532 : if (positionalAndRequired)
3370 477 : arg.SetPositional().SetRequired();
3371 :
3372 532 : SetAutoCompleteFunctionForFilename(arg, type);
3373 :
3374 532 : AddValidationAction(
3375 459 : [pValue]()
3376 : {
3377 458 : if (pValue->GetName() == "-")
3378 1 : pValue->Set("/vsistdin/");
3379 458 : return true;
3380 : });
3381 :
3382 532 : return arg;
3383 : }
3384 :
3385 : /************************************************************************/
3386 : /* GDALAlgorithm::AddInputDatasetArg() */
3387 : /************************************************************************/
3388 :
3389 2801 : 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 2801 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3398 5602 : pValue, type);
3399 2801 : if (positionalAndRequired)
3400 1674 : arg.SetPositional().SetRequired();
3401 :
3402 2801 : AddValidationAction(
3403 2052 : [pValue]()
3404 : {
3405 3991 : for (auto &val : *pValue)
3406 : {
3407 1939 : if (val.GetName() == "-")
3408 1 : val.Set("/vsistdin/");
3409 : }
3410 2052 : return true;
3411 : });
3412 2801 : return arg;
3413 : }
3414 :
3415 : /************************************************************************/
3416 : /* GDALAlgorithm::AddOutputDatasetArg() */
3417 : /************************************************************************/
3418 :
3419 2103 : 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 2103 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3429 6309 : pValue, type)
3430 2103 : .SetIsInput(true)
3431 2103 : .SetIsOutput(true)
3432 2103 : .SetDatasetInputFlags(GADV_NAME)
3433 2103 : .SetDatasetOutputFlags(GADV_OBJECT);
3434 2103 : if (positionalAndRequired)
3435 1423 : arg.SetPositional().SetRequired();
3436 :
3437 2103 : AddValidationAction(
3438 6365 : [this, &arg, pValue]()
3439 : {
3440 1756 : if (pValue->GetName() == "-")
3441 1 : pValue->Set("/vsistdout/");
3442 :
3443 1756 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3444 1745 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
3445 2685 : (!outputFormatArg->IsExplicitlySet() ||
3446 4441 : outputFormatArg->Get<std::string>().empty()) &&
3447 805 : arg.IsExplicitlySet())
3448 : {
3449 : const auto vrtCompatible =
3450 679 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3451 136 : if (vrtCompatible && !vrtCompatible->empty() &&
3452 815 : vrtCompatible->front() == "false" &&
3453 747 : 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 673 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
3468 1323 : EQUAL(pValue->GetName()
3469 : .substr(pValue->GetName().size() -
3470 : strlen(".gdalg.json"))
3471 : .c_str(),
3472 1996 : ".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 1750 : return true;
3482 : });
3483 :
3484 2103 : return arg;
3485 : }
3486 :
3487 : /************************************************************************/
3488 : /* GDALAlgorithm::AddOverwriteArg() */
3489 : /************************************************************************/
3490 :
3491 : GDALInConstructionAlgorithmArg &
3492 2087 : 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 4174 : pValue)
3499 4174 : .SetDefault(false);
3500 : }
3501 :
3502 : /************************************************************************/
3503 : /* GDALAlgorithm::AddOverwriteLayerArg() */
3504 : /************************************************************************/
3505 :
3506 : GDALInConstructionAlgorithmArg &
3507 678 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
3508 : {
3509 678 : AddValidationAction(
3510 607 : [this]
3511 : {
3512 606 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3513 606 : 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 605 : return true;
3521 : });
3522 : return AddArg(GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
3523 : MsgOrDefault(
3524 : helpMessage,
3525 : _("Whether overwriting existing output is allowed")),
3526 1356 : pValue)
3527 678 : .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 1368 : });
3537 : }
3538 :
3539 : /************************************************************************/
3540 : /* GDALAlgorithm::AddUpdateArg() */
3541 : /************************************************************************/
3542 :
3543 : GDALInConstructionAlgorithmArg &
3544 787 : 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 1574 : pValue)
3551 1574 : .SetDefault(false);
3552 : }
3553 :
3554 : /************************************************************************/
3555 : /* GDALAlgorithm::AddAppendLayerArg() */
3556 : /************************************************************************/
3557 :
3558 : GDALInConstructionAlgorithmArg &
3559 634 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
3560 : {
3561 634 : AddValidationAction(
3562 577 : [this]
3563 : {
3564 576 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3565 576 : 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 575 : return true;
3573 : });
3574 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
3575 : MsgOrDefault(
3576 : helpMessage,
3577 : _("Whether appending to existing layer is allowed")),
3578 1268 : pValue)
3579 634 : .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 1282 : });
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 2301 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
3724 : const char *helpMessage)
3725 : {
3726 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
3727 4602 : MsgOrDefault(helpMessage, _("Open options")), pValue)
3728 4602 : .AddAlias("oo")
3729 4602 : .SetMetaVar("<KEY>=<VALUE>")
3730 2301 : .SetPackedValuesAllowed(false)
3731 2301 : .SetCategory(GAAC_ADVANCED);
3732 :
3733 2 : arg.AddValidationAction([this, &arg]()
3734 2303 : { 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 2 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
3745 0 : 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 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
3771 : {
3772 1 : auto poDM = GetGDALDriverManager();
3773 1 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
3774 1 : const auto &osDSName = datasetValue.GetName();
3775 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
3776 1 : if (!osExt.empty())
3777 : {
3778 1 : std::set<std::string> oVisitedExtensions;
3779 221 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3780 : {
3781 220 : auto poDriver = poDM->GetDriver(i);
3782 660 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
3783 220 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3784 69 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
3785 440 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3786 69 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3787 0 : poDriver->GetMetadataItem(
3788 0 : GDAL_DCAP_MULTIDIM_RASTER)))
3789 : {
3790 : const char *pszExtensions =
3791 151 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3792 151 : if (pszExtensions)
3793 : {
3794 : const CPLStringList aosExts(
3795 98 : CSLTokenizeString2(pszExtensions, " ", 0));
3796 216 : for (const char *pszExt : cpl::Iterate(aosExts))
3797 : {
3798 122 : if (EQUAL(pszExt, osExt.c_str()) &&
3799 3 : !cpl::contains(oVisitedExtensions,
3800 : pszExt))
3801 : {
3802 1 : oVisitedExtensions.insert(pszExt);
3803 1 : if (AddOptionsSuggestions(
3804 : poDriver->GetMetadataItem(
3805 1 : GDAL_DMD_OPENOPTIONLIST),
3806 : datasetType, currentValue,
3807 : oRet))
3808 : {
3809 0 : return oRet;
3810 : }
3811 1 : break;
3812 : }
3813 : }
3814 : }
3815 : }
3816 : }
3817 : }
3818 : }
3819 :
3820 1 : return oRet;
3821 2301 : });
3822 :
3823 2301 : return arg;
3824 : }
3825 :
3826 : /************************************************************************/
3827 : /* ValidateFormat() */
3828 : /************************************************************************/
3829 :
3830 1083 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
3831 : bool bStreamAllowed,
3832 : bool bGDALGAllowed) const
3833 : {
3834 1083 : if (arg.GetChoices().empty())
3835 : {
3836 : const auto Validate =
3837 2519 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
3838 : {
3839 1044 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
3840 310 : return true;
3841 :
3842 736 : if (EQUAL(val.c_str(), "GDALG") &&
3843 2 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
3844 : {
3845 1 : if (bGDALGAllowed)
3846 : {
3847 1 : return true;
3848 : }
3849 : else
3850 : {
3851 0 : ReportError(CE_Failure, CPLE_NotSupported,
3852 : "GDALG output is not supported.");
3853 0 : return false;
3854 : }
3855 : }
3856 :
3857 : const auto vrtCompatible =
3858 733 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3859 150 : if (vrtCompatible && !vrtCompatible->empty() &&
3860 883 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
3861 : {
3862 7 : ReportError(CE_Failure, CPLE_NotSupported,
3863 : "VRT output is not supported.%s",
3864 : bGDALGAllowed
3865 : ? " Consider using the GDALG driver instead "
3866 : "(files with .gdalg.json extension)."
3867 : : "");
3868 7 : return false;
3869 : }
3870 :
3871 726 : auto hDriver = GDALGetDriverByName(val.c_str());
3872 726 : if (!hDriver)
3873 : {
3874 : auto poMissingDriver =
3875 2 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
3876 2 : if (poMissingDriver)
3877 : {
3878 : const std::string msg =
3879 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
3880 0 : ReportError(CE_Failure, CPLE_AppDefined,
3881 : "Invalid value for argument '%s'. Driver '%s' "
3882 : "not found but it known. However plugin %s",
3883 0 : arg.GetName().c_str(), val.c_str(),
3884 : msg.c_str());
3885 : }
3886 : else
3887 : {
3888 4 : ReportError(CE_Failure, CPLE_AppDefined,
3889 : "Invalid value for argument '%s'. Driver '%s' "
3890 : "does not exist.",
3891 2 : arg.GetName().c_str(), val.c_str());
3892 : }
3893 2 : return false;
3894 : }
3895 :
3896 724 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
3897 724 : if (caps)
3898 : {
3899 2166 : for (const std::string &cap : *caps)
3900 : {
3901 : const char *pszVal =
3902 1452 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
3903 1452 : if (!(pszVal && pszVal[0]))
3904 : {
3905 584 : if (cap == GDAL_DCAP_CREATECOPY &&
3906 0 : std::find(caps->begin(), caps->end(),
3907 291 : GDAL_DCAP_RASTER) != caps->end() &&
3908 291 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
3909 584 : nullptr) &&
3910 291 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
3911 : nullptr))
3912 : {
3913 : // if it supports Create, it supports CreateCopy
3914 : }
3915 2 : else if (cap == GDAL_DMD_EXTENSIONS)
3916 : {
3917 2 : ReportError(
3918 : CE_Failure, CPLE_AppDefined,
3919 : "Invalid value for argument '%s'. Driver '%s' "
3920 : "does "
3921 : "not advertise any file format extension.",
3922 1 : arg.GetName().c_str(), val.c_str());
3923 2 : return false;
3924 : }
3925 : else
3926 : {
3927 2 : ReportError(
3928 : CE_Failure, CPLE_AppDefined,
3929 : "Invalid value for argument '%s'. Driver '%s' "
3930 : "does "
3931 : "not expose the required '%s' capability.",
3932 1 : arg.GetName().c_str(), val.c_str(),
3933 : cap.c_str());
3934 1 : return false;
3935 : }
3936 : }
3937 : }
3938 : }
3939 722 : return true;
3940 1044 : };
3941 :
3942 1044 : if (arg.GetType() == GAAT_STRING)
3943 : {
3944 1041 : return Validate(arg.Get<std::string>());
3945 : }
3946 5 : else if (arg.GetType() == GAAT_STRING_LIST)
3947 : {
3948 8 : for (const auto &val : arg.Get<std::vector<std::string>>())
3949 : {
3950 5 : if (!Validate(val))
3951 2 : return false;
3952 : }
3953 : }
3954 : }
3955 :
3956 42 : return true;
3957 : }
3958 :
3959 : /************************************************************************/
3960 : /* FormatAutoCompleteFunction() */
3961 : /************************************************************************/
3962 :
3963 : /* static */
3964 4 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
3965 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
3966 : {
3967 4 : std::vector<std::string> res;
3968 4 : auto poDM = GetGDALDriverManager();
3969 4 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3970 4 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
3971 883 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3972 : {
3973 879 : auto poDriver = poDM->GetDriver(i);
3974 :
3975 0 : if (vrtCompatible && !vrtCompatible->empty() &&
3976 879 : vrtCompatible->front() == "false" &&
3977 0 : EQUAL(poDriver->GetDescription(), "VRT"))
3978 : {
3979 : // do nothing
3980 : }
3981 879 : else if (caps)
3982 : {
3983 879 : bool ok = true;
3984 1755 : for (const std::string &cap : *caps)
3985 : {
3986 1180 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
3987 : {
3988 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
3989 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
3990 : {
3991 0 : ok = false;
3992 0 : break;
3993 : }
3994 : }
3995 1180 : else if (const char *pszVal =
3996 1180 : poDriver->GetMetadataItem(cap.c_str());
3997 842 : pszVal && pszVal[0])
3998 : {
3999 : }
4000 534 : else if (cap == GDAL_DCAP_CREATECOPY &&
4001 0 : (std::find(caps->begin(), caps->end(),
4002 196 : GDAL_DCAP_RASTER) != caps->end() &&
4003 730 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4004 196 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4005 : {
4006 : // if it supports Create, it supports CreateCopy
4007 : }
4008 : else
4009 : {
4010 304 : ok = false;
4011 304 : break;
4012 : }
4013 : }
4014 879 : if (ok)
4015 : {
4016 575 : res.push_back(poDriver->GetDescription());
4017 : }
4018 : }
4019 : }
4020 4 : if (bGDALGAllowed)
4021 0 : res.push_back("GDALG");
4022 4 : return res;
4023 : }
4024 :
4025 : /************************************************************************/
4026 : /* GDALAlgorithm::AddInputFormatsArg() */
4027 : /************************************************************************/
4028 :
4029 : GDALInConstructionAlgorithmArg &
4030 2247 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4031 : const char *helpMessage)
4032 : {
4033 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4034 4494 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4035 4494 : .AddAlias("if")
4036 2247 : .SetCategory(GAAC_ADVANCED);
4037 5 : arg.AddValidationAction([this, &arg]()
4038 2252 : { return ValidateFormat(arg, false, false); });
4039 : arg.SetAutoCompleteFunction(
4040 0 : [&arg](const std::string &)
4041 2247 : { return FormatAutoCompleteFunction(arg, false, false); });
4042 2247 : return arg;
4043 : }
4044 :
4045 : /************************************************************************/
4046 : /* GDALAlgorithm::AddOutputFormatArg() */
4047 : /************************************************************************/
4048 :
4049 : GDALInConstructionAlgorithmArg &
4050 2420 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4051 : bool bGDALGAllowed, const char *helpMessage)
4052 : {
4053 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4054 : MsgOrDefault(helpMessage,
4055 : bGDALGAllowed
4056 : ? _("Output format (\"GDALG\" allowed)")
4057 : : _("Output format")),
4058 4840 : pValue)
4059 4840 : .AddAlias("of")
4060 2420 : .AddAlias("format");
4061 : arg.AddValidationAction(
4062 1076 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4063 3496 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4064 : arg.SetAutoCompleteFunction(
4065 2 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &) {
4066 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4067 2 : bGDALGAllowed);
4068 2420 : });
4069 2420 : return arg;
4070 : }
4071 :
4072 : /************************************************************************/
4073 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4074 : /************************************************************************/
4075 : GDALInConstructionAlgorithmArg &
4076 521 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4077 : const char *helpMessage)
4078 : {
4079 : auto &arg =
4080 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4081 1042 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4082 1042 : .AddAlias("ot")
4083 1042 : .AddAlias("datatype")
4084 1563 : .AddMetadataItem("type", {"GDALDataType"})
4085 : .SetChoices("Byte", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4086 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4087 521 : "Float32", "Float64", "CFloat32", "CFloat64");
4088 521 : return arg;
4089 : }
4090 :
4091 : /************************************************************************/
4092 : /* GDALAlgorithm::AddNodataArg() */
4093 : /************************************************************************/
4094 :
4095 : GDALInConstructionAlgorithmArg &
4096 218 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4097 : const std::string &optionName,
4098 : const char *helpMessage)
4099 : {
4100 : auto &arg = AddArg(
4101 : optionName, 0,
4102 : MsgOrDefault(helpMessage,
4103 : noneAllowed
4104 : ? _("Assign a specified nodata value to output bands "
4105 : "('none', numeric value, 'nan', 'inf', '-inf')")
4106 : : _("Assign a specified nodata value to output bands "
4107 : "(numeric value, 'nan', 'inf', '-inf')")),
4108 218 : pValue);
4109 : arg.AddValidationAction(
4110 107 : [this, pValue, noneAllowed, optionName]()
4111 : {
4112 23 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4113 : {
4114 20 : char *endptr = nullptr;
4115 20 : CPLStrtod(pValue->c_str(), &endptr);
4116 20 : if (endptr != pValue->c_str() + pValue->size())
4117 : {
4118 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4119 : "Value of '%s' should be %sa "
4120 : "numeric value, 'nan', 'inf' or '-inf'",
4121 : optionName.c_str(),
4122 : noneAllowed ? "'none', " : "");
4123 1 : return false;
4124 : }
4125 : }
4126 22 : return true;
4127 218 : });
4128 218 : return arg;
4129 : }
4130 :
4131 : /************************************************************************/
4132 : /* GDALAlgorithm::AddOutputStringArg() */
4133 : /************************************************************************/
4134 :
4135 : GDALInConstructionAlgorithmArg &
4136 2039 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4137 : {
4138 : return AddArg(
4139 : "output-string", 0,
4140 : MsgOrDefault(helpMessage,
4141 : _("Output string, in which the result is placed")),
4142 4078 : pValue)
4143 2039 : .SetHiddenForCLI()
4144 2039 : .SetIsInput(false)
4145 4078 : .SetIsOutput(true);
4146 : }
4147 :
4148 : /************************************************************************/
4149 : /* GDALAlgorithm::AddLayerNameArg() */
4150 : /************************************************************************/
4151 :
4152 : GDALInConstructionAlgorithmArg &
4153 123 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4154 : {
4155 : return AddArg("layer", 'l', MsgOrDefault(helpMessage, _("Layer name")),
4156 123 : pValue);
4157 : }
4158 :
4159 : /************************************************************************/
4160 : /* GDALAlgorithm::AddLayerNameArg() */
4161 : /************************************************************************/
4162 :
4163 : GDALInConstructionAlgorithmArg &
4164 184 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
4165 : const char *helpMessage)
4166 : {
4167 : return AddArg("layer", 'l', MsgOrDefault(helpMessage, _("Layer name")),
4168 184 : pValue);
4169 : }
4170 :
4171 : /************************************************************************/
4172 : /* GDALAlgorithm::AddGeometryTypeArg() */
4173 : /************************************************************************/
4174 :
4175 : GDALInConstructionAlgorithmArg &
4176 117 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
4177 : {
4178 : return AddArg("geometry-type", 0,
4179 234 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
4180 : .SetAutoCompleteFunction(
4181 2 : [](const std::string ¤tValue)
4182 : {
4183 2 : std::vector<std::string> oRet;
4184 34 : for (const char *type :
4185 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
4186 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
4187 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
4188 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
4189 36 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
4190 : {
4191 51 : if (currentValue.empty() ||
4192 17 : STARTS_WITH(type, currentValue.c_str()))
4193 : {
4194 18 : oRet.push_back(type);
4195 18 : oRet.push_back(std::string(type).append("Z"));
4196 18 : oRet.push_back(std::string(type).append("M"));
4197 18 : oRet.push_back(std::string(type).append("ZM"));
4198 : }
4199 : }
4200 2 : return oRet;
4201 234 : })
4202 : .AddValidationAction(
4203 25 : [this, pValue]()
4204 : {
4205 20 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
4206 23 : wkbUnknown &&
4207 3 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
4208 : {
4209 2 : ReportError(CE_Failure, CPLE_AppDefined,
4210 : "Invalid geometry type '%s'", pValue->c_str());
4211 2 : return false;
4212 : }
4213 18 : return true;
4214 234 : });
4215 : }
4216 :
4217 : /************************************************************************/
4218 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
4219 : /************************************************************************/
4220 :
4221 : /* static */
4222 537 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
4223 : GDALInConstructionAlgorithmArg &layerArg,
4224 : GDALInConstructionAlgorithmArg &datasetArg)
4225 : {
4226 537 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
4227 : datasetArg.GetType() == GAAT_DATASET_LIST);
4228 :
4229 : layerArg.SetAutoCompleteFunction(
4230 18 : [&datasetArg](const std::string ¤tValue)
4231 : {
4232 6 : std::vector<std::string> ret;
4233 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4234 6 : GDALArgDatasetValue *dsVal = nullptr;
4235 6 : if (datasetArg.GetType() == GAAT_DATASET)
4236 : {
4237 4 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
4238 : }
4239 : else
4240 : {
4241 2 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
4242 2 : if (val.size() == 1)
4243 : {
4244 2 : dsVal = &val[0];
4245 : }
4246 : }
4247 6 : if (dsVal && !dsVal->GetName().empty())
4248 : {
4249 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4250 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
4251 6 : if (poDS)
4252 : {
4253 12 : for (auto &&poLayer : poDS->GetLayers())
4254 : {
4255 6 : if (currentValue == poLayer->GetDescription())
4256 : {
4257 1 : ret.clear();
4258 1 : ret.push_back(poLayer->GetDescription());
4259 1 : break;
4260 : }
4261 5 : ret.push_back(poLayer->GetDescription());
4262 : }
4263 : }
4264 : }
4265 12 : return ret;
4266 537 : });
4267 537 : }
4268 :
4269 : /************************************************************************/
4270 : /* GDALAlgorithm::ValidateBandArg() */
4271 : /************************************************************************/
4272 :
4273 1654 : bool GDALAlgorithm::ValidateBandArg() const
4274 : {
4275 1654 : bool ret = true;
4276 1654 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
4277 1654 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT, false);
4278 780 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
4279 190 : (inputDatasetArg->GetType() == GAAT_DATASET ||
4280 2428 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
4281 98 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
4282 : {
4283 42 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
4284 : {
4285 38 : if (nBand > poDS->GetRasterCount())
4286 : {
4287 4 : ReportError(CE_Failure, CPLE_AppDefined,
4288 : "Value of 'band' should be greater or equal than "
4289 : "1 and less or equal than %d.",
4290 : poDS->GetRasterCount());
4291 4 : return false;
4292 : }
4293 34 : return true;
4294 41 : };
4295 :
4296 : const auto ValidateForOneDataset =
4297 96 : [&bandArg, &CheckBand](const GDALDataset *poDS)
4298 : {
4299 36 : bool l_ret = true;
4300 36 : if (bandArg->GetType() == GAAT_INTEGER)
4301 : {
4302 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
4303 : }
4304 12 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
4305 : {
4306 24 : for (int nBand : bandArg->Get<std::vector<int>>())
4307 : {
4308 14 : l_ret = l_ret && CheckBand(poDS, nBand);
4309 : }
4310 : }
4311 36 : return l_ret;
4312 41 : };
4313 :
4314 41 : if (inputDatasetArg->GetType() == GAAT_DATASET)
4315 : {
4316 : auto poDS =
4317 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
4318 6 : if (poDS && !ValidateForOneDataset(poDS))
4319 2 : ret = false;
4320 : }
4321 : else
4322 : {
4323 35 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
4324 34 : for (auto &datasetValue :
4325 103 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
4326 : {
4327 34 : auto poDS = datasetValue.GetDatasetRef();
4328 34 : if (poDS && !ValidateForOneDataset(poDS))
4329 2 : ret = false;
4330 : }
4331 : }
4332 : }
4333 1654 : return ret;
4334 : }
4335 :
4336 : /************************************************************************/
4337 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
4338 : /************************************************************************/
4339 :
4340 1203 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
4341 : {
4342 1203 : return ValidateBandArg();
4343 : }
4344 :
4345 : /************************************************************************/
4346 : /* GDALAlgorithm::AddBandArg() */
4347 : /************************************************************************/
4348 :
4349 : GDALInConstructionAlgorithmArg &
4350 510 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
4351 : {
4352 758 : AddValidationAction([this]() { return ValidateBandArg(); });
4353 :
4354 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
4355 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
4356 1020 : pValue)
4357 : .AddValidationAction(
4358 17 : [pValue]()
4359 : {
4360 17 : if (*pValue <= 0)
4361 : {
4362 1 : CPLError(CE_Failure, CPLE_AppDefined,
4363 : "Value of 'band' should greater or equal to 1.");
4364 1 : return false;
4365 : }
4366 16 : return true;
4367 1020 : });
4368 : }
4369 :
4370 : /************************************************************************/
4371 : /* GDALAlgorithm::AddBandArg() */
4372 : /************************************************************************/
4373 :
4374 : GDALInConstructionAlgorithmArg &
4375 208 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
4376 : {
4377 411 : AddValidationAction([this]() { return ValidateBandArg(); });
4378 :
4379 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
4380 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
4381 416 : pValue)
4382 : .AddValidationAction(
4383 31 : [pValue]()
4384 : {
4385 108 : for (int val : *pValue)
4386 : {
4387 78 : if (val <= 0)
4388 : {
4389 1 : CPLError(CE_Failure, CPLE_AppDefined,
4390 : "Value of 'band' should greater or equal "
4391 : "to 1.");
4392 1 : return false;
4393 : }
4394 : }
4395 30 : return true;
4396 416 : });
4397 : }
4398 :
4399 : /************************************************************************/
4400 : /* ParseAndValidateKeyValue() */
4401 : /************************************************************************/
4402 :
4403 117 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
4404 : {
4405 118 : const auto Validate = [this, &arg](const std::string &val)
4406 : {
4407 115 : if (val.find('=') == std::string::npos)
4408 : {
4409 3 : ReportError(
4410 : CE_Failure, CPLE_AppDefined,
4411 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
4412 3 : arg.GetName().c_str());
4413 3 : return false;
4414 : }
4415 :
4416 112 : return true;
4417 117 : };
4418 :
4419 117 : if (arg.GetType() == GAAT_STRING)
4420 : {
4421 0 : return Validate(arg.Get<std::string>());
4422 : }
4423 117 : else if (arg.GetType() == GAAT_STRING_LIST)
4424 : {
4425 117 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
4426 117 : if (vals.size() == 1)
4427 : {
4428 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
4429 206 : std::vector<std::string> newVals;
4430 206 : std::string curToken;
4431 103 : bool canSplitOnComma = true;
4432 103 : char lastSep = 0;
4433 103 : bool inString = false;
4434 103 : bool equalFoundInLastToken = false;
4435 1359 : for (char c : vals[0])
4436 : {
4437 1258 : if (!inString && c == ',')
4438 : {
4439 6 : if (lastSep != '=' || !equalFoundInLastToken)
4440 : {
4441 1 : canSplitOnComma = false;
4442 1 : break;
4443 : }
4444 5 : lastSep = c;
4445 5 : newVals.push_back(curToken);
4446 5 : curToken.clear();
4447 5 : equalFoundInLastToken = false;
4448 : }
4449 1252 : else if (!inString && c == '=')
4450 : {
4451 103 : if (lastSep == '=')
4452 : {
4453 1 : canSplitOnComma = false;
4454 1 : break;
4455 : }
4456 102 : equalFoundInLastToken = true;
4457 102 : lastSep = c;
4458 102 : curToken += c;
4459 : }
4460 1149 : else if (c == '"')
4461 : {
4462 2 : inString = !inString;
4463 2 : curToken += c;
4464 : }
4465 : else
4466 : {
4467 1147 : curToken += c;
4468 : }
4469 : }
4470 103 : if (canSplitOnComma && !inString && equalFoundInLastToken)
4471 : {
4472 96 : if (!curToken.empty())
4473 96 : newVals.emplace_back(std::move(curToken));
4474 96 : vals = std::move(newVals);
4475 : }
4476 : }
4477 :
4478 229 : for (const auto &val : vals)
4479 : {
4480 115 : if (!Validate(val))
4481 3 : return false;
4482 : }
4483 : }
4484 :
4485 114 : return true;
4486 : }
4487 :
4488 : /************************************************************************/
4489 : /* IsGDALGOutput() */
4490 : /************************************************************************/
4491 :
4492 726 : bool GDALAlgorithm::IsGDALGOutput() const
4493 : {
4494 726 : bool isGDALGOutput = false;
4495 726 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4496 726 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4497 1448 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
4498 722 : outputArg->IsExplicitlySet())
4499 : {
4500 1438 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4501 719 : outputFormatArg->IsExplicitlySet())
4502 : {
4503 : const auto &val =
4504 451 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
4505 451 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
4506 : }
4507 : else
4508 : {
4509 : const auto &filename =
4510 268 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
4511 268 : isGDALGOutput =
4512 536 : filename.GetName().size() > strlen(".gdalg.json") &&
4513 268 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
4514 : strlen(".gdalg.json"),
4515 : ".gdalg.json");
4516 : }
4517 : }
4518 726 : return isGDALGOutput;
4519 : }
4520 :
4521 : /************************************************************************/
4522 : /* ProcessGDALGOutput() */
4523 : /************************************************************************/
4524 :
4525 1237 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
4526 : {
4527 1237 : if (!SupportsStreamedOutput())
4528 698 : return ProcessGDALGOutputRet::NOT_GDALG;
4529 :
4530 539 : if (IsGDALGOutput())
4531 : {
4532 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4533 : const auto &filename =
4534 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
4535 : VSIStatBufL sStat;
4536 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
4537 : {
4538 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
4539 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
4540 : {
4541 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
4542 : {
4543 0 : CPLError(CE_Failure, CPLE_AppDefined,
4544 : "File '%s' already exists. Specify the "
4545 : "--overwrite option to overwrite it.",
4546 : filename.c_str());
4547 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
4548 : }
4549 : }
4550 : }
4551 :
4552 22 : std::string osCommandLine;
4553 :
4554 44 : for (const auto &path : GDALAlgorithm::m_callPath)
4555 : {
4556 33 : if (!osCommandLine.empty())
4557 22 : osCommandLine += ' ';
4558 33 : osCommandLine += path;
4559 : }
4560 :
4561 224 : for (const auto &arg : GetArgs())
4562 : {
4563 239 : if (arg->IsExplicitlySet() &&
4564 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
4565 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
4566 254 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
4567 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
4568 : {
4569 14 : osCommandLine += ' ';
4570 14 : std::string strArg;
4571 14 : if (!arg->Serialize(strArg))
4572 : {
4573 0 : CPLError(CE_Failure, CPLE_AppDefined,
4574 : "Cannot serialize argument %s",
4575 0 : arg->GetName().c_str());
4576 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
4577 : }
4578 14 : osCommandLine += strArg;
4579 : }
4580 : }
4581 :
4582 11 : osCommandLine += " --output-format stream --output streamed_dataset";
4583 :
4584 11 : return SaveGDALG(filename, osCommandLine)
4585 11 : ? ProcessGDALGOutputRet::GDALG_OK
4586 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
4587 : }
4588 :
4589 528 : return ProcessGDALGOutputRet::NOT_GDALG;
4590 : }
4591 :
4592 : /************************************************************************/
4593 : /* GDALAlgorithm::SaveGDALG() */
4594 : /************************************************************************/
4595 :
4596 19 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
4597 : const std::string &commandLine)
4598 : {
4599 38 : CPLJSONDocument oDoc;
4600 19 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
4601 19 : oDoc.GetRoot().Add("command_line", commandLine);
4602 19 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
4603 :
4604 38 : return oDoc.Save(filename);
4605 : }
4606 :
4607 : /************************************************************************/
4608 : /* GDALAlgorithm::AddCreationOptionsArg() */
4609 : /************************************************************************/
4610 :
4611 : GDALInConstructionAlgorithmArg &
4612 2154 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
4613 : const char *helpMessage)
4614 : {
4615 : auto &arg = AddArg("creation-option", 0,
4616 4308 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
4617 4308 : .AddAlias("co")
4618 4308 : .SetMetaVar("<KEY>=<VALUE>")
4619 2154 : .SetPackedValuesAllowed(false);
4620 57 : arg.AddValidationAction([this, &arg]()
4621 2211 : { return ParseAndValidateKeyValue(arg); });
4622 :
4623 : arg.SetAutoCompleteFunction(
4624 45 : [this](const std::string ¤tValue)
4625 : {
4626 15 : std::vector<std::string> oRet;
4627 :
4628 15 : int datasetType =
4629 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4630 15 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4631 15 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
4632 0 : outputArg->GetType() == GAAT_DATASET_LIST))
4633 : {
4634 15 : datasetType = outputArg->GetDatasetType();
4635 : }
4636 :
4637 15 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4638 30 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
4639 15 : outputFormat->IsExplicitlySet())
4640 : {
4641 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4642 6 : outputFormat->Get<std::string>().c_str());
4643 6 : if (poDriver)
4644 : {
4645 6 : AddOptionsSuggestions(
4646 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
4647 : datasetType, currentValue, oRet);
4648 : }
4649 6 : return oRet;
4650 : }
4651 :
4652 9 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
4653 : {
4654 9 : auto poDM = GetGDALDriverManager();
4655 9 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
4656 9 : const auto &osDSName = datasetValue.GetName();
4657 9 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4658 9 : if (!osExt.empty())
4659 : {
4660 9 : std::set<std::string> oVisitedExtensions;
4661 477 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4662 : {
4663 475 : auto poDriver = poDM->GetDriver(i);
4664 1425 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4665 475 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4666 138 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4667 950 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4668 138 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4669 0 : poDriver->GetMetadataItem(
4670 0 : GDAL_DCAP_MULTIDIM_RASTER)))
4671 : {
4672 : const char *pszExtensions =
4673 337 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4674 337 : if (pszExtensions)
4675 : {
4676 : const CPLStringList aosExts(
4677 217 : CSLTokenizeString2(pszExtensions, " ", 0));
4678 481 : for (const char *pszExt : cpl::Iterate(aosExts))
4679 : {
4680 286 : if (EQUAL(pszExt, osExt.c_str()) &&
4681 13 : !cpl::contains(oVisitedExtensions,
4682 : pszExt))
4683 : {
4684 9 : oVisitedExtensions.insert(pszExt);
4685 9 : if (AddOptionsSuggestions(
4686 : poDriver->GetMetadataItem(
4687 9 : GDAL_DMD_CREATIONOPTIONLIST),
4688 : datasetType, currentValue,
4689 : oRet))
4690 : {
4691 7 : return oRet;
4692 : }
4693 2 : break;
4694 : }
4695 : }
4696 : }
4697 : }
4698 : }
4699 : }
4700 : }
4701 :
4702 2 : return oRet;
4703 2154 : });
4704 :
4705 2154 : return arg;
4706 : }
4707 :
4708 : /************************************************************************/
4709 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
4710 : /************************************************************************/
4711 :
4712 : GDALInConstructionAlgorithmArg &
4713 737 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
4714 : const char *helpMessage)
4715 : {
4716 : auto &arg =
4717 : AddArg("layer-creation-option", 0,
4718 1474 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
4719 1474 : .AddAlias("lco")
4720 1474 : .SetMetaVar("<KEY>=<VALUE>")
4721 737 : .SetPackedValuesAllowed(false);
4722 10 : arg.AddValidationAction([this, &arg]()
4723 747 : { return ParseAndValidateKeyValue(arg); });
4724 :
4725 : arg.SetAutoCompleteFunction(
4726 5 : [this](const std::string ¤tValue)
4727 : {
4728 2 : std::vector<std::string> oRet;
4729 :
4730 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4731 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
4732 2 : outputFormat->IsExplicitlySet())
4733 : {
4734 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4735 1 : outputFormat->Get<std::string>().c_str());
4736 1 : if (poDriver)
4737 : {
4738 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
4739 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
4740 : GDAL_OF_VECTOR, currentValue, oRet);
4741 : }
4742 1 : return oRet;
4743 : }
4744 :
4745 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
4746 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
4747 : {
4748 1 : auto poDM = GetGDALDriverManager();
4749 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
4750 1 : const auto &osDSName = datasetValue.GetName();
4751 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4752 1 : if (!osExt.empty())
4753 : {
4754 1 : std::set<std::string> oVisitedExtensions;
4755 221 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4756 : {
4757 220 : auto poDriver = poDM->GetDriver(i);
4758 220 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
4759 : {
4760 : const char *pszExtensions =
4761 88 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4762 88 : if (pszExtensions)
4763 : {
4764 : const CPLStringList aosExts(
4765 61 : CSLTokenizeString2(pszExtensions, " ", 0));
4766 154 : for (const char *pszExt : cpl::Iterate(aosExts))
4767 : {
4768 95 : if (EQUAL(pszExt, osExt.c_str()) &&
4769 1 : !cpl::contains(oVisitedExtensions,
4770 : pszExt))
4771 : {
4772 1 : oVisitedExtensions.insert(pszExt);
4773 1 : if (AddOptionsSuggestions(
4774 : poDriver->GetMetadataItem(
4775 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
4776 : GDAL_OF_VECTOR, currentValue,
4777 : oRet))
4778 : {
4779 0 : return oRet;
4780 : }
4781 1 : break;
4782 : }
4783 : }
4784 : }
4785 : }
4786 : }
4787 : }
4788 : }
4789 :
4790 1 : return oRet;
4791 737 : });
4792 :
4793 737 : return arg;
4794 : }
4795 :
4796 : /************************************************************************/
4797 : /* GDALAlgorithm::AddBBOXArg() */
4798 : /************************************************************************/
4799 :
4800 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
4801 : GDALInConstructionAlgorithmArg &
4802 577 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
4803 : {
4804 : auto &arg = AddArg("bbox", 0,
4805 : MsgOrDefault(helpMessage,
4806 : _("Bounding box as xmin,ymin,xmax,ymax")),
4807 1154 : pValue)
4808 577 : .SetRepeatedArgAllowed(false)
4809 577 : .SetMinCount(4)
4810 577 : .SetMaxCount(4)
4811 577 : .SetDisplayHintAboutRepetition(false);
4812 : arg.AddValidationAction(
4813 58 : [&arg]()
4814 : {
4815 58 : const auto &val = arg.Get<std::vector<double>>();
4816 58 : CPLAssert(val.size() == 4);
4817 58 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
4818 : {
4819 4 : CPLError(CE_Failure, CPLE_AppDefined,
4820 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
4821 : "xmin <= xmax and ymin <= ymax");
4822 4 : return false;
4823 : }
4824 54 : return true;
4825 577 : });
4826 577 : return arg;
4827 : }
4828 :
4829 : /************************************************************************/
4830 : /* GDALAlgorithm::AddActiveLayerArg() */
4831 : /************************************************************************/
4832 :
4833 : GDALInConstructionAlgorithmArg &
4834 412 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
4835 : {
4836 : return AddArg("active-layer", 0,
4837 : MsgOrDefault(helpMessage,
4838 : _("Set active layer (if not specified, all)")),
4839 412 : pValue);
4840 : }
4841 :
4842 : /************************************************************************/
4843 : /* GDALAlgorithm::AddNumThreadsArg() */
4844 : /************************************************************************/
4845 :
4846 : GDALInConstructionAlgorithmArg &
4847 272 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
4848 : const char *helpMessage)
4849 : {
4850 : auto &arg =
4851 : AddArg("num-threads", 'j',
4852 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
4853 272 : pStrValue);
4854 1029 : auto lambda = [this, &arg, pValue, pStrValue]
4855 : {
4856 : #ifdef DEBUG
4857 : const int nCPUCount = std::max(
4858 286 : 1, atoi(CPLGetConfigOption("GDAL_DEBUG_CPU_COUNT",
4859 286 : CPLSPrintf("%d", CPLGetNumCPUs()))));
4860 : #else
4861 : const int nCPUCount = std::max(1, CPLGetNumCPUs());
4862 : #endif
4863 286 : int nNumThreads = nCPUCount;
4864 : const char *pszThreads =
4865 286 : CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
4866 286 : if (pszThreads && !EQUAL(pszThreads, "ALL_CPUS"))
4867 : {
4868 29 : nNumThreads = std::clamp(atoi(pszThreads), 1, nNumThreads);
4869 : }
4870 286 : if (EQUAL(pStrValue->c_str(), "ALL_CPUS"))
4871 : {
4872 229 : *pValue = nNumThreads;
4873 229 : return true;
4874 : }
4875 : else
4876 : {
4877 57 : char *endptr = nullptr;
4878 57 : const auto res = std::strtol(pStrValue->c_str(), &endptr, 10);
4879 57 : if (endptr == pStrValue->c_str() + pStrValue->size() && res >= 0 &&
4880 : res <= INT_MAX)
4881 : {
4882 57 : *pValue = std::min(static_cast<int>(res), nNumThreads);
4883 57 : return true;
4884 : }
4885 0 : ReportError(CE_Failure, CPLE_IllegalArg,
4886 : "Invalid value for '%s' argument",
4887 0 : arg.GetName().c_str());
4888 0 : return false;
4889 : }
4890 272 : };
4891 272 : if (!pStrValue->empty())
4892 : {
4893 267 : arg.SetDefault(*pStrValue);
4894 267 : lambda();
4895 : }
4896 272 : arg.AddValidationAction(std::move(lambda));
4897 272 : return arg;
4898 : }
4899 :
4900 : /************************************************************************/
4901 : /* GDALAlgorithm::AddAbsolutePathArg() */
4902 : /************************************************************************/
4903 :
4904 : GDALInConstructionAlgorithmArg &
4905 218 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
4906 : {
4907 : return AddArg(
4908 : "absolute-path", 0,
4909 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
4910 : "should be stored as an absolute path")),
4911 218 : pValue);
4912 : }
4913 :
4914 : /************************************************************************/
4915 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
4916 : /************************************************************************/
4917 :
4918 : GDALInConstructionAlgorithmArg &
4919 63 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
4920 : const char *helpMessage)
4921 : {
4922 :
4923 : const auto pixelFunctionNames =
4924 63 : VRTDerivedRasterBand::GetPixelFunctionNames();
4925 : return AddArg(
4926 : "pixel-function", 0,
4927 : MsgOrDefault(
4928 : helpMessage,
4929 : _("Specify a pixel function to calculate output value from "
4930 : "overlapping inputs")),
4931 126 : pValue)
4932 126 : .SetChoices(pixelFunctionNames);
4933 : }
4934 :
4935 : /************************************************************************/
4936 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
4937 : /************************************************************************/
4938 :
4939 : GDALInConstructionAlgorithmArg &
4940 63 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
4941 : const char *helpMessage)
4942 : {
4943 : auto &pixelFunctionArgArg =
4944 : AddArg("pixel-function-arg", 0,
4945 : MsgOrDefault(
4946 : helpMessage,
4947 : _("Specify argument(s) to pass to the pixel function")),
4948 126 : pValue)
4949 126 : .SetMetaVar("<NAME>=<VALUE>")
4950 63 : .SetRepeatedArgAllowed(true);
4951 : pixelFunctionArgArg.AddValidationAction(
4952 3 : [this, &pixelFunctionArgArg]()
4953 66 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
4954 :
4955 : pixelFunctionArgArg.SetAutoCompleteFunction(
4956 12 : [this](const std::string ¤tValue)
4957 : {
4958 12 : std::string pixelFunction;
4959 6 : const auto pixelFunctionArg = GetArg("pixel-function");
4960 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
4961 : {
4962 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
4963 : }
4964 :
4965 6 : std::vector<std::string> ret;
4966 :
4967 6 : if (!pixelFunction.empty())
4968 : {
4969 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
4970 : pixelFunction.c_str());
4971 5 : if (!pair)
4972 : {
4973 1 : ret.push_back("**");
4974 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
4975 1 : ret.push_back(std::string("\xC2\xA0"
4976 : "Invalid pixel function name"));
4977 : }
4978 4 : else if (pair->second.find("Argument name=") ==
4979 : std::string::npos)
4980 : {
4981 1 : ret.push_back("**");
4982 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
4983 1 : ret.push_back(
4984 2 : std::string(
4985 : "\xC2\xA0"
4986 : "No pixel function arguments for pixel function '")
4987 1 : .append(pixelFunction)
4988 1 : .append("'"));
4989 : }
4990 : else
4991 : {
4992 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
4993 : ret);
4994 : }
4995 : }
4996 :
4997 12 : return ret;
4998 63 : });
4999 :
5000 63 : return pixelFunctionArgArg;
5001 : }
5002 :
5003 : /************************************************************************/
5004 : /* GDALAlgorithm::AddProgressArg() */
5005 : /************************************************************************/
5006 :
5007 1777 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddProgressArg()
5008 : {
5009 : return AddArg("progress", 0, _("Display progress bar"),
5010 3554 : &m_progressBarRequested)
5011 1777 : .SetOnlyForCLI()
5012 3554 : .SetCategory(GAAC_COMMON);
5013 : }
5014 :
5015 : /************************************************************************/
5016 : /* GDALAlgorithm::Run() */
5017 : /************************************************************************/
5018 :
5019 2162 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
5020 : {
5021 2162 : WarnIfDeprecated();
5022 :
5023 2162 : if (m_selectedSubAlg)
5024 : {
5025 219 : if (m_calledFromCommandLine)
5026 123 : m_selectedSubAlg->m_calledFromCommandLine = true;
5027 219 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
5028 : }
5029 :
5030 1943 : if (m_helpRequested || m_helpDocRequested)
5031 : {
5032 15 : if (m_calledFromCommandLine)
5033 15 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
5034 15 : return true;
5035 : }
5036 :
5037 1928 : if (m_JSONUsageRequested)
5038 : {
5039 3 : if (m_calledFromCommandLine)
5040 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
5041 3 : return true;
5042 : }
5043 :
5044 1925 : if (!ValidateArguments())
5045 56 : return false;
5046 :
5047 1869 : switch (ProcessGDALGOutput())
5048 : {
5049 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
5050 0 : return false;
5051 :
5052 11 : case ProcessGDALGOutputRet::GDALG_OK:
5053 11 : return true;
5054 :
5055 1858 : case ProcessGDALGOutputRet::NOT_GDALG:
5056 1858 : break;
5057 : }
5058 :
5059 1858 : if (m_executionForStreamOutput)
5060 : {
5061 47 : if (!CheckSafeForStreamOutput())
5062 : {
5063 4 : return false;
5064 : }
5065 : }
5066 :
5067 1854 : return RunImpl(pfnProgress, pProgressData);
5068 : }
5069 :
5070 : /************************************************************************/
5071 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
5072 : /************************************************************************/
5073 :
5074 27 : bool GDALAlgorithm::CheckSafeForStreamOutput()
5075 : {
5076 27 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5077 27 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
5078 : {
5079 27 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5080 27 : if (!EQUAL(val.c_str(), "stream"))
5081 : {
5082 : // For security reasons, to avoid that reading a .gdalg.json file
5083 : // writes a file on the file system.
5084 4 : ReportError(
5085 : CE_Failure, CPLE_NotSupported,
5086 : "in streamed execution, --format stream should be used");
5087 4 : return false;
5088 : }
5089 : }
5090 23 : return true;
5091 : }
5092 :
5093 : /************************************************************************/
5094 : /* GDALAlgorithm::Finalize() */
5095 : /************************************************************************/
5096 :
5097 737 : bool GDALAlgorithm::Finalize()
5098 : {
5099 737 : bool ret = true;
5100 737 : if (m_selectedSubAlg)
5101 129 : ret = m_selectedSubAlg->Finalize();
5102 :
5103 12085 : for (auto &arg : m_args)
5104 : {
5105 11348 : if (arg->GetType() == GAAT_DATASET)
5106 : {
5107 595 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
5108 : }
5109 10753 : else if (arg->GetType() == GAAT_DATASET_LIST)
5110 : {
5111 766 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
5112 : {
5113 351 : ret = ds.Close() && ret;
5114 : }
5115 : }
5116 : }
5117 737 : return ret;
5118 : }
5119 :
5120 : /************************************************************************/
5121 : /* GDALAlgorithm::GetArgNamesForCLI() */
5122 : /************************************************************************/
5123 :
5124 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
5125 447 : GDALAlgorithm::GetArgNamesForCLI() const
5126 : {
5127 894 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5128 :
5129 447 : size_t maxOptLen = 0;
5130 4835 : for (const auto &arg : m_args)
5131 : {
5132 4388 : if (arg->IsHidden() || arg->IsHiddenForCLI())
5133 748 : continue;
5134 3640 : std::string opt;
5135 3640 : bool addComma = false;
5136 3640 : if (!arg->GetShortName().empty())
5137 : {
5138 894 : opt += '-';
5139 894 : opt += arg->GetShortName();
5140 894 : addComma = true;
5141 : }
5142 3640 : for (char alias : arg->GetShortNameAliases())
5143 : {
5144 0 : if (addComma)
5145 0 : opt += ", ";
5146 0 : opt += "-";
5147 0 : opt += alias;
5148 0 : addComma = true;
5149 : }
5150 3970 : for (const std::string &alias : arg->GetAliases())
5151 : {
5152 330 : if (addComma)
5153 115 : opt += ", ";
5154 330 : opt += "--";
5155 330 : opt += alias;
5156 330 : addComma = true;
5157 : }
5158 3640 : if (!arg->GetName().empty())
5159 : {
5160 3640 : if (addComma)
5161 1109 : opt += ", ";
5162 3640 : opt += "--";
5163 3640 : opt += arg->GetName();
5164 : }
5165 3640 : const auto &metaVar = arg->GetMetaVar();
5166 3640 : if (!metaVar.empty())
5167 : {
5168 2327 : opt += ' ';
5169 2327 : if (metaVar.front() != '<')
5170 1646 : opt += '<';
5171 2327 : opt += metaVar;
5172 2327 : if (metaVar.back() != '>')
5173 1662 : opt += '>';
5174 : }
5175 3640 : maxOptLen = std::max(maxOptLen, opt.size());
5176 3640 : options.emplace_back(arg.get(), opt);
5177 : }
5178 :
5179 894 : return std::make_pair(std::move(options), maxOptLen);
5180 : }
5181 :
5182 : /************************************************************************/
5183 : /* GDALAlgorithm::GetUsageForCLI() */
5184 : /************************************************************************/
5185 :
5186 : std::string
5187 268 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
5188 : const UsageOptions &usageOptions) const
5189 : {
5190 268 : if (m_selectedSubAlg)
5191 6 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
5192 :
5193 524 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
5194 524 : std::string osPath;
5195 513 : for (const std::string &s : m_callPath)
5196 : {
5197 251 : if (!osPath.empty())
5198 32 : osPath += ' ';
5199 251 : osPath += s;
5200 : }
5201 262 : osRet += ' ';
5202 262 : osRet += osPath;
5203 :
5204 262 : bool hasNonPositionals = false;
5205 2795 : for (const auto &arg : m_args)
5206 : {
5207 2533 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
5208 1931 : hasNonPositionals = true;
5209 : }
5210 :
5211 262 : if (HasSubAlgorithms())
5212 : {
5213 5 : if (m_callPath.size() == 1)
5214 : {
5215 4 : osRet += " <COMMAND>";
5216 4 : if (hasNonPositionals)
5217 4 : osRet += " [OPTIONS]";
5218 4 : osRet += "\nwhere <COMMAND> is one of:\n";
5219 : }
5220 : else
5221 : {
5222 1 : osRet += " <SUBCOMMAND>";
5223 1 : if (hasNonPositionals)
5224 1 : osRet += " [OPTIONS]";
5225 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
5226 : }
5227 5 : size_t maxNameLen = 0;
5228 44 : for (const auto &subAlgName : GetSubAlgorithmNames())
5229 : {
5230 39 : maxNameLen = std::max(maxNameLen, subAlgName.size());
5231 : }
5232 44 : for (const auto &subAlgName : GetSubAlgorithmNames())
5233 : {
5234 78 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
5235 39 : if (subAlg && !subAlg->IsHidden())
5236 : {
5237 39 : const std::string &name(subAlg->GetName());
5238 39 : osRet += " - ";
5239 39 : osRet += name;
5240 39 : osRet += ": ";
5241 39 : osRet.append(maxNameLen - name.size(), ' ');
5242 39 : osRet += subAlg->GetDescription();
5243 39 : if (!subAlg->m_aliases.empty())
5244 : {
5245 0 : bool first = true;
5246 0 : for (const auto &alias : subAlg->GetAliases())
5247 : {
5248 0 : if (alias ==
5249 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
5250 0 : break;
5251 0 : if (first)
5252 0 : osRet += " (alias: ";
5253 : else
5254 0 : osRet += ", ";
5255 0 : osRet += alias;
5256 0 : first = false;
5257 : }
5258 0 : if (!first)
5259 : {
5260 0 : osRet += ')';
5261 : }
5262 : }
5263 39 : osRet += '\n';
5264 : }
5265 : }
5266 :
5267 5 : if (shortUsage && hasNonPositionals)
5268 : {
5269 2 : osRet += "\nTry '";
5270 2 : osRet += osPath;
5271 2 : osRet += " --help' for help.\n";
5272 : }
5273 : }
5274 : else
5275 : {
5276 257 : if (!m_args.empty())
5277 : {
5278 257 : if (hasNonPositionals)
5279 257 : osRet += " [OPTIONS]";
5280 362 : for (const auto *arg : m_positionalArgs)
5281 : {
5282 : const bool optional =
5283 119 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
5284 14 : arg->GetName() == "pipeline"));
5285 105 : osRet += ' ';
5286 105 : if (optional)
5287 16 : osRet += '[';
5288 105 : const std::string &metavar = arg->GetMetaVar();
5289 105 : if (!metavar.empty() && metavar[0] == '<')
5290 : {
5291 4 : osRet += metavar;
5292 : }
5293 : else
5294 : {
5295 101 : osRet += '<';
5296 101 : osRet += metavar;
5297 101 : osRet += '>';
5298 : }
5299 135 : if (arg->GetType() == GAAT_DATASET_LIST &&
5300 30 : arg->GetMaxCount() > 1)
5301 : {
5302 20 : osRet += "...";
5303 : }
5304 105 : if (optional)
5305 16 : osRet += ']';
5306 : }
5307 : }
5308 :
5309 257 : const size_t nLenFirstLine = osRet.size();
5310 257 : osRet += '\n';
5311 257 : if (usageOptions.isPipelineStep)
5312 : {
5313 191 : osRet.append(nLenFirstLine, '-');
5314 191 : osRet += '\n';
5315 : }
5316 :
5317 257 : if (shortUsage)
5318 : {
5319 6 : osRet += "Try '";
5320 6 : osRet += osPath;
5321 6 : osRet += " --help' for help.\n";
5322 6 : return osRet;
5323 : }
5324 :
5325 251 : osRet += '\n';
5326 251 : osRet += m_description;
5327 251 : osRet += '\n';
5328 : }
5329 :
5330 256 : if (!m_args.empty() && !shortUsage)
5331 : {
5332 508 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5333 : size_t maxOptLen;
5334 254 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
5335 254 : if (usageOptions.maxOptLen)
5336 189 : maxOptLen = usageOptions.maxOptLen;
5337 :
5338 : const auto OutputArg =
5339 1390 : [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
5340 12133 : const std::string &opt)
5341 : {
5342 1390 : osRet += " ";
5343 1390 : osRet += opt;
5344 1390 : osRet += " ";
5345 1390 : osRet.append(maxOptLen - opt.size(), ' ');
5346 1390 : osRet += arg->GetDescription();
5347 :
5348 1390 : const auto &choices = arg->GetChoices();
5349 1390 : if (!choices.empty())
5350 : {
5351 129 : osRet += ". ";
5352 129 : osRet += arg->GetMetaVar();
5353 129 : osRet += '=';
5354 129 : bool firstChoice = true;
5355 1017 : for (const auto &choice : choices)
5356 : {
5357 888 : if (!firstChoice)
5358 759 : osRet += '|';
5359 888 : osRet += choice;
5360 888 : firstChoice = false;
5361 : }
5362 : }
5363 :
5364 2749 : if (arg->GetType() == GAAT_DATASET ||
5365 1359 : arg->GetType() == GAAT_DATASET_LIST)
5366 : {
5367 62 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
5368 1 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
5369 : {
5370 1 : osRet += " (created by algorithm)";
5371 : }
5372 : }
5373 :
5374 1390 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
5375 : {
5376 121 : osRet += " (default: ";
5377 121 : osRet += arg->GetDefault<std::string>();
5378 121 : osRet += ')';
5379 : }
5380 1269 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
5381 : {
5382 26 : if (arg->GetDefault<bool>())
5383 0 : osRet += " (default: true)";
5384 : }
5385 1243 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
5386 : {
5387 63 : osRet += " (default: ";
5388 63 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
5389 63 : osRet += ')';
5390 : }
5391 1180 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
5392 : {
5393 37 : osRet += " (default: ";
5394 37 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
5395 37 : osRet += ')';
5396 : }
5397 1387 : else if (arg->GetType() == GAAT_STRING_LIST &&
5398 244 : arg->HasDefaultValue())
5399 : {
5400 : const auto &defaultVal =
5401 0 : arg->GetDefault<std::vector<std::string>>();
5402 0 : if (defaultVal.size() == 1)
5403 : {
5404 0 : osRet += " (default: ";
5405 0 : osRet += defaultVal[0];
5406 0 : osRet += ')';
5407 : }
5408 : }
5409 1159 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
5410 16 : arg->HasDefaultValue())
5411 : {
5412 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
5413 0 : if (defaultVal.size() == 1)
5414 : {
5415 0 : osRet += " (default: ";
5416 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
5417 0 : osRet += ')';
5418 : }
5419 : }
5420 1143 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
5421 : {
5422 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
5423 0 : if (defaultVal.size() == 1)
5424 : {
5425 0 : osRet += " (default: ";
5426 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
5427 0 : osRet += ')';
5428 : }
5429 : }
5430 :
5431 1390 : if (arg->GetDisplayHintAboutRepetition())
5432 : {
5433 1424 : if (arg->GetMinCount() > 0 &&
5434 77 : arg->GetMinCount() == arg->GetMaxCount())
5435 : {
5436 15 : if (arg->GetMinCount() != 1)
5437 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
5438 : }
5439 1394 : else if (arg->GetMinCount() > 0 &&
5440 62 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
5441 : {
5442 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
5443 8 : arg->GetMaxCount());
5444 : }
5445 1324 : else if (arg->GetMinCount() > 0)
5446 : {
5447 54 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
5448 : }
5449 1270 : else if (arg->GetMaxCount() > 1)
5450 : {
5451 241 : osRet += " [may be repeated]";
5452 : }
5453 : }
5454 :
5455 1390 : if (arg->IsRequired())
5456 : {
5457 101 : osRet += " [required]";
5458 : }
5459 :
5460 1390 : osRet += '\n';
5461 :
5462 1390 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
5463 1390 : if (!mutualExclusionGroup.empty())
5464 : {
5465 196 : std::string otherArgs;
5466 1582 : for (const auto &otherArg : m_args)
5467 : {
5468 2838 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
5469 1354 : otherArg.get() == arg)
5470 228 : continue;
5471 1256 : if (otherArg->GetMutualExclusionGroup() ==
5472 : mutualExclusionGroup)
5473 : {
5474 128 : if (!otherArgs.empty())
5475 30 : otherArgs += ", ";
5476 128 : otherArgs += "--";
5477 128 : otherArgs += otherArg->GetName();
5478 : }
5479 : }
5480 98 : if (!otherArgs.empty())
5481 : {
5482 98 : osRet += " ";
5483 98 : osRet += " ";
5484 98 : osRet.append(maxOptLen, ' ');
5485 98 : osRet += "Mutually exclusive with ";
5486 98 : osRet += otherArgs;
5487 98 : osRet += '\n';
5488 : }
5489 : }
5490 1390 : };
5491 :
5492 254 : if (!m_positionalArgs.empty())
5493 : {
5494 91 : osRet += "\nPositional arguments:\n";
5495 823 : for (const auto &[arg, opt] : options)
5496 : {
5497 732 : if (arg->IsPositional())
5498 90 : OutputArg(arg, opt);
5499 : }
5500 : }
5501 :
5502 254 : if (hasNonPositionals)
5503 : {
5504 254 : bool hasCommon = false;
5505 254 : bool hasBase = false;
5506 254 : bool hasAdvanced = false;
5507 254 : bool hasEsoteric = false;
5508 508 : std::vector<std::string> categories;
5509 2224 : for (const auto &iter : options)
5510 : {
5511 1970 : const auto &arg = iter.first;
5512 1970 : if (!arg->IsPositional())
5513 : {
5514 1880 : const auto &category = arg->GetCategory();
5515 1880 : if (category == GAAC_COMMON)
5516 : {
5517 780 : hasCommon = true;
5518 : }
5519 1100 : else if (category == GAAC_BASE)
5520 : {
5521 1006 : hasBase = true;
5522 : }
5523 94 : else if (category == GAAC_ADVANCED)
5524 : {
5525 90 : hasAdvanced = true;
5526 : }
5527 4 : else if (category == GAAC_ESOTERIC)
5528 : {
5529 3 : hasEsoteric = true;
5530 : }
5531 1 : else if (std::find(categories.begin(), categories.end(),
5532 1 : category) == categories.end())
5533 : {
5534 1 : categories.push_back(category);
5535 : }
5536 : }
5537 : }
5538 254 : if (hasAdvanced)
5539 32 : categories.insert(categories.begin(), GAAC_ADVANCED);
5540 254 : if (hasBase)
5541 213 : categories.insert(categories.begin(), GAAC_BASE);
5542 254 : if (hasCommon && !usageOptions.isPipelineStep)
5543 62 : categories.insert(categories.begin(), GAAC_COMMON);
5544 254 : if (hasEsoteric)
5545 1 : categories.push_back(GAAC_ESOTERIC);
5546 :
5547 563 : for (const auto &category : categories)
5548 : {
5549 309 : osRet += "\n";
5550 309 : if (category != GAAC_BASE)
5551 : {
5552 96 : osRet += category;
5553 96 : osRet += ' ';
5554 : }
5555 309 : osRet += "Options:\n";
5556 2808 : for (const auto &[arg, opt] : options)
5557 : {
5558 2499 : if (!arg->IsPositional() && arg->GetCategory() == category)
5559 1300 : OutputArg(arg, opt);
5560 : }
5561 : }
5562 : }
5563 : }
5564 :
5565 256 : if (!m_longDescription.empty())
5566 : {
5567 5 : osRet += '\n';
5568 5 : osRet += m_longDescription;
5569 5 : osRet += '\n';
5570 : }
5571 :
5572 256 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
5573 : {
5574 243 : if (!m_helpURL.empty())
5575 : {
5576 243 : osRet += "\nFor more details, consult ";
5577 243 : osRet += GetHelpFullURL();
5578 243 : osRet += '\n';
5579 : }
5580 243 : osRet += GetUsageForCLIEnd();
5581 : }
5582 :
5583 256 : return osRet;
5584 : }
5585 :
5586 : /************************************************************************/
5587 : /* GDALAlgorithm::GetUsageForCLIEnd() */
5588 : /************************************************************************/
5589 :
5590 : //! @cond Doxygen_Suppress
5591 250 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
5592 : {
5593 250 : std::string osRet;
5594 :
5595 250 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
5596 : {
5597 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
5598 : "alternative interface to GDAL and OGR command line "
5599 : "utilities.\nThe project reserves the right to modify, "
5600 : "rename, reorganize, and change the behavior of the utility\n"
5601 : "until it is officially frozen in a future feature release of "
5602 12 : "GDAL.\n";
5603 : }
5604 250 : return osRet;
5605 : }
5606 :
5607 : //! @endcond
5608 :
5609 : /************************************************************************/
5610 : /* GDALAlgorithm::GetUsageAsJSON() */
5611 : /************************************************************************/
5612 :
5613 392 : std::string GDALAlgorithm::GetUsageAsJSON() const
5614 : {
5615 784 : CPLJSONDocument oDoc;
5616 784 : auto oRoot = oDoc.GetRoot();
5617 :
5618 392 : if (m_displayInJSONUsage)
5619 : {
5620 390 : oRoot.Add("name", m_name);
5621 390 : CPLJSONArray jFullPath;
5622 886 : for (const std::string &s : m_callPath)
5623 : {
5624 496 : jFullPath.Add(s);
5625 : }
5626 390 : oRoot.Add("full_path", jFullPath);
5627 : }
5628 :
5629 392 : oRoot.Add("description", m_description);
5630 392 : if (!m_helpURL.empty())
5631 : {
5632 391 : oRoot.Add("short_url", m_helpURL);
5633 391 : oRoot.Add("url", GetHelpFullURL());
5634 : }
5635 :
5636 784 : CPLJSONArray jSubAlgorithms;
5637 556 : for (const auto &subAlgName : GetSubAlgorithmNames())
5638 : {
5639 328 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
5640 164 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
5641 : {
5642 161 : CPLJSONDocument oSubDoc;
5643 161 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
5644 161 : jSubAlgorithms.Add(oSubDoc.GetRoot());
5645 : }
5646 : }
5647 392 : oRoot.Add("sub_algorithms", jSubAlgorithms);
5648 :
5649 3457 : const auto ProcessArg = [](const GDALAlgorithmArg *arg)
5650 : {
5651 3457 : CPLJSONObject jArg;
5652 3457 : jArg.Add("name", arg->GetName());
5653 3457 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
5654 3457 : jArg.Add("description", arg->GetDescription());
5655 :
5656 3457 : const auto &metaVar = arg->GetMetaVar();
5657 3457 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
5658 : {
5659 1117 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
5660 1117 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
5661 : std::string::npos)
5662 25 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
5663 : else
5664 576 : jArg.Add("metavar", metaVar);
5665 : }
5666 :
5667 3457 : const auto &choices = arg->GetChoices();
5668 3457 : if (!choices.empty())
5669 : {
5670 269 : CPLJSONArray jChoices;
5671 2374 : for (const auto &choice : choices)
5672 2105 : jChoices.Add(choice);
5673 269 : jArg.Add("choices", jChoices);
5674 : }
5675 3457 : if (arg->HasDefaultValue())
5676 : {
5677 852 : switch (arg->GetType())
5678 : {
5679 244 : case GAAT_BOOLEAN:
5680 244 : jArg.Add("default", arg->GetDefault<bool>());
5681 244 : break;
5682 268 : case GAAT_STRING:
5683 268 : jArg.Add("default", arg->GetDefault<std::string>());
5684 268 : break;
5685 176 : case GAAT_INTEGER:
5686 176 : jArg.Add("default", arg->GetDefault<int>());
5687 176 : break;
5688 160 : case GAAT_REAL:
5689 160 : jArg.Add("default", arg->GetDefault<double>());
5690 160 : break;
5691 4 : case GAAT_STRING_LIST:
5692 : {
5693 : const auto &val =
5694 4 : arg->GetDefault<std::vector<std::string>>();
5695 4 : if (val.size() == 1)
5696 : {
5697 4 : jArg.Add("default", val[0]);
5698 : }
5699 : else
5700 : {
5701 0 : CPLError(CE_Warning, CPLE_AppDefined,
5702 : "Unhandled default value for arg %s",
5703 0 : arg->GetName().c_str());
5704 : }
5705 4 : break;
5706 : }
5707 0 : case GAAT_INTEGER_LIST:
5708 : {
5709 0 : const auto &val = arg->GetDefault<std::vector<int>>();
5710 0 : if (val.size() == 1)
5711 : {
5712 0 : jArg.Add("default", val[0]);
5713 : }
5714 : else
5715 : {
5716 0 : CPLError(CE_Warning, CPLE_AppDefined,
5717 : "Unhandled default value for arg %s",
5718 0 : arg->GetName().c_str());
5719 : }
5720 0 : break;
5721 : }
5722 0 : case GAAT_REAL_LIST:
5723 : {
5724 0 : const auto &val = arg->GetDefault<std::vector<double>>();
5725 0 : if (val.size() == 1)
5726 : {
5727 0 : jArg.Add("default", val[0]);
5728 : }
5729 : else
5730 : {
5731 0 : CPLError(CE_Warning, CPLE_AppDefined,
5732 : "Unhandled default value for arg %s",
5733 0 : arg->GetName().c_str());
5734 : }
5735 0 : break;
5736 : }
5737 0 : case GAAT_DATASET:
5738 : case GAAT_DATASET_LIST:
5739 0 : CPLError(CE_Warning, CPLE_AppDefined,
5740 : "Unhandled default value for arg %s",
5741 0 : arg->GetName().c_str());
5742 0 : break;
5743 : }
5744 : }
5745 :
5746 3457 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
5747 3457 : if (!std::isnan(minVal))
5748 : {
5749 470 : if (arg->GetType() == GAAT_INTEGER ||
5750 205 : arg->GetType() == GAAT_INTEGER_LIST)
5751 73 : jArg.Add("min_value", static_cast<int>(minVal));
5752 : else
5753 192 : jArg.Add("min_value", minVal);
5754 265 : jArg.Add("min_value_is_included", minValIsIncluded);
5755 : }
5756 :
5757 3457 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
5758 3457 : if (!std::isnan(maxVal))
5759 : {
5760 143 : if (arg->GetType() == GAAT_INTEGER ||
5761 67 : arg->GetType() == GAAT_INTEGER_LIST)
5762 9 : jArg.Add("max_value", static_cast<int>(maxVal));
5763 : else
5764 67 : jArg.Add("max_value", maxVal);
5765 76 : jArg.Add("max_value_is_included", maxValIsIncluded);
5766 : }
5767 :
5768 3457 : jArg.Add("required", arg->IsRequired());
5769 3457 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
5770 : {
5771 1032 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
5772 1032 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
5773 1032 : jArg.Add("min_count", arg->GetMinCount());
5774 1032 : jArg.Add("max_count", arg->GetMaxCount());
5775 : }
5776 3457 : jArg.Add("category", arg->GetCategory());
5777 :
5778 6729 : if (arg->GetType() == GAAT_DATASET ||
5779 3272 : arg->GetType() == GAAT_DATASET_LIST)
5780 : {
5781 : {
5782 319 : CPLJSONArray jAr;
5783 319 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
5784 240 : jAr.Add("raster");
5785 319 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
5786 110 : jAr.Add("vector");
5787 319 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
5788 19 : jAr.Add("multidim_raster");
5789 319 : jArg.Add("dataset_type", jAr);
5790 : }
5791 :
5792 437 : const auto GetFlags = [](int flags)
5793 : {
5794 437 : CPLJSONArray jAr;
5795 437 : if (flags & GADV_NAME)
5796 319 : jAr.Add("name");
5797 437 : if (flags & GADV_OBJECT)
5798 415 : jAr.Add("dataset");
5799 437 : return jAr;
5800 : };
5801 :
5802 319 : if (arg->IsInput())
5803 : {
5804 319 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
5805 : }
5806 319 : if (arg->IsOutput())
5807 : {
5808 118 : jArg.Add("output_flags",
5809 236 : GetFlags(arg->GetDatasetOutputFlags()));
5810 : }
5811 : }
5812 :
5813 3457 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
5814 3457 : if (!mutualExclusionGroup.empty())
5815 : {
5816 329 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
5817 : }
5818 :
5819 6914 : const auto &metadata = arg->GetMetadata();
5820 3457 : if (!metadata.empty())
5821 : {
5822 329 : CPLJSONObject jMetadata;
5823 673 : for (const auto &[key, values] : metadata)
5824 : {
5825 688 : CPLJSONArray jValue;
5826 806 : for (const auto &value : values)
5827 462 : jValue.Add(value);
5828 344 : jMetadata.Add(key, jValue);
5829 : }
5830 329 : jArg.Add("metadata", jMetadata);
5831 : }
5832 :
5833 6914 : return jArg;
5834 : };
5835 :
5836 : {
5837 392 : CPLJSONArray jArgs;
5838 5711 : for (const auto &arg : m_args)
5839 : {
5840 8758 : if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
5841 3439 : !arg->IsOutput())
5842 3321 : jArgs.Add(ProcessArg(arg.get()));
5843 : }
5844 392 : oRoot.Add("input_arguments", jArgs);
5845 : }
5846 :
5847 : {
5848 392 : CPLJSONArray jArgs;
5849 5711 : for (const auto &arg : m_args)
5850 : {
5851 5337 : if (!arg->IsHidden() && !arg->IsOnlyForCLI() && !arg->IsInput() &&
5852 18 : arg->IsOutput())
5853 18 : jArgs.Add(ProcessArg(arg.get()));
5854 : }
5855 392 : oRoot.Add("output_arguments", jArgs);
5856 : }
5857 :
5858 : {
5859 392 : CPLJSONArray jArgs;
5860 5711 : for (const auto &arg : m_args)
5861 : {
5862 8758 : if (!arg->IsHidden() && !arg->IsOnlyForCLI() && arg->IsInput() &&
5863 3439 : arg->IsOutput())
5864 118 : jArgs.Add(ProcessArg(arg.get()));
5865 : }
5866 392 : oRoot.Add("input_output_arguments", jArgs);
5867 : }
5868 :
5869 392 : if (m_supportsStreamedOutput)
5870 : {
5871 75 : oRoot.Add("supports_streamed_output", true);
5872 : }
5873 :
5874 784 : return oDoc.SaveAsString();
5875 : }
5876 :
5877 : /************************************************************************/
5878 : /* GDALAlgorithm::GetAutoComplete() */
5879 : /************************************************************************/
5880 :
5881 : std::vector<std::string>
5882 167 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
5883 : bool lastWordIsComplete, bool showAllOptions)
5884 : {
5885 334 : std::vector<std::string> ret;
5886 :
5887 : // Get inner-most algorithm
5888 167 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
5889 167 : GDALAlgorithm *curAlg = this;
5890 343 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
5891 : {
5892 : auto subAlg = curAlg->InstantiateSubAlgorithm(
5893 244 : args.front(), /* suggestionAllowed = */ false);
5894 244 : if (!subAlg)
5895 67 : break;
5896 177 : if (args.size() == 1 && !lastWordIsComplete)
5897 : {
5898 4 : int nCount = 0;
5899 87 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
5900 : {
5901 83 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
5902 5 : nCount++;
5903 : }
5904 4 : if (nCount >= 2)
5905 : {
5906 11 : for (const std::string &subAlgName :
5907 23 : curAlg->GetSubAlgorithmNames())
5908 : {
5909 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
5910 11 : if (subAlg && !subAlg->IsHidden())
5911 11 : ret.push_back(subAlg->GetName());
5912 : }
5913 1 : return ret;
5914 : }
5915 : }
5916 176 : showAllOptions = false;
5917 176 : args.erase(args.begin());
5918 176 : curAlgHolder = std::move(subAlg);
5919 176 : curAlg = curAlgHolder.get();
5920 : }
5921 166 : if (curAlg != this)
5922 : {
5923 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
5924 87 : /* showAllOptions = */ false);
5925 : }
5926 :
5927 158 : std::string option;
5928 158 : std::string value;
5929 79 : ExtractLastOptionAndValue(args, option, value);
5930 :
5931 92 : if (option.empty() && !args.empty() && !args.back().empty() &&
5932 13 : args.back()[0] == '-')
5933 : {
5934 10 : const auto &lastArg = args.back();
5935 : // List available options
5936 170 : for (const auto &arg : GetArgs())
5937 : {
5938 299 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
5939 273 : (!showAllOptions &&
5940 375 : (arg->GetName() == "help" || arg->GetName() == "config" ||
5941 232 : arg->GetName() == "version" ||
5942 116 : arg->GetName() == "json-usage")))
5943 : {
5944 48 : continue;
5945 : }
5946 112 : if (!arg->GetShortName().empty())
5947 : {
5948 63 : std::string str = std::string("-").append(arg->GetShortName());
5949 21 : if (lastArg == str)
5950 0 : ret.push_back(std::move(str));
5951 : }
5952 112 : if (lastArg != "-" && lastArg != "--")
5953 : {
5954 52 : for (const std::string &alias : arg->GetAliases())
5955 : {
5956 48 : std::string str = std::string("--").append(alias);
5957 16 : if (cpl::starts_with(str, lastArg))
5958 3 : ret.push_back(std::move(str));
5959 : }
5960 : }
5961 112 : if (!arg->GetName().empty())
5962 : {
5963 336 : std::string str = std::string("--").append(arg->GetName());
5964 112 : if (cpl::starts_with(str, lastArg))
5965 78 : ret.push_back(std::move(str));
5966 : }
5967 : }
5968 10 : std::sort(ret.begin(), ret.end());
5969 : }
5970 69 : else if (!option.empty())
5971 : {
5972 : // List possible choices for current option
5973 62 : auto arg = GetArg(option);
5974 62 : if (arg && arg->GetType() != GAAT_BOOLEAN)
5975 : {
5976 62 : ret = arg->GetChoices();
5977 62 : if (ret.empty())
5978 : {
5979 : {
5980 58 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
5981 58 : SetParseForAutoCompletion();
5982 58 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
5983 : }
5984 58 : ret = arg->GetAutoCompleteChoices(value);
5985 : }
5986 : else
5987 : {
5988 4 : std::sort(ret.begin(), ret.end());
5989 : }
5990 62 : if (!ret.empty() && ret.back() == value)
5991 : {
5992 2 : ret.clear();
5993 : }
5994 60 : else if (ret.empty())
5995 : {
5996 6 : ret.push_back("**");
5997 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5998 12 : ret.push_back(std::string("\xC2\xA0"
5999 : "description: ")
6000 6 : .append(arg->GetDescription()));
6001 : }
6002 : }
6003 : }
6004 : else
6005 : {
6006 : // List possible sub-algorithms
6007 63 : for (const std::string &subAlgName : GetSubAlgorithmNames())
6008 : {
6009 112 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6010 56 : if (subAlg && !subAlg->IsHidden())
6011 56 : ret.push_back(subAlg->GetName());
6012 : }
6013 7 : if (!ret.empty())
6014 : {
6015 3 : std::sort(ret.begin(), ret.end());
6016 : }
6017 :
6018 : // Try filenames
6019 7 : if (ret.empty() && !args.empty())
6020 : {
6021 : {
6022 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6023 3 : SetParseForAutoCompletion();
6024 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6025 : }
6026 :
6027 3 : const std::string &lastArg = args.back();
6028 3 : GDALAlgorithmArg *arg = nullptr;
6029 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
6030 21 : "like", "source", "destination"})
6031 : {
6032 18 : if (!arg)
6033 : {
6034 5 : auto newArg = GetArg(name);
6035 5 : if (newArg)
6036 : {
6037 3 : if (!newArg->IsExplicitlySet())
6038 : {
6039 0 : arg = newArg;
6040 : }
6041 6 : else if (newArg->GetType() == GAAT_STRING ||
6042 5 : newArg->GetType() == GAAT_STRING_LIST ||
6043 8 : newArg->GetType() == GAAT_DATASET ||
6044 0 : newArg->GetType() == GAAT_DATASET_LIST)
6045 : {
6046 : VSIStatBufL sStat;
6047 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
6048 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
6049 : {
6050 3 : arg = newArg;
6051 : }
6052 : }
6053 : }
6054 : }
6055 : }
6056 3 : if (arg)
6057 : {
6058 3 : ret = arg->GetAutoCompleteChoices(lastArg);
6059 : }
6060 : }
6061 : }
6062 :
6063 79 : return ret;
6064 : }
6065 :
6066 : /************************************************************************/
6067 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
6068 : /************************************************************************/
6069 :
6070 79 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
6071 : std::string &option,
6072 : std::string &value) const
6073 : {
6074 79 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
6075 : {
6076 52 : const auto nPosEqual = args.back().find('=');
6077 52 : if (nPosEqual == std::string::npos)
6078 : {
6079 : // Deal with "gdal ... --option"
6080 37 : if (GetArg(args.back()))
6081 : {
6082 27 : option = args.back();
6083 27 : args.pop_back();
6084 : }
6085 : }
6086 : else
6087 : {
6088 : // Deal with "gdal ... --option=<value>"
6089 15 : if (GetArg(args.back().substr(0, nPosEqual)))
6090 : {
6091 15 : option = args.back().substr(0, nPosEqual);
6092 15 : value = args.back().substr(nPosEqual + 1);
6093 15 : args.pop_back();
6094 : }
6095 : }
6096 : }
6097 48 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
6098 21 : args[args.size() - 2][0] == '-')
6099 : {
6100 : // Deal with "gdal ... --option <value>"
6101 20 : auto arg = GetArg(args[args.size() - 2]);
6102 20 : if (arg && arg->GetType() != GAAT_BOOLEAN)
6103 : {
6104 20 : option = args[args.size() - 2];
6105 20 : value = args.back();
6106 20 : args.pop_back();
6107 : }
6108 : }
6109 :
6110 79 : const auto IsKeyValueOption = [](const std::string &osStr)
6111 : {
6112 208 : return osStr == "--co" || osStr == "--creation-option" ||
6113 189 : osStr == "--lco" || osStr == "--layer-creation-option" ||
6114 206 : osStr == "--oo" || osStr == "--open-option";
6115 : };
6116 :
6117 79 : if (IsKeyValueOption(option))
6118 : {
6119 19 : const auto nPosEqual = value.find('=');
6120 19 : if (nPosEqual != std::string::npos)
6121 : {
6122 10 : value.resize(nPosEqual);
6123 : }
6124 : }
6125 79 : }
6126 :
6127 : //! @cond Doxygen_Suppress
6128 :
6129 : /************************************************************************/
6130 : /* GDALContainerAlgorithm::RunImpl() */
6131 : /************************************************************************/
6132 :
6133 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
6134 : {
6135 0 : return false;
6136 : }
6137 :
6138 : //! @endcond
6139 :
6140 : /************************************************************************/
6141 : /* GDALAlgorithmRelease() */
6142 : /************************************************************************/
6143 :
6144 : /** Release a handle to an algorithm.
6145 : *
6146 : * @since 3.11
6147 : */
6148 3525 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
6149 : {
6150 3525 : delete hAlg;
6151 3525 : }
6152 :
6153 : /************************************************************************/
6154 : /* GDALAlgorithmGetName() */
6155 : /************************************************************************/
6156 :
6157 : /** Return the algorithm name.
6158 : *
6159 : * @param hAlg Handle to an algorithm. Must NOT be null.
6160 : * @return algorithm name whose lifetime is bound to hAlg and which must not
6161 : * be freed.
6162 : * @since 3.11
6163 : */
6164 33 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
6165 : {
6166 33 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6167 33 : return hAlg->ptr->GetName().c_str();
6168 : }
6169 :
6170 : /************************************************************************/
6171 : /* GDALAlgorithmGetDescription() */
6172 : /************************************************************************/
6173 :
6174 : /** Return the algorithm (short) description.
6175 : *
6176 : * @param hAlg Handle to an algorithm. Must NOT be null.
6177 : * @return algorithm description whose lifetime is bound to hAlg and which must
6178 : * not be freed.
6179 : * @since 3.11
6180 : */
6181 2 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
6182 : {
6183 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6184 2 : return hAlg->ptr->GetDescription().c_str();
6185 : }
6186 :
6187 : /************************************************************************/
6188 : /* GDALAlgorithmGetLongDescription() */
6189 : /************************************************************************/
6190 :
6191 : /** Return the algorithm (longer) description.
6192 : *
6193 : * @param hAlg Handle to an algorithm. Must NOT be null.
6194 : * @return algorithm description whose lifetime is bound to hAlg and which must
6195 : * not be freed.
6196 : * @since 3.11
6197 : */
6198 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
6199 : {
6200 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6201 2 : return hAlg->ptr->GetLongDescription().c_str();
6202 : }
6203 :
6204 : /************************************************************************/
6205 : /* GDALAlgorithmGetHelpFullURL() */
6206 : /************************************************************************/
6207 :
6208 : /** Return the algorithm full URL.
6209 : *
6210 : * @param hAlg Handle to an algorithm. Must NOT be null.
6211 : * @return algorithm URL whose lifetime is bound to hAlg and which must
6212 : * not be freed.
6213 : * @since 3.11
6214 : */
6215 2 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
6216 : {
6217 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6218 2 : return hAlg->ptr->GetHelpFullURL().c_str();
6219 : }
6220 :
6221 : /************************************************************************/
6222 : /* GDALAlgorithmHasSubAlgorithms() */
6223 : /************************************************************************/
6224 :
6225 : /** Return whether the algorithm has sub-algorithms.
6226 : *
6227 : * @param hAlg Handle to an algorithm. Must NOT be null.
6228 : * @since 3.11
6229 : */
6230 1910 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
6231 : {
6232 1910 : VALIDATE_POINTER1(hAlg, __func__, false);
6233 1910 : return hAlg->ptr->HasSubAlgorithms();
6234 : }
6235 :
6236 : /************************************************************************/
6237 : /* GDALAlgorithmGetSubAlgorithmNames() */
6238 : /************************************************************************/
6239 :
6240 : /** Get the names of registered algorithms.
6241 : *
6242 : * @param hAlg Handle to an algorithm. Must NOT be null.
6243 : * @return a NULL terminated list of names, which must be destroyed with
6244 : * CSLDestroy()
6245 : * @since 3.11
6246 : */
6247 7 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
6248 : {
6249 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6250 7 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
6251 : }
6252 :
6253 : /************************************************************************/
6254 : /* GDALAlgorithmInstantiateSubAlgorithm() */
6255 : /************************************************************************/
6256 :
6257 : /** Instantiate an algorithm by its name (or its alias).
6258 : *
6259 : * @param hAlg Handle to an algorithm. Must NOT be null.
6260 : * @param pszSubAlgName Algorithm name. Must NOT be null.
6261 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
6262 : * or NULL if the algorithm does not exist or another error occurred.
6263 : * @since 3.11
6264 : */
6265 1578 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
6266 : const char *pszSubAlgName)
6267 : {
6268 1578 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6269 1578 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
6270 3156 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
6271 : return subAlg
6272 3156 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
6273 3156 : : nullptr;
6274 : }
6275 :
6276 : /************************************************************************/
6277 : /* GDALAlgorithmParseCommandLineArguments() */
6278 : /************************************************************************/
6279 :
6280 : /** Parse a command line argument, which does not include the algorithm
6281 : * name, to set the value of corresponding arguments.
6282 : *
6283 : * @param hAlg Handle to an algorithm. Must NOT be null.
6284 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
6285 : * @return true if successful, false otherwise
6286 : * @since 3.11
6287 : */
6288 :
6289 295 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
6290 : CSLConstList papszArgs)
6291 : {
6292 295 : VALIDATE_POINTER1(hAlg, __func__, false);
6293 295 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
6294 : }
6295 :
6296 : /************************************************************************/
6297 : /* GDALAlgorithmGetActualAlgorithm() */
6298 : /************************************************************************/
6299 :
6300 : /** Return the actual algorithm that is going to be invoked, when the
6301 : * current algorithm has sub-algorithms.
6302 : *
6303 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
6304 : *
6305 : * Note that the lifetime of the returned algorithm does not exceed the one of
6306 : * the hAlg instance that owns it.
6307 : *
6308 : * @param hAlg Handle to an algorithm. Must NOT be null.
6309 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
6310 : * @since 3.11
6311 : */
6312 515 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
6313 : {
6314 515 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6315 515 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
6316 : }
6317 :
6318 : /************************************************************************/
6319 : /* GDALAlgorithmRun() */
6320 : /************************************************************************/
6321 :
6322 : /** Execute the algorithm, starting with ValidateArguments() and then
6323 : * calling RunImpl().
6324 : *
6325 : * @param hAlg Handle to an algorithm. Must NOT be null.
6326 : * @param pfnProgress Progress callback. May be null.
6327 : * @param pProgressData Progress callback user data. May be null.
6328 : * @return true if successful, false otherwise
6329 : * @since 3.11
6330 : */
6331 :
6332 1340 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
6333 : void *pProgressData)
6334 : {
6335 1340 : VALIDATE_POINTER1(hAlg, __func__, false);
6336 1340 : return hAlg->ptr->Run(pfnProgress, pProgressData);
6337 : }
6338 :
6339 : /************************************************************************/
6340 : /* GDALAlgorithmFinalize() */
6341 : /************************************************************************/
6342 :
6343 : /** Complete any pending actions, and return the final status.
6344 : * This is typically useful for algorithm that generate an output dataset.
6345 : *
6346 : * Note that this function does *NOT* release memory associated with the
6347 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
6348 : *
6349 : * @param hAlg Handle to an algorithm. Must NOT be null.
6350 : * @return true if successful, false otherwise
6351 : * @since 3.11
6352 : */
6353 :
6354 373 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
6355 : {
6356 373 : VALIDATE_POINTER1(hAlg, __func__, false);
6357 373 : return hAlg->ptr->Finalize();
6358 : }
6359 :
6360 : /************************************************************************/
6361 : /* GDALAlgorithmGetUsageAsJSON() */
6362 : /************************************************************************/
6363 :
6364 : /** Return the usage of the algorithm as a JSON-serialized string.
6365 : *
6366 : * This can be used to dynamically generate interfaces to algorithms.
6367 : *
6368 : * @param hAlg Handle to an algorithm. Must NOT be null.
6369 : * @return a string that must be freed with CPLFree()
6370 : * @since 3.11
6371 : */
6372 4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
6373 : {
6374 4 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6375 4 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
6376 : }
6377 :
6378 : /************************************************************************/
6379 : /* GDALAlgorithmGetArgNames() */
6380 : /************************************************************************/
6381 :
6382 : /** Return the list of available argument names.
6383 : *
6384 : * @param hAlg Handle to an algorithm. Must NOT be null.
6385 : * @return a NULL terminated list of names, which must be destroyed with
6386 : * CSLDestroy()
6387 : * @since 3.11
6388 : */
6389 106 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
6390 : {
6391 106 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6392 212 : CPLStringList list;
6393 2180 : for (const auto &arg : hAlg->ptr->GetArgs())
6394 2074 : list.AddString(arg->GetName().c_str());
6395 106 : return list.StealList();
6396 : }
6397 :
6398 : /************************************************************************/
6399 : /* GDALAlgorithmGetArg() */
6400 : /************************************************************************/
6401 :
6402 : /** Return an argument from its name.
6403 : *
6404 : * The lifetime of the returned object does not exceed the one of hAlg.
6405 : *
6406 : * @param hAlg Handle to an algorithm. Must NOT be null.
6407 : * @param pszArgName Argument name. Must NOT be null.
6408 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
6409 : * or nullptr in case of error
6410 : * @since 3.11
6411 : */
6412 6787 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
6413 : const char *pszArgName)
6414 : {
6415 6787 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
6416 6787 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
6417 6787 : auto arg = hAlg->ptr->GetArg(pszArgName);
6418 6787 : if (!arg)
6419 4 : return nullptr;
6420 6783 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
6421 : }
6422 :
6423 : /************************************************************************/
6424 : /* GDALAlgorithmArgRelease() */
6425 : /************************************************************************/
6426 :
6427 : /** Release a handle to an argument.
6428 : *
6429 : * @since 3.11
6430 : */
6431 6783 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
6432 : {
6433 6783 : delete hArg;
6434 6783 : }
6435 :
6436 : /************************************************************************/
6437 : /* GDALAlgorithmArgGetName() */
6438 : /************************************************************************/
6439 :
6440 : /** Return the name of an argument.
6441 : *
6442 : * @param hArg Handle to an argument. Must NOT be null.
6443 : * @return argument name whose lifetime is bound to hArg and which must not
6444 : * be freed.
6445 : * @since 3.11
6446 : */
6447 143 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
6448 : {
6449 143 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6450 143 : return hArg->ptr->GetName().c_str();
6451 : }
6452 :
6453 : /************************************************************************/
6454 : /* GDALAlgorithmArgGetType() */
6455 : /************************************************************************/
6456 :
6457 : /** Get the type of an argument
6458 : *
6459 : * @param hArg Handle to an argument. Must NOT be null.
6460 : * @since 3.11
6461 : */
6462 5037 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
6463 : {
6464 5037 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
6465 5037 : return hArg->ptr->GetType();
6466 : }
6467 :
6468 : /************************************************************************/
6469 : /* GDALAlgorithmArgGetDescription() */
6470 : /************************************************************************/
6471 :
6472 : /** Return the description of an argument.
6473 : *
6474 : * @param hArg Handle to an argument. Must NOT be null.
6475 : * @return argument descriptioin whose lifetime is bound to hArg and which must not
6476 : * be freed.
6477 : * @since 3.11
6478 : */
6479 1 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
6480 : {
6481 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6482 1 : return hArg->ptr->GetDescription().c_str();
6483 : }
6484 :
6485 : /************************************************************************/
6486 : /* GDALAlgorithmArgGetShortName() */
6487 : /************************************************************************/
6488 :
6489 : /** Return the short name, or empty string if there is none
6490 : *
6491 : * @param hArg Handle to an argument. Must NOT be null.
6492 : * @return short name whose lifetime is bound to hArg and which must not
6493 : * be freed.
6494 : * @since 3.11
6495 : */
6496 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
6497 : {
6498 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6499 1 : return hArg->ptr->GetShortName().c_str();
6500 : }
6501 :
6502 : /************************************************************************/
6503 : /* GDALAlgorithmArgGetAliases() */
6504 : /************************************************************************/
6505 :
6506 : /** Return the aliases (potentially none)
6507 : *
6508 : * @param hArg Handle to an argument. Must NOT be null.
6509 : * @return a NULL terminated list of names, which must be destroyed with
6510 : * CSLDestroy()
6511 :
6512 : * @since 3.11
6513 : */
6514 1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
6515 : {
6516 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6517 1 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
6518 : }
6519 :
6520 : /************************************************************************/
6521 : /* GDALAlgorithmArgGetMetaVar() */
6522 : /************************************************************************/
6523 :
6524 : /** Return the "meta-var" hint.
6525 : *
6526 : * By default, the meta-var value is the long name of the argument in
6527 : * upper case.
6528 : *
6529 : * @param hArg Handle to an argument. Must NOT be null.
6530 : * @return meta-var hint whose lifetime is bound to hArg and which must not
6531 : * be freed.
6532 : * @since 3.11
6533 : */
6534 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
6535 : {
6536 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6537 1 : return hArg->ptr->GetMetaVar().c_str();
6538 : }
6539 :
6540 : /************************************************************************/
6541 : /* GDALAlgorithmArgGetCategory() */
6542 : /************************************************************************/
6543 :
6544 : /** Return the argument category
6545 : *
6546 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
6547 : *
6548 : * @param hArg Handle to an argument. Must NOT be null.
6549 : * @return category whose lifetime is bound to hArg and which must not
6550 : * be freed.
6551 : * @since 3.11
6552 : */
6553 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
6554 : {
6555 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6556 1 : return hArg->ptr->GetCategory().c_str();
6557 : }
6558 :
6559 : /************************************************************************/
6560 : /* GDALAlgorithmArgIsPositional() */
6561 : /************************************************************************/
6562 :
6563 : /** Return if the argument is a positional one.
6564 : *
6565 : * @param hArg Handle to an argument. Must NOT be null.
6566 : * @since 3.11
6567 : */
6568 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
6569 : {
6570 1 : VALIDATE_POINTER1(hArg, __func__, false);
6571 1 : return hArg->ptr->IsPositional();
6572 : }
6573 :
6574 : /************************************************************************/
6575 : /* GDALAlgorithmArgIsRequired() */
6576 : /************************************************************************/
6577 :
6578 : /** Return whether the argument is required. Defaults to false.
6579 : *
6580 : * @param hArg Handle to an argument. Must NOT be null.
6581 : * @since 3.11
6582 : */
6583 1 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
6584 : {
6585 1 : VALIDATE_POINTER1(hArg, __func__, false);
6586 1 : return hArg->ptr->IsRequired();
6587 : }
6588 :
6589 : /************************************************************************/
6590 : /* GDALAlgorithmArgGetMinCount() */
6591 : /************************************************************************/
6592 :
6593 : /** Return the minimum number of values for the argument.
6594 : *
6595 : * Defaults to 0.
6596 : * Only applies to list type of arguments.
6597 : *
6598 : * @param hArg Handle to an argument. Must NOT be null.
6599 : * @since 3.11
6600 : */
6601 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
6602 : {
6603 1 : VALIDATE_POINTER1(hArg, __func__, 0);
6604 1 : return hArg->ptr->GetMinCount();
6605 : }
6606 :
6607 : /************************************************************************/
6608 : /* GDALAlgorithmArgGetMaxCount() */
6609 : /************************************************************************/
6610 :
6611 : /** Return the maximum number of values for the argument.
6612 : *
6613 : * Defaults to 1 for scalar types, and INT_MAX for list types.
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 GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
6620 : {
6621 1 : VALIDATE_POINTER1(hArg, __func__, 0);
6622 1 : return hArg->ptr->GetMaxCount();
6623 : }
6624 :
6625 : /************************************************************************/
6626 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
6627 : /************************************************************************/
6628 :
6629 : /** Return whether, for list type of arguments, several values, space
6630 : * separated, may be specified. That is "--foo=bar,baz".
6631 : * The default is true.
6632 : *
6633 : * @param hArg Handle to an argument. Must NOT be null.
6634 : * @since 3.11
6635 : */
6636 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
6637 : {
6638 1 : VALIDATE_POINTER1(hArg, __func__, false);
6639 1 : return hArg->ptr->GetPackedValuesAllowed();
6640 : }
6641 :
6642 : /************************************************************************/
6643 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
6644 : /************************************************************************/
6645 :
6646 : /** Return whether, for list type of arguments, the argument may be
6647 : * repeated. That is "--foo=bar --foo=baz".
6648 : * The default is true.
6649 : *
6650 : * @param hArg Handle to an argument. Must NOT be null.
6651 : * @since 3.11
6652 : */
6653 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
6654 : {
6655 1 : VALIDATE_POINTER1(hArg, __func__, false);
6656 1 : return hArg->ptr->GetRepeatedArgAllowed();
6657 : }
6658 :
6659 : /************************************************************************/
6660 : /* GDALAlgorithmArgGetChoices() */
6661 : /************************************************************************/
6662 :
6663 : /** Return the allowed values (as strings) for the argument.
6664 : *
6665 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
6666 : *
6667 : * @param hArg Handle to an argument. Must NOT be null.
6668 : * @return a NULL terminated list of names, which must be destroyed with
6669 : * CSLDestroy()
6670 :
6671 : * @since 3.11
6672 : */
6673 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
6674 : {
6675 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6676 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
6677 : }
6678 :
6679 : /************************************************************************/
6680 : /* GDALAlgorithmArgGetMetadataItem() */
6681 : /************************************************************************/
6682 :
6683 : /** Return the values of the metadata item of an argument.
6684 : *
6685 : * @param hArg Handle to an argument. Must NOT be null.
6686 : * @param pszItem Name of the item. Must NOT be null.
6687 : * @return a NULL terminated list of values, which must be destroyed with
6688 : * CSLDestroy()
6689 :
6690 : * @since 3.11
6691 : */
6692 25 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
6693 : const char *pszItem)
6694 : {
6695 25 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6696 25 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
6697 25 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
6698 25 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
6699 : }
6700 :
6701 : /************************************************************************/
6702 : /* GDALAlgorithmArgIsExplicitlySet() */
6703 : /************************************************************************/
6704 :
6705 : /** Return whether the argument value has been explicitly set with Set()
6706 : *
6707 : * @param hArg Handle to an argument. Must NOT be null.
6708 : * @since 3.11
6709 : */
6710 1 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
6711 : {
6712 1 : VALIDATE_POINTER1(hArg, __func__, false);
6713 1 : return hArg->ptr->IsExplicitlySet();
6714 : }
6715 :
6716 : /************************************************************************/
6717 : /* GDALAlgorithmArgHasDefaultValue() */
6718 : /************************************************************************/
6719 :
6720 : /** Return if the argument has a declared default value.
6721 : *
6722 : * @param hArg Handle to an argument. Must NOT be null.
6723 : * @since 3.11
6724 : */
6725 1 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
6726 : {
6727 1 : VALIDATE_POINTER1(hArg, __func__, false);
6728 1 : return hArg->ptr->HasDefaultValue();
6729 : }
6730 :
6731 : /************************************************************************/
6732 : /* GDALAlgorithmArgIsHiddenForCLI() */
6733 : /************************************************************************/
6734 :
6735 : /** Return whether the argument must not be mentioned in CLI usage.
6736 : *
6737 : * For example, "output-value" for "gdal raster info", which is only
6738 : * meant when the algorithm is used from a non-CLI context.
6739 : *
6740 : * @param hArg Handle to an argument. Must NOT be null.
6741 : * @since 3.11
6742 : */
6743 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
6744 : {
6745 1 : VALIDATE_POINTER1(hArg, __func__, false);
6746 1 : return hArg->ptr->IsHiddenForCLI();
6747 : }
6748 :
6749 : /************************************************************************/
6750 : /* GDALAlgorithmArgIsOnlyForCLI() */
6751 : /************************************************************************/
6752 :
6753 : /** Return whether the argument is only for CLI usage.
6754 : *
6755 : * For example "--help"
6756 : *
6757 : * @param hArg Handle to an argument. Must NOT be null.
6758 : * @since 3.11
6759 : */
6760 1 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
6761 : {
6762 1 : VALIDATE_POINTER1(hArg, __func__, false);
6763 1 : return hArg->ptr->IsOnlyForCLI();
6764 : }
6765 :
6766 : /************************************************************************/
6767 : /* GDALAlgorithmArgIsInput() */
6768 : /************************************************************************/
6769 :
6770 : /** Indicate whether the value of the argument is read-only during the
6771 : * execution of the algorithm.
6772 : *
6773 : * Default is true.
6774 : *
6775 : * @param hArg Handle to an argument. Must NOT be null.
6776 : * @since 3.11
6777 : */
6778 1 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
6779 : {
6780 1 : VALIDATE_POINTER1(hArg, __func__, false);
6781 1 : return hArg->ptr->IsInput();
6782 : }
6783 :
6784 : /************************************************************************/
6785 : /* GDALAlgorithmArgIsOutput() */
6786 : /************************************************************************/
6787 :
6788 : /** Return whether (at least part of) the value of the argument is set
6789 : * during the execution of the algorithm.
6790 : *
6791 : * For example, "output-value" for "gdal raster info"
6792 : * Default is false.
6793 : * An argument may return both IsInput() and IsOutput() as true.
6794 : * For example the "gdal raster convert" algorithm consumes the dataset
6795 : * name of its "output" argument, and sets the dataset object during its
6796 : * execution.
6797 : *
6798 : * @param hArg Handle to an argument. Must NOT be null.
6799 : * @since 3.11
6800 : */
6801 2050 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
6802 : {
6803 2050 : VALIDATE_POINTER1(hArg, __func__, false);
6804 2050 : return hArg->ptr->IsOutput();
6805 : }
6806 :
6807 : /************************************************************************/
6808 : /* GDALAlgorithmArgGetDatasetType() */
6809 : /************************************************************************/
6810 :
6811 : /** Get which type of dataset is allowed / generated.
6812 : *
6813 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
6814 : * GDAL_OF_MULTIDIM_RASTER.
6815 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
6816 : *
6817 : * @param hArg Handle to an argument. Must NOT be null.
6818 : * @since 3.11
6819 : */
6820 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
6821 : {
6822 2 : VALIDATE_POINTER1(hArg, __func__, 0);
6823 2 : return hArg->ptr->GetDatasetType();
6824 : }
6825 :
6826 : /************************************************************************/
6827 : /* GDALAlgorithmArgGetDatasetInputFlags() */
6828 : /************************************************************************/
6829 :
6830 : /** Indicates which components among name and dataset are accepted as
6831 : * input, when this argument serves as an input.
6832 : *
6833 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
6834 : * input.
6835 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
6836 : * accepted as input.
6837 : * If both bits are set, the algorithm can accept either a name or a dataset
6838 : * object.
6839 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
6840 : *
6841 : * @param hArg Handle to an argument. Must NOT be null.
6842 : * @return string whose lifetime is bound to hAlg and which must not
6843 : * be freed.
6844 : * @since 3.11
6845 : */
6846 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
6847 : {
6848 2 : VALIDATE_POINTER1(hArg, __func__, 0);
6849 2 : return hArg->ptr->GetDatasetInputFlags();
6850 : }
6851 :
6852 : /************************************************************************/
6853 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
6854 : /************************************************************************/
6855 :
6856 : /** Indicates which components among name and dataset are modified,
6857 : * when this argument serves as an output.
6858 : *
6859 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
6860 : * output (that is the algorithm will generate the name. Rarely used).
6861 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
6862 : * generated as output, and available for use after the algorithm has
6863 : * completed.
6864 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
6865 : *
6866 : * @param hArg Handle to an argument. Must NOT be null.
6867 : * @return string whose lifetime is bound to hAlg and which must not
6868 : * be freed.
6869 : * @since 3.11
6870 : */
6871 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
6872 : {
6873 2 : VALIDATE_POINTER1(hArg, __func__, 0);
6874 2 : return hArg->ptr->GetDatasetOutputFlags();
6875 : }
6876 :
6877 : /************************************************************************/
6878 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
6879 : /************************************************************************/
6880 :
6881 : /** Return the name of the mutual exclusion group to which this argument
6882 : * belongs to.
6883 : *
6884 : * Or empty string if it does not belong to any exclusion group.
6885 : *
6886 : * @param hArg Handle to an argument. Must NOT be null.
6887 : * @return string whose lifetime is bound to hArg and which must not
6888 : * be freed.
6889 : * @since 3.11
6890 : */
6891 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
6892 : {
6893 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6894 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
6895 : }
6896 :
6897 : /************************************************************************/
6898 : /* GDALAlgorithmArgGetAsBoolean() */
6899 : /************************************************************************/
6900 :
6901 : /** Return the argument value as a boolean.
6902 : *
6903 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
6904 : *
6905 : * @param hArg Handle to an argument. Must NOT be null.
6906 : * @since 3.11
6907 : */
6908 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
6909 : {
6910 8 : VALIDATE_POINTER1(hArg, __func__, false);
6911 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
6912 : {
6913 1 : CPLError(CE_Failure, CPLE_AppDefined,
6914 : "%s must only be called on arguments of type GAAT_BOOLEAN",
6915 : __func__);
6916 1 : return false;
6917 : }
6918 7 : return hArg->ptr->Get<bool>();
6919 : }
6920 :
6921 : /************************************************************************/
6922 : /* GDALAlgorithmArgGetAsBoolean() */
6923 : /************************************************************************/
6924 :
6925 : /** Return the argument value as a string.
6926 : *
6927 : * Must only be called on arguments whose type is GAAT_STRING.
6928 : *
6929 : * @param hArg Handle to an argument. Must NOT be null.
6930 : * @return string whose lifetime is bound to hArg and which must not
6931 : * be freed.
6932 : * @since 3.11
6933 : */
6934 102 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
6935 : {
6936 102 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6937 102 : if (hArg->ptr->GetType() != GAAT_STRING)
6938 : {
6939 1 : CPLError(CE_Failure, CPLE_AppDefined,
6940 : "%s must only be called on arguments of type GAAT_STRING",
6941 : __func__);
6942 1 : return nullptr;
6943 : }
6944 101 : return hArg->ptr->Get<std::string>().c_str();
6945 : }
6946 :
6947 : /************************************************************************/
6948 : /* GDALAlgorithmArgGetAsDatasetValue() */
6949 : /************************************************************************/
6950 :
6951 : /** Return the argument value as a GDALArgDatasetValueH.
6952 : *
6953 : * Must only be called on arguments whose type is GAAT_DATASET
6954 : *
6955 : * @param hArg Handle to an argument. Must NOT be null.
6956 : * @return handle to a GDALArgDatasetValue that must be released with
6957 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
6958 : * the one of hArg.
6959 : * @since 3.11
6960 : */
6961 1525 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
6962 : {
6963 1525 : VALIDATE_POINTER1(hArg, __func__, nullptr);
6964 1525 : if (hArg->ptr->GetType() != GAAT_DATASET)
6965 : {
6966 1 : CPLError(CE_Failure, CPLE_AppDefined,
6967 : "%s must only be called on arguments of type GAAT_DATASET",
6968 : __func__);
6969 1 : return nullptr;
6970 : }
6971 1524 : return std::make_unique<GDALArgDatasetValueHS>(
6972 3048 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
6973 1524 : .release();
6974 : }
6975 :
6976 : /************************************************************************/
6977 : /* GDALAlgorithmArgGetAsInteger() */
6978 : /************************************************************************/
6979 :
6980 : /** Return the argument value as a integer.
6981 : *
6982 : * Must only be called on arguments whose type is GAAT_INTEGER
6983 : *
6984 : * @param hArg Handle to an argument. Must NOT be null.
6985 : * @since 3.11
6986 : */
6987 8 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
6988 : {
6989 8 : VALIDATE_POINTER1(hArg, __func__, 0);
6990 8 : if (hArg->ptr->GetType() != GAAT_INTEGER)
6991 : {
6992 1 : CPLError(CE_Failure, CPLE_AppDefined,
6993 : "%s must only be called on arguments of type GAAT_INTEGER",
6994 : __func__);
6995 1 : return 0;
6996 : }
6997 7 : return hArg->ptr->Get<int>();
6998 : }
6999 :
7000 : /************************************************************************/
7001 : /* GDALAlgorithmArgGetAsDouble() */
7002 : /************************************************************************/
7003 :
7004 : /** Return the argument value as a double.
7005 : *
7006 : * Must only be called on arguments whose type is GAAT_REAL
7007 : *
7008 : * @param hArg Handle to an argument. Must NOT be null.
7009 : * @since 3.11
7010 : */
7011 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
7012 : {
7013 8 : VALIDATE_POINTER1(hArg, __func__, 0);
7014 8 : if (hArg->ptr->GetType() != GAAT_REAL)
7015 : {
7016 1 : CPLError(CE_Failure, CPLE_AppDefined,
7017 : "%s must only be called on arguments of type GAAT_REAL",
7018 : __func__);
7019 1 : return 0;
7020 : }
7021 7 : return hArg->ptr->Get<double>();
7022 : }
7023 :
7024 : /************************************************************************/
7025 : /* GDALAlgorithmArgGetAsStringList() */
7026 : /************************************************************************/
7027 :
7028 : /** Return the argument value as a double.
7029 : *
7030 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
7031 : *
7032 : * @param hArg Handle to an argument. Must NOT be null.
7033 : * @return a NULL terminated list of names, which must be destroyed with
7034 : * CSLDestroy()
7035 :
7036 : * @since 3.11
7037 : */
7038 2 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
7039 : {
7040 2 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7041 2 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
7042 : {
7043 1 : CPLError(CE_Failure, CPLE_AppDefined,
7044 : "%s must only be called on arguments of type GAAT_STRING_LIST",
7045 : __func__);
7046 1 : return nullptr;
7047 : }
7048 2 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
7049 1 : .StealList();
7050 : }
7051 :
7052 : /************************************************************************/
7053 : /* GDALAlgorithmArgGetAsIntegerList() */
7054 : /************************************************************************/
7055 :
7056 : /** Return the argument value as a integer.
7057 : *
7058 : * Must only be called on arguments whose type is GAAT_INTEGER
7059 : *
7060 : * @param hArg Handle to an argument. Must NOT be null.
7061 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7062 : * @since 3.11
7063 : */
7064 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
7065 : size_t *pnCount)
7066 : {
7067 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7068 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7069 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
7070 : {
7071 1 : CPLError(
7072 : CE_Failure, CPLE_AppDefined,
7073 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
7074 : __func__);
7075 1 : *pnCount = 0;
7076 1 : return nullptr;
7077 : }
7078 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
7079 7 : *pnCount = val.size();
7080 7 : return val.data();
7081 : }
7082 :
7083 : /************************************************************************/
7084 : /* GDALAlgorithmArgGetAsDoubleList() */
7085 : /************************************************************************/
7086 :
7087 : /** Return the argument value as a integer.
7088 : *
7089 : * Must only be called on arguments whose type is GAAT_INTEGER
7090 : *
7091 : * @param hArg Handle to an argument. Must NOT be null.
7092 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7093 : * @since 3.11
7094 : */
7095 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
7096 : size_t *pnCount)
7097 : {
7098 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7099 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7100 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
7101 : {
7102 1 : CPLError(CE_Failure, CPLE_AppDefined,
7103 : "%s must only be called on arguments of type GAAT_REAL_LIST",
7104 : __func__);
7105 1 : *pnCount = 0;
7106 1 : return nullptr;
7107 : }
7108 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
7109 7 : *pnCount = val.size();
7110 7 : return val.data();
7111 : }
7112 :
7113 : /************************************************************************/
7114 : /* GDALAlgorithmArgSetAsBoolean() */
7115 : /************************************************************************/
7116 :
7117 : /** Set the value for a GAAT_BOOLEAN argument.
7118 : *
7119 : * It cannot be called several times for a given argument.
7120 : * Validation checks and other actions are run.
7121 : *
7122 : * @param hArg Handle to an argument. Must NOT be null.
7123 : * @param value value.
7124 : * @return true if success.
7125 : * @since 3.11
7126 : */
7127 :
7128 298 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
7129 : {
7130 298 : VALIDATE_POINTER1(hArg, __func__, false);
7131 298 : return hArg->ptr->Set(value);
7132 : }
7133 :
7134 : /************************************************************************/
7135 : /* GDALAlgorithmArgSetAsString() */
7136 : /************************************************************************/
7137 :
7138 : /** Set the value for a GAAT_STRING argument.
7139 : *
7140 : * It cannot be called several times for a given argument.
7141 : * Validation checks and other actions are run.
7142 : *
7143 : * @param hArg Handle to an argument. Must NOT be null.
7144 : * @param value value (may be null)
7145 : * @return true if success.
7146 : * @since 3.11
7147 : */
7148 :
7149 1292 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
7150 : {
7151 1292 : VALIDATE_POINTER1(hArg, __func__, false);
7152 1292 : return hArg->ptr->Set(value ? value : "");
7153 : }
7154 :
7155 : /************************************************************************/
7156 : /* GDALAlgorithmArgSetAsInteger() */
7157 : /************************************************************************/
7158 :
7159 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
7160 : *
7161 : * It cannot be called several times for a given argument.
7162 : * Validation checks and other actions are run.
7163 : *
7164 : * @param hArg Handle to an argument. Must NOT be null.
7165 : * @param value value.
7166 : * @return true if success.
7167 : * @since 3.11
7168 : */
7169 :
7170 196 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
7171 : {
7172 196 : VALIDATE_POINTER1(hArg, __func__, false);
7173 196 : return hArg->ptr->Set(value);
7174 : }
7175 :
7176 : /************************************************************************/
7177 : /* GDALAlgorithmArgSetAsDouble() */
7178 : /************************************************************************/
7179 :
7180 : /** Set the value for a GAAT_REAL argument.
7181 : *
7182 : * It cannot be called several times for a given argument.
7183 : * Validation checks and other actions are run.
7184 : *
7185 : * @param hArg Handle to an argument. Must NOT be null.
7186 : * @param value value.
7187 : * @return true if success.
7188 : * @since 3.11
7189 : */
7190 :
7191 211 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
7192 : {
7193 211 : VALIDATE_POINTER1(hArg, __func__, false);
7194 211 : return hArg->ptr->Set(value);
7195 : }
7196 :
7197 : /************************************************************************/
7198 : /* GDALAlgorithmArgSetAsDatasetValue() */
7199 : /************************************************************************/
7200 :
7201 : /** Set the value for a GAAT_DATASET argument.
7202 : *
7203 : * It cannot be called several times for a given argument.
7204 : * Validation checks and other actions are run.
7205 : *
7206 : * @param hArg Handle to an argument. Must NOT be null.
7207 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
7208 : * @return true if success.
7209 : * @since 3.11
7210 : */
7211 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
7212 : GDALArgDatasetValueH value)
7213 : {
7214 2 : VALIDATE_POINTER1(hArg, __func__, false);
7215 2 : VALIDATE_POINTER1(value, __func__, false);
7216 2 : return hArg->ptr->SetFrom(*(value->ptr));
7217 : }
7218 :
7219 : /************************************************************************/
7220 : /* GDALAlgorithmArgSetDataset() */
7221 : /************************************************************************/
7222 :
7223 : /** Set dataset object, increasing its reference counter.
7224 : *
7225 : * @param hArg Handle to an argument. Must NOT be null.
7226 : * @param hDS Dataset object. May be null.
7227 : * @return true if success.
7228 : * @since 3.11
7229 : */
7230 :
7231 3 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
7232 : {
7233 3 : VALIDATE_POINTER1(hArg, __func__, false);
7234 3 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
7235 : }
7236 :
7237 : /************************************************************************/
7238 : /* GDALAlgorithmArgSetAsStringList() */
7239 : /************************************************************************/
7240 :
7241 : /** Set the value for a GAAT_STRING_LIST argument.
7242 : *
7243 : * It cannot be called several times for a given argument.
7244 : * Validation checks and other actions are run.
7245 : *
7246 : * @param hArg Handle to an argument. Must NOT be null.
7247 : * @param value value as a NULL terminated list (may be null)
7248 : * @return true if success.
7249 : * @since 3.11
7250 : */
7251 :
7252 257 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
7253 : {
7254 257 : VALIDATE_POINTER1(hArg, __func__, false);
7255 257 : return hArg->ptr->Set(
7256 514 : static_cast<std::vector<std::string>>(CPLStringList(value)));
7257 : }
7258 :
7259 : /************************************************************************/
7260 : /* GDALAlgorithmArgSetAsIntegerList() */
7261 : /************************************************************************/
7262 :
7263 : /** Set the value for a GAAT_INTEGER_LIST argument.
7264 : *
7265 : * It cannot be called several times for a given argument.
7266 : * Validation checks and other actions are run.
7267 : *
7268 : * @param hArg Handle to an argument. Must NOT be null.
7269 : * @param nCount Number of values in pnValues.
7270 : * @param pnValues Pointer to an array of integer values of size nCount.
7271 : * @return true if success.
7272 : * @since 3.11
7273 : */
7274 54 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
7275 : const int *pnValues)
7276 : {
7277 54 : VALIDATE_POINTER1(hArg, __func__, false);
7278 54 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
7279 : }
7280 :
7281 : /************************************************************************/
7282 : /* GDALAlgorithmArgSetAsDoubleList() */
7283 : /************************************************************************/
7284 :
7285 : /** Set the value for a GAAT_REAL_LIST argument.
7286 : *
7287 : * It cannot be called several times for a given argument.
7288 : * Validation checks and other actions are run.
7289 : *
7290 : * @param hArg Handle to an argument. Must NOT be null.
7291 : * @param nCount Number of values in pnValues.
7292 : * @param pnValues Pointer to an array of double values of size nCount.
7293 : * @return true if success.
7294 : * @since 3.11
7295 : */
7296 125 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
7297 : const double *pnValues)
7298 : {
7299 125 : VALIDATE_POINTER1(hArg, __func__, false);
7300 125 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
7301 : }
7302 :
7303 : /************************************************************************/
7304 : /* GDALAlgorithmArgSetDatasets() */
7305 : /************************************************************************/
7306 :
7307 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
7308 : *
7309 : * @param hArg Handle to an argument. Must NOT be null.
7310 : * @param nCount Number of values in pnValues.
7311 : * @param pahDS Pointer to an array of dataset of size nCount.
7312 : * @return true if success.
7313 : * @since 3.11
7314 : */
7315 :
7316 359 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
7317 : GDALDatasetH *pahDS)
7318 : {
7319 359 : VALIDATE_POINTER1(hArg, __func__, false);
7320 718 : std::vector<GDALArgDatasetValue> values;
7321 743 : for (size_t i = 0; i < nCount; ++i)
7322 : {
7323 384 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
7324 : }
7325 359 : return hArg->ptr->Set(std::move(values));
7326 : }
7327 :
7328 : /************************************************************************/
7329 : /* GDALAlgorithmArgSetDatasetNames() */
7330 : /************************************************************************/
7331 :
7332 : /** Set dataset names to a GAAT_DATASET_LIST argument.
7333 : *
7334 : * @param hArg Handle to an argument. Must NOT be null.
7335 : * @param names Dataset names as a NULL terminated list (may be null)
7336 : * @return true if success.
7337 : * @since 3.11
7338 : */
7339 :
7340 367 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
7341 : {
7342 367 : VALIDATE_POINTER1(hArg, __func__, false);
7343 734 : std::vector<GDALArgDatasetValue> values;
7344 767 : for (size_t i = 0; names[i]; ++i)
7345 : {
7346 400 : values.emplace_back(names[i]);
7347 : }
7348 367 : return hArg->ptr->Set(std::move(values));
7349 : }
7350 :
7351 : /************************************************************************/
7352 : /* GDALArgDatasetValueCreate() */
7353 : /************************************************************************/
7354 :
7355 : /** Instantiate an empty GDALArgDatasetValue
7356 : *
7357 : * @return new handle to free with GDALArgDatasetValueRelease()
7358 : * @since 3.11
7359 : */
7360 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
7361 : {
7362 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
7363 : }
7364 :
7365 : /************************************************************************/
7366 : /* GDALArgDatasetValueRelease() */
7367 : /************************************************************************/
7368 :
7369 : /** Release a handle to a GDALArgDatasetValue
7370 : *
7371 : * @since 3.11
7372 : */
7373 1525 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
7374 : {
7375 1525 : delete hValue;
7376 1525 : }
7377 :
7378 : /************************************************************************/
7379 : /* GDALArgDatasetValueGetName() */
7380 : /************************************************************************/
7381 :
7382 : /** Return the name component of the GDALArgDatasetValue
7383 : *
7384 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7385 : * @return string whose lifetime is bound to hAlg and which must not
7386 : * be freed.
7387 : * @since 3.11
7388 : */
7389 2 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
7390 : {
7391 2 : VALIDATE_POINTER1(hValue, __func__, nullptr);
7392 2 : return hValue->ptr->GetName().c_str();
7393 : }
7394 :
7395 : /************************************************************************/
7396 : /* GDALArgDatasetValueGetDatasetRef() */
7397 : /************************************************************************/
7398 :
7399 : /** Return the dataset component of the GDALArgDatasetValue.
7400 : *
7401 : * This does not modify the reference counter, hence the lifetime of the
7402 : * returned object is not guaranteed to exceed the one of hValue.
7403 : *
7404 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7405 : * @since 3.11
7406 : */
7407 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
7408 : {
7409 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
7410 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
7411 : }
7412 :
7413 : /************************************************************************/
7414 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
7415 : /************************************************************************/
7416 :
7417 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
7418 : * reference count if not null. Once done with the dataset, the caller should
7419 : * call GDALReleaseDataset().
7420 : *
7421 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7422 : * @since 3.11
7423 : */
7424 : GDALDatasetH
7425 493 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
7426 : {
7427 493 : VALIDATE_POINTER1(hValue, __func__, nullptr);
7428 493 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
7429 : }
7430 :
7431 : /************************************************************************/
7432 : /* GDALArgDatasetValueSetName() */
7433 : /************************************************************************/
7434 :
7435 : /** Set dataset name
7436 : *
7437 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7438 : * @param pszName Dataset name. May be null.
7439 : * @since 3.11
7440 : */
7441 :
7442 813 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
7443 : const char *pszName)
7444 : {
7445 813 : VALIDATE_POINTER0(hValue, __func__);
7446 813 : hValue->ptr->Set(pszName ? pszName : "");
7447 : }
7448 :
7449 : /************************************************************************/
7450 : /* GDALArgDatasetValueSetDataset() */
7451 : /************************************************************************/
7452 :
7453 : /** Set dataset object, increasing its reference counter.
7454 : *
7455 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
7456 : * @param hDS Dataset object. May be null.
7457 : * @since 3.11
7458 : */
7459 :
7460 220 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
7461 : GDALDatasetH hDS)
7462 : {
7463 220 : VALIDATE_POINTER0(hValue, __func__);
7464 220 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
7465 : }
|