Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: GDALAlgorithm class
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_error.h"
16 : #include "cpl_error_internal.h"
17 : #include "cpl_json.h"
18 : #include "cpl_levenshtein.h"
19 : #include "cpl_minixml.h"
20 : #include "cpl_multiproc.h"
21 :
22 : #include "gdalalgorithm.h"
23 : #include "gdalalg_abstract_pipeline.h"
24 : #include "gdal_priv.h"
25 : #include "gdal_thread_pool.h"
26 : #include "memdataset.h"
27 : #include "ogrsf_frmts.h"
28 : #include "ogr_spatialref.h"
29 : #include "vrtdataset.h"
30 :
31 : #include <algorithm>
32 : #include <cassert>
33 : #include <cerrno>
34 : #include <cmath>
35 : #include <cstdlib>
36 : #include <limits>
37 : #include <map>
38 : #include <type_traits>
39 : #include <string_view>
40 : #include <regex>
41 :
42 : #ifndef _
43 : #define _(x) (x)
44 : #endif
45 :
46 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
47 :
48 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
49 :
50 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
51 :
52 : //! @cond Doxygen_Suppress
53 : struct GDALAlgorithmArgHS
54 : {
55 : GDALAlgorithmArg *ptr = nullptr;
56 :
57 339706 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
58 : {
59 339706 : }
60 : };
61 :
62 : //! @endcond
63 :
64 : //! @cond Doxygen_Suppress
65 : struct GDALArgDatasetValueHS
66 : {
67 : GDALArgDatasetValue val{};
68 : GDALArgDatasetValue *ptr = nullptr;
69 :
70 1 : GDALArgDatasetValueHS() : ptr(&val)
71 : {
72 1 : }
73 :
74 3206 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
75 : {
76 3206 : }
77 :
78 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
79 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
80 : };
81 :
82 : //! @endcond
83 :
84 : /************************************************************************/
85 : /* GDALAlgorithmArgTypeIsList() */
86 : /************************************************************************/
87 :
88 412633 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
89 : {
90 412633 : switch (type)
91 : {
92 272064 : case GAAT_BOOLEAN:
93 : case GAAT_STRING:
94 : case GAAT_INTEGER:
95 : case GAAT_REAL:
96 : case GAAT_DATASET:
97 272064 : break;
98 :
99 140569 : case GAAT_STRING_LIST:
100 : case GAAT_INTEGER_LIST:
101 : case GAAT_REAL_LIST:
102 : case GAAT_DATASET_LIST:
103 140569 : return true;
104 : }
105 :
106 272064 : return false;
107 : }
108 :
109 : /************************************************************************/
110 : /* GDALAlgorithmArgTypeName() */
111 : /************************************************************************/
112 :
113 5480 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
114 : {
115 5480 : switch (type)
116 : {
117 1362 : case GAAT_BOOLEAN:
118 1362 : break;
119 1476 : case GAAT_STRING:
120 1476 : return "string";
121 379 : case GAAT_INTEGER:
122 379 : return "integer";
123 477 : case GAAT_REAL:
124 477 : return "real";
125 251 : case GAAT_DATASET:
126 251 : return "dataset";
127 1016 : case GAAT_STRING_LIST:
128 1016 : return "string_list";
129 93 : case GAAT_INTEGER_LIST:
130 93 : return "integer_list";
131 221 : case GAAT_REAL_LIST:
132 221 : return "real_list";
133 205 : case GAAT_DATASET_LIST:
134 205 : return "dataset_list";
135 : }
136 :
137 1362 : return "boolean";
138 : }
139 :
140 : /************************************************************************/
141 : /* GDALAlgorithmArgDatasetTypeName() */
142 : /************************************************************************/
143 :
144 22313 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
145 : {
146 22313 : std::string ret;
147 22313 : if ((type & GDAL_OF_RASTER) != 0)
148 12277 : ret = "raster";
149 22313 : if ((type & GDAL_OF_VECTOR) != 0)
150 : {
151 10830 : if (!ret.empty())
152 : {
153 1187 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
154 257 : ret += ", ";
155 : else
156 930 : ret += " or ";
157 : }
158 10830 : ret += "vector";
159 : }
160 22313 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
161 : {
162 576 : if (!ret.empty())
163 : {
164 316 : ret += " or ";
165 : }
166 576 : ret += "multidimensional raster";
167 : }
168 22313 : return ret;
169 : }
170 :
171 : /************************************************************************/
172 : /* GDALAlgorithmArgDecl() */
173 : /************************************************************************/
174 :
175 : // cppcheck-suppress uninitMemberVar
176 331756 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
177 : char chShortName,
178 : const std::string &description,
179 331756 : GDALAlgorithmArgType type)
180 : : m_longName(longName),
181 331756 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
182 : m_description(description), m_type(type),
183 663512 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
184 331756 : .toupper()),
185 995268 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
186 : {
187 331756 : if (m_type == GAAT_BOOLEAN)
188 : {
189 138411 : m_defaultValue = false;
190 : }
191 331756 : }
192 :
193 : /************************************************************************/
194 : /* GDALAlgorithmArgDecl::SetMinCount() */
195 : /************************************************************************/
196 :
197 18203 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
198 : {
199 18203 : if (!GDALAlgorithmArgTypeIsList(m_type))
200 : {
201 1 : CPLError(CE_Failure, CPLE_NotSupported,
202 : "SetMinCount() illegal on scalar argument '%s'",
203 1 : GetName().c_str());
204 : }
205 : else
206 : {
207 18202 : m_minCount = count;
208 : }
209 18203 : return *this;
210 : }
211 :
212 : /************************************************************************/
213 : /* GDALAlgorithmArgDecl::SetMaxCount() */
214 : /************************************************************************/
215 :
216 17235 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
217 : {
218 17235 : if (!GDALAlgorithmArgTypeIsList(m_type))
219 : {
220 1 : CPLError(CE_Failure, CPLE_NotSupported,
221 : "SetMaxCount() illegal on scalar argument '%s'",
222 1 : GetName().c_str());
223 : }
224 : else
225 : {
226 17234 : m_maxCount = count;
227 : }
228 17235 : return *this;
229 : }
230 :
231 : /************************************************************************/
232 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
233 : /************************************************************************/
234 :
235 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
236 :
237 : /************************************************************************/
238 : /* GDALAlgorithmArg::Set() */
239 : /************************************************************************/
240 :
241 1224 : bool GDALAlgorithmArg::Set(bool value)
242 : {
243 1224 : if (m_decl.GetType() != GAAT_BOOLEAN)
244 : {
245 14 : CPLError(
246 : CE_Failure, CPLE_AppDefined,
247 : "Calling Set(bool) on argument '%s' of type %s is not supported",
248 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
249 7 : return false;
250 : }
251 1217 : return SetInternal(value);
252 : }
253 :
254 4082 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
255 : {
256 4127 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
257 45 : value.front() == '@')
258 : {
259 2 : GByte *pabyData = nullptr;
260 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
261 2 : 10 * 1024 * 1024))
262 : {
263 : // Remove UTF-8 BOM
264 1 : size_t offset = 0;
265 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
266 1 : pabyData[2] == 0xBF)
267 : {
268 1 : offset = 3;
269 : }
270 1 : value = reinterpret_cast<const char *>(pabyData + offset);
271 1 : VSIFree(pabyData);
272 : }
273 : else
274 : {
275 1 : return false;
276 : }
277 : }
278 :
279 4081 : if (m_decl.IsRemoveSQLCommentsEnabled())
280 44 : value = CPLRemoveSQLComments(value);
281 :
282 4081 : return true;
283 : }
284 :
285 4098 : bool GDALAlgorithmArg::Set(const std::string &value)
286 : {
287 4098 : switch (m_decl.GetType())
288 : {
289 9 : case GAAT_BOOLEAN:
290 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
291 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
292 : {
293 4 : return Set(true);
294 : }
295 5 : else if (EQUAL(value.c_str(), "0") ||
296 4 : EQUAL(value.c_str(), "FALSE") ||
297 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
298 : {
299 4 : return Set(false);
300 : }
301 1 : break;
302 :
303 8 : case GAAT_INTEGER:
304 : case GAAT_INTEGER_LIST:
305 : {
306 8 : errno = 0;
307 8 : char *endptr = nullptr;
308 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
309 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
310 5 : endptr == value.c_str() + value.size())
311 : {
312 3 : if (m_decl.GetType() == GAAT_INTEGER)
313 3 : return Set(static_cast<int>(v));
314 : else
315 1 : return Set(std::vector<int>{static_cast<int>(v)});
316 : }
317 5 : break;
318 : }
319 :
320 5 : case GAAT_REAL:
321 : case GAAT_REAL_LIST:
322 : {
323 5 : char *endptr = nullptr;
324 5 : const double v = CPLStrtod(value.c_str(), &endptr);
325 5 : if (endptr == value.c_str() + value.size())
326 : {
327 3 : if (m_decl.GetType() == GAAT_REAL)
328 3 : return Set(v);
329 : else
330 1 : return Set(std::vector<double>{v});
331 : }
332 2 : break;
333 : }
334 :
335 4060 : case GAAT_STRING:
336 4060 : break;
337 :
338 1 : case GAAT_STRING_LIST:
339 2 : return Set(std::vector<std::string>{value});
340 :
341 15 : case GAAT_DATASET:
342 15 : return SetDatasetName(value);
343 :
344 0 : case GAAT_DATASET_LIST:
345 : {
346 0 : std::vector<GDALArgDatasetValue> v;
347 0 : v.resize(1);
348 0 : v[0].Set(value);
349 0 : return Set(std::move(v));
350 : }
351 : }
352 :
353 4068 : if (m_decl.GetType() != GAAT_STRING)
354 : {
355 16 : CPLError(CE_Failure, CPLE_AppDefined,
356 : "Calling Set(std::string) on argument '%s' of type %s is not "
357 : "supported",
358 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
359 8 : return false;
360 : }
361 :
362 4060 : std::string newValue(value);
363 4060 : return ProcessString(newValue) && SetInternal(newValue);
364 : }
365 :
366 872 : bool GDALAlgorithmArg::Set(int value)
367 : {
368 872 : if (m_decl.GetType() == GAAT_BOOLEAN)
369 : {
370 3 : if (value == 1)
371 1 : return Set(true);
372 2 : else if (value == 0)
373 1 : return Set(false);
374 : }
375 869 : else if (m_decl.GetType() == GAAT_REAL)
376 : {
377 3 : return Set(static_cast<double>(value));
378 : }
379 866 : else if (m_decl.GetType() == GAAT_STRING)
380 : {
381 2 : return Set(std::to_string(value));
382 : }
383 864 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
384 : {
385 1 : return Set(std::vector<int>{value});
386 : }
387 863 : else if (m_decl.GetType() == GAAT_REAL_LIST)
388 : {
389 1 : return Set(std::vector<double>{static_cast<double>(value)});
390 : }
391 862 : else if (m_decl.GetType() == GAAT_STRING_LIST)
392 : {
393 2 : return Set(std::vector<std::string>{std::to_string(value)});
394 : }
395 :
396 862 : if (m_decl.GetType() != GAAT_INTEGER)
397 : {
398 2 : CPLError(
399 : CE_Failure, CPLE_AppDefined,
400 : "Calling Set(int) on argument '%s' of type %s is not supported",
401 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
402 1 : return false;
403 : }
404 861 : return SetInternal(value);
405 : }
406 :
407 291 : bool GDALAlgorithmArg::Set(double value)
408 : {
409 294 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
410 294 : value <= INT_MAX && static_cast<int>(value) == value)
411 : {
412 2 : return Set(static_cast<int>(value));
413 : }
414 289 : else if (m_decl.GetType() == GAAT_STRING)
415 : {
416 2 : return Set(std::to_string(value));
417 : }
418 289 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
419 289 : value <= INT_MAX && static_cast<int>(value) == value)
420 : {
421 1 : return Set(std::vector<int>{static_cast<int>(value)});
422 : }
423 286 : else if (m_decl.GetType() == GAAT_REAL_LIST)
424 : {
425 0 : return Set(std::vector<double>{value});
426 : }
427 286 : else if (m_decl.GetType() == GAAT_STRING_LIST)
428 : {
429 2 : return Set(std::vector<std::string>{std::to_string(value)});
430 : }
431 285 : else if (m_decl.GetType() != GAAT_REAL)
432 : {
433 6 : CPLError(
434 : CE_Failure, CPLE_AppDefined,
435 : "Calling Set(double) on argument '%s' of type %s is not supported",
436 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
437 3 : return false;
438 : }
439 282 : return SetInternal(value);
440 : }
441 :
442 6292 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
443 : {
444 6295 : if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
445 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
446 : {
447 3 : CPLError(
448 : CE_Failure, CPLE_AppDefined,
449 : "Dataset object '%s' is created by algorithm and cannot be set "
450 : "as an input.",
451 3 : arg->GetName().c_str());
452 3 : return false;
453 : }
454 6289 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
455 : {
456 8 : CPLError(CE_Failure, CPLE_AppDefined,
457 : "Dataset%s '%s' must be provided by name, not as object.",
458 8 : arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
459 4 : return false;
460 : }
461 :
462 6285 : return true;
463 : }
464 :
465 33 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
466 : {
467 58 : if (m_decl.GetType() != GAAT_DATASET &&
468 25 : m_decl.GetType() != GAAT_DATASET_LIST)
469 : {
470 2 : CPLError(CE_Failure, CPLE_AppDefined,
471 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
472 : "is not supported",
473 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
474 1 : return false;
475 : }
476 32 : if (!CheckCanSetDatasetObject(this))
477 2 : return false;
478 30 : m_explicitlySet = true;
479 30 : if (m_decl.GetType() == GAAT_DATASET)
480 : {
481 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
482 6 : val.Set(ds);
483 : }
484 : else
485 : {
486 24 : CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
487 24 : auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
488 24 : val.resize(1);
489 24 : val[0].Set(ds);
490 : }
491 30 : return RunAllActions();
492 : }
493 :
494 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
495 : {
496 3 : if (m_decl.GetType() != GAAT_DATASET)
497 : {
498 2 : CPLError(CE_Failure, CPLE_AppDefined,
499 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
500 : "is not supported",
501 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
502 1 : return false;
503 : }
504 2 : if (!CheckCanSetDatasetObject(this))
505 1 : return false;
506 1 : m_explicitlySet = true;
507 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
508 1 : val.Set(std::move(ds));
509 1 : return RunAllActions();
510 : }
511 :
512 564 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
513 : {
514 564 : if (m_decl.GetType() != GAAT_DATASET)
515 : {
516 2 : CPLError(CE_Failure, CPLE_AppDefined,
517 : "Calling SetDatasetName() on argument '%s' of type %s is "
518 : "not supported",
519 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
520 1 : return false;
521 : }
522 563 : m_explicitlySet = true;
523 563 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
524 563 : return RunAllActions();
525 : }
526 :
527 986 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
528 : {
529 986 : if (m_decl.GetType() != GAAT_DATASET)
530 : {
531 2 : CPLError(CE_Failure, CPLE_AppDefined,
532 : "Calling SetFrom() on argument '%s' of type %s is "
533 : "not supported",
534 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
535 1 : return false;
536 : }
537 985 : if (!CheckCanSetDatasetObject(this))
538 1 : return false;
539 984 : m_explicitlySet = true;
540 984 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
541 984 : return RunAllActions();
542 : }
543 :
544 1092 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
545 : {
546 1092 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
547 : {
548 3 : std::vector<int> v_i;
549 4 : for (const std::string &s : value)
550 : {
551 3 : errno = 0;
552 3 : char *endptr = nullptr;
553 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
554 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
555 2 : endptr == s.c_str() + s.size())
556 : {
557 1 : v_i.push_back(static_cast<int>(v));
558 : }
559 : else
560 : {
561 2 : break;
562 : }
563 : }
564 3 : if (v_i.size() == value.size())
565 1 : return Set(v_i);
566 : }
567 1089 : else if (m_decl.GetType() == GAAT_REAL_LIST)
568 : {
569 2 : std::vector<double> v_d;
570 3 : for (const std::string &s : value)
571 : {
572 2 : char *endptr = nullptr;
573 2 : const double v = CPLStrtod(s.c_str(), &endptr);
574 2 : if (endptr == s.c_str() + s.size())
575 : {
576 1 : v_d.push_back(v);
577 : }
578 : else
579 : {
580 1 : break;
581 : }
582 : }
583 2 : if (v_d.size() == value.size())
584 1 : return Set(v_d);
585 : }
586 2172 : else if ((m_decl.GetType() == GAAT_INTEGER ||
587 2169 : m_decl.GetType() == GAAT_REAL ||
588 3258 : m_decl.GetType() == GAAT_STRING) &&
589 5 : value.size() == 1)
590 : {
591 4 : return Set(value[0]);
592 : }
593 1083 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
594 : {
595 30 : std::vector<GDALArgDatasetValue> dsVector;
596 46 : for (const std::string &s : value)
597 31 : dsVector.emplace_back(s);
598 15 : return Set(std::move(dsVector));
599 : }
600 :
601 1071 : if (m_decl.GetType() != GAAT_STRING_LIST)
602 : {
603 10 : CPLError(CE_Failure, CPLE_AppDefined,
604 : "Calling Set(const std::vector<std::string> &) on argument "
605 : "'%s' of type %s is not supported",
606 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
607 5 : return false;
608 : }
609 :
610 2113 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
611 1047 : m_decl.IsRemoveSQLCommentsEnabled())
612 : {
613 38 : std::vector<std::string> newValue(value);
614 41 : for (auto &s : newValue)
615 : {
616 22 : if (!ProcessString(s))
617 0 : return false;
618 : }
619 19 : return SetInternal(newValue);
620 : }
621 : else
622 : {
623 1047 : return SetInternal(value);
624 : }
625 : }
626 :
627 172 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
628 : {
629 172 : if (m_decl.GetType() == GAAT_REAL_LIST)
630 : {
631 2 : std::vector<double> v_d;
632 2 : for (int i : value)
633 1 : v_d.push_back(i);
634 1 : return Set(v_d);
635 : }
636 171 : else if (m_decl.GetType() == GAAT_STRING_LIST)
637 : {
638 2 : std::vector<std::string> v_s;
639 3 : for (int i : value)
640 2 : v_s.push_back(std::to_string(i));
641 1 : return Set(v_s);
642 : }
643 338 : else if ((m_decl.GetType() == GAAT_INTEGER ||
644 334 : m_decl.GetType() == GAAT_REAL ||
645 506 : m_decl.GetType() == GAAT_STRING) &&
646 5 : value.size() == 1)
647 : {
648 3 : return Set(value[0]);
649 : }
650 :
651 167 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
652 : {
653 6 : CPLError(CE_Failure, CPLE_AppDefined,
654 : "Calling Set(const std::vector<int> &) on argument '%s' of "
655 : "type %s is not supported",
656 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
657 3 : return false;
658 : }
659 164 : return SetInternal(value);
660 : }
661 :
662 268 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
663 : {
664 268 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
665 : {
666 2 : std::vector<int> v_i;
667 3 : for (double d : value)
668 : {
669 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
670 : {
671 1 : v_i.push_back(static_cast<int>(d));
672 : }
673 : else
674 : {
675 : break;
676 : }
677 : }
678 2 : if (v_i.size() == value.size())
679 1 : return Set(v_i);
680 : }
681 266 : else if (m_decl.GetType() == GAAT_STRING_LIST)
682 : {
683 2 : std::vector<std::string> v_s;
684 3 : for (double d : value)
685 2 : v_s.push_back(std::to_string(d));
686 1 : return Set(v_s);
687 : }
688 529 : else if ((m_decl.GetType() == GAAT_INTEGER ||
689 527 : m_decl.GetType() == GAAT_REAL ||
690 793 : m_decl.GetType() == GAAT_STRING) &&
691 3 : value.size() == 1)
692 : {
693 3 : return Set(value[0]);
694 : }
695 :
696 263 : if (m_decl.GetType() != GAAT_REAL_LIST)
697 : {
698 4 : CPLError(CE_Failure, CPLE_AppDefined,
699 : "Calling Set(const std::vector<double> &) on argument '%s' of "
700 : "type %s is not supported",
701 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
702 2 : return false;
703 : }
704 261 : return SetInternal(value);
705 : }
706 :
707 3437 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
708 : {
709 3437 : if (m_decl.GetType() != GAAT_DATASET_LIST)
710 : {
711 2 : CPLError(CE_Failure, CPLE_AppDefined,
712 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
713 : "argument '%s' of type %s is not supported",
714 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
715 1 : return false;
716 : }
717 3436 : m_explicitlySet = true;
718 3436 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
719 3436 : return RunAllActions();
720 : }
721 :
722 : GDALAlgorithmArg &
723 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
724 : {
725 0 : Set(std::move(value));
726 0 : return *this;
727 : }
728 :
729 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
730 : {
731 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
732 1 : return Set(value.exportToWkt(apszOptions));
733 : }
734 :
735 4254 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
736 : {
737 4254 : if (m_decl.GetType() != other.GetType())
738 : {
739 2 : CPLError(CE_Failure, CPLE_AppDefined,
740 : "Calling SetFrom() on argument '%s' of type %s whereas "
741 : "other argument type is %s is not supported",
742 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
743 : GDALAlgorithmArgTypeName(other.GetType()));
744 1 : return false;
745 : }
746 :
747 4253 : switch (m_decl.GetType())
748 : {
749 91 : case GAAT_BOOLEAN:
750 91 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
751 91 : break;
752 807 : case GAAT_STRING:
753 1614 : *std::get<std::string *>(m_value) =
754 807 : *std::get<std::string *>(other.m_value);
755 807 : break;
756 6 : case GAAT_INTEGER:
757 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
758 6 : break;
759 1 : case GAAT_REAL:
760 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
761 1 : break;
762 980 : case GAAT_DATASET:
763 980 : return SetFrom(other.Get<GDALArgDatasetValue>());
764 58 : case GAAT_STRING_LIST:
765 116 : *std::get<std::vector<std::string> *>(m_value) =
766 58 : *std::get<std::vector<std::string> *>(other.m_value);
767 58 : break;
768 1 : case GAAT_INTEGER_LIST:
769 2 : *std::get<std::vector<int> *>(m_value) =
770 1 : *std::get<std::vector<int> *>(other.m_value);
771 1 : break;
772 1 : case GAAT_REAL_LIST:
773 2 : *std::get<std::vector<double> *>(m_value) =
774 1 : *std::get<std::vector<double> *>(other.m_value);
775 1 : break;
776 2308 : case GAAT_DATASET_LIST:
777 : {
778 2308 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
779 2313 : for (const auto &val :
780 6934 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
781 : {
782 4626 : GDALArgDatasetValue v;
783 2313 : v.SetFrom(val);
784 2313 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
785 2313 : ->push_back(std::move(v));
786 : }
787 2308 : break;
788 : }
789 : }
790 3273 : m_explicitlySet = true;
791 3273 : return RunAllActions();
792 : }
793 :
794 : /************************************************************************/
795 : /* GDALAlgorithmArg::RunAllActions() */
796 : /************************************************************************/
797 :
798 16197 : bool GDALAlgorithmArg::RunAllActions()
799 : {
800 16197 : if (!RunValidationActions())
801 147 : return false;
802 16050 : RunActions();
803 16050 : return true;
804 : }
805 :
806 : /************************************************************************/
807 : /* GDALAlgorithmArg::RunActions() */
808 : /************************************************************************/
809 :
810 16051 : void GDALAlgorithmArg::RunActions()
811 : {
812 16347 : for (const auto &f : m_actions)
813 296 : f();
814 16051 : }
815 :
816 : /************************************************************************/
817 : /* GDALAlgorithmArg::ValidateChoice() */
818 : /************************************************************************/
819 :
820 : // Returns the canonical value if matching a valid choice, or empty string
821 : // otherwise.
822 2670 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
823 : {
824 15464 : for (const std::string &choice : GetChoices())
825 : {
826 15346 : if (EQUAL(value.c_str(), choice.c_str()))
827 : {
828 2552 : return choice;
829 : }
830 : }
831 :
832 190 : for (const std::string &choice : GetHiddenChoices())
833 : {
834 172 : if (EQUAL(value.c_str(), choice.c_str()))
835 : {
836 100 : return choice;
837 : }
838 : }
839 :
840 36 : std::string expected;
841 220 : for (const auto &choice : GetChoices())
842 : {
843 202 : if (!expected.empty())
844 184 : expected += ", ";
845 202 : expected += '\'';
846 202 : expected += choice;
847 202 : expected += '\'';
848 : }
849 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
850 : {
851 6 : return "?";
852 : }
853 24 : CPLError(CE_Failure, CPLE_IllegalArg,
854 : "Invalid value '%s' for string argument '%s'. Should be "
855 : "one among %s.",
856 12 : value.c_str(), GetName().c_str(), expected.c_str());
857 12 : return std::string();
858 : }
859 :
860 : /************************************************************************/
861 : /* GDALAlgorithmArg::ValidateIntRange() */
862 : /************************************************************************/
863 :
864 2728 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
865 : {
866 2728 : bool ret = true;
867 :
868 2728 : const auto [minVal, minValIsIncluded] = GetMinValue();
869 2728 : if (!std::isnan(minVal))
870 : {
871 2078 : if (minValIsIncluded && val < minVal)
872 : {
873 3 : CPLError(CE_Failure, CPLE_IllegalArg,
874 : "Value of argument '%s' is %d, but should be >= %d",
875 3 : GetName().c_str(), val, static_cast<int>(minVal));
876 3 : ret = false;
877 : }
878 2075 : else if (!minValIsIncluded && val <= minVal)
879 : {
880 1 : CPLError(CE_Failure, CPLE_IllegalArg,
881 : "Value of argument '%s' is %d, but should be > %d",
882 1 : GetName().c_str(), val, static_cast<int>(minVal));
883 1 : ret = false;
884 : }
885 : }
886 :
887 2728 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
888 2728 : if (!std::isnan(maxVal))
889 : {
890 :
891 430 : if (maxValIsIncluded && val > maxVal)
892 : {
893 1 : CPLError(CE_Failure, CPLE_IllegalArg,
894 : "Value of argument '%s' is %d, but should be <= %d",
895 1 : GetName().c_str(), val, static_cast<int>(maxVal));
896 1 : ret = false;
897 : }
898 429 : else if (!maxValIsIncluded && val >= maxVal)
899 : {
900 1 : CPLError(CE_Failure, CPLE_IllegalArg,
901 : "Value of argument '%s' is %d, but should be < %d",
902 1 : GetName().c_str(), val, static_cast<int>(maxVal));
903 1 : ret = false;
904 : }
905 : }
906 :
907 2728 : return ret;
908 : }
909 :
910 : /************************************************************************/
911 : /* GDALAlgorithmArg::ValidateRealRange() */
912 : /************************************************************************/
913 :
914 2138 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
915 : {
916 2138 : bool ret = true;
917 :
918 2138 : const auto [minVal, minValIsIncluded] = GetMinValue();
919 2138 : if (!std::isnan(minVal))
920 : {
921 216 : if (minValIsIncluded && !(val >= minVal))
922 : {
923 11 : CPLError(CE_Failure, CPLE_IllegalArg,
924 : "Value of argument '%s' is %g, but should be >= %g",
925 11 : GetName().c_str(), val, minVal);
926 11 : ret = false;
927 : }
928 205 : else if (!minValIsIncluded && !(val > minVal))
929 : {
930 4 : CPLError(CE_Failure, CPLE_IllegalArg,
931 : "Value of argument '%s' is %g, but should be > %g",
932 4 : GetName().c_str(), val, minVal);
933 4 : ret = false;
934 : }
935 : }
936 :
937 2138 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
938 2138 : if (!std::isnan(maxVal))
939 : {
940 :
941 58 : if (maxValIsIncluded && !(val <= maxVal))
942 : {
943 2 : CPLError(CE_Failure, CPLE_IllegalArg,
944 : "Value of argument '%s' is %g, but should be <= %g",
945 2 : GetName().c_str(), val, maxVal);
946 2 : ret = false;
947 : }
948 56 : else if (!maxValIsIncluded && !(val < maxVal))
949 : {
950 1 : CPLError(CE_Failure, CPLE_IllegalArg,
951 : "Value of argument '%s' is %g, but should be < %g",
952 1 : GetName().c_str(), val, maxVal);
953 1 : ret = false;
954 : }
955 : }
956 :
957 2138 : return ret;
958 : }
959 :
960 : /************************************************************************/
961 : /* CheckDuplicateValues() */
962 : /************************************************************************/
963 :
964 : template <class T>
965 92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
966 : const std::vector<T> &values)
967 : {
968 184 : auto tmpValues = values;
969 92 : bool bHasDupValues = false;
970 : if constexpr (std::is_floating_point_v<T>)
971 : {
972 : // Avoid undefined behavior with NaN values
973 4 : std::sort(tmpValues.begin(), tmpValues.end(),
974 21 : [](T a, T b)
975 : {
976 21 : if (std::isnan(a) && !std::isnan(b))
977 3 : return true;
978 18 : if (std::isnan(b))
979 10 : return false;
980 8 : return a < b;
981 : });
982 :
983 : bHasDupValues =
984 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
985 6 : [](T a, T b)
986 : {
987 6 : if (std::isnan(a) && std::isnan(b))
988 1 : return true;
989 5 : return a == b;
990 8 : }) != tmpValues.end();
991 : }
992 : else
993 : {
994 88 : std::sort(tmpValues.begin(), tmpValues.end());
995 88 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
996 176 : tmpValues.end()) != tmpValues.end();
997 : }
998 92 : if (bHasDupValues)
999 : {
1000 10 : CPLError(CE_Failure, CPLE_AppDefined,
1001 : "'%s' must be a list of unique values.",
1002 10 : arg->GetName().c_str());
1003 10 : return false;
1004 : }
1005 82 : return true;
1006 : }
1007 :
1008 : /************************************************************************/
1009 : /* GDALAlgorithmArg::RunValidationActions() */
1010 : /************************************************************************/
1011 :
1012 35760 : bool GDALAlgorithmArg::RunValidationActions()
1013 : {
1014 35760 : bool ret = true;
1015 :
1016 35760 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1017 : {
1018 1751 : auto &val = Get<std::string>();
1019 3502 : std::string validVal = ValidateChoice(val);
1020 1751 : if (validVal.empty())
1021 7 : ret = false;
1022 : else
1023 1744 : val = std::move(validVal);
1024 : }
1025 34009 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1026 : {
1027 659 : auto &values = Get<std::vector<std::string>>();
1028 1578 : for (std::string &val : values)
1029 : {
1030 1838 : std::string validVal = ValidateChoice(val);
1031 919 : if (validVal.empty())
1032 5 : ret = false;
1033 : else
1034 914 : val = std::move(validVal);
1035 : }
1036 : }
1037 :
1038 : const auto CheckMinCharCount =
1039 994 : [this, &ret](const std::string &val, int nMinCharCount)
1040 : {
1041 982 : if (val.size() < static_cast<size_t>(nMinCharCount))
1042 : {
1043 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1044 : "Value of argument '%s' is '%s', but should have at least "
1045 : "%d character%s",
1046 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1047 : nMinCharCount > 1 ? "s" : "");
1048 6 : ret = false;
1049 : }
1050 36742 : };
1051 :
1052 : const auto CheckMaxCharCount =
1053 12877 : [this, &ret](const std::string &val, int nMaxCharCount)
1054 : {
1055 12875 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1056 : {
1057 2 : CPLError(
1058 : CE_Failure, CPLE_IllegalArg,
1059 : "Value of argument '%s' is '%s', but should have no more than "
1060 : "%d character%s",
1061 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1062 : nMaxCharCount > 1 ? "s" : "");
1063 1 : ret = false;
1064 : }
1065 48635 : };
1066 :
1067 35760 : switch (GetType())
1068 : {
1069 2758 : case GAAT_BOOLEAN:
1070 2758 : break;
1071 :
1072 10071 : case GAAT_STRING:
1073 : {
1074 10071 : const auto &val = Get<std::string>();
1075 10071 : const int nMinCharCount = GetMinCharCount();
1076 10071 : if (nMinCharCount > 0)
1077 : {
1078 900 : CheckMinCharCount(val, nMinCharCount);
1079 : }
1080 :
1081 10071 : const int nMaxCharCount = GetMaxCharCount();
1082 10071 : CheckMaxCharCount(val, nMaxCharCount);
1083 10071 : break;
1084 : }
1085 :
1086 2335 : case GAAT_STRING_LIST:
1087 : {
1088 2335 : const int nMinCharCount = GetMinCharCount();
1089 2335 : const int nMaxCharCount = GetMaxCharCount();
1090 2335 : const auto &values = Get<std::vector<std::string>>();
1091 5139 : for (const auto &val : values)
1092 : {
1093 2804 : if (nMinCharCount > 0)
1094 82 : CheckMinCharCount(val, nMinCharCount);
1095 2804 : CheckMaxCharCount(val, nMaxCharCount);
1096 : }
1097 :
1098 2409 : if (!GetDuplicateValuesAllowed() &&
1099 74 : !CheckDuplicateValues(this, values))
1100 2 : ret = false;
1101 2335 : break;
1102 : }
1103 :
1104 2030 : case GAAT_INTEGER:
1105 : {
1106 2030 : ret = ValidateIntRange(Get<int>()) && ret;
1107 2030 : break;
1108 : }
1109 :
1110 344 : case GAAT_INTEGER_LIST:
1111 : {
1112 344 : const auto &values = Get<std::vector<int>>();
1113 1042 : for (int v : values)
1114 698 : ret = ValidateIntRange(v) && ret;
1115 :
1116 347 : if (!GetDuplicateValuesAllowed() &&
1117 3 : !CheckDuplicateValues(this, values))
1118 1 : ret = false;
1119 344 : break;
1120 : }
1121 :
1122 549 : case GAAT_REAL:
1123 : {
1124 549 : ret = ValidateRealRange(Get<double>()) && ret;
1125 549 : break;
1126 : }
1127 :
1128 576 : case GAAT_REAL_LIST:
1129 : {
1130 576 : const auto &values = Get<std::vector<double>>();
1131 2165 : for (double v : values)
1132 1589 : ret = ValidateRealRange(v) && ret;
1133 :
1134 580 : if (!GetDuplicateValuesAllowed() &&
1135 4 : !CheckDuplicateValues(this, values))
1136 2 : ret = false;
1137 576 : break;
1138 : }
1139 :
1140 5807 : case GAAT_DATASET:
1141 5807 : break;
1142 :
1143 11290 : case GAAT_DATASET_LIST:
1144 : {
1145 11290 : if (!GetDuplicateValuesAllowed())
1146 : {
1147 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1148 22 : std::vector<std::string> aosValues;
1149 34 : for (const auto &v : values)
1150 : {
1151 23 : const GDALDataset *poDS = v.GetDatasetRef();
1152 23 : if (poDS)
1153 : {
1154 16 : auto poDriver = poDS->GetDriver();
1155 : // The dataset name for a MEM driver is not relevant,
1156 : // so use the pointer address
1157 32 : if ((poDriver &&
1158 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1159 8 : poDS->GetDescription()[0] == 0)
1160 : {
1161 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1162 : }
1163 : else
1164 : {
1165 8 : aosValues.push_back(poDS->GetDescription());
1166 : }
1167 : }
1168 : else
1169 : {
1170 7 : aosValues.push_back(v.GetName());
1171 : }
1172 : }
1173 11 : if (!CheckDuplicateValues(this, aosValues))
1174 5 : ret = false;
1175 : }
1176 11290 : break;
1177 : }
1178 : }
1179 :
1180 35760 : if (GDALAlgorithmArgTypeIsList(GetType()))
1181 : {
1182 14545 : int valueCount = 0;
1183 14545 : if (GetType() == GAAT_STRING_LIST)
1184 : {
1185 2335 : valueCount =
1186 2335 : static_cast<int>(Get<std::vector<std::string>>().size());
1187 : }
1188 12210 : else if (GetType() == GAAT_INTEGER_LIST)
1189 : {
1190 344 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1191 : }
1192 11866 : else if (GetType() == GAAT_REAL_LIST)
1193 : {
1194 576 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1195 : }
1196 11290 : else if (GetType() == GAAT_DATASET_LIST)
1197 : {
1198 11290 : valueCount = static_cast<int>(
1199 11290 : Get<std::vector<GDALArgDatasetValue>>().size());
1200 : }
1201 :
1202 14545 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1203 : {
1204 14 : ReportError(CE_Failure, CPLE_AppDefined,
1205 : "%d value%s been specified for argument '%s', "
1206 : "whereas exactly %d %s expected.",
1207 : valueCount, valueCount > 1 ? "s have" : " has",
1208 7 : GetName().c_str(), GetMinCount(),
1209 7 : GetMinCount() > 1 ? "were" : "was");
1210 7 : ret = false;
1211 : }
1212 14538 : else if (valueCount < GetMinCount())
1213 : {
1214 6 : ReportError(CE_Failure, CPLE_AppDefined,
1215 : "Only %d value%s been specified for argument '%s', "
1216 : "whereas at least %d %s expected.",
1217 : valueCount, valueCount > 1 ? "s have" : " has",
1218 3 : GetName().c_str(), GetMinCount(),
1219 3 : GetMinCount() > 1 ? "were" : "was");
1220 3 : ret = false;
1221 : }
1222 14535 : else if (valueCount > GetMaxCount())
1223 : {
1224 2 : ReportError(CE_Failure, CPLE_AppDefined,
1225 : "%d value%s been specified for argument '%s', "
1226 : "whereas at most %d %s expected.",
1227 : valueCount, valueCount > 1 ? "s have" : " has",
1228 1 : GetName().c_str(), GetMaxCount(),
1229 1 : GetMaxCount() > 1 ? "were" : "was");
1230 1 : ret = false;
1231 : }
1232 : }
1233 :
1234 35760 : if (ret)
1235 : {
1236 42626 : for (const auto &f : m_validationActions)
1237 : {
1238 6930 : if (!f())
1239 92 : ret = false;
1240 : }
1241 : }
1242 :
1243 35760 : return ret;
1244 : }
1245 :
1246 : /************************************************************************/
1247 : /* GDALAlgorithmArg::ReportError() */
1248 : /************************************************************************/
1249 :
1250 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1251 : const char *fmt, ...) const
1252 : {
1253 : va_list args;
1254 11 : va_start(args, fmt);
1255 11 : if (m_owner)
1256 : {
1257 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1258 22 : CPLString().vPrintf(fmt, args).c_str());
1259 : }
1260 : else
1261 : {
1262 0 : CPLError(eErrClass, err_no, "%s",
1263 0 : CPLString().vPrintf(fmt, args).c_str());
1264 : }
1265 11 : va_end(args);
1266 11 : }
1267 :
1268 : /************************************************************************/
1269 : /* GDALAlgorithmArg::GetEscapedString() */
1270 : /************************************************************************/
1271 :
1272 : /* static */
1273 134 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1274 : {
1275 150 : if (s.find_first_of("\" \\,") != std::string::npos &&
1276 8 : !(s.size() > 4 &&
1277 8 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1278 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1279 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1280 : {
1281 12 : return std::string("\"")
1282 : .append(
1283 12 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1284 6 : .append("\"");
1285 : }
1286 : else
1287 : {
1288 128 : return s;
1289 : }
1290 : }
1291 :
1292 : /************************************************************************/
1293 : /* GDALAlgorithmArg::Serialize() */
1294 : /************************************************************************/
1295 :
1296 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1297 : bool absolutePath) const
1298 : {
1299 39 : serializedArg.clear();
1300 :
1301 39 : if (!IsExplicitlySet())
1302 : {
1303 0 : return false;
1304 : }
1305 :
1306 78 : std::string ret = "--";
1307 39 : ret += GetName();
1308 39 : if (GetType() == GAAT_BOOLEAN)
1309 : {
1310 0 : serializedArg = std::move(ret);
1311 0 : return true;
1312 : }
1313 :
1314 5 : const auto AddListValueSeparator = [this, &ret]()
1315 : {
1316 1 : if (GetPackedValuesAllowed())
1317 : {
1318 0 : ret += ',';
1319 : }
1320 : else
1321 : {
1322 1 : ret += " --";
1323 1 : ret += GetName();
1324 1 : ret += ' ';
1325 : }
1326 40 : };
1327 :
1328 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1329 : {
1330 : VSIStatBufL sStat;
1331 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1332 0 : !CPLIsFilenameRelative(filename.c_str()))
1333 0 : return filename;
1334 0 : char *pszCWD = CPLGetCurrentDir();
1335 0 : if (!pszCWD)
1336 0 : return filename;
1337 : const auto absPath =
1338 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1339 0 : CPLFree(pszCWD);
1340 0 : return absPath;
1341 : };
1342 :
1343 39 : ret += ' ';
1344 39 : switch (GetType())
1345 : {
1346 0 : case GAAT_BOOLEAN:
1347 0 : break;
1348 8 : case GAAT_STRING:
1349 : {
1350 8 : const auto &val = Get<std::string>();
1351 8 : ret += GetEscapedString(val);
1352 8 : break;
1353 : }
1354 0 : case GAAT_INTEGER:
1355 : {
1356 0 : ret += CPLSPrintf("%d", Get<int>());
1357 0 : break;
1358 : }
1359 0 : case GAAT_REAL:
1360 : {
1361 0 : ret += CPLSPrintf("%.17g", Get<double>());
1362 0 : break;
1363 : }
1364 2 : case GAAT_DATASET:
1365 : {
1366 2 : const auto &val = Get<GDALArgDatasetValue>();
1367 2 : const auto &str = val.GetName();
1368 2 : if (str.empty())
1369 : {
1370 0 : return false;
1371 : }
1372 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1373 2 : break;
1374 : }
1375 4 : case GAAT_STRING_LIST:
1376 : {
1377 4 : const auto &vals = Get<std::vector<std::string>>();
1378 8 : for (size_t i = 0; i < vals.size(); ++i)
1379 : {
1380 4 : if (i > 0)
1381 1 : AddListValueSeparator();
1382 4 : ret += GetEscapedString(vals[i]);
1383 : }
1384 4 : break;
1385 : }
1386 0 : case GAAT_INTEGER_LIST:
1387 : {
1388 0 : const auto &vals = Get<std::vector<int>>();
1389 0 : for (size_t i = 0; i < vals.size(); ++i)
1390 : {
1391 0 : if (i > 0)
1392 0 : AddListValueSeparator();
1393 0 : ret += CPLSPrintf("%d", vals[i]);
1394 : }
1395 0 : break;
1396 : }
1397 0 : case GAAT_REAL_LIST:
1398 : {
1399 0 : const auto &vals = Get<std::vector<double>>();
1400 0 : for (size_t i = 0; i < vals.size(); ++i)
1401 : {
1402 0 : if (i > 0)
1403 0 : AddListValueSeparator();
1404 0 : ret += CPLSPrintf("%.17g", vals[i]);
1405 : }
1406 0 : break;
1407 : }
1408 25 : case GAAT_DATASET_LIST:
1409 : {
1410 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1411 49 : for (size_t i = 0; i < vals.size(); ++i)
1412 : {
1413 25 : if (i > 0)
1414 0 : AddListValueSeparator();
1415 25 : const auto &val = vals[i];
1416 25 : const auto &str = val.GetName();
1417 25 : if (str.empty())
1418 : {
1419 1 : return false;
1420 : }
1421 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1422 24 : : str);
1423 : }
1424 24 : break;
1425 : }
1426 : }
1427 :
1428 38 : serializedArg = std::move(ret);
1429 38 : return true;
1430 : }
1431 :
1432 : /************************************************************************/
1433 : /* ~GDALInConstructionAlgorithmArg() */
1434 : /************************************************************************/
1435 :
1436 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1437 :
1438 : /************************************************************************/
1439 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1440 : /************************************************************************/
1441 :
1442 : GDALInConstructionAlgorithmArg &
1443 63155 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1444 : {
1445 63155 : m_decl.AddAlias(alias);
1446 63155 : if (m_owner)
1447 63155 : m_owner->AddAliasFor(this, alias);
1448 63155 : return *this;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1453 : /************************************************************************/
1454 :
1455 : GDALInConstructionAlgorithmArg &
1456 17095 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1457 : {
1458 17095 : m_decl.AddHiddenAlias(alias);
1459 17095 : if (m_owner)
1460 17095 : m_owner->AddAliasFor(this, alias);
1461 17095 : return *this;
1462 : }
1463 :
1464 : /************************************************************************/
1465 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1466 : /************************************************************************/
1467 :
1468 : GDALInConstructionAlgorithmArg &
1469 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1470 : {
1471 48 : m_decl.AddShortNameAlias(shortNameAlias);
1472 48 : if (m_owner)
1473 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1474 48 : return *this;
1475 : }
1476 :
1477 : /************************************************************************/
1478 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1479 : /************************************************************************/
1480 :
1481 21552 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1482 : {
1483 21552 : m_decl.SetPositional();
1484 21552 : if (m_owner)
1485 21552 : m_owner->SetPositional(this);
1486 21552 : return *this;
1487 : }
1488 :
1489 : /************************************************************************/
1490 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1491 : /************************************************************************/
1492 :
1493 1319 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1494 2638 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1495 1319 : m_nameSet(true)
1496 : {
1497 1319 : if (m_poDS)
1498 1319 : m_poDS->Reference();
1499 1319 : }
1500 :
1501 : /************************************************************************/
1502 : /* GDALArgDatasetValue::Set() */
1503 : /************************************************************************/
1504 :
1505 2283 : void GDALArgDatasetValue::Set(const std::string &name)
1506 : {
1507 2283 : Close();
1508 2283 : m_name = name;
1509 2283 : m_nameSet = true;
1510 2283 : if (m_ownerArg)
1511 2278 : m_ownerArg->NotifyValueSet();
1512 2283 : }
1513 :
1514 : /************************************************************************/
1515 : /* GDALArgDatasetValue::Set() */
1516 : /************************************************************************/
1517 :
1518 1986 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1519 : {
1520 1986 : Close();
1521 1986 : m_poDS = poDS.release();
1522 1986 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1523 1986 : m_nameSet = true;
1524 1986 : if (m_ownerArg)
1525 1846 : m_ownerArg->NotifyValueSet();
1526 1986 : }
1527 :
1528 : /************************************************************************/
1529 : /* GDALArgDatasetValue::Set() */
1530 : /************************************************************************/
1531 :
1532 7945 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1533 : {
1534 7945 : Close();
1535 7945 : m_poDS = poDS;
1536 7945 : if (m_poDS)
1537 7076 : m_poDS->Reference();
1538 7945 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1539 7945 : m_nameSet = true;
1540 7945 : if (m_ownerArg)
1541 3339 : m_ownerArg->NotifyValueSet();
1542 7945 : }
1543 :
1544 : /************************************************************************/
1545 : /* GDALArgDatasetValue::SetFrom() */
1546 : /************************************************************************/
1547 :
1548 3297 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1549 : {
1550 3297 : Close();
1551 3297 : m_name = other.m_name;
1552 3297 : m_nameSet = other.m_nameSet;
1553 3297 : m_poDS = other.m_poDS;
1554 3297 : if (m_poDS)
1555 2311 : m_poDS->Reference();
1556 3297 : }
1557 :
1558 : /************************************************************************/
1559 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1560 : /************************************************************************/
1561 :
1562 31342 : GDALArgDatasetValue::~GDALArgDatasetValue()
1563 : {
1564 31342 : Close();
1565 31342 : }
1566 :
1567 : /************************************************************************/
1568 : /* GDALArgDatasetValue::Close() */
1569 : /************************************************************************/
1570 :
1571 51979 : bool GDALArgDatasetValue::Close()
1572 : {
1573 51979 : bool ret = true;
1574 51979 : if (m_poDS && m_poDS->Dereference() == 0)
1575 : {
1576 3269 : ret = m_poDS->Close() == CE_None;
1577 3269 : delete m_poDS;
1578 : }
1579 51979 : m_poDS = nullptr;
1580 51979 : return ret;
1581 : }
1582 :
1583 : /************************************************************************/
1584 : /* GDALArgDatasetValue::operator=() */
1585 : /************************************************************************/
1586 :
1587 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1588 : {
1589 2 : Close();
1590 2 : m_poDS = other.m_poDS;
1591 2 : m_name = other.m_name;
1592 2 : m_nameSet = other.m_nameSet;
1593 2 : other.m_poDS = nullptr;
1594 2 : other.m_name.clear();
1595 2 : other.m_nameSet = false;
1596 2 : return *this;
1597 : }
1598 :
1599 : /************************************************************************/
1600 : /* GDALArgDatasetValue::GetDataset() */
1601 : /************************************************************************/
1602 :
1603 1051 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1604 : {
1605 1051 : if (m_poDS)
1606 1051 : m_poDS->Reference();
1607 1051 : return m_poDS;
1608 : }
1609 :
1610 : /************************************************************************/
1611 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1612 : /************************************************************************/
1613 :
1614 3116 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1615 3116 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1616 : {
1617 3116 : other.m_poDS = nullptr;
1618 3116 : other.m_name.clear();
1619 3116 : }
1620 :
1621 : /************************************************************************/
1622 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1623 : /************************************************************************/
1624 :
1625 3292 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1626 : bool noneAllowed, const std::vector<std::string> &specialValues)
1627 : {
1628 3292 : if (GetType() != GAAT_STRING)
1629 : {
1630 1 : CPLError(CE_Failure, CPLE_AppDefined,
1631 : "SetIsCRSArg() can only be called on a String argument");
1632 1 : return *this;
1633 : }
1634 : AddValidationAction(
1635 711 : [this, noneAllowed, specialValues]()
1636 : {
1637 : const std::string &osVal =
1638 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1639 352 : ->Get<std::string>();
1640 352 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1641 0 : return true;
1642 :
1643 691 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1644 339 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1645 691 : specialValues.end())
1646 : {
1647 331 : OGRSpatialReference oSRS;
1648 331 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1649 : {
1650 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1651 : "Invalid value for '%s' argument",
1652 7 : GetName().c_str());
1653 7 : return false;
1654 : }
1655 : }
1656 345 : return true;
1657 3291 : });
1658 :
1659 : SetAutoCompleteFunction(
1660 40 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1661 : {
1662 10 : bool bIsRaster = false;
1663 10 : OGREnvelope sDatasetLongLatEnv;
1664 20 : std::string osCelestialBodyName;
1665 10 : if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1666 : {
1667 10 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1668 10 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1669 : {
1670 : auto &val =
1671 10 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1672 10 : if (val.size() == 1)
1673 : {
1674 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1675 : auto poDS = std::unique_ptr<GDALDataset>(
1676 4 : GDALDataset::Open(val[0].GetName().c_str()));
1677 2 : if (poDS)
1678 : {
1679 2 : bIsRaster = poDS->GetRasterCount() != 0;
1680 2 : if (auto poCRS = poDS->GetSpatialRef())
1681 : {
1682 : const char *pszCelestialBodyName =
1683 2 : poCRS->GetCelestialBodyName();
1684 2 : if (pszCelestialBodyName)
1685 2 : osCelestialBodyName = pszCelestialBodyName;
1686 :
1687 2 : if (!pszCelestialBodyName ||
1688 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1689 : {
1690 0 : OGRSpatialReference oLongLat;
1691 0 : oLongLat.CopyGeogCSFrom(poCRS);
1692 0 : oLongLat.SetAxisMappingStrategy(
1693 : OAMS_TRADITIONAL_GIS_ORDER);
1694 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1695 0 : &oLongLat);
1696 : }
1697 : else
1698 : {
1699 2 : poDS->GetExtentWGS84LongLat(
1700 2 : &sDatasetLongLatEnv);
1701 : }
1702 : }
1703 : }
1704 : }
1705 : }
1706 : }
1707 :
1708 : const auto IsCRSCompatible =
1709 42959 : [bIsRaster, &sDatasetLongLatEnv,
1710 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1711 : {
1712 42959 : if (!sDatasetLongLatEnv.IsInit())
1713 30685 : return true;
1714 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1715 11834 : !(bIsRaster &&
1716 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1717 11652 : crsInfo->dfWestLongitudeDeg <
1718 11652 : crsInfo->dfEastLongitudeDeg &&
1719 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1720 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1721 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1722 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1723 329 : ((!osCelestialBodyName.empty() &&
1724 658 : crsInfo->pszCelestialBodyName &&
1725 329 : osCelestialBodyName ==
1726 329 : crsInfo->pszCelestialBodyName) ||
1727 0 : (osCelestialBodyName.empty() &&
1728 12274 : !crsInfo->pszCelestialBodyName));
1729 10 : };
1730 :
1731 10 : std::vector<std::string> oRet;
1732 10 : if (noneAllowed)
1733 0 : oRet.push_back("none");
1734 10 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1735 10 : if (!currentValue.empty())
1736 : {
1737 : const CPLStringList aosTokens(
1738 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1739 7 : int nCount = 0;
1740 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1741 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1742 : nullptr, &nCount),
1743 14 : OSRDestroyCRSInfoList);
1744 14 : std::string osCode;
1745 :
1746 14 : std::vector<const OSRCRSInfo *> candidates;
1747 46270 : for (int i = 0; i < nCount; ++i)
1748 : {
1749 46263 : const auto *entry = (pCRSList.get())[i];
1750 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1751 : {
1752 49425 : if (aosTokens.size() == 1 ||
1753 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1754 : {
1755 12666 : if (candidates.empty())
1756 7 : osCode = entry->pszCode;
1757 12666 : candidates.push_back(entry);
1758 : }
1759 : }
1760 : }
1761 7 : if (candidates.size() == 1)
1762 : {
1763 1 : oRet.push_back(std::move(osCode));
1764 : }
1765 : else
1766 : {
1767 6 : if (sDatasetLongLatEnv.IsInit())
1768 : {
1769 2 : std::sort(
1770 : candidates.begin(), candidates.end(),
1771 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1772 : {
1773 2999 : const double dfXa =
1774 2999 : a->dfWestLongitudeDeg >
1775 2999 : a->dfEastLongitudeDeg
1776 2999 : ? a->dfWestLongitudeDeg -
1777 0 : a->dfEastLongitudeDeg
1778 2999 : : (180 - a->dfWestLongitudeDeg) +
1779 2999 : (a->dfEastLongitudeDeg - -180);
1780 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1781 2999 : a->dfSouthLatitudeDeg;
1782 2999 : const double dfXb =
1783 2999 : b->dfWestLongitudeDeg >
1784 2999 : b->dfEastLongitudeDeg
1785 2999 : ? b->dfWestLongitudeDeg -
1786 0 : b->dfEastLongitudeDeg
1787 2999 : : (180 - b->dfWestLongitudeDeg) +
1788 2999 : (b->dfEastLongitudeDeg - -180);
1789 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1790 2999 : b->dfSouthLatitudeDeg;
1791 2999 : const double diffArea =
1792 2999 : dfXa * dfYa - dfXb * dfYb;
1793 2999 : if (diffArea < 0)
1794 279 : return true;
1795 2720 : if (diffArea == 0)
1796 : {
1797 2506 : if (std::string_view(a->pszName) ==
1798 2506 : b->pszName)
1799 : {
1800 57 : if (a->eType ==
1801 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1802 13 : b->eType !=
1803 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1804 13 : return true;
1805 44 : if (a->eType ==
1806 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1807 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1808 9 : return true;
1809 35 : return false;
1810 : }
1811 4898 : return std::string_view(a->pszCode) <
1812 4898 : b->pszCode;
1813 : }
1814 214 : return false;
1815 : });
1816 : }
1817 :
1818 12671 : for (const auto *entry : candidates)
1819 : {
1820 25330 : std::string val = std::string(entry->pszCode)
1821 12665 : .append(" -- ")
1822 25330 : .append(entry->pszName);
1823 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1824 1294 : val.append(" (geographic 2D)");
1825 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1826 446 : val.append(" (geographic 3D)");
1827 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1828 397 : val.append(" (geocentric)");
1829 12665 : oRet.push_back(std::move(val));
1830 : }
1831 : }
1832 : }
1833 10 : if (currentValue.empty() || oRet.empty())
1834 : {
1835 : const CPLStringList aosAuthorities(
1836 6 : OSRGetAuthorityListFromDatabase());
1837 18 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1838 : {
1839 15 : int nCount = 0;
1840 15 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1841 : pszAuth, nullptr, &nCount));
1842 15 : if (nCount)
1843 12 : oRet.push_back(std::string(pszAuth).append(":"));
1844 : }
1845 : }
1846 20 : return oRet;
1847 3291 : });
1848 :
1849 3291 : return *this;
1850 : }
1851 :
1852 : /************************************************************************/
1853 : /* GDALAlgorithm::GDALAlgorithm() */
1854 : /************************************************************************/
1855 :
1856 21621 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1857 : const std::string &description,
1858 21621 : const std::string &helpURL)
1859 : : m_name(name), m_description(description), m_helpURL(helpURL),
1860 43082 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1861 21621 : ? "https://gdal.org" + m_helpURL
1862 64448 : : m_helpURL)
1863 : {
1864 : auto &helpArg =
1865 : AddArg("help", 'h', _("Display help message and exit"),
1866 43242 : &m_helpRequested)
1867 21621 : .SetHiddenForAPI()
1868 43242 : .SetCategory(GAAC_COMMON)
1869 14 : .AddAction([this]()
1870 21621 : { m_specialActionRequested = m_calledFromCommandLine; });
1871 : auto &helpDocArg =
1872 : AddArg("help-doc", 0,
1873 : _("Display help message for use by documentation"),
1874 43242 : &m_helpDocRequested)
1875 21621 : .SetHidden()
1876 12 : .AddAction([this]()
1877 21621 : { m_specialActionRequested = m_calledFromCommandLine; });
1878 : auto &jsonUsageArg =
1879 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1880 43242 : &m_JSONUsageRequested)
1881 21621 : .SetHiddenForAPI()
1882 43242 : .SetCategory(GAAC_COMMON)
1883 4 : .AddAction([this]()
1884 21621 : { m_specialActionRequested = m_calledFromCommandLine; });
1885 43242 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1886 43242 : .SetMetaVar("<KEY>=<VALUE>")
1887 21621 : .SetHiddenForAPI()
1888 43242 : .SetCategory(GAAC_COMMON)
1889 : .AddAction(
1890 2 : [this]()
1891 : {
1892 2 : ReportError(
1893 : CE_Warning, CPLE_AppDefined,
1894 : "Configuration options passed with the 'config' argument "
1895 : "are ignored");
1896 21621 : });
1897 :
1898 21621 : AddValidationAction(
1899 13155 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1900 : {
1901 6774 : if (!m_calledFromCommandLine && m_specialActionRequested)
1902 : {
1903 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1904 : {
1905 0 : if (arg->IsExplicitlySet())
1906 : {
1907 0 : ReportError(CE_Failure, CPLE_AppDefined,
1908 : "'%s' argument only available when called "
1909 : "from command line",
1910 0 : arg->GetName().c_str());
1911 0 : return false;
1912 : }
1913 : }
1914 : }
1915 6774 : return true;
1916 : });
1917 21621 : }
1918 :
1919 : /************************************************************************/
1920 : /* GDALAlgorithm::~GDALAlgorithm() */
1921 : /************************************************************************/
1922 :
1923 : GDALAlgorithm::~GDALAlgorithm() = default;
1924 :
1925 : /************************************************************************/
1926 : /* GDALAlgorithm::ParseArgument() */
1927 : /************************************************************************/
1928 :
1929 3114 : bool GDALAlgorithm::ParseArgument(
1930 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1931 : std::map<
1932 : GDALAlgorithmArg *,
1933 : std::variant<std::vector<std::string>, std::vector<int>,
1934 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1935 : &inConstructionValues)
1936 : {
1937 : const bool isListArg =
1938 3114 : GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1939 3114 : if (arg->IsExplicitlySet() && !isListArg)
1940 : {
1941 : // Hack for "gdal info" to be able to pass an opened raster dataset
1942 : // by "gdal raster info" to the "gdal vector info" algorithm.
1943 4 : if (arg->SkipIfAlreadySet())
1944 : {
1945 1 : arg->SetSkipIfAlreadySet(false);
1946 1 : return true;
1947 : }
1948 :
1949 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1950 : "Argument '%s' has already been specified.", name.c_str());
1951 3 : return false;
1952 : }
1953 :
1954 3178 : if (!arg->GetRepeatedArgAllowed() &&
1955 68 : cpl::contains(inConstructionValues, arg))
1956 : {
1957 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1958 : "Argument '%s' has already been specified.", name.c_str());
1959 1 : return false;
1960 : }
1961 :
1962 3109 : switch (arg->GetType())
1963 : {
1964 320 : case GAAT_BOOLEAN:
1965 : {
1966 320 : if (value.empty() || value == "true")
1967 318 : return arg->Set(true);
1968 2 : else if (value == "false")
1969 1 : return arg->Set(false);
1970 : else
1971 : {
1972 1 : ReportError(
1973 : CE_Failure, CPLE_IllegalArg,
1974 : "Invalid value '%s' for boolean argument '%s'. Should be "
1975 : "'true' or 'false'.",
1976 : value.c_str(), name.c_str());
1977 1 : return false;
1978 : }
1979 : }
1980 :
1981 769 : case GAAT_STRING:
1982 : {
1983 769 : return arg->Set(value);
1984 : }
1985 :
1986 356 : case GAAT_INTEGER:
1987 : {
1988 356 : errno = 0;
1989 356 : char *endptr = nullptr;
1990 356 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1991 355 : if (errno == 0 && endptr &&
1992 711 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1993 : val <= INT_MAX)
1994 : {
1995 353 : return arg->Set(static_cast<int>(val));
1996 : }
1997 : else
1998 : {
1999 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2000 : "Expected integer value for argument '%s', "
2001 : "but got '%s'.",
2002 : name.c_str(), value.c_str());
2003 3 : return false;
2004 : }
2005 : }
2006 :
2007 32 : case GAAT_REAL:
2008 : {
2009 32 : char *endptr = nullptr;
2010 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
2011 32 : if (endptr != value.c_str() + value.size())
2012 : {
2013 1 : ReportError(
2014 : CE_Failure, CPLE_IllegalArg,
2015 : "Expected real value for argument '%s', but got '%s'.",
2016 : name.c_str(), value.c_str());
2017 1 : return false;
2018 : }
2019 31 : return arg->Set(dfValue);
2020 : }
2021 :
2022 547 : case GAAT_DATASET:
2023 : {
2024 547 : return arg->SetDatasetName(value);
2025 : }
2026 :
2027 268 : case GAAT_STRING_LIST:
2028 : {
2029 : const CPLStringList aosTokens(
2030 268 : arg->GetPackedValuesAllowed()
2031 171 : ? CSLTokenizeString2(value.c_str(), ",",
2032 : CSLT_HONOURSTRINGS |
2033 : CSLT_PRESERVEQUOTES)
2034 439 : : CSLAddString(nullptr, value.c_str()));
2035 268 : if (!cpl::contains(inConstructionValues, arg))
2036 : {
2037 244 : inConstructionValues[arg] = std::vector<std::string>();
2038 : }
2039 : auto &valueVector =
2040 268 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2041 568 : for (const char *v : aosTokens)
2042 : {
2043 300 : valueVector.push_back(v);
2044 : }
2045 268 : if (arg->GetMaxCount() == 1)
2046 : {
2047 3 : bool ret = arg->Set(std::move(valueVector));
2048 3 : inConstructionValues.erase(inConstructionValues.find(arg));
2049 3 : return ret;
2050 : }
2051 :
2052 265 : break;
2053 : }
2054 :
2055 65 : case GAAT_INTEGER_LIST:
2056 : {
2057 : const CPLStringList aosTokens(
2058 65 : arg->GetPackedValuesAllowed()
2059 65 : ? CSLTokenizeString2(
2060 : value.c_str(), ",",
2061 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2062 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2063 130 : : CSLAddString(nullptr, value.c_str()));
2064 65 : if (!cpl::contains(inConstructionValues, arg))
2065 : {
2066 61 : inConstructionValues[arg] = std::vector<int>();
2067 : }
2068 : auto &valueVector =
2069 65 : std::get<std::vector<int>>(inConstructionValues[arg]);
2070 199 : for (const char *v : aosTokens)
2071 : {
2072 140 : errno = 0;
2073 140 : char *endptr = nullptr;
2074 140 : const auto val = std::strtol(v, &endptr, 10);
2075 140 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2076 136 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2077 : {
2078 134 : valueVector.push_back(static_cast<int>(val));
2079 : }
2080 : else
2081 : {
2082 6 : ReportError(
2083 : CE_Failure, CPLE_IllegalArg,
2084 : "Expected list of integer value for argument '%s', "
2085 : "but got '%s'.",
2086 : name.c_str(), value.c_str());
2087 6 : return false;
2088 : }
2089 : }
2090 59 : if (arg->GetMaxCount() == 1)
2091 : {
2092 2 : bool ret = arg->Set(std::move(valueVector));
2093 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2094 2 : return ret;
2095 : }
2096 :
2097 57 : break;
2098 : }
2099 :
2100 102 : case GAAT_REAL_LIST:
2101 : {
2102 : const CPLStringList aosTokens(
2103 102 : arg->GetPackedValuesAllowed()
2104 102 : ? CSLTokenizeString2(
2105 : value.c_str(), ",",
2106 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2107 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2108 204 : : CSLAddString(nullptr, value.c_str()));
2109 102 : if (!cpl::contains(inConstructionValues, arg))
2110 : {
2111 100 : inConstructionValues[arg] = std::vector<double>();
2112 : }
2113 : auto &valueVector =
2114 102 : std::get<std::vector<double>>(inConstructionValues[arg]);
2115 410 : for (const char *v : aosTokens)
2116 : {
2117 312 : char *endptr = nullptr;
2118 312 : double dfValue = CPLStrtod(v, &endptr);
2119 312 : if (strlen(v) == 0 || endptr != v + strlen(v))
2120 : {
2121 4 : ReportError(
2122 : CE_Failure, CPLE_IllegalArg,
2123 : "Expected list of real value for argument '%s', "
2124 : "but got '%s'.",
2125 : name.c_str(), value.c_str());
2126 4 : return false;
2127 : }
2128 308 : valueVector.push_back(dfValue);
2129 : }
2130 98 : if (arg->GetMaxCount() == 1)
2131 : {
2132 2 : bool ret = arg->Set(std::move(valueVector));
2133 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2134 2 : return ret;
2135 : }
2136 :
2137 96 : break;
2138 : }
2139 :
2140 650 : case GAAT_DATASET_LIST:
2141 : {
2142 650 : if (!cpl::contains(inConstructionValues, arg))
2143 : {
2144 637 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2145 : }
2146 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2147 650 : inConstructionValues[arg]);
2148 650 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2149 : {
2150 12 : valueVector.push_back(GDALArgDatasetValue(value));
2151 : }
2152 : else
2153 : {
2154 : const CPLStringList aosTokens(
2155 638 : arg->GetPackedValuesAllowed()
2156 6 : ? CSLTokenizeString2(value.c_str(), ",",
2157 : CSLT_HONOURSTRINGS |
2158 : CSLT_STRIPLEADSPACES)
2159 1282 : : CSLAddString(nullptr, value.c_str()));
2160 1279 : for (const char *v : aosTokens)
2161 : {
2162 641 : valueVector.push_back(GDALArgDatasetValue(v));
2163 : }
2164 : }
2165 650 : if (arg->GetMaxCount() == 1)
2166 : {
2167 529 : bool ret = arg->Set(std::move(valueVector));
2168 529 : inConstructionValues.erase(inConstructionValues.find(arg));
2169 529 : return ret;
2170 : }
2171 :
2172 121 : break;
2173 : }
2174 : }
2175 :
2176 539 : return true;
2177 : }
2178 :
2179 : /************************************************************************/
2180 : /* GDALAlgorithm::ParseCommandLineArguments() */
2181 : /************************************************************************/
2182 :
2183 2072 : bool GDALAlgorithm::ParseCommandLineArguments(
2184 : const std::vector<std::string> &args)
2185 : {
2186 2072 : if (m_parsedSubStringAlreadyCalled)
2187 : {
2188 6 : ReportError(CE_Failure, CPLE_AppDefined,
2189 : "ParseCommandLineArguments() can only be called once per "
2190 : "instance.");
2191 6 : return false;
2192 : }
2193 2066 : m_parsedSubStringAlreadyCalled = true;
2194 :
2195 : // AWS like syntax supported too (not advertized)
2196 2066 : if (args.size() == 1 && args[0] == "help")
2197 : {
2198 1 : auto arg = GetArg("help");
2199 1 : assert(arg);
2200 1 : arg->Set(true);
2201 1 : arg->RunActions();
2202 1 : return true;
2203 : }
2204 :
2205 2065 : if (HasSubAlgorithms())
2206 : {
2207 445 : if (args.empty())
2208 : {
2209 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2210 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2211 2 : return false;
2212 : }
2213 443 : if (!args[0].empty() && args[0][0] == '-')
2214 : {
2215 : // go on argument parsing
2216 : }
2217 : else
2218 : {
2219 440 : const auto nCounter = CPLGetErrorCounter();
2220 440 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2221 440 : if (m_selectedSubAlgHolder)
2222 : {
2223 437 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2224 437 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2225 437 : m_referencePath);
2226 437 : m_selectedSubAlg->m_executionForStreamOutput =
2227 437 : m_executionForStreamOutput;
2228 437 : m_selectedSubAlg->m_calledFromCommandLine =
2229 437 : m_calledFromCommandLine;
2230 437 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2231 437 : m_skipValidationInParseCommandLine;
2232 437 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2233 874 : std::vector<std::string>(args.begin() + 1, args.end()));
2234 437 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2235 437 : return bRet;
2236 : }
2237 : else
2238 : {
2239 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2240 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2241 : {
2242 2 : ReportError(CE_Failure, CPLE_AppDefined,
2243 2 : "Unknown command: '%s'", args[0].c_str());
2244 : }
2245 3 : return false;
2246 : }
2247 : }
2248 : }
2249 :
2250 : std::map<
2251 : GDALAlgorithmArg *,
2252 : std::variant<std::vector<std::string>, std::vector<int>,
2253 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2254 3246 : inConstructionValues;
2255 :
2256 3246 : std::vector<std::string> lArgs(args);
2257 1623 : bool helpValueRequested = false;
2258 4741 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2259 : {
2260 3218 : const auto &strArg = lArgs[i];
2261 3218 : GDALAlgorithmArg *arg = nullptr;
2262 3218 : std::string name;
2263 3218 : std::string value;
2264 3218 : bool hasValue = false;
2265 3218 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2266 5 : helpValueRequested = true;
2267 3218 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2268 : {
2269 2046 : const auto equalPos = strArg.find('=');
2270 4092 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2271 2046 : : strArg;
2272 2046 : const std::string nameWithoutDash = name.substr(2);
2273 2046 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2274 2099 : if (m_arbitraryLongNameArgsAllowed &&
2275 2099 : iterArg == m_mapLongNameToArg.end())
2276 : {
2277 16 : GetArg(nameWithoutDash);
2278 16 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2279 : }
2280 2046 : if (iterArg == m_mapLongNameToArg.end())
2281 : {
2282 : const std::string bestCandidate =
2283 26 : GetSuggestionForArgumentName(nameWithoutDash);
2284 26 : if (!bestCandidate.empty())
2285 : {
2286 2 : ReportError(CE_Failure, CPLE_IllegalArg,
2287 : "Option '%s' is unknown. Do you mean '--%s'?",
2288 : name.c_str(), bestCandidate.c_str());
2289 : }
2290 : else
2291 : {
2292 24 : ReportError(CE_Failure, CPLE_IllegalArg,
2293 : "Option '%s' is unknown.", name.c_str());
2294 : }
2295 26 : return false;
2296 : }
2297 2020 : arg = iterArg->second;
2298 2020 : if (equalPos != std::string::npos)
2299 : {
2300 441 : hasValue = true;
2301 441 : value = strArg.substr(equalPos + 1);
2302 : }
2303 : }
2304 1246 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2305 74 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2306 : {
2307 143 : for (size_t j = 1; j < strArg.size(); ++j)
2308 : {
2309 74 : name.clear();
2310 74 : name += strArg[j];
2311 74 : const auto iterArg = m_mapShortNameToArg.find(name);
2312 74 : if (iterArg == m_mapShortNameToArg.end())
2313 : {
2314 5 : const std::string nameWithoutDash = strArg.substr(1);
2315 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2316 10 : m_mapLongNameToArg.end())
2317 : {
2318 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2319 : "Short name option '%s' is unknown. Do you "
2320 : "mean '--%s' (with leading double dash) ?",
2321 : name.c_str(), nameWithoutDash.c_str());
2322 : }
2323 : else
2324 : {
2325 : const std::string bestCandidate =
2326 8 : GetSuggestionForArgumentName(nameWithoutDash);
2327 4 : if (!bestCandidate.empty())
2328 : {
2329 1 : ReportError(
2330 : CE_Failure, CPLE_IllegalArg,
2331 : "Short name option '%s' is unknown. Do you "
2332 : "mean '--%s' (with leading double dash) ?",
2333 : name.c_str(), bestCandidate.c_str());
2334 : }
2335 : else
2336 : {
2337 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2338 : "Short name option '%s' is unknown.",
2339 : name.c_str());
2340 : }
2341 : }
2342 5 : return false;
2343 : }
2344 69 : arg = iterArg->second;
2345 69 : if (strArg.size() > 2)
2346 : {
2347 0 : if (arg->GetType() != GAAT_BOOLEAN)
2348 : {
2349 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2350 : "Invalid argument '%s'. Option '%s' is not "
2351 : "a boolean option.",
2352 : strArg.c_str(), name.c_str());
2353 0 : return false;
2354 : }
2355 :
2356 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2357 0 : return false;
2358 : }
2359 : }
2360 69 : if (strArg.size() > 2)
2361 : {
2362 0 : lArgs.erase(lArgs.begin() + i);
2363 0 : continue;
2364 : }
2365 : }
2366 : else
2367 : {
2368 1098 : ++i;
2369 1098 : continue;
2370 : }
2371 2089 : CPLAssert(arg);
2372 :
2373 2089 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2374 : {
2375 321 : if (!hasValue)
2376 : {
2377 318 : hasValue = true;
2378 318 : value = "true";
2379 : }
2380 : }
2381 :
2382 2089 : if (!hasValue)
2383 : {
2384 1330 : if (i + 1 == lArgs.size())
2385 : {
2386 30 : if (m_parseForAutoCompletion)
2387 : {
2388 24 : lArgs.erase(lArgs.begin() + i);
2389 24 : break;
2390 : }
2391 6 : ReportError(
2392 : CE_Failure, CPLE_IllegalArg,
2393 : "Expected value for argument '%s', but ran short of tokens",
2394 : name.c_str());
2395 6 : return false;
2396 : }
2397 1300 : value = lArgs[i + 1];
2398 1300 : lArgs.erase(lArgs.begin() + i + 1);
2399 : }
2400 :
2401 2059 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2402 39 : return false;
2403 :
2404 2020 : lArgs.erase(lArgs.begin() + i);
2405 : }
2406 :
2407 1547 : if (m_specialActionRequested)
2408 : {
2409 23 : return true;
2410 : }
2411 :
2412 1994 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2413 : {
2414 1960 : for (auto &[arg, value] : inConstructionValues)
2415 : {
2416 494 : if (arg->GetType() == GAAT_STRING_LIST)
2417 : {
2418 238 : if (!arg->Set(std::get<std::vector<std::string>>(
2419 238 : inConstructionValues[arg])))
2420 : {
2421 34 : return false;
2422 : }
2423 : }
2424 256 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2425 : {
2426 54 : if (!arg->Set(
2427 54 : std::get<std::vector<int>>(inConstructionValues[arg])))
2428 : {
2429 4 : return false;
2430 : }
2431 : }
2432 202 : else if (arg->GetType() == GAAT_REAL_LIST)
2433 : {
2434 94 : if (!arg->Set(std::get<std::vector<double>>(
2435 94 : inConstructionValues[arg])))
2436 : {
2437 10 : return false;
2438 : }
2439 : }
2440 108 : else if (arg->GetType() == GAAT_DATASET_LIST)
2441 : {
2442 108 : if (!arg->Set(
2443 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2444 108 : inConstructionValues[arg]))))
2445 : {
2446 2 : return false;
2447 : }
2448 : }
2449 : }
2450 1466 : return true;
2451 1524 : };
2452 :
2453 : // Process positional arguments that have not been set through their
2454 : // option name.
2455 1524 : size_t i = 0;
2456 1524 : size_t iCurPosArg = 0;
2457 :
2458 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2459 1547 : if (m_positionalArgs.size() == 3 &&
2460 24 : (m_positionalArgs[0]->IsRequired() ||
2461 23 : m_positionalArgs[0]->GetMinCount() == 1) &&
2462 44 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2463 29 : (m_positionalArgs[1]->IsRequired() ||
2464 29 : m_positionalArgs[1]->GetMinCount() == 1) &&
2465 : /* Second argument may have several occurrences */
2466 44 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2467 22 : (m_positionalArgs[2]->IsRequired() ||
2468 22 : m_positionalArgs[2]->GetMinCount() == 1) &&
2469 22 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2470 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2471 1556 : !m_positionalArgs[1]->IsExplicitlySet() &&
2472 9 : !m_positionalArgs[2]->IsExplicitlySet())
2473 : {
2474 7 : if (lArgs.size() - i < 3)
2475 : {
2476 1 : ReportError(CE_Failure, CPLE_AppDefined,
2477 : "Not enough positional values.");
2478 1 : return false;
2479 : }
2480 12 : bool ok = ParseArgument(m_positionalArgs[0],
2481 6 : m_positionalArgs[0]->GetName().c_str(),
2482 6 : lArgs[i], inConstructionValues);
2483 6 : if (ok)
2484 : {
2485 5 : ++i;
2486 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2487 : {
2488 12 : ok = ParseArgument(m_positionalArgs[1],
2489 6 : m_positionalArgs[1]->GetName().c_str(),
2490 6 : lArgs[i], inConstructionValues);
2491 : }
2492 : }
2493 6 : if (ok)
2494 : {
2495 10 : ok = ParseArgument(m_positionalArgs[2],
2496 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2497 : inConstructionValues);
2498 5 : ++i;
2499 : }
2500 6 : if (!ok)
2501 : {
2502 3 : ProcessInConstructionValues();
2503 3 : return false;
2504 : }
2505 : }
2506 :
2507 516 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2508 601 : !m_positionalArgs[0]->IsExplicitlySet() &&
2509 2356 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2510 106 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2511 53 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2512 : {
2513 53 : ++iCurPosArg;
2514 : }
2515 :
2516 2533 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2517 : {
2518 1020 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2519 1028 : while (arg->IsExplicitlySet())
2520 : {
2521 9 : ++iCurPosArg;
2522 9 : if (iCurPosArg == m_positionalArgs.size())
2523 1 : break;
2524 8 : arg = m_positionalArgs[iCurPosArg];
2525 : }
2526 1020 : if (iCurPosArg == m_positionalArgs.size())
2527 : {
2528 1 : break;
2529 : }
2530 1569 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2531 550 : arg->GetMinCount() != arg->GetMaxCount())
2532 : {
2533 109 : if (iCurPosArg == 0)
2534 : {
2535 73 : size_t nCountAtEnd = 0;
2536 102 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2537 : {
2538 31 : const auto *otherArg = m_positionalArgs[j];
2539 31 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2540 : {
2541 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2542 : {
2543 2 : ReportError(
2544 : CE_Failure, CPLE_AppDefined,
2545 : "Ambiguity in definition of positional "
2546 : "argument "
2547 : "'%s' given it has a varying number of values, "
2548 : "but follows argument '%s' which also has a "
2549 : "varying number of values",
2550 1 : otherArg->GetName().c_str(),
2551 1 : arg->GetName().c_str());
2552 1 : ProcessInConstructionValues();
2553 1 : return false;
2554 : }
2555 3 : nCountAtEnd += otherArg->GetMinCount();
2556 : }
2557 : else
2558 : {
2559 27 : if (!otherArg->IsRequired())
2560 : {
2561 2 : ReportError(
2562 : CE_Failure, CPLE_AppDefined,
2563 : "Ambiguity in definition of positional "
2564 : "argument "
2565 : "'%s', given it is not required but follows "
2566 : "argument '%s' which has a varying number of "
2567 : "values",
2568 1 : otherArg->GetName().c_str(),
2569 1 : arg->GetName().c_str());
2570 1 : ProcessInConstructionValues();
2571 1 : return false;
2572 : }
2573 26 : nCountAtEnd++;
2574 : }
2575 : }
2576 71 : if (lArgs.size() < nCountAtEnd)
2577 : {
2578 1 : ReportError(CE_Failure, CPLE_AppDefined,
2579 : "Not enough positional values.");
2580 1 : ProcessInConstructionValues();
2581 1 : return false;
2582 : }
2583 148 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2584 : {
2585 78 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2586 : inConstructionValues))
2587 : {
2588 0 : ProcessInConstructionValues();
2589 0 : return false;
2590 : }
2591 : }
2592 : }
2593 36 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2594 : {
2595 82 : for (; i < lArgs.size(); ++i)
2596 : {
2597 47 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2598 : inConstructionValues))
2599 : {
2600 0 : ProcessInConstructionValues();
2601 0 : return false;
2602 : }
2603 : }
2604 : }
2605 : else
2606 : {
2607 1 : ReportError(CE_Failure, CPLE_AppDefined,
2608 : "Ambiguity in definition of positional arguments: "
2609 : "arguments with varying number of values must be "
2610 : "first or last one.");
2611 1 : return false;
2612 : }
2613 : }
2614 : else
2615 : {
2616 910 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2617 : {
2618 1 : ReportError(CE_Failure, CPLE_AppDefined,
2619 : "Not enough positional values.");
2620 1 : return false;
2621 : }
2622 909 : const size_t iMax = i + arg->GetMaxCount();
2623 1821 : for (; i < iMax; ++i)
2624 : {
2625 913 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2626 : inConstructionValues))
2627 : {
2628 1 : ProcessInConstructionValues();
2629 1 : return false;
2630 : }
2631 : }
2632 : }
2633 1013 : ++iCurPosArg;
2634 : }
2635 :
2636 1514 : if (i < lArgs.size())
2637 : {
2638 21 : ReportError(CE_Failure, CPLE_AppDefined,
2639 : "Positional values starting at '%s' are not expected.",
2640 21 : lArgs[i].c_str());
2641 21 : return false;
2642 : }
2643 :
2644 1493 : if (!ProcessInConstructionValues())
2645 : {
2646 33 : return false;
2647 : }
2648 :
2649 : // Skip to first unset positional argument.
2650 2491 : while (iCurPosArg < m_positionalArgs.size() &&
2651 556 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2652 : {
2653 475 : ++iCurPosArg;
2654 : }
2655 : // Check if this positional argument is required.
2656 1540 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2657 80 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2658 48 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2659 32 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2660 : {
2661 69 : ReportError(CE_Failure, CPLE_AppDefined,
2662 : "Positional arguments starting at '%s' have not been "
2663 : "specified.",
2664 69 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2665 69 : return false;
2666 : }
2667 :
2668 1391 : if (m_calledFromCommandLine)
2669 : {
2670 4920 : for (auto &arg : m_args)
2671 : {
2672 6439 : if (arg->IsExplicitlySet() &&
2673 1021 : ((arg->GetType() == GAAT_STRING &&
2674 1018 : arg->Get<std::string>() == "?") ||
2675 936 : (arg->GetType() == GAAT_STRING_LIST &&
2676 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2677 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2678 : {
2679 : {
2680 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2681 5 : ValidateArguments();
2682 : }
2683 :
2684 5 : auto choices = arg->GetChoices();
2685 5 : if (choices.empty())
2686 2 : choices = arg->GetAutoCompleteChoices(std::string());
2687 5 : if (!choices.empty())
2688 : {
2689 5 : if (choices.size() == 1)
2690 : {
2691 4 : ReportError(
2692 : CE_Failure, CPLE_AppDefined,
2693 : "Single potential value for argument '%s' is '%s'",
2694 4 : arg->GetName().c_str(), choices.front().c_str());
2695 : }
2696 : else
2697 : {
2698 6 : std::string msg("Potential values for argument '");
2699 3 : msg += arg->GetName();
2700 3 : msg += "' are:";
2701 45 : for (const auto &v : choices)
2702 : {
2703 42 : msg += "\n- ";
2704 42 : msg += v;
2705 : }
2706 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2707 : msg.c_str());
2708 : }
2709 5 : return false;
2710 : }
2711 : }
2712 : }
2713 : }
2714 :
2715 1386 : return m_skipValidationInParseCommandLine || ValidateArguments();
2716 : }
2717 :
2718 : /************************************************************************/
2719 : /* GDALAlgorithm::ReportError() */
2720 : /************************************************************************/
2721 :
2722 : //! @cond Doxygen_Suppress
2723 902 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2724 : const char *fmt, ...) const
2725 : {
2726 : va_list args;
2727 902 : va_start(args, fmt);
2728 902 : CPLError(eErrClass, err_no, "%s",
2729 902 : std::string(m_name)
2730 902 : .append(": ")
2731 1804 : .append(CPLString().vPrintf(fmt, args))
2732 : .c_str());
2733 902 : va_end(args);
2734 902 : }
2735 :
2736 : //! @endcond
2737 :
2738 : /************************************************************************/
2739 : /* GDALAlgorithm::ProcessDatasetArg() */
2740 : /************************************************************************/
2741 :
2742 9908 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2743 : GDALAlgorithm *algForOutput)
2744 : {
2745 9908 : bool ret = true;
2746 :
2747 9908 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2748 9908 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2749 9908 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2750 :
2751 9908 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2752 9908 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2753 9908 : const bool append = hasAppendArg && appendArg->Get<bool>();
2754 :
2755 9908 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2756 : const bool overwrite =
2757 16355 : (arg->IsOutput() && overwriteArg &&
2758 16355 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2759 :
2760 9908 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2761 19816 : auto &val = [arg]() -> GDALArgDatasetValue &
2762 : {
2763 9908 : if (arg->GetType() == GAAT_DATASET_LIST)
2764 5655 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2765 : else
2766 4253 : return arg->Get<GDALArgDatasetValue>();
2767 9908 : }();
2768 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2769 15662 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2770 15670 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2771 8 : !overwrite;
2772 :
2773 : // Used for nested pipelines
2774 : const auto oIterDatasetNameToDataset =
2775 19813 : val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2776 9908 : : m_oMapDatasetNameToDataset.end();
2777 :
2778 9908 : if (!val.GetDatasetRef() && !val.IsNameSet())
2779 : {
2780 3 : ReportError(CE_Failure, CPLE_AppDefined,
2781 : "Argument '%s' has no dataset object or dataset name.",
2782 3 : arg->GetName().c_str());
2783 3 : ret = false;
2784 : }
2785 9905 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2786 : {
2787 3 : return false;
2788 : }
2789 220 : else if (m_inputDatasetCanBeOmitted &&
2790 10122 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2791 8 : !arg->IsOutput())
2792 : {
2793 8 : return true;
2794 : }
2795 14638 : else if (!val.GetDatasetRef() &&
2796 5044 : (arg->AutoOpenDataset() ||
2797 14938 : oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2798 4444 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2799 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2800 : {
2801 1345 : int flags = arg->GetDatasetType();
2802 1345 : bool assignToOutputArg = false;
2803 :
2804 : // Check if input and output parameters point to the same
2805 : // filename (for vector datasets)
2806 2488 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2807 2488 : outputArg && outputArg->GetType() == GAAT_DATASET)
2808 : {
2809 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2810 121 : if (!outputVal.GetDatasetRef() &&
2811 121 : outputVal.GetName() == val.GetName() &&
2812 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2813 : {
2814 2 : assignToOutputArg = true;
2815 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2816 : }
2817 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2818 : {
2819 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2820 : }
2821 : }
2822 :
2823 1345 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2824 1262 : flags |= GDAL_OF_VERBOSE_ERROR;
2825 1345 : if ((arg == outputArg || !outputArg) && update)
2826 : {
2827 85 : flags |= GDAL_OF_UPDATE;
2828 85 : if (!append)
2829 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2830 : }
2831 :
2832 1345 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2833 : const bool readOnly =
2834 1388 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2835 43 : readOnlyArg->Get<bool>());
2836 1345 : if (readOnly)
2837 12 : flags &= ~GDAL_OF_UPDATE;
2838 :
2839 2690 : CPLStringList aosOpenOptions;
2840 2690 : CPLStringList aosAllowedDrivers;
2841 1345 : if (arg->IsInput())
2842 : {
2843 1345 : if (arg == outputArg)
2844 : {
2845 83 : if (update && !overwrite)
2846 : {
2847 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2848 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2849 31 : aosOpenOptions = CPLStringList(
2850 31 : ooArg->Get<std::vector<std::string>>());
2851 : }
2852 : }
2853 : else
2854 : {
2855 1262 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2856 1262 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2857 : aosOpenOptions =
2858 1211 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2859 :
2860 1262 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2861 1262 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2862 : aosAllowedDrivers =
2863 1168 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2864 : }
2865 : }
2866 :
2867 2690 : std::string osDatasetName = val.GetName();
2868 1345 : if (!m_referencePath.empty())
2869 : {
2870 42 : osDatasetName = GDALDataset::BuildFilename(
2871 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2872 : }
2873 1345 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2874 0 : osDatasetName = "/vsistdin/";
2875 :
2876 : // Handle special case of overview delete in GTiff which would fail
2877 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2878 142 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2879 1489 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2880 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2881 : {
2882 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2883 : GDALDriverH hDrv =
2884 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2885 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2886 : {
2887 : // Cleaning does not break COG layout
2888 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2889 : }
2890 : }
2891 :
2892 : GDALDataset *poDS;
2893 2690 : CPLErrorAccumulator oAccumulator;
2894 : {
2895 2690 : auto oContext = oAccumulator.InstallForCurrentScope();
2896 :
2897 1345 : poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2898 1345 : ? oIterDatasetNameToDataset->second
2899 1342 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2900 1342 : aosAllowedDrivers.List(),
2901 1342 : aosOpenOptions.List());
2902 :
2903 65 : if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
2904 1410 : !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
2905 : {
2906 35 : auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
2907 70 : osDatasetName.c_str(), nullptr);
2908 35 : if (eErr == OGRERR_NONE)
2909 : {
2910 4 : auto poMemDS = std::make_unique<MEMDataset>();
2911 4 : auto *poLayer = poMemDS->CreateLayer(
2912 : "", poWktGeom->getSpatialReference(),
2913 2 : poWktGeom->getGeometryType());
2914 :
2915 2 : auto poFeatureDefn = poLayer->GetLayerDefn();
2916 4 : OGRFeature oFeature(poFeatureDefn);
2917 :
2918 2 : oFeature.SetGeometry(std::move(poWktGeom));
2919 2 : if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
2920 : {
2921 2 : poDS = poMemDS.release();
2922 2 : oAccumulator.ClearErrors();
2923 : }
2924 : }
2925 : }
2926 :
2927 : // Retry with PostGIS vector driver
2928 63 : if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
2929 61 : cpl::starts_with(osDatasetName, "PG:") &&
2930 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2931 1408 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2932 : {
2933 0 : oAccumulator.ClearErrors();
2934 0 : poDS = GDALDataset::Open(
2935 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2936 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2937 : }
2938 : }
2939 1345 : oAccumulator.ReplayErrors();
2940 :
2941 1345 : if (poDS)
2942 : {
2943 1282 : if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2944 : {
2945 3 : if (arg->GetType() == GAAT_DATASET)
2946 3 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2947 3 : poDS->Reference();
2948 3 : m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
2949 : }
2950 :
2951 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2952 : // where the PG: dataset will be first opened with the PostGISRaster
2953 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2954 1750 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2955 1840 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2956 90 : aosOpenOptions.empty())
2957 : {
2958 86 : auto poDrv = poDS->GetDriver();
2959 86 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2960 : {
2961 : // Retry with PostgreSQL (vector) driver
2962 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2963 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2964 0 : if (poTmpDS)
2965 : {
2966 0 : poDS->ReleaseRef();
2967 0 : poDS = poTmpDS.release();
2968 : }
2969 : }
2970 : }
2971 :
2972 1282 : if (assignToOutputArg)
2973 : {
2974 : // Avoid opening twice the same datasource if it is both
2975 : // the input and output.
2976 : // Known to cause problems with at least FGdb, SQLite
2977 : // and GPKG drivers. See #4270
2978 : // Restrict to those 3 drivers. For example it is known
2979 : // to break with the PG driver due to the way it
2980 : // manages transactions.
2981 2 : auto poDriver = poDS->GetDriver();
2982 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2983 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
2984 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
2985 : {
2986 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2987 : }
2988 : }
2989 1282 : val.SetDatasetOpenedByAlgorithm();
2990 1282 : val.Set(poDS);
2991 1282 : poDS->ReleaseRef();
2992 : }
2993 63 : else if (!append)
2994 : {
2995 61 : ret = false;
2996 : }
2997 : }
2998 :
2999 : // Deal with overwriting the output dataset
3000 9897 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
3001 : {
3002 3101 : if (!append)
3003 : {
3004 : // If outputting to MEM, do not try to erase a real file of the same name!
3005 : const auto outputFormatArg =
3006 3089 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3007 9229 : if (!(outputFormatArg &&
3008 3070 : outputFormatArg->GetType() == GAAT_STRING &&
3009 3070 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3010 2000 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3011 1072 : "stream") ||
3012 1072 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3013 : "Memory"))))
3014 : {
3015 1091 : const char *pszType = "";
3016 1091 : GDALDriver *poDriver = nullptr;
3017 2136 : if (!val.GetName().empty() &&
3018 1045 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
3019 : &poDriver))
3020 : {
3021 79 : if (!overwrite)
3022 : {
3023 68 : std::string options;
3024 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
3025 : {
3026 11 : options += "--";
3027 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
3028 : }
3029 34 : if (hasAppendArg)
3030 : {
3031 22 : if (!options.empty())
3032 8 : options += '/';
3033 22 : options += "--";
3034 22 : options += GDAL_ARG_NAME_APPEND;
3035 : }
3036 34 : if (hasUpdateArg)
3037 : {
3038 15 : if (!options.empty())
3039 12 : options += '/';
3040 15 : options += "--";
3041 15 : options += GDAL_ARG_NAME_UPDATE;
3042 : }
3043 :
3044 34 : if (poDriver)
3045 : {
3046 68 : const char *pszPrefix = poDriver->GetMetadataItem(
3047 34 : GDAL_DMD_CONNECTION_PREFIX);
3048 34 : if (pszPrefix &&
3049 0 : STARTS_WITH_CI(val.GetName().c_str(),
3050 : pszPrefix))
3051 : {
3052 0 : bool bExists = false;
3053 : {
3054 : CPLErrorStateBackuper oBackuper(
3055 0 : CPLQuietErrorHandler);
3056 0 : bExists = std::unique_ptr<GDALDataset>(
3057 : GDALDataset::Open(
3058 0 : val.GetName().c_str())) !=
3059 : nullptr;
3060 : }
3061 0 : if (bExists)
3062 : {
3063 0 : if (!options.empty())
3064 0 : options = " You may specify the " +
3065 0 : options + " option.";
3066 0 : ReportError(CE_Failure, CPLE_AppDefined,
3067 : "%s '%s' already exists.%s",
3068 0 : pszType, val.GetName().c_str(),
3069 : options.c_str());
3070 0 : return false;
3071 : }
3072 :
3073 0 : return true;
3074 : }
3075 : }
3076 :
3077 34 : if (!options.empty())
3078 28 : options = '/' + options;
3079 68 : ReportError(
3080 : CE_Failure, CPLE_AppDefined,
3081 : "%s '%s' already exists. You may specify the "
3082 : "--overwrite%s option.",
3083 34 : pszType, val.GetName().c_str(), options.c_str());
3084 34 : return false;
3085 : }
3086 45 : else if (EQUAL(pszType, "File"))
3087 : {
3088 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3089 : {
3090 0 : ReportError(CE_Failure, CPLE_AppDefined,
3091 : "Deleting %s failed: %s",
3092 0 : val.GetName().c_str(),
3093 0 : VSIStrerror(errno));
3094 0 : return false;
3095 : }
3096 : }
3097 44 : else if (EQUAL(pszType, "Directory"))
3098 : {
3099 : // We don't want the user to accidentally erase a non-GDAL dataset
3100 1 : ReportError(CE_Failure, CPLE_AppDefined,
3101 : "Directory '%s' already exists, but is not "
3102 : "recognized as a valid GDAL dataset. "
3103 : "Please manually delete it before retrying",
3104 1 : val.GetName().c_str());
3105 1 : return false;
3106 : }
3107 43 : else if (poDriver)
3108 : {
3109 : bool bDeleteOK;
3110 : {
3111 : CPLErrorStateBackuper oBackuper(
3112 43 : CPLQuietErrorHandler);
3113 43 : bDeleteOK = (poDriver->Delete(
3114 43 : val.GetName().c_str()) == CE_None);
3115 : }
3116 : VSIStatBufL sStat;
3117 46 : if (!bDeleteOK &&
3118 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3119 : {
3120 3 : if (VSI_ISDIR(sStat.st_mode))
3121 : {
3122 : // We don't want the user to accidentally erase a non-GDAL dataset
3123 0 : ReportError(
3124 : CE_Failure, CPLE_AppDefined,
3125 : "Directory '%s' already exists, but is not "
3126 : "recognized as a valid GDAL dataset. "
3127 : "Please manually delete it before retrying",
3128 0 : val.GetName().c_str());
3129 2 : return false;
3130 : }
3131 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3132 : {
3133 2 : ReportError(CE_Failure, CPLE_AppDefined,
3134 : "Deleting %s failed: %s",
3135 2 : val.GetName().c_str(),
3136 2 : VSIStrerror(errno));
3137 2 : return false;
3138 : }
3139 : }
3140 : }
3141 : }
3142 : }
3143 : }
3144 : }
3145 :
3146 : // If outputting to stdout, automatically turn off progress bar
3147 9860 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3148 : {
3149 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3150 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3151 5 : quietArg->Set(true);
3152 : }
3153 :
3154 9860 : return ret;
3155 : }
3156 :
3157 : /************************************************************************/
3158 : /* GDALAlgorithm::ValidateArguments() */
3159 : /************************************************************************/
3160 :
3161 6778 : bool GDALAlgorithm::ValidateArguments()
3162 : {
3163 6778 : if (m_selectedSubAlg)
3164 3 : return m_selectedSubAlg->ValidateArguments();
3165 :
3166 6775 : if (m_specialActionRequested)
3167 1 : return true;
3168 :
3169 6774 : m_arbitraryLongNameArgsAllowed = false;
3170 :
3171 : // If only --output=format=MEM/stream is specified and not --output,
3172 : // then set empty name for --output.
3173 6774 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3174 6774 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3175 4030 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3176 2679 : !outputArg->IsExplicitlySet() &&
3177 360 : outputFormatArg->GetType() == GAAT_STRING &&
3178 360 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3179 590 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3180 11137 : outputArg->GetType() == GAAT_DATASET &&
3181 333 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3182 : {
3183 333 : outputArg->Get<GDALArgDatasetValue>().Set("");
3184 : }
3185 :
3186 : // The method may emit several errors if several constraints are not met.
3187 6774 : bool ret = true;
3188 13548 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3189 13548 : std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3190 128872 : for (auto &arg : m_args)
3191 : {
3192 : // Check mutually exclusive/dependent arguments
3193 122098 : if (arg->IsExplicitlySet())
3194 : {
3195 :
3196 19563 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3197 19563 : if (!mutualExclusionGroup.empty())
3198 : {
3199 : auto oIter =
3200 769 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3201 769 : if (oIter != mutualExclusionGroupUsed.end())
3202 : {
3203 13 : ret = false;
3204 26 : ReportError(
3205 : CE_Failure, CPLE_AppDefined,
3206 : "Argument '%s' is mutually exclusive with '%s'.",
3207 26 : arg->GetName().c_str(), oIter->second.c_str());
3208 : }
3209 : else
3210 : {
3211 756 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3212 1512 : arg->GetName();
3213 : }
3214 : }
3215 :
3216 19563 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3217 19563 : if (!mutualDependencyGroup.empty())
3218 : {
3219 50 : if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3220 100 : mutualDependencyGroupUsed.end())
3221 : {
3222 87 : mutualDependencyGroupUsed[mutualDependencyGroup] = {
3223 87 : arg->GetName()};
3224 : }
3225 : else
3226 : {
3227 42 : mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3228 21 : arg->GetName());
3229 : }
3230 : }
3231 :
3232 : // Check direct dependencies
3233 19575 : for (const auto &dependency : arg->GetDirectDependencies())
3234 : {
3235 12 : auto depArg = GetArg(dependency);
3236 12 : if (!depArg)
3237 : {
3238 0 : ret = false;
3239 0 : ReportError(CE_Failure, CPLE_AppDefined,
3240 : "Argument '%s' depends on argument '%s' that "
3241 : "is not defined.",
3242 0 : arg->GetName().c_str(), dependency.c_str());
3243 : }
3244 12 : else if (!depArg->IsExplicitlySet())
3245 : {
3246 6 : ret = false;
3247 12 : ReportError(CE_Failure, CPLE_AppDefined,
3248 : "Argument '%s' depends on argument '%s' that "
3249 : "has not been specified.",
3250 6 : arg->GetName().c_str(),
3251 6 : depArg->GetName().c_str());
3252 : }
3253 : }
3254 : }
3255 :
3256 122252 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3257 154 : !arg->HasDefaultValue())
3258 : {
3259 154 : bool emitError = true;
3260 154 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3261 154 : if (!mutualExclusionGroup.empty())
3262 : {
3263 1765 : for (const auto &otherArg : m_args)
3264 : {
3265 1751 : if (otherArg->GetMutualExclusionGroup() ==
3266 1856 : mutualExclusionGroup &&
3267 105 : otherArg->IsExplicitlySet())
3268 : {
3269 74 : emitError = false;
3270 74 : break;
3271 : }
3272 : }
3273 : }
3274 232 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3275 48 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3276 60 : (arg->GetType() == GAAT_DATASET ||
3277 30 : arg->GetType() == GAAT_DATASET_LIST)))
3278 : {
3279 50 : ReportError(CE_Failure, CPLE_AppDefined,
3280 : "Required argument '%s' has not been specified.",
3281 50 : arg->GetName().c_str());
3282 50 : ret = false;
3283 : }
3284 : }
3285 121944 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3286 : {
3287 4253 : if (!ProcessDatasetArg(arg.get(), this))
3288 51 : ret = false;
3289 : }
3290 :
3291 122098 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3292 : {
3293 5522 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3294 5522 : if (listVal.size() == 1)
3295 : {
3296 5368 : if (!ProcessDatasetArg(arg.get(), this))
3297 39 : ret = false;
3298 : }
3299 : else
3300 : {
3301 473 : for (auto &val : listVal)
3302 : {
3303 319 : if (val.GetDatasetRef())
3304 : {
3305 120 : if (!CheckCanSetDatasetObject(arg.get()))
3306 : {
3307 0 : ret = false;
3308 : }
3309 315 : continue;
3310 : }
3311 :
3312 199 : if (val.GetName().empty())
3313 : {
3314 0 : ReportError(CE_Failure, CPLE_AppDefined,
3315 : "Argument '%s' has no dataset object or "
3316 : "dataset name.",
3317 0 : arg->GetName().c_str());
3318 0 : ret = false;
3319 0 : continue;
3320 : }
3321 :
3322 199 : auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3323 199 : if (oIter != m_oMapDatasetNameToDataset.end())
3324 : {
3325 2 : auto poDS = oIter->second;
3326 2 : val.SetDatasetOpenedByAlgorithm();
3327 2 : val.Set(poDS);
3328 2 : m_oMapDatasetNameToDataset.erase(oIter);
3329 2 : continue;
3330 : }
3331 :
3332 197 : if (!arg->AutoOpenDataset())
3333 193 : continue;
3334 :
3335 4 : int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3336 :
3337 8 : CPLStringList aosOpenOptions;
3338 8 : CPLStringList aosAllowedDrivers;
3339 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3340 : {
3341 4 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3342 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3343 : {
3344 4 : aosOpenOptions = CPLStringList(
3345 4 : ooArg->Get<std::vector<std::string>>());
3346 : }
3347 :
3348 4 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3349 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3350 : {
3351 4 : aosAllowedDrivers = CPLStringList(
3352 4 : ifArg->Get<std::vector<std::string>>());
3353 : }
3354 :
3355 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3356 4 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3357 0 : updateArg->Get<bool>())
3358 : {
3359 0 : flags |= GDAL_OF_UPDATE;
3360 : }
3361 : }
3362 :
3363 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3364 4 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3365 12 : aosOpenOptions.List()));
3366 4 : if (poDS)
3367 : {
3368 3 : val.Set(std::move(poDS));
3369 : }
3370 : else
3371 : {
3372 1 : ret = false;
3373 : }
3374 : }
3375 : }
3376 : }
3377 :
3378 122098 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3379 : {
3380 8 : ret = false;
3381 : }
3382 : }
3383 :
3384 : // Check mutual dependency groups
3385 6774 : std::vector<std::string> processedGroups;
3386 : // Loop through group map and check there are not required args in the group that are not set
3387 6803 : for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3388 : {
3389 29 : if (std::find(processedGroups.begin(), processedGroups.end(),
3390 29 : groupName) != processedGroups.end())
3391 0 : continue;
3392 58 : std::vector<std::string> missingArgs;
3393 562 : for (auto &arg : m_args)
3394 : {
3395 533 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3396 598 : if (mutualDependencyGroup == groupName &&
3397 65 : std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3398 598 : argNames.end())
3399 : {
3400 15 : missingArgs.push_back(arg->GetName());
3401 : }
3402 : }
3403 29 : if (!missingArgs.empty())
3404 : {
3405 12 : ret = false;
3406 24 : std::string missingArgsStr;
3407 27 : for (const auto &missingArg : missingArgs)
3408 : {
3409 15 : if (!missingArgsStr.empty())
3410 3 : missingArgsStr += ", ";
3411 15 : missingArgsStr += missingArg;
3412 : }
3413 24 : std::string givenArgsStr;
3414 27 : for (const auto &givenArg : argNames)
3415 : {
3416 15 : if (!givenArgsStr.empty())
3417 3 : givenArgsStr += ", ";
3418 15 : givenArgsStr += givenArg;
3419 : }
3420 12 : ReportError(CE_Failure, CPLE_AppDefined,
3421 : "Argument(s) '%s' require(s) that the following "
3422 : "argument(s) are also specified: %s.",
3423 : givenArgsStr.c_str(), missingArgsStr.c_str());
3424 : }
3425 29 : processedGroups.push_back(groupName);
3426 : }
3427 :
3428 29065 : for (const auto &f : m_validationActions)
3429 : {
3430 22291 : if (!f())
3431 80 : ret = false;
3432 : }
3433 :
3434 6774 : return ret;
3435 : }
3436 :
3437 : /************************************************************************/
3438 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3439 : /************************************************************************/
3440 :
3441 : std::unique_ptr<GDALAlgorithm>
3442 9975 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3443 : bool suggestionAllowed) const
3444 : {
3445 9975 : auto ret = m_subAlgRegistry.Instantiate(name);
3446 19950 : auto childCallPath = m_callPath;
3447 9975 : childCallPath.push_back(name);
3448 9975 : if (!ret)
3449 : {
3450 916 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3451 916 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3452 : }
3453 9975 : if (ret)
3454 : {
3455 9838 : ret->SetCallPath(childCallPath);
3456 : }
3457 137 : else if (suggestionAllowed)
3458 : {
3459 58 : std::string bestCandidate;
3460 29 : size_t bestDistance = std::numeric_limits<size_t>::max();
3461 470 : for (const std::string &candidate : GetSubAlgorithmNames())
3462 : {
3463 : const size_t distance =
3464 441 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3465 : /* transpositionAllowed = */ true);
3466 441 : if (distance < bestDistance)
3467 : {
3468 71 : bestCandidate = candidate;
3469 71 : bestDistance = distance;
3470 : }
3471 370 : else if (distance == bestDistance)
3472 : {
3473 45 : bestCandidate.clear();
3474 : }
3475 : }
3476 29 : if (!bestCandidate.empty() && bestDistance <= 2)
3477 : {
3478 4 : CPLError(CE_Failure, CPLE_AppDefined,
3479 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3480 : name.c_str(), bestCandidate.c_str());
3481 : }
3482 : }
3483 19950 : return ret;
3484 : }
3485 :
3486 : /************************************************************************/
3487 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3488 : /************************************************************************/
3489 :
3490 : std::string
3491 37 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3492 : {
3493 37 : if (osName.size() >= 3)
3494 : {
3495 34 : std::string bestCandidate;
3496 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3497 722 : for (const auto &[key, value] : m_mapLongNameToArg)
3498 : {
3499 688 : CPL_IGNORE_RET_VAL(value);
3500 688 : const size_t distance = CPLLevenshteinDistance(
3501 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3502 688 : if (distance < bestDistance)
3503 : {
3504 87 : bestCandidate = key;
3505 87 : bestDistance = distance;
3506 : }
3507 601 : else if (distance == bestDistance)
3508 : {
3509 80 : bestCandidate.clear();
3510 : }
3511 : }
3512 47 : if (!bestCandidate.empty() &&
3513 13 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3514 : {
3515 5 : return bestCandidate;
3516 : }
3517 : }
3518 32 : return std::string();
3519 : }
3520 :
3521 : /************************************************************************/
3522 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3523 : /************************************************************************/
3524 :
3525 : /* static */
3526 22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3527 : {
3528 66 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3529 66 : osName == GDAL_ARG_NAME_OVERWRITE ||
3530 44 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3531 : }
3532 :
3533 : /************************************************************************/
3534 : /* GDALAlgorithm::HasOutputString() */
3535 : /************************************************************************/
3536 :
3537 67 : bool GDALAlgorithm::HasOutputString() const
3538 : {
3539 67 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3540 67 : return outputStringArg && outputStringArg->IsOutput();
3541 : }
3542 :
3543 : /************************************************************************/
3544 : /* GDALAlgorithm::GetArg() */
3545 : /************************************************************************/
3546 :
3547 464319 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3548 : bool suggestionAllowed, bool isConst)
3549 : {
3550 464319 : const auto nPos = osName.find_first_not_of('-');
3551 464319 : if (nPos == std::string::npos)
3552 23 : return nullptr;
3553 928592 : std::string osKey = osName.substr(nPos);
3554 : {
3555 464296 : const auto oIter = m_mapLongNameToArg.find(osKey);
3556 464296 : if (oIter != m_mapLongNameToArg.end())
3557 432424 : return oIter->second;
3558 : }
3559 : {
3560 31872 : const auto oIter = m_mapShortNameToArg.find(osKey);
3561 31872 : if (oIter != m_mapShortNameToArg.end())
3562 6 : return oIter->second;
3563 : }
3564 :
3565 31866 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3566 : {
3567 22 : const auto nDotPos = osKey.find('.');
3568 : const std::string osKeyEnd =
3569 22 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3570 22 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3571 : {
3572 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3573 0 : std::make_unique<bool>());
3574 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3575 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3576 0 : .SetUserProvided();
3577 : }
3578 : else
3579 : {
3580 44 : const std::string osKeyInit = osKey;
3581 22 : if (osKey == "oo")
3582 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3583 22 : else if (osKey == "co")
3584 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3585 22 : else if (osKey == "of")
3586 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3587 22 : else if (osKey == "if")
3588 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3589 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3590 22 : std::make_unique<std::string>());
3591 : auto &arg =
3592 44 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3593 44 : m_arbitraryLongNameArgsValuesStr.back().get())
3594 22 : .SetUserProvided();
3595 22 : if (osKey != osKeyInit)
3596 0 : arg.AddAlias(osKeyInit);
3597 : }
3598 22 : const auto oIter = m_mapLongNameToArg.find(osKey);
3599 22 : CPLAssert(oIter != m_mapLongNameToArg.end());
3600 22 : return oIter->second;
3601 : }
3602 :
3603 31844 : if (suggestionAllowed)
3604 : {
3605 14 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3606 7 : if (!bestCandidate.empty())
3607 : {
3608 2 : CPLError(CE_Failure, CPLE_AppDefined,
3609 : "Argument '%s' is unknown. Do you mean '%s'?",
3610 : osName.c_str(), bestCandidate.c_str());
3611 : }
3612 : }
3613 :
3614 31844 : return nullptr;
3615 : }
3616 :
3617 : /************************************************************************/
3618 : /* GDALAlgorithm::AddAliasFor() */
3619 : /************************************************************************/
3620 :
3621 : //! @cond Doxygen_Suppress
3622 80250 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3623 : const std::string &alias)
3624 : {
3625 80250 : if (cpl::contains(m_mapLongNameToArg, alias))
3626 : {
3627 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3628 : alias.c_str());
3629 : }
3630 : else
3631 : {
3632 80249 : m_mapLongNameToArg[alias] = arg;
3633 : }
3634 80250 : }
3635 :
3636 : //! @endcond
3637 :
3638 : /************************************************************************/
3639 : /* GDALAlgorithm::AddShortNameAliasFor() */
3640 : /************************************************************************/
3641 :
3642 : //! @cond Doxygen_Suppress
3643 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3644 : char shortNameAlias)
3645 : {
3646 96 : std::string alias;
3647 48 : alias += shortNameAlias;
3648 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3649 : {
3650 0 : ReportError(CE_Failure, CPLE_AppDefined,
3651 : "Short name '%s' already declared.", alias.c_str());
3652 : }
3653 : else
3654 : {
3655 48 : m_mapShortNameToArg[alias] = arg;
3656 : }
3657 48 : }
3658 :
3659 : //! @endcond
3660 :
3661 : /************************************************************************/
3662 : /* GDALAlgorithm::SetPositional() */
3663 : /************************************************************************/
3664 :
3665 : //! @cond Doxygen_Suppress
3666 21552 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3667 : {
3668 21552 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3669 : arg) == m_positionalArgs.end());
3670 21552 : m_positionalArgs.push_back(arg);
3671 21552 : }
3672 :
3673 : //! @endcond
3674 :
3675 : /************************************************************************/
3676 : /* GDALAlgorithm::HasSubAlgorithms() */
3677 : /************************************************************************/
3678 :
3679 12640 : bool GDALAlgorithm::HasSubAlgorithms() const
3680 : {
3681 12640 : if (!m_subAlgRegistry.empty())
3682 3295 : return true;
3683 9345 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3684 18690 : .GetDeclaredSubAlgorithmNames(m_callPath)
3685 9345 : .empty();
3686 : }
3687 :
3688 : /************************************************************************/
3689 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3690 : /************************************************************************/
3691 :
3692 1330 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3693 : {
3694 1330 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3695 1330 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3696 2660 : .GetDeclaredSubAlgorithmNames(m_callPath);
3697 1330 : ret.insert(ret.end(), other.begin(), other.end());
3698 1330 : if (!other.empty())
3699 415 : std::sort(ret.begin(), ret.end());
3700 2660 : return ret;
3701 : }
3702 :
3703 : /************************************************************************/
3704 : /* GDALAlgorithm::AddArg() */
3705 : /************************************************************************/
3706 :
3707 : GDALInConstructionAlgorithmArg &
3708 310105 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3709 : {
3710 310105 : auto argRaw = arg.get();
3711 310105 : const auto &longName = argRaw->GetName();
3712 310105 : if (!longName.empty())
3713 : {
3714 310092 : if (longName[0] == '-')
3715 : {
3716 1 : ReportError(CE_Failure, CPLE_AppDefined,
3717 : "Long name '%s' should not start with '-'",
3718 : longName.c_str());
3719 : }
3720 310092 : if (longName.find('=') != std::string::npos)
3721 : {
3722 1 : ReportError(CE_Failure, CPLE_AppDefined,
3723 : "Long name '%s' should not contain a '=' character",
3724 : longName.c_str());
3725 : }
3726 310092 : if (cpl::contains(m_mapLongNameToArg, longName))
3727 : {
3728 1 : ReportError(CE_Failure, CPLE_AppDefined,
3729 : "Long name '%s' already declared", longName.c_str());
3730 : }
3731 310092 : m_mapLongNameToArg[longName] = argRaw;
3732 : }
3733 310105 : const auto &shortName = argRaw->GetShortName();
3734 310105 : if (!shortName.empty())
3735 : {
3736 149824 : if (shortName.size() != 1 ||
3737 74912 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3738 65 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3739 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3740 : {
3741 1 : ReportError(CE_Failure, CPLE_AppDefined,
3742 : "Short name '%s' should be a single letter or digit",
3743 : shortName.c_str());
3744 : }
3745 74912 : if (cpl::contains(m_mapShortNameToArg, shortName))
3746 : {
3747 1 : ReportError(CE_Failure, CPLE_AppDefined,
3748 : "Short name '%s' already declared", shortName.c_str());
3749 : }
3750 74912 : m_mapShortNameToArg[shortName] = argRaw;
3751 : }
3752 310105 : m_args.emplace_back(std::move(arg));
3753 : return *(
3754 310105 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3755 : }
3756 :
3757 : GDALInConstructionAlgorithmArg &
3758 138407 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3759 : const std::string &helpMessage, bool *pValue)
3760 : {
3761 138407 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3762 : this,
3763 276814 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3764 276814 : pValue));
3765 : }
3766 :
3767 : GDALInConstructionAlgorithmArg &
3768 50194 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3769 : const std::string &helpMessage, std::string *pValue)
3770 : {
3771 50194 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3772 : this,
3773 100388 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3774 100388 : pValue));
3775 : }
3776 :
3777 : GDALInConstructionAlgorithmArg &
3778 12313 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3779 : const std::string &helpMessage, int *pValue)
3780 : {
3781 12313 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3782 : this,
3783 24626 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3784 24626 : pValue));
3785 : }
3786 :
3787 : GDALInConstructionAlgorithmArg &
3788 10140 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3789 : const std::string &helpMessage, double *pValue)
3790 : {
3791 10140 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3792 : this,
3793 20280 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3794 20280 : pValue));
3795 : }
3796 :
3797 : GDALInConstructionAlgorithmArg &
3798 11681 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3799 : const std::string &helpMessage,
3800 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3801 : {
3802 23362 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3803 : this,
3804 23362 : GDALAlgorithmArgDecl(longName, chShortName,
3805 : helpMessage, GAAT_DATASET),
3806 11681 : pValue))
3807 11681 : .SetDatasetType(type);
3808 11681 : pValue->SetOwnerArgument(&arg);
3809 11681 : return arg;
3810 : }
3811 :
3812 : GDALInConstructionAlgorithmArg &
3813 66191 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3814 : const std::string &helpMessage,
3815 : std::vector<std::string> *pValue)
3816 : {
3817 66191 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3818 : this,
3819 132382 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3820 : GAAT_STRING_LIST),
3821 132382 : pValue));
3822 : }
3823 :
3824 : GDALInConstructionAlgorithmArg &
3825 2208 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3826 : const std::string &helpMessage, std::vector<int> *pValue)
3827 : {
3828 2208 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3829 : this,
3830 4416 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3831 : GAAT_INTEGER_LIST),
3832 4416 : pValue));
3833 : }
3834 :
3835 : GDALInConstructionAlgorithmArg &
3836 5181 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3837 : const std::string &helpMessage,
3838 : std::vector<double> *pValue)
3839 : {
3840 5181 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3841 : this,
3842 10362 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3843 : GAAT_REAL_LIST),
3844 10362 : pValue));
3845 : }
3846 :
3847 : GDALInConstructionAlgorithmArg &
3848 13790 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3849 : const std::string &helpMessage,
3850 : std::vector<GDALArgDatasetValue> *pValue,
3851 : GDALArgDatasetType type)
3852 : {
3853 27580 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3854 : this,
3855 27580 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3856 : GAAT_DATASET_LIST),
3857 13790 : pValue))
3858 27580 : .SetDatasetType(type);
3859 : }
3860 :
3861 : /************************************************************************/
3862 : /* MsgOrDefault() */
3863 : /************************************************************************/
3864 :
3865 103724 : inline const char *MsgOrDefault(const char *helpMessage,
3866 : const char *defaultMessage)
3867 : {
3868 103724 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3869 : }
3870 :
3871 : /************************************************************************/
3872 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3873 : /************************************************************************/
3874 :
3875 : /* static */
3876 16827 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3877 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3878 : {
3879 : arg.SetAutoCompleteFunction(
3880 7 : [&arg,
3881 2467 : type](const std::string ¤tValue) -> std::vector<std::string>
3882 : {
3883 14 : std::vector<std::string> oRet;
3884 :
3885 7 : if (arg.IsHidden())
3886 0 : return oRet;
3887 :
3888 : {
3889 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3890 : VSIStatBufL sStat;
3891 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3892 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3893 : {
3894 0 : return oRet;
3895 : }
3896 : }
3897 :
3898 7 : auto poDM = GetGDALDriverManager();
3899 14 : std::set<std::string> oExtensions;
3900 7 : if (type)
3901 : {
3902 1374 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3903 : {
3904 1368 : auto poDriver = poDM->GetDriver(i);
3905 3876 : if (((type & GDAL_OF_RASTER) != 0 &&
3906 1140 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3907 588 : ((type & GDAL_OF_VECTOR) != 0 &&
3908 2873 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3909 497 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3910 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3911 : {
3912 : const char *pszExtensions =
3913 871 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3914 871 : if (pszExtensions)
3915 : {
3916 : const CPLStringList aosExts(
3917 1154 : CSLTokenizeString2(pszExtensions, " ", 0));
3918 1303 : for (const char *pszExt : cpl::Iterate(aosExts))
3919 726 : oExtensions.insert(CPLString(pszExt).tolower());
3920 : }
3921 : }
3922 : }
3923 : }
3924 :
3925 14 : std::string osDir;
3926 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3927 14 : std::string osPrefix;
3928 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3929 : {
3930 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3931 : {
3932 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3933 : {
3934 2 : osPrefix = pszPrefix;
3935 2 : break;
3936 : }
3937 : }
3938 3 : if (osPrefix.empty())
3939 1 : return aosVSIPrefixes;
3940 2 : if (currentValue == osPrefix)
3941 1 : osDir = osPrefix;
3942 : }
3943 6 : if (osDir.empty())
3944 : {
3945 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3946 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3947 0 : osDir = std::move(osPrefix);
3948 : }
3949 :
3950 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3951 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3952 6 : if (currentValue.empty())
3953 1 : osDir.clear();
3954 : const std::string currentFilename =
3955 12 : CPLGetFilename(currentValue.c_str());
3956 6 : if (psDir)
3957 : {
3958 444 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3959 : {
3960 439 : if ((currentFilename.empty() ||
3961 219 : STARTS_WITH(psEntry->pszName,
3962 221 : currentFilename.c_str())) &&
3963 221 : strcmp(psEntry->pszName, ".") != 0 &&
3964 1319 : strcmp(psEntry->pszName, "..") != 0 &&
3965 221 : (oExtensions.empty() ||
3966 220 : !strstr(psEntry->pszName, ".aux.xml")))
3967 : {
3968 874 : if (oExtensions.empty() ||
3969 218 : cpl::contains(
3970 : oExtensions,
3971 437 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3972 655 : .tolower()) ||
3973 186 : VSI_ISDIR(psEntry->nMode))
3974 : {
3975 74 : std::string osVal;
3976 37 : if (osDir.empty() || osDir == ".")
3977 4 : osVal = psEntry->pszName;
3978 : else
3979 66 : osVal = CPLFormFilenameSafe(
3980 66 : osDir.c_str(), psEntry->pszName, nullptr);
3981 37 : if (VSI_ISDIR(psEntry->nMode))
3982 4 : osVal += osSep;
3983 37 : oRet.push_back(std::move(osVal));
3984 : }
3985 : }
3986 439 : }
3987 5 : VSICloseDir(psDir);
3988 : }
3989 6 : return oRet;
3990 16827 : });
3991 16827 : }
3992 :
3993 : /************************************************************************/
3994 : /* GDALAlgorithm::AddInputDatasetArg() */
3995 : /************************************************************************/
3996 :
3997 704 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3998 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3999 : bool positionalAndRequired, const char *helpMessage)
4000 : {
4001 : auto &arg = AddArg(
4002 : GDAL_ARG_NAME_INPUT, 'i',
4003 : MsgOrDefault(helpMessage,
4004 : CPLSPrintf("Input %s dataset",
4005 704 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4006 1408 : pValue, type);
4007 704 : if (positionalAndRequired)
4008 697 : arg.SetPositional().SetRequired();
4009 :
4010 704 : SetAutoCompleteFunctionForFilename(arg, type);
4011 :
4012 704 : AddValidationAction(
4013 155 : [pValue]()
4014 : {
4015 154 : if (pValue->GetName() == "-")
4016 1 : pValue->Set("/vsistdin/");
4017 154 : return true;
4018 : });
4019 :
4020 704 : return arg;
4021 : }
4022 :
4023 : /************************************************************************/
4024 : /* GDALAlgorithm::AddInputDatasetArg() */
4025 : /************************************************************************/
4026 :
4027 13321 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4028 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
4029 : bool positionalAndRequired, const char *helpMessage)
4030 : {
4031 : auto &arg =
4032 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
4033 : MsgOrDefault(
4034 : helpMessage,
4035 : CPLSPrintf("Input %s datasets",
4036 13321 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4037 39963 : pValue, type)
4038 13321 : .SetPackedValuesAllowed(false);
4039 13321 : if (positionalAndRequired)
4040 1722 : arg.SetPositional().SetRequired();
4041 :
4042 13321 : SetAutoCompleteFunctionForFilename(arg, type);
4043 :
4044 13321 : AddValidationAction(
4045 6227 : [pValue]()
4046 : {
4047 11840 : for (auto &val : *pValue)
4048 : {
4049 5613 : if (val.GetName() == "-")
4050 1 : val.Set("/vsistdin/");
4051 : }
4052 6227 : return true;
4053 : });
4054 13321 : return arg;
4055 : }
4056 :
4057 : /************************************************************************/
4058 : /* GDALAlgorithm::AddOutputDatasetArg() */
4059 : /************************************************************************/
4060 :
4061 8281 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4062 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4063 : bool positionalAndRequired, const char *helpMessage)
4064 : {
4065 : auto &arg =
4066 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4067 : MsgOrDefault(
4068 : helpMessage,
4069 : CPLSPrintf("Output %s dataset",
4070 8281 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4071 24843 : pValue, type)
4072 8281 : .SetIsInput(true)
4073 8281 : .SetIsOutput(true)
4074 8281 : .SetDatasetInputFlags(GADV_NAME)
4075 8281 : .SetDatasetOutputFlags(GADV_OBJECT);
4076 8281 : if (positionalAndRequired)
4077 4332 : arg.SetPositional().SetRequired();
4078 :
4079 8281 : AddValidationAction(
4080 11835 : [this, &arg, pValue]()
4081 : {
4082 3633 : if (pValue->GetName() == "-")
4083 4 : pValue->Set("/vsistdout/");
4084 :
4085 3633 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4086 3581 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4087 5768 : (!outputFormatArg->IsExplicitlySet() ||
4088 9401 : outputFormatArg->Get<std::string>().empty()) &&
4089 1394 : arg.IsExplicitlySet())
4090 : {
4091 : const auto vrtCompatible =
4092 1046 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4093 182 : if (vrtCompatible && !vrtCompatible->empty() &&
4094 1228 : vrtCompatible->front() == "false" &&
4095 1137 : EQUAL(
4096 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4097 : "VRT"))
4098 : {
4099 6 : ReportError(
4100 : CE_Failure, CPLE_NotSupported,
4101 : "VRT output is not supported.%s",
4102 6 : outputFormatArg->GetDescription().find("GDALG") !=
4103 : std::string::npos
4104 : ? " Consider using the GDALG driver instead (files "
4105 : "with .gdalg.json extension)"
4106 : : "");
4107 6 : return false;
4108 : }
4109 1040 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4110 2057 : EQUAL(pValue->GetName()
4111 : .substr(pValue->GetName().size() -
4112 : strlen(".gdalg.json"))
4113 : .c_str(),
4114 3097 : ".gdalg.json") &&
4115 27 : outputFormatArg->GetDescription().find("GDALG") ==
4116 : std::string::npos)
4117 : {
4118 0 : ReportError(CE_Failure, CPLE_NotSupported,
4119 : "GDALG output is not supported");
4120 0 : return false;
4121 : }
4122 : }
4123 3627 : return true;
4124 : });
4125 :
4126 8281 : return arg;
4127 : }
4128 :
4129 : /************************************************************************/
4130 : /* GDALAlgorithm::AddOverwriteArg() */
4131 : /************************************************************************/
4132 :
4133 : GDALInConstructionAlgorithmArg &
4134 8191 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4135 : {
4136 : return AddArg(
4137 : GDAL_ARG_NAME_OVERWRITE, 0,
4138 : MsgOrDefault(
4139 : helpMessage,
4140 : _("Whether overwriting existing output dataset is allowed")),
4141 16382 : pValue)
4142 16382 : .SetDefault(false);
4143 : }
4144 :
4145 : /************************************************************************/
4146 : /* GDALAlgorithm::AddOverwriteLayerArg() */
4147 : /************************************************************************/
4148 :
4149 : GDALInConstructionAlgorithmArg &
4150 3424 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4151 : {
4152 3424 : AddValidationAction(
4153 1554 : [this]
4154 : {
4155 1553 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4156 1553 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4157 : {
4158 1 : ReportError(CE_Failure, CPLE_AppDefined,
4159 : "--update argument must exist for "
4160 : "--overwrite-layer, even if hidden");
4161 1 : return false;
4162 : }
4163 1552 : return true;
4164 : });
4165 : return AddArg(
4166 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4167 : MsgOrDefault(
4168 : helpMessage,
4169 : _("Whether overwriting existing output layer is allowed")),
4170 6848 : pValue)
4171 3424 : .SetDefault(false)
4172 : .AddAction(
4173 19 : [this]
4174 : {
4175 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4176 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4177 : {
4178 19 : updateArg->Set(true);
4179 : }
4180 6867 : });
4181 : }
4182 :
4183 : /************************************************************************/
4184 : /* GDALAlgorithm::AddUpdateArg() */
4185 : /************************************************************************/
4186 :
4187 : GDALInConstructionAlgorithmArg &
4188 3976 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4189 : {
4190 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4191 : MsgOrDefault(
4192 : helpMessage,
4193 : _("Whether to open existing dataset in update mode")),
4194 7952 : pValue)
4195 7952 : .SetDefault(false);
4196 : }
4197 :
4198 : /************************************************************************/
4199 : /* GDALAlgorithm::AddAppendLayerArg() */
4200 : /************************************************************************/
4201 :
4202 : GDALInConstructionAlgorithmArg &
4203 3198 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4204 : {
4205 3198 : AddValidationAction(
4206 1509 : [this]
4207 : {
4208 1508 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4209 1508 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4210 : {
4211 1 : ReportError(CE_Failure, CPLE_AppDefined,
4212 : "--update argument must exist for --append, even "
4213 : "if hidden");
4214 1 : return false;
4215 : }
4216 1507 : return true;
4217 : });
4218 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4219 : MsgOrDefault(
4220 : helpMessage,
4221 : _("Whether appending to existing layer is allowed")),
4222 6396 : pValue)
4223 3198 : .SetDefault(false)
4224 : .AddAction(
4225 25 : [this]
4226 : {
4227 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4228 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4229 : {
4230 25 : updateArg->Set(true);
4231 : }
4232 6421 : });
4233 : }
4234 :
4235 : /************************************************************************/
4236 : /* GDALAlgorithm::AddOptionsSuggestions() */
4237 : /************************************************************************/
4238 :
4239 : /* static */
4240 29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4241 : const std::string ¤tValue,
4242 : std::vector<std::string> &oRet)
4243 : {
4244 29 : if (!pszXML)
4245 0 : return false;
4246 58 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4247 29 : if (!poTree)
4248 0 : return false;
4249 :
4250 58 : std::string typedOptionName = currentValue;
4251 29 : const auto posEqual = typedOptionName.find('=');
4252 58 : std::string typedValue;
4253 29 : if (posEqual != 0 && posEqual != std::string::npos)
4254 : {
4255 2 : typedValue = currentValue.substr(posEqual + 1);
4256 2 : typedOptionName.resize(posEqual);
4257 : }
4258 :
4259 405 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4260 376 : psChild = psChild->psNext)
4261 : {
4262 389 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4263 402 : if (pszName && typedOptionName == pszName &&
4264 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4265 2 : strcmp(psChild->pszValue, "Argument") == 0))
4266 : {
4267 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4268 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4269 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4270 13 : if (EQUAL(pszType, "string-select"))
4271 : {
4272 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4273 85 : psChild2 = psChild2->psNext)
4274 : {
4275 85 : if (EQUAL(psChild2->pszValue, "Value"))
4276 : {
4277 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4278 : }
4279 : }
4280 : }
4281 8 : else if (EQUAL(pszType, "boolean"))
4282 : {
4283 3 : if (typedValue == "YES" || typedValue == "NO")
4284 : {
4285 1 : oRet.push_back(currentValue);
4286 1 : return true;
4287 : }
4288 2 : oRet.push_back("NO");
4289 2 : oRet.push_back("YES");
4290 : }
4291 5 : else if (EQUAL(pszType, "int"))
4292 : {
4293 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4294 2 : atoi(pszMax) - atoi(pszMin) < 25)
4295 : {
4296 1 : const int nMax = atoi(pszMax);
4297 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4298 12 : oRet.push_back(std::to_string(i));
4299 : }
4300 : }
4301 :
4302 12 : if (oRet.empty())
4303 : {
4304 4 : if (pszMin && pszMax)
4305 : {
4306 1 : oRet.push_back(std::string("##"));
4307 2 : oRet.push_back(std::string("validity range: [")
4308 1 : .append(pszMin)
4309 1 : .append(",")
4310 1 : .append(pszMax)
4311 1 : .append("]"));
4312 : }
4313 3 : else if (pszMin)
4314 : {
4315 1 : oRet.push_back(std::string("##"));
4316 1 : oRet.push_back(
4317 1 : std::string("validity range: >= ").append(pszMin));
4318 : }
4319 2 : else if (pszMax)
4320 : {
4321 1 : oRet.push_back(std::string("##"));
4322 1 : oRet.push_back(
4323 1 : std::string("validity range: <= ").append(pszMax));
4324 : }
4325 1 : else if (const char *pszDescription =
4326 1 : CPLGetXMLValue(psChild, "description", nullptr))
4327 : {
4328 1 : oRet.push_back(std::string("##"));
4329 2 : oRet.push_back(std::string("type: ")
4330 1 : .append(pszType)
4331 1 : .append(", description: ")
4332 1 : .append(pszDescription));
4333 : }
4334 : }
4335 :
4336 12 : return true;
4337 : }
4338 : }
4339 :
4340 319 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4341 303 : psChild = psChild->psNext)
4342 : {
4343 303 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4344 303 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4345 5 : strcmp(psChild->pszValue, "Argument") == 0))
4346 : {
4347 300 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4348 300 : if (!pszScope ||
4349 40 : (EQUAL(pszScope, "raster") &&
4350 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4351 20 : (EQUAL(pszScope, "vector") &&
4352 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4353 : {
4354 280 : oRet.push_back(std::string(pszName).append("="));
4355 : }
4356 : }
4357 : }
4358 :
4359 16 : return false;
4360 : }
4361 :
4362 : /************************************************************************/
4363 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4364 : /************************************************************************/
4365 :
4366 : //! @cond Doxygen_Suppress
4367 : std::vector<std::string>
4368 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4369 : {
4370 2 : std::vector<std::string> oRet;
4371 :
4372 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4373 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4374 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4375 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4376 : {
4377 2 : datasetType = inputArg->GetDatasetType();
4378 : }
4379 :
4380 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4381 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4382 2 : inputFormat->IsExplicitlySet())
4383 : {
4384 : const auto &aosAllowedDrivers =
4385 1 : inputFormat->Get<std::vector<std::string>>();
4386 1 : if (aosAllowedDrivers.size() == 1)
4387 : {
4388 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4389 1 : aosAllowedDrivers[0].c_str());
4390 1 : if (poDriver)
4391 : {
4392 1 : AddOptionsSuggestions(
4393 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4394 : datasetType, currentValue, oRet);
4395 : }
4396 1 : return oRet;
4397 : }
4398 : }
4399 :
4400 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4401 373 : &oRet](const GDALArgDatasetValue &datasetValue)
4402 : {
4403 1 : auto poDM = GetGDALDriverManager();
4404 :
4405 1 : const auto &osDSName = datasetValue.GetName();
4406 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4407 1 : if (!osExt.empty())
4408 : {
4409 1 : std::set<std::string> oVisitedExtensions;
4410 229 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4411 : {
4412 228 : auto poDriver = poDM->GetDriver(i);
4413 684 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4414 228 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4415 72 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4416 456 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4417 72 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4418 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4419 : {
4420 : const char *pszExtensions =
4421 156 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4422 156 : if (pszExtensions)
4423 : {
4424 : const CPLStringList aosExts(
4425 103 : CSLTokenizeString2(pszExtensions, " ", 0));
4426 227 : for (const char *pszExt : cpl::Iterate(aosExts))
4427 : {
4428 128 : if (EQUAL(pszExt, osExt.c_str()) &&
4429 3 : !cpl::contains(oVisitedExtensions, pszExt))
4430 : {
4431 1 : oVisitedExtensions.insert(pszExt);
4432 1 : if (AddOptionsSuggestions(
4433 : poDriver->GetMetadataItem(
4434 1 : GDAL_DMD_OPENOPTIONLIST),
4435 : datasetType, currentValue, oRet))
4436 : {
4437 0 : return;
4438 : }
4439 1 : break;
4440 : }
4441 : }
4442 : }
4443 : }
4444 : }
4445 : }
4446 1 : };
4447 :
4448 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4449 : {
4450 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4451 0 : AddSuggestions(datasetValue);
4452 : }
4453 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4454 : {
4455 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4456 1 : if (datasetValues.size() == 1)
4457 1 : AddSuggestions(datasetValues[0]);
4458 : }
4459 :
4460 1 : return oRet;
4461 : }
4462 :
4463 : //! @endcond
4464 :
4465 : /************************************************************************/
4466 : /* GDALAlgorithm::AddOpenOptionsArg() */
4467 : /************************************************************************/
4468 :
4469 : GDALInConstructionAlgorithmArg &
4470 9126 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4471 : const char *helpMessage)
4472 : {
4473 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4474 18252 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4475 18252 : .AddAlias("oo")
4476 18252 : .SetMetaVar("<KEY>=<VALUE>")
4477 9126 : .SetPackedValuesAllowed(false)
4478 9126 : .SetCategory(GAAC_ADVANCED);
4479 :
4480 25 : arg.AddValidationAction([this, &arg]()
4481 9151 : { return ParseAndValidateKeyValue(arg); });
4482 :
4483 : arg.SetAutoCompleteFunction(
4484 2 : [this](const std::string ¤tValue)
4485 9128 : { return OpenOptionCompleteFunction(currentValue); });
4486 :
4487 9126 : return arg;
4488 : }
4489 :
4490 : /************************************************************************/
4491 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4492 : /************************************************************************/
4493 :
4494 : GDALInConstructionAlgorithmArg &
4495 3195 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4496 : const char *helpMessage)
4497 : {
4498 : auto &arg =
4499 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4500 6390 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4501 6390 : .AddAlias("output-oo")
4502 6390 : .SetMetaVar("<KEY>=<VALUE>")
4503 3195 : .SetPackedValuesAllowed(false)
4504 3195 : .SetCategory(GAAC_ADVANCED);
4505 :
4506 0 : arg.AddValidationAction([this, &arg]()
4507 3195 : { return ParseAndValidateKeyValue(arg); });
4508 :
4509 : arg.SetAutoCompleteFunction(
4510 0 : [this](const std::string ¤tValue)
4511 3195 : { return OpenOptionCompleteFunction(currentValue); });
4512 :
4513 3195 : return arg;
4514 : }
4515 :
4516 : /************************************************************************/
4517 : /* ValidateFormat() */
4518 : /************************************************************************/
4519 :
4520 4693 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4521 : bool bStreamAllowed,
4522 : bool bGDALGAllowed) const
4523 : {
4524 4693 : if (arg.GetChoices().empty())
4525 : {
4526 : const auto Validate =
4527 20205 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4528 : {
4529 4588 : if (const auto extraFormats =
4530 4588 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4531 : {
4532 60 : for (const auto &extraFormat : *extraFormats)
4533 : {
4534 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4535 14 : return true;
4536 : }
4537 : }
4538 :
4539 4574 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4540 1808 : return true;
4541 :
4542 2772 : if (EQUAL(val.c_str(), "GDALG") &&
4543 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4544 : {
4545 2 : if (bGDALGAllowed)
4546 : {
4547 2 : return true;
4548 : }
4549 : else
4550 : {
4551 0 : ReportError(CE_Failure, CPLE_NotSupported,
4552 : "GDALG output is not supported.");
4553 0 : return false;
4554 : }
4555 : }
4556 :
4557 : const auto vrtCompatible =
4558 2764 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4559 540 : if (vrtCompatible && !vrtCompatible->empty() &&
4560 3304 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4561 : {
4562 7 : ReportError(CE_Failure, CPLE_NotSupported,
4563 : "VRT output is not supported.%s",
4564 : bGDALGAllowed
4565 : ? " Consider using the GDALG driver instead "
4566 : "(files with .gdalg.json extension)."
4567 : : "");
4568 7 : return false;
4569 : }
4570 :
4571 : const auto allowedFormats =
4572 2757 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4573 2781 : if (allowedFormats && !allowedFormats->empty() &&
4574 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4575 2781 : val) != allowedFormats->end())
4576 : {
4577 9 : return true;
4578 : }
4579 :
4580 : const auto excludedFormats =
4581 2748 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4582 2763 : if (excludedFormats && !excludedFormats->empty() &&
4583 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4584 2763 : val) != excludedFormats->end())
4585 : {
4586 0 : ReportError(CE_Failure, CPLE_NotSupported,
4587 : "%s output is not supported.", val.c_str());
4588 0 : return false;
4589 : }
4590 :
4591 2748 : auto hDriver = GDALGetDriverByName(val.c_str());
4592 2748 : if (!hDriver)
4593 : {
4594 : auto poMissingDriver =
4595 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4596 4 : if (poMissingDriver)
4597 : {
4598 : const std::string msg =
4599 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4600 0 : ReportError(CE_Failure, CPLE_AppDefined,
4601 : "Invalid value for argument '%s'. Driver '%s' "
4602 : "not found but is known. However plugin %s",
4603 0 : arg.GetName().c_str(), val.c_str(),
4604 : msg.c_str());
4605 : }
4606 : else
4607 : {
4608 8 : ReportError(CE_Failure, CPLE_AppDefined,
4609 : "Invalid value for argument '%s'. Driver '%s' "
4610 : "does not exist.",
4611 4 : arg.GetName().c_str(), val.c_str());
4612 : }
4613 4 : return false;
4614 : }
4615 :
4616 2744 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4617 2744 : if (caps)
4618 : {
4619 8292 : for (const std::string &cap : *caps)
4620 : {
4621 : const char *pszVal =
4622 5573 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4623 5573 : if (!(pszVal && pszVal[0]))
4624 : {
4625 1569 : if (cap == GDAL_DCAP_CREATECOPY &&
4626 0 : std::find(caps->begin(), caps->end(),
4627 783 : GDAL_DCAP_RASTER) != caps->end() &&
4628 783 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4629 1569 : nullptr) &&
4630 783 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4631 : nullptr))
4632 : {
4633 : // if it supports Create, it supports CreateCopy
4634 : }
4635 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4636 : {
4637 2 : ReportError(
4638 : CE_Failure, CPLE_AppDefined,
4639 : "Invalid value for argument '%s'. Driver '%s' "
4640 : "does "
4641 : "not advertise any file format extension.",
4642 1 : arg.GetName().c_str(), val.c_str());
4643 3 : return false;
4644 : }
4645 : else
4646 : {
4647 2 : if (cap == GDAL_DCAP_CREATE)
4648 : {
4649 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4650 1 : if (updateArg &&
4651 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4652 1 : updateArg->IsExplicitlySet())
4653 : {
4654 0 : continue;
4655 : }
4656 :
4657 2 : ReportError(
4658 : CE_Failure, CPLE_AppDefined,
4659 : "Invalid value for argument '%s'. "
4660 : "Driver '%s' does not have write support.",
4661 1 : arg.GetName().c_str(), val.c_str());
4662 1 : return false;
4663 : }
4664 : else
4665 : {
4666 2 : ReportError(
4667 : CE_Failure, CPLE_AppDefined,
4668 : "Invalid value for argument '%s'. Driver "
4669 : "'%s' "
4670 : "does "
4671 : "not expose the required '%s' capability.",
4672 1 : arg.GetName().c_str(), val.c_str(),
4673 : cap.c_str());
4674 1 : return false;
4675 : }
4676 : }
4677 : }
4678 : }
4679 : }
4680 2741 : return true;
4681 4591 : };
4682 :
4683 4591 : if (arg.GetType() == GAAT_STRING)
4684 : {
4685 4581 : return Validate(arg.Get<std::string>());
4686 : }
4687 12 : else if (arg.GetType() == GAAT_STRING_LIST)
4688 : {
4689 19 : for (const auto &val : arg.Get<std::vector<std::string>>())
4690 : {
4691 9 : if (!Validate(val))
4692 2 : return false;
4693 : }
4694 : }
4695 : }
4696 :
4697 112 : return true;
4698 : }
4699 :
4700 : /************************************************************************/
4701 : /* FormatAutoCompleteFunction() */
4702 : /************************************************************************/
4703 :
4704 : /* static */
4705 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4706 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4707 : {
4708 7 : std::vector<std::string> res;
4709 7 : auto poDM = GetGDALDriverManager();
4710 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4711 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4712 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4713 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4714 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4715 0 : res = std::move(*extraFormats);
4716 1602 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4717 : {
4718 1595 : auto poDriver = poDM->GetDriver(i);
4719 :
4720 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4721 1595 : vrtCompatible->front() == "false" &&
4722 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4723 : {
4724 : // do nothing
4725 : }
4726 1595 : else if (allowedFormats && !allowedFormats->empty() &&
4727 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4728 1595 : poDriver->GetDescription()) != allowedFormats->end())
4729 : {
4730 0 : res.push_back(poDriver->GetDescription());
4731 : }
4732 1595 : else if (excludedFormats && !excludedFormats->empty() &&
4733 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4734 0 : poDriver->GetDescription()) !=
4735 1595 : excludedFormats->end())
4736 : {
4737 0 : continue;
4738 : }
4739 1595 : else if (caps)
4740 : {
4741 1595 : bool ok = true;
4742 3155 : for (const std::string &cap : *caps)
4743 : {
4744 2374 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4745 : {
4746 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4747 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4748 : {
4749 0 : ok = false;
4750 0 : break;
4751 : }
4752 : }
4753 2374 : else if (const char *pszVal =
4754 2374 : poDriver->GetMetadataItem(cap.c_str());
4755 1488 : pszVal && pszVal[0])
4756 : {
4757 : }
4758 1274 : else if (cap == GDAL_DCAP_CREATECOPY &&
4759 0 : (std::find(caps->begin(), caps->end(),
4760 388 : GDAL_DCAP_RASTER) != caps->end() &&
4761 1662 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4762 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4763 : {
4764 : // if it supports Create, it supports CreateCopy
4765 : }
4766 : else
4767 : {
4768 814 : ok = false;
4769 814 : break;
4770 : }
4771 : }
4772 1595 : if (ok)
4773 : {
4774 781 : res.push_back(poDriver->GetDescription());
4775 : }
4776 : }
4777 : }
4778 7 : if (bGDALGAllowed)
4779 4 : res.push_back("GDALG");
4780 7 : return res;
4781 : }
4782 :
4783 : /************************************************************************/
4784 : /* GDALAlgorithm::AddInputFormatsArg() */
4785 : /************************************************************************/
4786 :
4787 : GDALInConstructionAlgorithmArg &
4788 8864 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4789 : const char *helpMessage)
4790 : {
4791 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4792 17728 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4793 17728 : .AddAlias("if")
4794 8864 : .SetCategory(GAAC_ADVANCED);
4795 12 : arg.AddValidationAction([this, &arg]()
4796 8876 : { return ValidateFormat(arg, false, false); });
4797 : arg.SetAutoCompleteFunction(
4798 1 : [&arg](const std::string &)
4799 8865 : { return FormatAutoCompleteFunction(arg, false, false); });
4800 8864 : return arg;
4801 : }
4802 :
4803 : /************************************************************************/
4804 : /* GDALAlgorithm::AddOutputFormatArg() */
4805 : /************************************************************************/
4806 :
4807 : GDALInConstructionAlgorithmArg &
4808 9284 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4809 : bool bGDALGAllowed, const char *helpMessage)
4810 : {
4811 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4812 : MsgOrDefault(helpMessage,
4813 : bGDALGAllowed
4814 : ? _("Output format (\"GDALG\" allowed)")
4815 : : _("Output format")),
4816 18568 : pValue)
4817 18568 : .AddAlias("of")
4818 9284 : .AddAlias("format");
4819 : arg.AddValidationAction(
4820 4677 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4821 13961 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4822 : arg.SetAutoCompleteFunction(
4823 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4824 : {
4825 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4826 4 : bGDALGAllowed);
4827 9284 : });
4828 9284 : return arg;
4829 : }
4830 :
4831 : /************************************************************************/
4832 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4833 : /************************************************************************/
4834 : GDALInConstructionAlgorithmArg &
4835 1739 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4836 : const char *helpMessage)
4837 : {
4838 : auto &arg =
4839 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4840 3478 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4841 3478 : .AddAlias("ot")
4842 3478 : .AddAlias("datatype")
4843 5217 : .AddMetadataItem("type", {"GDALDataType"})
4844 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4845 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4846 1739 : "Float32", "Float64", "CFloat32", "CFloat64")
4847 1739 : .SetHiddenChoices("Byte");
4848 1739 : return arg;
4849 : }
4850 :
4851 : /************************************************************************/
4852 : /* GDALAlgorithm::AddNodataArg() */
4853 : /************************************************************************/
4854 :
4855 : GDALInConstructionAlgorithmArg &
4856 655 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4857 : const std::string &optionName,
4858 : const char *helpMessage)
4859 : {
4860 : auto &arg = AddArg(
4861 : optionName, 0,
4862 : MsgOrDefault(helpMessage,
4863 : noneAllowed
4864 : ? _("Assign a specified nodata value to output bands "
4865 : "('none', numeric value, 'nan', 'inf', '-inf')")
4866 : : _("Assign a specified nodata value to output bands "
4867 : "(numeric value, 'nan', 'inf', '-inf')")),
4868 655 : pValue);
4869 : arg.AddValidationAction(
4870 356 : [this, pValue, noneAllowed, optionName]()
4871 : {
4872 77 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4873 : {
4874 67 : char *endptr = nullptr;
4875 67 : CPLStrtod(pValue->c_str(), &endptr);
4876 67 : if (endptr != pValue->c_str() + pValue->size())
4877 : {
4878 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4879 : "Value of '%s' should be %sa "
4880 : "numeric value, 'nan', 'inf' or '-inf'",
4881 : optionName.c_str(),
4882 : noneAllowed ? "'none', " : "");
4883 1 : return false;
4884 : }
4885 : }
4886 76 : return true;
4887 655 : });
4888 655 : return arg;
4889 : }
4890 :
4891 : /************************************************************************/
4892 : /* GDALAlgorithm::AddOutputStringArg() */
4893 : /************************************************************************/
4894 :
4895 : GDALInConstructionAlgorithmArg &
4896 5937 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4897 : {
4898 : return AddArg(
4899 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4900 : MsgOrDefault(helpMessage,
4901 : _("Output string, in which the result is placed")),
4902 11874 : pValue)
4903 5937 : .SetHiddenForCLI()
4904 5937 : .SetIsInput(false)
4905 11874 : .SetIsOutput(true);
4906 : }
4907 :
4908 : /************************************************************************/
4909 : /* GDALAlgorithm::AddStdoutArg() */
4910 : /************************************************************************/
4911 :
4912 : GDALInConstructionAlgorithmArg &
4913 1477 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4914 : {
4915 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4916 : MsgOrDefault(helpMessage,
4917 : _("Directly output on stdout. If enabled, "
4918 : "output-string will be empty")),
4919 2954 : pValue)
4920 2954 : .SetHidden();
4921 : }
4922 :
4923 : /************************************************************************/
4924 : /* GDALAlgorithm::AddLayerNameArg() */
4925 : /************************************************************************/
4926 :
4927 : GDALInConstructionAlgorithmArg &
4928 208 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4929 : {
4930 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4931 208 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4932 : }
4933 :
4934 : /************************************************************************/
4935 : /* GDALAlgorithm::AddArrayNameArg() */
4936 : /************************************************************************/
4937 :
4938 : GDALInConstructionAlgorithmArg &
4939 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
4940 : {
4941 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
4942 100 : pValue)
4943 2 : .SetAutoCompleteFunction([this](const std::string &)
4944 102 : { return AutoCompleteArrayName(); });
4945 : }
4946 :
4947 : /************************************************************************/
4948 : /* GDALAlgorithm::AddArrayNameArg() */
4949 : /************************************************************************/
4950 :
4951 : GDALInConstructionAlgorithmArg &
4952 76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
4953 : const char *helpMessage)
4954 : {
4955 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
4956 152 : pValue)
4957 0 : .SetAutoCompleteFunction([this](const std::string &)
4958 152 : { return AutoCompleteArrayName(); });
4959 : }
4960 :
4961 : /************************************************************************/
4962 : /* GDALAlgorithm::AutoCompleteArrayName() */
4963 : /************************************************************************/
4964 :
4965 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
4966 : {
4967 2 : std::vector<std::string> ret;
4968 4 : std::string osDSName;
4969 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4970 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4971 : {
4972 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4973 0 : if (!inputDatasets.empty())
4974 : {
4975 0 : osDSName = inputDatasets[0].GetName();
4976 : }
4977 : }
4978 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
4979 : {
4980 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
4981 2 : osDSName = inputDataset.GetName();
4982 : }
4983 :
4984 2 : if (!osDSName.empty())
4985 : {
4986 4 : CPLStringList aosAllowedDrivers;
4987 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4988 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
4989 : aosAllowedDrivers =
4990 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
4991 :
4992 4 : CPLStringList aosOpenOptions;
4993 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
4994 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
4995 : aosOpenOptions =
4996 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
4997 :
4998 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4999 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
5000 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
5001 : {
5002 2 : if (auto poRG = poDS->GetRootGroup())
5003 : {
5004 1 : ret = poRG->GetMDArrayFullNamesRecursive();
5005 : }
5006 : }
5007 : }
5008 :
5009 4 : return ret;
5010 : }
5011 :
5012 : /************************************************************************/
5013 : /* GDALAlgorithm::AddMemorySizeArg() */
5014 : /************************************************************************/
5015 :
5016 : GDALInConstructionAlgorithmArg &
5017 224 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
5018 : const std::string &optionName,
5019 : const char *helpMessage)
5020 : {
5021 448 : return AddArg(optionName, 0, helpMessage, pStrValue)
5022 224 : .SetDefault(*pStrValue)
5023 : .AddValidationAction(
5024 139 : [this, pValue, pStrValue]()
5025 : {
5026 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
5027 : GIntBig nBytes;
5028 : bool bUnitSpecified;
5029 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
5030 47 : &bUnitSpecified) != CE_None)
5031 : {
5032 2 : return false;
5033 : }
5034 45 : if (!bUnitSpecified)
5035 : {
5036 1 : ReportError(CE_Failure, CPLE_AppDefined,
5037 : "Memory size must have a unit or be a "
5038 : "percentage of usable RAM (2GB, 5%%, etc.)");
5039 1 : return false;
5040 : }
5041 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5042 : {
5043 : // -1 to please CoverityScan
5044 : if (static_cast<std::uint64_t>(nBytes) >
5045 : std::numeric_limits<size_t>::max() - 1U)
5046 : {
5047 : ReportError(CE_Failure, CPLE_AppDefined,
5048 : "Memory size %s is too large.",
5049 : pStrValue->c_str());
5050 : return false;
5051 : }
5052 : }
5053 :
5054 44 : *pValue = static_cast<size_t>(nBytes);
5055 44 : return true;
5056 448 : });
5057 : }
5058 :
5059 : /************************************************************************/
5060 : /* GDALAlgorithm::AddOutputLayerNameArg() */
5061 : /************************************************************************/
5062 :
5063 : GDALInConstructionAlgorithmArg &
5064 474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5065 : const char *helpMessage)
5066 : {
5067 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5068 474 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5069 : }
5070 :
5071 : /************************************************************************/
5072 : /* GDALAlgorithm::AddLayerNameArg() */
5073 : /************************************************************************/
5074 :
5075 : GDALInConstructionAlgorithmArg &
5076 882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5077 : const char *helpMessage)
5078 : {
5079 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5080 882 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5081 : }
5082 :
5083 : /************************************************************************/
5084 : /* GDALAlgorithm::AddGeometryTypeArg() */
5085 : /************************************************************************/
5086 :
5087 : GDALInConstructionAlgorithmArg &
5088 422 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5089 : {
5090 : return AddArg("geometry-type", 0,
5091 844 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5092 : .SetAutoCompleteFunction(
5093 3 : [](const std::string ¤tValue)
5094 : {
5095 3 : std::vector<std::string> oRet;
5096 51 : for (const char *type :
5097 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5098 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5099 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5100 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5101 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5102 : {
5103 68 : if (currentValue.empty() ||
5104 17 : STARTS_WITH(type, currentValue.c_str()))
5105 : {
5106 35 : oRet.push_back(type);
5107 35 : oRet.push_back(std::string(type).append("Z"));
5108 35 : oRet.push_back(std::string(type).append("M"));
5109 35 : oRet.push_back(std::string(type).append("ZM"));
5110 : }
5111 : }
5112 3 : return oRet;
5113 844 : })
5114 : .AddValidationAction(
5115 118 : [this, pValue]()
5116 : {
5117 107 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5118 115 : wkbUnknown &&
5119 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5120 : {
5121 3 : ReportError(CE_Failure, CPLE_AppDefined,
5122 : "Invalid geometry type '%s'", pValue->c_str());
5123 3 : return false;
5124 : }
5125 104 : return true;
5126 844 : });
5127 : }
5128 :
5129 : /************************************************************************/
5130 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
5131 : /************************************************************************/
5132 :
5133 : /* static */
5134 2917 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5135 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5136 : {
5137 2917 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5138 : datasetArg.GetType() == GAAT_DATASET_LIST);
5139 :
5140 : layerArg.SetAutoCompleteFunction(
5141 18 : [&datasetArg](const std::string ¤tValue)
5142 : {
5143 6 : std::vector<std::string> ret;
5144 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5145 6 : GDALArgDatasetValue *dsVal = nullptr;
5146 6 : if (datasetArg.GetType() == GAAT_DATASET)
5147 : {
5148 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5149 : }
5150 : else
5151 : {
5152 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5153 6 : if (val.size() == 1)
5154 : {
5155 6 : dsVal = &val[0];
5156 : }
5157 : }
5158 6 : if (dsVal && !dsVal->GetName().empty())
5159 : {
5160 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5161 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5162 6 : if (poDS)
5163 : {
5164 12 : for (auto &&poLayer : poDS->GetLayers())
5165 : {
5166 6 : if (currentValue == poLayer->GetDescription())
5167 : {
5168 1 : ret.clear();
5169 1 : ret.push_back(poLayer->GetDescription());
5170 1 : break;
5171 : }
5172 5 : ret.push_back(poLayer->GetDescription());
5173 : }
5174 : }
5175 : }
5176 12 : return ret;
5177 2917 : });
5178 2917 : }
5179 :
5180 : /************************************************************************/
5181 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5182 : /************************************************************************/
5183 :
5184 131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5185 : GDALInConstructionAlgorithmArg &fieldArg,
5186 : GDALInConstructionAlgorithmArg &layerNameArg,
5187 : std::vector<GDALArgDatasetValue> &datasetArg)
5188 : {
5189 :
5190 : fieldArg.SetAutoCompleteFunction(
5191 12 : [&datasetArg, &layerNameArg](const std::string ¤tValue)
5192 : {
5193 8 : std::set<std::string> ret;
5194 4 : if (!datasetArg.empty())
5195 : {
5196 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5197 :
5198 14 : auto getLayerFields = [&ret, ¤tValue](OGRLayer *poLayer)
5199 : {
5200 2 : auto poDefn = poLayer->GetLayerDefn();
5201 2 : const int nFieldCount = poDefn->GetFieldCount();
5202 8 : for (int iField = 0; iField < nFieldCount; iField++)
5203 : {
5204 : const char *fieldName =
5205 6 : poDefn->GetFieldDefn(iField)->GetNameRef();
5206 6 : if (currentValue == fieldName)
5207 : {
5208 0 : ret.clear();
5209 0 : ret.insert(fieldName);
5210 0 : break;
5211 : }
5212 6 : ret.insert(fieldName);
5213 : }
5214 2 : };
5215 :
5216 2 : GDALArgDatasetValue &dsVal = datasetArg[0];
5217 :
5218 2 : if (!dsVal.GetName().empty())
5219 : {
5220 : auto poDS = std::unique_ptr<GDALDataset>(
5221 2 : GDALDataset::Open(dsVal.GetName().c_str(),
5222 4 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5223 2 : if (poDS)
5224 : {
5225 2 : const auto &layerName = layerNameArg.Get<std::string>();
5226 2 : if (layerName.empty())
5227 : {
5228 : // Loop through all layers
5229 4 : for (auto &&poLayer : poDS->GetLayers())
5230 : {
5231 2 : getLayerFields(poLayer);
5232 : }
5233 : }
5234 : else
5235 : {
5236 0 : const auto poLayer = poDS->GetLayerByName(
5237 0 : layerNameArg.Get<std::string>().c_str());
5238 0 : if (poLayer)
5239 : {
5240 0 : getLayerFields(poLayer);
5241 : }
5242 : }
5243 : }
5244 : }
5245 : }
5246 4 : std::vector<std::string> retVector(ret.begin(), ret.end());
5247 8 : return retVector;
5248 131 : });
5249 131 : }
5250 :
5251 : /************************************************************************/
5252 : /* GDALAlgorithm::AddFieldNameArg() */
5253 : /************************************************************************/
5254 :
5255 : GDALInConstructionAlgorithmArg &
5256 131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5257 : {
5258 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5259 131 : pValue);
5260 : }
5261 :
5262 : /************************************************************************/
5263 : /* GDALAlgorithm::ParseFieldDefinition() */
5264 : /************************************************************************/
5265 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5266 : OGRFieldDefn *poFieldDefn,
5267 : std::string *posError)
5268 : {
5269 : static const std::regex re(
5270 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5271 134 : std::smatch match;
5272 67 : if (std::regex_match(posStrDef, match, re))
5273 : {
5274 132 : const std::string name = match[1];
5275 132 : const std::string type = match[2];
5276 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5277 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5278 66 : poFieldDefn->SetName(name.c_str());
5279 :
5280 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5281 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5282 : {
5283 1 : if (posError)
5284 1 : *posError = "Unsupported field type: " + type;
5285 :
5286 1 : return false;
5287 : }
5288 65 : poFieldDefn->SetType(typeEnum);
5289 65 : poFieldDefn->SetWidth(width);
5290 65 : poFieldDefn->SetPrecision(precision);
5291 65 : return true;
5292 : }
5293 :
5294 1 : if (posError)
5295 : *posError = "Invalid field definition format. Expected "
5296 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5297 :
5298 1 : return false;
5299 : }
5300 :
5301 : /************************************************************************/
5302 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5303 : /************************************************************************/
5304 :
5305 : GDALInConstructionAlgorithmArg &
5306 129 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5307 : std::vector<OGRFieldDefn> *pFieldDefns,
5308 : const char *helpMessage)
5309 : {
5310 : auto &arg =
5311 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5312 258 : pValues)
5313 258 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5314 129 : .SetPackedValuesAllowed(true)
5315 129 : .SetRepeatedArgAllowed(true);
5316 :
5317 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5318 : {
5319 65 : pFieldDefns->clear();
5320 130 : for (const auto &strValue : *pValues)
5321 : {
5322 67 : OGRFieldDefn fieldDefn("", OFTString);
5323 67 : std::string error;
5324 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5325 : &error))
5326 : {
5327 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5328 2 : return false;
5329 : }
5330 : // Check uniqueness of field names
5331 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5332 : {
5333 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5334 : fieldDefn.GetNameRef()))
5335 : {
5336 0 : ReportError(CE_Failure, CPLE_AppDefined,
5337 : "Duplicate field name: '%s'",
5338 : fieldDefn.GetNameRef());
5339 0 : return false;
5340 : }
5341 : }
5342 65 : pFieldDefns->push_back(fieldDefn);
5343 : }
5344 63 : return true;
5345 129 : };
5346 :
5347 129 : arg.AddValidationAction(std::move(validationFunction));
5348 :
5349 129 : return arg;
5350 : }
5351 :
5352 : /************************************************************************/
5353 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5354 : /************************************************************************/
5355 :
5356 262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5357 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5358 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5359 : {
5360 : auto &arg =
5361 524 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5362 786 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5363 : .SetAutoCompleteFunction(
5364 1 : [](const std::string ¤tValue)
5365 : {
5366 1 : std::vector<std::string> oRet;
5367 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5368 : {
5369 : const char *pszSubType =
5370 5 : OGRFieldDefn::GetFieldSubTypeName(
5371 : static_cast<OGRFieldSubType>(i));
5372 5 : if (pszSubType != nullptr)
5373 : {
5374 5 : if (currentValue.empty() ||
5375 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5376 : {
5377 5 : oRet.push_back(pszSubType);
5378 : }
5379 : }
5380 : }
5381 :
5382 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5383 : {
5384 : // Skip deprecated
5385 14 : if (static_cast<OGRFieldType>(i) ==
5386 13 : OGRFieldType::OFTWideString ||
5387 : static_cast<OGRFieldType>(i) ==
5388 : OGRFieldType::OFTWideStringList)
5389 2 : continue;
5390 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5391 : static_cast<OGRFieldType>(i));
5392 12 : if (pszType != nullptr)
5393 : {
5394 12 : if (currentValue.empty() ||
5395 0 : STARTS_WITH(pszType, currentValue.c_str()))
5396 : {
5397 12 : oRet.push_back(pszType);
5398 : }
5399 : }
5400 : }
5401 1 : return oRet;
5402 262 : });
5403 :
5404 : auto validationFunction =
5405 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5406 : {
5407 120 : bool isValid{true};
5408 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5409 :
5410 : // String is returned for unknown types
5411 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5412 : {
5413 16 : isValid = false;
5414 : }
5415 :
5416 120 : *pSubtypeValue =
5417 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5418 :
5419 120 : if (*pSubtypeValue != OFSTNone)
5420 : {
5421 15 : isValid = true;
5422 15 : switch (*pSubtypeValue)
5423 : {
5424 6 : case OFSTBoolean:
5425 : case OFSTInt16:
5426 : {
5427 6 : *pTypeValue = OFTInteger;
5428 6 : break;
5429 : }
5430 3 : case OFSTFloat32:
5431 : {
5432 3 : *pTypeValue = OFTReal;
5433 3 : break;
5434 : }
5435 6 : default:
5436 : {
5437 6 : *pTypeValue = OFTString;
5438 6 : break;
5439 : }
5440 : }
5441 : }
5442 :
5443 120 : if (!isValid)
5444 : {
5445 2 : ReportError(CE_Failure, CPLE_AppDefined,
5446 : "Invalid value for argument '%s': '%s'",
5447 1 : arg.GetName().c_str(), pStrValue->c_str());
5448 : }
5449 :
5450 120 : return isValid;
5451 262 : };
5452 :
5453 262 : if (!pStrValue->empty())
5454 : {
5455 0 : arg.SetDefault(*pStrValue);
5456 0 : validationFunction();
5457 : }
5458 :
5459 262 : arg.AddValidationAction(std::move(validationFunction));
5460 :
5461 262 : return arg;
5462 : }
5463 :
5464 : /************************************************************************/
5465 : /* GDALAlgorithm::ValidateBandArg() */
5466 : /************************************************************************/
5467 :
5468 4139 : bool GDALAlgorithm::ValidateBandArg() const
5469 : {
5470 4139 : bool ret = true;
5471 4139 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5472 4139 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5473 1639 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5474 292 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5475 5772 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5476 149 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5477 : {
5478 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5479 : {
5480 99 : if (nBand > poDS->GetRasterCount())
5481 : {
5482 5 : ReportError(CE_Failure, CPLE_AppDefined,
5483 : "Value of 'band' should be greater or equal than "
5484 : "1 and less or equal than %d.",
5485 : poDS->GetRasterCount());
5486 5 : return false;
5487 : }
5488 94 : return true;
5489 92 : };
5490 :
5491 : const auto ValidateForOneDataset =
5492 304 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5493 : {
5494 87 : bool l_ret = true;
5495 87 : if (bandArg->GetType() == GAAT_INTEGER)
5496 : {
5497 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5498 : }
5499 63 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5500 : {
5501 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5502 : {
5503 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5504 : }
5505 : }
5506 87 : return l_ret;
5507 92 : };
5508 :
5509 92 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5510 : {
5511 : auto poDS =
5512 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5513 6 : if (poDS && !ValidateForOneDataset(poDS))
5514 2 : ret = false;
5515 : }
5516 : else
5517 : {
5518 86 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5519 85 : for (auto &datasetValue :
5520 256 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5521 : {
5522 85 : auto poDS = datasetValue.GetDatasetRef();
5523 85 : if (poDS && !ValidateForOneDataset(poDS))
5524 3 : ret = false;
5525 : }
5526 : }
5527 : }
5528 4139 : return ret;
5529 : }
5530 :
5531 : /************************************************************************/
5532 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5533 : /************************************************************************/
5534 :
5535 3223 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5536 : {
5537 3223 : return ValidateBandArg();
5538 : }
5539 :
5540 : /************************************************************************/
5541 : /* GDALAlgorithm::AddBandArg() */
5542 : /************************************************************************/
5543 :
5544 : GDALInConstructionAlgorithmArg &
5545 1667 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5546 : {
5547 2105 : AddValidationAction([this]() { return ValidateBandArg(); });
5548 :
5549 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5550 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5551 3334 : pValue)
5552 : .AddValidationAction(
5553 34 : [pValue]()
5554 : {
5555 34 : if (*pValue <= 0)
5556 : {
5557 1 : CPLError(CE_Failure, CPLE_AppDefined,
5558 : "Value of 'band' should greater or equal to 1.");
5559 1 : return false;
5560 : }
5561 33 : return true;
5562 3334 : });
5563 : }
5564 :
5565 : /************************************************************************/
5566 : /* GDALAlgorithm::AddBandArg() */
5567 : /************************************************************************/
5568 :
5569 : GDALInConstructionAlgorithmArg &
5570 853 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5571 : {
5572 1331 : AddValidationAction([this]() { return ValidateBandArg(); });
5573 :
5574 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5575 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5576 1706 : pValue)
5577 : .AddValidationAction(
5578 126 : [pValue]()
5579 : {
5580 397 : for (int val : *pValue)
5581 : {
5582 272 : if (val <= 0)
5583 : {
5584 1 : CPLError(CE_Failure, CPLE_AppDefined,
5585 : "Value of 'band' should greater or equal "
5586 : "to 1.");
5587 1 : return false;
5588 : }
5589 : }
5590 125 : return true;
5591 1706 : });
5592 : }
5593 :
5594 : /************************************************************************/
5595 : /* ParseAndValidateKeyValue() */
5596 : /************************************************************************/
5597 :
5598 543 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5599 : {
5600 503 : const auto Validate = [this, &arg](const std::string &val)
5601 : {
5602 498 : if (val.find('=') == std::string::npos)
5603 : {
5604 5 : ReportError(
5605 : CE_Failure, CPLE_AppDefined,
5606 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5607 5 : arg.GetName().c_str());
5608 5 : return false;
5609 : }
5610 :
5611 493 : return true;
5612 543 : };
5613 :
5614 543 : if (arg.GetType() == GAAT_STRING)
5615 : {
5616 0 : return Validate(arg.Get<std::string>());
5617 : }
5618 543 : else if (arg.GetType() == GAAT_STRING_LIST)
5619 : {
5620 543 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5621 543 : if (vals.size() == 1)
5622 : {
5623 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5624 880 : std::vector<std::string> newVals;
5625 880 : std::string curToken;
5626 440 : bool canSplitOnComma = true;
5627 440 : char lastSep = 0;
5628 440 : bool inString = false;
5629 440 : bool equalFoundInLastToken = false;
5630 6501 : for (char c : vals[0])
5631 : {
5632 6065 : if (!inString && c == ',')
5633 : {
5634 10 : if (lastSep != '=' || !equalFoundInLastToken)
5635 : {
5636 2 : canSplitOnComma = false;
5637 2 : break;
5638 : }
5639 8 : lastSep = c;
5640 8 : newVals.push_back(curToken);
5641 8 : curToken.clear();
5642 8 : equalFoundInLastToken = false;
5643 : }
5644 6055 : else if (!inString && c == '=')
5645 : {
5646 439 : if (lastSep == '=')
5647 : {
5648 2 : canSplitOnComma = false;
5649 2 : break;
5650 : }
5651 437 : equalFoundInLastToken = true;
5652 437 : lastSep = c;
5653 437 : curToken += c;
5654 : }
5655 5616 : else if (c == '"')
5656 : {
5657 4 : inString = !inString;
5658 4 : curToken += c;
5659 : }
5660 : else
5661 : {
5662 5612 : curToken += c;
5663 : }
5664 : }
5665 440 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5666 : {
5667 427 : if (!curToken.empty())
5668 427 : newVals.emplace_back(std::move(curToken));
5669 427 : vals = std::move(newVals);
5670 : }
5671 : }
5672 :
5673 1036 : for (const auto &val : vals)
5674 : {
5675 498 : if (!Validate(val))
5676 5 : return false;
5677 : }
5678 : }
5679 :
5680 538 : return true;
5681 : }
5682 :
5683 : /************************************************************************/
5684 : /* IsGDALGOutput() */
5685 : /************************************************************************/
5686 :
5687 2126 : bool GDALAlgorithm::IsGDALGOutput() const
5688 : {
5689 2126 : bool isGDALGOutput = false;
5690 2126 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5691 2126 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5692 3655 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5693 1529 : outputArg->IsExplicitlySet())
5694 : {
5695 3001 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5696 1488 : outputFormatArg->IsExplicitlySet())
5697 : {
5698 : const auto &val =
5699 955 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5700 955 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5701 : }
5702 : else
5703 : {
5704 : const auto &filename =
5705 558 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5706 558 : isGDALGOutput =
5707 1089 : filename.GetName().size() > strlen(".gdalg.json") &&
5708 531 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5709 : strlen(".gdalg.json"),
5710 : ".gdalg.json");
5711 : }
5712 : }
5713 2126 : return isGDALGOutput;
5714 : }
5715 :
5716 : /************************************************************************/
5717 : /* ProcessGDALGOutput() */
5718 : /************************************************************************/
5719 :
5720 2462 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5721 : {
5722 2462 : if (!SupportsStreamedOutput())
5723 798 : return ProcessGDALGOutputRet::NOT_GDALG;
5724 :
5725 1664 : if (IsGDALGOutput())
5726 : {
5727 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5728 : const auto &filename =
5729 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5730 : VSIStatBufL sStat;
5731 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5732 : {
5733 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5734 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5735 : {
5736 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5737 : {
5738 0 : CPLError(CE_Failure, CPLE_AppDefined,
5739 : "File '%s' already exists. Specify the "
5740 : "--overwrite option to overwrite it.",
5741 : filename.c_str());
5742 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5743 : }
5744 : }
5745 : }
5746 :
5747 22 : std::string osCommandLine;
5748 :
5749 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5750 : {
5751 33 : if (!osCommandLine.empty())
5752 22 : osCommandLine += ' ';
5753 33 : osCommandLine += path;
5754 : }
5755 :
5756 250 : for (const auto &arg : GetArgs())
5757 : {
5758 265 : if (arg->IsExplicitlySet() &&
5759 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5760 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5761 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5762 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5763 : {
5764 14 : osCommandLine += ' ';
5765 14 : std::string strArg;
5766 14 : if (!arg->Serialize(strArg))
5767 : {
5768 0 : CPLError(CE_Failure, CPLE_AppDefined,
5769 : "Cannot serialize argument %s",
5770 0 : arg->GetName().c_str());
5771 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5772 : }
5773 14 : osCommandLine += strArg;
5774 : }
5775 : }
5776 :
5777 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5778 :
5779 11 : std::string outStringUnused;
5780 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5781 11 : ? ProcessGDALGOutputRet::GDALG_OK
5782 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5783 : }
5784 :
5785 1653 : return ProcessGDALGOutputRet::NOT_GDALG;
5786 : }
5787 :
5788 : /************************************************************************/
5789 : /* GDALAlgorithm::SaveGDALG() */
5790 : /************************************************************************/
5791 :
5792 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5793 : std::string &outString,
5794 : const std::string &commandLine)
5795 : {
5796 44 : CPLJSONDocument oDoc;
5797 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5798 22 : oDoc.GetRoot().Add("command_line", commandLine);
5799 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5800 :
5801 22 : if (!filename.empty())
5802 21 : return oDoc.Save(filename);
5803 :
5804 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5805 1 : return true;
5806 : }
5807 :
5808 : /************************************************************************/
5809 : /* GDALAlgorithm::AddCreationOptionsArg() */
5810 : /************************************************************************/
5811 :
5812 : GDALInConstructionAlgorithmArg &
5813 8138 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5814 : const char *helpMessage)
5815 : {
5816 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5817 16276 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5818 16276 : .AddAlias("co")
5819 16276 : .SetMetaVar("<KEY>=<VALUE>")
5820 8138 : .SetPackedValuesAllowed(false);
5821 277 : arg.AddValidationAction([this, &arg]()
5822 8415 : { return ParseAndValidateKeyValue(arg); });
5823 :
5824 : arg.SetAutoCompleteFunction(
5825 48 : [this](const std::string ¤tValue)
5826 : {
5827 16 : std::vector<std::string> oRet;
5828 :
5829 16 : int datasetType =
5830 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5831 16 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5832 16 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5833 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5834 : {
5835 16 : datasetType = outputArg->GetDatasetType();
5836 : }
5837 :
5838 16 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5839 32 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5840 16 : outputFormat->IsExplicitlySet())
5841 : {
5842 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5843 6 : outputFormat->Get<std::string>().c_str());
5844 6 : if (poDriver)
5845 : {
5846 6 : AddOptionsSuggestions(
5847 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5848 : datasetType, currentValue, oRet);
5849 : }
5850 6 : return oRet;
5851 : }
5852 :
5853 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5854 : {
5855 10 : auto poDM = GetGDALDriverManager();
5856 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5857 10 : const auto &osDSName = datasetValue.GetName();
5858 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5859 10 : if (!osExt.empty())
5860 : {
5861 10 : std::set<std::string> oVisitedExtensions;
5862 715 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5863 : {
5864 712 : auto poDriver = poDM->GetDriver(i);
5865 2136 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5866 712 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5867 216 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5868 1424 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5869 216 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5870 0 : poDriver->GetMetadataItem(
5871 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5872 : {
5873 : const char *pszExtensions =
5874 496 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5875 496 : if (pszExtensions)
5876 : {
5877 : const CPLStringList aosExts(
5878 323 : CSLTokenizeString2(pszExtensions, " ", 0));
5879 716 : for (const char *pszExt : cpl::Iterate(aosExts))
5880 : {
5881 419 : if (EQUAL(pszExt, osExt.c_str()) &&
5882 16 : !cpl::contains(oVisitedExtensions,
5883 : pszExt))
5884 : {
5885 10 : oVisitedExtensions.insert(pszExt);
5886 10 : if (AddOptionsSuggestions(
5887 : poDriver->GetMetadataItem(
5888 10 : GDAL_DMD_CREATIONOPTIONLIST),
5889 : datasetType, currentValue,
5890 : oRet))
5891 : {
5892 7 : return oRet;
5893 : }
5894 3 : break;
5895 : }
5896 : }
5897 : }
5898 : }
5899 : }
5900 : }
5901 : }
5902 :
5903 3 : return oRet;
5904 8138 : });
5905 :
5906 8138 : return arg;
5907 : }
5908 :
5909 : /************************************************************************/
5910 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
5911 : /************************************************************************/
5912 :
5913 : GDALInConstructionAlgorithmArg &
5914 3894 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
5915 : const char *helpMessage)
5916 : {
5917 : auto &arg =
5918 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
5919 7788 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
5920 7788 : .AddAlias("lco")
5921 7788 : .SetMetaVar("<KEY>=<VALUE>")
5922 3894 : .SetPackedValuesAllowed(false);
5923 77 : arg.AddValidationAction([this, &arg]()
5924 3971 : { return ParseAndValidateKeyValue(arg); });
5925 :
5926 : arg.SetAutoCompleteFunction(
5927 5 : [this](const std::string ¤tValue)
5928 : {
5929 2 : std::vector<std::string> oRet;
5930 :
5931 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5932 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5933 2 : outputFormat->IsExplicitlySet())
5934 : {
5935 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5936 1 : outputFormat->Get<std::string>().c_str());
5937 1 : if (poDriver)
5938 : {
5939 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
5940 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5941 : GDAL_OF_VECTOR, currentValue, oRet);
5942 : }
5943 1 : return oRet;
5944 : }
5945 :
5946 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5947 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5948 : {
5949 1 : auto poDM = GetGDALDriverManager();
5950 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5951 1 : const auto &osDSName = datasetValue.GetName();
5952 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5953 1 : if (!osExt.empty())
5954 : {
5955 1 : std::set<std::string> oVisitedExtensions;
5956 229 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5957 : {
5958 228 : auto poDriver = poDM->GetDriver(i);
5959 228 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
5960 : {
5961 : const char *pszExtensions =
5962 91 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5963 91 : if (pszExtensions)
5964 : {
5965 : const CPLStringList aosExts(
5966 62 : CSLTokenizeString2(pszExtensions, " ", 0));
5967 156 : for (const char *pszExt : cpl::Iterate(aosExts))
5968 : {
5969 96 : if (EQUAL(pszExt, osExt.c_str()) &&
5970 1 : !cpl::contains(oVisitedExtensions,
5971 : pszExt))
5972 : {
5973 1 : oVisitedExtensions.insert(pszExt);
5974 1 : if (AddOptionsSuggestions(
5975 : poDriver->GetMetadataItem(
5976 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5977 : GDAL_OF_VECTOR, currentValue,
5978 : oRet))
5979 : {
5980 0 : return oRet;
5981 : }
5982 1 : break;
5983 : }
5984 : }
5985 : }
5986 : }
5987 : }
5988 : }
5989 : }
5990 :
5991 1 : return oRet;
5992 3894 : });
5993 :
5994 3894 : return arg;
5995 : }
5996 :
5997 : /************************************************************************/
5998 : /* GDALAlgorithm::AddBBOXArg() */
5999 : /************************************************************************/
6000 :
6001 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
6002 : GDALInConstructionAlgorithmArg &
6003 1848 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
6004 : {
6005 : auto &arg = AddArg("bbox", 0,
6006 : MsgOrDefault(helpMessage,
6007 : _("Bounding box as xmin,ymin,xmax,ymax")),
6008 3696 : pValue)
6009 1848 : .SetRepeatedArgAllowed(false)
6010 1848 : .SetMinCount(4)
6011 1848 : .SetMaxCount(4)
6012 1848 : .SetDisplayHintAboutRepetition(false);
6013 : arg.AddValidationAction(
6014 167 : [&arg]()
6015 : {
6016 167 : const auto &val = arg.Get<std::vector<double>>();
6017 167 : CPLAssert(val.size() == 4);
6018 167 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
6019 : {
6020 5 : CPLError(CE_Failure, CPLE_AppDefined,
6021 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
6022 : "xmin <= xmax and ymin <= ymax");
6023 5 : return false;
6024 : }
6025 162 : return true;
6026 1848 : });
6027 1848 : return arg;
6028 : }
6029 :
6030 : /************************************************************************/
6031 : /* GDALAlgorithm::AddActiveLayerArg() */
6032 : /************************************************************************/
6033 :
6034 : GDALInConstructionAlgorithmArg &
6035 1723 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6036 : {
6037 : return AddArg("active-layer", 0,
6038 : MsgOrDefault(helpMessage,
6039 : _("Set active layer (if not specified, all)")),
6040 1723 : pValue);
6041 : }
6042 :
6043 : /************************************************************************/
6044 : /* GDALAlgorithm::AddNumThreadsArg() */
6045 : /************************************************************************/
6046 :
6047 : GDALInConstructionAlgorithmArg &
6048 702 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6049 : const char *helpMessage)
6050 : {
6051 : auto &arg =
6052 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6053 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6054 702 : pStrValue);
6055 :
6056 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6057 1404 : _("Number of jobs (read-only, hidden argument)"), pValue)
6058 702 : .SetHidden();
6059 :
6060 2661 : auto lambda = [this, &arg, pValue, pStrValue]
6061 : {
6062 887 : bool bOK = false;
6063 887 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6064 : const int nLimit = std::clamp(
6065 887 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6066 1774 : CPLGetNumCPUs());
6067 : const int nNumThreads =
6068 887 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
6069 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6070 887 : if (bOK)
6071 : {
6072 887 : *pValue = nNumThreads;
6073 : }
6074 : else
6075 : {
6076 0 : ReportError(CE_Failure, CPLE_IllegalArg,
6077 : "Invalid value for '%s' argument",
6078 0 : arg.GetName().c_str());
6079 : }
6080 887 : return bOK;
6081 702 : };
6082 702 : if (!pStrValue->empty())
6083 : {
6084 656 : arg.SetDefault(*pStrValue);
6085 656 : lambda();
6086 : }
6087 702 : arg.AddValidationAction(std::move(lambda));
6088 702 : return arg;
6089 : }
6090 :
6091 : /************************************************************************/
6092 : /* GDALAlgorithm::AddAbsolutePathArg() */
6093 : /************************************************************************/
6094 :
6095 : GDALInConstructionAlgorithmArg &
6096 619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6097 : {
6098 : return AddArg(
6099 : "absolute-path", 0,
6100 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6101 : "should be stored as an absolute path")),
6102 619 : pValue);
6103 : }
6104 :
6105 : /************************************************************************/
6106 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
6107 : /************************************************************************/
6108 :
6109 : GDALInConstructionAlgorithmArg &
6110 137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6111 : const char *helpMessage)
6112 : {
6113 :
6114 : const auto pixelFunctionNames =
6115 137 : VRTDerivedRasterBand::GetPixelFunctionNames();
6116 : return AddArg(
6117 : "pixel-function", 0,
6118 : MsgOrDefault(
6119 : helpMessage,
6120 : _("Specify a pixel function to calculate output value from "
6121 : "overlapping inputs")),
6122 274 : pValue)
6123 274 : .SetChoices(pixelFunctionNames);
6124 : }
6125 :
6126 : /************************************************************************/
6127 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
6128 : /************************************************************************/
6129 :
6130 : GDALInConstructionAlgorithmArg &
6131 137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6132 : const char *helpMessage)
6133 : {
6134 : auto &pixelFunctionArgArg =
6135 : AddArg("pixel-function-arg", 0,
6136 : MsgOrDefault(
6137 : helpMessage,
6138 : _("Specify argument(s) to pass to the pixel function")),
6139 274 : pValue)
6140 274 : .SetMetaVar("<NAME>=<VALUE>")
6141 137 : .SetRepeatedArgAllowed(true);
6142 : pixelFunctionArgArg.AddValidationAction(
6143 7 : [this, &pixelFunctionArgArg]()
6144 144 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6145 :
6146 : pixelFunctionArgArg.SetAutoCompleteFunction(
6147 12 : [this](const std::string ¤tValue)
6148 : {
6149 12 : std::string pixelFunction;
6150 6 : const auto pixelFunctionArg = GetArg("pixel-function");
6151 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6152 : {
6153 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
6154 : }
6155 :
6156 6 : std::vector<std::string> ret;
6157 :
6158 6 : if (!pixelFunction.empty())
6159 : {
6160 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6161 : pixelFunction.c_str());
6162 5 : if (!pair)
6163 : {
6164 1 : ret.push_back("**");
6165 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6166 1 : ret.push_back(std::string("\xC2\xA0"
6167 : "Invalid pixel function name"));
6168 : }
6169 4 : else if (pair->second.find("Argument name=") ==
6170 : std::string::npos)
6171 : {
6172 1 : ret.push_back("**");
6173 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6174 1 : ret.push_back(
6175 2 : std::string(
6176 : "\xC2\xA0"
6177 : "No pixel function arguments for pixel function '")
6178 1 : .append(pixelFunction)
6179 1 : .append("'"));
6180 : }
6181 : else
6182 : {
6183 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6184 : ret);
6185 : }
6186 : }
6187 :
6188 12 : return ret;
6189 137 : });
6190 :
6191 137 : return pixelFunctionArgArg;
6192 : }
6193 :
6194 : /************************************************************************/
6195 : /* GDALAlgorithm::AddProgressArg() */
6196 : /************************************************************************/
6197 :
6198 8231 : void GDALAlgorithm::AddProgressArg()
6199 : {
6200 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6201 16462 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6202 8231 : .SetAvailableInPipelineStep(false)
6203 16462 : .SetCategory(GAAC_COMMON)
6204 8231 : .AddAction([this]() { m_progressBarRequested = false; });
6205 :
6206 16462 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6207 8231 : .SetAvailableInPipelineStep(false)
6208 8231 : .SetHidden();
6209 8231 : }
6210 :
6211 : /************************************************************************/
6212 : /* GDALAlgorithm::Run() */
6213 : /************************************************************************/
6214 :
6215 4636 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6216 : {
6217 4636 : WarnIfDeprecated();
6218 :
6219 4636 : if (m_selectedSubAlg)
6220 : {
6221 411 : if (m_calledFromCommandLine)
6222 233 : m_selectedSubAlg->m_calledFromCommandLine = true;
6223 411 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6224 : }
6225 :
6226 4225 : if (m_helpRequested || m_helpDocRequested)
6227 : {
6228 16 : if (m_calledFromCommandLine)
6229 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6230 16 : return true;
6231 : }
6232 :
6233 4209 : if (m_JSONUsageRequested)
6234 : {
6235 3 : if (m_calledFromCommandLine)
6236 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6237 3 : return true;
6238 : }
6239 :
6240 4206 : if (!ValidateArguments())
6241 125 : return false;
6242 :
6243 4081 : if (m_alreadyRun)
6244 : {
6245 3 : ReportError(CE_Failure, CPLE_AppDefined,
6246 : "Run() can be called only once per algorithm instance");
6247 3 : return false;
6248 : }
6249 4078 : m_alreadyRun = true;
6250 :
6251 4078 : switch (ProcessGDALGOutput())
6252 : {
6253 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6254 0 : return false;
6255 :
6256 11 : case ProcessGDALGOutputRet::GDALG_OK:
6257 11 : return true;
6258 :
6259 4067 : case ProcessGDALGOutputRet::NOT_GDALG:
6260 4067 : break;
6261 : }
6262 :
6263 4067 : if (m_executionForStreamOutput)
6264 : {
6265 93 : if (!CheckSafeForStreamOutput())
6266 : {
6267 4 : return false;
6268 : }
6269 : }
6270 :
6271 4063 : return RunImpl(pfnProgress, pProgressData);
6272 : }
6273 :
6274 : /************************************************************************/
6275 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6276 : /************************************************************************/
6277 :
6278 48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6279 : {
6280 48 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6281 48 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6282 : {
6283 48 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6284 48 : if (!EQUAL(val.c_str(), "stream"))
6285 : {
6286 : // For security reasons, to avoid that reading a .gdalg.json file
6287 : // writes a file on the file system.
6288 4 : ReportError(
6289 : CE_Failure, CPLE_NotSupported,
6290 : "in streamed execution, --format stream should be used");
6291 4 : return false;
6292 : }
6293 : }
6294 44 : return true;
6295 : }
6296 :
6297 : /************************************************************************/
6298 : /* GDALAlgorithm::Finalize() */
6299 : /************************************************************************/
6300 :
6301 1675 : bool GDALAlgorithm::Finalize()
6302 : {
6303 1675 : bool ret = true;
6304 1675 : if (m_selectedSubAlg)
6305 239 : ret = m_selectedSubAlg->Finalize();
6306 :
6307 31193 : for (auto &arg : m_args)
6308 : {
6309 29518 : if (arg->GetType() == GAAT_DATASET)
6310 : {
6311 1334 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6312 : }
6313 28184 : else if (arg->GetType() == GAAT_DATASET_LIST)
6314 : {
6315 2580 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6316 : {
6317 1220 : ret = ds.Close() && ret;
6318 : }
6319 : }
6320 : }
6321 1675 : return ret;
6322 : }
6323 :
6324 : /************************************************************************/
6325 : /* GDALAlgorithm::GetArgNamesForCLI() */
6326 : /************************************************************************/
6327 :
6328 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6329 693 : GDALAlgorithm::GetArgNamesForCLI() const
6330 : {
6331 1386 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6332 :
6333 693 : size_t maxOptLen = 0;
6334 8734 : for (const auto &arg : m_args)
6335 : {
6336 8041 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6337 1713 : continue;
6338 6328 : std::string opt;
6339 6328 : bool addComma = false;
6340 6328 : if (!arg->GetShortName().empty())
6341 : {
6342 1441 : opt += '-';
6343 1441 : opt += arg->GetShortName();
6344 1441 : addComma = true;
6345 : }
6346 6328 : for (char alias : arg->GetShortNameAliases())
6347 : {
6348 0 : if (addComma)
6349 0 : opt += ", ";
6350 0 : opt += "-";
6351 0 : opt += alias;
6352 0 : addComma = true;
6353 : }
6354 7080 : for (const std::string &alias : arg->GetAliases())
6355 : {
6356 752 : if (addComma)
6357 324 : opt += ", ";
6358 752 : opt += "--";
6359 752 : opt += alias;
6360 752 : addComma = true;
6361 : }
6362 6328 : if (!arg->GetName().empty())
6363 : {
6364 6328 : if (addComma)
6365 1869 : opt += ", ";
6366 6328 : opt += "--";
6367 6328 : opt += arg->GetName();
6368 : }
6369 6328 : const auto &metaVar = arg->GetMetaVar();
6370 6328 : if (!metaVar.empty())
6371 : {
6372 3956 : opt += ' ';
6373 3956 : if (metaVar.front() != '<')
6374 2837 : opt += '<';
6375 3956 : opt += metaVar;
6376 3956 : if (metaVar.back() != '>')
6377 2831 : opt += '>';
6378 : }
6379 6328 : maxOptLen = std::max(maxOptLen, opt.size());
6380 6328 : options.emplace_back(arg.get(), opt);
6381 : }
6382 :
6383 1386 : return std::make_pair(std::move(options), maxOptLen);
6384 : }
6385 :
6386 : /************************************************************************/
6387 : /* GDALAlgorithm::GetUsageForCLI() */
6388 : /************************************************************************/
6389 :
6390 : std::string
6391 412 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6392 : const UsageOptions &usageOptions) const
6393 : {
6394 412 : if (m_selectedSubAlg)
6395 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6396 :
6397 810 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6398 810 : std::string osPath;
6399 813 : for (const std::string &s : m_callPath)
6400 : {
6401 408 : if (!osPath.empty())
6402 49 : osPath += ' ';
6403 408 : osPath += s;
6404 : }
6405 405 : osRet += ' ';
6406 405 : osRet += osPath;
6407 :
6408 405 : bool hasNonPositionals = false;
6409 5064 : for (const auto &arg : m_args)
6410 : {
6411 4659 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6412 3356 : hasNonPositionals = true;
6413 : }
6414 :
6415 405 : if (HasSubAlgorithms())
6416 : {
6417 9 : if (m_callPath.size() == 1)
6418 : {
6419 8 : osRet += " <COMMAND>";
6420 8 : if (hasNonPositionals)
6421 8 : osRet += " [OPTIONS]";
6422 8 : if (usageOptions.isPipelineStep)
6423 : {
6424 5 : const size_t nLenFirstLine = osRet.size();
6425 5 : osRet += '\n';
6426 5 : osRet.append(nLenFirstLine, '-');
6427 5 : osRet += '\n';
6428 : }
6429 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6430 : }
6431 : else
6432 : {
6433 1 : osRet += " <SUBCOMMAND>";
6434 1 : if (hasNonPositionals)
6435 1 : osRet += " [OPTIONS]";
6436 1 : if (usageOptions.isPipelineStep)
6437 : {
6438 0 : const size_t nLenFirstLine = osRet.size();
6439 0 : osRet += '\n';
6440 0 : osRet.append(nLenFirstLine, '-');
6441 0 : osRet += '\n';
6442 : }
6443 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6444 : }
6445 9 : size_t maxNameLen = 0;
6446 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6447 : {
6448 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6449 : }
6450 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6451 : {
6452 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6453 43 : if (subAlg && !subAlg->IsHidden())
6454 : {
6455 43 : const std::string &name(subAlg->GetName());
6456 43 : osRet += " - ";
6457 43 : osRet += name;
6458 43 : osRet += ": ";
6459 43 : osRet.append(maxNameLen - name.size(), ' ');
6460 43 : osRet += subAlg->GetDescription();
6461 43 : if (!subAlg->m_aliases.empty())
6462 : {
6463 6 : bool first = true;
6464 6 : for (const auto &alias : subAlg->GetAliases())
6465 : {
6466 6 : if (alias ==
6467 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6468 6 : break;
6469 0 : if (first)
6470 0 : osRet += " (alias: ";
6471 : else
6472 0 : osRet += ", ";
6473 0 : osRet += alias;
6474 0 : first = false;
6475 : }
6476 6 : if (!first)
6477 : {
6478 0 : osRet += ')';
6479 : }
6480 : }
6481 43 : osRet += '\n';
6482 : }
6483 : }
6484 :
6485 9 : if (shortUsage && hasNonPositionals)
6486 : {
6487 2 : osRet += "\nTry '";
6488 2 : osRet += osPath;
6489 2 : osRet += " --help' for help.\n";
6490 : }
6491 : }
6492 : else
6493 : {
6494 396 : if (!m_args.empty())
6495 : {
6496 396 : if (hasNonPositionals)
6497 396 : osRet += " [OPTIONS]";
6498 584 : for (const auto *arg : m_positionalArgs)
6499 : {
6500 261 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6501 73 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6502 : {
6503 : const bool optional =
6504 199 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6505 28 : arg->GetName() == "pipeline"));
6506 171 : osRet += ' ';
6507 171 : if (optional)
6508 25 : osRet += '[';
6509 171 : const std::string &metavar = arg->GetMetaVar();
6510 171 : if (!metavar.empty() && metavar[0] == '<')
6511 : {
6512 4 : osRet += metavar;
6513 : }
6514 : else
6515 : {
6516 167 : osRet += '<';
6517 167 : osRet += metavar;
6518 167 : osRet += '>';
6519 : }
6520 213 : if (arg->GetType() == GAAT_DATASET_LIST &&
6521 42 : arg->GetMaxCount() > 1)
6522 : {
6523 28 : osRet += "...";
6524 : }
6525 171 : if (optional)
6526 25 : osRet += ']';
6527 : }
6528 : }
6529 : }
6530 :
6531 396 : const size_t nLenFirstLine = osRet.size();
6532 396 : osRet += '\n';
6533 396 : if (usageOptions.isPipelineStep)
6534 : {
6535 309 : osRet.append(nLenFirstLine, '-');
6536 309 : osRet += '\n';
6537 : }
6538 :
6539 396 : if (shortUsage)
6540 : {
6541 21 : osRet += "Try '";
6542 21 : osRet += osPath;
6543 21 : osRet += " --help' for help.\n";
6544 21 : return osRet;
6545 : }
6546 :
6547 375 : osRet += '\n';
6548 375 : osRet += m_description;
6549 375 : osRet += '\n';
6550 : }
6551 :
6552 384 : if (!m_args.empty() && !shortUsage)
6553 : {
6554 764 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6555 : size_t maxOptLen;
6556 382 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6557 382 : if (usageOptions.maxOptLen)
6558 311 : maxOptLen = usageOptions.maxOptLen;
6559 :
6560 764 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6561 382 : if (m_arbitraryLongNameArgsAllowed)
6562 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6563 :
6564 : const auto OutputArg =
6565 2386 : [this, maxOptLen, &osRet,
6566 23765 : &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6567 : {
6568 2386 : osRet += " ";
6569 2386 : osRet += opt;
6570 2386 : osRet += " ";
6571 2386 : osRet.append(maxOptLen - opt.size(), ' ');
6572 2386 : osRet += arg->GetDescription();
6573 :
6574 2386 : const auto &choices = arg->GetChoices();
6575 2386 : if (!choices.empty())
6576 : {
6577 224 : osRet += ". ";
6578 224 : osRet += arg->GetMetaVar();
6579 224 : osRet += '=';
6580 224 : bool firstChoice = true;
6581 1725 : for (const auto &choice : choices)
6582 : {
6583 1501 : if (!firstChoice)
6584 1277 : osRet += '|';
6585 1501 : osRet += choice;
6586 1501 : firstChoice = false;
6587 : }
6588 : }
6589 :
6590 4706 : if (arg->GetType() == GAAT_DATASET ||
6591 2320 : arg->GetType() == GAAT_DATASET_LIST)
6592 : {
6593 144 : if (arg->IsOutput() &&
6594 144 : arg->GetDatasetInputFlags() == GADV_NAME &&
6595 9 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6596 : {
6597 9 : osRet += " (created by algorithm)";
6598 : }
6599 : }
6600 :
6601 2386 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6602 : {
6603 190 : osRet += " (default: ";
6604 190 : osRet += arg->GetDefault<std::string>();
6605 190 : osRet += ')';
6606 : }
6607 2196 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6608 : {
6609 66 : if (arg->GetDefault<bool>())
6610 0 : osRet += " (default: true)";
6611 : }
6612 2130 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6613 : {
6614 76 : osRet += " (default: ";
6615 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6616 76 : osRet += ')';
6617 : }
6618 2054 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6619 : {
6620 49 : osRet += " (default: ";
6621 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6622 49 : osRet += ')';
6623 : }
6624 2417 : else if (arg->GetType() == GAAT_STRING_LIST &&
6625 412 : arg->HasDefaultValue())
6626 : {
6627 : const auto &defaultVal =
6628 9 : arg->GetDefault<std::vector<std::string>>();
6629 9 : if (defaultVal.size() == 1)
6630 : {
6631 9 : osRet += " (default: ";
6632 9 : osRet += defaultVal[0];
6633 9 : osRet += ')';
6634 : }
6635 : }
6636 2023 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6637 27 : arg->HasDefaultValue())
6638 : {
6639 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6640 0 : if (defaultVal.size() == 1)
6641 : {
6642 0 : osRet += " (default: ";
6643 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6644 0 : osRet += ')';
6645 : }
6646 : }
6647 1996 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6648 : {
6649 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6650 0 : if (defaultVal.size() == 1)
6651 : {
6652 0 : osRet += " (default: ";
6653 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6654 0 : osRet += ')';
6655 : }
6656 : }
6657 :
6658 2386 : if (arg->GetDisplayHintAboutRepetition())
6659 : {
6660 2419 : if (arg->GetMinCount() > 0 &&
6661 92 : arg->GetMinCount() == arg->GetMaxCount())
6662 : {
6663 18 : if (arg->GetMinCount() != 1)
6664 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6665 : }
6666 2383 : else if (arg->GetMinCount() > 0 &&
6667 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6668 : {
6669 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6670 8 : arg->GetMaxCount());
6671 : }
6672 2301 : else if (arg->GetMinCount() > 0)
6673 : {
6674 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6675 : }
6676 2235 : else if (arg->GetMaxCount() > 1)
6677 : {
6678 401 : osRet += " [may be repeated]";
6679 : }
6680 : }
6681 :
6682 2386 : if (arg->IsRequired())
6683 : {
6684 169 : osRet += " [required]";
6685 : }
6686 :
6687 2640 : if (!arg->IsAvailableInPipelineStep() &&
6688 254 : !usageOptions.isPipelineStep)
6689 : {
6690 28 : osRet += " [not available in pipelines]";
6691 : }
6692 :
6693 2386 : osRet += '\n';
6694 :
6695 2386 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6696 2386 : if (!mutualExclusionGroup.empty())
6697 : {
6698 434 : std::string otherArgs;
6699 4189 : for (const auto &otherArg : m_args)
6700 : {
6701 7256 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6702 3284 : otherArg.get() == arg)
6703 905 : continue;
6704 3067 : if (otherArg->GetMutualExclusionGroup() ==
6705 : mutualExclusionGroup)
6706 : {
6707 290 : if (!otherArgs.empty())
6708 77 : otherArgs += ", ";
6709 290 : otherArgs += "--";
6710 290 : otherArgs += otherArg->GetName();
6711 : }
6712 : }
6713 217 : if (!otherArgs.empty())
6714 : {
6715 213 : osRet += " ";
6716 213 : osRet += " ";
6717 213 : osRet.append(maxOptLen, ' ');
6718 213 : osRet += "Mutually exclusive with ";
6719 213 : osRet += otherArgs;
6720 213 : osRet += '\n';
6721 : }
6722 : }
6723 :
6724 : // Check dependency
6725 4772 : std::string dependencyArgs;
6726 :
6727 32 : for (const auto &dependencyArgumentName :
6728 2450 : GetArgDependencies(arg->GetName()))
6729 : {
6730 32 : const auto otherArg{GetArg(dependencyArgumentName)};
6731 32 : if (otherArg != nullptr)
6732 : {
6733 32 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6734 : otherArg == arg)
6735 : {
6736 0 : continue;
6737 : }
6738 :
6739 32 : if (!dependencyArgs.empty())
6740 : {
6741 3 : dependencyArgs += ", ";
6742 : }
6743 :
6744 32 : dependencyArgs += "--";
6745 32 : dependencyArgs += otherArg->GetName();
6746 : }
6747 : else
6748 : {
6749 0 : CPLError(CE_Warning, CPLE_AppDefined,
6750 : "Argument '%s' depends on unknown argument '%s'",
6751 0 : arg->GetName().c_str(),
6752 : dependencyArgumentName.c_str());
6753 : }
6754 : }
6755 :
6756 2386 : if (!dependencyArgs.empty())
6757 : {
6758 29 : osRet += " ";
6759 29 : osRet += " ";
6760 29 : osRet.append(maxOptLen, ' ');
6761 29 : osRet += "Depends on ";
6762 29 : osRet += dependencyArgs;
6763 29 : osRet += '\n';
6764 : }
6765 2386 : };
6766 :
6767 382 : if (!m_positionalArgs.empty())
6768 : {
6769 150 : osRet += "\nPositional arguments:\n";
6770 1613 : for (const auto &[arg, opt] : options)
6771 : {
6772 1463 : if (arg->IsPositional())
6773 141 : OutputArg(arg, opt);
6774 : }
6775 : }
6776 :
6777 382 : if (hasNonPositionals)
6778 : {
6779 382 : bool hasCommon = false;
6780 382 : bool hasBase = false;
6781 382 : bool hasAdvanced = false;
6782 382 : bool hasEsoteric = false;
6783 764 : std::vector<std::string> categories;
6784 3739 : for (const auto &iter : options)
6785 : {
6786 3357 : const auto &arg = iter.first;
6787 3357 : if (!arg->IsPositional())
6788 : {
6789 3216 : const auto &category = arg->GetCategory();
6790 3216 : if (category == GAAC_COMMON)
6791 : {
6792 1189 : hasCommon = true;
6793 : }
6794 2027 : else if (category == GAAC_BASE)
6795 : {
6796 1787 : hasBase = true;
6797 : }
6798 240 : else if (category == GAAC_ADVANCED)
6799 : {
6800 178 : hasAdvanced = true;
6801 : }
6802 62 : else if (category == GAAC_ESOTERIC)
6803 : {
6804 29 : hasEsoteric = true;
6805 : }
6806 33 : else if (std::find(categories.begin(), categories.end(),
6807 33 : category) == categories.end())
6808 : {
6809 9 : categories.push_back(category);
6810 : }
6811 : }
6812 : }
6813 382 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6814 67 : categories.insert(categories.begin(), GAAC_ADVANCED);
6815 382 : if (hasBase)
6816 336 : categories.insert(categories.begin(), GAAC_BASE);
6817 382 : if (hasCommon && !usageOptions.isPipelineStep)
6818 68 : categories.insert(categories.begin(), GAAC_COMMON);
6819 382 : if (hasEsoteric)
6820 11 : categories.push_back(GAAC_ESOTERIC);
6821 :
6822 873 : for (const auto &category : categories)
6823 : {
6824 491 : osRet += "\n";
6825 491 : if (category != GAAC_BASE)
6826 : {
6827 155 : osRet += category;
6828 155 : osRet += ' ';
6829 : }
6830 491 : osRet += "Options:\n";
6831 5362 : for (const auto &[arg, opt] : options)
6832 : {
6833 4871 : if (!arg->IsPositional() && arg->GetCategory() == category)
6834 2245 : OutputArg(arg, opt);
6835 : }
6836 491 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6837 : {
6838 2 : osRet += " ";
6839 2 : osRet += userProvidedOpt;
6840 2 : osRet += " ";
6841 2 : if (userProvidedOpt.size() < maxOptLen)
6842 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6843 2 : osRet += "Argument provided by user";
6844 2 : osRet += '\n';
6845 : }
6846 : }
6847 : }
6848 : }
6849 :
6850 384 : if (!m_longDescription.empty())
6851 : {
6852 6 : osRet += '\n';
6853 6 : osRet += m_longDescription;
6854 6 : osRet += '\n';
6855 : }
6856 :
6857 384 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6858 : {
6859 371 : if (!m_helpURL.empty())
6860 : {
6861 371 : osRet += "\nFor more details, consult ";
6862 371 : osRet += GetHelpFullURL();
6863 371 : osRet += '\n';
6864 : }
6865 371 : osRet += GetUsageForCLIEnd();
6866 : }
6867 :
6868 384 : return osRet;
6869 : }
6870 :
6871 : /************************************************************************/
6872 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6873 : /************************************************************************/
6874 :
6875 : //! @cond Doxygen_Suppress
6876 378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6877 : {
6878 378 : std::string osRet;
6879 :
6880 378 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6881 : {
6882 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6883 : "alternative interface to GDAL and OGR command line "
6884 : "utilities.\nThe project reserves the right to modify, "
6885 : "rename, reorganize, and change the behavior of the utility\n"
6886 : "until it is officially frozen in a future feature release of "
6887 13 : "GDAL.\n";
6888 : }
6889 378 : return osRet;
6890 : }
6891 :
6892 : //! @endcond
6893 :
6894 : /************************************************************************/
6895 : /* GDALAlgorithm::GetUsageAsJSON() */
6896 : /************************************************************************/
6897 :
6898 563 : std::string GDALAlgorithm::GetUsageAsJSON() const
6899 : {
6900 1126 : CPLJSONDocument oDoc;
6901 1126 : auto oRoot = oDoc.GetRoot();
6902 :
6903 563 : if (m_displayInJSONUsage)
6904 : {
6905 561 : oRoot.Add("name", m_name);
6906 561 : CPLJSONArray jFullPath;
6907 1168 : for (const std::string &s : m_callPath)
6908 : {
6909 607 : jFullPath.Add(s);
6910 : }
6911 561 : oRoot.Add("full_path", jFullPath);
6912 : }
6913 :
6914 563 : oRoot.Add("description", m_description);
6915 563 : if (!m_helpURL.empty())
6916 : {
6917 562 : oRoot.Add("short_url", m_helpURL);
6918 562 : oRoot.Add("url", GetHelpFullURL());
6919 : }
6920 :
6921 1126 : CPLJSONArray jSubAlgorithms;
6922 763 : for (const auto &subAlgName : GetSubAlgorithmNames())
6923 : {
6924 400 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6925 200 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
6926 : {
6927 198 : CPLJSONDocument oSubDoc;
6928 198 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
6929 198 : jSubAlgorithms.Add(oSubDoc.GetRoot());
6930 : }
6931 : }
6932 563 : oRoot.Add("sub_algorithms", jSubAlgorithms);
6933 :
6934 563 : if (m_arbitraryLongNameArgsAllowed)
6935 : {
6936 1 : oRoot.Add("user_provided_arguments_allowed", true);
6937 : }
6938 :
6939 10870 : const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
6940 : {
6941 5435 : CPLJSONObject jArg;
6942 5435 : jArg.Add("name", arg->GetName());
6943 5435 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
6944 5435 : jArg.Add("description", arg->GetDescription());
6945 :
6946 5435 : const auto &metaVar = arg->GetMetaVar();
6947 5435 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
6948 : {
6949 1671 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
6950 1671 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
6951 : std::string::npos)
6952 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
6953 : else
6954 888 : jArg.Add("metavar", metaVar);
6955 : }
6956 :
6957 5435 : if (!arg->IsAvailableInPipelineStep())
6958 : {
6959 1622 : jArg.Add("available_in_pipeline_step", false);
6960 : }
6961 :
6962 5435 : const auto &choices = arg->GetChoices();
6963 5435 : if (!choices.empty())
6964 : {
6965 408 : CPLJSONArray jChoices;
6966 3500 : for (const auto &choice : choices)
6967 3092 : jChoices.Add(choice);
6968 408 : jArg.Add("choices", jChoices);
6969 : }
6970 5435 : if (arg->HasDefaultValue())
6971 : {
6972 1188 : switch (arg->GetType())
6973 : {
6974 424 : case GAAT_BOOLEAN:
6975 424 : jArg.Add("default", arg->GetDefault<bool>());
6976 424 : break;
6977 366 : case GAAT_STRING:
6978 366 : jArg.Add("default", arg->GetDefault<std::string>());
6979 366 : break;
6980 200 : case GAAT_INTEGER:
6981 200 : jArg.Add("default", arg->GetDefault<int>());
6982 200 : break;
6983 178 : case GAAT_REAL:
6984 178 : jArg.Add("default", arg->GetDefault<double>());
6985 178 : break;
6986 18 : case GAAT_STRING_LIST:
6987 : {
6988 : const auto &val =
6989 18 : arg->GetDefault<std::vector<std::string>>();
6990 18 : if (val.size() == 1)
6991 : {
6992 17 : jArg.Add("default", val[0]);
6993 : }
6994 : else
6995 : {
6996 1 : CPLJSONArray jArr;
6997 3 : for (const auto &s : val)
6998 : {
6999 2 : jArr.Add(s);
7000 : }
7001 1 : jArg.Add("default", jArr);
7002 : }
7003 18 : break;
7004 : }
7005 1 : case GAAT_INTEGER_LIST:
7006 : {
7007 1 : const auto &val = arg->GetDefault<std::vector<int>>();
7008 1 : if (val.size() == 1)
7009 : {
7010 0 : jArg.Add("default", val[0]);
7011 : }
7012 : else
7013 : {
7014 1 : CPLJSONArray jArr;
7015 3 : for (int i : val)
7016 : {
7017 2 : jArr.Add(i);
7018 : }
7019 1 : jArg.Add("default", jArr);
7020 : }
7021 1 : break;
7022 : }
7023 1 : case GAAT_REAL_LIST:
7024 : {
7025 1 : const auto &val = arg->GetDefault<std::vector<double>>();
7026 1 : if (val.size() == 1)
7027 : {
7028 0 : jArg.Add("default", val[0]);
7029 : }
7030 : else
7031 : {
7032 1 : CPLJSONArray jArr;
7033 3 : for (double d : val)
7034 : {
7035 2 : jArr.Add(d);
7036 : }
7037 1 : jArg.Add("default", jArr);
7038 : }
7039 1 : break;
7040 : }
7041 0 : case GAAT_DATASET:
7042 : case GAAT_DATASET_LIST:
7043 0 : CPLError(CE_Warning, CPLE_AppDefined,
7044 : "Unhandled default value for arg %s",
7045 0 : arg->GetName().c_str());
7046 0 : break;
7047 : }
7048 : }
7049 :
7050 5435 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7051 5435 : if (!std::isnan(minVal))
7052 : {
7053 671 : if (arg->GetType() == GAAT_INTEGER ||
7054 261 : arg->GetType() == GAAT_INTEGER_LIST)
7055 173 : jArg.Add("min_value", static_cast<int>(minVal));
7056 : else
7057 237 : jArg.Add("min_value", minVal);
7058 410 : jArg.Add("min_value_is_included", minValIsIncluded);
7059 : }
7060 :
7061 5435 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7062 5435 : if (!std::isnan(maxVal))
7063 : {
7064 199 : if (arg->GetType() == GAAT_INTEGER ||
7065 82 : arg->GetType() == GAAT_INTEGER_LIST)
7066 35 : jArg.Add("max_value", static_cast<int>(maxVal));
7067 : else
7068 82 : jArg.Add("max_value", maxVal);
7069 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
7070 : }
7071 :
7072 5435 : jArg.Add("required", arg->IsRequired());
7073 5435 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7074 : {
7075 1519 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7076 1519 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7077 1519 : jArg.Add("min_count", arg->GetMinCount());
7078 1519 : jArg.Add("max_count", arg->GetMaxCount());
7079 : }
7080 :
7081 : // Process dependencies
7082 5435 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7083 5435 : if (!mutualDependencyGroup.empty())
7084 : {
7085 32 : jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7086 : }
7087 :
7088 10870 : CPLJSONArray jDependencies;
7089 50 : for (const auto &dependencyArgumentName :
7090 5535 : GetArgDependencies(arg->GetName()))
7091 : {
7092 50 : jDependencies.Add(dependencyArgumentName);
7093 : }
7094 :
7095 5435 : if (jDependencies.Size() > 0)
7096 : {
7097 50 : jArg.Add("depends_on", jDependencies);
7098 : }
7099 :
7100 5435 : jArg.Add("category", arg->GetCategory());
7101 :
7102 10620 : if (arg->GetType() == GAAT_DATASET ||
7103 5185 : arg->GetType() == GAAT_DATASET_LIST)
7104 : {
7105 : {
7106 453 : CPLJSONArray jAr;
7107 453 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
7108 311 : jAr.Add("raster");
7109 453 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7110 179 : jAr.Add("vector");
7111 453 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7112 28 : jAr.Add("multidim_raster");
7113 453 : jArg.Add("dataset_type", jAr);
7114 : }
7115 :
7116 620 : const auto GetFlags = [](int flags)
7117 : {
7118 620 : CPLJSONArray jAr;
7119 620 : if (flags & GADV_NAME)
7120 453 : jAr.Add("name");
7121 620 : if (flags & GADV_OBJECT)
7122 571 : jAr.Add("dataset");
7123 620 : return jAr;
7124 : };
7125 :
7126 453 : if (arg->IsInput())
7127 : {
7128 453 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7129 : }
7130 453 : if (arg->IsOutput())
7131 : {
7132 167 : jArg.Add("output_flags",
7133 334 : GetFlags(arg->GetDatasetOutputFlags()));
7134 : }
7135 : }
7136 :
7137 5435 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7138 5435 : if (!mutualExclusionGroup.empty())
7139 : {
7140 673 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7141 : }
7142 :
7143 10870 : const auto &metadata = arg->GetMetadata();
7144 5435 : if (!metadata.empty())
7145 : {
7146 450 : CPLJSONObject jMetadata;
7147 939 : for (const auto &[key, values] : metadata)
7148 : {
7149 978 : CPLJSONArray jValue;
7150 1183 : for (const auto &value : values)
7151 694 : jValue.Add(value);
7152 489 : jMetadata.Add(key, jValue);
7153 : }
7154 450 : jArg.Add("metadata", jMetadata);
7155 : }
7156 :
7157 10870 : return jArg;
7158 563 : };
7159 :
7160 : {
7161 563 : CPLJSONArray jArgs;
7162 8941 : for (const auto &arg : m_args)
7163 : {
7164 8378 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7165 5211 : jArgs.Add(ProcessArg(arg.get()));
7166 : }
7167 563 : oRoot.Add("input_arguments", jArgs);
7168 : }
7169 :
7170 : {
7171 563 : CPLJSONArray jArgs;
7172 8941 : for (const auto &arg : m_args)
7173 : {
7174 8378 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7175 57 : jArgs.Add(ProcessArg(arg.get()));
7176 : }
7177 563 : oRoot.Add("output_arguments", jArgs);
7178 : }
7179 :
7180 : {
7181 563 : CPLJSONArray jArgs;
7182 8941 : for (const auto &arg : m_args)
7183 : {
7184 8378 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7185 167 : jArgs.Add(ProcessArg(arg.get()));
7186 : }
7187 563 : oRoot.Add("input_output_arguments", jArgs);
7188 : }
7189 :
7190 563 : if (m_supportsStreamedOutput)
7191 : {
7192 117 : oRoot.Add("supports_streamed_output", true);
7193 : }
7194 :
7195 1126 : return oDoc.SaveAsString();
7196 : }
7197 :
7198 : /************************************************************************/
7199 : /* GDALAlgorithm::GetAutoComplete() */
7200 : /************************************************************************/
7201 :
7202 : std::vector<std::string>
7203 246 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7204 : bool lastWordIsComplete, bool showAllOptions)
7205 : {
7206 492 : std::vector<std::string> ret;
7207 :
7208 : // Get inner-most algorithm
7209 246 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
7210 246 : GDALAlgorithm *curAlg = this;
7211 487 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7212 : {
7213 : auto subAlg = curAlg->InstantiateSubAlgorithm(
7214 350 : args.front(), /* suggestionAllowed = */ false);
7215 350 : if (!subAlg)
7216 108 : break;
7217 242 : if (args.size() == 1 && !lastWordIsComplete)
7218 : {
7219 5 : int nCount = 0;
7220 115 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7221 : {
7222 110 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7223 6 : nCount++;
7224 : }
7225 5 : if (nCount >= 2)
7226 : {
7227 11 : for (const std::string &subAlgName :
7228 23 : curAlg->GetSubAlgorithmNames())
7229 : {
7230 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7231 11 : if (subAlg && !subAlg->IsHidden())
7232 11 : ret.push_back(subAlg->GetName());
7233 : }
7234 1 : return ret;
7235 : }
7236 : }
7237 241 : showAllOptions = false;
7238 241 : args.erase(args.begin());
7239 241 : curAlgHolder = std::move(subAlg);
7240 241 : curAlg = curAlgHolder.get();
7241 : }
7242 245 : if (curAlg != this)
7243 : {
7244 131 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7245 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
7246 131 : /* showAllOptions = */ false);
7247 : }
7248 :
7249 228 : std::string option;
7250 228 : std::string value;
7251 114 : ExtractLastOptionAndValue(args, option, value);
7252 :
7253 141 : if (option.empty() && !args.empty() && !args.back().empty() &&
7254 27 : args.back()[0] == '-')
7255 : {
7256 24 : const auto &lastArg = args.back();
7257 : // List available options
7258 355 : for (const auto &arg : GetArgs())
7259 : {
7260 615 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7261 563 : (!showAllOptions &&
7262 768 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7263 466 : arg->GetName() == "version" ||
7264 233 : arg->GetName() == "json-usage")))
7265 : {
7266 116 : continue;
7267 : }
7268 215 : if (!arg->GetShortName().empty())
7269 : {
7270 132 : std::string str = std::string("-").append(arg->GetShortName());
7271 44 : if (lastArg == str)
7272 0 : ret.push_back(std::move(str));
7273 : }
7274 215 : if (lastArg != "-" && lastArg != "--")
7275 : {
7276 54 : for (const std::string &alias : arg->GetAliases())
7277 : {
7278 48 : std::string str = std::string("--").append(alias);
7279 16 : if (cpl::starts_with(str, lastArg))
7280 3 : ret.push_back(std::move(str));
7281 : }
7282 : }
7283 215 : if (!arg->GetName().empty())
7284 : {
7285 645 : std::string str = std::string("--").append(arg->GetName());
7286 215 : if (cpl::starts_with(str, lastArg))
7287 179 : ret.push_back(std::move(str));
7288 : }
7289 : }
7290 24 : std::sort(ret.begin(), ret.end());
7291 : }
7292 90 : else if (!option.empty())
7293 : {
7294 : // List possible choices for current option
7295 84 : auto arg = GetArg(option);
7296 84 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7297 : {
7298 84 : ret = arg->GetChoices();
7299 84 : if (ret.empty())
7300 : {
7301 : {
7302 79 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7303 79 : SetParseForAutoCompletion();
7304 79 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7305 : }
7306 79 : ret = arg->GetAutoCompleteChoices(value);
7307 : }
7308 : else
7309 : {
7310 5 : std::sort(ret.begin(), ret.end());
7311 : }
7312 84 : if (!ret.empty() && ret.back() == value)
7313 : {
7314 2 : ret.clear();
7315 : }
7316 82 : else if (ret.empty())
7317 : {
7318 10 : ret.push_back("**");
7319 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7320 20 : ret.push_back(std::string("\xC2\xA0"
7321 : "description: ")
7322 10 : .append(arg->GetDescription()));
7323 : }
7324 : }
7325 : }
7326 : else
7327 : {
7328 : // List possible sub-algorithms
7329 59 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7330 : {
7331 106 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7332 53 : if (subAlg && !subAlg->IsHidden())
7333 53 : ret.push_back(subAlg->GetName());
7334 : }
7335 6 : if (!ret.empty())
7336 : {
7337 2 : std::sort(ret.begin(), ret.end());
7338 : }
7339 :
7340 : // Try filenames
7341 6 : if (ret.empty() && !args.empty())
7342 : {
7343 : {
7344 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7345 3 : SetParseForAutoCompletion();
7346 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7347 : }
7348 :
7349 3 : const std::string &lastArg = args.back();
7350 3 : GDALAlgorithmArg *arg = nullptr;
7351 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7352 21 : "like", "source", "destination"})
7353 : {
7354 18 : if (!arg)
7355 : {
7356 3 : auto newArg = GetArg(name);
7357 3 : if (newArg)
7358 : {
7359 3 : if (!newArg->IsExplicitlySet())
7360 : {
7361 0 : arg = newArg;
7362 : }
7363 6 : else if (newArg->GetType() == GAAT_STRING ||
7364 5 : newArg->GetType() == GAAT_STRING_LIST ||
7365 8 : newArg->GetType() == GAAT_DATASET ||
7366 2 : newArg->GetType() == GAAT_DATASET_LIST)
7367 : {
7368 : VSIStatBufL sStat;
7369 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7370 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7371 : {
7372 3 : arg = newArg;
7373 : }
7374 : }
7375 : }
7376 : }
7377 : }
7378 3 : if (arg)
7379 : {
7380 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7381 : }
7382 : }
7383 : }
7384 :
7385 114 : return ret;
7386 : }
7387 :
7388 : /************************************************************************/
7389 : /* GDALAlgorithm::GetFieldIndices() */
7390 : /************************************************************************/
7391 :
7392 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7393 : OGRLayerH hLayer, std::vector<int> &indices)
7394 : {
7395 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7396 :
7397 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7398 :
7399 44 : if (names.size() == 1 && names[0] == "ALL")
7400 : {
7401 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7402 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7403 : {
7404 16 : indices.push_back(i);
7405 : }
7406 : }
7407 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7408 : {
7409 6 : std::set<int> fieldsAdded;
7410 14 : for (const std::string &osFieldName : names)
7411 : {
7412 :
7413 : const int nIdx =
7414 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7415 :
7416 10 : if (nIdx < 0)
7417 : {
7418 2 : CPLError(CE_Failure, CPLE_AppDefined,
7419 : "Field '%s' does not exist in layer '%s'",
7420 2 : osFieldName.c_str(), layer.GetName());
7421 2 : return false;
7422 : }
7423 :
7424 8 : if (fieldsAdded.insert(nIdx).second)
7425 : {
7426 7 : indices.push_back(nIdx);
7427 : }
7428 : }
7429 : }
7430 :
7431 42 : return true;
7432 : }
7433 :
7434 : /************************************************************************/
7435 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7436 : /************************************************************************/
7437 :
7438 114 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7439 : std::string &option,
7440 : std::string &value) const
7441 : {
7442 114 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7443 : {
7444 84 : const auto nPosEqual = args.back().find('=');
7445 84 : if (nPosEqual == std::string::npos)
7446 : {
7447 : // Deal with "gdal ... --option"
7448 65 : if (GetArg(args.back()))
7449 : {
7450 41 : option = args.back();
7451 41 : args.pop_back();
7452 : }
7453 : }
7454 : else
7455 : {
7456 : // Deal with "gdal ... --option=<value>"
7457 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7458 : {
7459 19 : option = args.back().substr(0, nPosEqual);
7460 19 : value = args.back().substr(nPosEqual + 1);
7461 19 : args.pop_back();
7462 : }
7463 : }
7464 : }
7465 55 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7466 25 : args[args.size() - 2][0] == '-')
7467 : {
7468 : // Deal with "gdal ... --option <value>"
7469 24 : auto arg = GetArg(args[args.size() - 2]);
7470 24 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7471 : {
7472 24 : option = args[args.size() - 2];
7473 24 : value = args.back();
7474 24 : args.pop_back();
7475 : }
7476 : }
7477 :
7478 114 : const auto IsKeyValueOption = [](const std::string &osStr)
7479 : {
7480 308 : return osStr == "--co" || osStr == "--creation-option" ||
7481 285 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7482 306 : osStr == "--oo" || osStr == "--open-option";
7483 : };
7484 :
7485 114 : if (IsKeyValueOption(option))
7486 : {
7487 22 : const auto nPosEqual = value.find('=');
7488 22 : if (nPosEqual != std::string::npos)
7489 : {
7490 11 : value.resize(nPosEqual);
7491 : }
7492 : }
7493 114 : }
7494 :
7495 : /************************************************************************/
7496 : /* GDALAlgorithm::GetArgDependencies() */
7497 : /************************************************************************/
7498 :
7499 : std::vector<std::string>
7500 7830 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
7501 : {
7502 7830 : const auto arg = GetArg(osName, false);
7503 7830 : if (!arg)
7504 : {
7505 0 : ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
7506 : osName.c_str());
7507 0 : return {};
7508 : }
7509 15660 : std::vector<std::string> dependencies = arg->GetDirectDependencies();
7510 7830 : if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7511 7830 : !mutualDependencyGroup.empty())
7512 : {
7513 896 : for (const auto &otherArg : m_args)
7514 : {
7515 1627 : if (otherArg.get() == arg ||
7516 786 : mutualDependencyGroup.compare(
7517 786 : otherArg->GetMutualDependencyGroup()) != 0)
7518 783 : continue;
7519 58 : dependencies.push_back(otherArg->GetName());
7520 : }
7521 : }
7522 7830 : return dependencies;
7523 : }
7524 :
7525 : //! @cond Doxygen_Suppress
7526 :
7527 : /************************************************************************/
7528 : /* GDALContainerAlgorithm::RunImpl() */
7529 : /************************************************************************/
7530 :
7531 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7532 : {
7533 0 : return false;
7534 : }
7535 :
7536 : //! @endcond
7537 :
7538 : /************************************************************************/
7539 : /* GDALAlgorithmRelease() */
7540 : /************************************************************************/
7541 :
7542 : /** Release a handle to an algorithm.
7543 : *
7544 : * @since 3.11
7545 : */
7546 12421 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7547 : {
7548 12421 : delete hAlg;
7549 12421 : }
7550 :
7551 : /************************************************************************/
7552 : /* GDALAlgorithmGetName() */
7553 : /************************************************************************/
7554 :
7555 : /** Return the algorithm name.
7556 : *
7557 : * @param hAlg Handle to an algorithm. Must NOT be null.
7558 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7559 : * be freed.
7560 : * @since 3.11
7561 : */
7562 5699 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7563 : {
7564 5699 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7565 5699 : return hAlg->ptr->GetName().c_str();
7566 : }
7567 :
7568 : /************************************************************************/
7569 : /* GDALAlgorithmGetDescription() */
7570 : /************************************************************************/
7571 :
7572 : /** Return the algorithm (short) description.
7573 : *
7574 : * @param hAlg Handle to an algorithm. Must NOT be null.
7575 : * @return algorithm description whose lifetime is bound to hAlg and which must
7576 : * not be freed.
7577 : * @since 3.11
7578 : */
7579 5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7580 : {
7581 5619 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7582 5619 : return hAlg->ptr->GetDescription().c_str();
7583 : }
7584 :
7585 : /************************************************************************/
7586 : /* GDALAlgorithmGetLongDescription() */
7587 : /************************************************************************/
7588 :
7589 : /** Return the algorithm (longer) description.
7590 : *
7591 : * @param hAlg Handle to an algorithm. Must NOT be null.
7592 : * @return algorithm description whose lifetime is bound to hAlg and which must
7593 : * not be freed.
7594 : * @since 3.11
7595 : */
7596 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7597 : {
7598 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7599 2 : return hAlg->ptr->GetLongDescription().c_str();
7600 : }
7601 :
7602 : /************************************************************************/
7603 : /* GDALAlgorithmGetHelpFullURL() */
7604 : /************************************************************************/
7605 :
7606 : /** Return the algorithm full URL.
7607 : *
7608 : * @param hAlg Handle to an algorithm. Must NOT be null.
7609 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7610 : * not be freed.
7611 : * @since 3.11
7612 : */
7613 4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7614 : {
7615 4963 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7616 4963 : return hAlg->ptr->GetHelpFullURL().c_str();
7617 : }
7618 :
7619 : /************************************************************************/
7620 : /* GDALAlgorithmHasSubAlgorithms() */
7621 : /************************************************************************/
7622 :
7623 : /** Return whether the algorithm has sub-algorithms.
7624 : *
7625 : * @param hAlg Handle to an algorithm. Must NOT be null.
7626 : * @since 3.11
7627 : */
7628 9147 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7629 : {
7630 9147 : VALIDATE_POINTER1(hAlg, __func__, false);
7631 9147 : return hAlg->ptr->HasSubAlgorithms();
7632 : }
7633 :
7634 : /************************************************************************/
7635 : /* GDALAlgorithmGetSubAlgorithmNames() */
7636 : /************************************************************************/
7637 :
7638 : /** Get the names of registered algorithms.
7639 : *
7640 : * @param hAlg Handle to an algorithm. Must NOT be null.
7641 : * @return a NULL terminated list of names, which must be destroyed with
7642 : * CSLDestroy()
7643 : * @since 3.11
7644 : */
7645 707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7646 : {
7647 707 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7648 707 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7649 : }
7650 :
7651 : /************************************************************************/
7652 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7653 : /************************************************************************/
7654 :
7655 : /** Instantiate an algorithm by its name (or its alias).
7656 : *
7657 : * @param hAlg Handle to an algorithm. Must NOT be null.
7658 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7659 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7660 : * or NULL if the algorithm does not exist or another error occurred.
7661 : * @since 3.11
7662 : */
7663 8610 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7664 : const char *pszSubAlgName)
7665 : {
7666 8610 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7667 8610 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7668 17220 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7669 : return subAlg
7670 17220 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7671 17220 : : nullptr;
7672 : }
7673 :
7674 : /************************************************************************/
7675 : /* GDALAlgorithmParseCommandLineArguments() */
7676 : /************************************************************************/
7677 :
7678 : /** Parse a command line argument, which does not include the algorithm
7679 : * name, to set the value of corresponding arguments.
7680 : *
7681 : * @param hAlg Handle to an algorithm. Must NOT be null.
7682 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7683 : * @return true if successful, false otherwise
7684 : * @since 3.11
7685 : */
7686 :
7687 352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7688 : CSLConstList papszArgs)
7689 : {
7690 352 : VALIDATE_POINTER1(hAlg, __func__, false);
7691 352 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7692 : }
7693 :
7694 : /************************************************************************/
7695 : /* GDALAlgorithmGetActualAlgorithm() */
7696 : /************************************************************************/
7697 :
7698 : /** Return the actual algorithm that is going to be invoked, when the
7699 : * current algorithm has sub-algorithms.
7700 : *
7701 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7702 : *
7703 : * Note that the lifetime of the returned algorithm does not exceed the one of
7704 : * the hAlg instance that owns it.
7705 : *
7706 : * @param hAlg Handle to an algorithm. Must NOT be null.
7707 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7708 : * @since 3.11
7709 : */
7710 914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7711 : {
7712 914 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7713 914 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7714 : }
7715 :
7716 : /************************************************************************/
7717 : /* GDALAlgorithmRun() */
7718 : /************************************************************************/
7719 :
7720 : /** Execute the algorithm, starting with ValidateArguments() and then
7721 : * calling RunImpl().
7722 : *
7723 : * This function must be called at most once per instance.
7724 : *
7725 : * @param hAlg Handle to an algorithm. Must NOT be null.
7726 : * @param pfnProgress Progress callback. May be null.
7727 : * @param pProgressData Progress callback user data. May be null.
7728 : * @return true if successful, false otherwise
7729 : * @since 3.11
7730 : */
7731 :
7732 2720 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7733 : void *pProgressData)
7734 : {
7735 2720 : VALIDATE_POINTER1(hAlg, __func__, false);
7736 2720 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7737 : }
7738 :
7739 : /************************************************************************/
7740 : /* GDALAlgorithmFinalize() */
7741 : /************************************************************************/
7742 :
7743 : /** Complete any pending actions, and return the final status.
7744 : * This is typically useful for algorithm that generate an output dataset.
7745 : *
7746 : * Note that this function does *NOT* release memory associated with the
7747 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7748 : *
7749 : * @param hAlg Handle to an algorithm. Must NOT be null.
7750 : * @return true if successful, false otherwise
7751 : * @since 3.11
7752 : */
7753 :
7754 896 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7755 : {
7756 896 : VALIDATE_POINTER1(hAlg, __func__, false);
7757 896 : return hAlg->ptr->Finalize();
7758 : }
7759 :
7760 : /************************************************************************/
7761 : /* GDALAlgorithmGetUsageAsJSON() */
7762 : /************************************************************************/
7763 :
7764 : /** Return the usage of the algorithm as a JSON-serialized string.
7765 : *
7766 : * This can be used to dynamically generate interfaces to algorithms.
7767 : *
7768 : * @param hAlg Handle to an algorithm. Must NOT be null.
7769 : * @return a string that must be freed with CPLFree()
7770 : * @since 3.11
7771 : */
7772 6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7773 : {
7774 6 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7775 6 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7776 : }
7777 :
7778 : /************************************************************************/
7779 : /* GDALAlgorithmGetArgNames() */
7780 : /************************************************************************/
7781 :
7782 : /** Return the list of available argument names.
7783 : *
7784 : * @param hAlg Handle to an algorithm. Must NOT be null.
7785 : * @return a NULL terminated list of names, which must be destroyed with
7786 : * CSLDestroy()
7787 : * @since 3.11
7788 : */
7789 15433 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7790 : {
7791 15433 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7792 30866 : CPLStringList list;
7793 344478 : for (const auto &arg : hAlg->ptr->GetArgs())
7794 329045 : list.AddString(arg->GetName().c_str());
7795 15433 : return list.StealList();
7796 : }
7797 :
7798 : /************************************************************************/
7799 : /* GDALAlgorithmGetArg() */
7800 : /************************************************************************/
7801 :
7802 : /** Return an argument from its name.
7803 : *
7804 : * The lifetime of the returned object does not exceed the one of hAlg.
7805 : *
7806 : * @param hAlg Handle to an algorithm. Must NOT be null.
7807 : * @param pszArgName Argument name. Must NOT be null.
7808 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7809 : * or nullptr in case of error
7810 : * @since 3.11
7811 : */
7812 329955 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7813 : const char *pszArgName)
7814 : {
7815 329955 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7816 329955 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7817 659910 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7818 329955 : /* isConst = */ true);
7819 329955 : if (!arg)
7820 3 : return nullptr;
7821 329952 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7822 : }
7823 :
7824 : /************************************************************************/
7825 : /* GDALAlgorithmGetArgNonConst() */
7826 : /************************************************************************/
7827 :
7828 : /** Return an argument from its name, possibly allowing creation of user-provided
7829 : * argument if the algorithm allow it.
7830 : *
7831 : * The lifetime of the returned object does not exceed the one of hAlg.
7832 : *
7833 : * @param hAlg Handle to an algorithm. Must NOT be null.
7834 : * @param pszArgName Argument name. Must NOT be null.
7835 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7836 : * or nullptr in case of error
7837 : * @since 3.12
7838 : */
7839 9756 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7840 : const char *pszArgName)
7841 : {
7842 9756 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7843 9756 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7844 19512 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7845 9756 : /* isConst = */ false);
7846 9756 : if (!arg)
7847 2 : return nullptr;
7848 9754 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7849 : }
7850 :
7851 : /************************************************************************/
7852 : /* GDALAlgorithmGetArgDependencies() */
7853 : /************************************************************************/
7854 :
7855 : /** Return the list of argument names the specified argument depends on.
7856 : *
7857 : * This includes both regular dependencies and mutual dependencies.
7858 : *
7859 : * @param hAlg Handle to an algorithm. Must NOT be null.
7860 : * @param pszArgName Argument name. Must NOT be null.
7861 : * @return a NULL terminated list of names, which must be destroyed with
7862 : * CSLDestroy()
7863 : * @since 3.11
7864 : */
7865 7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
7866 : const char *pszArgName)
7867 : {
7868 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7869 7 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7870 7 : return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
7871 : }
7872 :
7873 : /************************************************************************/
7874 : /* GDALAlgorithmArgRelease() */
7875 : /************************************************************************/
7876 :
7877 : /** Release a handle to an argument.
7878 : *
7879 : * @since 3.11
7880 : */
7881 339706 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7882 : {
7883 339706 : delete hArg;
7884 339706 : }
7885 :
7886 : /************************************************************************/
7887 : /* GDALAlgorithmArgGetName() */
7888 : /************************************************************************/
7889 :
7890 : /** Return the name of an argument.
7891 : *
7892 : * @param hArg Handle to an argument. Must NOT be null.
7893 : * @return argument name whose lifetime is bound to hArg and which must not
7894 : * be freed.
7895 : * @since 3.11
7896 : */
7897 18865 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7898 : {
7899 18865 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7900 18865 : return hArg->ptr->GetName().c_str();
7901 : }
7902 :
7903 : /************************************************************************/
7904 : /* GDALAlgorithmArgGetType() */
7905 : /************************************************************************/
7906 :
7907 : /** Get the type of an argument
7908 : *
7909 : * @param hArg Handle to an argument. Must NOT be null.
7910 : * @since 3.11
7911 : */
7912 419641 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
7913 : {
7914 419641 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
7915 419641 : return hArg->ptr->GetType();
7916 : }
7917 :
7918 : /************************************************************************/
7919 : /* GDALAlgorithmArgGetDescription() */
7920 : /************************************************************************/
7921 :
7922 : /** Return the description of an argument.
7923 : *
7924 : * @param hArg Handle to an argument. Must NOT be null.
7925 : * @return argument description whose lifetime is bound to hArg and which must not
7926 : * be freed.
7927 : * @since 3.11
7928 : */
7929 83723 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
7930 : {
7931 83723 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7932 83723 : return hArg->ptr->GetDescription().c_str();
7933 : }
7934 :
7935 : /************************************************************************/
7936 : /* GDALAlgorithmArgGetShortName() */
7937 : /************************************************************************/
7938 :
7939 : /** Return the short name, or empty string if there is none
7940 : *
7941 : * @param hArg Handle to an argument. Must NOT be null.
7942 : * @return short name whose lifetime is bound to hArg and which must not
7943 : * be freed.
7944 : * @since 3.11
7945 : */
7946 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
7947 : {
7948 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7949 1 : return hArg->ptr->GetShortName().c_str();
7950 : }
7951 :
7952 : /************************************************************************/
7953 : /* GDALAlgorithmArgGetAliases() */
7954 : /************************************************************************/
7955 :
7956 : /** Return the aliases (potentially none)
7957 : *
7958 : * @param hArg Handle to an argument. Must NOT be null.
7959 : * @return a NULL terminated list of names, which must be destroyed with
7960 : * CSLDestroy()
7961 :
7962 : * @since 3.11
7963 : */
7964 158425 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
7965 : {
7966 158425 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7967 158425 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
7968 : }
7969 :
7970 : /************************************************************************/
7971 : /* GDALAlgorithmArgGetMetaVar() */
7972 : /************************************************************************/
7973 :
7974 : /** Return the "meta-var" hint.
7975 : *
7976 : * By default, the meta-var value is the long name of the argument in
7977 : * upper case.
7978 : *
7979 : * @param hArg Handle to an argument. Must NOT be null.
7980 : * @return meta-var hint whose lifetime is bound to hArg and which must not
7981 : * be freed.
7982 : * @since 3.11
7983 : */
7984 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
7985 : {
7986 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7987 1 : return hArg->ptr->GetMetaVar().c_str();
7988 : }
7989 :
7990 : /************************************************************************/
7991 : /* GDALAlgorithmArgGetCategory() */
7992 : /************************************************************************/
7993 :
7994 : /** Return the argument category
7995 : *
7996 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
7997 : *
7998 : * @param hArg Handle to an argument. Must NOT be null.
7999 : * @return category whose lifetime is bound to hArg and which must not
8000 : * be freed.
8001 : * @since 3.11
8002 : */
8003 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
8004 : {
8005 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8006 1 : return hArg->ptr->GetCategory().c_str();
8007 : }
8008 :
8009 : /************************************************************************/
8010 : /* GDALAlgorithmArgIsPositional() */
8011 : /************************************************************************/
8012 :
8013 : /** Return if the argument is a positional one.
8014 : *
8015 : * @param hArg Handle to an argument. Must NOT be null.
8016 : * @since 3.11
8017 : */
8018 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
8019 : {
8020 1 : VALIDATE_POINTER1(hArg, __func__, false);
8021 1 : return hArg->ptr->IsPositional();
8022 : }
8023 :
8024 : /************************************************************************/
8025 : /* GDALAlgorithmArgIsRequired() */
8026 : /************************************************************************/
8027 :
8028 : /** Return whether the argument is required. Defaults to false.
8029 : *
8030 : * @param hArg Handle to an argument. Must NOT be null.
8031 : * @since 3.11
8032 : */
8033 158425 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
8034 : {
8035 158425 : VALIDATE_POINTER1(hArg, __func__, false);
8036 158425 : return hArg->ptr->IsRequired();
8037 : }
8038 :
8039 : /************************************************************************/
8040 : /* GDALAlgorithmArgGetMinCount() */
8041 : /************************************************************************/
8042 :
8043 : /** Return the minimum number of values for the argument.
8044 : *
8045 : * Defaults to 0.
8046 : * Only applies to list type of arguments.
8047 : *
8048 : * @param hArg Handle to an argument. Must NOT be null.
8049 : * @since 3.11
8050 : */
8051 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
8052 : {
8053 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8054 1 : return hArg->ptr->GetMinCount();
8055 : }
8056 :
8057 : /************************************************************************/
8058 : /* GDALAlgorithmArgGetMaxCount() */
8059 : /************************************************************************/
8060 :
8061 : /** Return the maximum number of values for the argument.
8062 : *
8063 : * Defaults to 1 for scalar types, and INT_MAX for list types.
8064 : * Only applies to list type of arguments.
8065 : *
8066 : * @param hArg Handle to an argument. Must NOT be null.
8067 : * @since 3.11
8068 : */
8069 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8070 : {
8071 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8072 1 : return hArg->ptr->GetMaxCount();
8073 : }
8074 :
8075 : /************************************************************************/
8076 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
8077 : /************************************************************************/
8078 :
8079 : /** Return whether, for list type of arguments, several values, space
8080 : * separated, may be specified. That is "--foo=bar,baz".
8081 : * The default is true.
8082 : *
8083 : * @param hArg Handle to an argument. Must NOT be null.
8084 : * @since 3.11
8085 : */
8086 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8087 : {
8088 1 : VALIDATE_POINTER1(hArg, __func__, false);
8089 1 : return hArg->ptr->GetPackedValuesAllowed();
8090 : }
8091 :
8092 : /************************************************************************/
8093 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
8094 : /************************************************************************/
8095 :
8096 : /** Return whether, for list type of arguments, the argument may be
8097 : * repeated. That is "--foo=bar --foo=baz".
8098 : * The default is true.
8099 : *
8100 : * @param hArg Handle to an argument. Must NOT be null.
8101 : * @since 3.11
8102 : */
8103 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8104 : {
8105 1 : VALIDATE_POINTER1(hArg, __func__, false);
8106 1 : return hArg->ptr->GetRepeatedArgAllowed();
8107 : }
8108 :
8109 : /************************************************************************/
8110 : /* GDALAlgorithmArgGetChoices() */
8111 : /************************************************************************/
8112 :
8113 : /** Return the allowed values (as strings) for the argument.
8114 : *
8115 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8116 : *
8117 : * @param hArg Handle to an argument. Must NOT be null.
8118 : * @return a NULL terminated list of names, which must be destroyed with
8119 : * CSLDestroy()
8120 :
8121 : * @since 3.11
8122 : */
8123 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8124 : {
8125 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8126 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
8127 : }
8128 :
8129 : /************************************************************************/
8130 : /* GDALAlgorithmArgGetMetadataItem() */
8131 : /************************************************************************/
8132 :
8133 : /** Return the values of the metadata item of an argument.
8134 : *
8135 : * @param hArg Handle to an argument. Must NOT be null.
8136 : * @param pszItem Name of the item. Must NOT be null.
8137 : * @return a NULL terminated list of values, which must be destroyed with
8138 : * CSLDestroy()
8139 :
8140 : * @since 3.11
8141 : */
8142 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8143 : const char *pszItem)
8144 : {
8145 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8146 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
8147 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8148 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8149 : }
8150 :
8151 : /************************************************************************/
8152 : /* GDALAlgorithmArgIsExplicitlySet() */
8153 : /************************************************************************/
8154 :
8155 : /** Return whether the argument value has been explicitly set with Set()
8156 : *
8157 : * @param hArg Handle to an argument. Must NOT be null.
8158 : * @since 3.11
8159 : */
8160 632 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8161 : {
8162 632 : VALIDATE_POINTER1(hArg, __func__, false);
8163 632 : return hArg->ptr->IsExplicitlySet();
8164 : }
8165 :
8166 : /************************************************************************/
8167 : /* GDALAlgorithmArgHasDefaultValue() */
8168 : /************************************************************************/
8169 :
8170 : /** Return if the argument has a declared default value.
8171 : *
8172 : * @param hArg Handle to an argument. Must NOT be null.
8173 : * @since 3.11
8174 : */
8175 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8176 : {
8177 2 : VALIDATE_POINTER1(hArg, __func__, false);
8178 2 : return hArg->ptr->HasDefaultValue();
8179 : }
8180 :
8181 : /************************************************************************/
8182 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
8183 : /************************************************************************/
8184 :
8185 : /** Return the argument default value as a integer.
8186 : *
8187 : * Must only be called on arguments whose type is GAAT_BOOLEAN
8188 : *
8189 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8190 : * argument has a default value.
8191 : *
8192 : * @param hArg Handle to an argument. Must NOT be null.
8193 : * @since 3.12
8194 : */
8195 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8196 : {
8197 3 : VALIDATE_POINTER1(hArg, __func__, false);
8198 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8199 : {
8200 1 : CPLError(CE_Failure, CPLE_AppDefined,
8201 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8202 : __func__);
8203 1 : return false;
8204 : }
8205 2 : return hArg->ptr->GetDefault<bool>();
8206 : }
8207 :
8208 : /************************************************************************/
8209 : /* GDALAlgorithmArgGetDefaultAsString() */
8210 : /************************************************************************/
8211 :
8212 : /** Return the argument default value as a string.
8213 : *
8214 : * Must only be called on arguments whose type is GAAT_STRING.
8215 : *
8216 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8217 : * argument has a default value.
8218 : *
8219 : * @param hArg Handle to an argument. Must NOT be null.
8220 : * @return string whose lifetime is bound to hArg and which must not
8221 : * be freed.
8222 : * @since 3.11
8223 : */
8224 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8225 : {
8226 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8227 3 : if (hArg->ptr->GetType() != GAAT_STRING)
8228 : {
8229 2 : CPLError(CE_Failure, CPLE_AppDefined,
8230 : "%s must only be called on arguments of type GAAT_STRING",
8231 : __func__);
8232 2 : return nullptr;
8233 : }
8234 1 : return hArg->ptr->GetDefault<std::string>().c_str();
8235 : }
8236 :
8237 : /************************************************************************/
8238 : /* GDALAlgorithmArgGetDefaultAsInteger() */
8239 : /************************************************************************/
8240 :
8241 : /** Return the argument default value as a integer.
8242 : *
8243 : * Must only be called on arguments whose type is GAAT_INTEGER
8244 : *
8245 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8246 : * argument has a default value.
8247 : *
8248 : * @param hArg Handle to an argument. Must NOT be null.
8249 : * @since 3.12
8250 : */
8251 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8252 : {
8253 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8254 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8255 : {
8256 2 : CPLError(CE_Failure, CPLE_AppDefined,
8257 : "%s must only be called on arguments of type GAAT_INTEGER",
8258 : __func__);
8259 2 : return 0;
8260 : }
8261 1 : return hArg->ptr->GetDefault<int>();
8262 : }
8263 :
8264 : /************************************************************************/
8265 : /* GDALAlgorithmArgGetDefaultAsDouble() */
8266 : /************************************************************************/
8267 :
8268 : /** Return the argument default value as a double.
8269 : *
8270 : * Must only be called on arguments whose type is GAAT_REAL
8271 : *
8272 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8273 : * argument has a default value.
8274 : *
8275 : * @param hArg Handle to an argument. Must NOT be null.
8276 : * @since 3.12
8277 : */
8278 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8279 : {
8280 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8281 3 : if (hArg->ptr->GetType() != GAAT_REAL)
8282 : {
8283 2 : CPLError(CE_Failure, CPLE_AppDefined,
8284 : "%s must only be called on arguments of type GAAT_REAL",
8285 : __func__);
8286 2 : return 0;
8287 : }
8288 1 : return hArg->ptr->GetDefault<double>();
8289 : }
8290 :
8291 : /************************************************************************/
8292 : /* GDALAlgorithmArgGetDefaultAsStringList() */
8293 : /************************************************************************/
8294 :
8295 : /** Return the argument default value as a string list.
8296 : *
8297 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8298 : *
8299 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8300 : * argument has a default value.
8301 : *
8302 : * @param hArg Handle to an argument. Must NOT be null.
8303 : * @return a NULL terminated list of names, which must be destroyed with
8304 : * CSLDestroy()
8305 :
8306 : * @since 3.12
8307 : */
8308 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8309 : {
8310 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8311 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8312 : {
8313 2 : CPLError(CE_Failure, CPLE_AppDefined,
8314 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8315 : __func__);
8316 2 : return nullptr;
8317 : }
8318 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8319 1 : .StealList();
8320 : }
8321 :
8322 : /************************************************************************/
8323 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8324 : /************************************************************************/
8325 :
8326 : /** Return the argument default value as a integer list.
8327 : *
8328 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8329 : *
8330 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8331 : * argument has a default value.
8332 : *
8333 : * @param hArg Handle to an argument. Must NOT be null.
8334 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8335 : * @since 3.12
8336 : */
8337 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8338 : size_t *pnCount)
8339 : {
8340 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8341 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8342 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8343 : {
8344 2 : CPLError(
8345 : CE_Failure, CPLE_AppDefined,
8346 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8347 : __func__);
8348 2 : *pnCount = 0;
8349 2 : return nullptr;
8350 : }
8351 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8352 1 : *pnCount = val.size();
8353 1 : return val.data();
8354 : }
8355 :
8356 : /************************************************************************/
8357 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8358 : /************************************************************************/
8359 :
8360 : /** Return the argument default value as a real list.
8361 : *
8362 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8363 : *
8364 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8365 : * argument has a default value.
8366 : *
8367 : * @param hArg Handle to an argument. Must NOT be null.
8368 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8369 : * @since 3.12
8370 : */
8371 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8372 : size_t *pnCount)
8373 : {
8374 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8375 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8376 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8377 : {
8378 2 : CPLError(CE_Failure, CPLE_AppDefined,
8379 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8380 : __func__);
8381 2 : *pnCount = 0;
8382 2 : return nullptr;
8383 : }
8384 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8385 1 : *pnCount = val.size();
8386 1 : return val.data();
8387 : }
8388 :
8389 : /************************************************************************/
8390 : /* GDALAlgorithmArgIsHidden() */
8391 : /************************************************************************/
8392 :
8393 : /** Return whether the argument is hidden (for GDAL internal use)
8394 : *
8395 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8396 : * GDALAlgorithmArgIsHiddenForAPI().
8397 : *
8398 : * @param hArg Handle to an argument. Must NOT be null.
8399 : * @since 3.12
8400 : */
8401 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8402 : {
8403 1 : VALIDATE_POINTER1(hArg, __func__, false);
8404 1 : return hArg->ptr->IsHidden();
8405 : }
8406 :
8407 : /************************************************************************/
8408 : /* GDALAlgorithmArgIsHiddenForCLI() */
8409 : /************************************************************************/
8410 :
8411 : /** Return whether the argument must not be mentioned in CLI usage.
8412 : *
8413 : * For example, "output-value" for "gdal raster info", which is only
8414 : * meant when the algorithm is used from a non-CLI context.
8415 : *
8416 : * @param hArg Handle to an argument. Must NOT be null.
8417 : * @since 3.11
8418 : */
8419 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8420 : {
8421 1 : VALIDATE_POINTER1(hArg, __func__, false);
8422 1 : return hArg->ptr->IsHiddenForCLI();
8423 : }
8424 :
8425 : /************************************************************************/
8426 : /* GDALAlgorithmArgIsHiddenForAPI() */
8427 : /************************************************************************/
8428 :
8429 : /** Return whether the argument must not be mentioned in the context of an
8430 : * API use.
8431 : * Said otherwise, if it is only for CLI usage.
8432 : *
8433 : * For example "--help"
8434 : *
8435 : * @param hArg Handle to an argument. Must NOT be null.
8436 : * @since 3.12
8437 : */
8438 214103 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8439 : {
8440 214103 : VALIDATE_POINTER1(hArg, __func__, false);
8441 214103 : return hArg->ptr->IsHiddenForAPI();
8442 : }
8443 :
8444 : /************************************************************************/
8445 : /* GDALAlgorithmArgIsOnlyForCLI() */
8446 : /************************************************************************/
8447 :
8448 : /** Return whether the argument must not be mentioned in the context of an
8449 : * API use.
8450 : * Said otherwise, if it is only for CLI usage.
8451 : *
8452 : * For example "--help"
8453 : *
8454 : * @param hArg Handle to an argument. Must NOT be null.
8455 : * @since 3.11
8456 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8457 : */
8458 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8459 : {
8460 0 : VALIDATE_POINTER1(hArg, __func__, false);
8461 0 : return hArg->ptr->IsHiddenForAPI();
8462 : }
8463 :
8464 : /************************************************************************/
8465 : /* GDALAlgorithmArgIsAvailableInPipelineStep() */
8466 : /************************************************************************/
8467 :
8468 : /** Return whether the argument is available in a pipeline step.
8469 : *
8470 : * If false, it is only available in standalone mode.
8471 : *
8472 : * @param hArg Handle to an argument. Must NOT be null.
8473 : * @since 3.13
8474 : */
8475 2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8476 : {
8477 2 : VALIDATE_POINTER1(hArg, __func__, false);
8478 2 : return hArg->ptr->IsAvailableInPipelineStep();
8479 : }
8480 :
8481 : /************************************************************************/
8482 : /* GDALAlgorithmArgIsInput() */
8483 : /************************************************************************/
8484 :
8485 : /** Indicate whether the value of the argument is read-only during the
8486 : * execution of the algorithm.
8487 : *
8488 : * Default is true.
8489 : *
8490 : * @param hArg Handle to an argument. Must NOT be null.
8491 : * @since 3.11
8492 : */
8493 211233 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8494 : {
8495 211233 : VALIDATE_POINTER1(hArg, __func__, false);
8496 211233 : return hArg->ptr->IsInput();
8497 : }
8498 :
8499 : /************************************************************************/
8500 : /* GDALAlgorithmArgIsOutput() */
8501 : /************************************************************************/
8502 :
8503 : /** Return whether (at least part of) the value of the argument is set
8504 : * during the execution of the algorithm.
8505 : *
8506 : * For example, "output-value" for "gdal raster info"
8507 : * Default is false.
8508 : * An argument may return both IsInput() and IsOutput() as true.
8509 : * For example the "gdal raster convert" algorithm consumes the dataset
8510 : * name of its "output" argument, and sets the dataset object during its
8511 : * execution.
8512 : *
8513 : * @param hArg Handle to an argument. Must NOT be null.
8514 : * @since 3.11
8515 : */
8516 117788 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8517 : {
8518 117788 : VALIDATE_POINTER1(hArg, __func__, false);
8519 117788 : return hArg->ptr->IsOutput();
8520 : }
8521 :
8522 : /************************************************************************/
8523 : /* GDALAlgorithmArgGetDatasetType() */
8524 : /************************************************************************/
8525 :
8526 : /** Get which type of dataset is allowed / generated.
8527 : *
8528 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8529 : * GDAL_OF_MULTIDIM_RASTER.
8530 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8531 : *
8532 : * @param hArg Handle to an argument. Must NOT be null.
8533 : * @since 3.11
8534 : */
8535 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8536 : {
8537 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8538 2 : return hArg->ptr->GetDatasetType();
8539 : }
8540 :
8541 : /************************************************************************/
8542 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8543 : /************************************************************************/
8544 :
8545 : /** Indicates which components among name and dataset are accepted as
8546 : * input, when this argument serves as an input.
8547 : *
8548 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8549 : * input.
8550 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8551 : * accepted as input.
8552 : * If both bits are set, the algorithm can accept either a name or a dataset
8553 : * object.
8554 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8555 : *
8556 : * @param hArg Handle to an argument. Must NOT be null.
8557 : * @return string whose lifetime is bound to hAlg and which must not
8558 : * be freed.
8559 : * @since 3.11
8560 : */
8561 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8562 : {
8563 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8564 2 : return hArg->ptr->GetDatasetInputFlags();
8565 : }
8566 :
8567 : /************************************************************************/
8568 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8569 : /************************************************************************/
8570 :
8571 : /** Indicates which components among name and dataset are modified,
8572 : * when this argument serves as an output.
8573 : *
8574 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8575 : * output (that is the algorithm will generate the name. Rarely used).
8576 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8577 : * generated as output, and available for use after the algorithm has
8578 : * completed.
8579 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8580 : *
8581 : * @param hArg Handle to an argument. Must NOT be null.
8582 : * @return string whose lifetime is bound to hAlg and which must not
8583 : * be freed.
8584 : * @since 3.11
8585 : */
8586 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8587 : {
8588 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8589 2 : return hArg->ptr->GetDatasetOutputFlags();
8590 : }
8591 :
8592 : /************************************************************************/
8593 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8594 : /************************************************************************/
8595 :
8596 : /** Return the name of the mutual exclusion group to which this argument
8597 : * belongs to.
8598 : *
8599 : * Or empty string if it does not belong to any exclusion group.
8600 : *
8601 : * @param hArg Handle to an argument. Must NOT be null.
8602 : * @return string whose lifetime is bound to hArg and which must not
8603 : * be freed.
8604 : * @since 3.11
8605 : */
8606 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8607 : {
8608 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8609 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8610 : }
8611 :
8612 : /************************************************************************/
8613 : /* GDALAlgorithmArgGetMutualDependencyGroup() */
8614 : /************************************************************************/
8615 :
8616 : /** Return the name of the mutual dependency group to which this argument
8617 : * belongs to.
8618 : *
8619 : * Or empty string if it does not belong to any dependency group.
8620 : *
8621 : * @param hArg Handle to an argument. Must NOT be null.
8622 : * @return string whose lifetime is bound to hArg and which must not
8623 : * be freed.
8624 : * @since 3.13
8625 : */
8626 5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8627 : {
8628 5 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8629 5 : return hArg->ptr->GetMutualDependencyGroup().c_str();
8630 : }
8631 :
8632 : /************************************************************************/
8633 : /* GDALAlgorithmArgGetDirectDependencies() */
8634 : /************************************************************************/
8635 :
8636 : /** Return the list of names of arguments that this argument depends on.
8637 : *
8638 : * This is not necessarily a symmetric relationship.
8639 : * If argument A depends on argument B, it doesn't mean that B depends on A.
8640 : * Mutual dependency groups are a special case of dependencies,
8641 : * where all arguments of the group depend on each other and are not
8642 : * returned by this method.
8643 : *
8644 : * @param hArg Handle to an argument. Must NOT be null.
8645 : * @return a NULL terminated list of names, which must be destroyed with
8646 : * CSLDestroy()
8647 : * @since 3.13
8648 : */
8649 7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8650 : {
8651 7 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8652 7 : return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8653 : }
8654 :
8655 : /************************************************************************/
8656 : /* GDALAlgorithmArgGetAsBoolean() */
8657 : /************************************************************************/
8658 :
8659 : /** Return the argument value as a boolean.
8660 : *
8661 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8662 : *
8663 : * @param hArg Handle to an argument. Must NOT be null.
8664 : * @since 3.11
8665 : */
8666 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8667 : {
8668 8 : VALIDATE_POINTER1(hArg, __func__, false);
8669 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8670 : {
8671 1 : CPLError(CE_Failure, CPLE_AppDefined,
8672 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8673 : __func__);
8674 1 : return false;
8675 : }
8676 7 : return hArg->ptr->Get<bool>();
8677 : }
8678 :
8679 : /************************************************************************/
8680 : /* GDALAlgorithmArgGetAsString() */
8681 : /************************************************************************/
8682 :
8683 : /** Return the argument value as a string.
8684 : *
8685 : * Must only be called on arguments whose type is GAAT_STRING.
8686 : *
8687 : * @param hArg Handle to an argument. Must NOT be null.
8688 : * @return string whose lifetime is bound to hArg and which must not
8689 : * be freed.
8690 : * @since 3.11
8691 : */
8692 354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8693 : {
8694 354 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8695 354 : if (hArg->ptr->GetType() != GAAT_STRING)
8696 : {
8697 1 : CPLError(CE_Failure, CPLE_AppDefined,
8698 : "%s must only be called on arguments of type GAAT_STRING",
8699 : __func__);
8700 1 : return nullptr;
8701 : }
8702 353 : return hArg->ptr->Get<std::string>().c_str();
8703 : }
8704 :
8705 : /************************************************************************/
8706 : /* GDALAlgorithmArgGetAsDatasetValue() */
8707 : /************************************************************************/
8708 :
8709 : /** Return the argument value as a GDALArgDatasetValueH.
8710 : *
8711 : * Must only be called on arguments whose type is GAAT_DATASET
8712 : *
8713 : * @param hArg Handle to an argument. Must NOT be null.
8714 : * @return handle to a GDALArgDatasetValue that must be released with
8715 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8716 : * the one of hArg.
8717 : * @since 3.11
8718 : */
8719 3207 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8720 : {
8721 3207 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8722 3207 : if (hArg->ptr->GetType() != GAAT_DATASET)
8723 : {
8724 1 : CPLError(CE_Failure, CPLE_AppDefined,
8725 : "%s must only be called on arguments of type GAAT_DATASET",
8726 : __func__);
8727 1 : return nullptr;
8728 : }
8729 3206 : return std::make_unique<GDALArgDatasetValueHS>(
8730 6412 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8731 3206 : .release();
8732 : }
8733 :
8734 : /************************************************************************/
8735 : /* GDALAlgorithmArgGetAsInteger() */
8736 : /************************************************************************/
8737 :
8738 : /** Return the argument value as a integer.
8739 : *
8740 : * Must only be called on arguments whose type is GAAT_INTEGER
8741 : *
8742 : * @param hArg Handle to an argument. Must NOT be null.
8743 : * @since 3.11
8744 : */
8745 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8746 : {
8747 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8748 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8749 : {
8750 1 : CPLError(CE_Failure, CPLE_AppDefined,
8751 : "%s must only be called on arguments of type GAAT_INTEGER",
8752 : __func__);
8753 1 : return 0;
8754 : }
8755 25 : return hArg->ptr->Get<int>();
8756 : }
8757 :
8758 : /************************************************************************/
8759 : /* GDALAlgorithmArgGetAsDouble() */
8760 : /************************************************************************/
8761 :
8762 : /** Return the argument value as a double.
8763 : *
8764 : * Must only be called on arguments whose type is GAAT_REAL
8765 : *
8766 : * @param hArg Handle to an argument. Must NOT be null.
8767 : * @since 3.11
8768 : */
8769 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8770 : {
8771 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8772 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8773 : {
8774 1 : CPLError(CE_Failure, CPLE_AppDefined,
8775 : "%s must only be called on arguments of type GAAT_REAL",
8776 : __func__);
8777 1 : return 0;
8778 : }
8779 7 : return hArg->ptr->Get<double>();
8780 : }
8781 :
8782 : /************************************************************************/
8783 : /* GDALAlgorithmArgGetAsStringList() */
8784 : /************************************************************************/
8785 :
8786 : /** Return the argument value as a string list.
8787 : *
8788 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8789 : *
8790 : * @param hArg Handle to an argument. Must NOT be null.
8791 : * @return a NULL terminated list of names, which must be destroyed with
8792 : * CSLDestroy()
8793 :
8794 : * @since 3.11
8795 : */
8796 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8797 : {
8798 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8799 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8800 : {
8801 1 : CPLError(CE_Failure, CPLE_AppDefined,
8802 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8803 : __func__);
8804 1 : return nullptr;
8805 : }
8806 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8807 3 : .StealList();
8808 : }
8809 :
8810 : /************************************************************************/
8811 : /* GDALAlgorithmArgGetAsIntegerList() */
8812 : /************************************************************************/
8813 :
8814 : /** Return the argument value as a integer list.
8815 : *
8816 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8817 : *
8818 : * @param hArg Handle to an argument. Must NOT be null.
8819 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8820 : * @since 3.11
8821 : */
8822 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8823 : size_t *pnCount)
8824 : {
8825 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8826 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8827 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8828 : {
8829 1 : CPLError(
8830 : CE_Failure, CPLE_AppDefined,
8831 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8832 : __func__);
8833 1 : *pnCount = 0;
8834 1 : return nullptr;
8835 : }
8836 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8837 7 : *pnCount = val.size();
8838 7 : return val.data();
8839 : }
8840 :
8841 : /************************************************************************/
8842 : /* GDALAlgorithmArgGetAsDoubleList() */
8843 : /************************************************************************/
8844 :
8845 : /** Return the argument value as a real list.
8846 : *
8847 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8848 : *
8849 : * @param hArg Handle to an argument. Must NOT be null.
8850 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8851 : * @since 3.11
8852 : */
8853 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8854 : size_t *pnCount)
8855 : {
8856 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8857 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8858 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8859 : {
8860 1 : CPLError(CE_Failure, CPLE_AppDefined,
8861 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8862 : __func__);
8863 1 : *pnCount = 0;
8864 1 : return nullptr;
8865 : }
8866 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8867 7 : *pnCount = val.size();
8868 7 : return val.data();
8869 : }
8870 :
8871 : /************************************************************************/
8872 : /* GDALAlgorithmArgSetAsBoolean() */
8873 : /************************************************************************/
8874 :
8875 : /** Set the value for a GAAT_BOOLEAN argument.
8876 : *
8877 : * It cannot be called several times for a given argument.
8878 : * Validation checks and other actions are run.
8879 : *
8880 : * @param hArg Handle to an argument. Must NOT be null.
8881 : * @param value value.
8882 : * @return true if success.
8883 : * @since 3.11
8884 : */
8885 :
8886 698 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8887 : {
8888 698 : VALIDATE_POINTER1(hArg, __func__, false);
8889 698 : return hArg->ptr->Set(value);
8890 : }
8891 :
8892 : /************************************************************************/
8893 : /* GDALAlgorithmArgSetAsString() */
8894 : /************************************************************************/
8895 :
8896 : /** Set the value for a GAAT_STRING argument.
8897 : *
8898 : * It cannot be called several times for a given argument.
8899 : * Validation checks and other actions are run.
8900 : *
8901 : * @param hArg Handle to an argument. Must NOT be null.
8902 : * @param value value (may be null)
8903 : * @return true if success.
8904 : * @since 3.11
8905 : */
8906 :
8907 3121 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
8908 : {
8909 3121 : VALIDATE_POINTER1(hArg, __func__, false);
8910 3121 : return hArg->ptr->Set(value ? value : "");
8911 : }
8912 :
8913 : /************************************************************************/
8914 : /* GDALAlgorithmArgSetAsInteger() */
8915 : /************************************************************************/
8916 :
8917 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
8918 : *
8919 : * It cannot be called several times for a given argument.
8920 : * Validation checks and other actions are run.
8921 : *
8922 : * @param hArg Handle to an argument. Must NOT be null.
8923 : * @param value value.
8924 : * @return true if success.
8925 : * @since 3.11
8926 : */
8927 :
8928 472 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8929 : {
8930 472 : VALIDATE_POINTER1(hArg, __func__, false);
8931 472 : return hArg->ptr->Set(value);
8932 : }
8933 :
8934 : /************************************************************************/
8935 : /* GDALAlgorithmArgSetAsDouble() */
8936 : /************************************************************************/
8937 :
8938 : /** Set the value for a GAAT_REAL argument.
8939 : *
8940 : * It cannot be called several times for a given argument.
8941 : * Validation checks and other actions are run.
8942 : *
8943 : * @param hArg Handle to an argument. Must NOT be null.
8944 : * @param value value.
8945 : * @return true if success.
8946 : * @since 3.11
8947 : */
8948 :
8949 243 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
8950 : {
8951 243 : VALIDATE_POINTER1(hArg, __func__, false);
8952 243 : return hArg->ptr->Set(value);
8953 : }
8954 :
8955 : /************************************************************************/
8956 : /* GDALAlgorithmArgSetAsDatasetValue() */
8957 : /************************************************************************/
8958 :
8959 : /** Set the value for a GAAT_DATASET argument.
8960 : *
8961 : * It cannot be called several times for a given argument.
8962 : * Validation checks and other actions are run.
8963 : *
8964 : * @param hArg Handle to an argument. Must NOT be null.
8965 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
8966 : * @return true if success.
8967 : * @since 3.11
8968 : */
8969 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
8970 : GDALArgDatasetValueH value)
8971 : {
8972 2 : VALIDATE_POINTER1(hArg, __func__, false);
8973 2 : VALIDATE_POINTER1(value, __func__, false);
8974 2 : return hArg->ptr->SetFrom(*(value->ptr));
8975 : }
8976 :
8977 : /************************************************************************/
8978 : /* GDALAlgorithmArgSetDataset() */
8979 : /************************************************************************/
8980 :
8981 : /** Set dataset object, increasing its reference counter.
8982 : *
8983 : * @param hArg Handle to an argument. Must NOT be null.
8984 : * @param hDS Dataset object. May be null.
8985 : * @return true if success.
8986 : * @since 3.11
8987 : */
8988 :
8989 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
8990 : {
8991 2 : VALIDATE_POINTER1(hArg, __func__, false);
8992 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
8993 : }
8994 :
8995 : /************************************************************************/
8996 : /* GDALAlgorithmArgSetAsStringList() */
8997 : /************************************************************************/
8998 :
8999 : /** Set the value for a GAAT_STRING_LIST argument.
9000 : *
9001 : * It cannot be called several times for a given argument.
9002 : * Validation checks and other actions are run.
9003 : *
9004 : * @param hArg Handle to an argument. Must NOT be null.
9005 : * @param value value as a NULL terminated list (may be null)
9006 : * @return true if success.
9007 : * @since 3.11
9008 : */
9009 :
9010 785 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
9011 : {
9012 785 : VALIDATE_POINTER1(hArg, __func__, false);
9013 785 : return hArg->ptr->Set(
9014 1570 : static_cast<std::vector<std::string>>(CPLStringList(value)));
9015 : }
9016 :
9017 : /************************************************************************/
9018 : /* GDALAlgorithmArgSetAsIntegerList() */
9019 : /************************************************************************/
9020 :
9021 : /** Set the value for a GAAT_INTEGER_LIST argument.
9022 : *
9023 : * It cannot be called several times for a given argument.
9024 : * Validation checks and other actions are run.
9025 : *
9026 : * @param hArg Handle to an argument. Must NOT be null.
9027 : * @param nCount Number of values in pnValues.
9028 : * @param pnValues Pointer to an array of integer values of size nCount.
9029 : * @return true if success.
9030 : * @since 3.11
9031 : */
9032 100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
9033 : const int *pnValues)
9034 : {
9035 100 : VALIDATE_POINTER1(hArg, __func__, false);
9036 100 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
9037 : }
9038 :
9039 : /************************************************************************/
9040 : /* GDALAlgorithmArgSetAsDoubleList() */
9041 : /************************************************************************/
9042 :
9043 : /** Set the value for a GAAT_REAL_LIST argument.
9044 : *
9045 : * It cannot be called several times for a given argument.
9046 : * Validation checks and other actions are run.
9047 : *
9048 : * @param hArg Handle to an argument. Must NOT be null.
9049 : * @param nCount Number of values in pnValues.
9050 : * @param pnValues Pointer to an array of double values of size nCount.
9051 : * @return true if success.
9052 : * @since 3.11
9053 : */
9054 154 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
9055 : const double *pnValues)
9056 : {
9057 154 : VALIDATE_POINTER1(hArg, __func__, false);
9058 154 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
9059 : }
9060 :
9061 : /************************************************************************/
9062 : /* GDALAlgorithmArgSetDatasets() */
9063 : /************************************************************************/
9064 :
9065 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9066 : *
9067 : * @param hArg Handle to an argument. Must NOT be null.
9068 : * @param nCount Number of values in pnValues.
9069 : * @param pahDS Pointer to an array of dataset of size nCount.
9070 : * @return true if success.
9071 : * @since 3.11
9072 : */
9073 :
9074 1284 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9075 : GDALDatasetH *pahDS)
9076 : {
9077 1284 : VALIDATE_POINTER1(hArg, __func__, false);
9078 2568 : std::vector<GDALArgDatasetValue> values;
9079 2594 : for (size_t i = 0; i < nCount; ++i)
9080 : {
9081 1310 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9082 : }
9083 1284 : return hArg->ptr->Set(std::move(values));
9084 : }
9085 :
9086 : /************************************************************************/
9087 : /* GDALAlgorithmArgSetDatasetNames() */
9088 : /************************************************************************/
9089 :
9090 : /** Set dataset names to a GAAT_DATASET_LIST argument.
9091 : *
9092 : * @param hArg Handle to an argument. Must NOT be null.
9093 : * @param names Dataset names as a NULL terminated list (may be null)
9094 : * @return true if success.
9095 : * @since 3.11
9096 : */
9097 :
9098 739 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9099 : {
9100 739 : VALIDATE_POINTER1(hArg, __func__, false);
9101 1478 : std::vector<GDALArgDatasetValue> values;
9102 1549 : for (size_t i = 0; names[i]; ++i)
9103 : {
9104 810 : values.emplace_back(names[i]);
9105 : }
9106 739 : return hArg->ptr->Set(std::move(values));
9107 : }
9108 :
9109 : /************************************************************************/
9110 : /* GDALArgDatasetValueCreate() */
9111 : /************************************************************************/
9112 :
9113 : /** Instantiate an empty GDALArgDatasetValue
9114 : *
9115 : * @return new handle to free with GDALArgDatasetValueRelease()
9116 : * @since 3.11
9117 : */
9118 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
9119 : {
9120 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
9121 : }
9122 :
9123 : /************************************************************************/
9124 : /* GDALArgDatasetValueRelease() */
9125 : /************************************************************************/
9126 :
9127 : /** Release a handle to a GDALArgDatasetValue
9128 : *
9129 : * @since 3.11
9130 : */
9131 3207 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9132 : {
9133 3207 : delete hValue;
9134 3207 : }
9135 :
9136 : /************************************************************************/
9137 : /* GDALArgDatasetValueGetName() */
9138 : /************************************************************************/
9139 :
9140 : /** Return the name component of the GDALArgDatasetValue
9141 : *
9142 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9143 : * @return string whose lifetime is bound to hAlg and which must not
9144 : * be freed.
9145 : * @since 3.11
9146 : */
9147 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9148 : {
9149 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9150 1 : return hValue->ptr->GetName().c_str();
9151 : }
9152 :
9153 : /************************************************************************/
9154 : /* GDALArgDatasetValueGetDatasetRef() */
9155 : /************************************************************************/
9156 :
9157 : /** Return the dataset component of the GDALArgDatasetValue.
9158 : *
9159 : * This does not modify the reference counter, hence the lifetime of the
9160 : * returned object is not guaranteed to exceed the one of hValue.
9161 : *
9162 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9163 : * @since 3.11
9164 : */
9165 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9166 : {
9167 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9168 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9169 : }
9170 :
9171 : /************************************************************************/
9172 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
9173 : /************************************************************************/
9174 :
9175 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
9176 : * reference count if not null. Once done with the dataset, the caller should
9177 : * call GDALReleaseDataset().
9178 : *
9179 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9180 : * @since 3.11
9181 : */
9182 : GDALDatasetH
9183 1051 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9184 : {
9185 1051 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9186 1051 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9187 : }
9188 :
9189 : /************************************************************************/
9190 : /* GDALArgDatasetValueSetName() */
9191 : /************************************************************************/
9192 :
9193 : /** Set dataset name
9194 : *
9195 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9196 : * @param pszName Dataset name. May be null.
9197 : * @since 3.11
9198 : */
9199 :
9200 1376 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9201 : const char *pszName)
9202 : {
9203 1376 : VALIDATE_POINTER0(hValue, __func__);
9204 1376 : hValue->ptr->Set(pszName ? pszName : "");
9205 : }
9206 :
9207 : /************************************************************************/
9208 : /* GDALArgDatasetValueSetDataset() */
9209 : /************************************************************************/
9210 :
9211 : /** Set dataset object, increasing its reference counter.
9212 : *
9213 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9214 : * @param hDS Dataset object. May be null.
9215 : * @since 3.11
9216 : */
9217 :
9218 765 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9219 : GDALDatasetH hDS)
9220 : {
9221 765 : VALIDATE_POINTER0(hValue, __func__);
9222 765 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9223 : }
|