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_enumerate.h"
16 : #include "cpl_error.h"
17 : #include "cpl_error_internal.h"
18 : #include "cpl_json.h"
19 : #include "cpl_levenshtein.h"
20 : #include "cpl_minixml.h"
21 : #include "cpl_multiproc.h"
22 :
23 : #include "gdalalgorithm.h"
24 : #include "gdalalg_abstract_pipeline.h"
25 : #include "gdal_priv.h"
26 : #include "gdal_thread_pool.h"
27 : #include "memdataset.h"
28 : #include "ogrsf_frmts.h"
29 : #include "ogr_spatialref.h"
30 : #include "vrtdataset.h"
31 :
32 : #include <algorithm>
33 : #include <cassert>
34 : #include <cerrno>
35 : #include <cmath>
36 : #include <cstdlib>
37 : #include <limits>
38 : #include <map>
39 : #include <type_traits>
40 : #include <string_view>
41 : #include <regex>
42 :
43 : #ifndef _
44 : #define _(x) (x)
45 : #endif
46 :
47 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
48 :
49 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
50 :
51 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
52 :
53 : //! @cond Doxygen_Suppress
54 : struct GDALAlgorithmArgHS
55 : {
56 : GDALAlgorithmArg *ptr = nullptr;
57 :
58 341078 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
59 : {
60 341078 : }
61 : };
62 :
63 : //! @endcond
64 :
65 : //! @cond Doxygen_Suppress
66 : struct GDALArgDatasetValueHS
67 : {
68 : GDALArgDatasetValue val{};
69 : GDALArgDatasetValue *ptr = nullptr;
70 :
71 1 : GDALArgDatasetValueHS() : ptr(&val)
72 : {
73 1 : }
74 :
75 3240 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
76 : {
77 3240 : }
78 :
79 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
80 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
81 : };
82 :
83 : //! @endcond
84 :
85 : /************************************************************************/
86 : /* GDALAlgorithmArgTypeIsList() */
87 : /************************************************************************/
88 :
89 424571 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
90 : {
91 424571 : switch (type)
92 : {
93 279750 : case GAAT_BOOLEAN:
94 : case GAAT_STRING:
95 : case GAAT_INTEGER:
96 : case GAAT_REAL:
97 : case GAAT_DATASET:
98 279750 : break;
99 :
100 144821 : case GAAT_STRING_LIST:
101 : case GAAT_INTEGER_LIST:
102 : case GAAT_REAL_LIST:
103 : case GAAT_DATASET_LIST:
104 144821 : return true;
105 : }
106 :
107 279750 : return false;
108 : }
109 :
110 : /************************************************************************/
111 : /* GDALAlgorithmArgTypeName() */
112 : /************************************************************************/
113 :
114 5448 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
115 : {
116 5448 : switch (type)
117 : {
118 1323 : case GAAT_BOOLEAN:
119 1323 : break;
120 1482 : case GAAT_STRING:
121 1482 : return "string";
122 379 : case GAAT_INTEGER:
123 379 : return "integer";
124 477 : case GAAT_REAL:
125 477 : return "real";
126 252 : case GAAT_DATASET:
127 252 : return "dataset";
128 1016 : case GAAT_STRING_LIST:
129 1016 : return "string_list";
130 93 : case GAAT_INTEGER_LIST:
131 93 : return "integer_list";
132 221 : case GAAT_REAL_LIST:
133 221 : return "real_list";
134 205 : case GAAT_DATASET_LIST:
135 205 : return "dataset_list";
136 : }
137 :
138 1323 : return "boolean";
139 : }
140 :
141 : /************************************************************************/
142 : /* GDALAlgorithmArgDatasetTypeName() */
143 : /************************************************************************/
144 :
145 23182 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
146 : {
147 23182 : std::string ret;
148 23182 : if ((type & GDAL_OF_RASTER) != 0)
149 12692 : ret = "raster";
150 23182 : if ((type & GDAL_OF_VECTOR) != 0)
151 : {
152 11296 : if (!ret.empty())
153 : {
154 1417 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
155 286 : ret += ", ";
156 : else
157 1131 : ret += " or ";
158 : }
159 11296 : ret += "vector";
160 : }
161 23182 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
162 : {
163 739 : if (!ret.empty())
164 : {
165 345 : ret += " or ";
166 : }
167 739 : ret += "multidimensional raster";
168 : }
169 23182 : return ret;
170 : }
171 :
172 : /************************************************************************/
173 : /* GDALAlgorithmArgDecl() */
174 : /************************************************************************/
175 :
176 : // cppcheck-suppress uninitMemberVar
177 341137 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
178 : char chShortName,
179 : const std::string &description,
180 341137 : GDALAlgorithmArgType type)
181 : : m_longName(longName),
182 341137 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
183 : m_description(description), m_type(type),
184 682274 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
185 341137 : .toupper()),
186 1023410 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
187 : {
188 341137 : if (m_type == GAAT_BOOLEAN)
189 : {
190 142135 : m_defaultValue = false;
191 : }
192 341137 : }
193 :
194 : /************************************************************************/
195 : /* GDALAlgorithmArgDecl::SetMinCount() */
196 : /************************************************************************/
197 :
198 18793 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
199 : {
200 18793 : if (!GDALAlgorithmArgTypeIsList(m_type))
201 : {
202 1 : CPLError(CE_Failure, CPLE_NotSupported,
203 : "SetMinCount() illegal on scalar argument '%s'",
204 1 : GetName().c_str());
205 : }
206 : else
207 : {
208 18792 : m_minCount = count;
209 : }
210 18793 : return *this;
211 : }
212 :
213 : /************************************************************************/
214 : /* GDALAlgorithmArgDecl::SetMaxCount() */
215 : /************************************************************************/
216 :
217 17850 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
218 : {
219 17850 : if (!GDALAlgorithmArgTypeIsList(m_type))
220 : {
221 1 : CPLError(CE_Failure, CPLE_NotSupported,
222 : "SetMaxCount() illegal on scalar argument '%s'",
223 1 : GetName().c_str());
224 : }
225 : else
226 : {
227 17849 : m_maxCount = count;
228 : }
229 17850 : return *this;
230 : }
231 :
232 : /************************************************************************/
233 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
234 : /************************************************************************/
235 :
236 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
237 :
238 : /************************************************************************/
239 : /* GDALAlgorithmArg::Set() */
240 : /************************************************************************/
241 :
242 1228 : bool GDALAlgorithmArg::Set(bool value)
243 : {
244 1228 : if (m_decl.GetType() != GAAT_BOOLEAN)
245 : {
246 14 : CPLError(
247 : CE_Failure, CPLE_AppDefined,
248 : "Calling Set(bool) on argument '%s' of type %s is not supported",
249 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
250 7 : return false;
251 : }
252 1221 : return SetInternal(value);
253 : }
254 :
255 4200 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
256 : {
257 4245 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
258 45 : value.front() == '@')
259 : {
260 2 : GByte *pabyData = nullptr;
261 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
262 2 : 10 * 1024 * 1024))
263 : {
264 : // Remove UTF-8 BOM
265 1 : size_t offset = 0;
266 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
267 1 : pabyData[2] == 0xBF)
268 : {
269 1 : offset = 3;
270 : }
271 1 : value = reinterpret_cast<const char *>(pabyData + offset);
272 1 : VSIFree(pabyData);
273 : }
274 : else
275 : {
276 1 : return false;
277 : }
278 : }
279 :
280 4199 : if (m_decl.IsRemoveSQLCommentsEnabled())
281 44 : value = CPLRemoveSQLComments(value);
282 :
283 4199 : return true;
284 : }
285 :
286 4236 : bool GDALAlgorithmArg::Set(const std::string &value)
287 : {
288 4236 : switch (m_decl.GetType())
289 : {
290 9 : case GAAT_BOOLEAN:
291 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
292 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
293 : {
294 4 : return Set(true);
295 : }
296 5 : else if (EQUAL(value.c_str(), "0") ||
297 4 : EQUAL(value.c_str(), "FALSE") ||
298 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
299 : {
300 4 : return Set(false);
301 : }
302 1 : break;
303 :
304 8 : case GAAT_INTEGER:
305 : case GAAT_INTEGER_LIST:
306 : {
307 8 : errno = 0;
308 8 : char *endptr = nullptr;
309 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
310 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
311 5 : endptr == value.c_str() + value.size())
312 : {
313 3 : if (m_decl.GetType() == GAAT_INTEGER)
314 3 : return Set(static_cast<int>(v));
315 : else
316 1 : return Set(std::vector<int>{static_cast<int>(v)});
317 : }
318 5 : break;
319 : }
320 :
321 5 : case GAAT_REAL:
322 : case GAAT_REAL_LIST:
323 : {
324 5 : char *endptr = nullptr;
325 5 : const double v = CPLStrtod(value.c_str(), &endptr);
326 5 : if (endptr == value.c_str() + value.size())
327 : {
328 3 : if (m_decl.GetType() == GAAT_REAL)
329 3 : return Set(v);
330 : else
331 1 : return Set(std::vector<double>{v});
332 : }
333 2 : break;
334 : }
335 :
336 4178 : case GAAT_STRING:
337 4178 : break;
338 :
339 8 : case GAAT_STRING_LIST:
340 16 : return Set(std::vector<std::string>{value});
341 :
342 28 : case GAAT_DATASET:
343 28 : return SetDatasetName(value);
344 :
345 0 : case GAAT_DATASET_LIST:
346 : {
347 0 : std::vector<GDALArgDatasetValue> v;
348 0 : v.resize(1);
349 0 : v[0].Set(value);
350 0 : return Set(std::move(v));
351 : }
352 : }
353 :
354 4186 : if (m_decl.GetType() != GAAT_STRING)
355 : {
356 16 : CPLError(CE_Failure, CPLE_AppDefined,
357 : "Calling Set(std::string) on argument '%s' of type %s is not "
358 : "supported",
359 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
360 8 : return false;
361 : }
362 :
363 4178 : std::string newValue(value);
364 4178 : return ProcessString(newValue) && SetInternal(newValue);
365 : }
366 :
367 873 : bool GDALAlgorithmArg::Set(int value)
368 : {
369 873 : if (m_decl.GetType() == GAAT_BOOLEAN)
370 : {
371 3 : if (value == 1)
372 1 : return Set(true);
373 2 : else if (value == 0)
374 1 : return Set(false);
375 : }
376 870 : else if (m_decl.GetType() == GAAT_REAL)
377 : {
378 3 : return Set(static_cast<double>(value));
379 : }
380 867 : else if (m_decl.GetType() == GAAT_STRING)
381 : {
382 2 : return Set(std::to_string(value));
383 : }
384 865 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
385 : {
386 1 : return Set(std::vector<int>{value});
387 : }
388 864 : else if (m_decl.GetType() == GAAT_REAL_LIST)
389 : {
390 1 : return Set(std::vector<double>{static_cast<double>(value)});
391 : }
392 863 : else if (m_decl.GetType() == GAAT_STRING_LIST)
393 : {
394 2 : return Set(std::vector<std::string>{std::to_string(value)});
395 : }
396 :
397 863 : if (m_decl.GetType() != GAAT_INTEGER)
398 : {
399 2 : CPLError(
400 : CE_Failure, CPLE_AppDefined,
401 : "Calling Set(int) on argument '%s' of type %s is not supported",
402 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
403 1 : return false;
404 : }
405 862 : return SetInternal(value);
406 : }
407 :
408 291 : bool GDALAlgorithmArg::Set(double value)
409 : {
410 294 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
411 294 : value <= INT_MAX && static_cast<int>(value) == value)
412 : {
413 2 : return Set(static_cast<int>(value));
414 : }
415 289 : else if (m_decl.GetType() == GAAT_STRING)
416 : {
417 2 : return Set(std::to_string(value));
418 : }
419 289 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
420 289 : value <= INT_MAX && static_cast<int>(value) == value)
421 : {
422 1 : return Set(std::vector<int>{static_cast<int>(value)});
423 : }
424 286 : else if (m_decl.GetType() == GAAT_REAL_LIST)
425 : {
426 0 : return Set(std::vector<double>{value});
427 : }
428 286 : else if (m_decl.GetType() == GAAT_STRING_LIST)
429 : {
430 2 : return Set(std::vector<std::string>{std::to_string(value)});
431 : }
432 285 : else if (m_decl.GetType() != GAAT_REAL)
433 : {
434 6 : CPLError(
435 : CE_Failure, CPLE_AppDefined,
436 : "Calling Set(double) on argument '%s' of type %s is not supported",
437 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
438 3 : return false;
439 : }
440 282 : return SetInternal(value);
441 : }
442 :
443 5597 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
444 : {
445 5600 : if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
446 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
447 : {
448 3 : CPLError(
449 : CE_Failure, CPLE_AppDefined,
450 : "Dataset object '%s' is created by algorithm and cannot be set "
451 : "as an input.",
452 3 : arg->GetName().c_str());
453 3 : return false;
454 : }
455 5594 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
456 : {
457 8 : CPLError(CE_Failure, CPLE_AppDefined,
458 : "Dataset%s '%s' must be provided by name, not as object.",
459 8 : arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
460 4 : return false;
461 : }
462 :
463 5590 : return true;
464 : }
465 :
466 33 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
467 : {
468 58 : if (m_decl.GetType() != GAAT_DATASET &&
469 25 : m_decl.GetType() != GAAT_DATASET_LIST)
470 : {
471 2 : CPLError(CE_Failure, CPLE_AppDefined,
472 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
473 : "is not supported",
474 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
475 1 : return false;
476 : }
477 32 : if (!CheckCanSetDatasetObject(this))
478 2 : return false;
479 30 : m_explicitlySet = true;
480 30 : if (m_decl.GetType() == GAAT_DATASET)
481 : {
482 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
483 6 : val.Set(ds);
484 : }
485 : else
486 : {
487 24 : CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
488 24 : auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
489 24 : val.resize(1);
490 24 : val[0].Set(ds);
491 : }
492 30 : return RunAllActions();
493 : }
494 :
495 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
496 : {
497 3 : if (m_decl.GetType() != GAAT_DATASET)
498 : {
499 2 : CPLError(CE_Failure, CPLE_AppDefined,
500 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
501 : "is not supported",
502 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
503 1 : return false;
504 : }
505 2 : if (!CheckCanSetDatasetObject(this))
506 1 : return false;
507 1 : m_explicitlySet = true;
508 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
509 1 : val.Set(std::move(ds));
510 1 : return RunAllActions();
511 : }
512 :
513 614 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
514 : {
515 614 : if (m_decl.GetType() != GAAT_DATASET)
516 : {
517 2 : CPLError(CE_Failure, CPLE_AppDefined,
518 : "Calling SetDatasetName() on argument '%s' of type %s is "
519 : "not supported",
520 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
521 1 : return false;
522 : }
523 613 : m_explicitlySet = true;
524 613 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
525 613 : return RunAllActions();
526 : }
527 :
528 1014 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
529 : {
530 1014 : if (m_decl.GetType() != GAAT_DATASET)
531 : {
532 2 : CPLError(CE_Failure, CPLE_AppDefined,
533 : "Calling SetFrom() on argument '%s' of type %s is "
534 : "not supported",
535 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
536 1 : return false;
537 : }
538 1013 : if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
539 1 : return false;
540 1012 : m_explicitlySet = true;
541 1012 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
542 1012 : return RunAllActions();
543 : }
544 :
545 1094 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
546 : {
547 1094 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
548 : {
549 3 : std::vector<int> v_i;
550 4 : for (const std::string &s : value)
551 : {
552 3 : errno = 0;
553 3 : char *endptr = nullptr;
554 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
555 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
556 2 : endptr == s.c_str() + s.size())
557 : {
558 1 : v_i.push_back(static_cast<int>(v));
559 : }
560 : else
561 : {
562 2 : break;
563 : }
564 : }
565 3 : if (v_i.size() == value.size())
566 1 : return Set(v_i);
567 : }
568 1091 : else if (m_decl.GetType() == GAAT_REAL_LIST)
569 : {
570 2 : std::vector<double> v_d;
571 3 : for (const std::string &s : value)
572 : {
573 2 : char *endptr = nullptr;
574 2 : const double v = CPLStrtod(s.c_str(), &endptr);
575 2 : if (endptr == s.c_str() + s.size())
576 : {
577 1 : v_d.push_back(v);
578 : }
579 : else
580 : {
581 1 : break;
582 : }
583 : }
584 2 : if (v_d.size() == value.size())
585 1 : return Set(v_d);
586 : }
587 2176 : else if ((m_decl.GetType() == GAAT_INTEGER ||
588 2173 : m_decl.GetType() == GAAT_REAL ||
589 3264 : m_decl.GetType() == GAAT_STRING) &&
590 5 : value.size() == 1)
591 : {
592 4 : return Set(value[0]);
593 : }
594 1085 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
595 : {
596 30 : std::vector<GDALArgDatasetValue> dsVector;
597 46 : for (const std::string &s : value)
598 31 : dsVector.emplace_back(s);
599 15 : return Set(std::move(dsVector));
600 : }
601 :
602 1073 : if (m_decl.GetType() != GAAT_STRING_LIST)
603 : {
604 10 : CPLError(CE_Failure, CPLE_AppDefined,
605 : "Calling Set(const std::vector<std::string> &) on argument "
606 : "'%s' of type %s is not supported",
607 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
608 5 : return false;
609 : }
610 :
611 2117 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
612 1049 : m_decl.IsRemoveSQLCommentsEnabled())
613 : {
614 38 : std::vector<std::string> newValue(value);
615 41 : for (auto &s : newValue)
616 : {
617 22 : if (!ProcessString(s))
618 0 : return false;
619 : }
620 19 : return SetInternal(newValue);
621 : }
622 : else
623 : {
624 1049 : return SetInternal(value);
625 : }
626 : }
627 :
628 177 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
629 : {
630 177 : if (m_decl.GetType() == GAAT_REAL_LIST)
631 : {
632 2 : std::vector<double> v_d;
633 2 : for (int i : value)
634 1 : v_d.push_back(i);
635 1 : return Set(v_d);
636 : }
637 176 : else if (m_decl.GetType() == GAAT_STRING_LIST)
638 : {
639 2 : std::vector<std::string> v_s;
640 3 : for (int i : value)
641 2 : v_s.push_back(std::to_string(i));
642 1 : return Set(v_s);
643 : }
644 348 : else if ((m_decl.GetType() == GAAT_INTEGER ||
645 344 : m_decl.GetType() == GAAT_REAL ||
646 521 : m_decl.GetType() == GAAT_STRING) &&
647 5 : value.size() == 1)
648 : {
649 3 : return Set(value[0]);
650 : }
651 :
652 172 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
653 : {
654 6 : CPLError(CE_Failure, CPLE_AppDefined,
655 : "Calling Set(const std::vector<int> &) on argument '%s' of "
656 : "type %s is not supported",
657 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
658 3 : return false;
659 : }
660 169 : return SetInternal(value);
661 : }
662 :
663 273 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
664 : {
665 273 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
666 : {
667 2 : std::vector<int> v_i;
668 3 : for (double d : value)
669 : {
670 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
671 : {
672 1 : v_i.push_back(static_cast<int>(d));
673 : }
674 : else
675 : {
676 : break;
677 : }
678 : }
679 2 : if (v_i.size() == value.size())
680 1 : return Set(v_i);
681 : }
682 271 : else if (m_decl.GetType() == GAAT_STRING_LIST)
683 : {
684 2 : std::vector<std::string> v_s;
685 3 : for (double d : value)
686 2 : v_s.push_back(std::to_string(d));
687 1 : return Set(v_s);
688 : }
689 539 : else if ((m_decl.GetType() == GAAT_INTEGER ||
690 537 : m_decl.GetType() == GAAT_REAL ||
691 808 : m_decl.GetType() == GAAT_STRING) &&
692 3 : value.size() == 1)
693 : {
694 3 : return Set(value[0]);
695 : }
696 :
697 268 : if (m_decl.GetType() != GAAT_REAL_LIST)
698 : {
699 4 : CPLError(CE_Failure, CPLE_AppDefined,
700 : "Calling Set(const std::vector<double> &) on argument '%s' of "
701 : "type %s is not supported",
702 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
703 2 : return false;
704 : }
705 266 : return SetInternal(value);
706 : }
707 :
708 3547 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
709 : {
710 3547 : if (m_decl.GetType() != GAAT_DATASET_LIST)
711 : {
712 2 : CPLError(CE_Failure, CPLE_AppDefined,
713 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
714 : "argument '%s' of type %s is not supported",
715 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
716 1 : return false;
717 : }
718 3546 : m_explicitlySet = true;
719 3546 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
720 3546 : return RunAllActions();
721 : }
722 :
723 : GDALAlgorithmArg &
724 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
725 : {
726 0 : Set(std::move(value));
727 0 : return *this;
728 : }
729 :
730 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
731 : {
732 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
733 1 : return Set(value.exportToWkt(apszOptions));
734 : }
735 :
736 4336 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
737 : {
738 4336 : if (m_decl.GetType() != other.GetType())
739 : {
740 2 : CPLError(CE_Failure, CPLE_AppDefined,
741 : "Calling SetFrom() on argument '%s' of type %s whereas "
742 : "other argument type is %s is not supported",
743 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
744 : GDALAlgorithmArgTypeName(other.GetType()));
745 1 : return false;
746 : }
747 :
748 4335 : switch (m_decl.GetType())
749 : {
750 92 : case GAAT_BOOLEAN:
751 92 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
752 92 : break;
753 809 : case GAAT_STRING:
754 1618 : *std::get<std::string *>(m_value) =
755 809 : *std::get<std::string *>(other.m_value);
756 809 : break;
757 6 : case GAAT_INTEGER:
758 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
759 6 : break;
760 1 : case GAAT_REAL:
761 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
762 1 : break;
763 1008 : case GAAT_DATASET:
764 1008 : return SetFrom(other.Get<GDALArgDatasetValue>());
765 60 : case GAAT_STRING_LIST:
766 120 : *std::get<std::vector<std::string> *>(m_value) =
767 60 : *std::get<std::vector<std::string> *>(other.m_value);
768 60 : break;
769 1 : case GAAT_INTEGER_LIST:
770 2 : *std::get<std::vector<int> *>(m_value) =
771 1 : *std::get<std::vector<int> *>(other.m_value);
772 1 : break;
773 1 : case GAAT_REAL_LIST:
774 2 : *std::get<std::vector<double> *>(m_value) =
775 1 : *std::get<std::vector<double> *>(other.m_value);
776 1 : break;
777 2357 : case GAAT_DATASET_LIST:
778 : {
779 2357 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
780 2363 : for (const auto &val :
781 7083 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
782 : {
783 4726 : GDALArgDatasetValue v;
784 2363 : v.SetFrom(val);
785 2363 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
786 2363 : ->push_back(std::move(v));
787 : }
788 2357 : break;
789 : }
790 : }
791 3327 : m_explicitlySet = true;
792 3327 : return RunAllActions();
793 : }
794 :
795 : /************************************************************************/
796 : /* GDALAlgorithmArg::RunAllActions() */
797 : /************************************************************************/
798 :
799 16574 : bool GDALAlgorithmArg::RunAllActions()
800 : {
801 16574 : if (!RunValidationActions())
802 147 : return false;
803 16427 : RunActions();
804 16427 : return true;
805 : }
806 :
807 : /************************************************************************/
808 : /* GDALAlgorithmArg::RunActions() */
809 : /************************************************************************/
810 :
811 16428 : void GDALAlgorithmArg::RunActions()
812 : {
813 16728 : for (const auto &f : m_actions)
814 300 : f();
815 16428 : }
816 :
817 : /************************************************************************/
818 : /* GDALAlgorithmArg::ValidateChoice() */
819 : /************************************************************************/
820 :
821 : // Returns the canonical value if matching a valid choice, or empty string
822 : // otherwise.
823 2676 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
824 : {
825 15490 : for (const std::string &choice : GetChoices())
826 : {
827 15372 : if (EQUAL(value.c_str(), choice.c_str()))
828 : {
829 2558 : return choice;
830 : }
831 : }
832 :
833 190 : for (const std::string &choice : GetHiddenChoices())
834 : {
835 172 : if (EQUAL(value.c_str(), choice.c_str()))
836 : {
837 100 : return choice;
838 : }
839 : }
840 :
841 36 : std::string expected;
842 222 : for (const auto &choice : GetChoices())
843 : {
844 204 : if (!expected.empty())
845 186 : expected += ", ";
846 204 : expected += '\'';
847 204 : expected += choice;
848 204 : expected += '\'';
849 : }
850 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
851 : {
852 6 : return "?";
853 : }
854 24 : CPLError(CE_Failure, CPLE_IllegalArg,
855 : "Invalid value '%s' for string argument '%s'. Should be "
856 : "one among %s.",
857 12 : value.c_str(), GetName().c_str(), expected.c_str());
858 12 : return std::string();
859 : }
860 :
861 : /************************************************************************/
862 : /* GDALAlgorithmArg::ValidateIntRange() */
863 : /************************************************************************/
864 :
865 2752 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
866 : {
867 2752 : bool ret = true;
868 :
869 2752 : const auto [minVal, minValIsIncluded] = GetMinValue();
870 2752 : if (!std::isnan(minVal))
871 : {
872 2080 : if (minValIsIncluded && val < minVal)
873 : {
874 3 : CPLError(CE_Failure, CPLE_IllegalArg,
875 : "Value of argument '%s' is %d, but should be >= %d",
876 3 : GetName().c_str(), val, static_cast<int>(minVal));
877 3 : ret = false;
878 : }
879 2077 : else if (!minValIsIncluded && val <= minVal)
880 : {
881 1 : CPLError(CE_Failure, CPLE_IllegalArg,
882 : "Value of argument '%s' is %d, but should be > %d",
883 1 : GetName().c_str(), val, static_cast<int>(minVal));
884 1 : ret = false;
885 : }
886 : }
887 :
888 2752 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
889 2752 : if (!std::isnan(maxVal))
890 : {
891 :
892 430 : if (maxValIsIncluded && val > maxVal)
893 : {
894 1 : CPLError(CE_Failure, CPLE_IllegalArg,
895 : "Value of argument '%s' is %d, but should be <= %d",
896 1 : GetName().c_str(), val, static_cast<int>(maxVal));
897 1 : ret = false;
898 : }
899 429 : else if (!maxValIsIncluded && val >= maxVal)
900 : {
901 1 : CPLError(CE_Failure, CPLE_IllegalArg,
902 : "Value of argument '%s' is %d, but should be < %d",
903 1 : GetName().c_str(), val, static_cast<int>(maxVal));
904 1 : ret = false;
905 : }
906 : }
907 :
908 2752 : return ret;
909 : }
910 :
911 : /************************************************************************/
912 : /* GDALAlgorithmArg::ValidateRealRange() */
913 : /************************************************************************/
914 :
915 2147 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
916 : {
917 2147 : bool ret = true;
918 :
919 2147 : const auto [minVal, minValIsIncluded] = GetMinValue();
920 2147 : if (!std::isnan(minVal))
921 : {
922 216 : if (minValIsIncluded && !(val >= minVal))
923 : {
924 11 : CPLError(CE_Failure, CPLE_IllegalArg,
925 : "Value of argument '%s' is %g, but should be >= %g",
926 11 : GetName().c_str(), val, minVal);
927 11 : ret = false;
928 : }
929 205 : else if (!minValIsIncluded && !(val > minVal))
930 : {
931 4 : CPLError(CE_Failure, CPLE_IllegalArg,
932 : "Value of argument '%s' is %g, but should be > %g",
933 4 : GetName().c_str(), val, minVal);
934 4 : ret = false;
935 : }
936 : }
937 :
938 2147 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
939 2147 : if (!std::isnan(maxVal))
940 : {
941 :
942 58 : if (maxValIsIncluded && !(val <= maxVal))
943 : {
944 2 : CPLError(CE_Failure, CPLE_IllegalArg,
945 : "Value of argument '%s' is %g, but should be <= %g",
946 2 : GetName().c_str(), val, maxVal);
947 2 : ret = false;
948 : }
949 56 : else if (!maxValIsIncluded && !(val < maxVal))
950 : {
951 1 : CPLError(CE_Failure, CPLE_IllegalArg,
952 : "Value of argument '%s' is %g, but should be < %g",
953 1 : GetName().c_str(), val, maxVal);
954 1 : ret = false;
955 : }
956 : }
957 :
958 2147 : return ret;
959 : }
960 :
961 : /************************************************************************/
962 : /* CheckDuplicateValues() */
963 : /************************************************************************/
964 :
965 : template <class T>
966 92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
967 : const std::vector<T> &values)
968 : {
969 184 : auto tmpValues = values;
970 92 : bool bHasDupValues = false;
971 : if constexpr (std::is_floating_point_v<T>)
972 : {
973 : // Avoid undefined behavior with NaN values
974 4 : std::sort(tmpValues.begin(), tmpValues.end(),
975 21 : [](T a, T b)
976 : {
977 21 : if (std::isnan(a) && !std::isnan(b))
978 3 : return true;
979 18 : if (std::isnan(b))
980 10 : return false;
981 8 : return a < b;
982 : });
983 :
984 : bHasDupValues =
985 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
986 6 : [](T a, T b)
987 : {
988 6 : if (std::isnan(a) && std::isnan(b))
989 1 : return true;
990 5 : return a == b;
991 8 : }) != tmpValues.end();
992 : }
993 : else
994 : {
995 88 : std::sort(tmpValues.begin(), tmpValues.end());
996 88 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
997 176 : tmpValues.end()) != tmpValues.end();
998 : }
999 92 : if (bHasDupValues)
1000 : {
1001 10 : CPLError(CE_Failure, CPLE_AppDefined,
1002 : "'%s' must be a list of unique values.",
1003 10 : arg->GetName().c_str());
1004 10 : return false;
1005 : }
1006 82 : return true;
1007 : }
1008 :
1009 : /************************************************************************/
1010 : /* GDALAlgorithmArg::RunValidationActions() */
1011 : /************************************************************************/
1012 :
1013 36843 : bool GDALAlgorithmArg::RunValidationActions()
1014 : {
1015 36843 : bool ret = true;
1016 :
1017 36843 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1018 : {
1019 1755 : auto &val = Get<std::string>();
1020 3510 : std::string validVal = ValidateChoice(val);
1021 1755 : if (validVal.empty())
1022 7 : ret = false;
1023 : else
1024 1748 : val = std::move(validVal);
1025 : }
1026 35088 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1027 : {
1028 661 : auto &values = Get<std::vector<std::string>>();
1029 1582 : for (std::string &val : values)
1030 : {
1031 1842 : std::string validVal = ValidateChoice(val);
1032 921 : if (validVal.empty())
1033 5 : ret = false;
1034 : else
1035 916 : val = std::move(validVal);
1036 : }
1037 : }
1038 :
1039 : const auto CheckMinCharCount =
1040 1097 : [this, &ret](const std::string &val, int nMinCharCount)
1041 : {
1042 1085 : if (val.size() < static_cast<size_t>(nMinCharCount))
1043 : {
1044 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1045 : "Value of argument '%s' is '%s', but should have at least "
1046 : "%d character%s",
1047 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1048 : nMinCharCount > 1 ? "s" : "");
1049 6 : ret = false;
1050 : }
1051 37928 : };
1052 :
1053 : const auto CheckMaxCharCount =
1054 13214 : [this, &ret](const std::string &val, int nMaxCharCount)
1055 : {
1056 13212 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1057 : {
1058 2 : CPLError(
1059 : CE_Failure, CPLE_IllegalArg,
1060 : "Value of argument '%s' is '%s', but should have no more than "
1061 : "%d character%s",
1062 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1063 : nMaxCharCount > 1 ? "s" : "");
1064 1 : ret = false;
1065 : }
1066 50055 : };
1067 :
1068 36843 : switch (GetType())
1069 : {
1070 2770 : case GAAT_BOOLEAN:
1071 2770 : break;
1072 :
1073 10383 : case GAAT_STRING:
1074 : {
1075 10383 : const auto &val = Get<std::string>();
1076 10383 : const int nMinCharCount = GetMinCharCount();
1077 10383 : if (nMinCharCount > 0)
1078 : {
1079 1003 : CheckMinCharCount(val, nMinCharCount);
1080 : }
1081 :
1082 10383 : const int nMaxCharCount = GetMaxCharCount();
1083 10383 : CheckMaxCharCount(val, nMaxCharCount);
1084 10383 : break;
1085 : }
1086 :
1087 2357 : case GAAT_STRING_LIST:
1088 : {
1089 2357 : const int nMinCharCount = GetMinCharCount();
1090 2357 : const int nMaxCharCount = GetMaxCharCount();
1091 2357 : const auto &values = Get<std::vector<std::string>>();
1092 5186 : for (const auto &val : values)
1093 : {
1094 2829 : if (nMinCharCount > 0)
1095 82 : CheckMinCharCount(val, nMinCharCount);
1096 2829 : CheckMaxCharCount(val, nMaxCharCount);
1097 : }
1098 :
1099 2431 : if (!GetDuplicateValuesAllowed() &&
1100 74 : !CheckDuplicateValues(this, values))
1101 2 : ret = false;
1102 2357 : break;
1103 : }
1104 :
1105 2032 : case GAAT_INTEGER:
1106 : {
1107 2032 : ret = ValidateIntRange(Get<int>()) && ret;
1108 2032 : break;
1109 : }
1110 :
1111 356 : case GAAT_INTEGER_LIST:
1112 : {
1113 356 : const auto &values = Get<std::vector<int>>();
1114 1076 : for (int v : values)
1115 720 : ret = ValidateIntRange(v) && ret;
1116 :
1117 359 : if (!GetDuplicateValuesAllowed() &&
1118 3 : !CheckDuplicateValues(this, values))
1119 1 : ret = false;
1120 356 : break;
1121 : }
1122 :
1123 549 : case GAAT_REAL:
1124 : {
1125 549 : ret = ValidateRealRange(Get<double>()) && ret;
1126 549 : break;
1127 : }
1128 :
1129 588 : case GAAT_REAL_LIST:
1130 : {
1131 588 : const auto &values = Get<std::vector<double>>();
1132 2186 : for (double v : values)
1133 1598 : ret = ValidateRealRange(v) && ret;
1134 :
1135 592 : if (!GetDuplicateValuesAllowed() &&
1136 4 : !CheckDuplicateValues(this, values))
1137 2 : ret = false;
1138 588 : break;
1139 : }
1140 :
1141 6057 : case GAAT_DATASET:
1142 6057 : break;
1143 :
1144 11751 : case GAAT_DATASET_LIST:
1145 : {
1146 11751 : if (!GetDuplicateValuesAllowed())
1147 : {
1148 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1149 22 : std::vector<std::string> aosValues;
1150 34 : for (const auto &v : values)
1151 : {
1152 23 : const GDALDataset *poDS = v.GetDatasetRef();
1153 23 : if (poDS)
1154 : {
1155 16 : auto poDriver = poDS->GetDriver();
1156 : // The dataset name for a MEM driver is not relevant,
1157 : // so use the pointer address
1158 32 : if ((poDriver &&
1159 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1160 8 : poDS->GetDescription()[0] == 0)
1161 : {
1162 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1163 : }
1164 : else
1165 : {
1166 8 : aosValues.push_back(poDS->GetDescription());
1167 : }
1168 : }
1169 : else
1170 : {
1171 7 : aosValues.push_back(v.GetName());
1172 : }
1173 : }
1174 11 : if (!CheckDuplicateValues(this, aosValues))
1175 5 : ret = false;
1176 : }
1177 11751 : break;
1178 : }
1179 : }
1180 :
1181 36843 : if (GDALAlgorithmArgTypeIsList(GetType()))
1182 : {
1183 15052 : int valueCount = 0;
1184 15052 : if (GetType() == GAAT_STRING_LIST)
1185 : {
1186 2357 : valueCount =
1187 2357 : static_cast<int>(Get<std::vector<std::string>>().size());
1188 : }
1189 12695 : else if (GetType() == GAAT_INTEGER_LIST)
1190 : {
1191 356 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1192 : }
1193 12339 : else if (GetType() == GAAT_REAL_LIST)
1194 : {
1195 588 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1196 : }
1197 11751 : else if (GetType() == GAAT_DATASET_LIST)
1198 : {
1199 11751 : valueCount = static_cast<int>(
1200 11751 : Get<std::vector<GDALArgDatasetValue>>().size());
1201 : }
1202 :
1203 15052 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1204 : {
1205 14 : ReportError(CE_Failure, CPLE_AppDefined,
1206 : "%d value%s been specified for argument '%s', "
1207 : "whereas exactly %d %s expected.",
1208 : valueCount, valueCount > 1 ? "s have" : " has",
1209 7 : GetName().c_str(), GetMinCount(),
1210 7 : GetMinCount() > 1 ? "were" : "was");
1211 7 : ret = false;
1212 : }
1213 15045 : else if (valueCount < GetMinCount())
1214 : {
1215 6 : ReportError(CE_Failure, CPLE_AppDefined,
1216 : "Only %d value%s been specified for argument '%s', "
1217 : "whereas at least %d %s expected.",
1218 : valueCount, valueCount > 1 ? "s have" : " has",
1219 3 : GetName().c_str(), GetMinCount(),
1220 3 : GetMinCount() > 1 ? "were" : "was");
1221 3 : ret = false;
1222 : }
1223 15042 : else if (valueCount > GetMaxCount())
1224 : {
1225 2 : ReportError(CE_Failure, CPLE_AppDefined,
1226 : "%d value%s been specified for argument '%s', "
1227 : "whereas at most %d %s expected.",
1228 : valueCount, valueCount > 1 ? "s have" : " has",
1229 1 : GetName().c_str(), GetMaxCount(),
1230 1 : GetMaxCount() > 1 ? "were" : "was");
1231 1 : ret = false;
1232 : }
1233 : }
1234 :
1235 36843 : if (ret)
1236 : {
1237 43803 : for (const auto &f : m_validationActions)
1238 : {
1239 7024 : if (!f())
1240 92 : ret = false;
1241 : }
1242 : }
1243 :
1244 36843 : return ret;
1245 : }
1246 :
1247 : /************************************************************************/
1248 : /* GDALAlgorithmArg::ReportError() */
1249 : /************************************************************************/
1250 :
1251 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1252 : const char *fmt, ...) const
1253 : {
1254 : va_list args;
1255 11 : va_start(args, fmt);
1256 11 : if (m_owner)
1257 : {
1258 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1259 22 : CPLString().vPrintf(fmt, args).c_str());
1260 : }
1261 : else
1262 : {
1263 0 : CPLError(eErrClass, err_no, "%s",
1264 0 : CPLString().vPrintf(fmt, args).c_str());
1265 : }
1266 11 : va_end(args);
1267 11 : }
1268 :
1269 : /************************************************************************/
1270 : /* GDALAlgorithmArg::GetEscapedString() */
1271 : /************************************************************************/
1272 :
1273 : /* static */
1274 181 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1275 : {
1276 199 : if (s.find_first_of("\" \\,") != std::string::npos &&
1277 9 : !(s.size() > 4 &&
1278 9 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1279 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1280 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1281 : {
1282 14 : return std::string("\"")
1283 : .append(
1284 14 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1285 7 : .append("\"");
1286 : }
1287 : else
1288 : {
1289 174 : return s;
1290 : }
1291 : }
1292 :
1293 : /************************************************************************/
1294 : /* GDALAlgorithmArg::Serialize() */
1295 : /************************************************************************/
1296 :
1297 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1298 : bool absolutePath) const
1299 : {
1300 39 : serializedArg.clear();
1301 :
1302 39 : if (!IsExplicitlySet())
1303 : {
1304 0 : return false;
1305 : }
1306 :
1307 78 : std::string ret = "--";
1308 39 : ret += GetName();
1309 39 : if (GetType() == GAAT_BOOLEAN)
1310 : {
1311 0 : serializedArg = std::move(ret);
1312 0 : return true;
1313 : }
1314 :
1315 5 : const auto AddListValueSeparator = [this, &ret]()
1316 : {
1317 1 : if (GetPackedValuesAllowed())
1318 : {
1319 0 : ret += ',';
1320 : }
1321 : else
1322 : {
1323 1 : ret += " --";
1324 1 : ret += GetName();
1325 1 : ret += ' ';
1326 : }
1327 40 : };
1328 :
1329 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1330 : {
1331 : VSIStatBufL sStat;
1332 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1333 0 : !CPLIsFilenameRelative(filename.c_str()))
1334 0 : return filename;
1335 0 : char *pszCWD = CPLGetCurrentDir();
1336 0 : if (!pszCWD)
1337 0 : return filename;
1338 : const auto absPath =
1339 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1340 0 : CPLFree(pszCWD);
1341 0 : return absPath;
1342 : };
1343 :
1344 39 : ret += ' ';
1345 39 : switch (GetType())
1346 : {
1347 0 : case GAAT_BOOLEAN:
1348 0 : break;
1349 8 : case GAAT_STRING:
1350 : {
1351 8 : const auto &val = Get<std::string>();
1352 8 : ret += GetEscapedString(val);
1353 8 : break;
1354 : }
1355 0 : case GAAT_INTEGER:
1356 : {
1357 0 : ret += CPLSPrintf("%d", Get<int>());
1358 0 : break;
1359 : }
1360 0 : case GAAT_REAL:
1361 : {
1362 0 : ret += CPLSPrintf("%.17g", Get<double>());
1363 0 : break;
1364 : }
1365 2 : case GAAT_DATASET:
1366 : {
1367 2 : const auto &val = Get<GDALArgDatasetValue>();
1368 2 : const auto &str = val.GetName();
1369 2 : if (str.empty())
1370 : {
1371 0 : return false;
1372 : }
1373 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1374 2 : break;
1375 : }
1376 4 : case GAAT_STRING_LIST:
1377 : {
1378 4 : const auto &vals = Get<std::vector<std::string>>();
1379 8 : for (size_t i = 0; i < vals.size(); ++i)
1380 : {
1381 4 : if (i > 0)
1382 1 : AddListValueSeparator();
1383 4 : ret += GetEscapedString(vals[i]);
1384 : }
1385 4 : break;
1386 : }
1387 0 : case GAAT_INTEGER_LIST:
1388 : {
1389 0 : const auto &vals = Get<std::vector<int>>();
1390 0 : for (size_t i = 0; i < vals.size(); ++i)
1391 : {
1392 0 : if (i > 0)
1393 0 : AddListValueSeparator();
1394 0 : ret += CPLSPrintf("%d", vals[i]);
1395 : }
1396 0 : break;
1397 : }
1398 0 : case GAAT_REAL_LIST:
1399 : {
1400 0 : const auto &vals = Get<std::vector<double>>();
1401 0 : for (size_t i = 0; i < vals.size(); ++i)
1402 : {
1403 0 : if (i > 0)
1404 0 : AddListValueSeparator();
1405 0 : ret += CPLSPrintf("%.17g", vals[i]);
1406 : }
1407 0 : break;
1408 : }
1409 25 : case GAAT_DATASET_LIST:
1410 : {
1411 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1412 49 : for (size_t i = 0; i < vals.size(); ++i)
1413 : {
1414 25 : if (i > 0)
1415 0 : AddListValueSeparator();
1416 25 : const auto &val = vals[i];
1417 25 : const auto &str = val.GetName();
1418 25 : if (str.empty())
1419 : {
1420 1 : return false;
1421 : }
1422 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1423 24 : : str);
1424 : }
1425 24 : break;
1426 : }
1427 : }
1428 :
1429 38 : serializedArg = std::move(ret);
1430 38 : return true;
1431 : }
1432 :
1433 : /************************************************************************/
1434 : /* ~GDALInConstructionAlgorithmArg() */
1435 : /************************************************************************/
1436 :
1437 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1438 :
1439 : /************************************************************************/
1440 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1441 : /************************************************************************/
1442 :
1443 : GDALInConstructionAlgorithmArg &
1444 64550 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1445 : {
1446 64550 : m_decl.AddAlias(alias);
1447 64550 : if (m_owner)
1448 64550 : m_owner->AddAliasFor(this, alias);
1449 64550 : return *this;
1450 : }
1451 :
1452 : /************************************************************************/
1453 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1454 : /************************************************************************/
1455 :
1456 : GDALInConstructionAlgorithmArg &
1457 17405 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1458 : {
1459 17405 : m_decl.AddHiddenAlias(alias);
1460 17405 : if (m_owner)
1461 17405 : m_owner->AddAliasFor(this, alias);
1462 17405 : return *this;
1463 : }
1464 :
1465 : /************************************************************************/
1466 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1467 : /************************************************************************/
1468 :
1469 : GDALInConstructionAlgorithmArg &
1470 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1471 : {
1472 48 : m_decl.AddShortNameAlias(shortNameAlias);
1473 48 : if (m_owner)
1474 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1475 48 : return *this;
1476 : }
1477 :
1478 : /************************************************************************/
1479 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1480 : /************************************************************************/
1481 :
1482 22269 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1483 : {
1484 22269 : m_decl.SetPositional();
1485 22269 : if (m_owner)
1486 22269 : m_owner->SetPositional(this);
1487 22269 : return *this;
1488 : }
1489 :
1490 : /************************************************************************/
1491 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1492 : /************************************************************************/
1493 :
1494 1323 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1495 2646 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1496 1323 : m_nameSet(true)
1497 : {
1498 1323 : if (m_poDS)
1499 1323 : m_poDS->Reference();
1500 1323 : }
1501 :
1502 : /************************************************************************/
1503 : /* GDALArgDatasetValue::Set() */
1504 : /************************************************************************/
1505 :
1506 2352 : void GDALArgDatasetValue::Set(const std::string &name)
1507 : {
1508 2352 : Close();
1509 2352 : m_name = name;
1510 2352 : m_nameSet = true;
1511 2352 : if (m_ownerArg)
1512 2347 : m_ownerArg->NotifyValueSet();
1513 2352 : }
1514 :
1515 : /************************************************************************/
1516 : /* GDALArgDatasetValue::Set() */
1517 : /************************************************************************/
1518 :
1519 2092 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1520 : {
1521 2092 : Close();
1522 2092 : m_poDS = poDS.release();
1523 2092 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1524 2092 : m_nameSet = true;
1525 2092 : if (m_ownerArg)
1526 1933 : m_ownerArg->NotifyValueSet();
1527 2092 : }
1528 :
1529 : /************************************************************************/
1530 : /* GDALArgDatasetValue::Set() */
1531 : /************************************************************************/
1532 :
1533 8336 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1534 : {
1535 8336 : Close();
1536 8336 : m_poDS = poDS;
1537 8336 : if (m_poDS)
1538 7451 : m_poDS->Reference();
1539 8336 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1540 8336 : m_nameSet = true;
1541 8336 : if (m_ownerArg)
1542 3442 : m_ownerArg->NotifyValueSet();
1543 8336 : }
1544 :
1545 : /************************************************************************/
1546 : /* GDALArgDatasetValue::SetFrom() */
1547 : /************************************************************************/
1548 :
1549 3375 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1550 : {
1551 3375 : Close();
1552 3375 : m_name = other.m_name;
1553 3375 : m_nameSet = other.m_nameSet;
1554 3375 : m_poDS = other.m_poDS;
1555 3375 : if (m_poDS)
1556 2349 : m_poDS->Reference();
1557 3375 : }
1558 :
1559 : /************************************************************************/
1560 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1561 : /************************************************************************/
1562 :
1563 32437 : GDALArgDatasetValue::~GDALArgDatasetValue()
1564 : {
1565 32437 : Close();
1566 32437 : }
1567 :
1568 : /************************************************************************/
1569 : /* GDALArgDatasetValue::Close() */
1570 : /************************************************************************/
1571 :
1572 54133 : bool GDALArgDatasetValue::Close()
1573 : {
1574 54133 : bool ret = true;
1575 54133 : if (m_poDS && m_poDS->Dereference() == 0)
1576 : {
1577 3479 : ret = m_poDS->Close() == CE_None;
1578 3479 : delete m_poDS;
1579 : }
1580 54133 : m_poDS = nullptr;
1581 54133 : return ret;
1582 : }
1583 :
1584 : /************************************************************************/
1585 : /* GDALArgDatasetValue::operator=() */
1586 : /************************************************************************/
1587 :
1588 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1589 : {
1590 2 : Close();
1591 2 : m_poDS = other.m_poDS;
1592 2 : m_name = other.m_name;
1593 2 : m_nameSet = other.m_nameSet;
1594 2 : other.m_poDS = nullptr;
1595 2 : other.m_name.clear();
1596 2 : other.m_nameSet = false;
1597 2 : return *this;
1598 : }
1599 :
1600 : /************************************************************************/
1601 : /* GDALArgDatasetValue::GetDataset() */
1602 : /************************************************************************/
1603 :
1604 1069 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1605 : {
1606 1069 : if (m_poDS)
1607 1068 : m_poDS->Reference();
1608 1069 : return m_poDS;
1609 : }
1610 :
1611 : /************************************************************************/
1612 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1613 : /************************************************************************/
1614 :
1615 3240 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1616 3240 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1617 : {
1618 3240 : other.m_poDS = nullptr;
1619 3240 : other.m_name.clear();
1620 3240 : }
1621 :
1622 : /************************************************************************/
1623 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1624 : /************************************************************************/
1625 :
1626 3395 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1627 : bool noneAllowed, const std::vector<std::string> &specialValues)
1628 : {
1629 3395 : if (GetType() != GAAT_STRING)
1630 : {
1631 1 : CPLError(CE_Failure, CPLE_AppDefined,
1632 : "SetIsCRSArg() can only be called on a String argument");
1633 1 : return *this;
1634 : }
1635 : AddValidationAction(
1636 763 : [this, noneAllowed, specialValues]()
1637 : {
1638 : const std::string &osVal =
1639 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1640 378 : ->Get<std::string>();
1641 378 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1642 0 : return true;
1643 :
1644 743 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1645 365 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1646 743 : specialValues.end())
1647 : {
1648 357 : OGRSpatialReference oSRS;
1649 357 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1650 : {
1651 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1652 : "Invalid value for '%s' argument",
1653 7 : GetName().c_str());
1654 7 : return false;
1655 : }
1656 : }
1657 371 : return true;
1658 3394 : });
1659 :
1660 : SetAutoCompleteFunction(
1661 44 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1662 : {
1663 11 : bool bIsRaster = false;
1664 11 : OGREnvelope sDatasetLongLatEnv;
1665 22 : std::string osCelestialBodyName;
1666 11 : if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1667 : {
1668 11 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1669 11 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1670 : {
1671 : auto &val =
1672 11 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1673 11 : if (val.size() == 1)
1674 : {
1675 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1676 : auto poDS = std::unique_ptr<GDALDataset>(
1677 4 : GDALDataset::Open(val[0].GetName().c_str()));
1678 2 : if (poDS)
1679 : {
1680 2 : bIsRaster = poDS->GetRasterCount() != 0;
1681 2 : if (auto poCRS = poDS->GetSpatialRef())
1682 : {
1683 : const char *pszCelestialBodyName =
1684 2 : poCRS->GetCelestialBodyName();
1685 2 : if (pszCelestialBodyName)
1686 2 : osCelestialBodyName = pszCelestialBodyName;
1687 :
1688 2 : if (!pszCelestialBodyName ||
1689 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1690 : {
1691 0 : OGRSpatialReference oLongLat;
1692 0 : oLongLat.CopyGeogCSFrom(poCRS);
1693 0 : oLongLat.SetAxisMappingStrategy(
1694 : OAMS_TRADITIONAL_GIS_ORDER);
1695 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1696 0 : &oLongLat);
1697 : }
1698 : else
1699 : {
1700 2 : poDS->GetExtentWGS84LongLat(
1701 2 : &sDatasetLongLatEnv);
1702 : }
1703 : }
1704 : }
1705 : }
1706 : }
1707 : }
1708 :
1709 : const auto IsCRSCompatible =
1710 42959 : [bIsRaster, &sDatasetLongLatEnv,
1711 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1712 : {
1713 42959 : if (!sDatasetLongLatEnv.IsInit())
1714 30685 : return true;
1715 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1716 11834 : !(bIsRaster &&
1717 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1718 11652 : crsInfo->dfWestLongitudeDeg <
1719 11652 : crsInfo->dfEastLongitudeDeg &&
1720 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1721 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1722 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1723 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1724 329 : ((!osCelestialBodyName.empty() &&
1725 658 : crsInfo->pszCelestialBodyName &&
1726 329 : osCelestialBodyName ==
1727 329 : crsInfo->pszCelestialBodyName) ||
1728 0 : (osCelestialBodyName.empty() &&
1729 12274 : !crsInfo->pszCelestialBodyName));
1730 11 : };
1731 :
1732 11 : std::vector<std::string> oRet;
1733 11 : if (noneAllowed)
1734 0 : oRet.push_back("none");
1735 11 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1736 11 : if (!currentValue.empty())
1737 : {
1738 : const CPLStringList aosTokens(
1739 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1740 7 : int nCount = 0;
1741 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1742 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1743 : nullptr, &nCount),
1744 14 : OSRDestroyCRSInfoList);
1745 14 : std::string osCode;
1746 :
1747 14 : std::vector<const OSRCRSInfo *> candidates;
1748 46270 : for (int i = 0; i < nCount; ++i)
1749 : {
1750 46263 : const auto *entry = (pCRSList.get())[i];
1751 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1752 : {
1753 49425 : if (aosTokens.size() == 1 ||
1754 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1755 : {
1756 12666 : if (candidates.empty())
1757 7 : osCode = entry->pszCode;
1758 12666 : candidates.push_back(entry);
1759 : }
1760 : }
1761 : }
1762 7 : if (candidates.size() == 1)
1763 : {
1764 1 : oRet.push_back(std::move(osCode));
1765 : }
1766 : else
1767 : {
1768 6 : if (sDatasetLongLatEnv.IsInit())
1769 : {
1770 2 : std::sort(
1771 : candidates.begin(), candidates.end(),
1772 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1773 : {
1774 2999 : const double dfXa =
1775 2999 : a->dfWestLongitudeDeg >
1776 2999 : a->dfEastLongitudeDeg
1777 2999 : ? a->dfWestLongitudeDeg -
1778 0 : a->dfEastLongitudeDeg
1779 2999 : : (180 - a->dfWestLongitudeDeg) +
1780 2999 : (a->dfEastLongitudeDeg - -180);
1781 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1782 2999 : a->dfSouthLatitudeDeg;
1783 2999 : const double dfXb =
1784 2999 : b->dfWestLongitudeDeg >
1785 2999 : b->dfEastLongitudeDeg
1786 2999 : ? b->dfWestLongitudeDeg -
1787 0 : b->dfEastLongitudeDeg
1788 2999 : : (180 - b->dfWestLongitudeDeg) +
1789 2999 : (b->dfEastLongitudeDeg - -180);
1790 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1791 2999 : b->dfSouthLatitudeDeg;
1792 2999 : const double diffArea =
1793 2999 : dfXa * dfYa - dfXb * dfYb;
1794 2999 : if (diffArea < 0)
1795 279 : return true;
1796 2720 : if (diffArea == 0)
1797 : {
1798 2506 : if (std::string_view(a->pszName) ==
1799 2506 : b->pszName)
1800 : {
1801 57 : if (a->eType ==
1802 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1803 13 : b->eType !=
1804 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1805 13 : return true;
1806 44 : if (a->eType ==
1807 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1808 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1809 9 : return true;
1810 35 : return false;
1811 : }
1812 4898 : return std::string_view(a->pszCode) <
1813 4898 : b->pszCode;
1814 : }
1815 214 : return false;
1816 : });
1817 : }
1818 :
1819 12671 : for (const auto *entry : candidates)
1820 : {
1821 25330 : std::string val = std::string(entry->pszCode)
1822 12665 : .append(" -- ")
1823 25330 : .append(entry->pszName);
1824 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1825 1294 : val.append(" (geographic 2D)");
1826 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1827 446 : val.append(" (geographic 3D)");
1828 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1829 397 : val.append(" (geocentric)");
1830 12665 : oRet.push_back(std::move(val));
1831 : }
1832 : }
1833 : }
1834 11 : if (currentValue.empty() || oRet.empty())
1835 : {
1836 : const CPLStringList aosAuthorities(
1837 8 : OSRGetAuthorityListFromDatabase());
1838 24 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1839 : {
1840 20 : int nCount = 0;
1841 20 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1842 : pszAuth, nullptr, &nCount));
1843 20 : if (nCount)
1844 16 : oRet.push_back(std::string(pszAuth).append(":"));
1845 : }
1846 : }
1847 22 : return oRet;
1848 3394 : });
1849 :
1850 3394 : return *this;
1851 : }
1852 :
1853 : /************************************************************************/
1854 : /* GDALAlgorithm::GDALAlgorithm() */
1855 : /************************************************************************/
1856 :
1857 22577 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1858 : const std::string &description,
1859 22577 : const std::string &helpURL)
1860 : : m_name(name), m_description(description), m_helpURL(helpURL),
1861 44804 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1862 22577 : ? "https://gdal.org" + m_helpURL
1863 67126 : : m_helpURL)
1864 : {
1865 : auto &helpArg =
1866 : AddArg("help", 'h', _("Display help message and exit"),
1867 45154 : &m_helpRequested)
1868 22577 : .SetHiddenForAPI()
1869 45154 : .SetCategory(GAAC_COMMON)
1870 14 : .AddAction([this]()
1871 22577 : { m_specialActionRequested = m_calledFromCommandLine; });
1872 : auto &helpDocArg =
1873 : AddArg("help-doc", 0,
1874 : _("Display help message for use by documentation"),
1875 45154 : &m_helpDocRequested)
1876 22577 : .SetHidden()
1877 12 : .AddAction([this]()
1878 22577 : { m_specialActionRequested = m_calledFromCommandLine; });
1879 : auto &jsonUsageArg =
1880 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1881 45154 : &m_JSONUsageRequested)
1882 22577 : .SetHiddenForAPI()
1883 45154 : .SetCategory(GAAC_COMMON)
1884 4 : .AddAction([this]()
1885 22577 : { m_specialActionRequested = m_calledFromCommandLine; });
1886 45154 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1887 45154 : .SetMetaVar("<KEY>=<VALUE>")
1888 22577 : .SetHiddenForAPI()
1889 45154 : .SetCategory(GAAC_COMMON)
1890 : .AddAction(
1891 2 : [this]()
1892 : {
1893 2 : ReportError(
1894 : CE_Warning, CPLE_AppDefined,
1895 : "Configuration options passed with the 'config' argument "
1896 : "are ignored");
1897 22577 : });
1898 :
1899 22577 : AddValidationAction(
1900 14019 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1901 : {
1902 7231 : if (!m_calledFromCommandLine && m_specialActionRequested)
1903 : {
1904 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1905 : {
1906 0 : if (arg->IsExplicitlySet())
1907 : {
1908 0 : ReportError(CE_Failure, CPLE_AppDefined,
1909 : "'%s' argument only available when called "
1910 : "from command line",
1911 0 : arg->GetName().c_str());
1912 0 : return false;
1913 : }
1914 : }
1915 : }
1916 7231 : return true;
1917 : });
1918 22577 : }
1919 :
1920 : /************************************************************************/
1921 : /* GDALAlgorithm::~GDALAlgorithm() */
1922 : /************************************************************************/
1923 :
1924 : GDALAlgorithm::~GDALAlgorithm() = default;
1925 :
1926 : /************************************************************************/
1927 : /* GDALAlgorithm::ParseArgument() */
1928 : /************************************************************************/
1929 :
1930 3272 : bool GDALAlgorithm::ParseArgument(
1931 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1932 : std::map<
1933 : GDALAlgorithmArg *,
1934 : std::variant<std::vector<std::string>, std::vector<int>,
1935 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1936 : &inConstructionValues)
1937 : {
1938 : const bool isListArg =
1939 3272 : GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1940 3272 : if (arg->IsExplicitlySet() && !isListArg)
1941 : {
1942 : // Hack for "gdal info" to be able to pass an opened raster dataset
1943 : // by "gdal raster info" to the "gdal vector info" algorithm.
1944 4 : if (arg->SkipIfAlreadySet())
1945 : {
1946 1 : arg->SetSkipIfAlreadySet(false);
1947 1 : return true;
1948 : }
1949 :
1950 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1951 : "Argument '%s' has already been specified.", name.c_str());
1952 3 : return false;
1953 : }
1954 :
1955 3335 : if (!arg->GetRepeatedArgAllowed() &&
1956 67 : cpl::contains(inConstructionValues, arg))
1957 : {
1958 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1959 : "Argument '%s' has already been specified.", name.c_str());
1960 1 : return false;
1961 : }
1962 :
1963 3267 : switch (arg->GetType())
1964 : {
1965 321 : case GAAT_BOOLEAN:
1966 : {
1967 321 : if (value.empty() || value == "true")
1968 319 : return arg->Set(true);
1969 2 : else if (value == "false")
1970 1 : return arg->Set(false);
1971 : else
1972 : {
1973 1 : ReportError(
1974 : CE_Failure, CPLE_IllegalArg,
1975 : "Invalid value '%s' for boolean argument '%s'. Should be "
1976 : "'true' or 'false'.",
1977 : value.c_str(), name.c_str());
1978 1 : return false;
1979 : }
1980 : }
1981 :
1982 819 : case GAAT_STRING:
1983 : {
1984 819 : return arg->Set(value);
1985 : }
1986 :
1987 356 : case GAAT_INTEGER:
1988 : {
1989 356 : errno = 0;
1990 356 : char *endptr = nullptr;
1991 356 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1992 355 : if (errno == 0 && endptr &&
1993 711 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1994 : val <= INT_MAX)
1995 : {
1996 353 : return arg->Set(static_cast<int>(val));
1997 : }
1998 : else
1999 : {
2000 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2001 : "Expected integer value for argument '%s', "
2002 : "but got '%s'.",
2003 : name.c_str(), value.c_str());
2004 3 : return false;
2005 : }
2006 : }
2007 :
2008 32 : case GAAT_REAL:
2009 : {
2010 32 : char *endptr = nullptr;
2011 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
2012 32 : if (endptr != value.c_str() + value.size())
2013 : {
2014 1 : ReportError(
2015 : CE_Failure, CPLE_IllegalArg,
2016 : "Expected real value for argument '%s', but got '%s'.",
2017 : name.c_str(), value.c_str());
2018 1 : return false;
2019 : }
2020 31 : return arg->Set(dfValue);
2021 : }
2022 :
2023 584 : case GAAT_DATASET:
2024 : {
2025 584 : return arg->SetDatasetName(value);
2026 : }
2027 :
2028 260 : case GAAT_STRING_LIST:
2029 : {
2030 : const CPLStringList aosTokens(
2031 260 : arg->GetPackedValuesAllowed()
2032 173 : ? CSLTokenizeString2(value.c_str(), ",",
2033 : CSLT_HONOURSTRINGS |
2034 : CSLT_PRESERVEQUOTES)
2035 433 : : CSLAddString(nullptr, value.c_str()));
2036 260 : if (!cpl::contains(inConstructionValues, arg))
2037 : {
2038 236 : inConstructionValues[arg] = std::vector<std::string>();
2039 : }
2040 : auto &valueVector =
2041 260 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2042 552 : for (const char *v : aosTokens)
2043 : {
2044 292 : valueVector.push_back(v);
2045 : }
2046 260 : if (arg->GetMaxCount() == 1)
2047 : {
2048 4 : bool ret = arg->Set(std::move(valueVector));
2049 4 : inConstructionValues.erase(inConstructionValues.find(arg));
2050 4 : return ret;
2051 : }
2052 :
2053 256 : break;
2054 : }
2055 :
2056 65 : case GAAT_INTEGER_LIST:
2057 : {
2058 : const CPLStringList aosTokens(
2059 65 : arg->GetPackedValuesAllowed()
2060 65 : ? CSLTokenizeString2(
2061 : value.c_str(), ",",
2062 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2063 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2064 130 : : CSLAddString(nullptr, value.c_str()));
2065 65 : if (!cpl::contains(inConstructionValues, arg))
2066 : {
2067 61 : inConstructionValues[arg] = std::vector<int>();
2068 : }
2069 : auto &valueVector =
2070 65 : std::get<std::vector<int>>(inConstructionValues[arg]);
2071 197 : for (const char *v : aosTokens)
2072 : {
2073 138 : errno = 0;
2074 138 : char *endptr = nullptr;
2075 138 : const auto val = std::strtol(v, &endptr, 10);
2076 138 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2077 134 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2078 : {
2079 132 : valueVector.push_back(static_cast<int>(val));
2080 : }
2081 : else
2082 : {
2083 6 : ReportError(
2084 : CE_Failure, CPLE_IllegalArg,
2085 : "Expected list of integer value for argument '%s', "
2086 : "but got '%s'.",
2087 : name.c_str(), value.c_str());
2088 6 : return false;
2089 : }
2090 : }
2091 59 : if (arg->GetMaxCount() == 1)
2092 : {
2093 2 : bool ret = arg->Set(std::move(valueVector));
2094 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2095 2 : return ret;
2096 : }
2097 :
2098 57 : break;
2099 : }
2100 :
2101 102 : case GAAT_REAL_LIST:
2102 : {
2103 : const CPLStringList aosTokens(
2104 102 : arg->GetPackedValuesAllowed()
2105 102 : ? CSLTokenizeString2(
2106 : value.c_str(), ",",
2107 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2108 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2109 204 : : CSLAddString(nullptr, value.c_str()));
2110 102 : if (!cpl::contains(inConstructionValues, arg))
2111 : {
2112 100 : inConstructionValues[arg] = std::vector<double>();
2113 : }
2114 : auto &valueVector =
2115 102 : std::get<std::vector<double>>(inConstructionValues[arg]);
2116 407 : for (const char *v : aosTokens)
2117 : {
2118 309 : char *endptr = nullptr;
2119 309 : double dfValue = CPLStrtod(v, &endptr);
2120 309 : if (strlen(v) == 0 || endptr != v + strlen(v))
2121 : {
2122 4 : ReportError(
2123 : CE_Failure, CPLE_IllegalArg,
2124 : "Expected list of real value for argument '%s', "
2125 : "but got '%s'.",
2126 : name.c_str(), value.c_str());
2127 4 : return false;
2128 : }
2129 305 : valueVector.push_back(dfValue);
2130 : }
2131 98 : if (arg->GetMaxCount() == 1)
2132 : {
2133 2 : bool ret = arg->Set(std::move(valueVector));
2134 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2135 2 : return ret;
2136 : }
2137 :
2138 96 : break;
2139 : }
2140 :
2141 728 : case GAAT_DATASET_LIST:
2142 : {
2143 728 : if (!cpl::contains(inConstructionValues, arg))
2144 : {
2145 720 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2146 : }
2147 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2148 728 : inConstructionValues[arg]);
2149 728 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2150 : {
2151 12 : valueVector.push_back(GDALArgDatasetValue(value));
2152 : }
2153 : else
2154 : {
2155 : const CPLStringList aosTokens(
2156 716 : arg->GetPackedValuesAllowed()
2157 6 : ? CSLTokenizeString2(value.c_str(), ",",
2158 : CSLT_HONOURSTRINGS |
2159 : CSLT_STRIPLEADSPACES)
2160 1438 : : CSLAddString(nullptr, value.c_str()));
2161 1435 : for (const char *v : aosTokens)
2162 : {
2163 719 : valueVector.push_back(GDALArgDatasetValue(v));
2164 : }
2165 : }
2166 728 : if (arg->GetMaxCount() == 1)
2167 : {
2168 625 : bool ret = arg->Set(std::move(valueVector));
2169 625 : inConstructionValues.erase(inConstructionValues.find(arg));
2170 625 : return ret;
2171 : }
2172 :
2173 103 : break;
2174 : }
2175 : }
2176 :
2177 512 : return true;
2178 : }
2179 :
2180 : /************************************************************************/
2181 : /* FormatSuggestionsAsString() */
2182 : /************************************************************************/
2183 :
2184 : static std::string
2185 6 : FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
2186 : bool addDashDashPrefix)
2187 : {
2188 6 : std::string ret;
2189 14 : for (auto [i, suggestion] : cpl::enumerate(suggestions))
2190 : {
2191 8 : if (i > 0)
2192 : {
2193 2 : ret += (i + 1 < suggestions.size()) ? ", " : " or ";
2194 : }
2195 8 : ret += '\'';
2196 8 : if (addDashDashPrefix)
2197 6 : ret += "--";
2198 8 : ret += suggestion;
2199 8 : ret += '\'';
2200 : }
2201 6 : return ret;
2202 : }
2203 :
2204 : /************************************************************************/
2205 : /* GDALAlgorithm::ParseCommandLineArguments() */
2206 : /************************************************************************/
2207 :
2208 2212 : bool GDALAlgorithm::ParseCommandLineArguments(
2209 : const std::vector<std::string> &args)
2210 : {
2211 2212 : if (m_parsedSubStringAlreadyCalled)
2212 : {
2213 6 : ReportError(CE_Failure, CPLE_AppDefined,
2214 : "ParseCommandLineArguments() can only be called once per "
2215 : "instance.");
2216 6 : return false;
2217 : }
2218 2206 : m_parsedSubStringAlreadyCalled = true;
2219 :
2220 : // AWS like syntax supported too (not advertized)
2221 2206 : if (args.size() == 1 && args[0] == "help")
2222 : {
2223 1 : auto arg = GetArg("help");
2224 1 : assert(arg);
2225 1 : arg->Set(true);
2226 1 : arg->RunActions();
2227 1 : return true;
2228 : }
2229 :
2230 2205 : if (HasSubAlgorithms())
2231 : {
2232 475 : if (args.empty())
2233 : {
2234 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2235 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2236 2 : return false;
2237 : }
2238 473 : if (!args[0].empty() && args[0][0] == '-')
2239 : {
2240 : // go on argument parsing
2241 : }
2242 : else
2243 : {
2244 470 : const auto nCounter = CPLGetErrorCounter();
2245 470 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2246 470 : if (m_selectedSubAlgHolder)
2247 : {
2248 467 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2249 467 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2250 467 : m_referencePath);
2251 467 : m_selectedSubAlg->m_executionForStreamOutput =
2252 467 : m_executionForStreamOutput;
2253 467 : m_selectedSubAlg->m_calledFromCommandLine =
2254 467 : m_calledFromCommandLine;
2255 467 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2256 467 : m_skipValidationInParseCommandLine;
2257 467 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2258 934 : std::vector<std::string>(args.begin() + 1, args.end()));
2259 467 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2260 467 : return bRet;
2261 : }
2262 : else
2263 : {
2264 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2265 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2266 : {
2267 2 : ReportError(CE_Failure, CPLE_AppDefined,
2268 2 : "Unknown command: '%s'", args[0].c_str());
2269 : }
2270 3 : return false;
2271 : }
2272 : }
2273 : }
2274 :
2275 : std::map<
2276 : GDALAlgorithmArg *,
2277 : std::variant<std::vector<std::string>, std::vector<int>,
2278 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2279 3466 : inConstructionValues;
2280 :
2281 3466 : std::vector<std::string> lArgs(args);
2282 1733 : bool helpValueRequested = false;
2283 5009 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2284 : {
2285 3389 : const auto &strArg = lArgs[i];
2286 3389 : GDALAlgorithmArg *arg = nullptr;
2287 3389 : std::string name;
2288 3389 : std::string value;
2289 3389 : bool hasValue = false;
2290 3389 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2291 5 : helpValueRequested = true;
2292 3389 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2293 : {
2294 2094 : const auto equalPos = strArg.find('=');
2295 4188 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2296 2094 : : strArg;
2297 2094 : const std::string nameWithoutDash = name.substr(2);
2298 2094 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2299 2150 : if (m_arbitraryLongNameArgsAllowed &&
2300 2150 : iterArg == m_mapLongNameToArg.end())
2301 : {
2302 17 : GetArg(nameWithoutDash);
2303 17 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2304 : }
2305 2094 : if (iterArg == m_mapLongNameToArg.end())
2306 : {
2307 : const auto suggestions =
2308 28 : GetSuggestionsForArgumentName(nameWithoutDash);
2309 28 : if (!suggestions.empty())
2310 : {
2311 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2312 : "Option '%s' is unknown. Do you mean %s?",
2313 : name.c_str(),
2314 6 : FormatSuggestionsAsString(
2315 : suggestions, /* addDashDashPrefix = */ true)
2316 : .c_str());
2317 : }
2318 : else
2319 : {
2320 25 : ReportError(CE_Failure, CPLE_IllegalArg,
2321 : "Option '%s' is unknown.", name.c_str());
2322 : }
2323 28 : return false;
2324 : }
2325 2066 : arg = iterArg->second;
2326 2066 : if (equalPos != std::string::npos)
2327 : {
2328 444 : hasValue = true;
2329 444 : value = strArg.substr(equalPos + 1);
2330 : }
2331 : }
2332 1371 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2333 76 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2334 : {
2335 147 : for (size_t j = 1; j < strArg.size(); ++j)
2336 : {
2337 76 : name.clear();
2338 76 : name += strArg[j];
2339 76 : const auto iterArg = m_mapShortNameToArg.find(name);
2340 76 : if (iterArg == m_mapShortNameToArg.end())
2341 : {
2342 5 : const std::string nameWithoutDash = strArg.substr(1);
2343 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2344 10 : m_mapLongNameToArg.end())
2345 : {
2346 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2347 : "Short name option '%s' is unknown. Do you "
2348 : "mean '--%s' (with leading double dash) ?",
2349 : name.c_str(), nameWithoutDash.c_str());
2350 : }
2351 : else
2352 : {
2353 : const auto suggestions =
2354 8 : GetSuggestionsForArgumentName(nameWithoutDash);
2355 4 : if (!suggestions.empty())
2356 : {
2357 1 : ReportError(
2358 : CE_Failure, CPLE_IllegalArg,
2359 : "Short name option '%s' is unknown. Do you "
2360 : "mean %s (with leading double dash) ?",
2361 : name.c_str(),
2362 2 : FormatSuggestionsAsString(
2363 : suggestions, /* addDashDashPrefix = */ true)
2364 : .c_str());
2365 : }
2366 : else
2367 : {
2368 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2369 : "Short name option '%s' is unknown.",
2370 : name.c_str());
2371 : }
2372 : }
2373 5 : return false;
2374 : }
2375 71 : arg = iterArg->second;
2376 71 : if (strArg.size() > 2)
2377 : {
2378 0 : if (arg->GetType() != GAAT_BOOLEAN)
2379 : {
2380 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2381 : "Invalid argument '%s'. Option '%s' is not "
2382 : "a boolean option.",
2383 : strArg.c_str(), name.c_str());
2384 0 : return false;
2385 : }
2386 :
2387 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2388 0 : return false;
2389 : }
2390 : }
2391 71 : if (strArg.size() > 2)
2392 : {
2393 0 : lArgs.erase(lArgs.begin() + i);
2394 0 : continue;
2395 : }
2396 : }
2397 : else
2398 : {
2399 1219 : ++i;
2400 1219 : continue;
2401 : }
2402 2137 : CPLAssert(arg);
2403 :
2404 2137 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2405 : {
2406 322 : if (!hasValue)
2407 : {
2408 319 : hasValue = true;
2409 319 : value = "true";
2410 : }
2411 : }
2412 :
2413 2137 : if (!hasValue)
2414 : {
2415 1374 : if (i + 1 == lArgs.size())
2416 : {
2417 41 : if (m_parseForAutoCompletion)
2418 : {
2419 35 : lArgs.erase(lArgs.begin() + i);
2420 35 : break;
2421 : }
2422 6 : ReportError(
2423 : CE_Failure, CPLE_IllegalArg,
2424 : "Expected value for argument '%s', but ran short of tokens",
2425 : name.c_str());
2426 6 : return false;
2427 : }
2428 1333 : value = lArgs[i + 1];
2429 1333 : lArgs.erase(lArgs.begin() + i + 1);
2430 : }
2431 :
2432 2096 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2433 39 : return false;
2434 :
2435 2057 : lArgs.erase(lArgs.begin() + i);
2436 : }
2437 :
2438 1655 : if (m_specialActionRequested)
2439 : {
2440 23 : return true;
2441 : }
2442 :
2443 2078 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2444 : {
2445 2044 : for (auto &[arg, value] : inConstructionValues)
2446 : {
2447 470 : if (arg->GetType() == GAAT_STRING_LIST)
2448 : {
2449 227 : if (!arg->Set(std::get<std::vector<std::string>>(
2450 227 : inConstructionValues[arg])))
2451 : {
2452 34 : return false;
2453 : }
2454 : }
2455 243 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2456 : {
2457 54 : if (!arg->Set(
2458 54 : std::get<std::vector<int>>(inConstructionValues[arg])))
2459 : {
2460 4 : return false;
2461 : }
2462 : }
2463 189 : else if (arg->GetType() == GAAT_REAL_LIST)
2464 : {
2465 94 : if (!arg->Set(std::get<std::vector<double>>(
2466 94 : inConstructionValues[arg])))
2467 : {
2468 10 : return false;
2469 : }
2470 : }
2471 95 : else if (arg->GetType() == GAAT_DATASET_LIST)
2472 : {
2473 95 : if (!arg->Set(
2474 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2475 95 : inConstructionValues[arg]))))
2476 : {
2477 2 : return false;
2478 : }
2479 : }
2480 : }
2481 1574 : return true;
2482 1632 : };
2483 :
2484 : // Process positional arguments that have not been set through their
2485 : // option name.
2486 1632 : size_t i = 0;
2487 1632 : size_t iCurPosArg = 0;
2488 :
2489 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2490 1655 : if (m_positionalArgs.size() == 3 &&
2491 24 : (m_positionalArgs[0]->IsRequired() ||
2492 23 : m_positionalArgs[0]->GetMinCount() == 1) &&
2493 44 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2494 29 : (m_positionalArgs[1]->IsRequired() ||
2495 29 : m_positionalArgs[1]->GetMinCount() == 1) &&
2496 : /* Second argument may have several occurrences */
2497 44 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2498 22 : (m_positionalArgs[2]->IsRequired() ||
2499 22 : m_positionalArgs[2]->GetMinCount() == 1) &&
2500 22 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2501 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2502 1664 : !m_positionalArgs[1]->IsExplicitlySet() &&
2503 9 : !m_positionalArgs[2]->IsExplicitlySet())
2504 : {
2505 7 : if (lArgs.size() - i < 3)
2506 : {
2507 1 : ReportError(CE_Failure, CPLE_AppDefined,
2508 : "Not enough positional values.");
2509 1 : return false;
2510 : }
2511 12 : bool ok = ParseArgument(m_positionalArgs[0],
2512 6 : m_positionalArgs[0]->GetName().c_str(),
2513 6 : lArgs[i], inConstructionValues);
2514 6 : if (ok)
2515 : {
2516 5 : ++i;
2517 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2518 : {
2519 12 : ok = ParseArgument(m_positionalArgs[1],
2520 6 : m_positionalArgs[1]->GetName().c_str(),
2521 6 : lArgs[i], inConstructionValues);
2522 : }
2523 : }
2524 6 : if (ok)
2525 : {
2526 10 : ok = ParseArgument(m_positionalArgs[2],
2527 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2528 : inConstructionValues);
2529 5 : ++i;
2530 : }
2531 6 : if (!ok)
2532 : {
2533 3 : ProcessInConstructionValues();
2534 3 : return false;
2535 : }
2536 : }
2537 :
2538 519 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2539 601 : !m_positionalArgs[0]->IsExplicitlySet() &&
2540 2457 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2541 72 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2542 36 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2543 : {
2544 36 : ++iCurPosArg;
2545 : }
2546 :
2547 2767 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2548 : {
2549 1146 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2550 1157 : while (arg->IsExplicitlySet())
2551 : {
2552 12 : ++iCurPosArg;
2553 12 : if (iCurPosArg == m_positionalArgs.size())
2554 1 : break;
2555 11 : arg = m_positionalArgs[iCurPosArg];
2556 : }
2557 1146 : if (iCurPosArg == m_positionalArgs.size())
2558 : {
2559 1 : break;
2560 : }
2561 1769 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2562 624 : arg->GetMinCount() != arg->GetMaxCount())
2563 : {
2564 98 : if (iCurPosArg == 0)
2565 : {
2566 76 : size_t nCountAtEnd = 0;
2567 105 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2568 : {
2569 31 : const auto *otherArg = m_positionalArgs[j];
2570 31 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2571 : {
2572 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2573 : {
2574 2 : ReportError(
2575 : CE_Failure, CPLE_AppDefined,
2576 : "Ambiguity in definition of positional "
2577 : "argument "
2578 : "'%s' given it has a varying number of values, "
2579 : "but follows argument '%s' which also has a "
2580 : "varying number of values",
2581 1 : otherArg->GetName().c_str(),
2582 1 : arg->GetName().c_str());
2583 1 : ProcessInConstructionValues();
2584 1 : return false;
2585 : }
2586 3 : nCountAtEnd += otherArg->GetMinCount();
2587 : }
2588 : else
2589 : {
2590 27 : if (!otherArg->IsRequired())
2591 : {
2592 2 : ReportError(
2593 : CE_Failure, CPLE_AppDefined,
2594 : "Ambiguity in definition of positional "
2595 : "argument "
2596 : "'%s', given it is not required but follows "
2597 : "argument '%s' which has a varying number of "
2598 : "values",
2599 1 : otherArg->GetName().c_str(),
2600 1 : arg->GetName().c_str());
2601 1 : ProcessInConstructionValues();
2602 1 : return false;
2603 : }
2604 26 : nCountAtEnd++;
2605 : }
2606 : }
2607 74 : if (lArgs.size() < nCountAtEnd)
2608 : {
2609 1 : ReportError(CE_Failure, CPLE_AppDefined,
2610 : "Not enough positional values.");
2611 1 : ProcessInConstructionValues();
2612 1 : return false;
2613 : }
2614 154 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2615 : {
2616 81 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2617 : inConstructionValues))
2618 : {
2619 0 : ProcessInConstructionValues();
2620 0 : return false;
2621 : }
2622 : }
2623 : }
2624 22 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2625 : {
2626 49 : for (; i < lArgs.size(); ++i)
2627 : {
2628 28 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2629 : inConstructionValues))
2630 : {
2631 0 : ProcessInConstructionValues();
2632 0 : return false;
2633 : }
2634 : }
2635 : }
2636 : else
2637 : {
2638 1 : ReportError(CE_Failure, CPLE_AppDefined,
2639 : "Ambiguity in definition of positional arguments: "
2640 : "arguments with varying number of values must be "
2641 : "first or last one.");
2642 1 : return false;
2643 : }
2644 : }
2645 : else
2646 : {
2647 1047 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2648 : {
2649 1 : ReportError(CE_Failure, CPLE_AppDefined,
2650 : "Not enough positional values.");
2651 1 : return false;
2652 : }
2653 1046 : const size_t iMax = i + arg->GetMaxCount();
2654 2095 : for (; i < iMax; ++i)
2655 : {
2656 1050 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2657 : inConstructionValues))
2658 : {
2659 1 : ProcessInConstructionValues();
2660 1 : return false;
2661 : }
2662 : }
2663 : }
2664 1139 : ++iCurPosArg;
2665 : }
2666 :
2667 1622 : if (i < lArgs.size())
2668 : {
2669 21 : ReportError(CE_Failure, CPLE_AppDefined,
2670 : "Positional values starting at '%s' are not expected.",
2671 21 : lArgs[i].c_str());
2672 21 : return false;
2673 : }
2674 :
2675 1601 : if (!ProcessInConstructionValues())
2676 : {
2677 33 : return false;
2678 : }
2679 :
2680 : // Skip to first unset positional argument.
2681 2584 : while (iCurPosArg < m_positionalArgs.size() &&
2682 557 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2683 : {
2684 459 : ++iCurPosArg;
2685 : }
2686 : // Check if this positional argument is required.
2687 1665 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2688 97 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2689 50 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2690 47 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2691 : {
2692 86 : ReportError(CE_Failure, CPLE_AppDefined,
2693 : "Positional arguments starting at '%s' have not been "
2694 : "specified.",
2695 86 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2696 86 : return false;
2697 : }
2698 :
2699 1482 : if (m_calledFromCommandLine)
2700 : {
2701 5426 : for (auto &arg : m_args)
2702 : {
2703 7034 : if (arg->IsExplicitlySet() &&
2704 1098 : ((arg->GetType() == GAAT_STRING &&
2705 1095 : arg->Get<std::string>() == "?") ||
2706 994 : (arg->GetType() == GAAT_STRING_LIST &&
2707 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2708 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2709 : {
2710 : {
2711 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2712 5 : ValidateArguments();
2713 : }
2714 :
2715 5 : auto choices = arg->GetChoices();
2716 5 : if (choices.empty())
2717 2 : choices = arg->GetAutoCompleteChoices(std::string());
2718 5 : if (!choices.empty())
2719 : {
2720 5 : if (choices.size() == 1)
2721 : {
2722 4 : ReportError(
2723 : CE_Failure, CPLE_AppDefined,
2724 : "Single potential value for argument '%s' is '%s'",
2725 4 : arg->GetName().c_str(), choices.front().c_str());
2726 : }
2727 : else
2728 : {
2729 6 : std::string msg("Potential values for argument '");
2730 3 : msg += arg->GetName();
2731 3 : msg += "' are:";
2732 45 : for (const auto &v : choices)
2733 : {
2734 42 : msg += "\n- ";
2735 42 : msg += v;
2736 : }
2737 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2738 : msg.c_str());
2739 : }
2740 5 : return false;
2741 : }
2742 : }
2743 : }
2744 : }
2745 :
2746 1477 : return m_skipValidationInParseCommandLine || ValidateArguments();
2747 : }
2748 :
2749 : /************************************************************************/
2750 : /* GDALAlgorithm::ReportError() */
2751 : /************************************************************************/
2752 :
2753 : //! @cond Doxygen_Suppress
2754 954 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2755 : const char *fmt, ...) const
2756 : {
2757 : va_list args;
2758 954 : va_start(args, fmt);
2759 954 : CPLError(eErrClass, err_no, "%s",
2760 954 : std::string(m_name)
2761 954 : .append(": ")
2762 1908 : .append(CPLString().vPrintf(fmt, args))
2763 : .c_str());
2764 954 : va_end(args);
2765 954 : }
2766 :
2767 : //! @endcond
2768 :
2769 : /************************************************************************/
2770 : /* GDALAlgorithm::ProcessDatasetArg() */
2771 : /************************************************************************/
2772 :
2773 10438 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2774 : GDALAlgorithm *algForOutput)
2775 : {
2776 10438 : bool ret = true;
2777 :
2778 10438 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2779 10438 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2780 10438 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2781 :
2782 10438 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2783 10438 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2784 10438 : const bool append = hasAppendArg && appendArg->Get<bool>();
2785 :
2786 10438 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2787 : const bool overwrite =
2788 17161 : (arg->IsOutput() && overwriteArg &&
2789 17161 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2790 :
2791 10438 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2792 20876 : auto &val = [arg]() -> GDALArgDatasetValue &
2793 : {
2794 10438 : if (arg->GetType() == GAAT_DATASET_LIST)
2795 6013 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2796 : else
2797 4425 : return arg->Get<GDALArgDatasetValue>();
2798 10438 : }();
2799 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2800 16548 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2801 16556 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2802 8 : !overwrite;
2803 :
2804 : // Used for nested pipelines
2805 : const auto oIterDatasetNameToDataset =
2806 20873 : val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2807 10438 : : m_oMapDatasetNameToDataset.end();
2808 :
2809 10438 : if (!val.GetDatasetRef() && !val.IsNameSet())
2810 : {
2811 3 : ReportError(CE_Failure, CPLE_AppDefined,
2812 : "Argument '%s' has no dataset object or dataset name.",
2813 3 : arg->GetName().c_str());
2814 3 : ret = false;
2815 : }
2816 10435 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2817 : {
2818 3 : return false;
2819 : }
2820 284 : else if (m_inputDatasetCanBeOmitted &&
2821 10716 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2822 17 : !arg->IsOutput())
2823 : {
2824 17 : return true;
2825 : }
2826 15404 : else if (!val.GetDatasetRef() &&
2827 5293 : (arg->AutoOpenDataset() ||
2828 15708 : oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2829 4685 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2830 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2831 : {
2832 1447 : int flags = arg->GetDatasetType();
2833 1447 : bool assignToOutputArg = false;
2834 :
2835 : // Check if input and output parameters point to the same
2836 : // filename (for vector datasets)
2837 2686 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2838 2686 : outputArg && outputArg->GetType() == GAAT_DATASET)
2839 : {
2840 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2841 121 : if (!outputVal.GetDatasetRef() &&
2842 121 : outputVal.GetName() == val.GetName() &&
2843 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2844 : {
2845 2 : assignToOutputArg = true;
2846 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2847 : }
2848 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2849 : {
2850 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2851 : }
2852 : }
2853 :
2854 1447 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2855 1364 : flags |= GDAL_OF_VERBOSE_ERROR;
2856 1447 : if ((arg == outputArg || !outputArg) && update)
2857 : {
2858 85 : flags |= GDAL_OF_UPDATE;
2859 85 : if (!append)
2860 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2861 : }
2862 :
2863 1447 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2864 : const bool readOnly =
2865 1491 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2866 44 : readOnlyArg->Get<bool>());
2867 1447 : if (readOnly)
2868 12 : flags &= ~GDAL_OF_UPDATE;
2869 :
2870 2894 : CPLStringList aosOpenOptions;
2871 2894 : CPLStringList aosAllowedDrivers;
2872 1447 : if (arg->IsInput())
2873 : {
2874 1447 : if (arg == outputArg)
2875 : {
2876 83 : if (update && !overwrite)
2877 : {
2878 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2879 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2880 31 : aosOpenOptions = CPLStringList(
2881 31 : ooArg->Get<std::vector<std::string>>());
2882 : }
2883 : }
2884 : else
2885 : {
2886 1364 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2887 1364 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2888 : aosOpenOptions =
2889 1297 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2890 :
2891 1364 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2892 1364 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2893 : aosAllowedDrivers =
2894 1253 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2895 : }
2896 : }
2897 :
2898 2894 : std::string osDatasetName = val.GetName();
2899 1447 : if (!m_referencePath.empty())
2900 : {
2901 42 : osDatasetName = GDALDataset::BuildFilename(
2902 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2903 : }
2904 1447 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2905 0 : osDatasetName = "/vsistdin/";
2906 :
2907 : // Handle special case of overview delete in GTiff which would fail
2908 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2909 145 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2910 1594 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2911 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2912 : {
2913 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2914 : GDALDriverH hDrv =
2915 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2916 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2917 : {
2918 : // Cleaning does not break COG layout
2919 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2920 : }
2921 : }
2922 :
2923 : GDALDataset *poDS;
2924 2894 : CPLErrorAccumulator oAccumulator;
2925 : {
2926 2894 : auto oContext = oAccumulator.InstallForCurrentScope();
2927 :
2928 1447 : poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2929 1447 : ? oIterDatasetNameToDataset->second
2930 1434 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2931 1434 : aosAllowedDrivers.List(),
2932 1434 : aosOpenOptions.List());
2933 :
2934 66 : if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
2935 1513 : !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
2936 : {
2937 36 : auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
2938 72 : osDatasetName.c_str(), nullptr);
2939 36 : if (eErr == OGRERR_NONE)
2940 : {
2941 4 : auto poMemDS = std::make_unique<MEMDataset>();
2942 4 : auto *poLayer = poMemDS->CreateLayer(
2943 : "", poWktGeom->getSpatialReference(),
2944 2 : poWktGeom->getGeometryType());
2945 :
2946 2 : auto poFeatureDefn = poLayer->GetLayerDefn();
2947 4 : OGRFeature oFeature(poFeatureDefn);
2948 :
2949 2 : oFeature.SetGeometry(std::move(poWktGeom));
2950 2 : if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
2951 : {
2952 2 : poDS = poMemDS.release();
2953 2 : oAccumulator.ClearErrors();
2954 : }
2955 : }
2956 : }
2957 :
2958 : // Retry with PostGIS vector driver
2959 64 : if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
2960 62 : cpl::starts_with(osDatasetName, "PG:") &&
2961 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2962 1511 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2963 : {
2964 0 : oAccumulator.ClearErrors();
2965 0 : poDS = GDALDataset::Open(
2966 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2967 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2968 : }
2969 : }
2970 1447 : oAccumulator.ReplayErrors();
2971 :
2972 1447 : if (poDS)
2973 : {
2974 1383 : if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2975 : {
2976 13 : if (arg->GetType() == GAAT_DATASET)
2977 7 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2978 13 : poDS->Reference();
2979 13 : m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
2980 : }
2981 :
2982 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2983 : // where the PG: dataset will be first opened with the PostGISRaster
2984 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2985 1894 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2986 2005 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2987 111 : aosOpenOptions.empty())
2988 : {
2989 107 : auto poDrv = poDS->GetDriver();
2990 107 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2991 : {
2992 : // Retry with PostgreSQL (vector) driver
2993 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2994 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2995 0 : if (poTmpDS)
2996 : {
2997 0 : poDS->ReleaseRef();
2998 0 : poDS = poTmpDS.release();
2999 : }
3000 : }
3001 : }
3002 :
3003 1383 : if (assignToOutputArg)
3004 : {
3005 : // Avoid opening twice the same datasource if it is both
3006 : // the input and output.
3007 : // Known to cause problems with at least FGdb, SQLite
3008 : // and GPKG drivers. See #4270
3009 : // Restrict to those 3 drivers. For example it is known
3010 : // to break with the PG driver due to the way it
3011 : // manages transactions.
3012 2 : auto poDriver = poDS->GetDriver();
3013 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
3014 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
3015 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
3016 : {
3017 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
3018 : }
3019 : }
3020 1383 : val.SetDatasetOpenedByAlgorithm();
3021 1383 : val.Set(poDS);
3022 1383 : poDS->ReleaseRef();
3023 : }
3024 64 : else if (!append)
3025 : {
3026 62 : ret = false;
3027 : }
3028 : }
3029 :
3030 : // Deal with overwriting the output dataset
3031 10418 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
3032 : {
3033 3240 : if (!append)
3034 : {
3035 : // If outputting to MEM, do not try to erase a real file of the same name!
3036 : const auto outputFormatArg =
3037 3228 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3038 9646 : if (!(outputFormatArg &&
3039 3209 : outputFormatArg->GetType() == GAAT_STRING &&
3040 3209 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3041 2129 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3042 1191 : "stream") ||
3043 1191 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3044 : "Memory"))))
3045 : {
3046 1210 : const char *pszType = "";
3047 1210 : GDALDriver *poDriver = nullptr;
3048 2374 : if (!val.GetName().empty() &&
3049 1164 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
3050 : &poDriver))
3051 : {
3052 79 : if (!overwrite)
3053 : {
3054 68 : std::string options;
3055 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
3056 : {
3057 11 : options += "--";
3058 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
3059 : }
3060 34 : if (hasAppendArg)
3061 : {
3062 22 : if (!options.empty())
3063 8 : options += '/';
3064 22 : options += "--";
3065 22 : options += GDAL_ARG_NAME_APPEND;
3066 : }
3067 34 : if (hasUpdateArg)
3068 : {
3069 15 : if (!options.empty())
3070 12 : options += '/';
3071 15 : options += "--";
3072 15 : options += GDAL_ARG_NAME_UPDATE;
3073 : }
3074 :
3075 34 : if (poDriver)
3076 : {
3077 68 : const char *pszPrefix = poDriver->GetMetadataItem(
3078 34 : GDAL_DMD_CONNECTION_PREFIX);
3079 34 : if (pszPrefix &&
3080 0 : STARTS_WITH_CI(val.GetName().c_str(),
3081 : pszPrefix))
3082 : {
3083 0 : bool bExists = false;
3084 : {
3085 : CPLErrorStateBackuper oBackuper(
3086 0 : CPLQuietErrorHandler);
3087 0 : bExists = std::unique_ptr<GDALDataset>(
3088 : GDALDataset::Open(
3089 0 : val.GetName().c_str())) !=
3090 : nullptr;
3091 : }
3092 0 : if (bExists)
3093 : {
3094 0 : if (!options.empty())
3095 0 : options = " You may specify the " +
3096 0 : options + " option.";
3097 0 : ReportError(CE_Failure, CPLE_AppDefined,
3098 : "%s '%s' already exists.%s",
3099 0 : pszType, val.GetName().c_str(),
3100 : options.c_str());
3101 0 : return false;
3102 : }
3103 :
3104 0 : return true;
3105 : }
3106 : }
3107 :
3108 34 : if (!options.empty())
3109 28 : options = '/' + options;
3110 68 : ReportError(
3111 : CE_Failure, CPLE_AppDefined,
3112 : "%s '%s' already exists. You may specify the "
3113 : "--overwrite%s option.",
3114 34 : pszType, val.GetName().c_str(), options.c_str());
3115 34 : return false;
3116 : }
3117 45 : else if (EQUAL(pszType, "File"))
3118 : {
3119 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3120 : {
3121 0 : ReportError(CE_Failure, CPLE_AppDefined,
3122 : "Deleting %s failed: %s",
3123 0 : val.GetName().c_str(),
3124 0 : VSIStrerror(errno));
3125 0 : return false;
3126 : }
3127 : }
3128 44 : else if (EQUAL(pszType, "Directory"))
3129 : {
3130 : // We don't want the user to accidentally erase a non-GDAL dataset
3131 1 : ReportError(CE_Failure, CPLE_AppDefined,
3132 : "Directory '%s' already exists, but is not "
3133 : "recognized as a valid GDAL dataset. "
3134 : "Please manually delete it before retrying",
3135 1 : val.GetName().c_str());
3136 1 : return false;
3137 : }
3138 43 : else if (poDriver)
3139 : {
3140 : bool bDeleteOK;
3141 : {
3142 : CPLErrorStateBackuper oBackuper(
3143 43 : CPLQuietErrorHandler);
3144 43 : bDeleteOK = (poDriver->Delete(
3145 43 : val.GetName().c_str()) == CE_None);
3146 : }
3147 : VSIStatBufL sStat;
3148 46 : if (!bDeleteOK &&
3149 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3150 : {
3151 3 : if (VSI_ISDIR(sStat.st_mode))
3152 : {
3153 : // We don't want the user to accidentally erase a non-GDAL dataset
3154 0 : ReportError(
3155 : CE_Failure, CPLE_AppDefined,
3156 : "Directory '%s' already exists, but is not "
3157 : "recognized as a valid GDAL dataset. "
3158 : "Please manually delete it before retrying",
3159 0 : val.GetName().c_str());
3160 2 : return false;
3161 : }
3162 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3163 : {
3164 2 : ReportError(CE_Failure, CPLE_AppDefined,
3165 : "Deleting %s failed: %s",
3166 2 : val.GetName().c_str(),
3167 2 : VSIStrerror(errno));
3168 2 : return false;
3169 : }
3170 : }
3171 : }
3172 : }
3173 : }
3174 : }
3175 : }
3176 :
3177 : // If outputting to stdout, automatically turn off progress bar
3178 10381 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3179 : {
3180 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3181 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3182 5 : quietArg->Set(true);
3183 : }
3184 :
3185 10381 : return ret;
3186 : }
3187 :
3188 : /************************************************************************/
3189 : /* GDALAlgorithm::ValidateArguments() */
3190 : /************************************************************************/
3191 :
3192 7235 : bool GDALAlgorithm::ValidateArguments()
3193 : {
3194 7235 : if (m_selectedSubAlg)
3195 3 : return m_selectedSubAlg->ValidateArguments();
3196 :
3197 7232 : if (m_specialActionRequested)
3198 1 : return true;
3199 :
3200 7231 : m_arbitraryLongNameArgsAllowed = false;
3201 :
3202 : // If only --output=format=MEM/stream is specified and not --output,
3203 : // then set empty name for --output.
3204 7231 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3205 7231 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3206 4300 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3207 2721 : !outputArg->IsExplicitlySet() &&
3208 363 : outputFormatArg->GetType() == GAAT_STRING &&
3209 363 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3210 593 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3211 11865 : outputArg->GetType() == GAAT_DATASET &&
3212 334 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3213 : {
3214 334 : outputArg->Get<GDALArgDatasetValue>().Set("");
3215 : }
3216 :
3217 : // The method may emit several errors if several constraints are not met.
3218 7231 : bool ret = true;
3219 14462 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3220 14462 : std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3221 134962 : for (auto &arg : m_args)
3222 : {
3223 : // Check mutually exclusive/dependent arguments
3224 127731 : if (arg->IsExplicitlySet())
3225 : {
3226 :
3227 20269 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3228 20269 : if (!mutualExclusionGroup.empty())
3229 : {
3230 : auto oIter =
3231 804 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3232 804 : if (oIter != mutualExclusionGroupUsed.end())
3233 : {
3234 13 : ret = false;
3235 26 : ReportError(
3236 : CE_Failure, CPLE_AppDefined,
3237 : "Argument '%s' is mutually exclusive with '%s'.",
3238 26 : arg->GetName().c_str(), oIter->second.c_str());
3239 : }
3240 : else
3241 : {
3242 791 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3243 1582 : arg->GetName();
3244 : }
3245 : }
3246 :
3247 20269 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3248 20269 : if (!mutualDependencyGroup.empty())
3249 : {
3250 50 : if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3251 100 : mutualDependencyGroupUsed.end())
3252 : {
3253 87 : mutualDependencyGroupUsed[mutualDependencyGroup] = {
3254 87 : arg->GetName()};
3255 : }
3256 : else
3257 : {
3258 42 : mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3259 21 : arg->GetName());
3260 : }
3261 : }
3262 :
3263 : // Check direct dependencies
3264 20281 : for (const auto &dependency : arg->GetDirectDependencies())
3265 : {
3266 12 : auto depArg = GetArg(dependency);
3267 12 : if (!depArg)
3268 : {
3269 0 : ret = false;
3270 0 : ReportError(CE_Failure, CPLE_AppDefined,
3271 : "Argument '%s' depends on argument '%s' that "
3272 : "is not defined.",
3273 0 : arg->GetName().c_str(), dependency.c_str());
3274 : }
3275 12 : else if (!depArg->IsExplicitlySet())
3276 : {
3277 6 : ret = false;
3278 12 : ReportError(CE_Failure, CPLE_AppDefined,
3279 : "Argument '%s' depends on argument '%s' that "
3280 : "has not been specified.",
3281 6 : arg->GetName().c_str(),
3282 6 : depArg->GetName().c_str());
3283 : }
3284 : }
3285 : }
3286 :
3287 127907 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3288 176 : !arg->HasDefaultValue())
3289 : {
3290 176 : bool emitError = true;
3291 176 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3292 176 : if (!mutualExclusionGroup.empty())
3293 : {
3294 1885 : for (const auto &otherArg : m_args)
3295 : {
3296 1859 : if (otherArg->GetMutualExclusionGroup() ==
3297 1988 : mutualExclusionGroup &&
3298 129 : otherArg->IsExplicitlySet())
3299 : {
3300 74 : emitError = false;
3301 74 : break;
3302 : }
3303 : }
3304 : }
3305 266 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3306 57 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3307 66 : (arg->GetType() == GAAT_DATASET ||
3308 33 : arg->GetType() == GAAT_DATASET_LIST)))
3309 : {
3310 69 : ReportError(CE_Failure, CPLE_AppDefined,
3311 : "Required argument '%s' has not been specified.",
3312 69 : arg->GetName().c_str());
3313 69 : ret = false;
3314 : }
3315 : }
3316 127555 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3317 : {
3318 4425 : if (!ProcessDatasetArg(arg.get(), this))
3319 51 : ret = false;
3320 : }
3321 :
3322 127731 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3323 : {
3324 5824 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3325 5824 : if (listVal.size() == 1)
3326 : {
3327 5670 : if (!ProcessDatasetArg(arg.get(), this))
3328 40 : ret = false;
3329 : }
3330 : else
3331 : {
3332 473 : for (auto &val : listVal)
3333 : {
3334 319 : if (val.GetDatasetRef())
3335 : {
3336 120 : if (!CheckCanSetDatasetObject(arg.get()))
3337 : {
3338 0 : ret = false;
3339 : }
3340 315 : continue;
3341 : }
3342 :
3343 199 : if (val.GetName().empty())
3344 : {
3345 0 : ReportError(CE_Failure, CPLE_AppDefined,
3346 : "Argument '%s' has no dataset object or "
3347 : "dataset name.",
3348 0 : arg->GetName().c_str());
3349 0 : ret = false;
3350 0 : continue;
3351 : }
3352 :
3353 199 : auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3354 199 : if (oIter != m_oMapDatasetNameToDataset.end())
3355 : {
3356 2 : auto poDS = oIter->second;
3357 2 : val.SetDatasetOpenedByAlgorithm();
3358 2 : val.Set(poDS);
3359 2 : m_oMapDatasetNameToDataset.erase(oIter);
3360 2 : continue;
3361 : }
3362 :
3363 197 : if (!arg->AutoOpenDataset())
3364 193 : continue;
3365 :
3366 4 : int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3367 :
3368 8 : CPLStringList aosOpenOptions;
3369 8 : CPLStringList aosAllowedDrivers;
3370 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3371 : {
3372 4 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3373 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3374 : {
3375 4 : aosOpenOptions = CPLStringList(
3376 4 : ooArg->Get<std::vector<std::string>>());
3377 : }
3378 :
3379 4 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3380 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3381 : {
3382 4 : aosAllowedDrivers = CPLStringList(
3383 4 : ifArg->Get<std::vector<std::string>>());
3384 : }
3385 :
3386 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3387 4 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3388 0 : updateArg->Get<bool>())
3389 : {
3390 0 : flags |= GDAL_OF_UPDATE;
3391 : }
3392 : }
3393 :
3394 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3395 4 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3396 12 : aosOpenOptions.List()));
3397 4 : if (poDS)
3398 : {
3399 3 : val.Set(std::move(poDS));
3400 : }
3401 : else
3402 : {
3403 1 : ret = false;
3404 : }
3405 : }
3406 : }
3407 : }
3408 :
3409 127731 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3410 : {
3411 8 : ret = false;
3412 : }
3413 : }
3414 :
3415 : // Check mutual dependency groups
3416 7231 : std::vector<std::string> processedGroups;
3417 : // Loop through group map and check there are not required args in the group that are not set
3418 7260 : for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3419 : {
3420 29 : if (std::find(processedGroups.begin(), processedGroups.end(),
3421 29 : groupName) != processedGroups.end())
3422 0 : continue;
3423 58 : std::vector<std::string> missingArgs;
3424 562 : for (auto &arg : m_args)
3425 : {
3426 533 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3427 598 : if (mutualDependencyGroup == groupName &&
3428 65 : std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3429 598 : argNames.end())
3430 : {
3431 15 : missingArgs.push_back(arg->GetName());
3432 : }
3433 : }
3434 29 : if (!missingArgs.empty())
3435 : {
3436 12 : ret = false;
3437 24 : std::string missingArgsStr;
3438 27 : for (const auto &missingArg : missingArgs)
3439 : {
3440 15 : if (!missingArgsStr.empty())
3441 3 : missingArgsStr += ", ";
3442 15 : missingArgsStr += missingArg;
3443 : }
3444 24 : std::string givenArgsStr;
3445 27 : for (const auto &givenArg : argNames)
3446 : {
3447 15 : if (!givenArgsStr.empty())
3448 3 : givenArgsStr += ", ";
3449 15 : givenArgsStr += givenArg;
3450 : }
3451 12 : ReportError(CE_Failure, CPLE_AppDefined,
3452 : "Argument(s) '%s' require(s) that the following "
3453 : "argument(s) are also specified: %s.",
3454 : givenArgsStr.c_str(), missingArgsStr.c_str());
3455 : }
3456 29 : processedGroups.push_back(groupName);
3457 : }
3458 :
3459 30807 : for (const auto &f : m_validationActions)
3460 : {
3461 23576 : if (!f())
3462 81 : ret = false;
3463 : }
3464 :
3465 7231 : return ret;
3466 : }
3467 :
3468 : /************************************************************************/
3469 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3470 : /************************************************************************/
3471 :
3472 : std::unique_ptr<GDALAlgorithm>
3473 10228 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3474 : bool suggestionAllowed) const
3475 : {
3476 10228 : auto ret = m_subAlgRegistry.Instantiate(name);
3477 20456 : auto childCallPath = m_callPath;
3478 10228 : childCallPath.push_back(name);
3479 10228 : if (!ret)
3480 : {
3481 1045 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3482 1045 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3483 : }
3484 10228 : if (ret)
3485 : {
3486 10053 : ret->SetCallPath(childCallPath);
3487 : }
3488 175 : else if (suggestionAllowed)
3489 : {
3490 64 : std::string bestCandidate;
3491 32 : size_t bestDistance = std::numeric_limits<size_t>::max();
3492 490 : for (const std::string &candidate : GetSubAlgorithmNames())
3493 : {
3494 : const size_t distance =
3495 458 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3496 : /* transpositionAllowed = */ true);
3497 458 : if (distance < bestDistance)
3498 : {
3499 75 : bestCandidate = candidate;
3500 75 : bestDistance = distance;
3501 : }
3502 383 : else if (distance == bestDistance)
3503 : {
3504 47 : bestCandidate.clear();
3505 : }
3506 : }
3507 32 : if (!bestCandidate.empty() && bestDistance <= 2)
3508 : {
3509 4 : CPLError(CE_Failure, CPLE_AppDefined,
3510 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3511 : name.c_str(), bestCandidate.c_str());
3512 : }
3513 : }
3514 20456 : return ret;
3515 : }
3516 :
3517 : /************************************************************************/
3518 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3519 : /************************************************************************/
3520 :
3521 : std::string
3522 39 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3523 : {
3524 39 : if (osName.size() >= 3)
3525 : {
3526 34 : std::string bestCandidate;
3527 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3528 752 : for (const auto &[key, value] : m_mapLongNameToArg)
3529 : {
3530 718 : CPL_IGNORE_RET_VAL(value);
3531 718 : const size_t distance = CPLLevenshteinDistance(
3532 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3533 718 : if (distance < bestDistance)
3534 : {
3535 89 : bestCandidate = key;
3536 89 : bestDistance = distance;
3537 : }
3538 629 : else if (distance == bestDistance)
3539 : {
3540 77 : bestCandidate.clear();
3541 : }
3542 : }
3543 48 : if (!bestCandidate.empty() &&
3544 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3545 : {
3546 5 : return bestCandidate;
3547 : }
3548 : }
3549 34 : return std::string();
3550 : }
3551 :
3552 : /************************************************************************/
3553 : /* GDALAlgorithm::GetSuggestionsForArgumentName() */
3554 : /************************************************************************/
3555 :
3556 : std::vector<std::string>
3557 39 : GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
3558 : {
3559 39 : std::vector<std::string> ret;
3560 78 : std::string suggestion = GetSuggestionForArgumentName(osName);
3561 39 : if (!suggestion.empty())
3562 : {
3563 5 : ret.push_back(std::move(suggestion));
3564 : }
3565 34 : else if (osName.size() >= 3)
3566 : {
3567 : // e.g "crs" for reproject will match "input-crs" and "target-crs"
3568 87 : const std::string dashName = std::string("-").append(osName);
3569 532 : for (const auto &arg : m_args)
3570 : {
3571 503 : if (cpl::ends_with(arg->GetName(), dashName))
3572 : {
3573 3 : ret.push_back(arg->GetName());
3574 : }
3575 : }
3576 : }
3577 78 : return ret;
3578 : }
3579 :
3580 : /************************************************************************/
3581 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3582 : /************************************************************************/
3583 :
3584 : /* static */
3585 23 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3586 : {
3587 69 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3588 69 : osName == GDAL_ARG_NAME_OVERWRITE ||
3589 46 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3590 : }
3591 :
3592 : /************************************************************************/
3593 : /* GDALAlgorithm::HasOutputString() */
3594 : /************************************************************************/
3595 :
3596 74 : bool GDALAlgorithm::HasOutputString() const
3597 : {
3598 74 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3599 74 : return outputStringArg && outputStringArg->IsOutput();
3600 : }
3601 :
3602 : /************************************************************************/
3603 : /* GDALAlgorithm::GetArg() */
3604 : /************************************************************************/
3605 :
3606 471468 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3607 : bool suggestionAllowed, bool isConst)
3608 : {
3609 471468 : const auto nPos = osName.find_first_not_of('-');
3610 471468 : if (nPos == std::string::npos)
3611 27 : return nullptr;
3612 942882 : std::string osKey = osName.substr(nPos);
3613 : {
3614 471441 : const auto oIter = m_mapLongNameToArg.find(osKey);
3615 471441 : if (oIter != m_mapLongNameToArg.end())
3616 437449 : return oIter->second;
3617 : }
3618 : {
3619 33992 : const auto oIter = m_mapShortNameToArg.find(osKey);
3620 33992 : if (oIter != m_mapShortNameToArg.end())
3621 8 : return oIter->second;
3622 : }
3623 :
3624 33984 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3625 : {
3626 23 : const auto nDotPos = osKey.find('.');
3627 : const std::string osKeyEnd =
3628 23 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3629 23 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3630 : {
3631 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3632 0 : std::make_unique<bool>());
3633 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3634 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3635 0 : .SetUserProvided();
3636 : }
3637 : else
3638 : {
3639 46 : const std::string osKeyInit = osKey;
3640 23 : if (osKey == "oo")
3641 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3642 23 : else if (osKey == "co")
3643 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3644 23 : else if (osKey == "of")
3645 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3646 23 : else if (osKey == "if")
3647 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3648 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3649 23 : std::make_unique<std::string>());
3650 : auto &arg =
3651 46 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3652 46 : m_arbitraryLongNameArgsValuesStr.back().get())
3653 23 : .SetUserProvided();
3654 23 : if (osKey != osKeyInit)
3655 0 : arg.AddAlias(osKeyInit);
3656 : }
3657 23 : const auto oIter = m_mapLongNameToArg.find(osKey);
3658 23 : CPLAssert(oIter != m_mapLongNameToArg.end());
3659 23 : return oIter->second;
3660 : }
3661 :
3662 33961 : if (suggestionAllowed)
3663 : {
3664 14 : const auto suggestions = GetSuggestionsForArgumentName(osName);
3665 7 : if (!suggestions.empty())
3666 : {
3667 2 : CPLError(CE_Failure, CPLE_AppDefined,
3668 : "Argument '%s' is unknown. Do you mean %s?",
3669 : osName.c_str(),
3670 4 : FormatSuggestionsAsString(suggestions,
3671 : /* addDashDashPrefix = */ false)
3672 : .c_str());
3673 : }
3674 : }
3675 :
3676 33961 : return nullptr;
3677 : }
3678 :
3679 : /************************************************************************/
3680 : /* GDALAlgorithm::AddAliasFor() */
3681 : /************************************************************************/
3682 :
3683 : //! @cond Doxygen_Suppress
3684 81955 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3685 : const std::string &alias)
3686 : {
3687 81955 : if (cpl::contains(m_mapLongNameToArg, alias))
3688 : {
3689 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3690 : alias.c_str());
3691 : }
3692 : else
3693 : {
3694 81954 : m_mapLongNameToArg[alias] = arg;
3695 : }
3696 81955 : }
3697 :
3698 : //! @endcond
3699 :
3700 : /************************************************************************/
3701 : /* GDALAlgorithm::AddShortNameAliasFor() */
3702 : /************************************************************************/
3703 :
3704 : //! @cond Doxygen_Suppress
3705 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3706 : char shortNameAlias)
3707 : {
3708 96 : std::string alias;
3709 48 : alias += shortNameAlias;
3710 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3711 : {
3712 0 : ReportError(CE_Failure, CPLE_AppDefined,
3713 : "Short name '%s' already declared.", alias.c_str());
3714 : }
3715 : else
3716 : {
3717 48 : m_mapShortNameToArg[alias] = arg;
3718 : }
3719 48 : }
3720 :
3721 : //! @endcond
3722 :
3723 : /************************************************************************/
3724 : /* GDALAlgorithm::SetPositional() */
3725 : /************************************************************************/
3726 :
3727 : //! @cond Doxygen_Suppress
3728 22269 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3729 : {
3730 22269 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3731 : arg) == m_positionalArgs.end());
3732 22269 : m_positionalArgs.push_back(arg);
3733 22269 : }
3734 :
3735 : //! @endcond
3736 :
3737 : /************************************************************************/
3738 : /* GDALAlgorithm::HasSubAlgorithms() */
3739 : /************************************************************************/
3740 :
3741 13135 : bool GDALAlgorithm::HasSubAlgorithms() const
3742 : {
3743 13135 : if (!m_subAlgRegistry.empty())
3744 3350 : return true;
3745 9785 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3746 19570 : .GetDeclaredSubAlgorithmNames(m_callPath)
3747 9785 : .empty();
3748 : }
3749 :
3750 : /************************************************************************/
3751 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3752 : /************************************************************************/
3753 :
3754 1378 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3755 : {
3756 1378 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3757 1378 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3758 2756 : .GetDeclaredSubAlgorithmNames(m_callPath);
3759 1378 : ret.insert(ret.end(), other.begin(), other.end());
3760 1378 : if (!other.empty())
3761 461 : std::sort(ret.begin(), ret.end());
3762 2756 : return ret;
3763 : }
3764 :
3765 : /************************************************************************/
3766 : /* GDALAlgorithm::AddArg() */
3767 : /************************************************************************/
3768 :
3769 : GDALInConstructionAlgorithmArg &
3770 318530 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3771 : {
3772 318530 : auto argRaw = arg.get();
3773 318530 : const auto &longName = argRaw->GetName();
3774 318530 : if (!longName.empty())
3775 : {
3776 318517 : if (longName[0] == '-')
3777 : {
3778 1 : ReportError(CE_Failure, CPLE_AppDefined,
3779 : "Long name '%s' should not start with '-'",
3780 : longName.c_str());
3781 : }
3782 318517 : if (longName.find('=') != std::string::npos)
3783 : {
3784 1 : ReportError(CE_Failure, CPLE_AppDefined,
3785 : "Long name '%s' should not contain a '=' character",
3786 : longName.c_str());
3787 : }
3788 318517 : if (cpl::contains(m_mapLongNameToArg, longName))
3789 : {
3790 1 : ReportError(CE_Failure, CPLE_AppDefined,
3791 : "Long name '%s' already declared", longName.c_str());
3792 : }
3793 318517 : m_mapLongNameToArg[longName] = argRaw;
3794 : }
3795 318530 : const auto &shortName = argRaw->GetShortName();
3796 318530 : if (!shortName.empty())
3797 : {
3798 154352 : if (shortName.size() != 1 ||
3799 77176 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3800 65 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3801 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3802 : {
3803 1 : ReportError(CE_Failure, CPLE_AppDefined,
3804 : "Short name '%s' should be a single letter or digit",
3805 : shortName.c_str());
3806 : }
3807 77176 : if (cpl::contains(m_mapShortNameToArg, shortName))
3808 : {
3809 1 : ReportError(CE_Failure, CPLE_AppDefined,
3810 : "Short name '%s' already declared", shortName.c_str());
3811 : }
3812 77176 : m_mapShortNameToArg[shortName] = argRaw;
3813 : }
3814 318530 : m_args.emplace_back(std::move(arg));
3815 : return *(
3816 318530 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3817 : }
3818 :
3819 : GDALInConstructionAlgorithmArg &
3820 142131 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3821 : const std::string &helpMessage, bool *pValue)
3822 : {
3823 142131 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3824 : this,
3825 284262 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3826 284262 : pValue));
3827 : }
3828 :
3829 : GDALInConstructionAlgorithmArg &
3830 51732 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3831 : const std::string &helpMessage, std::string *pValue)
3832 : {
3833 51732 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3834 : this,
3835 103464 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3836 103464 : pValue));
3837 : }
3838 :
3839 : GDALInConstructionAlgorithmArg &
3840 12531 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3841 : const std::string &helpMessage, int *pValue)
3842 : {
3843 12531 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3844 : this,
3845 25062 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3846 25062 : pValue));
3847 : }
3848 :
3849 : GDALInConstructionAlgorithmArg &
3850 10242 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3851 : const std::string &helpMessage, double *pValue)
3852 : {
3853 10242 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3854 : this,
3855 20484 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3856 20484 : pValue));
3857 : }
3858 :
3859 : GDALInConstructionAlgorithmArg &
3860 12130 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3861 : const std::string &helpMessage,
3862 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3863 : {
3864 24260 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3865 : this,
3866 24260 : GDALAlgorithmArgDecl(longName, chShortName,
3867 : helpMessage, GAAT_DATASET),
3868 12130 : pValue))
3869 12130 : .SetDatasetType(type);
3870 12130 : pValue->SetOwnerArgument(&arg);
3871 12130 : return arg;
3872 : }
3873 :
3874 : GDALInConstructionAlgorithmArg &
3875 67974 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3876 : const std::string &helpMessage,
3877 : std::vector<std::string> *pValue)
3878 : {
3879 67974 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3880 : this,
3881 135948 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3882 : GAAT_STRING_LIST),
3883 135948 : pValue));
3884 : }
3885 :
3886 : GDALInConstructionAlgorithmArg &
3887 2259 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3888 : const std::string &helpMessage, std::vector<int> *pValue)
3889 : {
3890 2259 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3891 : this,
3892 4518 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3893 : GAAT_INTEGER_LIST),
3894 4518 : pValue));
3895 : }
3896 :
3897 : GDALInConstructionAlgorithmArg &
3898 5280 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3899 : const std::string &helpMessage,
3900 : std::vector<double> *pValue)
3901 : {
3902 5280 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3903 : this,
3904 10560 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3905 : GAAT_REAL_LIST),
3906 10560 : pValue));
3907 : }
3908 :
3909 : GDALInConstructionAlgorithmArg &
3910 14251 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3911 : const std::string &helpMessage,
3912 : std::vector<GDALArgDatasetValue> *pValue,
3913 : GDALArgDatasetType type)
3914 : {
3915 28502 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3916 : this,
3917 28502 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3918 : GAAT_DATASET_LIST),
3919 14251 : pValue))
3920 28502 : .SetDatasetType(type);
3921 : }
3922 :
3923 : /************************************************************************/
3924 : /* MsgOrDefault() */
3925 : /************************************************************************/
3926 :
3927 106432 : inline const char *MsgOrDefault(const char *helpMessage,
3928 : const char *defaultMessage)
3929 : {
3930 106432 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3931 : }
3932 :
3933 : /************************************************************************/
3934 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3935 : /************************************************************************/
3936 :
3937 : /* static */
3938 17456 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3939 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3940 : {
3941 : arg.SetAutoCompleteFunction(
3942 7 : [&arg,
3943 2467 : type](const std::string ¤tValue) -> std::vector<std::string>
3944 : {
3945 14 : std::vector<std::string> oRet;
3946 :
3947 7 : if (arg.IsHidden())
3948 0 : return oRet;
3949 :
3950 : {
3951 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3952 : VSIStatBufL sStat;
3953 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3954 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3955 : {
3956 0 : return oRet;
3957 : }
3958 : }
3959 :
3960 7 : auto poDM = GetGDALDriverManager();
3961 14 : std::set<std::string> oExtensions;
3962 7 : if (type)
3963 : {
3964 1374 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3965 : {
3966 1368 : auto poDriver = poDM->GetDriver(i);
3967 3876 : if (((type & GDAL_OF_RASTER) != 0 &&
3968 1140 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3969 588 : ((type & GDAL_OF_VECTOR) != 0 &&
3970 2873 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3971 497 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3972 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3973 : {
3974 : const char *pszExtensions =
3975 871 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3976 871 : if (pszExtensions)
3977 : {
3978 : const CPLStringList aosExts(
3979 1154 : CSLTokenizeString2(pszExtensions, " ", 0));
3980 1303 : for (const char *pszExt : cpl::Iterate(aosExts))
3981 726 : oExtensions.insert(CPLString(pszExt).tolower());
3982 : }
3983 : }
3984 : }
3985 : }
3986 :
3987 14 : std::string osDir;
3988 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3989 14 : std::string osPrefix;
3990 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3991 : {
3992 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3993 : {
3994 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3995 : {
3996 2 : osPrefix = pszPrefix;
3997 2 : break;
3998 : }
3999 : }
4000 3 : if (osPrefix.empty())
4001 1 : return aosVSIPrefixes;
4002 2 : if (currentValue == osPrefix)
4003 1 : osDir = osPrefix;
4004 : }
4005 6 : if (osDir.empty())
4006 : {
4007 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
4008 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
4009 0 : osDir = std::move(osPrefix);
4010 : }
4011 :
4012 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
4013 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
4014 6 : if (currentValue.empty())
4015 1 : osDir.clear();
4016 : const std::string currentFilename =
4017 12 : CPLGetFilename(currentValue.c_str());
4018 6 : if (psDir)
4019 : {
4020 448 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
4021 : {
4022 443 : if ((currentFilename.empty() ||
4023 221 : STARTS_WITH(psEntry->pszName,
4024 223 : currentFilename.c_str())) &&
4025 223 : strcmp(psEntry->pszName, ".") != 0 &&
4026 1331 : strcmp(psEntry->pszName, "..") != 0 &&
4027 223 : (oExtensions.empty() ||
4028 222 : !strstr(psEntry->pszName, ".aux.xml")))
4029 : {
4030 882 : if (oExtensions.empty() ||
4031 220 : cpl::contains(
4032 : oExtensions,
4033 441 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
4034 661 : .tolower()) ||
4035 188 : VSI_ISDIR(psEntry->nMode))
4036 : {
4037 74 : std::string osVal;
4038 37 : if (osDir.empty() || osDir == ".")
4039 4 : osVal = psEntry->pszName;
4040 : else
4041 66 : osVal = CPLFormFilenameSafe(
4042 66 : osDir.c_str(), psEntry->pszName, nullptr);
4043 37 : if (VSI_ISDIR(psEntry->nMode))
4044 4 : osVal += osSep;
4045 37 : oRet.push_back(std::move(osVal));
4046 : }
4047 : }
4048 443 : }
4049 5 : VSICloseDir(psDir);
4050 : }
4051 6 : return oRet;
4052 17456 : });
4053 17456 : }
4054 :
4055 : /************************************************************************/
4056 : /* GDALAlgorithm::AddInputDatasetArg() */
4057 : /************************************************************************/
4058 :
4059 854 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4060 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4061 : bool positionalAndRequired, const char *helpMessage)
4062 : {
4063 : auto &arg = AddArg(
4064 : GDAL_ARG_NAME_INPUT, 'i',
4065 : MsgOrDefault(helpMessage,
4066 : CPLSPrintf("Input %s dataset",
4067 854 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4068 1708 : pValue, type);
4069 854 : if (positionalAndRequired)
4070 847 : arg.SetPositional().SetRequired();
4071 :
4072 854 : SetAutoCompleteFunctionForFilename(arg, type);
4073 :
4074 854 : AddValidationAction(
4075 157 : [pValue]()
4076 : {
4077 156 : if (pValue->GetName() == "-")
4078 1 : pValue->Set("/vsistdin/");
4079 156 : return true;
4080 : });
4081 :
4082 854 : return arg;
4083 : }
4084 :
4085 : /************************************************************************/
4086 : /* GDALAlgorithm::AddInputDatasetArg() */
4087 : /************************************************************************/
4088 :
4089 13787 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4090 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
4091 : bool positionalAndRequired, const char *helpMessage)
4092 : {
4093 : auto &arg =
4094 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
4095 : MsgOrDefault(
4096 : helpMessage,
4097 : CPLSPrintf("Input %s datasets",
4098 13787 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4099 41361 : pValue, type)
4100 13787 : .SetPackedValuesAllowed(false);
4101 13787 : if (positionalAndRequired)
4102 1734 : arg.SetPositional().SetRequired();
4103 :
4104 13787 : SetAutoCompleteFunctionForFilename(arg, type);
4105 :
4106 13787 : AddValidationAction(
4107 6674 : [pValue]()
4108 : {
4109 12587 : for (auto &val : *pValue)
4110 : {
4111 5913 : if (val.GetName() == "-")
4112 1 : val.Set("/vsistdin/");
4113 : }
4114 6674 : return true;
4115 : });
4116 13787 : return arg;
4117 : }
4118 :
4119 : /************************************************************************/
4120 : /* GDALAlgorithm::AddOutputDatasetArg() */
4121 : /************************************************************************/
4122 :
4123 8534 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4124 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4125 : bool positionalAndRequired, const char *helpMessage)
4126 : {
4127 : auto &arg =
4128 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4129 : MsgOrDefault(
4130 : helpMessage,
4131 : CPLSPrintf("Output %s dataset",
4132 8534 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4133 25602 : pValue, type)
4134 8534 : .SetIsInput(true)
4135 8534 : .SetIsOutput(true)
4136 8534 : .SetDatasetInputFlags(GADV_NAME)
4137 8534 : .SetDatasetOutputFlags(GADV_OBJECT);
4138 8534 : if (positionalAndRequired)
4139 4392 : arg.SetPositional().SetRequired();
4140 :
4141 8534 : AddValidationAction(
4142 12854 : [this, &arg, pValue]()
4143 : {
4144 3882 : if (pValue->GetName() == "-")
4145 4 : pValue->Set("/vsistdout/");
4146 :
4147 3882 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4148 3830 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4149 6056 : (!outputFormatArg->IsExplicitlySet() ||
4150 9938 : outputFormatArg->Get<std::string>().empty()) &&
4151 1604 : arg.IsExplicitlySet())
4152 : {
4153 : const auto vrtCompatible =
4154 1148 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4155 192 : if (vrtCompatible && !vrtCompatible->empty() &&
4156 1340 : vrtCompatible->front() == "false" &&
4157 1244 : EQUAL(
4158 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4159 : "VRT"))
4160 : {
4161 6 : ReportError(
4162 : CE_Failure, CPLE_NotSupported,
4163 : "VRT output is not supported.%s",
4164 6 : outputFormatArg->GetDescription().find("GDALG") !=
4165 : std::string::npos
4166 : ? " Consider using the GDALG driver instead (files "
4167 : "with .gdalg.json extension)"
4168 : : "");
4169 6 : return false;
4170 : }
4171 1142 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4172 2261 : EQUAL(pValue->GetName()
4173 : .substr(pValue->GetName().size() -
4174 : strlen(".gdalg.json"))
4175 : .c_str(),
4176 3403 : ".gdalg.json") &&
4177 27 : outputFormatArg->GetDescription().find("GDALG") ==
4178 : std::string::npos)
4179 : {
4180 0 : ReportError(CE_Failure, CPLE_NotSupported,
4181 : "GDALG output is not supported");
4182 0 : return false;
4183 : }
4184 : }
4185 3876 : return true;
4186 : });
4187 :
4188 8534 : return arg;
4189 : }
4190 :
4191 : /************************************************************************/
4192 : /* GDALAlgorithm::AddOverwriteArg() */
4193 : /************************************************************************/
4194 :
4195 : GDALInConstructionAlgorithmArg &
4196 8405 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4197 : {
4198 : return AddArg(
4199 : GDAL_ARG_NAME_OVERWRITE, 0,
4200 : MsgOrDefault(
4201 : helpMessage,
4202 : _("Whether overwriting existing output dataset is allowed")),
4203 16810 : pValue)
4204 16810 : .SetDefault(false);
4205 : }
4206 :
4207 : /************************************************************************/
4208 : /* GDALAlgorithm::AddOverwriteLayerArg() */
4209 : /************************************************************************/
4210 :
4211 : GDALInConstructionAlgorithmArg &
4212 3476 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4213 : {
4214 3476 : AddValidationAction(
4215 1602 : [this]
4216 : {
4217 1601 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4218 1601 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4219 : {
4220 1 : ReportError(CE_Failure, CPLE_AppDefined,
4221 : "--update argument must exist for "
4222 : "--overwrite-layer, even if hidden");
4223 1 : return false;
4224 : }
4225 1600 : return true;
4226 : });
4227 : return AddArg(
4228 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4229 : MsgOrDefault(
4230 : helpMessage,
4231 : _("Whether overwriting existing output layer is allowed")),
4232 6952 : pValue)
4233 3476 : .SetDefault(false)
4234 : .AddAction(
4235 19 : [this]
4236 : {
4237 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4238 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4239 : {
4240 19 : updateArg->Set(true);
4241 : }
4242 6971 : });
4243 : }
4244 :
4245 : /************************************************************************/
4246 : /* GDALAlgorithm::AddUpdateArg() */
4247 : /************************************************************************/
4248 :
4249 : GDALInConstructionAlgorithmArg &
4250 4039 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4251 : {
4252 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4253 : MsgOrDefault(
4254 : helpMessage,
4255 : _("Whether to open existing dataset in update mode")),
4256 8078 : pValue)
4257 8078 : .SetDefault(false);
4258 : }
4259 :
4260 : /************************************************************************/
4261 : /* GDALAlgorithm::AddAppendLayerArg() */
4262 : /************************************************************************/
4263 :
4264 : GDALInConstructionAlgorithmArg &
4265 3251 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4266 : {
4267 3251 : AddValidationAction(
4268 1557 : [this]
4269 : {
4270 1556 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4271 1556 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4272 : {
4273 1 : ReportError(CE_Failure, CPLE_AppDefined,
4274 : "--update argument must exist for --append, even "
4275 : "if hidden");
4276 1 : return false;
4277 : }
4278 1555 : return true;
4279 : });
4280 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4281 : MsgOrDefault(
4282 : helpMessage,
4283 : _("Whether appending to existing layer is allowed")),
4284 6502 : pValue)
4285 3251 : .SetDefault(false)
4286 : .AddAction(
4287 25 : [this]
4288 : {
4289 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4290 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4291 : {
4292 25 : updateArg->Set(true);
4293 : }
4294 6527 : });
4295 : }
4296 :
4297 : /************************************************************************/
4298 : /* GDALAlgorithm::AddOptionsSuggestions() */
4299 : /************************************************************************/
4300 :
4301 : /* static */
4302 30 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4303 : const std::string ¤tValue,
4304 : std::vector<std::string> &oRet)
4305 : {
4306 30 : if (!pszXML)
4307 0 : return false;
4308 60 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4309 30 : if (!poTree)
4310 0 : return false;
4311 :
4312 60 : std::string typedOptionName = currentValue;
4313 30 : const auto posEqual = typedOptionName.find('=');
4314 60 : std::string typedValue;
4315 30 : if (posEqual != 0 && posEqual != std::string::npos)
4316 : {
4317 2 : typedValue = currentValue.substr(posEqual + 1);
4318 2 : typedOptionName.resize(posEqual);
4319 : }
4320 :
4321 453 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4322 423 : psChild = psChild->psNext)
4323 : {
4324 436 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4325 449 : if (pszName && typedOptionName == pszName &&
4326 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4327 2 : strcmp(psChild->pszValue, "Argument") == 0))
4328 : {
4329 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4330 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4331 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4332 13 : if (EQUAL(pszType, "string-select"))
4333 : {
4334 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4335 85 : psChild2 = psChild2->psNext)
4336 : {
4337 85 : if (EQUAL(psChild2->pszValue, "Value"))
4338 : {
4339 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4340 : }
4341 : }
4342 : }
4343 8 : else if (EQUAL(pszType, "boolean"))
4344 : {
4345 3 : if (typedValue == "YES" || typedValue == "NO")
4346 : {
4347 1 : oRet.push_back(currentValue);
4348 1 : return true;
4349 : }
4350 2 : oRet.push_back("NO");
4351 2 : oRet.push_back("YES");
4352 : }
4353 5 : else if (EQUAL(pszType, "int"))
4354 : {
4355 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4356 2 : atoi(pszMax) - atoi(pszMin) < 25)
4357 : {
4358 1 : const int nMax = atoi(pszMax);
4359 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4360 12 : oRet.push_back(std::to_string(i));
4361 : }
4362 : }
4363 :
4364 12 : if (oRet.empty())
4365 : {
4366 4 : if (pszMin && pszMax)
4367 : {
4368 1 : oRet.push_back(std::string("##"));
4369 2 : oRet.push_back(std::string("validity range: [")
4370 1 : .append(pszMin)
4371 1 : .append(",")
4372 1 : .append(pszMax)
4373 1 : .append("]"));
4374 : }
4375 3 : else if (pszMin)
4376 : {
4377 1 : oRet.push_back(std::string("##"));
4378 1 : oRet.push_back(
4379 1 : std::string("validity range: >= ").append(pszMin));
4380 : }
4381 2 : else if (pszMax)
4382 : {
4383 1 : oRet.push_back(std::string("##"));
4384 1 : oRet.push_back(
4385 1 : std::string("validity range: <= ").append(pszMax));
4386 : }
4387 1 : else if (const char *pszDescription =
4388 1 : CPLGetXMLValue(psChild, "description", nullptr))
4389 : {
4390 1 : oRet.push_back(std::string("##"));
4391 2 : oRet.push_back(std::string("type: ")
4392 1 : .append(pszType)
4393 1 : .append(", description: ")
4394 1 : .append(pszDescription));
4395 : }
4396 : }
4397 :
4398 12 : return true;
4399 : }
4400 : }
4401 :
4402 367 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4403 350 : psChild = psChild->psNext)
4404 : {
4405 350 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4406 350 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4407 5 : strcmp(psChild->pszValue, "Argument") == 0))
4408 : {
4409 347 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4410 347 : if (!pszScope ||
4411 40 : (EQUAL(pszScope, "raster") &&
4412 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4413 20 : (EQUAL(pszScope, "vector") &&
4414 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4415 : {
4416 327 : oRet.push_back(std::string(pszName).append("="));
4417 : }
4418 : }
4419 : }
4420 :
4421 17 : return false;
4422 : }
4423 :
4424 : /************************************************************************/
4425 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4426 : /************************************************************************/
4427 :
4428 : //! @cond Doxygen_Suppress
4429 : std::vector<std::string>
4430 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4431 : {
4432 2 : std::vector<std::string> oRet;
4433 :
4434 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4435 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4436 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4437 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4438 : {
4439 2 : datasetType = inputArg->GetDatasetType();
4440 : }
4441 :
4442 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4443 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4444 2 : inputFormat->IsExplicitlySet())
4445 : {
4446 : const auto &aosAllowedDrivers =
4447 1 : inputFormat->Get<std::vector<std::string>>();
4448 1 : if (aosAllowedDrivers.size() == 1)
4449 : {
4450 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4451 1 : aosAllowedDrivers[0].c_str());
4452 1 : if (poDriver)
4453 : {
4454 1 : AddOptionsSuggestions(
4455 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4456 : datasetType, currentValue, oRet);
4457 : }
4458 1 : return oRet;
4459 : }
4460 : }
4461 :
4462 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4463 373 : &oRet](const GDALArgDatasetValue &datasetValue)
4464 : {
4465 1 : auto poDM = GetGDALDriverManager();
4466 :
4467 1 : const auto &osDSName = datasetValue.GetName();
4468 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4469 1 : if (!osExt.empty())
4470 : {
4471 1 : std::set<std::string> oVisitedExtensions;
4472 229 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4473 : {
4474 228 : auto poDriver = poDM->GetDriver(i);
4475 684 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4476 228 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4477 72 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4478 456 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4479 72 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4480 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4481 : {
4482 : const char *pszExtensions =
4483 156 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4484 156 : if (pszExtensions)
4485 : {
4486 : const CPLStringList aosExts(
4487 103 : CSLTokenizeString2(pszExtensions, " ", 0));
4488 227 : for (const char *pszExt : cpl::Iterate(aosExts))
4489 : {
4490 128 : if (EQUAL(pszExt, osExt.c_str()) &&
4491 3 : !cpl::contains(oVisitedExtensions, pszExt))
4492 : {
4493 1 : oVisitedExtensions.insert(pszExt);
4494 1 : if (AddOptionsSuggestions(
4495 : poDriver->GetMetadataItem(
4496 1 : GDAL_DMD_OPENOPTIONLIST),
4497 : datasetType, currentValue, oRet))
4498 : {
4499 0 : return;
4500 : }
4501 1 : break;
4502 : }
4503 : }
4504 : }
4505 : }
4506 : }
4507 : }
4508 1 : };
4509 :
4510 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4511 : {
4512 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4513 0 : AddSuggestions(datasetValue);
4514 : }
4515 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4516 : {
4517 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4518 1 : if (datasetValues.size() == 1)
4519 1 : AddSuggestions(datasetValues[0]);
4520 : }
4521 :
4522 1 : return oRet;
4523 : }
4524 :
4525 : //! @endcond
4526 :
4527 : /************************************************************************/
4528 : /* GDALAlgorithm::AddOpenOptionsArg() */
4529 : /************************************************************************/
4530 :
4531 : GDALInConstructionAlgorithmArg &
4532 9317 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4533 : const char *helpMessage)
4534 : {
4535 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4536 18634 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4537 18634 : .AddAlias("oo")
4538 18634 : .SetMetaVar("<KEY>=<VALUE>")
4539 9317 : .SetPackedValuesAllowed(false)
4540 9317 : .SetCategory(GAAC_ADVANCED);
4541 :
4542 31 : arg.AddValidationAction([this, &arg]()
4543 9348 : { return ParseAndValidateKeyValue(arg); });
4544 :
4545 : arg.SetAutoCompleteFunction(
4546 2 : [this](const std::string ¤tValue)
4547 9319 : { return OpenOptionCompleteFunction(currentValue); });
4548 :
4549 9317 : return arg;
4550 : }
4551 :
4552 : /************************************************************************/
4553 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4554 : /************************************************************************/
4555 :
4556 : GDALInConstructionAlgorithmArg &
4557 3249 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4558 : const char *helpMessage)
4559 : {
4560 : auto &arg =
4561 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4562 6498 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4563 6498 : .AddAlias("output-oo")
4564 6498 : .SetMetaVar("<KEY>=<VALUE>")
4565 3249 : .SetPackedValuesAllowed(false)
4566 3249 : .SetCategory(GAAC_ADVANCED);
4567 :
4568 0 : arg.AddValidationAction([this, &arg]()
4569 3249 : { return ParseAndValidateKeyValue(arg); });
4570 :
4571 : arg.SetAutoCompleteFunction(
4572 0 : [this](const std::string ¤tValue)
4573 3249 : { return OpenOptionCompleteFunction(currentValue); });
4574 :
4575 3249 : return arg;
4576 : }
4577 :
4578 : /************************************************************************/
4579 : /* ValidateFormat() */
4580 : /************************************************************************/
4581 :
4582 4755 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4583 : bool bStreamAllowed,
4584 : bool bGDALGAllowed) const
4585 : {
4586 4755 : if (arg.GetChoices().empty())
4587 : {
4588 : const auto Validate =
4589 20519 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4590 : {
4591 4650 : if (const auto extraFormats =
4592 4650 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4593 : {
4594 60 : for (const auto &extraFormat : *extraFormats)
4595 : {
4596 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4597 14 : return true;
4598 : }
4599 : }
4600 :
4601 4636 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4602 1821 : return true;
4603 :
4604 2821 : if (EQUAL(val.c_str(), "GDALG") &&
4605 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4606 : {
4607 2 : if (bGDALGAllowed)
4608 : {
4609 2 : return true;
4610 : }
4611 : else
4612 : {
4613 0 : ReportError(CE_Failure, CPLE_NotSupported,
4614 : "GDALG output is not supported.");
4615 0 : return false;
4616 : }
4617 : }
4618 :
4619 : const auto vrtCompatible =
4620 2813 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4621 540 : if (vrtCompatible && !vrtCompatible->empty() &&
4622 3353 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4623 : {
4624 7 : ReportError(CE_Failure, CPLE_NotSupported,
4625 : "VRT output is not supported.%s",
4626 : bGDALGAllowed
4627 : ? " Consider using the GDALG driver instead "
4628 : "(files with .gdalg.json extension)."
4629 : : "");
4630 7 : return false;
4631 : }
4632 :
4633 : const auto allowedFormats =
4634 2806 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4635 2859 : if (allowedFormats && !allowedFormats->empty() &&
4636 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4637 2859 : val) != allowedFormats->end())
4638 : {
4639 12 : return true;
4640 : }
4641 :
4642 : const auto excludedFormats =
4643 2794 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4644 2841 : if (excludedFormats && !excludedFormats->empty() &&
4645 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4646 2841 : val) != excludedFormats->end())
4647 : {
4648 0 : ReportError(CE_Failure, CPLE_NotSupported,
4649 : "%s output is not supported.", val.c_str());
4650 0 : return false;
4651 : }
4652 :
4653 2794 : auto hDriver = GDALGetDriverByName(val.c_str());
4654 2794 : if (!hDriver)
4655 : {
4656 : auto poMissingDriver =
4657 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4658 4 : if (poMissingDriver)
4659 : {
4660 : const std::string msg =
4661 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4662 0 : ReportError(CE_Failure, CPLE_AppDefined,
4663 : "Invalid value for argument '%s'. Driver '%s' "
4664 : "not found but is known. However plugin %s",
4665 0 : arg.GetName().c_str(), val.c_str(),
4666 : msg.c_str());
4667 : }
4668 : else
4669 : {
4670 8 : ReportError(CE_Failure, CPLE_AppDefined,
4671 : "Invalid value for argument '%s'. Driver '%s' "
4672 : "does not exist.",
4673 4 : arg.GetName().c_str(), val.c_str());
4674 : }
4675 4 : return false;
4676 : }
4677 :
4678 2790 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4679 2790 : if (caps)
4680 : {
4681 8464 : for (const std::string &cap : *caps)
4682 : {
4683 : const char *pszVal =
4684 5705 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4685 5705 : if (!(pszVal && pszVal[0]))
4686 : {
4687 1587 : if (cap == GDAL_DCAP_CREATECOPY &&
4688 0 : std::find(caps->begin(), caps->end(),
4689 792 : GDAL_DCAP_RASTER) != caps->end() &&
4690 792 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4691 1587 : nullptr) &&
4692 792 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4693 : nullptr))
4694 : {
4695 : // if it supports Create, it supports CreateCopy
4696 : }
4697 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4698 : {
4699 2 : ReportError(
4700 : CE_Failure, CPLE_AppDefined,
4701 : "Invalid value for argument '%s'. Driver '%s' "
4702 : "does "
4703 : "not advertise any file format extension.",
4704 1 : arg.GetName().c_str(), val.c_str());
4705 3 : return false;
4706 : }
4707 : else
4708 : {
4709 2 : if (cap == GDAL_DCAP_CREATE)
4710 : {
4711 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4712 1 : if (updateArg &&
4713 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4714 1 : updateArg->IsExplicitlySet())
4715 : {
4716 0 : continue;
4717 : }
4718 :
4719 2 : ReportError(
4720 : CE_Failure, CPLE_AppDefined,
4721 : "Invalid value for argument '%s'. "
4722 : "Driver '%s' does not have write support.",
4723 1 : arg.GetName().c_str(), val.c_str());
4724 1 : return false;
4725 : }
4726 : else
4727 : {
4728 2 : ReportError(
4729 : CE_Failure, CPLE_AppDefined,
4730 : "Invalid value for argument '%s'. Driver "
4731 : "'%s' "
4732 : "does "
4733 : "not expose the required '%s' capability.",
4734 1 : arg.GetName().c_str(), val.c_str(),
4735 : cap.c_str());
4736 1 : return false;
4737 : }
4738 : }
4739 : }
4740 : }
4741 : }
4742 2787 : return true;
4743 4653 : };
4744 :
4745 4653 : if (arg.GetType() == GAAT_STRING)
4746 : {
4747 4640 : return Validate(arg.Get<std::string>());
4748 : }
4749 15 : else if (arg.GetType() == GAAT_STRING_LIST)
4750 : {
4751 25 : for (const auto &val : arg.Get<std::vector<std::string>>())
4752 : {
4753 12 : if (!Validate(val))
4754 2 : return false;
4755 : }
4756 : }
4757 : }
4758 :
4759 115 : return true;
4760 : }
4761 :
4762 : /************************************************************************/
4763 : /* FormatAutoCompleteFunction() */
4764 : /************************************************************************/
4765 :
4766 : /* static */
4767 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4768 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4769 : {
4770 7 : std::vector<std::string> res;
4771 7 : auto poDM = GetGDALDriverManager();
4772 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4773 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4774 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4775 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4776 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4777 0 : res = std::move(*extraFormats);
4778 1602 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4779 : {
4780 1595 : auto poDriver = poDM->GetDriver(i);
4781 :
4782 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4783 1595 : vrtCompatible->front() == "false" &&
4784 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4785 : {
4786 : // do nothing
4787 : }
4788 1595 : else if (allowedFormats && !allowedFormats->empty() &&
4789 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4790 1595 : poDriver->GetDescription()) != allowedFormats->end())
4791 : {
4792 0 : res.push_back(poDriver->GetDescription());
4793 : }
4794 1595 : else if (excludedFormats && !excludedFormats->empty() &&
4795 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4796 0 : poDriver->GetDescription()) !=
4797 1595 : excludedFormats->end())
4798 : {
4799 0 : continue;
4800 : }
4801 1595 : else if (caps)
4802 : {
4803 1595 : bool ok = true;
4804 3155 : for (const std::string &cap : *caps)
4805 : {
4806 2374 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4807 : {
4808 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4809 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4810 : {
4811 0 : ok = false;
4812 0 : break;
4813 : }
4814 : }
4815 2374 : else if (const char *pszVal =
4816 2374 : poDriver->GetMetadataItem(cap.c_str());
4817 1488 : pszVal && pszVal[0])
4818 : {
4819 : }
4820 1274 : else if (cap == GDAL_DCAP_CREATECOPY &&
4821 0 : (std::find(caps->begin(), caps->end(),
4822 388 : GDAL_DCAP_RASTER) != caps->end() &&
4823 1662 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4824 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4825 : {
4826 : // if it supports Create, it supports CreateCopy
4827 : }
4828 : else
4829 : {
4830 814 : ok = false;
4831 814 : break;
4832 : }
4833 : }
4834 1595 : if (ok)
4835 : {
4836 781 : res.push_back(poDriver->GetDescription());
4837 : }
4838 : }
4839 : }
4840 7 : if (bGDALGAllowed)
4841 4 : res.push_back("GDALG");
4842 7 : return res;
4843 : }
4844 :
4845 : /************************************************************************/
4846 : /* GDALAlgorithm::AddInputFormatsArg() */
4847 : /************************************************************************/
4848 :
4849 : GDALInConstructionAlgorithmArg &
4850 9102 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4851 : const char *helpMessage)
4852 : {
4853 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4854 18204 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4855 18204 : .AddAlias("if")
4856 9102 : .SetCategory(GAAC_ADVANCED);
4857 15 : arg.AddValidationAction([this, &arg]()
4858 9117 : { return ValidateFormat(arg, false, false); });
4859 : arg.SetAutoCompleteFunction(
4860 1 : [&arg](const std::string &)
4861 9103 : { return FormatAutoCompleteFunction(arg, false, false); });
4862 9102 : return arg;
4863 : }
4864 :
4865 : /************************************************************************/
4866 : /* GDALAlgorithm::AddOutputFormatArg() */
4867 : /************************************************************************/
4868 :
4869 : GDALInConstructionAlgorithmArg &
4870 9553 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4871 : bool bGDALGAllowed, const char *helpMessage)
4872 : {
4873 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4874 : MsgOrDefault(helpMessage,
4875 : bGDALGAllowed
4876 : ? _("Output format (\"GDALG\" allowed)")
4877 : : _("Output format")),
4878 19106 : pValue)
4879 19106 : .AddAlias("of")
4880 9553 : .AddAlias("format");
4881 : arg.AddValidationAction(
4882 4736 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4883 14289 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4884 : arg.SetAutoCompleteFunction(
4885 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4886 : {
4887 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4888 4 : bGDALGAllowed);
4889 9553 : });
4890 9553 : return arg;
4891 : }
4892 :
4893 : /************************************************************************/
4894 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4895 : /************************************************************************/
4896 : GDALInConstructionAlgorithmArg &
4897 1755 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4898 : const char *helpMessage)
4899 : {
4900 : auto &arg =
4901 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4902 3510 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4903 3510 : .AddAlias("ot")
4904 3510 : .AddAlias("datatype")
4905 5265 : .AddMetadataItem("type", {"GDALDataType"})
4906 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4907 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4908 1755 : "Float32", "Float64", "CFloat32", "CFloat64")
4909 1755 : .SetHiddenChoices("Byte");
4910 1755 : return arg;
4911 : }
4912 :
4913 : /************************************************************************/
4914 : /* GDALAlgorithm::AddNodataArg() */
4915 : /************************************************************************/
4916 :
4917 : GDALInConstructionAlgorithmArg &
4918 653 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4919 : const std::string &optionName,
4920 : const char *helpMessage)
4921 : {
4922 : auto &arg = AddArg(
4923 : optionName, 0,
4924 : MsgOrDefault(helpMessage,
4925 : noneAllowed
4926 : ? _("Assign a specified nodata value to output bands "
4927 : "('none', numeric value, 'nan', 'inf', '-inf')")
4928 : : _("Assign a specified nodata value to output bands "
4929 : "(numeric value, 'nan', 'inf', '-inf')")),
4930 653 : pValue);
4931 : arg.AddValidationAction(
4932 336 : [this, pValue, noneAllowed, optionName]()
4933 : {
4934 73 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4935 : {
4936 63 : char *endptr = nullptr;
4937 63 : CPLStrtod(pValue->c_str(), &endptr);
4938 63 : if (endptr != pValue->c_str() + pValue->size())
4939 : {
4940 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4941 : "Value of '%s' should be %sa "
4942 : "numeric value, 'nan', 'inf' or '-inf'",
4943 : optionName.c_str(),
4944 : noneAllowed ? "'none', " : "");
4945 1 : return false;
4946 : }
4947 : }
4948 72 : return true;
4949 653 : });
4950 653 : return arg;
4951 : }
4952 :
4953 : /************************************************************************/
4954 : /* GDALAlgorithm::AddOutputStringArg() */
4955 : /************************************************************************/
4956 :
4957 : GDALInConstructionAlgorithmArg &
4958 6128 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4959 : {
4960 : return AddArg(
4961 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4962 : MsgOrDefault(helpMessage,
4963 : _("Output string, in which the result is placed")),
4964 12256 : pValue)
4965 6128 : .SetHiddenForCLI()
4966 6128 : .SetIsInput(false)
4967 12256 : .SetIsOutput(true);
4968 : }
4969 :
4970 : /************************************************************************/
4971 : /* GDALAlgorithm::AddStdoutArg() */
4972 : /************************************************************************/
4973 :
4974 : GDALInConstructionAlgorithmArg &
4975 1560 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4976 : {
4977 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4978 : MsgOrDefault(helpMessage,
4979 : _("Directly output on stdout. If enabled, "
4980 : "output-string will be empty")),
4981 3120 : pValue)
4982 3120 : .SetHidden();
4983 : }
4984 :
4985 : /************************************************************************/
4986 : /* GDALAlgorithm::AddLayerNameArg() */
4987 : /************************************************************************/
4988 :
4989 : GDALInConstructionAlgorithmArg &
4990 218 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4991 : {
4992 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4993 218 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4994 : }
4995 :
4996 : /************************************************************************/
4997 : /* GDALAlgorithm::AddArrayNameArg() */
4998 : /************************************************************************/
4999 :
5000 : GDALInConstructionAlgorithmArg &
5001 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
5002 : {
5003 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
5004 100 : pValue)
5005 2 : .SetAutoCompleteFunction([this](const std::string &)
5006 102 : { return AutoCompleteArrayName(); });
5007 : }
5008 :
5009 : /************************************************************************/
5010 : /* GDALAlgorithm::AddArrayNameArg() */
5011 : /************************************************************************/
5012 :
5013 : GDALInConstructionAlgorithmArg &
5014 76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
5015 : const char *helpMessage)
5016 : {
5017 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
5018 152 : pValue)
5019 0 : .SetAutoCompleteFunction([this](const std::string &)
5020 152 : { return AutoCompleteArrayName(); });
5021 : }
5022 :
5023 : /************************************************************************/
5024 : /* GDALAlgorithm::AutoCompleteArrayName() */
5025 : /************************************************************************/
5026 :
5027 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
5028 : {
5029 2 : std::vector<std::string> ret;
5030 4 : std::string osDSName;
5031 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
5032 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
5033 : {
5034 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
5035 0 : if (!inputDatasets.empty())
5036 : {
5037 0 : osDSName = inputDatasets[0].GetName();
5038 : }
5039 : }
5040 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
5041 : {
5042 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
5043 2 : osDSName = inputDataset.GetName();
5044 : }
5045 :
5046 2 : if (!osDSName.empty())
5047 : {
5048 4 : CPLStringList aosAllowedDrivers;
5049 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
5050 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
5051 : aosAllowedDrivers =
5052 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
5053 :
5054 4 : CPLStringList aosOpenOptions;
5055 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
5056 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
5057 : aosOpenOptions =
5058 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
5059 :
5060 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5061 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
5062 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
5063 : {
5064 2 : if (auto poRG = poDS->GetRootGroup())
5065 : {
5066 1 : ret = poRG->GetMDArrayFullNamesRecursive();
5067 : }
5068 : }
5069 : }
5070 :
5071 4 : return ret;
5072 : }
5073 :
5074 : /************************************************************************/
5075 : /* GDALAlgorithm::AddMemorySizeArg() */
5076 : /************************************************************************/
5077 :
5078 : GDALInConstructionAlgorithmArg &
5079 226 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
5080 : const std::string &optionName,
5081 : const char *helpMessage)
5082 : {
5083 452 : return AddArg(optionName, 0, helpMessage, pStrValue)
5084 226 : .SetDefault(*pStrValue)
5085 : .AddValidationAction(
5086 139 : [this, pValue, pStrValue]()
5087 : {
5088 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
5089 : GIntBig nBytes;
5090 : bool bUnitSpecified;
5091 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
5092 47 : &bUnitSpecified) != CE_None)
5093 : {
5094 2 : return false;
5095 : }
5096 45 : if (!bUnitSpecified)
5097 : {
5098 1 : ReportError(CE_Failure, CPLE_AppDefined,
5099 : "Memory size must have a unit or be a "
5100 : "percentage of usable RAM (2GB, 5%%, etc.)");
5101 1 : return false;
5102 : }
5103 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5104 : {
5105 : // -1 to please CoverityScan
5106 : if (static_cast<std::uint64_t>(nBytes) >
5107 : std::numeric_limits<size_t>::max() - 1U)
5108 : {
5109 : ReportError(CE_Failure, CPLE_AppDefined,
5110 : "Memory size %s is too large.",
5111 : pStrValue->c_str());
5112 : return false;
5113 : }
5114 : }
5115 :
5116 44 : *pValue = static_cast<size_t>(nBytes);
5117 44 : return true;
5118 452 : });
5119 : }
5120 :
5121 : /************************************************************************/
5122 : /* GDALAlgorithm::AddOutputLayerNameArg() */
5123 : /************************************************************************/
5124 :
5125 : GDALInConstructionAlgorithmArg &
5126 476 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5127 : const char *helpMessage)
5128 : {
5129 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5130 476 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5131 : }
5132 :
5133 : /************************************************************************/
5134 : /* GDALAlgorithm::AddLayerNameArg() */
5135 : /************************************************************************/
5136 :
5137 : GDALInConstructionAlgorithmArg &
5138 894 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5139 : const char *helpMessage)
5140 : {
5141 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5142 894 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5143 : }
5144 :
5145 : /************************************************************************/
5146 : /* GDALAlgorithm::AddGeometryTypeArg() */
5147 : /************************************************************************/
5148 :
5149 : GDALInConstructionAlgorithmArg &
5150 427 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5151 : {
5152 : return AddArg("geometry-type", 0,
5153 854 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5154 : .SetAutoCompleteFunction(
5155 3 : [](const std::string ¤tValue)
5156 : {
5157 3 : std::vector<std::string> oRet;
5158 51 : for (const char *type :
5159 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5160 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5161 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5162 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5163 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5164 : {
5165 68 : if (currentValue.empty() ||
5166 17 : STARTS_WITH(type, currentValue.c_str()))
5167 : {
5168 35 : oRet.push_back(type);
5169 35 : oRet.push_back(std::string(type).append("Z"));
5170 35 : oRet.push_back(std::string(type).append("M"));
5171 35 : oRet.push_back(std::string(type).append("ZM"));
5172 : }
5173 : }
5174 3 : return oRet;
5175 854 : })
5176 : .AddValidationAction(
5177 118 : [this, pValue]()
5178 : {
5179 107 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5180 115 : wkbUnknown &&
5181 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5182 : {
5183 3 : ReportError(CE_Failure, CPLE_AppDefined,
5184 : "Invalid geometry type '%s'", pValue->c_str());
5185 3 : return false;
5186 : }
5187 104 : return true;
5188 854 : });
5189 : }
5190 :
5191 : /************************************************************************/
5192 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
5193 : /************************************************************************/
5194 :
5195 : /* static */
5196 2991 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5197 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5198 : {
5199 2991 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5200 : datasetArg.GetType() == GAAT_DATASET_LIST);
5201 :
5202 : layerArg.SetAutoCompleteFunction(
5203 18 : [&datasetArg](const std::string ¤tValue)
5204 : {
5205 6 : std::vector<std::string> ret;
5206 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5207 6 : GDALArgDatasetValue *dsVal = nullptr;
5208 6 : if (datasetArg.GetType() == GAAT_DATASET)
5209 : {
5210 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5211 : }
5212 : else
5213 : {
5214 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5215 6 : if (val.size() == 1)
5216 : {
5217 6 : dsVal = &val[0];
5218 : }
5219 : }
5220 6 : if (dsVal && !dsVal->GetName().empty())
5221 : {
5222 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5223 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5224 6 : if (poDS)
5225 : {
5226 12 : for (auto &&poLayer : poDS->GetLayers())
5227 : {
5228 6 : if (currentValue == poLayer->GetDescription())
5229 : {
5230 1 : ret.clear();
5231 1 : ret.push_back(poLayer->GetDescription());
5232 1 : break;
5233 : }
5234 5 : ret.push_back(poLayer->GetDescription());
5235 : }
5236 : }
5237 : }
5238 12 : return ret;
5239 2991 : });
5240 2991 : }
5241 :
5242 : /************************************************************************/
5243 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5244 : /************************************************************************/
5245 :
5246 372 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5247 : GDALInConstructionAlgorithmArg &fieldArg,
5248 : const GDALAlgorithmArg *layerNameArg, bool attributeFields,
5249 : bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg)
5250 : {
5251 :
5252 : fieldArg.SetAutoCompleteFunction(
5253 10 : [&datasetArg, layerNameArg, attributeFields,
5254 43 : geometryFields](const std::string ¤tValue)
5255 : {
5256 20 : std::set<std::string> ret;
5257 10 : if (!datasetArg.empty())
5258 : {
5259 16 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5260 :
5261 : const auto getLayerFields =
5262 6 : [&ret, ¤tValue, attributeFields,
5263 50 : geometryFields](const OGRLayer *poLayer)
5264 : {
5265 6 : const auto poDefn = poLayer->GetLayerDefn();
5266 6 : if (attributeFields)
5267 : {
5268 24 : for (const auto poFieldDefn : poDefn->GetFields())
5269 : {
5270 18 : const char *fieldName = poFieldDefn->GetNameRef();
5271 18 : if (currentValue == fieldName)
5272 : {
5273 0 : ret.clear();
5274 0 : ret.insert(fieldName);
5275 0 : break;
5276 : }
5277 18 : ret.insert(fieldName);
5278 : }
5279 : }
5280 6 : if (geometryFields)
5281 : {
5282 2 : for (const auto poFieldDefn : poDefn->GetGeomFields())
5283 : {
5284 1 : const char *fieldName = poFieldDefn->GetNameRef();
5285 1 : if (fieldName[0] == 0)
5286 1 : fieldName = "OGR_GEOMETRY";
5287 1 : if (currentValue == fieldName)
5288 : {
5289 0 : ret.clear();
5290 0 : ret.insert(fieldName);
5291 0 : break;
5292 : }
5293 1 : ret.insert(fieldName);
5294 : }
5295 : }
5296 6 : };
5297 :
5298 8 : const GDALArgDatasetValue &dsVal = datasetArg[0];
5299 :
5300 8 : if (!dsVal.GetName().empty())
5301 : {
5302 : auto poDS = std::unique_ptr<GDALDataset>(
5303 8 : GDALDataset::Open(dsVal.GetName().c_str(),
5304 16 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5305 8 : if (poDS)
5306 : {
5307 16 : std::vector<std::string> layerNames;
5308 8 : if (layerNameArg && layerNameArg->IsExplicitlySet())
5309 : {
5310 4 : if (layerNameArg->GetType() == GAAT_STRING_LIST)
5311 : {
5312 : layerNames =
5313 : layerNameArg
5314 2 : ->Get<std::vector<std::string>>();
5315 : }
5316 2 : else if (layerNameArg->GetType() == GAAT_STRING)
5317 : {
5318 2 : layerNames.push_back(
5319 2 : layerNameArg->Get<std::string>());
5320 : }
5321 : }
5322 8 : if (layerNames.empty())
5323 : {
5324 : // Loop through all layers
5325 8 : for (const auto *poLayer : poDS->GetLayers())
5326 : {
5327 4 : getLayerFields(poLayer);
5328 : }
5329 : }
5330 : else
5331 : {
5332 8 : for (const std::string &layerName : layerNames)
5333 : {
5334 : const auto poLayer =
5335 4 : poDS->GetLayerByName(layerName.c_str());
5336 4 : if (poLayer)
5337 : {
5338 2 : getLayerFields(poLayer);
5339 : }
5340 : }
5341 : }
5342 : }
5343 : }
5344 : }
5345 10 : std::vector<std::string> retVector(ret.begin(), ret.end());
5346 20 : return retVector;
5347 372 : });
5348 372 : }
5349 :
5350 : /************************************************************************/
5351 : /* GDALAlgorithm::AddFieldNameArg() */
5352 : /************************************************************************/
5353 :
5354 : GDALInConstructionAlgorithmArg &
5355 137 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5356 : {
5357 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5358 137 : pValue);
5359 : }
5360 :
5361 : /************************************************************************/
5362 : /* GDALAlgorithm::ParseFieldDefinition() */
5363 : /************************************************************************/
5364 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5365 : OGRFieldDefn *poFieldDefn,
5366 : std::string *posError)
5367 : {
5368 : static const std::regex re(
5369 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5370 134 : std::smatch match;
5371 67 : if (std::regex_match(posStrDef, match, re))
5372 : {
5373 132 : const std::string name = match[1];
5374 132 : const std::string type = match[2];
5375 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5376 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5377 66 : poFieldDefn->SetName(name.c_str());
5378 :
5379 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5380 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5381 : {
5382 1 : if (posError)
5383 1 : *posError = "Unsupported field type: " + type;
5384 :
5385 1 : return false;
5386 : }
5387 65 : poFieldDefn->SetType(typeEnum);
5388 65 : poFieldDefn->SetWidth(width);
5389 65 : poFieldDefn->SetPrecision(precision);
5390 65 : return true;
5391 : }
5392 :
5393 1 : if (posError)
5394 : *posError = "Invalid field definition format. Expected "
5395 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5396 :
5397 1 : return false;
5398 : }
5399 :
5400 : /************************************************************************/
5401 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5402 : /************************************************************************/
5403 :
5404 : GDALInConstructionAlgorithmArg &
5405 131 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5406 : std::vector<OGRFieldDefn> *pFieldDefns,
5407 : const char *helpMessage)
5408 : {
5409 : auto &arg =
5410 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5411 262 : pValues)
5412 262 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5413 131 : .SetPackedValuesAllowed(true)
5414 131 : .SetRepeatedArgAllowed(true);
5415 :
5416 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5417 : {
5418 65 : pFieldDefns->clear();
5419 130 : for (const auto &strValue : *pValues)
5420 : {
5421 67 : OGRFieldDefn fieldDefn("", OFTString);
5422 67 : std::string error;
5423 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5424 : &error))
5425 : {
5426 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5427 2 : return false;
5428 : }
5429 : // Check uniqueness of field names
5430 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5431 : {
5432 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5433 : fieldDefn.GetNameRef()))
5434 : {
5435 0 : ReportError(CE_Failure, CPLE_AppDefined,
5436 : "Duplicate field name: '%s'",
5437 : fieldDefn.GetNameRef());
5438 0 : return false;
5439 : }
5440 : }
5441 65 : pFieldDefns->push_back(fieldDefn);
5442 : }
5443 63 : return true;
5444 131 : };
5445 :
5446 131 : arg.AddValidationAction(std::move(validationFunction));
5447 :
5448 131 : return arg;
5449 : }
5450 :
5451 : /************************************************************************/
5452 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5453 : /************************************************************************/
5454 :
5455 274 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5456 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5457 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5458 : {
5459 : auto &arg =
5460 548 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5461 822 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5462 : .SetAutoCompleteFunction(
5463 1 : [](const std::string ¤tValue)
5464 : {
5465 1 : std::vector<std::string> oRet;
5466 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5467 : {
5468 : const char *pszSubType =
5469 5 : OGRFieldDefn::GetFieldSubTypeName(
5470 : static_cast<OGRFieldSubType>(i));
5471 5 : if (pszSubType != nullptr)
5472 : {
5473 5 : if (currentValue.empty() ||
5474 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5475 : {
5476 5 : oRet.push_back(pszSubType);
5477 : }
5478 : }
5479 : }
5480 :
5481 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5482 : {
5483 : // Skip deprecated
5484 14 : if (static_cast<OGRFieldType>(i) ==
5485 13 : OGRFieldType::OFTWideString ||
5486 : static_cast<OGRFieldType>(i) ==
5487 : OGRFieldType::OFTWideStringList)
5488 2 : continue;
5489 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5490 : static_cast<OGRFieldType>(i));
5491 12 : if (pszType != nullptr)
5492 : {
5493 12 : if (currentValue.empty() ||
5494 0 : STARTS_WITH(pszType, currentValue.c_str()))
5495 : {
5496 12 : oRet.push_back(pszType);
5497 : }
5498 : }
5499 : }
5500 1 : return oRet;
5501 274 : });
5502 :
5503 : auto validationFunction =
5504 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5505 : {
5506 120 : bool isValid{true};
5507 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5508 :
5509 : // String is returned for unknown types
5510 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5511 : {
5512 16 : isValid = false;
5513 : }
5514 :
5515 120 : *pSubtypeValue =
5516 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5517 :
5518 120 : if (*pSubtypeValue != OFSTNone)
5519 : {
5520 15 : isValid = true;
5521 15 : switch (*pSubtypeValue)
5522 : {
5523 6 : case OFSTBoolean:
5524 : case OFSTInt16:
5525 : {
5526 6 : *pTypeValue = OFTInteger;
5527 6 : break;
5528 : }
5529 3 : case OFSTFloat32:
5530 : {
5531 3 : *pTypeValue = OFTReal;
5532 3 : break;
5533 : }
5534 6 : default:
5535 : {
5536 6 : *pTypeValue = OFTString;
5537 6 : break;
5538 : }
5539 : }
5540 : }
5541 :
5542 120 : if (!isValid)
5543 : {
5544 2 : ReportError(CE_Failure, CPLE_AppDefined,
5545 : "Invalid value for argument '%s': '%s'",
5546 1 : arg.GetName().c_str(), pStrValue->c_str());
5547 : }
5548 :
5549 120 : return isValid;
5550 274 : };
5551 :
5552 274 : if (!pStrValue->empty())
5553 : {
5554 0 : arg.SetDefault(*pStrValue);
5555 0 : validationFunction();
5556 : }
5557 :
5558 274 : arg.AddValidationAction(std::move(validationFunction));
5559 :
5560 274 : return arg;
5561 : }
5562 :
5563 : /************************************************************************/
5564 : /* GDALAlgorithm::ValidateBandArg() */
5565 : /************************************************************************/
5566 :
5567 4281 : bool GDALAlgorithm::ValidateBandArg() const
5568 : {
5569 4281 : bool ret = true;
5570 4281 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5571 4281 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5572 1669 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5573 292 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5574 5944 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5575 149 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5576 : {
5577 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5578 : {
5579 99 : if (nBand > poDS->GetRasterCount())
5580 : {
5581 5 : ReportError(CE_Failure, CPLE_AppDefined,
5582 : "Value of 'band' should be greater or equal than "
5583 : "1 and less or equal than %d.",
5584 : poDS->GetRasterCount());
5585 5 : return false;
5586 : }
5587 94 : return true;
5588 92 : };
5589 :
5590 : const auto ValidateForOneDataset =
5591 304 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5592 : {
5593 87 : bool l_ret = true;
5594 87 : if (bandArg->GetType() == GAAT_INTEGER)
5595 : {
5596 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5597 : }
5598 63 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5599 : {
5600 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5601 : {
5602 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5603 : }
5604 : }
5605 87 : return l_ret;
5606 92 : };
5607 :
5608 92 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5609 : {
5610 : auto poDS =
5611 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5612 6 : if (poDS && !ValidateForOneDataset(poDS))
5613 2 : ret = false;
5614 : }
5615 : else
5616 : {
5617 86 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5618 85 : for (auto &datasetValue :
5619 256 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5620 : {
5621 85 : auto poDS = datasetValue.GetDatasetRef();
5622 85 : if (poDS && !ValidateForOneDataset(poDS))
5623 3 : ret = false;
5624 : }
5625 : }
5626 : }
5627 4281 : return ret;
5628 : }
5629 :
5630 : /************************************************************************/
5631 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5632 : /************************************************************************/
5633 :
5634 3346 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5635 : {
5636 3346 : return ValidateBandArg();
5637 : }
5638 :
5639 : /************************************************************************/
5640 : /* GDALAlgorithm::AddBandArg() */
5641 : /************************************************************************/
5642 :
5643 : GDALInConstructionAlgorithmArg &
5644 1685 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5645 : {
5646 2134 : AddValidationAction([this]() { return ValidateBandArg(); });
5647 :
5648 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5649 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5650 3370 : pValue)
5651 : .AddValidationAction(
5652 34 : [pValue]()
5653 : {
5654 34 : if (*pValue <= 0)
5655 : {
5656 1 : CPLError(CE_Failure, CPLE_AppDefined,
5657 : "Value of 'band' should greater or equal to 1.");
5658 1 : return false;
5659 : }
5660 33 : return true;
5661 3370 : });
5662 : }
5663 :
5664 : /************************************************************************/
5665 : /* GDALAlgorithm::AddBandArg() */
5666 : /************************************************************************/
5667 :
5668 : GDALInConstructionAlgorithmArg &
5669 868 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5670 : {
5671 1354 : AddValidationAction([this]() { return ValidateBandArg(); });
5672 :
5673 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5674 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5675 1736 : pValue)
5676 : .AddValidationAction(
5677 126 : [pValue]()
5678 : {
5679 397 : for (int val : *pValue)
5680 : {
5681 272 : if (val <= 0)
5682 : {
5683 1 : CPLError(CE_Failure, CPLE_AppDefined,
5684 : "Value of 'band' should greater or equal "
5685 : "to 1.");
5686 1 : return false;
5687 : }
5688 : }
5689 125 : return true;
5690 1736 : });
5691 : }
5692 :
5693 : /************************************************************************/
5694 : /* ParseAndValidateKeyValue() */
5695 : /************************************************************************/
5696 :
5697 557 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5698 : {
5699 521 : const auto Validate = [this, &arg](const std::string &val)
5700 : {
5701 516 : if (val.find('=') == std::string::npos)
5702 : {
5703 5 : ReportError(
5704 : CE_Failure, CPLE_AppDefined,
5705 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5706 5 : arg.GetName().c_str());
5707 5 : return false;
5708 : }
5709 :
5710 511 : return true;
5711 557 : };
5712 :
5713 557 : if (arg.GetType() == GAAT_STRING)
5714 : {
5715 0 : return Validate(arg.Get<std::string>());
5716 : }
5717 557 : else if (arg.GetType() == GAAT_STRING_LIST)
5718 : {
5719 557 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5720 557 : if (vals.size() == 1)
5721 : {
5722 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5723 900 : std::vector<std::string> newVals;
5724 900 : std::string curToken;
5725 450 : bool canSplitOnComma = true;
5726 450 : char lastSep = 0;
5727 450 : bool inString = false;
5728 450 : bool equalFoundInLastToken = false;
5729 6948 : for (char c : vals[0])
5730 : {
5731 6502 : if (!inString && c == ',')
5732 : {
5733 10 : if (lastSep != '=' || !equalFoundInLastToken)
5734 : {
5735 2 : canSplitOnComma = false;
5736 2 : break;
5737 : }
5738 8 : lastSep = c;
5739 8 : newVals.push_back(curToken);
5740 8 : curToken.clear();
5741 8 : equalFoundInLastToken = false;
5742 : }
5743 6492 : else if (!inString && c == '=')
5744 : {
5745 449 : if (lastSep == '=')
5746 : {
5747 2 : canSplitOnComma = false;
5748 2 : break;
5749 : }
5750 447 : equalFoundInLastToken = true;
5751 447 : lastSep = c;
5752 447 : curToken += c;
5753 : }
5754 6043 : else if (c == '"')
5755 : {
5756 4 : inString = !inString;
5757 4 : curToken += c;
5758 : }
5759 : else
5760 : {
5761 6039 : curToken += c;
5762 : }
5763 : }
5764 450 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5765 : {
5766 437 : if (!curToken.empty())
5767 437 : newVals.emplace_back(std::move(curToken));
5768 437 : vals = std::move(newVals);
5769 : }
5770 : }
5771 :
5772 1068 : for (const auto &val : vals)
5773 : {
5774 516 : if (!Validate(val))
5775 5 : return false;
5776 : }
5777 : }
5778 :
5779 552 : return true;
5780 : }
5781 :
5782 : /************************************************************************/
5783 : /* IsGDALGOutput() */
5784 : /************************************************************************/
5785 :
5786 2228 : bool GDALAlgorithm::IsGDALGOutput() const
5787 : {
5788 2228 : bool isGDALGOutput = false;
5789 2228 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5790 2228 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5791 3841 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5792 1613 : outputArg->IsExplicitlySet())
5793 : {
5794 3167 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5795 1571 : outputFormatArg->IsExplicitlySet())
5796 : {
5797 : const auto &val =
5798 975 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5799 975 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5800 : }
5801 : else
5802 : {
5803 : const auto &filename =
5804 621 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5805 621 : isGDALGOutput =
5806 1215 : filename.GetName().size() > strlen(".gdalg.json") &&
5807 594 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5808 : strlen(".gdalg.json"),
5809 : ".gdalg.json");
5810 : }
5811 : }
5812 2228 : return isGDALGOutput;
5813 : }
5814 :
5815 : /************************************************************************/
5816 : /* ProcessGDALGOutput() */
5817 : /************************************************************************/
5818 :
5819 2497 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5820 : {
5821 2497 : if (!SupportsStreamedOutput())
5822 813 : return ProcessGDALGOutputRet::NOT_GDALG;
5823 :
5824 1684 : if (IsGDALGOutput())
5825 : {
5826 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5827 : const auto &filename =
5828 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5829 : VSIStatBufL sStat;
5830 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5831 : {
5832 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5833 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5834 : {
5835 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5836 : {
5837 0 : CPLError(CE_Failure, CPLE_AppDefined,
5838 : "File '%s' already exists. Specify the "
5839 : "--overwrite option to overwrite it.",
5840 : filename.c_str());
5841 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5842 : }
5843 : }
5844 : }
5845 :
5846 22 : std::string osCommandLine;
5847 :
5848 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5849 : {
5850 33 : if (!osCommandLine.empty())
5851 22 : osCommandLine += ' ';
5852 33 : osCommandLine += path;
5853 : }
5854 :
5855 250 : for (const auto &arg : GetArgs())
5856 : {
5857 265 : if (arg->IsExplicitlySet() &&
5858 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5859 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5860 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5861 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5862 : {
5863 14 : osCommandLine += ' ';
5864 14 : std::string strArg;
5865 14 : if (!arg->Serialize(strArg))
5866 : {
5867 0 : CPLError(CE_Failure, CPLE_AppDefined,
5868 : "Cannot serialize argument %s",
5869 0 : arg->GetName().c_str());
5870 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5871 : }
5872 14 : osCommandLine += strArg;
5873 : }
5874 : }
5875 :
5876 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5877 :
5878 11 : std::string outStringUnused;
5879 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5880 11 : ? ProcessGDALGOutputRet::GDALG_OK
5881 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5882 : }
5883 :
5884 1673 : return ProcessGDALGOutputRet::NOT_GDALG;
5885 : }
5886 :
5887 : /************************************************************************/
5888 : /* GDALAlgorithm::SaveGDALG() */
5889 : /************************************************************************/
5890 :
5891 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5892 : std::string &outString,
5893 : const std::string &commandLine)
5894 : {
5895 44 : CPLJSONDocument oDoc;
5896 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5897 22 : oDoc.GetRoot().Add("command_line", commandLine);
5898 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5899 :
5900 22 : if (!filename.empty())
5901 21 : return oDoc.Save(filename);
5902 :
5903 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5904 1 : return true;
5905 : }
5906 :
5907 : /************************************************************************/
5908 : /* GDALAlgorithm::AddCreationOptionsArg() */
5909 : /************************************************************************/
5910 :
5911 : GDALInConstructionAlgorithmArg &
5912 8275 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5913 : const char *helpMessage)
5914 : {
5915 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5916 16550 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5917 16550 : .AddAlias("co")
5918 16550 : .SetMetaVar("<KEY>=<VALUE>")
5919 8275 : .SetPackedValuesAllowed(false);
5920 293 : arg.AddValidationAction([this, &arg]()
5921 8568 : { return ParseAndValidateKeyValue(arg); });
5922 :
5923 : arg.SetAutoCompleteFunction(
5924 51 : [this](const std::string ¤tValue)
5925 : {
5926 17 : std::vector<std::string> oRet;
5927 :
5928 17 : int datasetType =
5929 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5930 17 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5931 17 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5932 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5933 : {
5934 17 : datasetType = outputArg->GetDatasetType();
5935 : }
5936 :
5937 17 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5938 34 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5939 17 : outputFormat->IsExplicitlySet())
5940 : {
5941 14 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5942 7 : outputFormat->Get<std::string>().c_str());
5943 7 : if (poDriver)
5944 : {
5945 7 : AddOptionsSuggestions(
5946 7 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5947 : datasetType, currentValue, oRet);
5948 : }
5949 7 : return oRet;
5950 : }
5951 :
5952 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5953 : {
5954 10 : auto poDM = GetGDALDriverManager();
5955 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5956 10 : const auto &osDSName = datasetValue.GetName();
5957 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5958 10 : if (!osExt.empty())
5959 : {
5960 10 : std::set<std::string> oVisitedExtensions;
5961 715 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5962 : {
5963 712 : auto poDriver = poDM->GetDriver(i);
5964 2136 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5965 712 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5966 216 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5967 1424 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5968 216 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5969 0 : poDriver->GetMetadataItem(
5970 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5971 : {
5972 : const char *pszExtensions =
5973 496 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5974 496 : if (pszExtensions)
5975 : {
5976 : const CPLStringList aosExts(
5977 323 : CSLTokenizeString2(pszExtensions, " ", 0));
5978 716 : for (const char *pszExt : cpl::Iterate(aosExts))
5979 : {
5980 419 : if (EQUAL(pszExt, osExt.c_str()) &&
5981 16 : !cpl::contains(oVisitedExtensions,
5982 : pszExt))
5983 : {
5984 10 : oVisitedExtensions.insert(pszExt);
5985 10 : if (AddOptionsSuggestions(
5986 : poDriver->GetMetadataItem(
5987 10 : GDAL_DMD_CREATIONOPTIONLIST),
5988 : datasetType, currentValue,
5989 : oRet))
5990 : {
5991 7 : return oRet;
5992 : }
5993 3 : break;
5994 : }
5995 : }
5996 : }
5997 : }
5998 : }
5999 : }
6000 : }
6001 :
6002 3 : return oRet;
6003 8275 : });
6004 :
6005 8275 : return arg;
6006 : }
6007 :
6008 : /************************************************************************/
6009 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
6010 : /************************************************************************/
6011 :
6012 : GDALInConstructionAlgorithmArg &
6013 3960 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
6014 : const char *helpMessage)
6015 : {
6016 : auto &arg =
6017 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
6018 7920 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
6019 7920 : .AddAlias("lco")
6020 7920 : .SetMetaVar("<KEY>=<VALUE>")
6021 3960 : .SetPackedValuesAllowed(false);
6022 76 : arg.AddValidationAction([this, &arg]()
6023 4036 : { return ParseAndValidateKeyValue(arg); });
6024 :
6025 : arg.SetAutoCompleteFunction(
6026 5 : [this](const std::string ¤tValue)
6027 : {
6028 2 : std::vector<std::string> oRet;
6029 :
6030 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6031 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
6032 2 : outputFormat->IsExplicitlySet())
6033 : {
6034 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
6035 1 : outputFormat->Get<std::string>().c_str());
6036 1 : if (poDriver)
6037 : {
6038 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
6039 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6040 : GDAL_OF_VECTOR, currentValue, oRet);
6041 : }
6042 1 : return oRet;
6043 : }
6044 :
6045 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
6046 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
6047 : {
6048 1 : auto poDM = GetGDALDriverManager();
6049 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6050 1 : const auto &osDSName = datasetValue.GetName();
6051 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6052 1 : if (!osExt.empty())
6053 : {
6054 1 : std::set<std::string> oVisitedExtensions;
6055 229 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
6056 : {
6057 228 : auto poDriver = poDM->GetDriver(i);
6058 228 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
6059 : {
6060 : const char *pszExtensions =
6061 91 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6062 91 : if (pszExtensions)
6063 : {
6064 : const CPLStringList aosExts(
6065 62 : CSLTokenizeString2(pszExtensions, " ", 0));
6066 156 : for (const char *pszExt : cpl::Iterate(aosExts))
6067 : {
6068 96 : if (EQUAL(pszExt, osExt.c_str()) &&
6069 1 : !cpl::contains(oVisitedExtensions,
6070 : pszExt))
6071 : {
6072 1 : oVisitedExtensions.insert(pszExt);
6073 1 : if (AddOptionsSuggestions(
6074 : poDriver->GetMetadataItem(
6075 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6076 : GDAL_OF_VECTOR, currentValue,
6077 : oRet))
6078 : {
6079 0 : return oRet;
6080 : }
6081 1 : break;
6082 : }
6083 : }
6084 : }
6085 : }
6086 : }
6087 : }
6088 : }
6089 :
6090 1 : return oRet;
6091 3960 : });
6092 :
6093 3960 : return arg;
6094 : }
6095 :
6096 : /************************************************************************/
6097 : /* GDALAlgorithm::AddBBOXArg() */
6098 : /************************************************************************/
6099 :
6100 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
6101 : GDALInConstructionAlgorithmArg &
6102 1894 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
6103 : {
6104 : auto &arg = AddArg("bbox", 0,
6105 : MsgOrDefault(helpMessage,
6106 : _("Bounding box as xmin,ymin,xmax,ymax")),
6107 3788 : pValue)
6108 1894 : .SetRepeatedArgAllowed(false)
6109 1894 : .SetMinCount(4)
6110 1894 : .SetMaxCount(4)
6111 1894 : .SetDisplayHintAboutRepetition(false);
6112 : arg.AddValidationAction(
6113 166 : [&arg]()
6114 : {
6115 166 : const auto &val = arg.Get<std::vector<double>>();
6116 166 : CPLAssert(val.size() == 4);
6117 166 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
6118 : {
6119 5 : CPLError(CE_Failure, CPLE_AppDefined,
6120 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
6121 : "xmin <= xmax and ymin <= ymax");
6122 5 : return false;
6123 : }
6124 161 : return true;
6125 1894 : });
6126 1894 : return arg;
6127 : }
6128 :
6129 : /************************************************************************/
6130 : /* GDALAlgorithm::AddActiveLayerArg() */
6131 : /************************************************************************/
6132 :
6133 : GDALInConstructionAlgorithmArg &
6134 1783 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6135 : {
6136 : return AddArg("active-layer", 0,
6137 : MsgOrDefault(helpMessage,
6138 : _("Set active layer (if not specified, all)")),
6139 1783 : pValue);
6140 : }
6141 :
6142 : /************************************************************************/
6143 : /* GDALAlgorithm::AddNumThreadsArg() */
6144 : /************************************************************************/
6145 :
6146 : GDALInConstructionAlgorithmArg &
6147 723 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6148 : const char *helpMessage)
6149 : {
6150 : auto &arg =
6151 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6152 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6153 723 : pStrValue);
6154 :
6155 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6156 1446 : _("Number of jobs (read-only, hidden argument)"), pValue)
6157 723 : .SetHidden();
6158 :
6159 2724 : auto lambda = [this, &arg, pValue, pStrValue]
6160 : {
6161 908 : bool bOK = false;
6162 908 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6163 : const int nLimit = std::clamp(
6164 908 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6165 1816 : CPLGetNumCPUs());
6166 : const int nNumThreads =
6167 908 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
6168 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6169 908 : if (bOK)
6170 : {
6171 908 : *pValue = nNumThreads;
6172 : }
6173 : else
6174 : {
6175 0 : ReportError(CE_Failure, CPLE_IllegalArg,
6176 : "Invalid value for '%s' argument",
6177 0 : arg.GetName().c_str());
6178 : }
6179 908 : return bOK;
6180 723 : };
6181 723 : if (!pStrValue->empty())
6182 : {
6183 677 : arg.SetDefault(*pStrValue);
6184 677 : lambda();
6185 : }
6186 723 : arg.AddValidationAction(std::move(lambda));
6187 723 : return arg;
6188 : }
6189 :
6190 : /************************************************************************/
6191 : /* GDALAlgorithm::AddAbsolutePathArg() */
6192 : /************************************************************************/
6193 :
6194 : GDALInConstructionAlgorithmArg &
6195 622 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6196 : {
6197 : return AddArg(
6198 : "absolute-path", 0,
6199 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6200 : "should be stored as an absolute path")),
6201 622 : pValue);
6202 : }
6203 :
6204 : /************************************************************************/
6205 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
6206 : /************************************************************************/
6207 :
6208 : GDALInConstructionAlgorithmArg &
6209 138 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6210 : const char *helpMessage)
6211 : {
6212 :
6213 : const auto pixelFunctionNames =
6214 138 : VRTDerivedRasterBand::GetPixelFunctionNames();
6215 : return AddArg(
6216 : "pixel-function", 0,
6217 : MsgOrDefault(
6218 : helpMessage,
6219 : _("Specify a pixel function to calculate output value from "
6220 : "overlapping inputs")),
6221 276 : pValue)
6222 276 : .SetChoices(pixelFunctionNames);
6223 : }
6224 :
6225 : /************************************************************************/
6226 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
6227 : /************************************************************************/
6228 :
6229 : GDALInConstructionAlgorithmArg &
6230 138 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6231 : const char *helpMessage)
6232 : {
6233 : auto &pixelFunctionArgArg =
6234 : AddArg("pixel-function-arg", 0,
6235 : MsgOrDefault(
6236 : helpMessage,
6237 : _("Specify argument(s) to pass to the pixel function")),
6238 276 : pValue)
6239 276 : .SetMetaVar("<NAME>=<VALUE>")
6240 138 : .SetRepeatedArgAllowed(true);
6241 : pixelFunctionArgArg.AddValidationAction(
6242 7 : [this, &pixelFunctionArgArg]()
6243 145 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6244 :
6245 : pixelFunctionArgArg.SetAutoCompleteFunction(
6246 12 : [this](const std::string ¤tValue)
6247 : {
6248 12 : std::string pixelFunction;
6249 6 : const auto pixelFunctionArg = GetArg("pixel-function");
6250 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6251 : {
6252 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
6253 : }
6254 :
6255 6 : std::vector<std::string> ret;
6256 :
6257 6 : if (!pixelFunction.empty())
6258 : {
6259 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6260 : pixelFunction.c_str());
6261 5 : if (!pair)
6262 : {
6263 1 : ret.push_back("**");
6264 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6265 1 : ret.push_back(std::string("\xC2\xA0"
6266 : "Invalid pixel function name"));
6267 : }
6268 4 : else if (pair->second.find("Argument name=") ==
6269 : std::string::npos)
6270 : {
6271 1 : ret.push_back("**");
6272 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6273 1 : ret.push_back(
6274 2 : std::string(
6275 : "\xC2\xA0"
6276 : "No pixel function arguments for pixel function '")
6277 1 : .append(pixelFunction)
6278 1 : .append("'"));
6279 : }
6280 : else
6281 : {
6282 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6283 : ret);
6284 : }
6285 : }
6286 :
6287 12 : return ret;
6288 138 : });
6289 :
6290 138 : return pixelFunctionArgArg;
6291 : }
6292 :
6293 : /************************************************************************/
6294 : /* GDALAlgorithm::AddProgressArg() */
6295 : /************************************************************************/
6296 :
6297 8059 : void GDALAlgorithm::AddProgressArg()
6298 : {
6299 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6300 16118 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6301 8059 : .SetAvailableInPipelineStep(false)
6302 16118 : .SetCategory(GAAC_COMMON)
6303 8059 : .AddAction([this]() { m_progressBarRequested = false; });
6304 :
6305 16118 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6306 8059 : .SetAvailableInPipelineStep(false)
6307 8059 : .SetHidden();
6308 8059 : }
6309 :
6310 : /************************************************************************/
6311 : /* GDALAlgorithm::Run() */
6312 : /************************************************************************/
6313 :
6314 4797 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6315 : {
6316 4797 : WarnIfDeprecated();
6317 :
6318 4797 : if (m_selectedSubAlg)
6319 : {
6320 443 : if (m_calledFromCommandLine)
6321 265 : m_selectedSubAlg->m_calledFromCommandLine = true;
6322 443 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6323 : }
6324 :
6325 4354 : if (m_helpRequested || m_helpDocRequested)
6326 : {
6327 16 : if (m_calledFromCommandLine)
6328 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6329 16 : return true;
6330 : }
6331 :
6332 4338 : if (m_JSONUsageRequested)
6333 : {
6334 3 : if (m_calledFromCommandLine)
6335 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6336 3 : return true;
6337 : }
6338 :
6339 4335 : if (!ValidateArguments())
6340 125 : return false;
6341 :
6342 4210 : if (m_alreadyRun)
6343 : {
6344 3 : ReportError(CE_Failure, CPLE_AppDefined,
6345 : "Run() can be called only once per algorithm instance");
6346 3 : return false;
6347 : }
6348 4207 : m_alreadyRun = true;
6349 :
6350 4207 : switch (ProcessGDALGOutput())
6351 : {
6352 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6353 0 : return false;
6354 :
6355 11 : case ProcessGDALGOutputRet::GDALG_OK:
6356 11 : return true;
6357 :
6358 4196 : case ProcessGDALGOutputRet::NOT_GDALG:
6359 4196 : break;
6360 : }
6361 :
6362 4196 : if (m_executionForStreamOutput)
6363 : {
6364 93 : if (!CheckSafeForStreamOutput())
6365 : {
6366 4 : return false;
6367 : }
6368 : }
6369 :
6370 4192 : return RunImpl(pfnProgress, pProgressData);
6371 : }
6372 :
6373 : /************************************************************************/
6374 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6375 : /************************************************************************/
6376 :
6377 48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6378 : {
6379 48 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6380 48 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6381 : {
6382 48 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6383 48 : if (!EQUAL(val.c_str(), "stream"))
6384 : {
6385 : // For security reasons, to avoid that reading a .gdalg.json file
6386 : // writes a file on the file system.
6387 4 : ReportError(
6388 : CE_Failure, CPLE_NotSupported,
6389 : "in streamed execution, --format stream should be used");
6390 4 : return false;
6391 : }
6392 : }
6393 44 : return true;
6394 : }
6395 :
6396 : /************************************************************************/
6397 : /* GDALAlgorithm::Finalize() */
6398 : /************************************************************************/
6399 :
6400 1829 : bool GDALAlgorithm::Finalize()
6401 : {
6402 1829 : bool ret = true;
6403 1829 : if (m_selectedSubAlg)
6404 271 : ret = m_selectedSubAlg->Finalize();
6405 :
6406 33083 : for (auto &arg : m_args)
6407 : {
6408 31254 : if (arg->GetType() == GAAT_DATASET)
6409 : {
6410 1444 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6411 : }
6412 29810 : else if (arg->GetType() == GAAT_DATASET_LIST)
6413 : {
6414 2794 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6415 : {
6416 1314 : ret = ds.Close() && ret;
6417 : }
6418 : }
6419 : }
6420 1829 : return ret;
6421 : }
6422 :
6423 : /************************************************************************/
6424 : /* GDALAlgorithm::GetArgNamesForCLI() */
6425 : /************************************************************************/
6426 :
6427 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6428 693 : GDALAlgorithm::GetArgNamesForCLI() const
6429 : {
6430 1386 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6431 :
6432 693 : size_t maxOptLen = 0;
6433 8658 : for (const auto &arg : m_args)
6434 : {
6435 7965 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6436 1671 : continue;
6437 6294 : std::string opt;
6438 6294 : bool addComma = false;
6439 6294 : if (!arg->GetShortName().empty())
6440 : {
6441 1399 : opt += '-';
6442 1399 : opt += arg->GetShortName();
6443 1399 : addComma = true;
6444 : }
6445 6294 : for (char alias : arg->GetShortNameAliases())
6446 : {
6447 0 : if (addComma)
6448 0 : opt += ", ";
6449 0 : opt += "-";
6450 0 : opt += alias;
6451 0 : addComma = true;
6452 : }
6453 7046 : for (const std::string &alias : arg->GetAliases())
6454 : {
6455 752 : if (addComma)
6456 324 : opt += ", ";
6457 752 : opt += "--";
6458 752 : opt += alias;
6459 752 : addComma = true;
6460 : }
6461 6294 : if (!arg->GetName().empty())
6462 : {
6463 6294 : if (addComma)
6464 1827 : opt += ", ";
6465 6294 : opt += "--";
6466 6294 : opt += arg->GetName();
6467 : }
6468 6294 : const auto &metaVar = arg->GetMetaVar();
6469 6294 : if (!metaVar.empty())
6470 : {
6471 3964 : opt += ' ';
6472 3964 : if (metaVar.front() != '<')
6473 2845 : opt += '<';
6474 3964 : opt += metaVar;
6475 3964 : if (metaVar.back() != '>')
6476 2839 : opt += '>';
6477 : }
6478 6294 : maxOptLen = std::max(maxOptLen, opt.size());
6479 6294 : options.emplace_back(arg.get(), opt);
6480 : }
6481 :
6482 1386 : return std::make_pair(std::move(options), maxOptLen);
6483 : }
6484 :
6485 : /************************************************************************/
6486 : /* GDALAlgorithm::GetUsageForCLI() */
6487 : /************************************************************************/
6488 :
6489 : std::string
6490 414 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6491 : const UsageOptions &usageOptions) const
6492 : {
6493 414 : if (m_selectedSubAlg)
6494 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6495 :
6496 814 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6497 814 : std::string osPath;
6498 819 : for (const std::string &s : m_callPath)
6499 : {
6500 412 : if (!osPath.empty())
6501 51 : osPath += ' ';
6502 412 : osPath += s;
6503 : }
6504 407 : osRet += ' ';
6505 407 : osRet += osPath;
6506 :
6507 407 : bool hasNonPositionals = false;
6508 5046 : for (const auto &arg : m_args)
6509 : {
6510 4639 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6511 3347 : hasNonPositionals = true;
6512 : }
6513 :
6514 407 : if (HasSubAlgorithms())
6515 : {
6516 9 : if (m_callPath.size() == 1)
6517 : {
6518 8 : osRet += " <COMMAND>";
6519 8 : if (hasNonPositionals)
6520 8 : osRet += " [OPTIONS]";
6521 8 : if (usageOptions.isPipelineStep)
6522 : {
6523 5 : const size_t nLenFirstLine = osRet.size();
6524 5 : osRet += '\n';
6525 5 : osRet.append(nLenFirstLine, '-');
6526 5 : osRet += '\n';
6527 : }
6528 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6529 : }
6530 : else
6531 : {
6532 1 : osRet += " <SUBCOMMAND>";
6533 1 : if (hasNonPositionals)
6534 1 : osRet += " [OPTIONS]";
6535 1 : if (usageOptions.isPipelineStep)
6536 : {
6537 0 : const size_t nLenFirstLine = osRet.size();
6538 0 : osRet += '\n';
6539 0 : osRet.append(nLenFirstLine, '-');
6540 0 : osRet += '\n';
6541 : }
6542 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6543 : }
6544 9 : size_t maxNameLen = 0;
6545 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6546 : {
6547 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6548 : }
6549 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6550 : {
6551 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6552 43 : if (subAlg && !subAlg->IsHidden())
6553 : {
6554 43 : const std::string &name(subAlg->GetName());
6555 43 : osRet += " - ";
6556 43 : osRet += name;
6557 43 : osRet += ": ";
6558 43 : osRet.append(maxNameLen - name.size(), ' ');
6559 43 : osRet += subAlg->GetDescription();
6560 43 : if (!subAlg->m_aliases.empty())
6561 : {
6562 6 : bool first = true;
6563 6 : for (const auto &alias : subAlg->GetAliases())
6564 : {
6565 6 : if (alias ==
6566 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6567 6 : break;
6568 0 : if (first)
6569 0 : osRet += " (alias: ";
6570 : else
6571 0 : osRet += ", ";
6572 0 : osRet += alias;
6573 0 : first = false;
6574 : }
6575 6 : if (!first)
6576 : {
6577 0 : osRet += ')';
6578 : }
6579 : }
6580 43 : osRet += '\n';
6581 : }
6582 : }
6583 :
6584 9 : if (shortUsage && hasNonPositionals)
6585 : {
6586 2 : osRet += "\nTry '";
6587 2 : osRet += osPath;
6588 2 : osRet += " --help' for help.\n";
6589 : }
6590 : }
6591 : else
6592 : {
6593 398 : if (!m_args.empty())
6594 : {
6595 398 : if (hasNonPositionals)
6596 398 : osRet += " [OPTIONS]";
6597 588 : for (const auto *arg : m_positionalArgs)
6598 : {
6599 265 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6600 75 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6601 : {
6602 : const bool optional =
6603 202 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6604 29 : arg->GetName() == "pipeline"));
6605 173 : osRet += ' ';
6606 173 : if (optional)
6607 25 : osRet += '[';
6608 173 : const std::string &metavar = arg->GetMetaVar();
6609 173 : if (!metavar.empty() && metavar[0] == '<')
6610 : {
6611 4 : osRet += metavar;
6612 : }
6613 : else
6614 : {
6615 169 : osRet += '<';
6616 169 : osRet += metavar;
6617 169 : osRet += '>';
6618 : }
6619 215 : if (arg->GetType() == GAAT_DATASET_LIST &&
6620 42 : arg->GetMaxCount() > 1)
6621 : {
6622 28 : osRet += "...";
6623 : }
6624 173 : if (optional)
6625 25 : osRet += ']';
6626 : }
6627 : }
6628 : }
6629 :
6630 398 : const size_t nLenFirstLine = osRet.size();
6631 398 : osRet += '\n';
6632 398 : if (usageOptions.isPipelineStep)
6633 : {
6634 309 : osRet.append(nLenFirstLine, '-');
6635 309 : osRet += '\n';
6636 : }
6637 :
6638 398 : if (shortUsage)
6639 : {
6640 23 : osRet += "Try '";
6641 23 : osRet += osPath;
6642 23 : osRet += " --help' for help.\n";
6643 23 : return osRet;
6644 : }
6645 :
6646 375 : osRet += '\n';
6647 375 : osRet += m_description;
6648 375 : osRet += '\n';
6649 : }
6650 :
6651 384 : if (!m_args.empty() && !shortUsage)
6652 : {
6653 764 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6654 : size_t maxOptLen;
6655 382 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6656 382 : if (usageOptions.maxOptLen)
6657 311 : maxOptLen = usageOptions.maxOptLen;
6658 :
6659 764 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6660 382 : if (m_arbitraryLongNameArgsAllowed)
6661 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6662 :
6663 : const auto OutputArg =
6664 2394 : [this, maxOptLen, &osRet,
6665 23823 : &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6666 : {
6667 2394 : osRet += " ";
6668 2394 : osRet += opt;
6669 2394 : osRet += " ";
6670 2394 : osRet.append(maxOptLen - opt.size(), ' ');
6671 2394 : osRet += arg->GetDescription();
6672 :
6673 2394 : const auto &choices = arg->GetChoices();
6674 2394 : if (!choices.empty())
6675 : {
6676 224 : osRet += ". ";
6677 224 : osRet += arg->GetMetaVar();
6678 224 : osRet += '=';
6679 224 : bool firstChoice = true;
6680 1729 : for (const auto &choice : choices)
6681 : {
6682 1505 : if (!firstChoice)
6683 1281 : osRet += '|';
6684 1505 : osRet += choice;
6685 1505 : firstChoice = false;
6686 : }
6687 : }
6688 :
6689 4722 : if (arg->GetType() == GAAT_DATASET ||
6690 2328 : arg->GetType() == GAAT_DATASET_LIST)
6691 : {
6692 144 : if (arg->IsOutput() &&
6693 144 : arg->GetDatasetInputFlags() == GADV_NAME &&
6694 9 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6695 : {
6696 9 : osRet += " (created by algorithm)";
6697 : }
6698 : }
6699 :
6700 2394 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6701 : {
6702 190 : osRet += " (default: ";
6703 190 : osRet += arg->GetDefault<std::string>();
6704 190 : osRet += ')';
6705 : }
6706 2204 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6707 : {
6708 70 : if (arg->GetDefault<bool>())
6709 0 : osRet += " (default: true)";
6710 : }
6711 2134 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6712 : {
6713 76 : osRet += " (default: ";
6714 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6715 76 : osRet += ')';
6716 : }
6717 2058 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6718 : {
6719 49 : osRet += " (default: ";
6720 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6721 49 : osRet += ')';
6722 : }
6723 2421 : else if (arg->GetType() == GAAT_STRING_LIST &&
6724 412 : arg->HasDefaultValue())
6725 : {
6726 : const auto &defaultVal =
6727 9 : arg->GetDefault<std::vector<std::string>>();
6728 9 : if (defaultVal.size() == 1)
6729 : {
6730 9 : osRet += " (default: ";
6731 9 : osRet += defaultVal[0];
6732 9 : osRet += ')';
6733 : }
6734 : }
6735 2027 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6736 27 : arg->HasDefaultValue())
6737 : {
6738 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6739 0 : if (defaultVal.size() == 1)
6740 : {
6741 0 : osRet += " (default: ";
6742 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6743 0 : osRet += ')';
6744 : }
6745 : }
6746 2000 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6747 : {
6748 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6749 0 : if (defaultVal.size() == 1)
6750 : {
6751 0 : osRet += " (default: ";
6752 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6753 0 : osRet += ')';
6754 : }
6755 : }
6756 :
6757 2394 : if (arg->GetDisplayHintAboutRepetition())
6758 : {
6759 2427 : if (arg->GetMinCount() > 0 &&
6760 92 : arg->GetMinCount() == arg->GetMaxCount())
6761 : {
6762 18 : if (arg->GetMinCount() != 1)
6763 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6764 : }
6765 2391 : else if (arg->GetMinCount() > 0 &&
6766 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6767 : {
6768 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6769 8 : arg->GetMaxCount());
6770 : }
6771 2309 : else if (arg->GetMinCount() > 0)
6772 : {
6773 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6774 : }
6775 2243 : else if (arg->GetMaxCount() > 1)
6776 : {
6777 401 : osRet += " [may be repeated]";
6778 : }
6779 : }
6780 :
6781 2394 : if (arg->IsRequired())
6782 : {
6783 169 : osRet += " [required]";
6784 : }
6785 :
6786 2642 : if (!arg->IsAvailableInPipelineStep() &&
6787 248 : !usageOptions.isPipelineStep)
6788 : {
6789 28 : osRet += " [not available in pipelines]";
6790 : }
6791 :
6792 2394 : osRet += '\n';
6793 :
6794 2394 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6795 2394 : if (!mutualExclusionGroup.empty())
6796 : {
6797 434 : std::string otherArgs;
6798 4123 : for (const auto &otherArg : m_args)
6799 : {
6800 7157 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6801 3251 : otherArg.get() == arg)
6802 872 : continue;
6803 3034 : if (otherArg->GetMutualExclusionGroup() ==
6804 : mutualExclusionGroup)
6805 : {
6806 290 : if (!otherArgs.empty())
6807 77 : otherArgs += ", ";
6808 290 : otherArgs += "--";
6809 290 : otherArgs += otherArg->GetName();
6810 : }
6811 : }
6812 217 : if (!otherArgs.empty())
6813 : {
6814 213 : osRet += " ";
6815 213 : osRet += " ";
6816 213 : osRet.append(maxOptLen, ' ');
6817 213 : osRet += "Mutually exclusive with ";
6818 213 : osRet += otherArgs;
6819 213 : osRet += '\n';
6820 : }
6821 : }
6822 :
6823 : // Check dependency
6824 4788 : std::string dependencyArgs;
6825 :
6826 32 : for (const auto &dependencyArgumentName :
6827 2458 : GetArgDependencies(arg->GetName()))
6828 : {
6829 32 : const auto otherArg{GetArg(dependencyArgumentName)};
6830 32 : if (otherArg != nullptr)
6831 : {
6832 32 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6833 : otherArg == arg)
6834 : {
6835 0 : continue;
6836 : }
6837 :
6838 32 : if (!dependencyArgs.empty())
6839 : {
6840 3 : dependencyArgs += ", ";
6841 : }
6842 :
6843 32 : dependencyArgs += "--";
6844 32 : dependencyArgs += otherArg->GetName();
6845 : }
6846 : else
6847 : {
6848 0 : CPLError(CE_Warning, CPLE_AppDefined,
6849 : "Argument '%s' depends on unknown argument '%s'",
6850 0 : arg->GetName().c_str(),
6851 : dependencyArgumentName.c_str());
6852 : }
6853 : }
6854 :
6855 2394 : if (!dependencyArgs.empty())
6856 : {
6857 29 : osRet += " ";
6858 29 : osRet += " ";
6859 29 : osRet.append(maxOptLen, ' ');
6860 29 : osRet += "Depends on ";
6861 29 : osRet += dependencyArgs;
6862 29 : osRet += '\n';
6863 : }
6864 2394 : };
6865 :
6866 382 : if (!m_positionalArgs.empty())
6867 : {
6868 150 : osRet += "\nPositional arguments:\n";
6869 1600 : for (const auto &[arg, opt] : options)
6870 : {
6871 1450 : if (arg->IsPositional())
6872 141 : OutputArg(arg, opt);
6873 : }
6874 : }
6875 :
6876 382 : if (hasNonPositionals)
6877 : {
6878 382 : bool hasCommon = false;
6879 382 : bool hasBase = false;
6880 382 : bool hasAdvanced = false;
6881 382 : bool hasEsoteric = false;
6882 764 : std::vector<std::string> categories;
6883 3722 : for (const auto &iter : options)
6884 : {
6885 3340 : const auto &arg = iter.first;
6886 3340 : if (!arg->IsPositional())
6887 : {
6888 3199 : const auto &category = arg->GetCategory();
6889 3199 : if (category == GAAC_COMMON)
6890 : {
6891 1164 : hasCommon = true;
6892 : }
6893 2035 : else if (category == GAAC_BASE)
6894 : {
6895 1795 : hasBase = true;
6896 : }
6897 240 : else if (category == GAAC_ADVANCED)
6898 : {
6899 178 : hasAdvanced = true;
6900 : }
6901 62 : else if (category == GAAC_ESOTERIC)
6902 : {
6903 29 : hasEsoteric = true;
6904 : }
6905 33 : else if (std::find(categories.begin(), categories.end(),
6906 33 : category) == categories.end())
6907 : {
6908 9 : categories.push_back(category);
6909 : }
6910 : }
6911 : }
6912 382 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6913 67 : categories.insert(categories.begin(), GAAC_ADVANCED);
6914 382 : if (hasBase)
6915 336 : categories.insert(categories.begin(), GAAC_BASE);
6916 382 : if (hasCommon && !usageOptions.isPipelineStep)
6917 68 : categories.insert(categories.begin(), GAAC_COMMON);
6918 382 : if (hasEsoteric)
6919 11 : categories.push_back(GAAC_ESOTERIC);
6920 :
6921 873 : for (const auto &category : categories)
6922 : {
6923 491 : osRet += "\n";
6924 491 : if (category != GAAC_BASE)
6925 : {
6926 155 : osRet += category;
6927 155 : osRet += ' ';
6928 : }
6929 491 : osRet += "Options:\n";
6930 5328 : for (const auto &[arg, opt] : options)
6931 : {
6932 4837 : if (!arg->IsPositional() && arg->GetCategory() == category)
6933 2253 : OutputArg(arg, opt);
6934 : }
6935 491 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6936 : {
6937 2 : osRet += " ";
6938 2 : osRet += userProvidedOpt;
6939 2 : osRet += " ";
6940 2 : if (userProvidedOpt.size() < maxOptLen)
6941 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6942 2 : osRet += "Argument provided by user";
6943 2 : osRet += '\n';
6944 : }
6945 : }
6946 : }
6947 : }
6948 :
6949 384 : if (!m_longDescription.empty())
6950 : {
6951 6 : osRet += '\n';
6952 6 : osRet += m_longDescription;
6953 6 : osRet += '\n';
6954 : }
6955 :
6956 384 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6957 : {
6958 371 : if (!m_helpURL.empty())
6959 : {
6960 371 : osRet += "\nFor more details, consult ";
6961 371 : osRet += GetHelpFullURL();
6962 371 : osRet += '\n';
6963 : }
6964 371 : osRet += GetUsageForCLIEnd();
6965 : }
6966 :
6967 384 : return osRet;
6968 : }
6969 :
6970 : /************************************************************************/
6971 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6972 : /************************************************************************/
6973 :
6974 : //! @cond Doxygen_Suppress
6975 378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6976 : {
6977 378 : std::string osRet;
6978 :
6979 378 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6980 : {
6981 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6982 : "alternative interface to GDAL and OGR command line "
6983 : "utilities.\nThe project reserves the right to modify, "
6984 : "rename, reorganize, and change the behavior of the utility\n"
6985 : "until it is officially frozen in a future feature release of "
6986 13 : "GDAL.\n";
6987 : }
6988 378 : return osRet;
6989 : }
6990 :
6991 : //! @endcond
6992 :
6993 : /************************************************************************/
6994 : /* GDALAlgorithm::GetUsageAsJSON() */
6995 : /************************************************************************/
6996 :
6997 565 : std::string GDALAlgorithm::GetUsageAsJSON() const
6998 : {
6999 1130 : CPLJSONDocument oDoc;
7000 1130 : auto oRoot = oDoc.GetRoot();
7001 :
7002 565 : if (m_displayInJSONUsage)
7003 : {
7004 563 : oRoot.Add("name", m_name);
7005 563 : CPLJSONArray jFullPath;
7006 1177 : for (const std::string &s : m_callPath)
7007 : {
7008 614 : jFullPath.Add(s);
7009 : }
7010 563 : oRoot.Add("full_path", jFullPath);
7011 : }
7012 :
7013 565 : oRoot.Add("description", m_description);
7014 565 : if (!m_helpURL.empty())
7015 : {
7016 563 : oRoot.Add("short_url", m_helpURL);
7017 563 : oRoot.Add("url", GetHelpFullURL());
7018 : }
7019 :
7020 1130 : CPLJSONArray jSubAlgorithms;
7021 767 : for (const auto &subAlgName : GetSubAlgorithmNames())
7022 : {
7023 404 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7024 202 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
7025 : {
7026 200 : CPLJSONDocument oSubDoc;
7027 200 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
7028 200 : jSubAlgorithms.Add(oSubDoc.GetRoot());
7029 : }
7030 : }
7031 565 : oRoot.Add("sub_algorithms", jSubAlgorithms);
7032 :
7033 565 : if (m_arbitraryLongNameArgsAllowed)
7034 : {
7035 1 : oRoot.Add("user_provided_arguments_allowed", true);
7036 : }
7037 :
7038 10806 : const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
7039 : {
7040 5403 : CPLJSONObject jArg;
7041 5403 : jArg.Add("name", arg->GetName());
7042 5403 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
7043 5403 : jArg.Add("description", arg->GetDescription());
7044 :
7045 5403 : const auto &metaVar = arg->GetMetaVar();
7046 5403 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
7047 : {
7048 1671 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
7049 1671 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
7050 : std::string::npos)
7051 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
7052 : else
7053 888 : jArg.Add("metavar", metaVar);
7054 : }
7055 :
7056 5403 : if (!arg->IsAvailableInPipelineStep())
7057 : {
7058 1567 : jArg.Add("available_in_pipeline_step", false);
7059 : }
7060 :
7061 5403 : const auto &choices = arg->GetChoices();
7062 5403 : if (!choices.empty())
7063 : {
7064 409 : CPLJSONArray jChoices;
7065 3510 : for (const auto &choice : choices)
7066 3101 : jChoices.Add(choice);
7067 409 : jArg.Add("choices", jChoices);
7068 : }
7069 5403 : if (arg->HasDefaultValue())
7070 : {
7071 1193 : switch (arg->GetType())
7072 : {
7073 429 : case GAAT_BOOLEAN:
7074 429 : jArg.Add("default", arg->GetDefault<bool>());
7075 429 : break;
7076 366 : case GAAT_STRING:
7077 366 : jArg.Add("default", arg->GetDefault<std::string>());
7078 366 : break;
7079 200 : case GAAT_INTEGER:
7080 200 : jArg.Add("default", arg->GetDefault<int>());
7081 200 : break;
7082 178 : case GAAT_REAL:
7083 178 : jArg.Add("default", arg->GetDefault<double>());
7084 178 : break;
7085 18 : case GAAT_STRING_LIST:
7086 : {
7087 : const auto &val =
7088 18 : arg->GetDefault<std::vector<std::string>>();
7089 18 : if (val.size() == 1)
7090 : {
7091 17 : jArg.Add("default", val[0]);
7092 : }
7093 : else
7094 : {
7095 1 : CPLJSONArray jArr;
7096 3 : for (const auto &s : val)
7097 : {
7098 2 : jArr.Add(s);
7099 : }
7100 1 : jArg.Add("default", jArr);
7101 : }
7102 18 : break;
7103 : }
7104 1 : case GAAT_INTEGER_LIST:
7105 : {
7106 1 : const auto &val = arg->GetDefault<std::vector<int>>();
7107 1 : if (val.size() == 1)
7108 : {
7109 0 : jArg.Add("default", val[0]);
7110 : }
7111 : else
7112 : {
7113 1 : CPLJSONArray jArr;
7114 3 : for (int i : val)
7115 : {
7116 2 : jArr.Add(i);
7117 : }
7118 1 : jArg.Add("default", jArr);
7119 : }
7120 1 : break;
7121 : }
7122 1 : case GAAT_REAL_LIST:
7123 : {
7124 1 : const auto &val = arg->GetDefault<std::vector<double>>();
7125 1 : if (val.size() == 1)
7126 : {
7127 0 : jArg.Add("default", val[0]);
7128 : }
7129 : else
7130 : {
7131 1 : CPLJSONArray jArr;
7132 3 : for (double d : val)
7133 : {
7134 2 : jArr.Add(d);
7135 : }
7136 1 : jArg.Add("default", jArr);
7137 : }
7138 1 : break;
7139 : }
7140 0 : case GAAT_DATASET:
7141 : case GAAT_DATASET_LIST:
7142 0 : CPLError(CE_Warning, CPLE_AppDefined,
7143 : "Unhandled default value for arg %s",
7144 0 : arg->GetName().c_str());
7145 0 : break;
7146 : }
7147 : }
7148 :
7149 5403 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7150 5403 : if (!std::isnan(minVal))
7151 : {
7152 671 : if (arg->GetType() == GAAT_INTEGER ||
7153 261 : arg->GetType() == GAAT_INTEGER_LIST)
7154 173 : jArg.Add("min_value", static_cast<int>(minVal));
7155 : else
7156 237 : jArg.Add("min_value", minVal);
7157 410 : jArg.Add("min_value_is_included", minValIsIncluded);
7158 : }
7159 :
7160 5403 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7161 5403 : if (!std::isnan(maxVal))
7162 : {
7163 199 : if (arg->GetType() == GAAT_INTEGER ||
7164 82 : arg->GetType() == GAAT_INTEGER_LIST)
7165 35 : jArg.Add("max_value", static_cast<int>(maxVal));
7166 : else
7167 82 : jArg.Add("max_value", maxVal);
7168 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
7169 : }
7170 :
7171 5403 : jArg.Add("required", arg->IsRequired());
7172 5403 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7173 : {
7174 1519 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7175 1519 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7176 1519 : jArg.Add("min_count", arg->GetMinCount());
7177 1519 : jArg.Add("max_count", arg->GetMaxCount());
7178 : }
7179 :
7180 : // Process dependencies
7181 5403 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7182 5403 : if (!mutualDependencyGroup.empty())
7183 : {
7184 32 : jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7185 : }
7186 :
7187 10806 : CPLJSONArray jDependencies;
7188 50 : for (const auto &dependencyArgumentName :
7189 5503 : GetArgDependencies(arg->GetName()))
7190 : {
7191 50 : jDependencies.Add(dependencyArgumentName);
7192 : }
7193 :
7194 5403 : if (jDependencies.Size() > 0)
7195 : {
7196 50 : jArg.Add("depends_on", jDependencies);
7197 : }
7198 :
7199 5403 : jArg.Add("category", arg->GetCategory());
7200 :
7201 10555 : if (arg->GetType() == GAAT_DATASET ||
7202 5152 : arg->GetType() == GAAT_DATASET_LIST)
7203 : {
7204 : {
7205 454 : CPLJSONArray jAr;
7206 454 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
7207 311 : jAr.Add("raster");
7208 454 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7209 179 : jAr.Add("vector");
7210 454 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7211 29 : jAr.Add("multidim_raster");
7212 454 : jArg.Add("dataset_type", jAr);
7213 : }
7214 :
7215 621 : const auto GetFlags = [](int flags)
7216 : {
7217 621 : CPLJSONArray jAr;
7218 621 : if (flags & GADV_NAME)
7219 454 : jAr.Add("name");
7220 621 : if (flags & GADV_OBJECT)
7221 572 : jAr.Add("dataset");
7222 621 : return jAr;
7223 : };
7224 :
7225 454 : if (arg->IsInput())
7226 : {
7227 454 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7228 : }
7229 454 : if (arg->IsOutput())
7230 : {
7231 167 : jArg.Add("output_flags",
7232 334 : GetFlags(arg->GetDatasetOutputFlags()));
7233 : }
7234 : }
7235 :
7236 5403 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7237 5403 : if (!mutualExclusionGroup.empty())
7238 : {
7239 673 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7240 : }
7241 :
7242 10806 : const auto &metadata = arg->GetMetadata();
7243 5403 : if (!metadata.empty())
7244 : {
7245 450 : CPLJSONObject jMetadata;
7246 939 : for (const auto &[key, values] : metadata)
7247 : {
7248 978 : CPLJSONArray jValue;
7249 1183 : for (const auto &value : values)
7250 694 : jValue.Add(value);
7251 489 : jMetadata.Add(key, jValue);
7252 : }
7253 450 : jArg.Add("metadata", jMetadata);
7254 : }
7255 :
7256 10806 : return jArg;
7257 565 : };
7258 :
7259 : {
7260 565 : CPLJSONArray jArgs;
7261 8880 : for (const auto &arg : m_args)
7262 : {
7263 8315 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7264 5179 : jArgs.Add(ProcessArg(arg.get()));
7265 : }
7266 565 : oRoot.Add("input_arguments", jArgs);
7267 : }
7268 :
7269 : {
7270 565 : CPLJSONArray jArgs;
7271 8880 : for (const auto &arg : m_args)
7272 : {
7273 8315 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7274 57 : jArgs.Add(ProcessArg(arg.get()));
7275 : }
7276 565 : oRoot.Add("output_arguments", jArgs);
7277 : }
7278 :
7279 : {
7280 565 : CPLJSONArray jArgs;
7281 8880 : for (const auto &arg : m_args)
7282 : {
7283 8315 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7284 167 : jArgs.Add(ProcessArg(arg.get()));
7285 : }
7286 565 : oRoot.Add("input_output_arguments", jArgs);
7287 : }
7288 :
7289 565 : if (m_supportsStreamedOutput)
7290 : {
7291 117 : oRoot.Add("supports_streamed_output", true);
7292 : }
7293 :
7294 1130 : return oDoc.SaveAsString();
7295 : }
7296 :
7297 : /************************************************************************/
7298 : /* GDALAlgorithm::GetAutoComplete() */
7299 : /************************************************************************/
7300 :
7301 : std::vector<std::string>
7302 293 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7303 : bool lastWordIsComplete, bool showAllOptions)
7304 : {
7305 586 : std::vector<std::string> ret;
7306 :
7307 : // Get inner-most algorithm
7308 293 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
7309 293 : GDALAlgorithm *curAlg = this;
7310 576 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7311 : {
7312 : auto subAlg = curAlg->InstantiateSubAlgorithm(
7313 427 : args.front(), /* suggestionAllowed = */ false);
7314 427 : if (!subAlg)
7315 143 : break;
7316 284 : if (args.size() == 1 && !lastWordIsComplete)
7317 : {
7318 5 : int nCount = 0;
7319 115 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7320 : {
7321 110 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7322 6 : nCount++;
7323 : }
7324 5 : if (nCount >= 2)
7325 : {
7326 11 : for (const std::string &subAlgName :
7327 23 : curAlg->GetSubAlgorithmNames())
7328 : {
7329 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7330 11 : if (subAlg && !subAlg->IsHidden())
7331 11 : ret.push_back(subAlg->GetName());
7332 : }
7333 1 : return ret;
7334 : }
7335 : }
7336 283 : showAllOptions = false;
7337 283 : args.erase(args.begin());
7338 283 : curAlgHolder = std::move(subAlg);
7339 283 : curAlg = curAlgHolder.get();
7340 : }
7341 292 : if (curAlg != this)
7342 : {
7343 154 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7344 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
7345 154 : /* showAllOptions = */ false);
7346 : }
7347 :
7348 276 : std::string option;
7349 276 : std::string value;
7350 138 : ExtractLastOptionAndValue(args, option, value);
7351 :
7352 169 : if (option.empty() && !args.empty() && !args.back().empty() &&
7353 31 : args.back()[0] == '-')
7354 : {
7355 28 : const auto &lastArg = args.back();
7356 : // List available options
7357 405 : for (const auto &arg : GetArgs())
7358 : {
7359 701 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7360 643 : (!showAllOptions &&
7361 876 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7362 530 : arg->GetName() == "version" ||
7363 265 : arg->GetName() == "json-usage")))
7364 : {
7365 134 : continue;
7366 : }
7367 243 : if (!arg->GetShortName().empty())
7368 : {
7369 153 : std::string str = std::string("-").append(arg->GetShortName());
7370 51 : if (lastArg == str)
7371 0 : ret.push_back(std::move(str));
7372 : }
7373 243 : if (lastArg != "-" && lastArg != "--")
7374 : {
7375 54 : for (const std::string &alias : arg->GetAliases())
7376 : {
7377 48 : std::string str = std::string("--").append(alias);
7378 16 : if (cpl::starts_with(str, lastArg))
7379 3 : ret.push_back(std::move(str));
7380 : }
7381 : }
7382 243 : if (!arg->GetName().empty())
7383 : {
7384 729 : std::string str = std::string("--").append(arg->GetName());
7385 243 : if (cpl::starts_with(str, lastArg))
7386 207 : ret.push_back(std::move(str));
7387 : }
7388 : }
7389 28 : std::sort(ret.begin(), ret.end());
7390 : }
7391 110 : else if (!option.empty())
7392 : {
7393 : // List possible choices for current option
7394 103 : auto arg = GetArg(option);
7395 103 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7396 : {
7397 103 : ret = arg->GetChoices();
7398 103 : if (ret.empty())
7399 : {
7400 : {
7401 98 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7402 98 : SetParseForAutoCompletion();
7403 98 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7404 : }
7405 98 : ret = arg->GetAutoCompleteChoices(value);
7406 : }
7407 : else
7408 : {
7409 5 : std::sort(ret.begin(), ret.end());
7410 : }
7411 103 : if (!ret.empty() && ret.back() == value)
7412 : {
7413 2 : ret.clear();
7414 : }
7415 101 : else if (ret.empty())
7416 : {
7417 13 : ret.push_back("**");
7418 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7419 26 : ret.push_back(std::string("\xC2\xA0"
7420 : "description: ")
7421 13 : .append(arg->GetDescription()));
7422 : }
7423 : }
7424 : }
7425 : else
7426 : {
7427 : // List possible sub-algorithms
7428 69 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7429 : {
7430 124 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7431 62 : if (subAlg && !subAlg->IsHidden())
7432 62 : ret.push_back(subAlg->GetName());
7433 : }
7434 7 : if (!ret.empty())
7435 : {
7436 3 : std::sort(ret.begin(), ret.end());
7437 : }
7438 :
7439 : // Try filenames
7440 7 : if (ret.empty() && !args.empty())
7441 : {
7442 : {
7443 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7444 3 : SetParseForAutoCompletion();
7445 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7446 : }
7447 :
7448 3 : const std::string &lastArg = args.back();
7449 3 : GDALAlgorithmArg *arg = nullptr;
7450 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7451 21 : "like", "source", "destination"})
7452 : {
7453 18 : if (!arg)
7454 : {
7455 3 : auto newArg = GetArg(name);
7456 3 : if (newArg)
7457 : {
7458 3 : if (!newArg->IsExplicitlySet())
7459 : {
7460 0 : arg = newArg;
7461 : }
7462 6 : else if (newArg->GetType() == GAAT_STRING ||
7463 5 : newArg->GetType() == GAAT_STRING_LIST ||
7464 8 : newArg->GetType() == GAAT_DATASET ||
7465 2 : newArg->GetType() == GAAT_DATASET_LIST)
7466 : {
7467 : VSIStatBufL sStat;
7468 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7469 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7470 : {
7471 3 : arg = newArg;
7472 : }
7473 : }
7474 : }
7475 : }
7476 : }
7477 3 : if (arg)
7478 : {
7479 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7480 : }
7481 : }
7482 : }
7483 :
7484 138 : return ret;
7485 : }
7486 :
7487 : /************************************************************************/
7488 : /* GDALAlgorithm::GetFieldIndices() */
7489 : /************************************************************************/
7490 :
7491 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7492 : OGRLayerH hLayer, std::vector<int> &indices)
7493 : {
7494 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7495 :
7496 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7497 :
7498 44 : if (names.size() == 1 && names[0] == "ALL")
7499 : {
7500 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7501 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7502 : {
7503 16 : indices.push_back(i);
7504 : }
7505 : }
7506 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7507 : {
7508 6 : std::set<int> fieldsAdded;
7509 14 : for (const std::string &osFieldName : names)
7510 : {
7511 :
7512 : const int nIdx =
7513 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7514 :
7515 10 : if (nIdx < 0)
7516 : {
7517 2 : CPLError(CE_Failure, CPLE_AppDefined,
7518 : "Field '%s' does not exist in layer '%s'",
7519 2 : osFieldName.c_str(), layer.GetName());
7520 2 : return false;
7521 : }
7522 :
7523 8 : if (fieldsAdded.insert(nIdx).second)
7524 : {
7525 7 : indices.push_back(nIdx);
7526 : }
7527 : }
7528 : }
7529 :
7530 42 : return true;
7531 : }
7532 :
7533 : /************************************************************************/
7534 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7535 : /************************************************************************/
7536 :
7537 138 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7538 : std::string &option,
7539 : std::string &value) const
7540 : {
7541 138 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7542 : {
7543 96 : const auto nPosEqual = args.back().find('=');
7544 96 : if (nPosEqual == std::string::npos)
7545 : {
7546 : // Deal with "gdal ... --option"
7547 77 : if (GetArg(args.back()))
7548 : {
7549 49 : option = args.back();
7550 49 : args.pop_back();
7551 : }
7552 : }
7553 : else
7554 : {
7555 : // Deal with "gdal ... --option=<value>"
7556 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7557 : {
7558 19 : option = args.back().substr(0, nPosEqual);
7559 19 : value = args.back().substr(nPosEqual + 1);
7560 19 : args.pop_back();
7561 : }
7562 : }
7563 : }
7564 78 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7565 36 : args[args.size() - 2][0] == '-')
7566 : {
7567 : // Deal with "gdal ... --option <value>"
7568 35 : auto arg = GetArg(args[args.size() - 2]);
7569 35 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7570 : {
7571 35 : option = args[args.size() - 2];
7572 35 : value = args.back();
7573 35 : args.pop_back();
7574 : }
7575 : }
7576 :
7577 138 : const auto IsKeyValueOption = [](const std::string &osStr)
7578 : {
7579 379 : return osStr == "--co" || osStr == "--creation-option" ||
7580 354 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7581 377 : osStr == "--oo" || osStr == "--open-option";
7582 : };
7583 :
7584 138 : if (IsKeyValueOption(option))
7585 : {
7586 23 : const auto nPosEqual = value.find('=');
7587 23 : if (nPosEqual != std::string::npos)
7588 : {
7589 11 : value.resize(nPosEqual);
7590 : }
7591 : }
7592 138 : }
7593 :
7594 : /************************************************************************/
7595 : /* GDALAlgorithm::GetArgDependencies() */
7596 : /************************************************************************/
7597 :
7598 : std::vector<std::string>
7599 7806 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
7600 : {
7601 7806 : const auto arg = GetArg(osName, false);
7602 7806 : if (!arg)
7603 : {
7604 0 : ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
7605 : osName.c_str());
7606 0 : return {};
7607 : }
7608 15612 : std::vector<std::string> dependencies = arg->GetDirectDependencies();
7609 7806 : if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7610 7806 : !mutualDependencyGroup.empty())
7611 : {
7612 896 : for (const auto &otherArg : m_args)
7613 : {
7614 1627 : if (otherArg.get() == arg ||
7615 786 : mutualDependencyGroup.compare(
7616 786 : otherArg->GetMutualDependencyGroup()) != 0)
7617 783 : continue;
7618 58 : dependencies.push_back(otherArg->GetName());
7619 : }
7620 : }
7621 7806 : return dependencies;
7622 : }
7623 :
7624 : //! @cond Doxygen_Suppress
7625 :
7626 : /************************************************************************/
7627 : /* GDALContainerAlgorithm::RunImpl() */
7628 : /************************************************************************/
7629 :
7630 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7631 : {
7632 0 : return false;
7633 : }
7634 :
7635 : //! @endcond
7636 :
7637 : /************************************************************************/
7638 : /* GDALAlgorithmRelease() */
7639 : /************************************************************************/
7640 :
7641 : /** Release a handle to an algorithm.
7642 : *
7643 : * @since 3.11
7644 : */
7645 12603 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7646 : {
7647 12603 : delete hAlg;
7648 12603 : }
7649 :
7650 : /************************************************************************/
7651 : /* GDALAlgorithmGetName() */
7652 : /************************************************************************/
7653 :
7654 : /** Return the algorithm name.
7655 : *
7656 : * @param hAlg Handle to an algorithm. Must NOT be null.
7657 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7658 : * be freed.
7659 : * @since 3.11
7660 : */
7661 5783 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7662 : {
7663 5783 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7664 5783 : return hAlg->ptr->GetName().c_str();
7665 : }
7666 :
7667 : /************************************************************************/
7668 : /* GDALAlgorithmGetDescription() */
7669 : /************************************************************************/
7670 :
7671 : /** Return the algorithm (short) description.
7672 : *
7673 : * @param hAlg Handle to an algorithm. Must NOT be null.
7674 : * @return algorithm description whose lifetime is bound to hAlg and which must
7675 : * not be freed.
7676 : * @since 3.11
7677 : */
7678 5701 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7679 : {
7680 5701 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7681 5701 : return hAlg->ptr->GetDescription().c_str();
7682 : }
7683 :
7684 : /************************************************************************/
7685 : /* GDALAlgorithmGetLongDescription() */
7686 : /************************************************************************/
7687 :
7688 : /** Return the algorithm (longer) description.
7689 : *
7690 : * @param hAlg Handle to an algorithm. Must NOT be null.
7691 : * @return algorithm description whose lifetime is bound to hAlg and which must
7692 : * not be freed.
7693 : * @since 3.11
7694 : */
7695 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7696 : {
7697 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7698 2 : return hAlg->ptr->GetLongDescription().c_str();
7699 : }
7700 :
7701 : /************************************************************************/
7702 : /* GDALAlgorithmGetHelpFullURL() */
7703 : /************************************************************************/
7704 :
7705 : /** Return the algorithm full URL.
7706 : *
7707 : * @param hAlg Handle to an algorithm. Must NOT be null.
7708 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7709 : * not be freed.
7710 : * @since 3.11
7711 : */
7712 5004 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7713 : {
7714 5004 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7715 5004 : return hAlg->ptr->GetHelpFullURL().c_str();
7716 : }
7717 :
7718 : /************************************************************************/
7719 : /* GDALAlgorithmHasSubAlgorithms() */
7720 : /************************************************************************/
7721 :
7722 : /** Return whether the algorithm has sub-algorithms.
7723 : *
7724 : * @param hAlg Handle to an algorithm. Must NOT be null.
7725 : * @since 3.11
7726 : */
7727 9261 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7728 : {
7729 9261 : VALIDATE_POINTER1(hAlg, __func__, false);
7730 9261 : return hAlg->ptr->HasSubAlgorithms();
7731 : }
7732 :
7733 : /************************************************************************/
7734 : /* GDALAlgorithmGetSubAlgorithmNames() */
7735 : /************************************************************************/
7736 :
7737 : /** Get the names of registered algorithms.
7738 : *
7739 : * @param hAlg Handle to an algorithm. Must NOT be null.
7740 : * @return a NULL terminated list of names, which must be destroyed with
7741 : * CSLDestroy()
7742 : * @since 3.11
7743 : */
7744 749 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7745 : {
7746 749 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7747 749 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7748 : }
7749 :
7750 : /************************************************************************/
7751 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7752 : /************************************************************************/
7753 :
7754 : /** Instantiate an algorithm by its name (or its alias).
7755 : *
7756 : * @param hAlg Handle to an algorithm. Must NOT be null.
7757 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7758 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7759 : * or NULL if the algorithm does not exist or another error occurred.
7760 : * @since 3.11
7761 : */
7762 8725 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7763 : const char *pszSubAlgName)
7764 : {
7765 8725 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7766 8725 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7767 17450 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7768 : return subAlg
7769 17450 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7770 17450 : : nullptr;
7771 : }
7772 :
7773 : /************************************************************************/
7774 : /* GDALAlgorithmParseCommandLineArguments() */
7775 : /************************************************************************/
7776 :
7777 : /** Parse a command line argument, which does not include the algorithm
7778 : * name, to set the value of corresponding arguments.
7779 : *
7780 : * @param hAlg Handle to an algorithm. Must NOT be null.
7781 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7782 : * @return true if successful, false otherwise
7783 : * @since 3.11
7784 : */
7785 :
7786 356 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7787 : CSLConstList papszArgs)
7788 : {
7789 356 : VALIDATE_POINTER1(hAlg, __func__, false);
7790 356 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7791 : }
7792 :
7793 : /************************************************************************/
7794 : /* GDALAlgorithmGetActualAlgorithm() */
7795 : /************************************************************************/
7796 :
7797 : /** Return the actual algorithm that is going to be invoked, when the
7798 : * current algorithm has sub-algorithms.
7799 : *
7800 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7801 : *
7802 : * Note that the lifetime of the returned algorithm does not exceed the one of
7803 : * the hAlg instance that owns it.
7804 : *
7805 : * @param hAlg Handle to an algorithm. Must NOT be null.
7806 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7807 : * @since 3.11
7808 : */
7809 914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7810 : {
7811 914 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7812 914 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7813 : }
7814 :
7815 : /************************************************************************/
7816 : /* GDALAlgorithmRun() */
7817 : /************************************************************************/
7818 :
7819 : /** Execute the algorithm, starting with ValidateArguments() and then
7820 : * calling RunImpl().
7821 : *
7822 : * This function must be called at most once per instance.
7823 : *
7824 : * @param hAlg Handle to an algorithm. Must NOT be null.
7825 : * @param pfnProgress Progress callback. May be null.
7826 : * @param pProgressData Progress callback user data. May be null.
7827 : * @return true if successful, false otherwise
7828 : * @since 3.11
7829 : */
7830 :
7831 2789 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7832 : void *pProgressData)
7833 : {
7834 2789 : VALIDATE_POINTER1(hAlg, __func__, false);
7835 2789 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7836 : }
7837 :
7838 : /************************************************************************/
7839 : /* GDALAlgorithmFinalize() */
7840 : /************************************************************************/
7841 :
7842 : /** Complete any pending actions, and return the final status.
7843 : * This is typically useful for algorithm that generate an output dataset.
7844 : *
7845 : * Note that this function does *NOT* release memory associated with the
7846 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7847 : *
7848 : * @param hAlg Handle to an algorithm. Must NOT be null.
7849 : * @return true if successful, false otherwise
7850 : * @since 3.11
7851 : */
7852 :
7853 922 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7854 : {
7855 922 : VALIDATE_POINTER1(hAlg, __func__, false);
7856 922 : return hAlg->ptr->Finalize();
7857 : }
7858 :
7859 : /************************************************************************/
7860 : /* GDALAlgorithmGetUsageAsJSON() */
7861 : /************************************************************************/
7862 :
7863 : /** Return the usage of the algorithm as a JSON-serialized string.
7864 : *
7865 : * This can be used to dynamically generate interfaces to algorithms.
7866 : *
7867 : * @param hAlg Handle to an algorithm. Must NOT be null.
7868 : * @return a string that must be freed with CPLFree()
7869 : * @since 3.11
7870 : */
7871 6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7872 : {
7873 6 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7874 6 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7875 : }
7876 :
7877 : /************************************************************************/
7878 : /* GDALAlgorithmGetArgNames() */
7879 : /************************************************************************/
7880 :
7881 : /** Return the list of available argument names.
7882 : *
7883 : * @param hAlg Handle to an algorithm. Must NOT be null.
7884 : * @return a NULL terminated list of names, which must be destroyed with
7885 : * CSLDestroy()
7886 : * @since 3.11
7887 : */
7888 15574 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7889 : {
7890 15574 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7891 31148 : CPLStringList list;
7892 345890 : for (const auto &arg : hAlg->ptr->GetArgs())
7893 330316 : list.AddString(arg->GetName().c_str());
7894 15574 : return list.StealList();
7895 : }
7896 :
7897 : /************************************************************************/
7898 : /* GDALAlgorithmGetArg() */
7899 : /************************************************************************/
7900 :
7901 : /** Return an argument from its name.
7902 : *
7903 : * The lifetime of the returned object does not exceed the one of hAlg.
7904 : *
7905 : * @param hAlg Handle to an algorithm. Must NOT be null.
7906 : * @param pszArgName Argument name. Must NOT be null.
7907 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7908 : * or nullptr in case of error
7909 : * @since 3.11
7910 : */
7911 331226 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7912 : const char *pszArgName)
7913 : {
7914 331226 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7915 331226 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7916 662452 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7917 331226 : /* isConst = */ true);
7918 331226 : if (!arg)
7919 3 : return nullptr;
7920 331223 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7921 : }
7922 :
7923 : /************************************************************************/
7924 : /* GDALAlgorithmGetArgNonConst() */
7925 : /************************************************************************/
7926 :
7927 : /** Return an argument from its name, possibly allowing creation of user-provided
7928 : * argument if the algorithm allow it.
7929 : *
7930 : * The lifetime of the returned object does not exceed the one of hAlg.
7931 : *
7932 : * @param hAlg Handle to an algorithm. Must NOT be null.
7933 : * @param pszArgName Argument name. Must NOT be null.
7934 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7935 : * or nullptr in case of error
7936 : * @since 3.12
7937 : */
7938 9857 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7939 : const char *pszArgName)
7940 : {
7941 9857 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7942 9857 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7943 19714 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7944 9857 : /* isConst = */ false);
7945 9857 : if (!arg)
7946 2 : return nullptr;
7947 9855 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7948 : }
7949 :
7950 : /************************************************************************/
7951 : /* GDALAlgorithmGetArgDependencies() */
7952 : /************************************************************************/
7953 :
7954 : /** Return the list of argument names the specified argument depends on.
7955 : *
7956 : * This includes both regular dependencies and mutual dependencies.
7957 : *
7958 : * @param hAlg Handle to an algorithm. Must NOT be null.
7959 : * @param pszArgName Argument name. Must NOT be null.
7960 : * @return a NULL terminated list of names, which must be destroyed with
7961 : * CSLDestroy()
7962 : * @since 3.11
7963 : */
7964 7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
7965 : const char *pszArgName)
7966 : {
7967 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7968 7 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7969 7 : return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
7970 : }
7971 :
7972 : /************************************************************************/
7973 : /* GDALAlgorithmArgRelease() */
7974 : /************************************************************************/
7975 :
7976 : /** Release a handle to an argument.
7977 : *
7978 : * @since 3.11
7979 : */
7980 341078 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7981 : {
7982 341078 : delete hArg;
7983 341078 : }
7984 :
7985 : /************************************************************************/
7986 : /* GDALAlgorithmArgGetName() */
7987 : /************************************************************************/
7988 :
7989 : /** Return the name of an argument.
7990 : *
7991 : * @param hArg Handle to an argument. Must NOT be null.
7992 : * @return argument name whose lifetime is bound to hArg and which must not
7993 : * be freed.
7994 : * @since 3.11
7995 : */
7996 18947 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7997 : {
7998 18947 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7999 18947 : return hArg->ptr->GetName().c_str();
8000 : }
8001 :
8002 : /************************************************************************/
8003 : /* GDALAlgorithmArgGetType() */
8004 : /************************************************************************/
8005 :
8006 : /** Get the type of an argument
8007 : *
8008 : * @param hArg Handle to an argument. Must NOT be null.
8009 : * @since 3.11
8010 : */
8011 420660 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
8012 : {
8013 420660 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
8014 420660 : return hArg->ptr->GetType();
8015 : }
8016 :
8017 : /************************************************************************/
8018 : /* GDALAlgorithmArgGetDescription() */
8019 : /************************************************************************/
8020 :
8021 : /** Return the description of an argument.
8022 : *
8023 : * @param hArg Handle to an argument. Must NOT be null.
8024 : * @return argument description whose lifetime is bound to hArg and which must not
8025 : * be freed.
8026 : * @since 3.11
8027 : */
8028 83887 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
8029 : {
8030 83887 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8031 83887 : return hArg->ptr->GetDescription().c_str();
8032 : }
8033 :
8034 : /************************************************************************/
8035 : /* GDALAlgorithmArgGetShortName() */
8036 : /************************************************************************/
8037 :
8038 : /** Return the short name, or empty string if there is none
8039 : *
8040 : * @param hArg Handle to an argument. Must NOT be null.
8041 : * @return short name whose lifetime is bound to hArg and which must not
8042 : * be freed.
8043 : * @since 3.11
8044 : */
8045 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
8046 : {
8047 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8048 1 : return hArg->ptr->GetShortName().c_str();
8049 : }
8050 :
8051 : /************************************************************************/
8052 : /* GDALAlgorithmArgGetAliases() */
8053 : /************************************************************************/
8054 :
8055 : /** Return the aliases (potentially none)
8056 : *
8057 : * @param hArg Handle to an argument. Must NOT be null.
8058 : * @return a NULL terminated list of names, which must be destroyed with
8059 : * CSLDestroy()
8060 :
8061 : * @since 3.11
8062 : */
8063 158753 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
8064 : {
8065 158753 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8066 158753 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
8067 : }
8068 :
8069 : /************************************************************************/
8070 : /* GDALAlgorithmArgGetMetaVar() */
8071 : /************************************************************************/
8072 :
8073 : /** Return the "meta-var" hint.
8074 : *
8075 : * By default, the meta-var value is the long name of the argument in
8076 : * upper case.
8077 : *
8078 : * @param hArg Handle to an argument. Must NOT be null.
8079 : * @return meta-var hint whose lifetime is bound to hArg and which must not
8080 : * be freed.
8081 : * @since 3.11
8082 : */
8083 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
8084 : {
8085 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8086 1 : return hArg->ptr->GetMetaVar().c_str();
8087 : }
8088 :
8089 : /************************************************************************/
8090 : /* GDALAlgorithmArgGetCategory() */
8091 : /************************************************************************/
8092 :
8093 : /** Return the argument category
8094 : *
8095 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
8096 : *
8097 : * @param hArg Handle to an argument. Must NOT be null.
8098 : * @return category whose lifetime is bound to hArg and which must not
8099 : * be freed.
8100 : * @since 3.11
8101 : */
8102 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
8103 : {
8104 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8105 1 : return hArg->ptr->GetCategory().c_str();
8106 : }
8107 :
8108 : /************************************************************************/
8109 : /* GDALAlgorithmArgIsPositional() */
8110 : /************************************************************************/
8111 :
8112 : /** Return if the argument is a positional one.
8113 : *
8114 : * @param hArg Handle to an argument. Must NOT be null.
8115 : * @since 3.11
8116 : */
8117 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
8118 : {
8119 1 : VALIDATE_POINTER1(hArg, __func__, false);
8120 1 : return hArg->ptr->IsPositional();
8121 : }
8122 :
8123 : /************************************************************************/
8124 : /* GDALAlgorithmArgIsRequired() */
8125 : /************************************************************************/
8126 :
8127 : /** Return whether the argument is required. Defaults to false.
8128 : *
8129 : * @param hArg Handle to an argument. Must NOT be null.
8130 : * @since 3.11
8131 : */
8132 158753 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
8133 : {
8134 158753 : VALIDATE_POINTER1(hArg, __func__, false);
8135 158753 : return hArg->ptr->IsRequired();
8136 : }
8137 :
8138 : /************************************************************************/
8139 : /* GDALAlgorithmArgGetMinCount() */
8140 : /************************************************************************/
8141 :
8142 : /** Return the minimum number of values for the argument.
8143 : *
8144 : * Defaults to 0.
8145 : * Only applies to list type of arguments.
8146 : *
8147 : * @param hArg Handle to an argument. Must NOT be null.
8148 : * @since 3.11
8149 : */
8150 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
8151 : {
8152 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8153 1 : return hArg->ptr->GetMinCount();
8154 : }
8155 :
8156 : /************************************************************************/
8157 : /* GDALAlgorithmArgGetMaxCount() */
8158 : /************************************************************************/
8159 :
8160 : /** Return the maximum number of values for the argument.
8161 : *
8162 : * Defaults to 1 for scalar types, and INT_MAX for list types.
8163 : * Only applies to list type of arguments.
8164 : *
8165 : * @param hArg Handle to an argument. Must NOT be null.
8166 : * @since 3.11
8167 : */
8168 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8169 : {
8170 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8171 1 : return hArg->ptr->GetMaxCount();
8172 : }
8173 :
8174 : /************************************************************************/
8175 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
8176 : /************************************************************************/
8177 :
8178 : /** Return whether, for list type of arguments, several values, space
8179 : * separated, may be specified. That is "--foo=bar,baz".
8180 : * The default is true.
8181 : *
8182 : * @param hArg Handle to an argument. Must NOT be null.
8183 : * @since 3.11
8184 : */
8185 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8186 : {
8187 1 : VALIDATE_POINTER1(hArg, __func__, false);
8188 1 : return hArg->ptr->GetPackedValuesAllowed();
8189 : }
8190 :
8191 : /************************************************************************/
8192 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
8193 : /************************************************************************/
8194 :
8195 : /** Return whether, for list type of arguments, the argument may be
8196 : * repeated. That is "--foo=bar --foo=baz".
8197 : * The default is true.
8198 : *
8199 : * @param hArg Handle to an argument. Must NOT be null.
8200 : * @since 3.11
8201 : */
8202 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8203 : {
8204 1 : VALIDATE_POINTER1(hArg, __func__, false);
8205 1 : return hArg->ptr->GetRepeatedArgAllowed();
8206 : }
8207 :
8208 : /************************************************************************/
8209 : /* GDALAlgorithmArgGetChoices() */
8210 : /************************************************************************/
8211 :
8212 : /** Return the allowed values (as strings) for the argument.
8213 : *
8214 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8215 : *
8216 : * @param hArg Handle to an argument. Must NOT be null.
8217 : * @return a NULL terminated list of names, which must be destroyed with
8218 : * CSLDestroy()
8219 :
8220 : * @since 3.11
8221 : */
8222 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8223 : {
8224 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8225 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
8226 : }
8227 :
8228 : /************************************************************************/
8229 : /* GDALAlgorithmArgGetMetadataItem() */
8230 : /************************************************************************/
8231 :
8232 : /** Return the values of the metadata item of an argument.
8233 : *
8234 : * @param hArg Handle to an argument. Must NOT be null.
8235 : * @param pszItem Name of the item. Must NOT be null.
8236 : * @return a NULL terminated list of values, which must be destroyed with
8237 : * CSLDestroy()
8238 :
8239 : * @since 3.11
8240 : */
8241 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8242 : const char *pszItem)
8243 : {
8244 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8245 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
8246 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8247 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8248 : }
8249 :
8250 : /************************************************************************/
8251 : /* GDALAlgorithmArgIsExplicitlySet() */
8252 : /************************************************************************/
8253 :
8254 : /** Return whether the argument value has been explicitly set with Set()
8255 : *
8256 : * @param hArg Handle to an argument. Must NOT be null.
8257 : * @since 3.11
8258 : */
8259 663 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8260 : {
8261 663 : VALIDATE_POINTER1(hArg, __func__, false);
8262 663 : return hArg->ptr->IsExplicitlySet();
8263 : }
8264 :
8265 : /************************************************************************/
8266 : /* GDALAlgorithmArgHasDefaultValue() */
8267 : /************************************************************************/
8268 :
8269 : /** Return if the argument has a declared default value.
8270 : *
8271 : * @param hArg Handle to an argument. Must NOT be null.
8272 : * @since 3.11
8273 : */
8274 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8275 : {
8276 2 : VALIDATE_POINTER1(hArg, __func__, false);
8277 2 : return hArg->ptr->HasDefaultValue();
8278 : }
8279 :
8280 : /************************************************************************/
8281 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
8282 : /************************************************************************/
8283 :
8284 : /** Return the argument default value as a integer.
8285 : *
8286 : * Must only be called on arguments whose type is GAAT_BOOLEAN
8287 : *
8288 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8289 : * argument has a default value.
8290 : *
8291 : * @param hArg Handle to an argument. Must NOT be null.
8292 : * @since 3.12
8293 : */
8294 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8295 : {
8296 3 : VALIDATE_POINTER1(hArg, __func__, false);
8297 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8298 : {
8299 1 : CPLError(CE_Failure, CPLE_AppDefined,
8300 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8301 : __func__);
8302 1 : return false;
8303 : }
8304 2 : return hArg->ptr->GetDefault<bool>();
8305 : }
8306 :
8307 : /************************************************************************/
8308 : /* GDALAlgorithmArgGetDefaultAsString() */
8309 : /************************************************************************/
8310 :
8311 : /** Return the argument default value as a string.
8312 : *
8313 : * Must only be called on arguments whose type is GAAT_STRING.
8314 : *
8315 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8316 : * argument has a default value.
8317 : *
8318 : * @param hArg Handle to an argument. Must NOT be null.
8319 : * @return string whose lifetime is bound to hArg and which must not
8320 : * be freed.
8321 : * @since 3.11
8322 : */
8323 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8324 : {
8325 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8326 3 : if (hArg->ptr->GetType() != GAAT_STRING)
8327 : {
8328 2 : CPLError(CE_Failure, CPLE_AppDefined,
8329 : "%s must only be called on arguments of type GAAT_STRING",
8330 : __func__);
8331 2 : return nullptr;
8332 : }
8333 1 : return hArg->ptr->GetDefault<std::string>().c_str();
8334 : }
8335 :
8336 : /************************************************************************/
8337 : /* GDALAlgorithmArgGetDefaultAsInteger() */
8338 : /************************************************************************/
8339 :
8340 : /** Return the argument default value as a integer.
8341 : *
8342 : * Must only be called on arguments whose type is GAAT_INTEGER
8343 : *
8344 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8345 : * argument has a default value.
8346 : *
8347 : * @param hArg Handle to an argument. Must NOT be null.
8348 : * @since 3.12
8349 : */
8350 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8351 : {
8352 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8353 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8354 : {
8355 2 : CPLError(CE_Failure, CPLE_AppDefined,
8356 : "%s must only be called on arguments of type GAAT_INTEGER",
8357 : __func__);
8358 2 : return 0;
8359 : }
8360 1 : return hArg->ptr->GetDefault<int>();
8361 : }
8362 :
8363 : /************************************************************************/
8364 : /* GDALAlgorithmArgGetDefaultAsDouble() */
8365 : /************************************************************************/
8366 :
8367 : /** Return the argument default value as a double.
8368 : *
8369 : * Must only be called on arguments whose type is GAAT_REAL
8370 : *
8371 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8372 : * argument has a default value.
8373 : *
8374 : * @param hArg Handle to an argument. Must NOT be null.
8375 : * @since 3.12
8376 : */
8377 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8378 : {
8379 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8380 3 : if (hArg->ptr->GetType() != GAAT_REAL)
8381 : {
8382 2 : CPLError(CE_Failure, CPLE_AppDefined,
8383 : "%s must only be called on arguments of type GAAT_REAL",
8384 : __func__);
8385 2 : return 0;
8386 : }
8387 1 : return hArg->ptr->GetDefault<double>();
8388 : }
8389 :
8390 : /************************************************************************/
8391 : /* GDALAlgorithmArgGetDefaultAsStringList() */
8392 : /************************************************************************/
8393 :
8394 : /** Return the argument default value as a string list.
8395 : *
8396 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8397 : *
8398 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8399 : * argument has a default value.
8400 : *
8401 : * @param hArg Handle to an argument. Must NOT be null.
8402 : * @return a NULL terminated list of names, which must be destroyed with
8403 : * CSLDestroy()
8404 :
8405 : * @since 3.12
8406 : */
8407 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8408 : {
8409 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8410 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8411 : {
8412 2 : CPLError(CE_Failure, CPLE_AppDefined,
8413 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8414 : __func__);
8415 2 : return nullptr;
8416 : }
8417 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8418 1 : .StealList();
8419 : }
8420 :
8421 : /************************************************************************/
8422 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8423 : /************************************************************************/
8424 :
8425 : /** Return the argument default value as a integer list.
8426 : *
8427 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8428 : *
8429 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8430 : * argument has a default value.
8431 : *
8432 : * @param hArg Handle to an argument. Must NOT be null.
8433 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8434 : * @since 3.12
8435 : */
8436 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8437 : size_t *pnCount)
8438 : {
8439 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8440 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8441 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8442 : {
8443 2 : CPLError(
8444 : CE_Failure, CPLE_AppDefined,
8445 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8446 : __func__);
8447 2 : *pnCount = 0;
8448 2 : return nullptr;
8449 : }
8450 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8451 1 : *pnCount = val.size();
8452 1 : return val.data();
8453 : }
8454 :
8455 : /************************************************************************/
8456 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8457 : /************************************************************************/
8458 :
8459 : /** Return the argument default value as a real list.
8460 : *
8461 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8462 : *
8463 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8464 : * argument has a default value.
8465 : *
8466 : * @param hArg Handle to an argument. Must NOT be null.
8467 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8468 : * @since 3.12
8469 : */
8470 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8471 : size_t *pnCount)
8472 : {
8473 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8474 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8475 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8476 : {
8477 2 : CPLError(CE_Failure, CPLE_AppDefined,
8478 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8479 : __func__);
8480 2 : *pnCount = 0;
8481 2 : return nullptr;
8482 : }
8483 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8484 1 : *pnCount = val.size();
8485 1 : return val.data();
8486 : }
8487 :
8488 : /************************************************************************/
8489 : /* GDALAlgorithmArgIsHidden() */
8490 : /************************************************************************/
8491 :
8492 : /** Return whether the argument is hidden (for GDAL internal use)
8493 : *
8494 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8495 : * GDALAlgorithmArgIsHiddenForAPI().
8496 : *
8497 : * @param hArg Handle to an argument. Must NOT be null.
8498 : * @since 3.12
8499 : */
8500 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8501 : {
8502 1 : VALIDATE_POINTER1(hArg, __func__, false);
8503 1 : return hArg->ptr->IsHidden();
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* GDALAlgorithmArgIsHiddenForCLI() */
8508 : /************************************************************************/
8509 :
8510 : /** Return whether the argument must not be mentioned in CLI usage.
8511 : *
8512 : * For example, "output-value" for "gdal raster info", which is only
8513 : * meant when the algorithm is used from a non-CLI context.
8514 : *
8515 : * @param hArg Handle to an argument. Must NOT be null.
8516 : * @since 3.11
8517 : */
8518 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8519 : {
8520 1 : VALIDATE_POINTER1(hArg, __func__, false);
8521 1 : return hArg->ptr->IsHiddenForCLI();
8522 : }
8523 :
8524 : /************************************************************************/
8525 : /* GDALAlgorithmArgIsHiddenForAPI() */
8526 : /************************************************************************/
8527 :
8528 : /** Return whether the argument must not be mentioned in the context of an
8529 : * API use.
8530 : * Said otherwise, if it is only for CLI usage.
8531 : *
8532 : * For example "--help"
8533 : *
8534 : * @param hArg Handle to an argument. Must NOT be null.
8535 : * @since 3.12
8536 : */
8537 214759 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8538 : {
8539 214759 : VALIDATE_POINTER1(hArg, __func__, false);
8540 214759 : return hArg->ptr->IsHiddenForAPI();
8541 : }
8542 :
8543 : /************************************************************************/
8544 : /* GDALAlgorithmArgIsOnlyForCLI() */
8545 : /************************************************************************/
8546 :
8547 : /** Return whether the argument must not be mentioned in the context of an
8548 : * API use.
8549 : * Said otherwise, if it is only for CLI usage.
8550 : *
8551 : * For example "--help"
8552 : *
8553 : * @param hArg Handle to an argument. Must NOT be null.
8554 : * @since 3.11
8555 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8556 : */
8557 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8558 : {
8559 0 : VALIDATE_POINTER1(hArg, __func__, false);
8560 0 : return hArg->ptr->IsHiddenForAPI();
8561 : }
8562 :
8563 : /************************************************************************/
8564 : /* GDALAlgorithmArgIsAvailableInPipelineStep() */
8565 : /************************************************************************/
8566 :
8567 : /** Return whether the argument is available in a pipeline step.
8568 : *
8569 : * If false, it is only available in standalone mode.
8570 : *
8571 : * @param hArg Handle to an argument. Must NOT be null.
8572 : * @since 3.13
8573 : */
8574 2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8575 : {
8576 2 : VALIDATE_POINTER1(hArg, __func__, false);
8577 2 : return hArg->ptr->IsAvailableInPipelineStep();
8578 : }
8579 :
8580 : /************************************************************************/
8581 : /* GDALAlgorithmArgIsInput() */
8582 : /************************************************************************/
8583 :
8584 : /** Indicate whether the value of the argument is read-only during the
8585 : * execution of the algorithm.
8586 : *
8587 : * Default is true.
8588 : *
8589 : * @param hArg Handle to an argument. Must NOT be null.
8590 : * @since 3.11
8591 : */
8592 211889 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8593 : {
8594 211889 : VALIDATE_POINTER1(hArg, __func__, false);
8595 211889 : return hArg->ptr->IsInput();
8596 : }
8597 :
8598 : /************************************************************************/
8599 : /* GDALAlgorithmArgIsOutput() */
8600 : /************************************************************************/
8601 :
8602 : /** Return whether (at least part of) the value of the argument is set
8603 : * during the execution of the algorithm.
8604 : *
8605 : * For example, "output-value" for "gdal raster info"
8606 : * Default is false.
8607 : * An argument may return both IsInput() and IsOutput() as true.
8608 : * For example the "gdal raster convert" algorithm consumes the dataset
8609 : * name of its "output" argument, and sets the dataset object during its
8610 : * execution.
8611 : *
8612 : * @param hArg Handle to an argument. Must NOT be null.
8613 : * @since 3.11
8614 : */
8615 118403 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8616 : {
8617 118403 : VALIDATE_POINTER1(hArg, __func__, false);
8618 118403 : return hArg->ptr->IsOutput();
8619 : }
8620 :
8621 : /************************************************************************/
8622 : /* GDALAlgorithmArgGetDatasetType() */
8623 : /************************************************************************/
8624 :
8625 : /** Get which type of dataset is allowed / generated.
8626 : *
8627 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8628 : * GDAL_OF_MULTIDIM_RASTER.
8629 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8630 : *
8631 : * @param hArg Handle to an argument. Must NOT be null.
8632 : * @since 3.11
8633 : */
8634 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8635 : {
8636 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8637 2 : return hArg->ptr->GetDatasetType();
8638 : }
8639 :
8640 : /************************************************************************/
8641 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8642 : /************************************************************************/
8643 :
8644 : /** Indicates which components among name and dataset are accepted as
8645 : * input, when this argument serves as an input.
8646 : *
8647 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8648 : * input.
8649 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8650 : * accepted as input.
8651 : * If both bits are set, the algorithm can accept either a name or a dataset
8652 : * object.
8653 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8654 : *
8655 : * @param hArg Handle to an argument. Must NOT be null.
8656 : * @return string whose lifetime is bound to hAlg and which must not
8657 : * be freed.
8658 : * @since 3.11
8659 : */
8660 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8661 : {
8662 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8663 2 : return hArg->ptr->GetDatasetInputFlags();
8664 : }
8665 :
8666 : /************************************************************************/
8667 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8668 : /************************************************************************/
8669 :
8670 : /** Indicates which components among name and dataset are modified,
8671 : * when this argument serves as an output.
8672 : *
8673 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8674 : * output (that is the algorithm will generate the name. Rarely used).
8675 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8676 : * generated as output, and available for use after the algorithm has
8677 : * completed.
8678 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8679 : *
8680 : * @param hArg Handle to an argument. Must NOT be null.
8681 : * @return string whose lifetime is bound to hAlg and which must not
8682 : * be freed.
8683 : * @since 3.11
8684 : */
8685 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8686 : {
8687 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8688 2 : return hArg->ptr->GetDatasetOutputFlags();
8689 : }
8690 :
8691 : /************************************************************************/
8692 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8693 : /************************************************************************/
8694 :
8695 : /** Return the name of the mutual exclusion group to which this argument
8696 : * belongs to.
8697 : *
8698 : * Or empty string if it does not belong to any exclusion group.
8699 : *
8700 : * @param hArg Handle to an argument. Must NOT be null.
8701 : * @return string whose lifetime is bound to hArg and which must not
8702 : * be freed.
8703 : * @since 3.11
8704 : */
8705 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8706 : {
8707 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8708 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8709 : }
8710 :
8711 : /************************************************************************/
8712 : /* GDALAlgorithmArgGetMutualDependencyGroup() */
8713 : /************************************************************************/
8714 :
8715 : /** Return the name of the mutual dependency group to which this argument
8716 : * belongs to.
8717 : *
8718 : * Or empty string if it does not belong to any dependency group.
8719 : *
8720 : * @param hArg Handle to an argument. Must NOT be null.
8721 : * @return string whose lifetime is bound to hArg and which must not
8722 : * be freed.
8723 : * @since 3.13
8724 : */
8725 5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8726 : {
8727 5 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8728 5 : return hArg->ptr->GetMutualDependencyGroup().c_str();
8729 : }
8730 :
8731 : /************************************************************************/
8732 : /* GDALAlgorithmArgGetDirectDependencies() */
8733 : /************************************************************************/
8734 :
8735 : /** Return the list of names of arguments that this argument depends on.
8736 : *
8737 : * This is not necessarily a symmetric relationship.
8738 : * If argument A depends on argument B, it doesn't mean that B depends on A.
8739 : * Mutual dependency groups are a special case of dependencies,
8740 : * where all arguments of the group depend on each other and are not
8741 : * returned by this method.
8742 : *
8743 : * @param hArg Handle to an argument. Must NOT be null.
8744 : * @return a NULL terminated list of names, which must be destroyed with
8745 : * CSLDestroy()
8746 : * @since 3.13
8747 : */
8748 7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8749 : {
8750 7 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8751 7 : return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8752 : }
8753 :
8754 : /************************************************************************/
8755 : /* GDALAlgorithmArgGetAsBoolean() */
8756 : /************************************************************************/
8757 :
8758 : /** Return the argument value as a boolean.
8759 : *
8760 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8761 : *
8762 : * @param hArg Handle to an argument. Must NOT be null.
8763 : * @since 3.11
8764 : */
8765 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8766 : {
8767 8 : VALIDATE_POINTER1(hArg, __func__, false);
8768 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8769 : {
8770 1 : CPLError(CE_Failure, CPLE_AppDefined,
8771 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8772 : __func__);
8773 1 : return false;
8774 : }
8775 7 : return hArg->ptr->Get<bool>();
8776 : }
8777 :
8778 : /************************************************************************/
8779 : /* GDALAlgorithmArgGetAsString() */
8780 : /************************************************************************/
8781 :
8782 : /** Return the argument value as a string.
8783 : *
8784 : * Must only be called on arguments whose type is GAAT_STRING.
8785 : *
8786 : * @param hArg Handle to an argument. Must NOT be null.
8787 : * @return string whose lifetime is bound to hArg and which must not
8788 : * be freed.
8789 : * @since 3.11
8790 : */
8791 355 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8792 : {
8793 355 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8794 355 : if (hArg->ptr->GetType() != GAAT_STRING)
8795 : {
8796 1 : CPLError(CE_Failure, CPLE_AppDefined,
8797 : "%s must only be called on arguments of type GAAT_STRING",
8798 : __func__);
8799 1 : return nullptr;
8800 : }
8801 354 : return hArg->ptr->Get<std::string>().c_str();
8802 : }
8803 :
8804 : /************************************************************************/
8805 : /* GDALAlgorithmArgGetAsDatasetValue() */
8806 : /************************************************************************/
8807 :
8808 : /** Return the argument value as a GDALArgDatasetValueH.
8809 : *
8810 : * Must only be called on arguments whose type is GAAT_DATASET
8811 : *
8812 : * @param hArg Handle to an argument. Must NOT be null.
8813 : * @return handle to a GDALArgDatasetValue that must be released with
8814 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8815 : * the one of hArg.
8816 : * @since 3.11
8817 : */
8818 3241 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8819 : {
8820 3241 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8821 3241 : if (hArg->ptr->GetType() != GAAT_DATASET)
8822 : {
8823 1 : CPLError(CE_Failure, CPLE_AppDefined,
8824 : "%s must only be called on arguments of type GAAT_DATASET",
8825 : __func__);
8826 1 : return nullptr;
8827 : }
8828 3240 : return std::make_unique<GDALArgDatasetValueHS>(
8829 6480 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8830 3240 : .release();
8831 : }
8832 :
8833 : /************************************************************************/
8834 : /* GDALAlgorithmArgGetAsInteger() */
8835 : /************************************************************************/
8836 :
8837 : /** Return the argument value as a integer.
8838 : *
8839 : * Must only be called on arguments whose type is GAAT_INTEGER
8840 : *
8841 : * @param hArg Handle to an argument. Must NOT be null.
8842 : * @since 3.11
8843 : */
8844 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8845 : {
8846 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8847 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8848 : {
8849 1 : CPLError(CE_Failure, CPLE_AppDefined,
8850 : "%s must only be called on arguments of type GAAT_INTEGER",
8851 : __func__);
8852 1 : return 0;
8853 : }
8854 25 : return hArg->ptr->Get<int>();
8855 : }
8856 :
8857 : /************************************************************************/
8858 : /* GDALAlgorithmArgGetAsDouble() */
8859 : /************************************************************************/
8860 :
8861 : /** Return the argument value as a double.
8862 : *
8863 : * Must only be called on arguments whose type is GAAT_REAL
8864 : *
8865 : * @param hArg Handle to an argument. Must NOT be null.
8866 : * @since 3.11
8867 : */
8868 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8869 : {
8870 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8871 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8872 : {
8873 1 : CPLError(CE_Failure, CPLE_AppDefined,
8874 : "%s must only be called on arguments of type GAAT_REAL",
8875 : __func__);
8876 1 : return 0;
8877 : }
8878 7 : return hArg->ptr->Get<double>();
8879 : }
8880 :
8881 : /************************************************************************/
8882 : /* GDALAlgorithmArgGetAsStringList() */
8883 : /************************************************************************/
8884 :
8885 : /** Return the argument value as a string list.
8886 : *
8887 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8888 : *
8889 : * @param hArg Handle to an argument. Must NOT be null.
8890 : * @return a NULL terminated list of names, which must be destroyed with
8891 : * CSLDestroy()
8892 :
8893 : * @since 3.11
8894 : */
8895 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8896 : {
8897 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8898 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8899 : {
8900 1 : CPLError(CE_Failure, CPLE_AppDefined,
8901 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8902 : __func__);
8903 1 : return nullptr;
8904 : }
8905 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8906 3 : .StealList();
8907 : }
8908 :
8909 : /************************************************************************/
8910 : /* GDALAlgorithmArgGetAsIntegerList() */
8911 : /************************************************************************/
8912 :
8913 : /** Return the argument value as a integer list.
8914 : *
8915 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8916 : *
8917 : * @param hArg Handle to an argument. Must NOT be null.
8918 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8919 : * @since 3.11
8920 : */
8921 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8922 : size_t *pnCount)
8923 : {
8924 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8925 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8926 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8927 : {
8928 1 : CPLError(
8929 : CE_Failure, CPLE_AppDefined,
8930 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8931 : __func__);
8932 1 : *pnCount = 0;
8933 1 : return nullptr;
8934 : }
8935 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8936 7 : *pnCount = val.size();
8937 7 : return val.data();
8938 : }
8939 :
8940 : /************************************************************************/
8941 : /* GDALAlgorithmArgGetAsDoubleList() */
8942 : /************************************************************************/
8943 :
8944 : /** Return the argument value as a real list.
8945 : *
8946 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8947 : *
8948 : * @param hArg Handle to an argument. Must NOT be null.
8949 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8950 : * @since 3.11
8951 : */
8952 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8953 : size_t *pnCount)
8954 : {
8955 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8956 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8957 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8958 : {
8959 1 : CPLError(CE_Failure, CPLE_AppDefined,
8960 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8961 : __func__);
8962 1 : *pnCount = 0;
8963 1 : return nullptr;
8964 : }
8965 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8966 7 : *pnCount = val.size();
8967 7 : return val.data();
8968 : }
8969 :
8970 : /************************************************************************/
8971 : /* GDALAlgorithmArgSetAsBoolean() */
8972 : /************************************************************************/
8973 :
8974 : /** Set the value for a GAAT_BOOLEAN argument.
8975 : *
8976 : * It cannot be called several times for a given argument.
8977 : * Validation checks and other actions are run.
8978 : *
8979 : * @param hArg Handle to an argument. Must NOT be null.
8980 : * @param value value.
8981 : * @return true if success.
8982 : * @since 3.11
8983 : */
8984 :
8985 698 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8986 : {
8987 698 : VALIDATE_POINTER1(hArg, __func__, false);
8988 698 : return hArg->ptr->Set(value);
8989 : }
8990 :
8991 : /************************************************************************/
8992 : /* GDALAlgorithmArgSetAsString() */
8993 : /************************************************************************/
8994 :
8995 : /** Set the value for a GAAT_STRING argument.
8996 : *
8997 : * It cannot be called several times for a given argument.
8998 : * Validation checks and other actions are run.
8999 : *
9000 : * @param hArg Handle to an argument. Must NOT be null.
9001 : * @param value value (may be null)
9002 : * @return true if success.
9003 : * @since 3.11
9004 : */
9005 :
9006 3177 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
9007 : {
9008 3177 : VALIDATE_POINTER1(hArg, __func__, false);
9009 3177 : return hArg->ptr->Set(value ? value : "");
9010 : }
9011 :
9012 : /************************************************************************/
9013 : /* GDALAlgorithmArgSetAsInteger() */
9014 : /************************************************************************/
9015 :
9016 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
9017 : *
9018 : * It cannot be called several times for a given argument.
9019 : * Validation checks and other actions are run.
9020 : *
9021 : * @param hArg Handle to an argument. Must NOT be null.
9022 : * @param value value.
9023 : * @return true if success.
9024 : * @since 3.11
9025 : */
9026 :
9027 473 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
9028 : {
9029 473 : VALIDATE_POINTER1(hArg, __func__, false);
9030 473 : return hArg->ptr->Set(value);
9031 : }
9032 :
9033 : /************************************************************************/
9034 : /* GDALAlgorithmArgSetAsDouble() */
9035 : /************************************************************************/
9036 :
9037 : /** Set the value for a GAAT_REAL argument.
9038 : *
9039 : * It cannot be called several times for a given argument.
9040 : * Validation checks and other actions are run.
9041 : *
9042 : * @param hArg Handle to an argument. Must NOT be null.
9043 : * @param value value.
9044 : * @return true if success.
9045 : * @since 3.11
9046 : */
9047 :
9048 243 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
9049 : {
9050 243 : VALIDATE_POINTER1(hArg, __func__, false);
9051 243 : return hArg->ptr->Set(value);
9052 : }
9053 :
9054 : /************************************************************************/
9055 : /* GDALAlgorithmArgSetAsDatasetValue() */
9056 : /************************************************************************/
9057 :
9058 : /** Set the value for a GAAT_DATASET argument.
9059 : *
9060 : * It cannot be called several times for a given argument.
9061 : * Validation checks and other actions are run.
9062 : *
9063 : * @param hArg Handle to an argument. Must NOT be null.
9064 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
9065 : * @return true if success.
9066 : * @since 3.11
9067 : */
9068 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
9069 : GDALArgDatasetValueH value)
9070 : {
9071 2 : VALIDATE_POINTER1(hArg, __func__, false);
9072 2 : VALIDATE_POINTER1(value, __func__, false);
9073 2 : return hArg->ptr->SetFrom(*(value->ptr));
9074 : }
9075 :
9076 : /************************************************************************/
9077 : /* GDALAlgorithmArgSetDataset() */
9078 : /************************************************************************/
9079 :
9080 : /** Set dataset object, increasing its reference counter.
9081 : *
9082 : * @param hArg Handle to an argument. Must NOT be null.
9083 : * @param hDS Dataset object. May be null.
9084 : * @return true if success.
9085 : * @since 3.11
9086 : */
9087 :
9088 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
9089 : {
9090 2 : VALIDATE_POINTER1(hArg, __func__, false);
9091 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
9092 : }
9093 :
9094 : /************************************************************************/
9095 : /* GDALAlgorithmArgSetAsStringList() */
9096 : /************************************************************************/
9097 :
9098 : /** Set the value for a GAAT_STRING_LIST argument.
9099 : *
9100 : * It cannot be called several times for a given argument.
9101 : * Validation checks and other actions are run.
9102 : *
9103 : * @param hArg Handle to an argument. Must NOT be null.
9104 : * @param value value as a NULL terminated list (may be null)
9105 : * @return true if success.
9106 : * @since 3.11
9107 : */
9108 :
9109 788 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
9110 : {
9111 788 : VALIDATE_POINTER1(hArg, __func__, false);
9112 788 : return hArg->ptr->Set(
9113 1576 : static_cast<std::vector<std::string>>(CPLStringList(value)));
9114 : }
9115 :
9116 : /************************************************************************/
9117 : /* GDALAlgorithmArgSetAsIntegerList() */
9118 : /************************************************************************/
9119 :
9120 : /** Set the value for a GAAT_INTEGER_LIST argument.
9121 : *
9122 : * It cannot be called several times for a given argument.
9123 : * Validation checks and other actions are run.
9124 : *
9125 : * @param hArg Handle to an argument. Must NOT be null.
9126 : * @param nCount Number of values in pnValues.
9127 : * @param pnValues Pointer to an array of integer values of size nCount.
9128 : * @return true if success.
9129 : * @since 3.11
9130 : */
9131 105 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
9132 : const int *pnValues)
9133 : {
9134 105 : VALIDATE_POINTER1(hArg, __func__, false);
9135 105 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
9136 : }
9137 :
9138 : /************************************************************************/
9139 : /* GDALAlgorithmArgSetAsDoubleList() */
9140 : /************************************************************************/
9141 :
9142 : /** Set the value for a GAAT_REAL_LIST argument.
9143 : *
9144 : * It cannot be called several times for a given argument.
9145 : * Validation checks and other actions are run.
9146 : *
9147 : * @param hArg Handle to an argument. Must NOT be null.
9148 : * @param nCount Number of values in pnValues.
9149 : * @param pnValues Pointer to an array of double values of size nCount.
9150 : * @return true if success.
9151 : * @since 3.11
9152 : */
9153 159 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
9154 : const double *pnValues)
9155 : {
9156 159 : VALIDATE_POINTER1(hArg, __func__, false);
9157 159 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
9158 : }
9159 :
9160 : /************************************************************************/
9161 : /* GDALAlgorithmArgSetDatasets() */
9162 : /************************************************************************/
9163 :
9164 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9165 : *
9166 : * @param hArg Handle to an argument. Must NOT be null.
9167 : * @param nCount Number of values in pnValues.
9168 : * @param pahDS Pointer to an array of dataset of size nCount.
9169 : * @return true if success.
9170 : * @since 3.11
9171 : */
9172 :
9173 1288 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9174 : GDALDatasetH *pahDS)
9175 : {
9176 1288 : VALIDATE_POINTER1(hArg, __func__, false);
9177 2576 : std::vector<GDALArgDatasetValue> values;
9178 2602 : for (size_t i = 0; i < nCount; ++i)
9179 : {
9180 1314 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9181 : }
9182 1288 : return hArg->ptr->Set(std::move(values));
9183 : }
9184 :
9185 : /************************************************************************/
9186 : /* GDALAlgorithmArgSetDatasetNames() */
9187 : /************************************************************************/
9188 :
9189 : /** Set dataset names to a GAAT_DATASET_LIST argument.
9190 : *
9191 : * @param hArg Handle to an argument. Must NOT be null.
9192 : * @param names Dataset names as a NULL terminated list (may be null)
9193 : * @return true if success.
9194 : * @since 3.11
9195 : */
9196 :
9197 750 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9198 : {
9199 750 : VALIDATE_POINTER1(hArg, __func__, false);
9200 1500 : std::vector<GDALArgDatasetValue> values;
9201 1571 : for (size_t i = 0; names[i]; ++i)
9202 : {
9203 821 : values.emplace_back(names[i]);
9204 : }
9205 750 : return hArg->ptr->Set(std::move(values));
9206 : }
9207 :
9208 : /************************************************************************/
9209 : /* GDALArgDatasetValueCreate() */
9210 : /************************************************************************/
9211 :
9212 : /** Instantiate an empty GDALArgDatasetValue
9213 : *
9214 : * @return new handle to free with GDALArgDatasetValueRelease()
9215 : * @since 3.11
9216 : */
9217 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
9218 : {
9219 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
9220 : }
9221 :
9222 : /************************************************************************/
9223 : /* GDALArgDatasetValueRelease() */
9224 : /************************************************************************/
9225 :
9226 : /** Release a handle to a GDALArgDatasetValue
9227 : *
9228 : * @since 3.11
9229 : */
9230 3241 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9231 : {
9232 3241 : delete hValue;
9233 3241 : }
9234 :
9235 : /************************************************************************/
9236 : /* GDALArgDatasetValueGetName() */
9237 : /************************************************************************/
9238 :
9239 : /** Return the name component of the GDALArgDatasetValue
9240 : *
9241 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9242 : * @return string whose lifetime is bound to hAlg and which must not
9243 : * be freed.
9244 : * @since 3.11
9245 : */
9246 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9247 : {
9248 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9249 1 : return hValue->ptr->GetName().c_str();
9250 : }
9251 :
9252 : /************************************************************************/
9253 : /* GDALArgDatasetValueGetDatasetRef() */
9254 : /************************************************************************/
9255 :
9256 : /** Return the dataset component of the GDALArgDatasetValue.
9257 : *
9258 : * This does not modify the reference counter, hence the lifetime of the
9259 : * returned object is not guaranteed to exceed the one of hValue.
9260 : *
9261 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9262 : * @since 3.11
9263 : */
9264 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9265 : {
9266 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9267 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9268 : }
9269 :
9270 : /************************************************************************/
9271 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
9272 : /************************************************************************/
9273 :
9274 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
9275 : * reference count if not null. Once done with the dataset, the caller should
9276 : * call GDALReleaseDataset().
9277 : *
9278 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9279 : * @since 3.11
9280 : */
9281 : GDALDatasetH
9282 1069 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9283 : {
9284 1069 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9285 1069 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9286 : }
9287 :
9288 : /************************************************************************/
9289 : /* GDALArgDatasetValueSetName() */
9290 : /************************************************************************/
9291 :
9292 : /** Set dataset name
9293 : *
9294 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9295 : * @param pszName Dataset name. May be null.
9296 : * @since 3.11
9297 : */
9298 :
9299 1390 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9300 : const char *pszName)
9301 : {
9302 1390 : VALIDATE_POINTER0(hValue, __func__);
9303 1390 : hValue->ptr->Set(pszName ? pszName : "");
9304 : }
9305 :
9306 : /************************************************************************/
9307 : /* GDALArgDatasetValueSetDataset() */
9308 : /************************************************************************/
9309 :
9310 : /** Set dataset object, increasing its reference counter.
9311 : *
9312 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9313 : * @param hDS Dataset object. May be null.
9314 : * @since 3.11
9315 : */
9316 :
9317 767 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9318 : GDALDatasetH hDS)
9319 : {
9320 767 : VALIDATE_POINTER0(hValue, __func__);
9321 767 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9322 : }
|