Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: GDALAlgorithm class
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_enumerate.h"
16 : #include "cpl_error.h"
17 : #include "cpl_error_internal.h"
18 : #include "cpl_json.h"
19 : #include "cpl_levenshtein.h"
20 : #include "cpl_minixml.h"
21 : #include "cpl_multiproc.h"
22 :
23 : #include "gdalalgorithm.h"
24 : #include "gdalalg_abstract_pipeline.h"
25 : #include "gdal_priv.h"
26 : #include "gdal_thread_pool.h"
27 : #include "memdataset.h"
28 : #include "ogrsf_frmts.h"
29 : #include "ogr_p.h"
30 : #include "ogr_spatialref.h"
31 : #include "vrtdataset.h"
32 :
33 : #include <algorithm>
34 : #include <cassert>
35 : #include <cerrno>
36 : #include <cmath>
37 : #include <cstdlib>
38 : #include <limits>
39 : #include <map>
40 : #include <type_traits>
41 : #include <string_view>
42 : #include <regex>
43 :
44 : #ifndef _
45 : #define _(x) (x)
46 : #endif
47 :
48 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
49 :
50 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
51 :
52 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
53 :
54 : //! @cond Doxygen_Suppress
55 : struct GDALAlgorithmArgHS
56 : {
57 : GDALAlgorithmArg *ptr = nullptr;
58 :
59 366422 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
60 : {
61 366422 : }
62 : };
63 :
64 : //! @endcond
65 :
66 : //! @cond Doxygen_Suppress
67 : struct GDALArgDatasetValueHS
68 : {
69 : GDALArgDatasetValue val{};
70 : GDALArgDatasetValue *ptr = nullptr;
71 :
72 1 : GDALArgDatasetValueHS() : ptr(&val)
73 : {
74 1 : }
75 :
76 3514 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
77 : {
78 3514 : }
79 :
80 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
81 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
82 : };
83 :
84 : //! @endcond
85 :
86 : /************************************************************************/
87 : /* GDALAlgorithmArgTypeIsList() */
88 : /************************************************************************/
89 :
90 473671 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
91 : {
92 473671 : switch (type)
93 : {
94 311676 : case GAAT_BOOLEAN:
95 : case GAAT_STRING:
96 : case GAAT_INTEGER:
97 : case GAAT_REAL:
98 : case GAAT_DATASET:
99 311676 : break;
100 :
101 161995 : case GAAT_STRING_LIST:
102 : case GAAT_INTEGER_LIST:
103 : case GAAT_REAL_LIST:
104 : case GAAT_DATASET_LIST:
105 161995 : return true;
106 : }
107 :
108 311676 : return false;
109 : }
110 :
111 : /************************************************************************/
112 : /* GDALAlgorithmArgTypeName() */
113 : /************************************************************************/
114 :
115 5792 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
116 : {
117 5792 : switch (type)
118 : {
119 1426 : case GAAT_BOOLEAN:
120 1426 : break;
121 1578 : case GAAT_STRING:
122 1578 : return "string";
123 397 : case GAAT_INTEGER:
124 397 : return "integer";
125 491 : case GAAT_REAL:
126 491 : return "real";
127 273 : case GAAT_DATASET:
128 273 : return "dataset";
129 1102 : case GAAT_STRING_LIST:
130 1102 : return "string_list";
131 85 : case GAAT_INTEGER_LIST:
132 85 : return "integer_list";
133 221 : case GAAT_REAL_LIST:
134 221 : return "real_list";
135 219 : case GAAT_DATASET_LIST:
136 219 : return "dataset_list";
137 : }
138 :
139 1426 : return "boolean";
140 : }
141 :
142 : /************************************************************************/
143 : /* GDALAlgorithmArgDatasetTypeName() */
144 : /************************************************************************/
145 :
146 29975 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
147 : {
148 29975 : std::string ret;
149 29975 : if ((type & GDAL_OF_RASTER) != 0)
150 17849 : ret = "raster";
151 29975 : if ((type & GDAL_OF_VECTOR) != 0)
152 : {
153 12767 : if (!ret.empty())
154 : {
155 1806 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
156 294 : ret += ", ";
157 : else
158 1512 : ret += " or ";
159 : }
160 12767 : ret += "vector";
161 : }
162 29975 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
163 : {
164 1387 : if (!ret.empty())
165 : {
166 439 : ret += " or ";
167 : }
168 1387 : ret += "multidimensional raster";
169 : }
170 29975 : return ret;
171 : }
172 :
173 : /************************************************************************/
174 : /* GDALAlgorithmArgDecl() */
175 : /************************************************************************/
176 :
177 : // cppcheck-suppress uninitMemberVar
178 379595 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
179 : char chShortName,
180 : const std::string &description,
181 379595 : GDALAlgorithmArgType type)
182 : : m_longName(longName),
183 379595 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
184 : m_description(description), m_type(type),
185 759190 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
186 379595 : .toupper()),
187 1138780 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
188 : {
189 379595 : if (m_type == GAAT_BOOLEAN)
190 : {
191 161510 : m_defaultValue = false;
192 : }
193 379595 : }
194 :
195 : /************************************************************************/
196 : /* GDALAlgorithmArgDecl::SetMinCount() */
197 : /************************************************************************/
198 :
199 20919 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
200 : {
201 20919 : if (!GDALAlgorithmArgTypeIsList(m_type))
202 : {
203 1 : CPLError(CE_Failure, CPLE_NotSupported,
204 : "SetMinCount() illegal on scalar argument '%s'",
205 1 : GetName().c_str());
206 : }
207 : else
208 : {
209 20918 : m_minCount = count;
210 : }
211 20919 : return *this;
212 : }
213 :
214 : /************************************************************************/
215 : /* GDALAlgorithmArgDecl::SetMaxCount() */
216 : /************************************************************************/
217 :
218 20094 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
219 : {
220 20094 : if (!GDALAlgorithmArgTypeIsList(m_type))
221 : {
222 1 : CPLError(CE_Failure, CPLE_NotSupported,
223 : "SetMaxCount() illegal on scalar argument '%s'",
224 1 : GetName().c_str());
225 : }
226 : else
227 : {
228 20093 : m_maxCount = count;
229 : }
230 20094 : return *this;
231 : }
232 :
233 : /************************************************************************/
234 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
235 : /************************************************************************/
236 :
237 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
238 :
239 : /************************************************************************/
240 : /* GDALAlgorithmArg::Set() */
241 : /************************************************************************/
242 :
243 1329 : bool GDALAlgorithmArg::Set(bool value)
244 : {
245 1329 : if (m_decl.GetType() != GAAT_BOOLEAN)
246 : {
247 14 : CPLError(
248 : CE_Failure, CPLE_AppDefined,
249 : "Calling Set(bool) on argument '%s' of type %s is not supported",
250 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
251 7 : return false;
252 : }
253 1322 : return SetInternal(value);
254 : }
255 :
256 4555 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
257 : {
258 4607 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
259 52 : value.front() == '@')
260 : {
261 2 : GByte *pabyData = nullptr;
262 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
263 2 : 10 * 1024 * 1024))
264 : {
265 : // Remove UTF-8 BOM
266 1 : size_t offset = 0;
267 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
268 1 : pabyData[2] == 0xBF)
269 : {
270 1 : offset = 3;
271 : }
272 1 : value = reinterpret_cast<const char *>(pabyData + offset);
273 1 : VSIFree(pabyData);
274 : }
275 : else
276 : {
277 1 : return false;
278 : }
279 : }
280 :
281 4554 : if (m_decl.IsRemoveSQLCommentsEnabled())
282 51 : value = CPLRemoveSQLComments(value);
283 :
284 4554 : return true;
285 : }
286 :
287 4590 : bool GDALAlgorithmArg::Set(const std::string &value)
288 : {
289 4590 : switch (m_decl.GetType())
290 : {
291 9 : case GAAT_BOOLEAN:
292 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
293 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
294 : {
295 4 : return Set(true);
296 : }
297 5 : else if (EQUAL(value.c_str(), "0") ||
298 4 : EQUAL(value.c_str(), "FALSE") ||
299 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
300 : {
301 4 : return Set(false);
302 : }
303 1 : break;
304 :
305 8 : case GAAT_INTEGER:
306 : case GAAT_INTEGER_LIST:
307 : {
308 8 : errno = 0;
309 8 : char *endptr = nullptr;
310 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
311 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
312 5 : endptr == value.c_str() + value.size())
313 : {
314 3 : if (m_decl.GetType() == GAAT_INTEGER)
315 3 : return Set(static_cast<int>(v));
316 : else
317 1 : return Set(std::vector<int>{static_cast<int>(v)});
318 : }
319 5 : break;
320 : }
321 :
322 5 : case GAAT_REAL:
323 : case GAAT_REAL_LIST:
324 : {
325 5 : char *endptr = nullptr;
326 5 : const double v = CPLStrtod(value.c_str(), &endptr);
327 5 : if (endptr == value.c_str() + value.size())
328 : {
329 3 : if (m_decl.GetType() == GAAT_REAL)
330 3 : return Set(v);
331 : else
332 1 : return Set(std::vector<double>{v});
333 : }
334 2 : break;
335 : }
336 :
337 4532 : case GAAT_STRING:
338 4532 : break;
339 :
340 8 : case GAAT_STRING_LIST:
341 16 : return Set(std::vector<std::string>{value});
342 :
343 28 : case GAAT_DATASET:
344 28 : return SetDatasetName(value);
345 :
346 0 : case GAAT_DATASET_LIST:
347 : {
348 0 : std::vector<GDALArgDatasetValue> v;
349 0 : v.resize(1);
350 0 : v[0].Set(value);
351 0 : return Set(std::move(v));
352 : }
353 : }
354 :
355 4540 : if (m_decl.GetType() != GAAT_STRING)
356 : {
357 16 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "Calling Set(std::string) on argument '%s' of type %s is not "
359 : "supported",
360 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
361 8 : return false;
362 : }
363 :
364 4532 : std::string newValue(value);
365 4532 : return ProcessString(newValue) && SetInternal(newValue);
366 : }
367 :
368 897 : bool GDALAlgorithmArg::Set(int value)
369 : {
370 897 : if (m_decl.GetType() == GAAT_BOOLEAN)
371 : {
372 3 : if (value == 1)
373 1 : return Set(true);
374 2 : else if (value == 0)
375 1 : return Set(false);
376 : }
377 894 : else if (m_decl.GetType() == GAAT_REAL)
378 : {
379 3 : return Set(static_cast<double>(value));
380 : }
381 891 : else if (m_decl.GetType() == GAAT_STRING)
382 : {
383 2 : return Set(std::to_string(value));
384 : }
385 889 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
386 : {
387 1 : return Set(std::vector<int>{value});
388 : }
389 888 : else if (m_decl.GetType() == GAAT_REAL_LIST)
390 : {
391 1 : return Set(std::vector<double>{static_cast<double>(value)});
392 : }
393 887 : else if (m_decl.GetType() == GAAT_STRING_LIST)
394 : {
395 2 : return Set(std::vector<std::string>{std::to_string(value)});
396 : }
397 :
398 887 : if (m_decl.GetType() != GAAT_INTEGER)
399 : {
400 2 : CPLError(
401 : CE_Failure, CPLE_AppDefined,
402 : "Calling Set(int) on argument '%s' of type %s is not supported",
403 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
404 1 : return false;
405 : }
406 886 : return SetInternal(value);
407 : }
408 :
409 365 : bool GDALAlgorithmArg::Set(double value)
410 : {
411 368 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
412 368 : value <= INT_MAX && static_cast<int>(value) == value)
413 : {
414 2 : return Set(static_cast<int>(value));
415 : }
416 363 : else if (m_decl.GetType() == GAAT_STRING)
417 : {
418 2 : return Set(std::to_string(value));
419 : }
420 363 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
421 363 : value <= INT_MAX && static_cast<int>(value) == value)
422 : {
423 1 : return Set(std::vector<int>{static_cast<int>(value)});
424 : }
425 360 : else if (m_decl.GetType() == GAAT_REAL_LIST)
426 : {
427 0 : return Set(std::vector<double>{value});
428 : }
429 360 : else if (m_decl.GetType() == GAAT_STRING_LIST)
430 : {
431 2 : return Set(std::vector<std::string>{std::to_string(value)});
432 : }
433 359 : else if (m_decl.GetType() != GAAT_REAL)
434 : {
435 6 : CPLError(
436 : CE_Failure, CPLE_AppDefined,
437 : "Calling Set(double) on argument '%s' of type %s is not supported",
438 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
439 3 : return false;
440 : }
441 356 : return SetInternal(value);
442 : }
443 :
444 6338 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
445 : {
446 6341 : if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
447 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
448 : {
449 3 : CPLError(
450 : CE_Failure, CPLE_AppDefined,
451 : "Dataset object '%s' is created by algorithm and cannot be set "
452 : "as an input.",
453 3 : arg->GetName().c_str());
454 3 : return false;
455 : }
456 6335 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
457 : {
458 8 : CPLError(CE_Failure, CPLE_AppDefined,
459 : "Dataset%s '%s' must be provided by name, not as object.",
460 8 : arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
461 4 : return false;
462 : }
463 :
464 6331 : return true;
465 : }
466 :
467 32 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
468 : {
469 56 : if (m_decl.GetType() != GAAT_DATASET &&
470 24 : m_decl.GetType() != GAAT_DATASET_LIST)
471 : {
472 2 : CPLError(CE_Failure, CPLE_AppDefined,
473 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
474 : "is not supported",
475 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
476 1 : return false;
477 : }
478 31 : if (!CheckCanSetDatasetObject(this))
479 2 : return false;
480 29 : m_explicitlySet = true;
481 29 : if (m_decl.GetType() == GAAT_DATASET)
482 : {
483 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
484 6 : val.Set(ds);
485 : }
486 : else
487 : {
488 23 : CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
489 23 : auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
490 23 : val.resize(1);
491 23 : val[0].Set(ds);
492 : }
493 29 : return RunAllActions();
494 : }
495 :
496 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
497 : {
498 3 : if (m_decl.GetType() != GAAT_DATASET)
499 : {
500 2 : CPLError(CE_Failure, CPLE_AppDefined,
501 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
502 : "is not supported",
503 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
504 1 : return false;
505 : }
506 2 : if (!CheckCanSetDatasetObject(this))
507 1 : return false;
508 1 : m_explicitlySet = true;
509 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
510 1 : val.Set(std::move(ds));
511 1 : return RunAllActions();
512 : }
513 :
514 649 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
515 : {
516 649 : if (m_decl.GetType() != GAAT_DATASET)
517 : {
518 2 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "Calling SetDatasetName() on argument '%s' of type %s is "
520 : "not supported",
521 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
522 1 : return false;
523 : }
524 648 : m_explicitlySet = true;
525 648 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
526 648 : return RunAllActions();
527 : }
528 :
529 1146 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
530 : {
531 1146 : if (m_decl.GetType() != GAAT_DATASET)
532 : {
533 2 : CPLError(CE_Failure, CPLE_AppDefined,
534 : "Calling SetFrom() on argument '%s' of type %s is "
535 : "not supported",
536 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
537 1 : return false;
538 : }
539 1145 : if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
540 1 : return false;
541 1144 : m_explicitlySet = true;
542 1144 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
543 1144 : return RunAllActions();
544 : }
545 :
546 1296 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
547 : {
548 1296 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
549 : {
550 3 : std::vector<int> v_i;
551 4 : for (const std::string &s : value)
552 : {
553 3 : errno = 0;
554 3 : char *endptr = nullptr;
555 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
556 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
557 2 : endptr == s.c_str() + s.size())
558 : {
559 1 : v_i.push_back(static_cast<int>(v));
560 : }
561 : else
562 : {
563 2 : break;
564 : }
565 : }
566 3 : if (v_i.size() == value.size())
567 1 : return Set(v_i);
568 : }
569 1293 : else if (m_decl.GetType() == GAAT_REAL_LIST)
570 : {
571 2 : std::vector<double> v_d;
572 3 : for (const std::string &s : value)
573 : {
574 2 : char *endptr = nullptr;
575 2 : const double v = CPLStrtod(s.c_str(), &endptr);
576 2 : if (endptr == s.c_str() + s.size())
577 : {
578 1 : v_d.push_back(v);
579 : }
580 : else
581 : {
582 1 : break;
583 : }
584 : }
585 2 : if (v_d.size() == value.size())
586 1 : return Set(v_d);
587 : }
588 2580 : else if ((m_decl.GetType() == GAAT_INTEGER ||
589 2577 : m_decl.GetType() == GAAT_REAL ||
590 3870 : m_decl.GetType() == GAAT_STRING) &&
591 5 : value.size() == 1)
592 : {
593 4 : return Set(value[0]);
594 : }
595 1287 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
596 : {
597 30 : std::vector<GDALArgDatasetValue> dsVector;
598 46 : for (const std::string &s : value)
599 31 : dsVector.emplace_back(s);
600 15 : return Set(std::move(dsVector));
601 : }
602 :
603 1275 : if (m_decl.GetType() != GAAT_STRING_LIST)
604 : {
605 10 : CPLError(CE_Failure, CPLE_AppDefined,
606 : "Calling Set(const std::vector<std::string> &) on argument "
607 : "'%s' of type %s is not supported",
608 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
609 5 : return false;
610 : }
611 :
612 2520 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
613 1250 : m_decl.IsRemoveSQLCommentsEnabled())
614 : {
615 40 : std::vector<std::string> newValue(value);
616 43 : for (auto &s : newValue)
617 : {
618 23 : if (!ProcessString(s))
619 0 : return false;
620 : }
621 20 : return SetInternal(newValue);
622 : }
623 : else
624 : {
625 1250 : return SetInternal(value);
626 : }
627 : }
628 :
629 135 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
630 : {
631 135 : if (m_decl.GetType() == GAAT_REAL_LIST)
632 : {
633 2 : std::vector<double> v_d;
634 2 : for (int i : value)
635 1 : v_d.push_back(i);
636 1 : return Set(v_d);
637 : }
638 134 : else if (m_decl.GetType() == GAAT_STRING_LIST)
639 : {
640 2 : std::vector<std::string> v_s;
641 3 : for (int i : value)
642 2 : v_s.push_back(std::to_string(i));
643 1 : return Set(v_s);
644 : }
645 264 : else if ((m_decl.GetType() == GAAT_INTEGER ||
646 260 : m_decl.GetType() == GAAT_REAL ||
647 395 : m_decl.GetType() == GAAT_STRING) &&
648 5 : value.size() == 1)
649 : {
650 3 : return Set(value[0]);
651 : }
652 :
653 130 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
654 : {
655 6 : CPLError(CE_Failure, CPLE_AppDefined,
656 : "Calling Set(const std::vector<int> &) on argument '%s' of "
657 : "type %s is not supported",
658 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
659 3 : return false;
660 : }
661 127 : return SetInternal(value);
662 : }
663 :
664 361 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
665 : {
666 361 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
667 : {
668 2 : std::vector<int> v_i;
669 3 : for (double d : value)
670 : {
671 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
672 : {
673 1 : v_i.push_back(static_cast<int>(d));
674 : }
675 : else
676 : {
677 : break;
678 : }
679 : }
680 2 : if (v_i.size() == value.size())
681 1 : return Set(v_i);
682 : }
683 359 : else if (m_decl.GetType() == GAAT_STRING_LIST)
684 : {
685 2 : std::vector<std::string> v_s;
686 3 : for (double d : value)
687 2 : v_s.push_back(std::to_string(d));
688 1 : return Set(v_s);
689 : }
690 715 : else if ((m_decl.GetType() == GAAT_INTEGER ||
691 713 : m_decl.GetType() == GAAT_REAL ||
692 1072 : m_decl.GetType() == GAAT_STRING) &&
693 3 : value.size() == 1)
694 : {
695 3 : return Set(value[0]);
696 : }
697 :
698 356 : if (m_decl.GetType() != GAAT_REAL_LIST)
699 : {
700 4 : CPLError(CE_Failure, CPLE_AppDefined,
701 : "Calling Set(const std::vector<double> &) on argument '%s' of "
702 : "type %s is not supported",
703 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
704 2 : return false;
705 : }
706 354 : return SetInternal(value);
707 : }
708 :
709 4025 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
710 : {
711 4025 : if (m_decl.GetType() != GAAT_DATASET_LIST)
712 : {
713 2 : CPLError(CE_Failure, CPLE_AppDefined,
714 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
715 : "argument '%s' of type %s is not supported",
716 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
717 1 : return false;
718 : }
719 4024 : m_explicitlySet = true;
720 4024 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
721 4024 : return RunAllActions();
722 : }
723 :
724 : GDALAlgorithmArg &
725 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
726 : {
727 0 : Set(std::move(value));
728 0 : return *this;
729 : }
730 :
731 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
732 : {
733 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
734 1 : return Set(value.exportToWkt(apszOptions));
735 : }
736 :
737 5057 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
738 : {
739 5057 : if (m_decl.GetType() != other.GetType())
740 : {
741 2 : CPLError(CE_Failure, CPLE_AppDefined,
742 : "Calling SetFrom() on argument '%s' of type %s whereas "
743 : "other argument type is %s is not supported",
744 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
745 : GDALAlgorithmArgTypeName(other.GetType()));
746 1 : return false;
747 : }
748 :
749 5056 : switch (m_decl.GetType())
750 : {
751 103 : case GAAT_BOOLEAN:
752 103 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
753 103 : break;
754 925 : case GAAT_STRING:
755 1850 : *std::get<std::string *>(m_value) =
756 925 : *std::get<std::string *>(other.m_value);
757 925 : break;
758 7 : case GAAT_INTEGER:
759 7 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
760 7 : break;
761 1 : case GAAT_REAL:
762 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
763 1 : break;
764 1140 : case GAAT_DATASET:
765 1140 : return SetFrom(other.Get<GDALArgDatasetValue>());
766 65 : case GAAT_STRING_LIST:
767 130 : *std::get<std::vector<std::string> *>(m_value) =
768 65 : *std::get<std::vector<std::string> *>(other.m_value);
769 65 : break;
770 1 : case GAAT_INTEGER_LIST:
771 2 : *std::get<std::vector<int> *>(m_value) =
772 1 : *std::get<std::vector<int> *>(other.m_value);
773 1 : break;
774 1 : case GAAT_REAL_LIST:
775 2 : *std::get<std::vector<double> *>(m_value) =
776 1 : *std::get<std::vector<double> *>(other.m_value);
777 1 : break;
778 2813 : case GAAT_DATASET_LIST:
779 : {
780 2813 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
781 2819 : for (const auto &val :
782 8451 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
783 : {
784 5638 : GDALArgDatasetValue v;
785 2819 : v.SetFrom(val);
786 2819 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
787 2819 : ->push_back(std::move(v));
788 : }
789 2813 : break;
790 : }
791 : }
792 3916 : m_explicitlySet = true;
793 3916 : return RunAllActions();
794 : }
795 :
796 : /************************************************************************/
797 : /* GDALAlgorithmArg::RunAllActions() */
798 : /************************************************************************/
799 :
800 18608 : bool GDALAlgorithmArg::RunAllActions()
801 : {
802 18608 : if (!RunValidationActions())
803 156 : return false;
804 18452 : RunActions();
805 18452 : return true;
806 : }
807 :
808 : /************************************************************************/
809 : /* GDALAlgorithmArg::RunActions() */
810 : /************************************************************************/
811 :
812 18453 : void GDALAlgorithmArg::RunActions()
813 : {
814 18768 : for (const auto &f : m_actions)
815 315 : f();
816 18453 : }
817 :
818 : /************************************************************************/
819 : /* GDALAlgorithmArg::ValidateChoice() */
820 : /************************************************************************/
821 :
822 : // Returns the canonical value if matching a valid choice, or empty string
823 : // otherwise.
824 2801 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
825 : {
826 15492 : for (const std::string &choice : GetChoices())
827 : {
828 15348 : if (EQUAL(value.c_str(), choice.c_str()))
829 : {
830 2657 : return choice;
831 : }
832 : }
833 :
834 221 : for (const std::string &choice : GetHiddenChoices())
835 : {
836 198 : if (EQUAL(value.c_str(), choice.c_str()))
837 : {
838 121 : return choice;
839 : }
840 : }
841 :
842 46 : std::string expected;
843 262 : for (const auto &choice : GetChoices())
844 : {
845 239 : if (!expected.empty())
846 216 : expected += ", ";
847 239 : expected += '\'';
848 239 : expected += choice;
849 239 : expected += '\'';
850 : }
851 23 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
852 : {
853 6 : return "?";
854 : }
855 34 : CPLError(CE_Failure, CPLE_IllegalArg,
856 : "Invalid value '%s' for string argument '%s'. Should be "
857 : "one among %s.",
858 17 : value.c_str(), GetName().c_str(), expected.c_str());
859 17 : return std::string();
860 : }
861 :
862 : /************************************************************************/
863 : /* GDALAlgorithmArg::ValidateIntRange() */
864 : /************************************************************************/
865 :
866 2631 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
867 : {
868 2631 : bool ret = true;
869 :
870 2631 : const auto [minVal, minValIsIncluded] = GetMinValue();
871 2631 : if (!std::isnan(minVal))
872 : {
873 1950 : if (minValIsIncluded && val < minVal)
874 : {
875 3 : CPLError(CE_Failure, CPLE_IllegalArg,
876 : "Value of argument '%s' is %d, but should be >= %d",
877 3 : GetName().c_str(), val, static_cast<int>(minVal));
878 3 : ret = false;
879 : }
880 1947 : else if (!minValIsIncluded && val <= minVal)
881 : {
882 1 : CPLError(CE_Failure, CPLE_IllegalArg,
883 : "Value of argument '%s' is %d, but should be > %d",
884 1 : GetName().c_str(), val, static_cast<int>(minVal));
885 1 : ret = false;
886 : }
887 : }
888 :
889 2631 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
890 2631 : if (!std::isnan(maxVal))
891 : {
892 :
893 428 : if (maxValIsIncluded && val > maxVal)
894 : {
895 1 : CPLError(CE_Failure, CPLE_IllegalArg,
896 : "Value of argument '%s' is %d, but should be <= %d",
897 1 : GetName().c_str(), val, static_cast<int>(maxVal));
898 1 : ret = false;
899 : }
900 427 : else if (!maxValIsIncluded && val >= maxVal)
901 : {
902 1 : CPLError(CE_Failure, CPLE_IllegalArg,
903 : "Value of argument '%s' is %d, but should be < %d",
904 1 : GetName().c_str(), val, static_cast<int>(maxVal));
905 1 : ret = false;
906 : }
907 : }
908 :
909 2631 : return ret;
910 : }
911 :
912 : /************************************************************************/
913 : /* GDALAlgorithmArg::ValidateRealRange() */
914 : /************************************************************************/
915 :
916 2791 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
917 : {
918 2791 : bool ret = true;
919 :
920 2791 : const auto [minVal, minValIsIncluded] = GetMinValue();
921 2791 : if (!std::isnan(minVal))
922 : {
923 216 : if (minValIsIncluded && !(val >= minVal))
924 : {
925 11 : CPLError(CE_Failure, CPLE_IllegalArg,
926 : "Value of argument '%s' is %g, but should be >= %g",
927 11 : GetName().c_str(), val, minVal);
928 11 : ret = false;
929 : }
930 205 : else if (!minValIsIncluded && !(val > minVal))
931 : {
932 4 : CPLError(CE_Failure, CPLE_IllegalArg,
933 : "Value of argument '%s' is %g, but should be > %g",
934 4 : GetName().c_str(), val, minVal);
935 4 : ret = false;
936 : }
937 : }
938 :
939 2791 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
940 2791 : if (!std::isnan(maxVal))
941 : {
942 :
943 58 : if (maxValIsIncluded && !(val <= maxVal))
944 : {
945 2 : CPLError(CE_Failure, CPLE_IllegalArg,
946 : "Value of argument '%s' is %g, but should be <= %g",
947 2 : GetName().c_str(), val, maxVal);
948 2 : ret = false;
949 : }
950 56 : else if (!maxValIsIncluded && !(val < maxVal))
951 : {
952 1 : CPLError(CE_Failure, CPLE_IllegalArg,
953 : "Value of argument '%s' is %g, but should be < %g",
954 1 : GetName().c_str(), val, maxVal);
955 1 : ret = false;
956 : }
957 : }
958 :
959 2791 : return ret;
960 : }
961 :
962 : /************************************************************************/
963 : /* CheckDuplicateValues() */
964 : /************************************************************************/
965 :
966 : template <class T>
967 180 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
968 : const std::vector<T> &values)
969 : {
970 360 : auto tmpValues = values;
971 180 : bool bHasDupValues = false;
972 : if constexpr (std::is_floating_point_v<T>)
973 : {
974 : // Avoid undefined behavior with NaN values
975 4 : std::sort(tmpValues.begin(), tmpValues.end(),
976 21 : [](T a, T b)
977 : {
978 21 : if (std::isnan(a) && !std::isnan(b))
979 3 : return true;
980 18 : if (std::isnan(b))
981 10 : return false;
982 8 : return a < b;
983 : });
984 :
985 : bHasDupValues =
986 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
987 6 : [](T a, T b)
988 : {
989 6 : if (std::isnan(a) && std::isnan(b))
990 1 : return true;
991 5 : return a == b;
992 8 : }) != tmpValues.end();
993 : }
994 : else
995 : {
996 176 : std::sort(tmpValues.begin(), tmpValues.end());
997 176 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
998 352 : tmpValues.end()) != tmpValues.end();
999 : }
1000 180 : if (bHasDupValues)
1001 : {
1002 10 : CPLError(CE_Failure, CPLE_AppDefined,
1003 : "'%s' must be a list of unique values.",
1004 10 : arg->GetName().c_str());
1005 10 : return false;
1006 : }
1007 170 : return true;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* GDALAlgorithmArg::RunValidationActions() */
1012 : /************************************************************************/
1013 :
1014 41076 : bool GDALAlgorithmArg::RunValidationActions()
1015 : {
1016 41076 : bool ret = true;
1017 :
1018 41076 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1019 : {
1020 1864 : auto &val = Get<std::string>();
1021 3728 : std::string validVal = ValidateChoice(val);
1022 1864 : if (validVal.empty())
1023 12 : ret = false;
1024 : else
1025 1852 : val = std::move(validVal);
1026 : }
1027 39212 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1028 : {
1029 677 : auto &values = Get<std::vector<std::string>>();
1030 1614 : for (std::string &val : values)
1031 : {
1032 1874 : std::string validVal = ValidateChoice(val);
1033 937 : if (validVal.empty())
1034 5 : ret = false;
1035 : else
1036 932 : val = std::move(validVal);
1037 : }
1038 : }
1039 :
1040 : const auto CheckMinCharCount =
1041 1149 : [this, &ret](const std::string &val, int nMinCharCount)
1042 : {
1043 1137 : if (val.size() < static_cast<size_t>(nMinCharCount))
1044 : {
1045 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1046 : "Value of argument '%s' is '%s', but should have at least "
1047 : "%d character%s",
1048 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1049 : nMinCharCount > 1 ? "s" : "");
1050 6 : ret = false;
1051 : }
1052 42213 : };
1053 :
1054 : const auto CheckMaxCharCount =
1055 14839 : [this, &ret](const std::string &val, int nMaxCharCount)
1056 : {
1057 14837 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1058 : {
1059 2 : CPLError(
1060 : CE_Failure, CPLE_IllegalArg,
1061 : "Value of argument '%s' is '%s', but should have no more than "
1062 : "%d character%s",
1063 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1064 : nMaxCharCount > 1 ? "s" : "");
1065 1 : ret = false;
1066 : }
1067 55913 : };
1068 :
1069 41076 : switch (GetType())
1070 : {
1071 2987 : case GAAT_BOOLEAN:
1072 2987 : break;
1073 :
1074 11333 : case GAAT_STRING:
1075 : {
1076 11333 : const auto &val = Get<std::string>();
1077 11333 : const int nMinCharCount = GetMinCharCount();
1078 11333 : if (nMinCharCount > 0)
1079 : {
1080 1055 : CheckMinCharCount(val, nMinCharCount);
1081 : }
1082 :
1083 11333 : const int nMaxCharCount = GetMaxCharCount();
1084 11333 : CheckMaxCharCount(val, nMaxCharCount);
1085 11333 : break;
1086 : }
1087 :
1088 2793 : case GAAT_STRING_LIST:
1089 : {
1090 2793 : const int nMinCharCount = GetMinCharCount();
1091 2793 : const int nMaxCharCount = GetMaxCharCount();
1092 2793 : const auto &values = Get<std::vector<std::string>>();
1093 6297 : for (const auto &val : values)
1094 : {
1095 3504 : if (nMinCharCount > 0)
1096 82 : CheckMinCharCount(val, nMinCharCount);
1097 3504 : CheckMaxCharCount(val, nMaxCharCount);
1098 : }
1099 :
1100 2955 : if (!GetDuplicateValuesAllowed() &&
1101 162 : !CheckDuplicateValues(this, values))
1102 2 : ret = false;
1103 2793 : break;
1104 : }
1105 :
1106 2083 : case GAAT_INTEGER:
1107 : {
1108 2083 : ret = ValidateIntRange(Get<int>()) && ret;
1109 2083 : break;
1110 : }
1111 :
1112 270 : case GAAT_INTEGER_LIST:
1113 : {
1114 270 : const auto &values = Get<std::vector<int>>();
1115 818 : for (int v : values)
1116 548 : ret = ValidateIntRange(v) && ret;
1117 :
1118 273 : if (!GetDuplicateValuesAllowed() &&
1119 3 : !CheckDuplicateValues(this, values))
1120 1 : ret = false;
1121 270 : break;
1122 : }
1123 :
1124 701 : case GAAT_REAL:
1125 : {
1126 701 : ret = ValidateRealRange(Get<double>()) && ret;
1127 701 : break;
1128 : }
1129 :
1130 767 : case GAAT_REAL_LIST:
1131 : {
1132 767 : const auto &values = Get<std::vector<double>>();
1133 2857 : for (double v : values)
1134 2090 : ret = ValidateRealRange(v) && ret;
1135 :
1136 771 : if (!GetDuplicateValuesAllowed() &&
1137 4 : !CheckDuplicateValues(this, values))
1138 2 : ret = false;
1139 767 : break;
1140 : }
1141 :
1142 6615 : case GAAT_DATASET:
1143 6615 : break;
1144 :
1145 13527 : case GAAT_DATASET_LIST:
1146 : {
1147 13527 : if (!GetDuplicateValuesAllowed())
1148 : {
1149 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1150 22 : std::vector<std::string> aosValues;
1151 34 : for (const auto &v : values)
1152 : {
1153 23 : const GDALDataset *poDS = v.GetDatasetRef();
1154 23 : if (poDS)
1155 : {
1156 16 : auto poDriver = poDS->GetDriver();
1157 : // The dataset name for a MEM driver is not relevant,
1158 : // so use the pointer address
1159 32 : if ((poDriver &&
1160 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1161 8 : poDS->GetDescription()[0] == 0)
1162 : {
1163 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1164 : }
1165 : else
1166 : {
1167 8 : aosValues.push_back(poDS->GetDescription());
1168 : }
1169 : }
1170 : else
1171 : {
1172 7 : aosValues.push_back(v.GetName());
1173 : }
1174 : }
1175 11 : if (!CheckDuplicateValues(this, aosValues))
1176 5 : ret = false;
1177 : }
1178 13527 : break;
1179 : }
1180 : }
1181 :
1182 41076 : if (GDALAlgorithmArgTypeIsList(GetType()))
1183 : {
1184 17357 : int valueCount = 0;
1185 17357 : if (GetType() == GAAT_STRING_LIST)
1186 : {
1187 2793 : valueCount =
1188 2793 : static_cast<int>(Get<std::vector<std::string>>().size());
1189 : }
1190 14564 : else if (GetType() == GAAT_INTEGER_LIST)
1191 : {
1192 270 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1193 : }
1194 14294 : else if (GetType() == GAAT_REAL_LIST)
1195 : {
1196 767 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1197 : }
1198 13527 : else if (GetType() == GAAT_DATASET_LIST)
1199 : {
1200 13527 : valueCount = static_cast<int>(
1201 13527 : Get<std::vector<GDALArgDatasetValue>>().size());
1202 : }
1203 :
1204 17357 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1205 : {
1206 14 : ReportError(CE_Failure, CPLE_AppDefined,
1207 : "%d value%s been specified for argument '%s', "
1208 : "whereas exactly %d %s expected.",
1209 : valueCount, valueCount > 1 ? "s have" : " has",
1210 7 : GetName().c_str(), GetMinCount(),
1211 7 : GetMinCount() > 1 ? "were" : "was");
1212 7 : ret = false;
1213 : }
1214 17350 : else if (valueCount < GetMinCount())
1215 : {
1216 6 : ReportError(CE_Failure, CPLE_AppDefined,
1217 : "Only %d value%s been specified for argument '%s', "
1218 : "whereas at least %d %s expected.",
1219 : valueCount, valueCount > 1 ? "s have" : " has",
1220 3 : GetName().c_str(), GetMinCount(),
1221 3 : GetMinCount() > 1 ? "were" : "was");
1222 3 : ret = false;
1223 : }
1224 17347 : else if (valueCount > GetMaxCount())
1225 : {
1226 2 : ReportError(CE_Failure, CPLE_AppDefined,
1227 : "%d value%s been specified for argument '%s', "
1228 : "whereas at most %d %s expected.",
1229 : valueCount, valueCount > 1 ? "s have" : " has",
1230 1 : GetName().c_str(), GetMaxCount(),
1231 1 : GetMaxCount() > 1 ? "were" : "was");
1232 1 : ret = false;
1233 : }
1234 : }
1235 :
1236 41076 : if (ret)
1237 : {
1238 49048 : for (const auto &f : m_validationActions)
1239 : {
1240 8041 : if (!f())
1241 96 : ret = false;
1242 : }
1243 : }
1244 :
1245 41076 : return ret;
1246 : }
1247 :
1248 : /************************************************************************/
1249 : /* GDALAlgorithmArg::ReportError() */
1250 : /************************************************************************/
1251 :
1252 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1253 : const char *fmt, ...) const
1254 : {
1255 : va_list args;
1256 11 : va_start(args, fmt);
1257 11 : if (m_owner)
1258 : {
1259 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1260 22 : CPLString().vPrintf(fmt, args).c_str());
1261 : }
1262 : else
1263 : {
1264 0 : CPLError(eErrClass, err_no, "%s",
1265 0 : CPLString().vPrintf(fmt, args).c_str());
1266 : }
1267 11 : va_end(args);
1268 11 : }
1269 :
1270 : /************************************************************************/
1271 : /* GDALAlgorithmArg::GetEscapedString() */
1272 : /************************************************************************/
1273 :
1274 : /* static */
1275 197 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1276 : {
1277 215 : if (s.find_first_of("\" \\,") != std::string::npos &&
1278 9 : !(s.size() > 4 &&
1279 9 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1280 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1281 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1282 : {
1283 14 : return std::string("\"")
1284 : .append(
1285 14 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1286 7 : .append("\"");
1287 : }
1288 : else
1289 : {
1290 190 : return s;
1291 : }
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* GDALAlgorithmArg::Serialize() */
1296 : /************************************************************************/
1297 :
1298 43 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1299 : bool absolutePath) const
1300 : {
1301 43 : serializedArg.clear();
1302 :
1303 43 : if (!IsExplicitlySet())
1304 : {
1305 0 : return false;
1306 : }
1307 :
1308 86 : std::string ret = "--";
1309 43 : ret += GetName();
1310 43 : if (GetType() == GAAT_BOOLEAN)
1311 : {
1312 0 : serializedArg = std::move(ret);
1313 0 : return true;
1314 : }
1315 :
1316 7 : const auto AddListValueSeparator = [this, &ret]()
1317 : {
1318 2 : if (GetPackedValuesAllowed())
1319 : {
1320 1 : ret += ',';
1321 : }
1322 : else
1323 : {
1324 1 : ret += " --";
1325 1 : ret += GetName();
1326 1 : ret += ' ';
1327 : }
1328 45 : };
1329 :
1330 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1331 : {
1332 : VSIStatBufL sStat;
1333 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1334 0 : !CPLIsFilenameRelative(filename.c_str()))
1335 0 : return filename;
1336 0 : char *pszCWD = CPLGetCurrentDir();
1337 0 : if (!pszCWD)
1338 0 : return filename;
1339 : const auto absPath =
1340 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1341 0 : CPLFree(pszCWD);
1342 0 : return absPath;
1343 : };
1344 :
1345 43 : ret += ' ';
1346 43 : switch (GetType())
1347 : {
1348 0 : case GAAT_BOOLEAN:
1349 0 : break;
1350 8 : case GAAT_STRING:
1351 : {
1352 8 : const auto &val = Get<std::string>();
1353 8 : ret += GetEscapedString(val);
1354 8 : break;
1355 : }
1356 1 : case GAAT_INTEGER:
1357 : {
1358 1 : ret += CPLSPrintf("%d", Get<int>());
1359 1 : break;
1360 : }
1361 0 : case GAAT_REAL:
1362 : {
1363 0 : ret += CPLSPrintf("%.17g", Get<double>());
1364 0 : break;
1365 : }
1366 2 : case GAAT_DATASET:
1367 : {
1368 2 : const auto &val = Get<GDALArgDatasetValue>();
1369 2 : const auto &str = val.GetName();
1370 2 : if (str.empty())
1371 : {
1372 0 : return false;
1373 : }
1374 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1375 2 : break;
1376 : }
1377 5 : case GAAT_STRING_LIST:
1378 : {
1379 5 : const auto &vals = Get<std::vector<std::string>>();
1380 11 : for (size_t i = 0; i < vals.size(); ++i)
1381 : {
1382 6 : if (i > 0)
1383 2 : AddListValueSeparator();
1384 6 : ret += GetEscapedString(vals[i]);
1385 : }
1386 5 : break;
1387 : }
1388 0 : case GAAT_INTEGER_LIST:
1389 : {
1390 0 : const auto &vals = Get<std::vector<int>>();
1391 0 : for (size_t i = 0; i < vals.size(); ++i)
1392 : {
1393 0 : if (i > 0)
1394 0 : AddListValueSeparator();
1395 0 : ret += CPLSPrintf("%d", vals[i]);
1396 : }
1397 0 : break;
1398 : }
1399 0 : case GAAT_REAL_LIST:
1400 : {
1401 0 : const auto &vals = Get<std::vector<double>>();
1402 0 : for (size_t i = 0; i < vals.size(); ++i)
1403 : {
1404 0 : if (i > 0)
1405 0 : AddListValueSeparator();
1406 0 : ret += CPLSPrintf("%.17g", vals[i]);
1407 : }
1408 0 : break;
1409 : }
1410 27 : case GAAT_DATASET_LIST:
1411 : {
1412 27 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1413 53 : for (size_t i = 0; i < vals.size(); ++i)
1414 : {
1415 27 : if (i > 0)
1416 0 : AddListValueSeparator();
1417 27 : const auto &val = vals[i];
1418 27 : const auto &str = val.GetName();
1419 27 : if (str.empty())
1420 : {
1421 1 : return false;
1422 : }
1423 52 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1424 26 : : str);
1425 : }
1426 26 : break;
1427 : }
1428 : }
1429 :
1430 42 : serializedArg = std::move(ret);
1431 42 : return true;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* ~GDALInConstructionAlgorithmArg() */
1436 : /************************************************************************/
1437 :
1438 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1439 :
1440 : /************************************************************************/
1441 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1442 : /************************************************************************/
1443 :
1444 : GDALInConstructionAlgorithmArg &
1445 70350 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1446 : {
1447 70350 : m_decl.AddAlias(alias);
1448 70350 : if (m_owner)
1449 70350 : m_owner->AddAliasFor(this, alias);
1450 70350 : return *this;
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1455 : /************************************************************************/
1456 :
1457 : GDALInConstructionAlgorithmArg &
1458 18894 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1459 : {
1460 18894 : m_decl.AddHiddenAlias(alias);
1461 18894 : if (m_owner)
1462 18894 : m_owner->AddAliasFor(this, alias);
1463 18894 : return *this;
1464 : }
1465 :
1466 : /************************************************************************/
1467 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1468 : /************************************************************************/
1469 :
1470 : GDALInConstructionAlgorithmArg &
1471 50 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1472 : {
1473 50 : m_decl.AddShortNameAlias(shortNameAlias);
1474 50 : if (m_owner)
1475 50 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1476 50 : return *this;
1477 : }
1478 :
1479 : /************************************************************************/
1480 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1481 : /************************************************************************/
1482 :
1483 24528 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1484 : {
1485 24528 : m_decl.SetPositional();
1486 24528 : if (m_owner)
1487 24528 : m_owner->SetPositional(this);
1488 24528 : return *this;
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1493 : /************************************************************************/
1494 :
1495 1530 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1496 3060 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1497 1530 : m_nameSet(true)
1498 : {
1499 1530 : if (m_poDS)
1500 1530 : m_poDS->Reference();
1501 1530 : }
1502 :
1503 : /************************************************************************/
1504 : /* GDALArgDatasetValue::Set() */
1505 : /************************************************************************/
1506 :
1507 2564 : void GDALArgDatasetValue::Set(const std::string &name)
1508 : {
1509 2564 : Close();
1510 2564 : m_name = name;
1511 2564 : m_nameSet = true;
1512 2564 : if (m_ownerArg)
1513 2558 : m_ownerArg->NotifyValueSet();
1514 2564 : }
1515 :
1516 : /************************************************************************/
1517 : /* GDALArgDatasetValue::Set() */
1518 : /************************************************************************/
1519 :
1520 2377 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1521 : {
1522 2377 : Close();
1523 2377 : m_poDS = poDS.release();
1524 2377 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1525 2377 : m_nameSet = true;
1526 2377 : if (m_ownerArg)
1527 2160 : m_ownerArg->NotifyValueSet();
1528 2377 : }
1529 :
1530 : /************************************************************************/
1531 : /* GDALArgDatasetValue::Set() */
1532 : /************************************************************************/
1533 :
1534 9460 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1535 : {
1536 9460 : Close();
1537 9460 : m_poDS = poDS;
1538 9460 : if (m_poDS)
1539 8445 : m_poDS->Reference();
1540 9460 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1541 9460 : m_nameSet = true;
1542 9460 : if (m_ownerArg)
1543 3765 : m_ownerArg->NotifyValueSet();
1544 9460 : }
1545 :
1546 : /************************************************************************/
1547 : /* GDALArgDatasetValue::SetFrom() */
1548 : /************************************************************************/
1549 :
1550 3963 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1551 : {
1552 3963 : Close();
1553 3963 : m_name = other.m_name;
1554 3963 : m_nameSet = other.m_nameSet;
1555 3963 : m_poDS = other.m_poDS;
1556 3963 : if (m_poDS)
1557 2799 : m_poDS->Reference();
1558 3963 : }
1559 :
1560 : /************************************************************************/
1561 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1562 : /************************************************************************/
1563 :
1564 36588 : GDALArgDatasetValue::~GDALArgDatasetValue()
1565 : {
1566 36588 : Close();
1567 36588 : }
1568 :
1569 : /************************************************************************/
1570 : /* GDALArgDatasetValue::Close() */
1571 : /************************************************************************/
1572 :
1573 61362 : bool GDALArgDatasetValue::Close()
1574 : {
1575 61362 : bool ret = true;
1576 61362 : if (m_poDS && m_poDS->Dereference() == 0)
1577 : {
1578 3899 : ret = m_poDS->Close() == CE_None;
1579 3899 : delete m_poDS;
1580 : }
1581 61362 : m_poDS = nullptr;
1582 61362 : return ret;
1583 : }
1584 :
1585 : /************************************************************************/
1586 : /* GDALArgDatasetValue::operator=() */
1587 : /************************************************************************/
1588 :
1589 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1590 : {
1591 2 : Close();
1592 2 : m_poDS = other.m_poDS;
1593 2 : m_name = other.m_name;
1594 2 : m_nameSet = other.m_nameSet;
1595 2 : other.m_poDS = nullptr;
1596 2 : other.m_name.clear();
1597 2 : other.m_nameSet = false;
1598 2 : return *this;
1599 : }
1600 :
1601 : /************************************************************************/
1602 : /* GDALArgDatasetValue::GetDataset() */
1603 : /************************************************************************/
1604 :
1605 1233 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1606 : {
1607 1233 : if (m_poDS)
1608 1228 : m_poDS->Reference();
1609 1233 : return m_poDS;
1610 : }
1611 :
1612 : /************************************************************************/
1613 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1614 : /************************************************************************/
1615 :
1616 3770 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1617 3770 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1618 : {
1619 3770 : other.m_poDS = nullptr;
1620 3770 : other.m_name.clear();
1621 3770 : }
1622 :
1623 : /************************************************************************/
1624 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1625 : /************************************************************************/
1626 :
1627 3818 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1628 : bool noneAllowed, const std::vector<std::string> &specialValues)
1629 : {
1630 3818 : if (GetType() != GAAT_STRING)
1631 : {
1632 1 : CPLError(CE_Failure, CPLE_AppDefined,
1633 : "SetIsCRSArg() can only be called on a String argument");
1634 1 : return *this;
1635 : }
1636 : AddValidationAction(
1637 921 : [this, noneAllowed, specialValues]()
1638 : {
1639 : const std::string &osVal =
1640 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1641 457 : ->Get<std::string>();
1642 457 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1643 0 : return true;
1644 :
1645 901 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1646 444 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1647 901 : specialValues.end())
1648 : {
1649 436 : OGRSpatialReference oSRS;
1650 436 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1651 : {
1652 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1653 : "Invalid value for '%s' argument",
1654 7 : GetName().c_str());
1655 7 : return false;
1656 : }
1657 : }
1658 450 : return true;
1659 3817 : });
1660 :
1661 : SetAutoCompleteFunction(
1662 44 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1663 : {
1664 11 : bool bIsRaster = false;
1665 11 : OGREnvelope sDatasetLongLatEnv;
1666 22 : std::string osCelestialBodyName;
1667 11 : if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1668 : {
1669 11 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1670 11 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1671 : {
1672 : auto &val =
1673 11 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1674 11 : if (val.size() == 1)
1675 : {
1676 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1677 : auto poDS = std::unique_ptr<GDALDataset>(
1678 4 : GDALDataset::Open(val[0].GetName().c_str()));
1679 2 : if (poDS)
1680 : {
1681 2 : bIsRaster = poDS->GetRasterCount() != 0;
1682 2 : if (auto poCRS = poDS->GetSpatialRef())
1683 : {
1684 : const char *pszCelestialBodyName =
1685 2 : poCRS->GetCelestialBodyName();
1686 2 : if (pszCelestialBodyName)
1687 2 : osCelestialBodyName = pszCelestialBodyName;
1688 :
1689 2 : if (!pszCelestialBodyName ||
1690 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1691 : {
1692 0 : OGRSpatialReference oLongLat;
1693 0 : oLongLat.CopyGeogCSFrom(poCRS);
1694 0 : oLongLat.SetAxisMappingStrategy(
1695 : OAMS_TRADITIONAL_GIS_ORDER);
1696 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1697 0 : &oLongLat);
1698 : }
1699 : else
1700 : {
1701 2 : poDS->GetExtentWGS84LongLat(
1702 2 : &sDatasetLongLatEnv);
1703 : }
1704 : }
1705 : }
1706 : }
1707 : }
1708 : }
1709 :
1710 : const auto IsCRSCompatible =
1711 42959 : [bIsRaster, &sDatasetLongLatEnv,
1712 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1713 : {
1714 42959 : if (!sDatasetLongLatEnv.IsInit())
1715 30685 : return true;
1716 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1717 11834 : !(bIsRaster &&
1718 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1719 11652 : crsInfo->dfWestLongitudeDeg <
1720 11652 : crsInfo->dfEastLongitudeDeg &&
1721 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1722 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1723 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1724 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1725 329 : ((!osCelestialBodyName.empty() &&
1726 658 : crsInfo->pszCelestialBodyName &&
1727 329 : osCelestialBodyName ==
1728 329 : crsInfo->pszCelestialBodyName) ||
1729 0 : (osCelestialBodyName.empty() &&
1730 12274 : !crsInfo->pszCelestialBodyName));
1731 11 : };
1732 :
1733 11 : std::vector<std::string> oRet;
1734 11 : if (noneAllowed)
1735 0 : oRet.push_back("none");
1736 11 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1737 11 : if (!currentValue.empty())
1738 : {
1739 : const CPLStringList aosTokens(
1740 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1741 7 : int nCount = 0;
1742 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1743 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1744 : nullptr, &nCount),
1745 14 : OSRDestroyCRSInfoList);
1746 14 : std::string osCode;
1747 :
1748 14 : std::vector<const OSRCRSInfo *> candidates;
1749 46270 : for (int i = 0; i < nCount; ++i)
1750 : {
1751 46263 : const auto *entry = (pCRSList.get())[i];
1752 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1753 : {
1754 49425 : if (aosTokens.size() == 1 ||
1755 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1756 : {
1757 12666 : if (candidates.empty())
1758 7 : osCode = entry->pszCode;
1759 12666 : candidates.push_back(entry);
1760 : }
1761 : }
1762 : }
1763 7 : if (candidates.size() == 1)
1764 : {
1765 1 : oRet.push_back(std::move(osCode));
1766 : }
1767 : else
1768 : {
1769 6 : if (sDatasetLongLatEnv.IsInit())
1770 : {
1771 2 : std::sort(
1772 : candidates.begin(), candidates.end(),
1773 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1774 : {
1775 2999 : const double dfXa =
1776 2999 : a->dfWestLongitudeDeg >
1777 2999 : a->dfEastLongitudeDeg
1778 2999 : ? a->dfWestLongitudeDeg -
1779 0 : a->dfEastLongitudeDeg
1780 2999 : : (180 - a->dfWestLongitudeDeg) +
1781 2999 : (a->dfEastLongitudeDeg - -180);
1782 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1783 2999 : a->dfSouthLatitudeDeg;
1784 2999 : const double dfXb =
1785 2999 : b->dfWestLongitudeDeg >
1786 2999 : b->dfEastLongitudeDeg
1787 2999 : ? b->dfWestLongitudeDeg -
1788 0 : b->dfEastLongitudeDeg
1789 2999 : : (180 - b->dfWestLongitudeDeg) +
1790 2999 : (b->dfEastLongitudeDeg - -180);
1791 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1792 2999 : b->dfSouthLatitudeDeg;
1793 2999 : const double diffArea =
1794 2999 : dfXa * dfYa - dfXb * dfYb;
1795 2999 : if (diffArea < 0)
1796 279 : return true;
1797 2720 : if (diffArea == 0)
1798 : {
1799 2506 : if (std::string_view(a->pszName) ==
1800 2506 : b->pszName)
1801 : {
1802 57 : if (a->eType ==
1803 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1804 13 : b->eType !=
1805 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1806 13 : return true;
1807 44 : if (a->eType ==
1808 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1809 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1810 9 : return true;
1811 35 : return false;
1812 : }
1813 4898 : return std::string_view(a->pszCode) <
1814 4898 : b->pszCode;
1815 : }
1816 214 : return false;
1817 : });
1818 : }
1819 :
1820 12671 : for (const auto *entry : candidates)
1821 : {
1822 25330 : std::string val = std::string(entry->pszCode)
1823 12665 : .append(" -- ")
1824 25330 : .append(entry->pszName);
1825 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1826 1294 : val.append(" (geographic 2D)");
1827 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1828 446 : val.append(" (geographic 3D)");
1829 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1830 397 : val.append(" (geocentric)");
1831 12665 : oRet.push_back(std::move(val));
1832 : }
1833 : }
1834 : }
1835 11 : if (currentValue.empty() || oRet.empty())
1836 : {
1837 : const CPLStringList aosAuthorities(
1838 8 : OSRGetAuthorityListFromDatabase());
1839 24 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1840 : {
1841 20 : int nCount = 0;
1842 20 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1843 : pszAuth, nullptr, &nCount));
1844 20 : if (nCount)
1845 16 : oRet.push_back(std::string(pszAuth).append(":"));
1846 : }
1847 : }
1848 22 : return oRet;
1849 3817 : });
1850 :
1851 3817 : return *this;
1852 : }
1853 :
1854 : /************************************************************************/
1855 : /* GDALAlgorithm::GDALAlgorithm() */
1856 : /************************************************************************/
1857 :
1858 25157 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1859 : const std::string &description,
1860 25157 : const std::string &helpURL)
1861 : : m_name(name), m_description(description), m_helpURL(helpURL),
1862 49724 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1863 25157 : ? "https://gdal.org" + m_helpURL
1864 74622 : : m_helpURL)
1865 : {
1866 : auto &helpArg =
1867 : AddArg("help", 'h', _("Display help message and exit"),
1868 50314 : &m_helpRequested)
1869 25157 : .SetHiddenForAPI()
1870 50314 : .SetCategory(GAAC_COMMON)
1871 14 : .AddAction([this]()
1872 25157 : { m_specialActionRequested = m_calledFromCommandLine; });
1873 : auto &helpDocArg =
1874 : AddArg("help-doc", 0,
1875 : _("Display help message for use by documentation"),
1876 50314 : &m_helpDocRequested)
1877 25157 : .SetHidden()
1878 16 : .AddAction([this]()
1879 25157 : { m_specialActionRequested = m_calledFromCommandLine; });
1880 : auto &jsonUsageArg =
1881 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1882 50314 : &m_JSONUsageRequested)
1883 25157 : .SetHiddenForAPI()
1884 50314 : .SetCategory(GAAC_COMMON)
1885 4 : .AddAction([this]()
1886 25157 : { m_specialActionRequested = m_calledFromCommandLine; });
1887 50314 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1888 50314 : .SetMetaVar("<KEY>=<VALUE>")
1889 25157 : .SetHiddenForAPI()
1890 50314 : .SetCategory(GAAC_COMMON)
1891 : .AddAction(
1892 2 : [this]()
1893 : {
1894 2 : ReportError(
1895 : CE_Warning, CPLE_AppDefined,
1896 : "Configuration options passed with the 'config' argument "
1897 : "are ignored");
1898 25157 : });
1899 :
1900 25157 : AddValidationAction(
1901 15856 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1902 : {
1903 8152 : if (!m_calledFromCommandLine && m_specialActionRequested)
1904 : {
1905 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1906 : {
1907 0 : if (arg->IsExplicitlySet())
1908 : {
1909 0 : ReportError(CE_Failure, CPLE_AppDefined,
1910 : "'%s' argument only available when called "
1911 : "from command line",
1912 0 : arg->GetName().c_str());
1913 0 : return false;
1914 : }
1915 : }
1916 : }
1917 8152 : return true;
1918 : });
1919 25157 : }
1920 :
1921 : /************************************************************************/
1922 : /* GDALAlgorithm::~GDALAlgorithm() */
1923 : /************************************************************************/
1924 :
1925 : GDALAlgorithm::~GDALAlgorithm() = default;
1926 :
1927 : /************************************************************************/
1928 : /* GDALAlgorithm::ParseArgument() */
1929 : /************************************************************************/
1930 :
1931 3473 : bool GDALAlgorithm::ParseArgument(
1932 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1933 : std::map<
1934 : GDALAlgorithmArg *,
1935 : std::variant<std::vector<std::string>, std::vector<int>,
1936 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1937 : &inConstructionValues)
1938 : {
1939 : const bool isListArg =
1940 3473 : GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1941 3473 : if (arg->IsExplicitlySet() && !isListArg)
1942 : {
1943 : // Hack for "gdal info" to be able to pass an opened raster dataset
1944 : // by "gdal raster info" to the "gdal vector info" algorithm.
1945 4 : if (arg->SkipIfAlreadySet())
1946 : {
1947 1 : arg->SetSkipIfAlreadySet(false);
1948 1 : return true;
1949 : }
1950 :
1951 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1952 : "Argument '%s' has already been specified.", name.c_str());
1953 3 : return false;
1954 : }
1955 :
1956 3543 : if (!arg->GetRepeatedArgAllowed() &&
1957 74 : cpl::contains(inConstructionValues, arg))
1958 : {
1959 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1960 : "Argument '%s' has already been specified.", name.c_str());
1961 1 : return false;
1962 : }
1963 :
1964 3468 : switch (arg->GetType())
1965 : {
1966 336 : case GAAT_BOOLEAN:
1967 : {
1968 336 : if (value.empty() || value == "true")
1969 334 : return arg->Set(true);
1970 2 : else if (value == "false")
1971 1 : return arg->Set(false);
1972 : else
1973 : {
1974 1 : ReportError(
1975 : CE_Failure, CPLE_IllegalArg,
1976 : "Invalid value '%s' for boolean argument '%s'. Should be "
1977 : "'true' or 'false'.",
1978 : value.c_str(), name.c_str());
1979 1 : return false;
1980 : }
1981 : }
1982 :
1983 872 : case GAAT_STRING:
1984 : {
1985 872 : return arg->Set(value);
1986 : }
1987 :
1988 359 : case GAAT_INTEGER:
1989 : {
1990 359 : errno = 0;
1991 359 : char *endptr = nullptr;
1992 359 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1993 358 : if (errno == 0 && endptr &&
1994 717 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1995 : val <= INT_MAX)
1996 : {
1997 356 : return arg->Set(static_cast<int>(val));
1998 : }
1999 : else
2000 : {
2001 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2002 : "Expected integer value for argument '%s', "
2003 : "but got '%s'.",
2004 : name.c_str(), value.c_str());
2005 3 : return false;
2006 : }
2007 : }
2008 :
2009 36 : case GAAT_REAL:
2010 : {
2011 36 : char *endptr = nullptr;
2012 36 : double dfValue = CPLStrtod(value.c_str(), &endptr);
2013 36 : if (endptr != value.c_str() + value.size())
2014 : {
2015 1 : ReportError(
2016 : CE_Failure, CPLE_IllegalArg,
2017 : "Expected real value for argument '%s', but got '%s'.",
2018 : name.c_str(), value.c_str());
2019 1 : return false;
2020 : }
2021 35 : return arg->Set(dfValue);
2022 : }
2023 :
2024 619 : case GAAT_DATASET:
2025 : {
2026 619 : return arg->SetDatasetName(value);
2027 : }
2028 :
2029 275 : case GAAT_STRING_LIST:
2030 : {
2031 : const CPLStringList aosTokens(
2032 275 : arg->GetPackedValuesAllowed()
2033 184 : ? CSLTokenizeString2(value.c_str(), ",",
2034 : CSLT_HONOURSTRINGS |
2035 : CSLT_PRESERVEQUOTES)
2036 459 : : CSLAddString(nullptr, value.c_str()));
2037 275 : if (!cpl::contains(inConstructionValues, arg))
2038 : {
2039 250 : inConstructionValues[arg] = std::vector<std::string>();
2040 : }
2041 : auto &valueVector =
2042 275 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2043 583 : for (const char *v : aosTokens)
2044 : {
2045 308 : valueVector.push_back(v);
2046 : }
2047 275 : if (arg->GetMaxCount() == 1)
2048 : {
2049 4 : bool ret = arg->Set(std::move(valueVector));
2050 4 : inConstructionValues.erase(inConstructionValues.find(arg));
2051 4 : return ret;
2052 : }
2053 :
2054 271 : break;
2055 : }
2056 :
2057 63 : case GAAT_INTEGER_LIST:
2058 : {
2059 : const CPLStringList aosTokens(
2060 63 : arg->GetPackedValuesAllowed()
2061 63 : ? CSLTokenizeString2(
2062 : value.c_str(), ",",
2063 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2064 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2065 126 : : CSLAddString(nullptr, value.c_str()));
2066 63 : if (!cpl::contains(inConstructionValues, arg))
2067 : {
2068 59 : inConstructionValues[arg] = std::vector<int>();
2069 : }
2070 : auto &valueVector =
2071 63 : std::get<std::vector<int>>(inConstructionValues[arg]);
2072 191 : for (const char *v : aosTokens)
2073 : {
2074 134 : errno = 0;
2075 134 : char *endptr = nullptr;
2076 134 : const auto val = std::strtol(v, &endptr, 10);
2077 134 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2078 130 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2079 : {
2080 128 : valueVector.push_back(static_cast<int>(val));
2081 : }
2082 : else
2083 : {
2084 6 : ReportError(
2085 : CE_Failure, CPLE_IllegalArg,
2086 : "Expected list of integer value for argument '%s', "
2087 : "but got '%s'.",
2088 : name.c_str(), value.c_str());
2089 6 : return false;
2090 : }
2091 : }
2092 57 : if (arg->GetMaxCount() == 1)
2093 : {
2094 2 : bool ret = arg->Set(std::move(valueVector));
2095 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2096 2 : return ret;
2097 : }
2098 :
2099 55 : break;
2100 : }
2101 :
2102 109 : case GAAT_REAL_LIST:
2103 : {
2104 : const CPLStringList aosTokens(
2105 109 : arg->GetPackedValuesAllowed()
2106 109 : ? CSLTokenizeString2(
2107 : value.c_str(), ",",
2108 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2109 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2110 218 : : CSLAddString(nullptr, value.c_str()));
2111 109 : if (!cpl::contains(inConstructionValues, arg))
2112 : {
2113 107 : inConstructionValues[arg] = std::vector<double>();
2114 : }
2115 : auto &valueVector =
2116 109 : std::get<std::vector<double>>(inConstructionValues[arg]);
2117 442 : for (const char *v : aosTokens)
2118 : {
2119 337 : char *endptr = nullptr;
2120 337 : double dfValue = CPLStrtod(v, &endptr);
2121 337 : if (strlen(v) == 0 || endptr != v + strlen(v))
2122 : {
2123 4 : ReportError(
2124 : CE_Failure, CPLE_IllegalArg,
2125 : "Expected list of real value for argument '%s', "
2126 : "but got '%s'.",
2127 : name.c_str(), value.c_str());
2128 4 : return false;
2129 : }
2130 333 : valueVector.push_back(dfValue);
2131 : }
2132 105 : if (arg->GetMaxCount() == 1)
2133 : {
2134 2 : bool ret = arg->Set(std::move(valueVector));
2135 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2136 2 : return ret;
2137 : }
2138 :
2139 103 : break;
2140 : }
2141 :
2142 799 : case GAAT_DATASET_LIST:
2143 : {
2144 799 : if (!cpl::contains(inConstructionValues, arg))
2145 : {
2146 790 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2147 : }
2148 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2149 799 : inConstructionValues[arg]);
2150 799 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2151 : {
2152 12 : valueVector.push_back(GDALArgDatasetValue(value));
2153 : }
2154 : else
2155 : {
2156 : const CPLStringList aosTokens(
2157 787 : arg->GetPackedValuesAllowed()
2158 6 : ? CSLTokenizeString2(value.c_str(), ",",
2159 : CSLT_HONOURSTRINGS |
2160 : CSLT_STRIPLEADSPACES)
2161 1580 : : CSLAddString(nullptr, value.c_str()));
2162 1577 : for (const char *v : aosTokens)
2163 : {
2164 790 : valueVector.push_back(GDALArgDatasetValue(v));
2165 : }
2166 : }
2167 799 : if (arg->GetMaxCount() == 1)
2168 : {
2169 689 : bool ret = arg->Set(std::move(valueVector));
2170 689 : inConstructionValues.erase(inConstructionValues.find(arg));
2171 689 : return ret;
2172 : }
2173 :
2174 110 : break;
2175 : }
2176 : }
2177 :
2178 539 : return true;
2179 : }
2180 :
2181 : /************************************************************************/
2182 : /* FormatSuggestionsAsString() */
2183 : /************************************************************************/
2184 :
2185 : static std::string
2186 6 : FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
2187 : bool addDashDashPrefix)
2188 : {
2189 6 : std::string ret;
2190 14 : for (auto [i, suggestion] : cpl::enumerate(suggestions))
2191 : {
2192 8 : if (i > 0)
2193 : {
2194 2 : ret += (i + 1 < suggestions.size()) ? ", " : " or ";
2195 : }
2196 8 : ret += '\'';
2197 8 : if (addDashDashPrefix)
2198 6 : ret += "--";
2199 8 : ret += suggestion;
2200 8 : ret += '\'';
2201 : }
2202 6 : return ret;
2203 : }
2204 :
2205 : /************************************************************************/
2206 : /* GDALAlgorithm::ParseCommandLineArguments() */
2207 : /************************************************************************/
2208 :
2209 2375 : bool GDALAlgorithm::ParseCommandLineArguments(
2210 : const std::vector<std::string> &args)
2211 : {
2212 2375 : if (m_parsedSubStringAlreadyCalled)
2213 : {
2214 6 : ReportError(CE_Failure, CPLE_AppDefined,
2215 : "ParseCommandLineArguments() can only be called once per "
2216 : "instance.");
2217 6 : return false;
2218 : }
2219 2369 : m_parsedSubStringAlreadyCalled = true;
2220 :
2221 : // AWS like syntax supported too (not advertized)
2222 2369 : if (args.size() == 1 && args[0] == "help")
2223 : {
2224 1 : auto arg = GetArg("help");
2225 1 : assert(arg);
2226 1 : arg->Set(true);
2227 1 : arg->RunActions();
2228 1 : return true;
2229 : }
2230 :
2231 2368 : if (HasSubAlgorithms())
2232 : {
2233 495 : if (args.empty())
2234 : {
2235 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2236 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2237 2 : return false;
2238 : }
2239 493 : if (!args[0].empty() && args[0][0] == '-')
2240 : {
2241 : // go on argument parsing
2242 : }
2243 : else
2244 : {
2245 490 : const auto nCounter = CPLGetErrorCounter();
2246 490 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2247 490 : if (m_selectedSubAlgHolder)
2248 : {
2249 487 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2250 487 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2251 487 : m_referencePath);
2252 487 : m_selectedSubAlg->m_executionForStreamOutput =
2253 487 : m_executionForStreamOutput;
2254 487 : m_selectedSubAlg->m_calledFromCommandLine =
2255 487 : m_calledFromCommandLine;
2256 487 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2257 487 : m_skipValidationInParseCommandLine;
2258 487 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2259 974 : std::vector<std::string>(args.begin() + 1, args.end()));
2260 487 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2261 487 : return bRet;
2262 : }
2263 : else
2264 : {
2265 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2266 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2267 : {
2268 2 : ReportError(CE_Failure, CPLE_AppDefined,
2269 2 : "Unknown command: '%s'", args[0].c_str());
2270 : }
2271 3 : return false;
2272 : }
2273 : }
2274 : }
2275 :
2276 : std::map<
2277 : GDALAlgorithmArg *,
2278 : std::variant<std::vector<std::string>, std::vector<int>,
2279 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2280 3752 : inConstructionValues;
2281 :
2282 2245 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2283 : {
2284 2210 : for (auto &[arg, value] : inConstructionValues)
2285 : {
2286 497 : if (arg->GetType() == GAAT_STRING_LIST)
2287 : {
2288 243 : if (!arg->Set(std::get<std::vector<std::string>>(
2289 243 : inConstructionValues[arg])))
2290 : {
2291 35 : return false;
2292 : }
2293 : }
2294 254 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2295 : {
2296 52 : if (!arg->Set(
2297 52 : std::get<std::vector<int>>(inConstructionValues[arg])))
2298 : {
2299 4 : return false;
2300 : }
2301 : }
2302 202 : else if (arg->GetType() == GAAT_REAL_LIST)
2303 : {
2304 101 : if (!arg->Set(std::get<std::vector<double>>(
2305 101 : inConstructionValues[arg])))
2306 : {
2307 10 : return false;
2308 : }
2309 : }
2310 101 : else if (arg->GetType() == GAAT_DATASET_LIST)
2311 : {
2312 101 : if (!arg->Set(
2313 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2314 101 : inConstructionValues[arg]))))
2315 : {
2316 2 : return false;
2317 : }
2318 : }
2319 : }
2320 1713 : return true;
2321 1876 : };
2322 :
2323 3752 : std::vector<std::string> lArgs(args);
2324 1876 : bool helpValueRequested = false;
2325 5352 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2326 : {
2327 3589 : const auto &strArg = lArgs[i];
2328 3589 : GDALAlgorithmArg *arg = nullptr;
2329 3589 : std::string name;
2330 3589 : std::string value;
2331 3589 : bool hasValue = false;
2332 3589 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2333 5 : helpValueRequested = true;
2334 3589 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2335 : {
2336 2210 : const auto equalPos = strArg.find('=');
2337 4420 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2338 2210 : : strArg;
2339 2210 : const std::string nameWithoutDash = name.substr(2);
2340 2210 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2341 2266 : if (m_arbitraryLongNameArgsAllowed &&
2342 2266 : iterArg == m_mapLongNameToArg.end())
2343 : {
2344 17 : GetArg(nameWithoutDash);
2345 17 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2346 : }
2347 2210 : if (iterArg == m_mapLongNameToArg.end())
2348 : {
2349 : const auto suggestions =
2350 28 : GetSuggestionsForArgumentName(nameWithoutDash);
2351 28 : if (!suggestions.empty())
2352 : {
2353 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2354 : "Option '%s' is unknown. Do you mean %s?",
2355 : name.c_str(),
2356 6 : FormatSuggestionsAsString(
2357 : suggestions, /* addDashDashPrefix = */ true)
2358 : .c_str());
2359 : }
2360 : else
2361 : {
2362 25 : ReportError(CE_Failure, CPLE_IllegalArg,
2363 : "Option '%s' is unknown.", name.c_str());
2364 : }
2365 28 : return false;
2366 : }
2367 2182 : arg = iterArg->second;
2368 2182 : if (equalPos != std::string::npos)
2369 : {
2370 472 : hasValue = true;
2371 472 : value = strArg.substr(equalPos + 1);
2372 : }
2373 : }
2374 1458 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2375 79 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2376 : {
2377 153 : for (size_t j = 1; j < strArg.size(); ++j)
2378 : {
2379 79 : name.clear();
2380 79 : name += strArg[j];
2381 79 : const auto iterArg = m_mapShortNameToArg.find(name);
2382 79 : if (iterArg == m_mapShortNameToArg.end())
2383 : {
2384 5 : const std::string nameWithoutDash = strArg.substr(1);
2385 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2386 10 : m_mapLongNameToArg.end())
2387 : {
2388 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2389 : "Short name option '%s' is unknown. Do you "
2390 : "mean '--%s' (with leading double dash) ?",
2391 : name.c_str(), nameWithoutDash.c_str());
2392 : }
2393 : else
2394 : {
2395 : const auto suggestions =
2396 8 : GetSuggestionsForArgumentName(nameWithoutDash);
2397 4 : if (!suggestions.empty())
2398 : {
2399 1 : ReportError(
2400 : CE_Failure, CPLE_IllegalArg,
2401 : "Short name option '%s' is unknown. Do you "
2402 : "mean %s (with leading double dash) ?",
2403 : name.c_str(),
2404 2 : FormatSuggestionsAsString(
2405 : suggestions, /* addDashDashPrefix = */ true)
2406 : .c_str());
2407 : }
2408 : else
2409 : {
2410 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2411 : "Short name option '%s' is unknown.",
2412 : name.c_str());
2413 : }
2414 : }
2415 5 : return false;
2416 : }
2417 74 : arg = iterArg->second;
2418 74 : if (strArg.size() > 2)
2419 : {
2420 0 : if (arg->GetType() != GAAT_BOOLEAN)
2421 : {
2422 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2423 : "Invalid argument '%s'. Option '%s' is not "
2424 : "a boolean option.",
2425 : strArg.c_str(), name.c_str());
2426 0 : return false;
2427 : }
2428 :
2429 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2430 0 : return false;
2431 : }
2432 : }
2433 74 : if (strArg.size() > 2)
2434 : {
2435 0 : lArgs.erase(lArgs.begin() + i);
2436 0 : continue;
2437 : }
2438 : }
2439 : else
2440 : {
2441 1300 : ++i;
2442 1300 : continue;
2443 : }
2444 2256 : CPLAssert(arg);
2445 :
2446 2256 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2447 : {
2448 337 : if (!hasValue)
2449 : {
2450 334 : hasValue = true;
2451 334 : value = "true";
2452 : }
2453 : }
2454 :
2455 2256 : lArgs.erase(lArgs.begin() + i);
2456 :
2457 2256 : if (!hasValue)
2458 : {
2459 1450 : if (i == lArgs.size())
2460 : {
2461 41 : if (m_parseForAutoCompletion)
2462 : {
2463 35 : break;
2464 : }
2465 6 : ReportError(
2466 : CE_Failure, CPLE_IllegalArg,
2467 : "Expected value for argument '%s', but ran short of tokens",
2468 : name.c_str());
2469 6 : return false;
2470 : }
2471 1409 : value = lArgs[i];
2472 1409 : lArgs.erase(lArgs.begin() + i);
2473 : }
2474 :
2475 2215 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2476 : {
2477 39 : return false;
2478 : }
2479 :
2480 : // Consume next strings if it is a positional argument, until finding
2481 : // a value starting with dash.
2482 2557 : if (!hasValue && arg && GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2483 381 : std::find(m_positionalArgs.begin(), m_positionalArgs.end(), arg) !=
2484 2557 : m_positionalArgs.end())
2485 : {
2486 113 : int countVals = 1;
2487 114 : while (i < lArgs.size() && !lArgs[i].empty() && lArgs[i][0] != '-')
2488 : {
2489 5 : if (countVals == arg->GetMaxCount())
2490 4 : break;
2491 1 : if (!ParseArgument(arg, name, lArgs[i], inConstructionValues))
2492 : {
2493 0 : ProcessInConstructionValues();
2494 0 : return false;
2495 : }
2496 1 : lArgs.erase(lArgs.begin() + i);
2497 1 : ++countVals;
2498 : }
2499 : }
2500 : }
2501 :
2502 1798 : if (m_specialActionRequested)
2503 : {
2504 26 : return true;
2505 : }
2506 :
2507 : // Process positional arguments that have not been set through their
2508 : // option name.
2509 1772 : size_t i = 0;
2510 1772 : size_t iCurPosArg = 0;
2511 :
2512 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2513 1804 : if (m_positionalArgs.size() == 3 &&
2514 33 : (m_positionalArgs[0]->IsRequired() ||
2515 32 : m_positionalArgs[0]->GetMinCount() == 1) &&
2516 62 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2517 38 : (m_positionalArgs[1]->IsRequired() ||
2518 38 : m_positionalArgs[1]->GetMinCount() == 1) &&
2519 : /* Second argument may have several occurrences */
2520 62 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2521 49 : (m_positionalArgs[2]->IsRequired() ||
2522 31 : m_positionalArgs[2]->GetMinCount() == 1) &&
2523 13 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2524 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2525 1813 : !m_positionalArgs[1]->IsExplicitlySet() &&
2526 9 : !m_positionalArgs[2]->IsExplicitlySet())
2527 : {
2528 7 : if (lArgs.size() - i < 3)
2529 : {
2530 1 : ReportError(CE_Failure, CPLE_AppDefined,
2531 : "Not enough positional values.");
2532 1 : return false;
2533 : }
2534 12 : bool ok = ParseArgument(m_positionalArgs[0],
2535 6 : m_positionalArgs[0]->GetName().c_str(),
2536 6 : lArgs[i], inConstructionValues);
2537 6 : if (ok)
2538 : {
2539 5 : ++i;
2540 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2541 : {
2542 12 : ok = ParseArgument(m_positionalArgs[1],
2543 6 : m_positionalArgs[1]->GetName().c_str(),
2544 6 : lArgs[i], inConstructionValues);
2545 : }
2546 : }
2547 6 : if (ok)
2548 : {
2549 10 : ok = ParseArgument(m_positionalArgs[2],
2550 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2551 : inConstructionValues);
2552 5 : ++i;
2553 : }
2554 6 : if (!ok)
2555 : {
2556 3 : ProcessInConstructionValues();
2557 3 : return false;
2558 : }
2559 : }
2560 :
2561 592 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2562 660 : !m_positionalArgs[0]->IsExplicitlySet() &&
2563 2694 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2564 78 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2565 39 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2566 : {
2567 39 : ++iCurPosArg;
2568 : }
2569 :
2570 2987 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2571 : {
2572 1226 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2573 1237 : while (arg->IsExplicitlySet())
2574 : {
2575 12 : ++iCurPosArg;
2576 12 : if (iCurPosArg == m_positionalArgs.size())
2577 1 : break;
2578 11 : arg = m_positionalArgs[iCurPosArg];
2579 : }
2580 1226 : if (iCurPosArg == m_positionalArgs.size())
2581 : {
2582 1 : break;
2583 : }
2584 1914 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2585 689 : arg->GetMinCount() != arg->GetMaxCount())
2586 : {
2587 103 : if (iCurPosArg == 0)
2588 : {
2589 81 : size_t nCountAtEnd = 0;
2590 110 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2591 : {
2592 31 : const auto *otherArg = m_positionalArgs[j];
2593 31 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2594 : {
2595 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2596 : {
2597 2 : ReportError(
2598 : CE_Failure, CPLE_AppDefined,
2599 : "Ambiguity in definition of positional "
2600 : "argument "
2601 : "'%s' given it has a varying number of values, "
2602 : "but follows argument '%s' which also has a "
2603 : "varying number of values",
2604 1 : otherArg->GetName().c_str(),
2605 1 : arg->GetName().c_str());
2606 1 : ProcessInConstructionValues();
2607 1 : return false;
2608 : }
2609 3 : nCountAtEnd += otherArg->GetMinCount();
2610 : }
2611 : else
2612 : {
2613 27 : if (!otherArg->IsRequired())
2614 : {
2615 2 : ReportError(
2616 : CE_Failure, CPLE_AppDefined,
2617 : "Ambiguity in definition of positional "
2618 : "argument "
2619 : "'%s', given it is not required but follows "
2620 : "argument '%s' which has a varying number of "
2621 : "values",
2622 1 : otherArg->GetName().c_str(),
2623 1 : arg->GetName().c_str());
2624 1 : ProcessInConstructionValues();
2625 1 : return false;
2626 : }
2627 26 : nCountAtEnd++;
2628 : }
2629 : }
2630 79 : if (lArgs.size() < nCountAtEnd)
2631 : {
2632 1 : ReportError(CE_Failure, CPLE_AppDefined,
2633 : "Not enough positional values.");
2634 1 : ProcessInConstructionValues();
2635 1 : return false;
2636 : }
2637 165 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2638 : {
2639 87 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2640 : inConstructionValues))
2641 : {
2642 0 : ProcessInConstructionValues();
2643 0 : return false;
2644 : }
2645 : }
2646 : }
2647 22 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2648 : {
2649 49 : for (; i < lArgs.size(); ++i)
2650 : {
2651 28 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2652 : inConstructionValues))
2653 : {
2654 0 : ProcessInConstructionValues();
2655 0 : return false;
2656 : }
2657 : }
2658 : }
2659 : else
2660 : {
2661 1 : ReportError(CE_Failure, CPLE_AppDefined,
2662 : "Ambiguity in definition of positional arguments: "
2663 : "arguments with varying number of values must be "
2664 : "first or last one.");
2665 1 : return false;
2666 : }
2667 : }
2668 : else
2669 : {
2670 1122 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2671 : {
2672 1 : ReportError(CE_Failure, CPLE_AppDefined,
2673 : "Not enough positional values.");
2674 1 : return false;
2675 : }
2676 1121 : const size_t iMax = i + arg->GetMaxCount();
2677 2245 : for (; i < iMax; ++i)
2678 : {
2679 1125 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2680 : inConstructionValues))
2681 : {
2682 1 : ProcessInConstructionValues();
2683 1 : return false;
2684 : }
2685 : }
2686 : }
2687 1219 : ++iCurPosArg;
2688 : }
2689 :
2690 1762 : if (i < lArgs.size())
2691 : {
2692 21 : ReportError(CE_Failure, CPLE_AppDefined,
2693 : "Positional values starting at '%s' are not expected.",
2694 21 : lArgs[i].c_str());
2695 21 : return false;
2696 : }
2697 :
2698 1741 : if (!ProcessInConstructionValues())
2699 : {
2700 34 : return false;
2701 : }
2702 :
2703 : // Skip to first unset positional argument.
2704 2807 : while (iCurPosArg < m_positionalArgs.size() &&
2705 604 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2706 : {
2707 496 : ++iCurPosArg;
2708 : }
2709 : // Check if this positional argument is required.
2710 1814 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2711 107 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2712 59 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2713 48 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2714 : {
2715 87 : ReportError(CE_Failure, CPLE_AppDefined,
2716 : "Positional arguments starting at '%s' have not been "
2717 : "specified.",
2718 87 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2719 87 : return false;
2720 : }
2721 :
2722 1620 : if (m_calledFromCommandLine)
2723 : {
2724 5522 : for (auto &arg : m_args)
2725 : {
2726 7138 : if (arg->IsExplicitlySet() &&
2727 1105 : ((arg->GetType() == GAAT_STRING &&
2728 1102 : arg->Get<std::string>() == "?") ||
2729 999 : (arg->GetType() == GAAT_STRING_LIST &&
2730 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2731 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2732 : {
2733 : {
2734 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2735 5 : ValidateArguments();
2736 : }
2737 :
2738 5 : auto choices = arg->GetChoices();
2739 5 : if (choices.empty())
2740 2 : choices = arg->GetAutoCompleteChoices(std::string());
2741 5 : if (!choices.empty())
2742 : {
2743 5 : if (choices.size() == 1)
2744 : {
2745 4 : ReportError(
2746 : CE_Failure, CPLE_AppDefined,
2747 : "Single potential value for argument '%s' is '%s'",
2748 4 : arg->GetName().c_str(), choices.front().c_str());
2749 : }
2750 : else
2751 : {
2752 6 : std::string msg("Potential values for argument '");
2753 3 : msg += arg->GetName();
2754 3 : msg += "' are:";
2755 45 : for (const auto &v : choices)
2756 : {
2757 42 : msg += "\n- ";
2758 42 : msg += v;
2759 : }
2760 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2761 : msg.c_str());
2762 : }
2763 5 : return false;
2764 : }
2765 : }
2766 : }
2767 : }
2768 :
2769 1615 : return m_skipValidationInParseCommandLine || ValidateArguments();
2770 : }
2771 :
2772 : /************************************************************************/
2773 : /* GDALAlgorithm::ReportError() */
2774 : /************************************************************************/
2775 :
2776 : //! @cond Doxygen_Suppress
2777 993 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2778 : const char *fmt, ...) const
2779 : {
2780 : va_list args;
2781 993 : va_start(args, fmt);
2782 993 : CPLError(eErrClass, err_no, "%s",
2783 993 : std::string(m_name)
2784 993 : .append(": ")
2785 1986 : .append(CPLString().vPrintf(fmt, args))
2786 : .c_str());
2787 993 : va_end(args);
2788 993 : }
2789 :
2790 : //! @endcond
2791 :
2792 : /************************************************************************/
2793 : /* GDALAlgorithm::ProcessDatasetArg() */
2794 : /************************************************************************/
2795 :
2796 11707 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2797 : GDALAlgorithm *algForOutput)
2798 : {
2799 11707 : bool ret = true;
2800 :
2801 11707 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2802 11707 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2803 11707 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2804 :
2805 11707 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2806 11707 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2807 11707 : const bool append = hasAppendArg && appendArg->Get<bool>();
2808 :
2809 11707 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2810 : const bool overwrite =
2811 19146 : (arg->IsOutput() && overwriteArg &&
2812 19146 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2813 :
2814 11707 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2815 23414 : auto &val = [arg]() -> GDALArgDatasetValue &
2816 : {
2817 11707 : if (arg->GetType() == GAAT_DATASET_LIST)
2818 6891 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2819 : else
2820 4816 : return arg->Get<GDALArgDatasetValue>();
2821 11707 : }();
2822 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2823 18618 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2824 18626 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2825 8 : !overwrite;
2826 :
2827 : // Used for nested pipelines
2828 : const auto oIterDatasetNameToDataset =
2829 23411 : val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2830 11707 : : m_oMapDatasetNameToDataset.end();
2831 :
2832 11707 : if (!val.GetDatasetRef() && !val.IsNameSet())
2833 : {
2834 3 : ReportError(CE_Failure, CPLE_AppDefined,
2835 : "Argument '%s' has no dataset object or dataset name.",
2836 3 : arg->GetName().c_str());
2837 3 : ret = false;
2838 : }
2839 11704 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2840 : {
2841 3 : return false;
2842 : }
2843 319 : else if (m_inputDatasetCanBeOmitted &&
2844 12020 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2845 17 : !arg->IsOutput())
2846 : {
2847 17 : return true;
2848 : }
2849 17200 : else if (!val.GetDatasetRef() &&
2850 5838 : (arg->AutoOpenDataset() ||
2851 17522 : oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2852 5195 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2853 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2854 : {
2855 1599 : int flags = arg->GetDatasetType();
2856 1599 : bool assignToOutputArg = false;
2857 :
2858 : // Check if input and output parameters point to the same
2859 : // filename (for vector datasets)
2860 2953 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2861 2953 : outputArg && outputArg->GetType() == GAAT_DATASET)
2862 : {
2863 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2864 121 : if (!outputVal.GetDatasetRef() &&
2865 121 : outputVal.GetName() == val.GetName() &&
2866 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2867 : {
2868 2 : assignToOutputArg = true;
2869 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2870 : }
2871 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2872 : {
2873 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2874 : }
2875 : }
2876 :
2877 1599 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2878 1516 : flags |= GDAL_OF_VERBOSE_ERROR;
2879 1599 : if ((arg == outputArg || !outputArg) && update)
2880 : {
2881 85 : flags |= GDAL_OF_UPDATE;
2882 85 : if (!append)
2883 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2884 : }
2885 :
2886 1599 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2887 : const bool readOnly =
2888 1643 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2889 44 : readOnlyArg->Get<bool>());
2890 1599 : if (readOnly)
2891 12 : flags &= ~GDAL_OF_UPDATE;
2892 :
2893 3198 : CPLStringList aosOpenOptions;
2894 3198 : CPLStringList aosAllowedDrivers;
2895 1599 : if (arg->IsInput())
2896 : {
2897 1599 : if (arg == outputArg)
2898 : {
2899 83 : if (update && !overwrite)
2900 : {
2901 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2902 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2903 46 : aosOpenOptions = CPLStringList(
2904 46 : ooArg->Get<std::vector<std::string>>());
2905 : }
2906 : }
2907 : else
2908 : {
2909 1516 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2910 1516 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2911 : aosOpenOptions =
2912 1436 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2913 :
2914 1516 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2915 1516 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2916 : aosAllowedDrivers =
2917 1392 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2918 : }
2919 : }
2920 :
2921 3198 : std::string osDatasetName = val.GetName();
2922 1599 : if (!m_referencePath.empty())
2923 : {
2924 46 : osDatasetName = GDALDataset::BuildFilename(
2925 23 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2926 : }
2927 1599 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2928 0 : osDatasetName = "/vsistdin/";
2929 :
2930 : // Handle special case of overview delete in GTiff which would fail
2931 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2932 145 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2933 1746 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2934 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2935 : {
2936 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2937 : GDALDriverH hDrv =
2938 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2939 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2940 : {
2941 : // Cleaning does not break COG layout
2942 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2943 : }
2944 : }
2945 :
2946 : GDALDataset *poDS;
2947 3198 : CPLErrorAccumulator oAccumulator;
2948 : {
2949 3198 : auto oContext = oAccumulator.InstallForCurrentScope();
2950 :
2951 1599 : poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2952 1599 : ? oIterDatasetNameToDataset->second
2953 1583 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2954 1583 : aosAllowedDrivers.List(),
2955 1583 : aosOpenOptions.List());
2956 :
2957 71 : if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
2958 1670 : !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
2959 : {
2960 41 : auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
2961 82 : osDatasetName.c_str(), nullptr);
2962 41 : if (eErr == OGRERR_NONE)
2963 : {
2964 12 : auto poMemDS = std::make_unique<MEMDataset>();
2965 12 : auto *poLayer = poMemDS->CreateLayer(
2966 : "layer", poWktGeom->getSpatialReference(),
2967 6 : poWktGeom->getGeometryType());
2968 :
2969 6 : auto poFeatureDefn = poLayer->GetLayerDefn();
2970 12 : OGRFeature oFeature(poFeatureDefn);
2971 :
2972 6 : oFeature.SetGeometry(std::move(poWktGeom));
2973 6 : if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
2974 : {
2975 6 : poDS = poMemDS.release();
2976 6 : oAccumulator.ClearErrors();
2977 : }
2978 : }
2979 : }
2980 :
2981 : // Retry with PostGIS vector driver
2982 65 : if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
2983 63 : cpl::starts_with(osDatasetName, "PG:") &&
2984 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2985 1664 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2986 : {
2987 0 : oAccumulator.ClearErrors();
2988 0 : poDS = GDALDataset::Open(
2989 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2990 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2991 : }
2992 : }
2993 1599 : oAccumulator.ReplayErrors();
2994 :
2995 1599 : if (poDS)
2996 : {
2997 1534 : if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2998 : {
2999 16 : if (arg->GetType() == GAAT_DATASET)
3000 8 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
3001 16 : poDS->Reference();
3002 16 : m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
3003 : }
3004 :
3005 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
3006 : // where the PG: dataset will be first opened with the PostGISRaster
3007 : // driver whereas the PostgreSQL (vector) one is actually wanted.
3008 2156 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
3009 2271 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
3010 115 : aosOpenOptions.empty())
3011 : {
3012 111 : auto poDrv = poDS->GetDriver();
3013 111 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
3014 : {
3015 : // Retry with PostgreSQL (vector) driver
3016 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
3017 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
3018 0 : if (poTmpDS)
3019 : {
3020 0 : poDS->ReleaseRef();
3021 0 : poDS = poTmpDS.release();
3022 : }
3023 : }
3024 : }
3025 :
3026 1534 : if (assignToOutputArg)
3027 : {
3028 : // Avoid opening twice the same datasource if it is both
3029 : // the input and output.
3030 : // Known to cause problems with at least FGdb, SQLite
3031 : // and GPKG drivers. See #4270
3032 : // Restrict to those 3 drivers. For example it is known
3033 : // to break with the PG driver due to the way it
3034 : // manages transactions.
3035 2 : auto poDriver = poDS->GetDriver();
3036 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
3037 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
3038 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
3039 : {
3040 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
3041 : }
3042 : }
3043 1534 : val.SetDatasetOpenedByAlgorithm();
3044 1534 : val.Set(poDS);
3045 1534 : poDS->ReleaseRef();
3046 : }
3047 65 : else if (!append)
3048 : {
3049 63 : ret = false;
3050 : }
3051 : }
3052 :
3053 : // Deal with overwriting the output dataset
3054 11687 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
3055 : {
3056 3598 : if (!append)
3057 : {
3058 : // If outputting to MEM, do not try to erase a real file of the same name!
3059 : const auto outputFormatArg =
3060 3586 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3061 10720 : if (!(outputFormatArg &&
3062 3567 : outputFormatArg->GetType() == GAAT_STRING &&
3063 3567 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3064 2311 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3065 1305 : "stream") ||
3066 1305 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3067 : "Memory"))))
3068 : {
3069 1324 : const char *pszType = "";
3070 1324 : GDALDriver *poDriver = nullptr;
3071 2602 : if (!val.GetName().empty() &&
3072 1278 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
3073 : &poDriver))
3074 : {
3075 79 : if (!overwrite)
3076 : {
3077 68 : std::string options;
3078 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
3079 : {
3080 11 : options += "--";
3081 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
3082 : }
3083 34 : if (hasAppendArg)
3084 : {
3085 22 : if (!options.empty())
3086 8 : options += '/';
3087 22 : options += "--";
3088 22 : options += GDAL_ARG_NAME_APPEND;
3089 : }
3090 34 : if (hasUpdateArg)
3091 : {
3092 15 : if (!options.empty())
3093 12 : options += '/';
3094 15 : options += "--";
3095 15 : options += GDAL_ARG_NAME_UPDATE;
3096 : }
3097 :
3098 34 : if (poDriver)
3099 : {
3100 68 : const char *pszPrefix = poDriver->GetMetadataItem(
3101 34 : GDAL_DMD_CONNECTION_PREFIX);
3102 34 : if (pszPrefix &&
3103 0 : STARTS_WITH_CI(val.GetName().c_str(),
3104 : pszPrefix))
3105 : {
3106 0 : bool bExists = false;
3107 : {
3108 : CPLErrorStateBackuper oBackuper(
3109 0 : CPLQuietErrorHandler);
3110 0 : bExists = std::unique_ptr<GDALDataset>(
3111 : GDALDataset::Open(
3112 0 : val.GetName().c_str())) !=
3113 : nullptr;
3114 : }
3115 0 : if (bExists)
3116 : {
3117 0 : if (!options.empty())
3118 0 : options = " You may specify the " +
3119 0 : options + " option.";
3120 0 : ReportError(CE_Failure, CPLE_AppDefined,
3121 : "%s '%s' already exists.%s",
3122 0 : pszType, val.GetName().c_str(),
3123 : options.c_str());
3124 0 : return false;
3125 : }
3126 :
3127 0 : return true;
3128 : }
3129 : }
3130 :
3131 34 : if (!options.empty())
3132 28 : options = '/' + options;
3133 68 : ReportError(
3134 : CE_Failure, CPLE_AppDefined,
3135 : "%s '%s' already exists. You may specify the "
3136 : "--overwrite%s option.",
3137 34 : pszType, val.GetName().c_str(), options.c_str());
3138 34 : return false;
3139 : }
3140 45 : else if (EQUAL(pszType, "File"))
3141 : {
3142 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3143 : {
3144 0 : ReportError(CE_Failure, CPLE_AppDefined,
3145 : "Deleting %s failed: %s",
3146 0 : val.GetName().c_str(),
3147 0 : VSIStrerror(errno));
3148 0 : return false;
3149 : }
3150 : }
3151 44 : else if (EQUAL(pszType, "Directory"))
3152 : {
3153 : // We don't want the user to accidentally erase a non-GDAL dataset
3154 1 : ReportError(CE_Failure, CPLE_AppDefined,
3155 : "Directory '%s' already exists, but is not "
3156 : "recognized as a valid GDAL dataset. "
3157 : "Please manually delete it before retrying",
3158 1 : val.GetName().c_str());
3159 1 : return false;
3160 : }
3161 43 : else if (poDriver)
3162 : {
3163 : bool bDeleteOK;
3164 : {
3165 : CPLErrorStateBackuper oBackuper(
3166 43 : CPLQuietErrorHandler);
3167 43 : bDeleteOK = (poDriver->Delete(
3168 43 : val.GetName().c_str()) == CE_None);
3169 : }
3170 : VSIStatBufL sStat;
3171 46 : if (!bDeleteOK &&
3172 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3173 : {
3174 3 : if (VSI_ISDIR(sStat.st_mode))
3175 : {
3176 : // We don't want the user to accidentally erase a non-GDAL dataset
3177 0 : ReportError(
3178 : CE_Failure, CPLE_AppDefined,
3179 : "Directory '%s' already exists, but is not "
3180 : "recognized as a valid GDAL dataset. "
3181 : "Please manually delete it before retrying",
3182 0 : val.GetName().c_str());
3183 2 : return false;
3184 : }
3185 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3186 : {
3187 2 : ReportError(CE_Failure, CPLE_AppDefined,
3188 : "Deleting %s failed: %s",
3189 2 : val.GetName().c_str(),
3190 2 : VSIStrerror(errno));
3191 2 : return false;
3192 : }
3193 : }
3194 : }
3195 : }
3196 : }
3197 : }
3198 : }
3199 :
3200 : // If outputting to stdout, automatically turn off progress bar
3201 11650 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3202 : {
3203 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3204 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3205 5 : quietArg->Set(true);
3206 : }
3207 :
3208 11650 : return ret;
3209 : }
3210 :
3211 : /************************************************************************/
3212 : /* GDALAlgorithm::ValidateArguments() */
3213 : /************************************************************************/
3214 :
3215 8156 : bool GDALAlgorithm::ValidateArguments()
3216 : {
3217 8156 : if (m_selectedSubAlg)
3218 3 : return m_selectedSubAlg->ValidateArguments();
3219 :
3220 8153 : if (m_specialActionRequested)
3221 1 : return true;
3222 :
3223 8152 : m_arbitraryLongNameArgsAllowed = false;
3224 :
3225 : // If only --output=format=MEM/stream is specified and not --output,
3226 : // then set empty name for --output.
3227 8152 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3228 8152 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3229 4705 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3230 3034 : !outputArg->IsExplicitlySet() &&
3231 418 : outputFormatArg->GetType() == GAAT_STRING &&
3232 418 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3233 660 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3234 13246 : outputArg->GetType() == GAAT_DATASET &&
3235 389 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3236 : {
3237 389 : outputArg->Get<GDALArgDatasetValue>().Set("");
3238 : }
3239 :
3240 : // The method may emit several errors if several constraints are not met.
3241 8152 : bool ret = true;
3242 16304 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3243 16304 : std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3244 150919 : for (auto &arg : m_args)
3245 : {
3246 : // Check mutually exclusive/dependent arguments
3247 142767 : if (arg->IsExplicitlySet())
3248 : {
3249 :
3250 22468 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3251 22468 : if (!mutualExclusionGroup.empty())
3252 : {
3253 : auto oIter =
3254 992 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3255 992 : if (oIter != mutualExclusionGroupUsed.end())
3256 : {
3257 13 : ret = false;
3258 26 : ReportError(
3259 : CE_Failure, CPLE_AppDefined,
3260 : "Argument '%s' is mutually exclusive with '%s'.",
3261 26 : arg->GetName().c_str(), oIter->second.c_str());
3262 : }
3263 : else
3264 : {
3265 979 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3266 1958 : arg->GetName();
3267 : }
3268 : }
3269 :
3270 22468 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3271 22468 : if (!mutualDependencyGroup.empty())
3272 : {
3273 78 : if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3274 156 : mutualDependencyGroupUsed.end())
3275 : {
3276 129 : mutualDependencyGroupUsed[mutualDependencyGroup] = {
3277 129 : arg->GetName()};
3278 : }
3279 : else
3280 : {
3281 70 : mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3282 35 : arg->GetName());
3283 : }
3284 : }
3285 :
3286 : // Check direct dependencies
3287 22480 : for (const auto &dependency : arg->GetDirectDependencies())
3288 : {
3289 12 : auto depArg = GetArg(dependency);
3290 12 : if (!depArg)
3291 : {
3292 0 : ret = false;
3293 0 : ReportError(CE_Failure, CPLE_AppDefined,
3294 : "Argument '%s' depends on argument '%s' that "
3295 : "is not defined.",
3296 0 : arg->GetName().c_str(), dependency.c_str());
3297 : }
3298 12 : else if (!depArg->IsExplicitlySet())
3299 : {
3300 6 : ret = false;
3301 12 : ReportError(CE_Failure, CPLE_AppDefined,
3302 : "Argument '%s' depends on argument '%s' that "
3303 : "has not been specified.",
3304 6 : arg->GetName().c_str(),
3305 6 : depArg->GetName().c_str());
3306 : }
3307 : }
3308 : }
3309 :
3310 142939 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3311 172 : !arg->HasDefaultValue())
3312 : {
3313 172 : bool emitError = true;
3314 172 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3315 172 : if (!mutualExclusionGroup.empty())
3316 : {
3317 1885 : for (const auto &otherArg : m_args)
3318 : {
3319 1859 : if (otherArg->GetMutualExclusionGroup() ==
3320 1988 : mutualExclusionGroup &&
3321 129 : otherArg->IsExplicitlySet())
3322 : {
3323 74 : emitError = false;
3324 74 : break;
3325 : }
3326 : }
3327 : }
3328 262 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3329 55 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3330 70 : (arg->GetType() == GAAT_DATASET ||
3331 35 : arg->GetType() == GAAT_DATASET_LIST)))
3332 : {
3333 63 : ReportError(CE_Failure, CPLE_AppDefined,
3334 : "Required argument '%s' has not been specified.",
3335 63 : arg->GetName().c_str());
3336 63 : ret = false;
3337 : }
3338 : }
3339 142595 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3340 : {
3341 4816 : if (!ProcessDatasetArg(arg.get(), this))
3342 50 : ret = false;
3343 : }
3344 :
3345 142767 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3346 : {
3347 6667 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3348 6667 : if (listVal.size() == 1)
3349 : {
3350 6507 : if (!ProcessDatasetArg(arg.get(), this))
3351 42 : ret = false;
3352 : }
3353 : else
3354 : {
3355 491 : for (auto &val : listVal)
3356 : {
3357 331 : if (val.GetDatasetRef())
3358 : {
3359 120 : if (!CheckCanSetDatasetObject(arg.get()))
3360 : {
3361 0 : ret = false;
3362 : }
3363 327 : continue;
3364 : }
3365 :
3366 211 : if (val.GetName().empty())
3367 : {
3368 0 : ReportError(CE_Failure, CPLE_AppDefined,
3369 : "Argument '%s' has no dataset object or "
3370 : "dataset name.",
3371 0 : arg->GetName().c_str());
3372 0 : ret = false;
3373 0 : continue;
3374 : }
3375 :
3376 211 : auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3377 211 : if (oIter != m_oMapDatasetNameToDataset.end())
3378 : {
3379 2 : auto poDS = oIter->second;
3380 2 : val.SetDatasetOpenedByAlgorithm();
3381 2 : val.Set(poDS);
3382 2 : m_oMapDatasetNameToDataset.erase(oIter);
3383 2 : continue;
3384 : }
3385 :
3386 209 : if (!arg->AutoOpenDataset())
3387 205 : continue;
3388 :
3389 4 : int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3390 :
3391 8 : CPLStringList aosOpenOptions;
3392 8 : CPLStringList aosAllowedDrivers;
3393 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3394 : {
3395 4 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3396 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3397 : {
3398 4 : aosOpenOptions = CPLStringList(
3399 4 : ooArg->Get<std::vector<std::string>>());
3400 : }
3401 :
3402 4 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3403 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3404 : {
3405 4 : aosAllowedDrivers = CPLStringList(
3406 4 : ifArg->Get<std::vector<std::string>>());
3407 : }
3408 :
3409 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3410 4 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3411 0 : updateArg->Get<bool>())
3412 : {
3413 0 : flags |= GDAL_OF_UPDATE;
3414 : }
3415 : }
3416 :
3417 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3418 4 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3419 12 : aosOpenOptions.List()));
3420 4 : if (poDS)
3421 : {
3422 3 : val.Set(std::move(poDS));
3423 : }
3424 : else
3425 : {
3426 1 : ret = false;
3427 : }
3428 : }
3429 : }
3430 : }
3431 :
3432 142767 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3433 : {
3434 8 : ret = false;
3435 : }
3436 : }
3437 :
3438 : // Check mutual dependency groups
3439 8152 : std::vector<std::string> processedGroups;
3440 : // Loop through group map and check there are not required args in the group that are not set
3441 8195 : for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3442 : {
3443 43 : if (std::find(processedGroups.begin(), processedGroups.end(),
3444 43 : groupName) != processedGroups.end())
3445 0 : continue;
3446 86 : std::vector<std::string> missingArgs;
3447 848 : for (auto &arg : m_args)
3448 : {
3449 805 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3450 898 : if (mutualDependencyGroup == groupName &&
3451 93 : std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3452 898 : argNames.end())
3453 : {
3454 15 : missingArgs.push_back(arg->GetName());
3455 : }
3456 : }
3457 43 : if (!missingArgs.empty())
3458 : {
3459 12 : ret = false;
3460 24 : std::string missingArgsStr;
3461 27 : for (const auto &missingArg : missingArgs)
3462 : {
3463 15 : if (!missingArgsStr.empty())
3464 3 : missingArgsStr += ", ";
3465 15 : missingArgsStr += missingArg;
3466 : }
3467 24 : std::string givenArgsStr;
3468 27 : for (const auto &givenArg : argNames)
3469 : {
3470 15 : if (!givenArgsStr.empty())
3471 3 : givenArgsStr += ", ";
3472 15 : givenArgsStr += givenArg;
3473 : }
3474 12 : ReportError(CE_Failure, CPLE_AppDefined,
3475 : "Argument(s) '%s' require(s) that the following "
3476 : "argument(s) are also specified: %s.",
3477 : givenArgsStr.c_str(), missingArgsStr.c_str());
3478 : }
3479 43 : processedGroups.push_back(groupName);
3480 : }
3481 :
3482 34507 : for (const auto &f : m_validationActions)
3483 : {
3484 26355 : if (!f())
3485 82 : ret = false;
3486 : }
3487 :
3488 8152 : return ret;
3489 : }
3490 :
3491 : /************************************************************************/
3492 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3493 : /************************************************************************/
3494 :
3495 : std::unique_ptr<GDALAlgorithm>
3496 11150 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3497 : bool suggestionAllowed) const
3498 : {
3499 11150 : auto ret = m_subAlgRegistry.Instantiate(name);
3500 22300 : auto childCallPath = m_callPath;
3501 11150 : childCallPath.push_back(name);
3502 11150 : if (!ret)
3503 : {
3504 1214 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3505 1214 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3506 : }
3507 11150 : if (ret)
3508 : {
3509 10969 : ret->SetCallPath(childCallPath);
3510 : }
3511 181 : else if (suggestionAllowed)
3512 : {
3513 72 : std::string bestCandidate;
3514 36 : size_t bestDistance = std::numeric_limits<size_t>::max();
3515 533 : for (const std::string &candidate : GetSubAlgorithmNames())
3516 : {
3517 : const size_t distance =
3518 497 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3519 : /* transpositionAllowed = */ true);
3520 497 : if (distance < bestDistance)
3521 : {
3522 83 : bestCandidate = candidate;
3523 83 : bestDistance = distance;
3524 : }
3525 414 : else if (distance == bestDistance)
3526 : {
3527 51 : bestCandidate.clear();
3528 : }
3529 : }
3530 36 : if (!bestCandidate.empty() && bestDistance <= 2)
3531 : {
3532 4 : CPLError(CE_Failure, CPLE_AppDefined,
3533 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3534 : name.c_str(), bestCandidate.c_str());
3535 : }
3536 : }
3537 22300 : return ret;
3538 : }
3539 :
3540 : /************************************************************************/
3541 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3542 : /************************************************************************/
3543 :
3544 : std::string
3545 39 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3546 : {
3547 39 : if (osName.size() >= 3)
3548 : {
3549 34 : std::string bestCandidate;
3550 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3551 776 : for (const auto &[key, value] : m_mapLongNameToArg)
3552 : {
3553 742 : CPL_IGNORE_RET_VAL(value);
3554 742 : const size_t distance = CPLLevenshteinDistance(
3555 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3556 742 : if (distance < bestDistance)
3557 : {
3558 89 : bestCandidate = key;
3559 89 : bestDistance = distance;
3560 : }
3561 653 : else if (distance == bestDistance)
3562 : {
3563 78 : bestCandidate.clear();
3564 : }
3565 : }
3566 48 : if (!bestCandidate.empty() &&
3567 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3568 : {
3569 5 : return bestCandidate;
3570 : }
3571 : }
3572 34 : return std::string();
3573 : }
3574 :
3575 : /************************************************************************/
3576 : /* GDALAlgorithm::GetSuggestionsForArgumentName() */
3577 : /************************************************************************/
3578 :
3579 : std::vector<std::string>
3580 39 : GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
3581 : {
3582 39 : std::vector<std::string> ret;
3583 78 : std::string suggestion = GetSuggestionForArgumentName(osName);
3584 39 : if (!suggestion.empty())
3585 : {
3586 5 : ret.push_back(std::move(suggestion));
3587 : }
3588 34 : else if (osName.size() >= 3)
3589 : {
3590 : // e.g "crs" for reproject will match "input-crs" and "target-crs"
3591 87 : const std::string dashName = std::string("-").append(osName);
3592 554 : for (const auto &arg : m_args)
3593 : {
3594 525 : if (cpl::ends_with(arg->GetName(), dashName))
3595 : {
3596 3 : ret.push_back(arg->GetName());
3597 : }
3598 : }
3599 : }
3600 78 : return ret;
3601 : }
3602 :
3603 : /************************************************************************/
3604 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3605 : /************************************************************************/
3606 :
3607 : /* static */
3608 23 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3609 : {
3610 69 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3611 69 : osName == GDAL_ARG_NAME_OVERWRITE ||
3612 46 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3613 : }
3614 :
3615 : /************************************************************************/
3616 : /* GDALAlgorithm::HasOutputString() */
3617 : /************************************************************************/
3618 :
3619 74 : bool GDALAlgorithm::HasOutputString() const
3620 : {
3621 74 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3622 74 : return outputStringArg && outputStringArg->IsOutput();
3623 : }
3624 :
3625 : /************************************************************************/
3626 : /* GDALAlgorithm::GetArg() */
3627 : /************************************************************************/
3628 :
3629 514683 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3630 : bool suggestionAllowed, bool isConst)
3631 : {
3632 514683 : const auto nPos = osName.find_first_not_of('-');
3633 514683 : if (nPos == std::string::npos)
3634 27 : return nullptr;
3635 1029310 : std::string osKey = osName.substr(nPos);
3636 : {
3637 514656 : const auto oIter = m_mapLongNameToArg.find(osKey);
3638 514656 : if (oIter != m_mapLongNameToArg.end())
3639 474711 : return oIter->second;
3640 : }
3641 : {
3642 39945 : const auto oIter = m_mapShortNameToArg.find(osKey);
3643 39945 : if (oIter != m_mapShortNameToArg.end())
3644 8 : return oIter->second;
3645 : }
3646 :
3647 39937 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3648 : {
3649 23 : const auto nDotPos = osKey.find('.');
3650 : const std::string osKeyEnd =
3651 23 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3652 23 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3653 : {
3654 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3655 0 : std::make_unique<bool>());
3656 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3657 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3658 0 : .SetUserProvided();
3659 : }
3660 : else
3661 : {
3662 46 : const std::string osKeyInit = osKey;
3663 23 : if (osKey == "oo")
3664 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3665 23 : else if (osKey == "co")
3666 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3667 23 : else if (osKey == "of")
3668 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3669 23 : else if (osKey == "if")
3670 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3671 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3672 23 : std::make_unique<std::string>());
3673 : auto &arg =
3674 46 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3675 46 : m_arbitraryLongNameArgsValuesStr.back().get())
3676 23 : .SetUserProvided();
3677 23 : if (osKey != osKeyInit)
3678 0 : arg.AddAlias(osKeyInit);
3679 : }
3680 23 : const auto oIter = m_mapLongNameToArg.find(osKey);
3681 23 : CPLAssert(oIter != m_mapLongNameToArg.end());
3682 23 : return oIter->second;
3683 : }
3684 :
3685 39914 : if (suggestionAllowed)
3686 : {
3687 14 : const auto suggestions = GetSuggestionsForArgumentName(osName);
3688 7 : if (!suggestions.empty())
3689 : {
3690 2 : CPLError(CE_Failure, CPLE_AppDefined,
3691 : "Argument '%s' is unknown. Do you mean %s?",
3692 : osName.c_str(),
3693 4 : FormatSuggestionsAsString(suggestions,
3694 : /* addDashDashPrefix = */ false)
3695 : .c_str());
3696 : }
3697 : }
3698 :
3699 39914 : return nullptr;
3700 : }
3701 :
3702 : /************************************************************************/
3703 : /* GDALAlgorithm::AddAliasFor() */
3704 : /************************************************************************/
3705 :
3706 : //! @cond Doxygen_Suppress
3707 89244 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3708 : const std::string &alias)
3709 : {
3710 89244 : if (cpl::contains(m_mapLongNameToArg, alias))
3711 : {
3712 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3713 : alias.c_str());
3714 : }
3715 : else
3716 : {
3717 89243 : m_mapLongNameToArg[alias] = arg;
3718 : }
3719 89244 : }
3720 :
3721 : //! @endcond
3722 :
3723 : /************************************************************************/
3724 : /* GDALAlgorithm::AddShortNameAliasFor() */
3725 : /************************************************************************/
3726 :
3727 : //! @cond Doxygen_Suppress
3728 50 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3729 : char shortNameAlias)
3730 : {
3731 100 : std::string alias;
3732 50 : alias += shortNameAlias;
3733 50 : if (cpl::contains(m_mapShortNameToArg, alias))
3734 : {
3735 0 : ReportError(CE_Failure, CPLE_AppDefined,
3736 : "Short name '%s' already declared.", alias.c_str());
3737 : }
3738 : else
3739 : {
3740 50 : m_mapShortNameToArg[alias] = arg;
3741 : }
3742 50 : }
3743 :
3744 : //! @endcond
3745 :
3746 : /************************************************************************/
3747 : /* GDALAlgorithm::SetPositional() */
3748 : /************************************************************************/
3749 :
3750 : //! @cond Doxygen_Suppress
3751 24528 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3752 : {
3753 24528 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3754 : arg) == m_positionalArgs.end());
3755 24528 : m_positionalArgs.push_back(arg);
3756 24528 : }
3757 :
3758 : //! @endcond
3759 :
3760 : /************************************************************************/
3761 : /* GDALAlgorithm::HasSubAlgorithms() */
3762 : /************************************************************************/
3763 :
3764 14479 : bool GDALAlgorithm::HasSubAlgorithms() const
3765 : {
3766 14479 : if (!m_subAlgRegistry.empty())
3767 3808 : return true;
3768 10671 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3769 21342 : .GetDeclaredSubAlgorithmNames(m_callPath)
3770 10671 : .empty();
3771 : }
3772 :
3773 : /************************************************************************/
3774 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3775 : /************************************************************************/
3776 :
3777 1613 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3778 : {
3779 1613 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3780 1613 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3781 3226 : .GetDeclaredSubAlgorithmNames(m_callPath);
3782 1613 : ret.insert(ret.end(), other.begin(), other.end());
3783 1613 : if (!other.empty())
3784 521 : std::sort(ret.begin(), ret.end());
3785 3226 : return ret;
3786 : }
3787 :
3788 : /************************************************************************/
3789 : /* GDALAlgorithm::AddArg() */
3790 : /************************************************************************/
3791 :
3792 : GDALInConstructionAlgorithmArg &
3793 354408 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3794 : {
3795 354408 : auto argRaw = arg.get();
3796 354408 : const auto &longName = argRaw->GetName();
3797 354408 : if (!longName.empty())
3798 : {
3799 354395 : if (longName[0] == '-')
3800 : {
3801 1 : ReportError(CE_Failure, CPLE_AppDefined,
3802 : "Long name '%s' should not start with '-'",
3803 : longName.c_str());
3804 : }
3805 354395 : if (longName.find('=') != std::string::npos)
3806 : {
3807 1 : ReportError(CE_Failure, CPLE_AppDefined,
3808 : "Long name '%s' should not contain a '=' character",
3809 : longName.c_str());
3810 : }
3811 354395 : if (cpl::contains(m_mapLongNameToArg, longName))
3812 : {
3813 1 : ReportError(CE_Failure, CPLE_AppDefined,
3814 : "Long name '%s' already declared", longName.c_str());
3815 : }
3816 354395 : m_mapLongNameToArg[longName] = argRaw;
3817 : }
3818 354408 : const auto &shortName = argRaw->GetShortName();
3819 354408 : if (!shortName.empty())
3820 : {
3821 174022 : if (shortName.size() != 1 ||
3822 87011 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3823 66 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3824 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3825 : {
3826 1 : ReportError(CE_Failure, CPLE_AppDefined,
3827 : "Short name '%s' should be a single letter or digit",
3828 : shortName.c_str());
3829 : }
3830 87011 : if (cpl::contains(m_mapShortNameToArg, shortName))
3831 : {
3832 1 : ReportError(CE_Failure, CPLE_AppDefined,
3833 : "Short name '%s' already declared", shortName.c_str());
3834 : }
3835 87011 : m_mapShortNameToArg[shortName] = argRaw;
3836 : }
3837 354408 : m_args.emplace_back(std::move(arg));
3838 : return *(
3839 354408 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3840 : }
3841 :
3842 : GDALInConstructionAlgorithmArg &
3843 161506 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3844 : const std::string &helpMessage, bool *pValue)
3845 : {
3846 161506 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3847 : this,
3848 323012 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3849 323012 : pValue));
3850 : }
3851 :
3852 : GDALInConstructionAlgorithmArg &
3853 56075 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3854 : const std::string &helpMessage, std::string *pValue)
3855 : {
3856 56075 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3857 : this,
3858 112150 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3859 112150 : pValue));
3860 : }
3861 :
3862 : GDALInConstructionAlgorithmArg &
3863 13058 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3864 : const std::string &helpMessage, int *pValue)
3865 : {
3866 13058 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3867 : this,
3868 26116 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3869 26116 : pValue));
3870 : }
3871 :
3872 : GDALInConstructionAlgorithmArg &
3873 10667 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3874 : const std::string &helpMessage, double *pValue)
3875 : {
3876 10667 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3877 : this,
3878 21334 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3879 21334 : pValue));
3880 : }
3881 :
3882 : GDALInConstructionAlgorithmArg &
3883 13477 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3884 : const std::string &helpMessage,
3885 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3886 : {
3887 26954 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3888 : this,
3889 26954 : GDALAlgorithmArgDecl(longName, chShortName,
3890 : helpMessage, GAAT_DATASET),
3891 13477 : pValue))
3892 13477 : .SetDatasetType(type);
3893 13477 : pValue->SetOwnerArgument(&arg);
3894 13477 : return arg;
3895 : }
3896 :
3897 : GDALInConstructionAlgorithmArg &
3898 76013 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3899 : const std::string &helpMessage,
3900 : std::vector<std::string> *pValue)
3901 : {
3902 76013 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3903 : this,
3904 152026 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3905 : GAAT_STRING_LIST),
3906 152026 : pValue));
3907 : }
3908 :
3909 : GDALInConstructionAlgorithmArg &
3910 2144 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3911 : const std::string &helpMessage, std::vector<int> *pValue)
3912 : {
3913 2144 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3914 : this,
3915 4288 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3916 : GAAT_INTEGER_LIST),
3917 4288 : pValue));
3918 : }
3919 :
3920 : GDALInConstructionAlgorithmArg &
3921 5490 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3922 : const std::string &helpMessage,
3923 : std::vector<double> *pValue)
3924 : {
3925 5490 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3926 : this,
3927 10980 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3928 : GAAT_REAL_LIST),
3929 10980 : pValue));
3930 : }
3931 :
3932 : GDALInConstructionAlgorithmArg &
3933 15978 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3934 : const std::string &helpMessage,
3935 : std::vector<GDALArgDatasetValue> *pValue,
3936 : GDALArgDatasetType type)
3937 : {
3938 31956 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3939 : this,
3940 31956 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3941 : GAAT_DATASET_LIST),
3942 15978 : pValue))
3943 31956 : .SetDatasetType(type);
3944 : }
3945 :
3946 : /************************************************************************/
3947 : /* MsgOrDefault() */
3948 : /************************************************************************/
3949 :
3950 116813 : inline const char *MsgOrDefault(const char *helpMessage,
3951 : const char *defaultMessage)
3952 : {
3953 116813 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3954 : }
3955 :
3956 : /************************************************************************/
3957 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3958 : /************************************************************************/
3959 :
3960 : /* static */
3961 19560 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3962 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3963 : {
3964 : arg.SetAutoCompleteFunction(
3965 7 : [&arg,
3966 2483 : type](const std::string ¤tValue) -> std::vector<std::string>
3967 : {
3968 14 : std::vector<std::string> oRet;
3969 :
3970 7 : if (arg.IsHidden())
3971 0 : return oRet;
3972 :
3973 : {
3974 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3975 : VSIStatBufL sStat;
3976 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3977 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3978 : {
3979 0 : return oRet;
3980 : }
3981 : }
3982 :
3983 7 : auto poDM = GetGDALDriverManager();
3984 14 : std::set<std::string> oExtensions;
3985 7 : if (type)
3986 : {
3987 1386 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3988 : {
3989 1380 : auto poDriver = poDM->GetDriver(i);
3990 3910 : if (((type & GDAL_OF_RASTER) != 0 &&
3991 1150 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3992 590 : ((type & GDAL_OF_VECTOR) != 0 &&
3993 2899 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3994 499 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3995 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3996 : {
3997 : const char *pszExtensions =
3998 881 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3999 881 : if (pszExtensions)
4000 : {
4001 : const CPLStringList aosExts(
4002 1164 : CSLTokenizeString2(pszExtensions, " ", 0));
4003 1313 : for (const char *pszExt : cpl::Iterate(aosExts))
4004 731 : oExtensions.insert(CPLString(pszExt).tolower());
4005 : }
4006 : }
4007 : }
4008 : }
4009 :
4010 14 : std::string osDir;
4011 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
4012 14 : std::string osPrefix;
4013 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
4014 : {
4015 82 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
4016 : {
4017 81 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
4018 : {
4019 2 : osPrefix = pszPrefix;
4020 2 : break;
4021 : }
4022 : }
4023 3 : if (osPrefix.empty())
4024 1 : return aosVSIPrefixes;
4025 2 : if (currentValue == osPrefix)
4026 1 : osDir = osPrefix;
4027 : }
4028 6 : if (osDir.empty())
4029 : {
4030 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
4031 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
4032 0 : osDir = std::move(osPrefix);
4033 : }
4034 :
4035 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
4036 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
4037 6 : if (currentValue.empty())
4038 1 : osDir.clear();
4039 : const std::string currentFilename =
4040 12 : CPLGetFilename(currentValue.c_str());
4041 6 : if (psDir)
4042 : {
4043 460 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
4044 : {
4045 455 : if ((currentFilename.empty() ||
4046 227 : STARTS_WITH(psEntry->pszName,
4047 229 : currentFilename.c_str())) &&
4048 229 : strcmp(psEntry->pszName, ".") != 0 &&
4049 1367 : strcmp(psEntry->pszName, "..") != 0 &&
4050 229 : (oExtensions.empty() ||
4051 228 : !strstr(psEntry->pszName, ".aux.xml")))
4052 : {
4053 906 : if (oExtensions.empty() ||
4054 226 : cpl::contains(
4055 : oExtensions,
4056 453 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
4057 679 : .tolower()) ||
4058 194 : VSI_ISDIR(psEntry->nMode))
4059 : {
4060 74 : std::string osVal;
4061 37 : if (osDir.empty() || osDir == ".")
4062 4 : osVal = psEntry->pszName;
4063 : else
4064 66 : osVal = CPLFormFilenameSafe(
4065 66 : osDir.c_str(), psEntry->pszName, nullptr);
4066 37 : if (VSI_ISDIR(psEntry->nMode))
4067 4 : osVal += osSep;
4068 37 : oRet.push_back(std::move(osVal));
4069 : }
4070 : }
4071 455 : }
4072 5 : VSICloseDir(psDir);
4073 : }
4074 6 : return oRet;
4075 19560 : });
4076 19560 : }
4077 :
4078 : /************************************************************************/
4079 : /* GDALAlgorithm::AddInputDatasetArg() */
4080 : /************************************************************************/
4081 :
4082 909 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4083 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4084 : bool positionalAndRequired, const char *helpMessage)
4085 : {
4086 : auto &arg = AddArg(
4087 : GDAL_ARG_NAME_INPUT, 'i',
4088 : MsgOrDefault(helpMessage,
4089 : CPLSPrintf("Input %s dataset",
4090 909 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4091 1818 : pValue, type);
4092 909 : if (positionalAndRequired)
4093 902 : arg.SetPositional().SetRequired();
4094 :
4095 909 : SetAutoCompleteFunctionForFilename(arg, type);
4096 :
4097 909 : AddValidationAction(
4098 80 : [pValue]()
4099 : {
4100 79 : if (pValue->GetName() == "-")
4101 1 : pValue->Set("/vsistdin/");
4102 79 : return true;
4103 : });
4104 :
4105 909 : return arg;
4106 : }
4107 :
4108 : /************************************************************************/
4109 : /* GDALAlgorithm::AddInputDatasetArg() */
4110 : /************************************************************************/
4111 :
4112 15509 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4113 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
4114 : bool positionalAndRequired, const char *helpMessage)
4115 : {
4116 : auto &arg =
4117 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
4118 : MsgOrDefault(
4119 : helpMessage,
4120 : CPLSPrintf("Input %s datasets",
4121 15509 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4122 46527 : pValue, type)
4123 15509 : .SetPackedValuesAllowed(false);
4124 15509 : if (positionalAndRequired)
4125 1685 : arg.SetPositional().SetRequired();
4126 :
4127 15509 : SetAutoCompleteFunctionForFilename(arg, type);
4128 :
4129 15509 : AddValidationAction(
4130 7657 : [pValue]()
4131 : {
4132 14419 : for (auto &val : *pValue)
4133 : {
4134 6762 : if (val.GetName() == "-")
4135 1 : val.Set("/vsistdin/");
4136 : }
4137 7657 : return true;
4138 : });
4139 15509 : return arg;
4140 : }
4141 :
4142 : /************************************************************************/
4143 : /* GDALAlgorithm::AddOutputDatasetArg() */
4144 : /************************************************************************/
4145 :
4146 9294 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4147 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4148 : bool positionalAndRequired, const char *helpMessage)
4149 : {
4150 : auto &arg =
4151 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4152 : MsgOrDefault(
4153 : helpMessage,
4154 : CPLSPrintf("Output %s dataset",
4155 9294 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4156 27882 : pValue, type)
4157 9294 : .SetIsInput(true)
4158 9294 : .SetIsOutput(true)
4159 9294 : .SetDatasetInputFlags(GADV_NAME)
4160 9294 : .SetDatasetOutputFlags(GADV_OBJECT);
4161 9294 : if (positionalAndRequired)
4162 4665 : arg.SetPositional().SetRequired();
4163 :
4164 9294 : AddValidationAction(
4165 14101 : [this, &arg, pValue]()
4166 : {
4167 4283 : if (pValue->GetName() == "-")
4168 4 : pValue->Set("/vsistdout/");
4169 :
4170 4283 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4171 4231 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4172 6715 : (!outputFormatArg->IsExplicitlySet() ||
4173 10998 : outputFormatArg->Get<std::string>().empty()) &&
4174 1747 : arg.IsExplicitlySet())
4175 : {
4176 : const auto vrtCompatible =
4177 1248 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4178 196 : if (vrtCompatible && !vrtCompatible->empty() &&
4179 1444 : vrtCompatible->front() == "false" &&
4180 1346 : EQUAL(
4181 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4182 : "VRT"))
4183 : {
4184 6 : ReportError(
4185 : CE_Failure, CPLE_NotSupported,
4186 : "VRT output is not supported.%s",
4187 6 : outputFormatArg->GetDescription().find("GDALG") !=
4188 : std::string::npos
4189 : ? " Consider using the GDALG driver instead (files "
4190 : "with .gdalg.json extension)"
4191 : : "");
4192 6 : return false;
4193 : }
4194 1242 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4195 2461 : EQUAL(pValue->GetName()
4196 : .substr(pValue->GetName().size() -
4197 : strlen(".gdalg.json"))
4198 : .c_str(),
4199 3703 : ".gdalg.json") &&
4200 28 : outputFormatArg->GetDescription().find("GDALG") ==
4201 : std::string::npos)
4202 : {
4203 0 : ReportError(CE_Failure, CPLE_NotSupported,
4204 : "GDALG output is not supported");
4205 0 : return false;
4206 : }
4207 : }
4208 4277 : return true;
4209 : });
4210 :
4211 9294 : return arg;
4212 : }
4213 :
4214 : /************************************************************************/
4215 : /* GDALAlgorithm::AddOverwriteArg() */
4216 : /************************************************************************/
4217 :
4218 : GDALInConstructionAlgorithmArg &
4219 9162 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4220 : {
4221 : return AddArg(
4222 : GDAL_ARG_NAME_OVERWRITE, 0,
4223 : MsgOrDefault(
4224 : helpMessage,
4225 : _("Whether overwriting existing output dataset is allowed")),
4226 18324 : pValue)
4227 18324 : .SetDefault(false);
4228 : }
4229 :
4230 : /************************************************************************/
4231 : /* GDALAlgorithm::AddOverwriteLayerArg() */
4232 : /************************************************************************/
4233 :
4234 : GDALInConstructionAlgorithmArg &
4235 3812 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4236 : {
4237 3812 : AddValidationAction(
4238 1793 : [this]
4239 : {
4240 1792 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4241 1792 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4242 : {
4243 1 : ReportError(CE_Failure, CPLE_AppDefined,
4244 : "--update argument must exist for "
4245 : "--overwrite-layer, even if hidden");
4246 1 : return false;
4247 : }
4248 1791 : return true;
4249 : });
4250 : return AddArg(
4251 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4252 : MsgOrDefault(
4253 : helpMessage,
4254 : _("Whether overwriting existing output layer is allowed")),
4255 7624 : pValue)
4256 3812 : .SetDefault(false)
4257 : .AddAction(
4258 19 : [this]
4259 : {
4260 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4261 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4262 : {
4263 19 : updateArg->Set(true);
4264 : }
4265 7643 : });
4266 : }
4267 :
4268 : /************************************************************************/
4269 : /* GDALAlgorithm::AddUpdateArg() */
4270 : /************************************************************************/
4271 :
4272 : GDALInConstructionAlgorithmArg &
4273 4384 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4274 : {
4275 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4276 : MsgOrDefault(
4277 : helpMessage,
4278 : _("Whether to open existing dataset in update mode")),
4279 8768 : pValue)
4280 8768 : .SetDefault(false);
4281 : }
4282 :
4283 : /************************************************************************/
4284 : /* GDALAlgorithm::AddAppendLayerArg() */
4285 : /************************************************************************/
4286 :
4287 : GDALInConstructionAlgorithmArg &
4288 3584 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4289 : {
4290 3584 : AddValidationAction(
4291 1749 : [this]
4292 : {
4293 1748 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4294 1748 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4295 : {
4296 1 : ReportError(CE_Failure, CPLE_AppDefined,
4297 : "--update argument must exist for --append, even "
4298 : "if hidden");
4299 1 : return false;
4300 : }
4301 1747 : return true;
4302 : });
4303 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4304 : MsgOrDefault(
4305 : helpMessage,
4306 : _("Whether appending to existing layer is allowed")),
4307 7168 : pValue)
4308 3584 : .SetDefault(false)
4309 : .AddAction(
4310 25 : [this]
4311 : {
4312 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4313 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4314 : {
4315 25 : updateArg->Set(true);
4316 : }
4317 7193 : });
4318 : }
4319 :
4320 : /************************************************************************/
4321 : /* GDALAlgorithm::AddOptionsSuggestions() */
4322 : /************************************************************************/
4323 :
4324 : /* static */
4325 30 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4326 : const std::string ¤tValue,
4327 : std::vector<std::string> &oRet)
4328 : {
4329 30 : if (!pszXML)
4330 0 : return false;
4331 60 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4332 30 : if (!poTree)
4333 0 : return false;
4334 :
4335 60 : std::string typedOptionName = currentValue;
4336 30 : const auto posEqual = typedOptionName.find('=');
4337 60 : std::string typedValue;
4338 30 : if (posEqual != 0 && posEqual != std::string::npos)
4339 : {
4340 2 : typedValue = currentValue.substr(posEqual + 1);
4341 2 : typedOptionName.resize(posEqual);
4342 : }
4343 :
4344 453 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4345 423 : psChild = psChild->psNext)
4346 : {
4347 436 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4348 449 : if (pszName && typedOptionName == pszName &&
4349 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4350 2 : strcmp(psChild->pszValue, "Argument") == 0))
4351 : {
4352 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4353 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4354 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4355 13 : if (EQUAL(pszType, "string-select"))
4356 : {
4357 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4358 85 : psChild2 = psChild2->psNext)
4359 : {
4360 85 : if (EQUAL(psChild2->pszValue, "Value"))
4361 : {
4362 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4363 : }
4364 : }
4365 : }
4366 8 : else if (EQUAL(pszType, "boolean"))
4367 : {
4368 3 : if (typedValue == "YES" || typedValue == "NO")
4369 : {
4370 1 : oRet.push_back(currentValue);
4371 1 : return true;
4372 : }
4373 2 : oRet.push_back("NO");
4374 2 : oRet.push_back("YES");
4375 : }
4376 5 : else if (EQUAL(pszType, "int"))
4377 : {
4378 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4379 2 : atoi(pszMax) - atoi(pszMin) < 25)
4380 : {
4381 1 : const int nMax = atoi(pszMax);
4382 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4383 12 : oRet.push_back(std::to_string(i));
4384 : }
4385 : }
4386 :
4387 12 : if (oRet.empty())
4388 : {
4389 4 : if (pszMin && pszMax)
4390 : {
4391 1 : oRet.push_back(std::string("##"));
4392 2 : oRet.push_back(std::string("validity range: [")
4393 1 : .append(pszMin)
4394 1 : .append(",")
4395 1 : .append(pszMax)
4396 1 : .append("]"));
4397 : }
4398 3 : else if (pszMin)
4399 : {
4400 1 : oRet.push_back(std::string("##"));
4401 1 : oRet.push_back(
4402 1 : std::string("validity range: >= ").append(pszMin));
4403 : }
4404 2 : else if (pszMax)
4405 : {
4406 1 : oRet.push_back(std::string("##"));
4407 1 : oRet.push_back(
4408 1 : std::string("validity range: <= ").append(pszMax));
4409 : }
4410 1 : else if (const char *pszDescription =
4411 1 : CPLGetXMLValue(psChild, "description", nullptr))
4412 : {
4413 1 : oRet.push_back(std::string("##"));
4414 2 : oRet.push_back(std::string("type: ")
4415 1 : .append(pszType)
4416 1 : .append(", description: ")
4417 1 : .append(pszDescription));
4418 : }
4419 : }
4420 :
4421 12 : return true;
4422 : }
4423 : }
4424 :
4425 367 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4426 350 : psChild = psChild->psNext)
4427 : {
4428 350 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4429 350 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4430 5 : strcmp(psChild->pszValue, "Argument") == 0))
4431 : {
4432 347 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4433 347 : if (!pszScope ||
4434 40 : (EQUAL(pszScope, "raster") &&
4435 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4436 20 : (EQUAL(pszScope, "vector") &&
4437 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4438 : {
4439 327 : oRet.push_back(std::string(pszName).append("="));
4440 : }
4441 : }
4442 : }
4443 :
4444 17 : return false;
4445 : }
4446 :
4447 : /************************************************************************/
4448 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4449 : /************************************************************************/
4450 :
4451 : //! @cond Doxygen_Suppress
4452 : std::vector<std::string>
4453 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4454 : {
4455 2 : std::vector<std::string> oRet;
4456 :
4457 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4458 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4459 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4460 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4461 : {
4462 2 : datasetType = inputArg->GetDatasetType();
4463 : }
4464 :
4465 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4466 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4467 2 : inputFormat->IsExplicitlySet())
4468 : {
4469 : const auto &aosAllowedDrivers =
4470 1 : inputFormat->Get<std::vector<std::string>>();
4471 1 : if (aosAllowedDrivers.size() == 1)
4472 : {
4473 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4474 1 : aosAllowedDrivers[0].c_str());
4475 1 : if (poDriver)
4476 : {
4477 1 : AddOptionsSuggestions(
4478 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4479 : datasetType, currentValue, oRet);
4480 : }
4481 1 : return oRet;
4482 : }
4483 : }
4484 :
4485 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4486 375 : &oRet](const GDALArgDatasetValue &datasetValue)
4487 : {
4488 1 : auto poDM = GetGDALDriverManager();
4489 :
4490 1 : const auto &osDSName = datasetValue.GetName();
4491 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4492 1 : if (!osExt.empty())
4493 : {
4494 1 : std::set<std::string> oVisitedExtensions;
4495 231 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4496 : {
4497 230 : auto poDriver = poDM->GetDriver(i);
4498 690 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4499 230 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4500 72 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4501 460 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4502 72 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4503 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4504 : {
4505 : const char *pszExtensions =
4506 158 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4507 158 : if (pszExtensions)
4508 : {
4509 : const CPLStringList aosExts(
4510 104 : CSLTokenizeString2(pszExtensions, " ", 0));
4511 229 : for (const char *pszExt : cpl::Iterate(aosExts))
4512 : {
4513 129 : if (EQUAL(pszExt, osExt.c_str()) &&
4514 3 : !cpl::contains(oVisitedExtensions, pszExt))
4515 : {
4516 1 : oVisitedExtensions.insert(pszExt);
4517 1 : if (AddOptionsSuggestions(
4518 : poDriver->GetMetadataItem(
4519 1 : GDAL_DMD_OPENOPTIONLIST),
4520 : datasetType, currentValue, oRet))
4521 : {
4522 0 : return;
4523 : }
4524 1 : break;
4525 : }
4526 : }
4527 : }
4528 : }
4529 : }
4530 : }
4531 1 : };
4532 :
4533 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4534 : {
4535 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4536 0 : AddSuggestions(datasetValue);
4537 : }
4538 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4539 : {
4540 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4541 1 : if (datasetValues.size() == 1)
4542 1 : AddSuggestions(datasetValues[0]);
4543 : }
4544 :
4545 1 : return oRet;
4546 : }
4547 :
4548 : //! @endcond
4549 :
4550 : /************************************************************************/
4551 : /* GDALAlgorithm::AddOpenOptionsArg() */
4552 : /************************************************************************/
4553 :
4554 : GDALInConstructionAlgorithmArg &
4555 10288 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4556 : const char *helpMessage)
4557 : {
4558 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4559 20576 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4560 20576 : .AddAlias("oo")
4561 20576 : .SetMetaVar("<KEY>=<VALUE>")
4562 10288 : .SetPackedValuesAllowed(false)
4563 10288 : .SetCategory(GAAC_ADVANCED);
4564 :
4565 31 : arg.AddValidationAction([this, &arg]()
4566 10319 : { return ParseAndValidateKeyValue(arg); });
4567 :
4568 : arg.SetAutoCompleteFunction(
4569 2 : [this](const std::string ¤tValue)
4570 10290 : { return OpenOptionCompleteFunction(currentValue); });
4571 :
4572 10288 : return arg;
4573 : }
4574 :
4575 : /************************************************************************/
4576 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4577 : /************************************************************************/
4578 :
4579 : GDALInConstructionAlgorithmArg &
4580 3660 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4581 : const char *helpMessage)
4582 : {
4583 : auto &arg =
4584 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4585 7320 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4586 7320 : .AddAlias("output-oo")
4587 7320 : .SetMetaVar("<KEY>=<VALUE>")
4588 3660 : .SetPackedValuesAllowed(false)
4589 3660 : .SetCategory(GAAC_ADVANCED);
4590 :
4591 0 : arg.AddValidationAction([this, &arg]()
4592 3660 : { return ParseAndValidateKeyValue(arg); });
4593 :
4594 : arg.SetAutoCompleteFunction(
4595 0 : [this](const std::string ¤tValue)
4596 3660 : { return OpenOptionCompleteFunction(currentValue); });
4597 :
4598 3660 : return arg;
4599 : }
4600 :
4601 : /************************************************************************/
4602 : /* NormalizeRequiredCapability() */
4603 : /************************************************************************/
4604 :
4605 : /** Expands the GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER alias into the
4606 : * generic form of alternatives separated by '|'.
4607 : */
4608 8872 : static std::string NormalizeRequiredCapability(const std::string &osRequiredCap)
4609 : {
4610 8872 : return osRequiredCap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER
4611 : ? GDAL_DCAP_RASTER "|" GDAL_DCAP_MULTIDIM_RASTER
4612 8872 : : osRequiredCap;
4613 : }
4614 :
4615 : /************************************************************************/
4616 : /* DriverHasCapability() */
4617 : /************************************************************************/
4618 :
4619 : /** Returns whether poDriver meets the osRequiredCap requirement, which may
4620 : * express alternatives separated by '|', among the AND-ed list of
4621 : * requirements requiredCaps.
4622 : */
4623 8869 : static bool DriverHasCapability(GDALDriver *poDriver,
4624 : const std::string &osRequiredCap,
4625 : const std::vector<std::string> &requiredCaps)
4626 : {
4627 : const CPLStringList aosAlternatives(CSLTokenizeString2(
4628 17738 : NormalizeRequiredCapability(osRequiredCap).c_str(), "|", 0));
4629 9702 : for (const char *pszCap : cpl::Iterate(aosAlternatives))
4630 : {
4631 8873 : const char *pszVal = poDriver->GetMetadataItem(pszCap);
4632 8873 : if (pszVal && pszVal[0])
4633 : {
4634 8040 : return true;
4635 : }
4636 : // if it supports Create, it supports CreateCopy. GDAL_DCAP_RASTER is
4637 : // matched as a whole entry, i.e. not as one of the '|' alternatives of
4638 : // an entry, which is how all requirement lists spell it.
4639 1421 : else if (EQUAL(pszCap, GDAL_DCAP_CREATECOPY) &&
4640 0 : std::find(requiredCaps.begin(), requiredCaps.end(),
4641 1421 : GDAL_DCAP_RASTER) != requiredCaps.end() &&
4642 4772 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4643 1421 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4644 : {
4645 1097 : return true;
4646 : }
4647 : }
4648 829 : return false;
4649 : }
4650 :
4651 : /************************************************************************/
4652 : /* ValidateFormat() */
4653 : /************************************************************************/
4654 :
4655 5297 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4656 : bool bStreamAllowed,
4657 : bool bGDALGAllowed) const
4658 : {
4659 5297 : if (arg.GetChoices().empty())
4660 : {
4661 : const auto Validate =
4662 23131 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4663 : {
4664 5176 : if (const auto extraFormats =
4665 5176 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4666 : {
4667 60 : for (const auto &extraFormat : *extraFormats)
4668 : {
4669 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4670 14 : return true;
4671 : }
4672 : }
4673 :
4674 5162 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4675 1957 : return true;
4676 :
4677 3213 : if (EQUAL(val.c_str(), "GDALG") &&
4678 8 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4679 : {
4680 4 : if (bGDALGAllowed)
4681 : {
4682 4 : return true;
4683 : }
4684 : else
4685 : {
4686 0 : ReportError(CE_Failure, CPLE_NotSupported,
4687 : "GDALG output is not supported.");
4688 0 : return false;
4689 : }
4690 : }
4691 :
4692 : const auto vrtCompatible =
4693 3201 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4694 536 : if (vrtCompatible && !vrtCompatible->empty() &&
4695 3737 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4696 : {
4697 7 : ReportError(CE_Failure, CPLE_NotSupported,
4698 : "VRT output is not supported.%s",
4699 : bGDALGAllowed
4700 : ? " Consider using the GDALG driver instead "
4701 : "(files with .gdalg.json extension)."
4702 : : "");
4703 7 : return false;
4704 : }
4705 :
4706 : const auto allowedFormats =
4707 3194 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4708 3247 : if (allowedFormats && !allowedFormats->empty() &&
4709 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4710 3247 : val) != allowedFormats->end())
4711 : {
4712 12 : return true;
4713 : }
4714 :
4715 : const auto excludedFormats =
4716 3182 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4717 3229 : if (excludedFormats && !excludedFormats->empty() &&
4718 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4719 3229 : val) != excludedFormats->end())
4720 : {
4721 0 : ReportError(CE_Failure, CPLE_NotSupported,
4722 : "%s output is not supported.", val.c_str());
4723 0 : return false;
4724 : }
4725 :
4726 3182 : auto hDriver = GDALGetDriverByName(val.c_str());
4727 3182 : if (!hDriver)
4728 : {
4729 : auto poMissingDriver =
4730 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4731 4 : if (poMissingDriver)
4732 : {
4733 : const std::string msg =
4734 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4735 0 : ReportError(CE_Failure, CPLE_AppDefined,
4736 : "Invalid value for argument '%s'. Driver '%s' "
4737 : "not found but is known. However plugin %s",
4738 0 : arg.GetName().c_str(), val.c_str(),
4739 : msg.c_str());
4740 : }
4741 : else
4742 : {
4743 8 : ReportError(CE_Failure, CPLE_AppDefined,
4744 : "Invalid value for argument '%s'. Driver '%s' "
4745 : "does not exist.",
4746 4 : arg.GetName().c_str(), val.c_str());
4747 : }
4748 4 : return false;
4749 : }
4750 :
4751 3178 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4752 3178 : if (caps)
4753 : {
4754 3150 : auto poDriver = GDALDriver::FromHandle(hDriver);
4755 9616 : for (const std::string &cap : *caps)
4756 : {
4757 6471 : if (DriverHasCapability(poDriver, cap, *caps))
4758 6466 : continue;
4759 :
4760 5 : if (cap == GDAL_DMD_EXTENSIONS)
4761 : {
4762 2 : ReportError(CE_Failure, CPLE_AppDefined,
4763 : "Invalid value for argument '%s'. Driver "
4764 : "'%s' does not advertise any file format "
4765 : "extension.",
4766 1 : arg.GetName().c_str(), val.c_str());
4767 5 : return false;
4768 : }
4769 4 : else if (cap == GDAL_DCAP_CREATE)
4770 : {
4771 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4772 2 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
4773 1 : updateArg->IsExplicitlySet())
4774 : {
4775 0 : continue;
4776 : }
4777 :
4778 2 : ReportError(CE_Failure, CPLE_AppDefined,
4779 : "Invalid value for argument '%s'. "
4780 : "Driver '%s' does not have write support.",
4781 1 : arg.GetName().c_str(), val.c_str());
4782 1 : return false;
4783 : }
4784 : else
4785 : {
4786 3 : CPLString osCap(NormalizeRequiredCapability(cap));
4787 3 : osCap.replaceAll("|", " or ");
4788 6 : ReportError(CE_Failure, CPLE_AppDefined,
4789 : "Invalid value for argument '%s'. Driver "
4790 : "'%s' does not expose the required '%s' "
4791 : "capability.",
4792 3 : arg.GetName().c_str(), val.c_str(),
4793 : osCap.c_str());
4794 3 : return false;
4795 : }
4796 : }
4797 : }
4798 3173 : return true;
4799 5179 : };
4800 :
4801 5179 : if (arg.GetType() == GAAT_STRING)
4802 : {
4803 5160 : return Validate(arg.Get<std::string>());
4804 : }
4805 22 : else if (arg.GetType() == GAAT_STRING_LIST)
4806 : {
4807 38 : for (const auto &val : arg.Get<std::vector<std::string>>())
4808 : {
4809 19 : if (!Validate(val))
4810 3 : return false;
4811 : }
4812 : }
4813 : }
4814 :
4815 137 : return true;
4816 : }
4817 :
4818 : /************************************************************************/
4819 : /* FormatAutoCompleteFunction() */
4820 : /************************************************************************/
4821 :
4822 : /* static */
4823 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4824 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4825 : {
4826 7 : std::vector<std::string> res;
4827 7 : auto poDM = GetGDALDriverManager();
4828 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4829 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4830 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4831 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4832 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4833 0 : res = std::move(*extraFormats);
4834 1616 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4835 : {
4836 1609 : auto poDriver = poDM->GetDriver(i);
4837 :
4838 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4839 1609 : vrtCompatible->front() == "false" &&
4840 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4841 : {
4842 : // do nothing
4843 : }
4844 1609 : else if (allowedFormats && !allowedFormats->empty() &&
4845 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4846 1609 : poDriver->GetDescription()) != allowedFormats->end())
4847 : {
4848 0 : res.push_back(poDriver->GetDescription());
4849 : }
4850 1609 : else if (excludedFormats && !excludedFormats->empty() &&
4851 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4852 0 : poDriver->GetDescription()) !=
4853 1609 : excludedFormats->end())
4854 : {
4855 0 : continue;
4856 : }
4857 1609 : else if (caps)
4858 : {
4859 1609 : bool ok = true;
4860 3183 : for (const std::string &cap : *caps)
4861 : {
4862 2398 : if (!DriverHasCapability(poDriver, cap, *caps))
4863 : {
4864 824 : ok = false;
4865 824 : break;
4866 : }
4867 : }
4868 1609 : if (ok)
4869 : {
4870 785 : res.push_back(poDriver->GetDescription());
4871 : }
4872 : }
4873 : }
4874 7 : if (bGDALGAllowed)
4875 4 : res.push_back("GDALG");
4876 7 : return res;
4877 : }
4878 :
4879 : /************************************************************************/
4880 : /* GDALAlgorithm::AddInputFormatsArg() */
4881 : /************************************************************************/
4882 :
4883 : GDALInConstructionAlgorithmArg &
4884 10072 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4885 : const char *helpMessage)
4886 : {
4887 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4888 20144 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4889 20144 : .AddAlias("if")
4890 10072 : .SetCategory(GAAC_ADVANCED);
4891 22 : arg.AddValidationAction([this, &arg]()
4892 10094 : { return ValidateFormat(arg, false, false); });
4893 : arg.SetAutoCompleteFunction(
4894 1 : [&arg](const std::string &)
4895 10073 : { return FormatAutoCompleteFunction(arg, false, false); });
4896 10072 : return arg;
4897 : }
4898 :
4899 : /************************************************************************/
4900 : /* GDALAlgorithm::AddOutputFormatArg() */
4901 : /************************************************************************/
4902 :
4903 : GDALInConstructionAlgorithmArg &
4904 10351 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4905 : bool bGDALGAllowed, const char *helpMessage)
4906 : {
4907 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4908 : MsgOrDefault(helpMessage,
4909 : bGDALGAllowed
4910 : ? _("Output format (\"GDALG\" allowed)")
4911 : : _("Output format")),
4912 20702 : pValue)
4913 20702 : .AddAlias("of")
4914 10351 : .AddAlias("format");
4915 : arg.AddValidationAction(
4916 5271 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4917 15622 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4918 : arg.SetAutoCompleteFunction(
4919 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4920 : {
4921 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4922 4 : bGDALGAllowed);
4923 10351 : });
4924 10351 : return arg;
4925 : }
4926 :
4927 : /************************************************************************/
4928 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4929 : /************************************************************************/
4930 : GDALInConstructionAlgorithmArg &
4931 1863 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4932 : const char *helpMessage)
4933 : {
4934 : auto &arg =
4935 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4936 3726 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4937 3726 : .AddAlias("ot")
4938 3726 : .AddAlias("datatype")
4939 5589 : .AddMetadataItem("type", {"GDALDataType"})
4940 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4941 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4942 1863 : "Float32", "Float64", "CFloat32", "CFloat64")
4943 1863 : .SetHiddenChoices("Byte");
4944 1863 : return arg;
4945 : }
4946 :
4947 : /************************************************************************/
4948 : /* GDALAlgorithm::AddNodataArg() */
4949 : /************************************************************************/
4950 :
4951 : GDALInConstructionAlgorithmArg &
4952 809 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4953 : const std::string &optionName,
4954 : const char *helpMessage)
4955 : {
4956 : auto &arg = AddArg(
4957 : optionName, 0,
4958 : MsgOrDefault(helpMessage,
4959 : noneAllowed
4960 : ? _("Assign a specified nodata value to output bands "
4961 : "('none', numeric value, 'nan', 'inf', '-inf')")
4962 : : _("Assign a specified nodata value to output bands "
4963 : "(numeric value, 'nan', 'inf', '-inf')")),
4964 809 : pValue);
4965 : arg.AddValidationAction(
4966 566 : [this, pValue, noneAllowed, optionName]()
4967 : {
4968 119 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4969 : {
4970 109 : char *endptr = nullptr;
4971 109 : CPLStrtod(pValue->c_str(), &endptr);
4972 109 : if (endptr != pValue->c_str() + pValue->size())
4973 : {
4974 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4975 : "Value of '%s' should be %sa "
4976 : "numeric value, 'nan', 'inf' or '-inf'",
4977 : optionName.c_str(),
4978 : noneAllowed ? "'none', " : "");
4979 1 : return false;
4980 : }
4981 : }
4982 118 : return true;
4983 809 : });
4984 809 : return arg;
4985 : }
4986 :
4987 : /************************************************************************/
4988 : /* GDALAlgorithm::AddOutputStringArg() */
4989 : /************************************************************************/
4990 :
4991 : GDALInConstructionAlgorithmArg &
4992 7131 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4993 : {
4994 : return AddArg(
4995 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4996 : MsgOrDefault(helpMessage,
4997 : _("Output string, in which the result is placed")),
4998 14262 : pValue)
4999 7131 : .SetHiddenForCLI()
5000 7131 : .SetIsInput(false)
5001 14262 : .SetIsOutput(true);
5002 : }
5003 :
5004 : /************************************************************************/
5005 : /* GDALAlgorithm::AddStdoutArg() */
5006 : /************************************************************************/
5007 :
5008 : GDALInConstructionAlgorithmArg &
5009 1687 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
5010 : {
5011 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
5012 : MsgOrDefault(helpMessage,
5013 : _("Directly output on stdout. If enabled, "
5014 : "output-string will be empty")),
5015 3374 : pValue)
5016 3374 : .SetHidden();
5017 : }
5018 :
5019 : /************************************************************************/
5020 : /* GDALAlgorithm::AddLayerNameArg() */
5021 : /************************************************************************/
5022 :
5023 : GDALInConstructionAlgorithmArg &
5024 222 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
5025 : {
5026 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5027 222 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5028 : }
5029 :
5030 : /************************************************************************/
5031 : /* GDALAlgorithm::AddArrayNameArg() */
5032 : /************************************************************************/
5033 :
5034 : GDALInConstructionAlgorithmArg &
5035 69 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
5036 : {
5037 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
5038 138 : pValue)
5039 2 : .SetAutoCompleteFunction([this](const std::string &)
5040 140 : { return AutoCompleteArrayName(); });
5041 : }
5042 :
5043 : /************************************************************************/
5044 : /* GDALAlgorithm::AddArrayNameArg() */
5045 : /************************************************************************/
5046 :
5047 : GDALInConstructionAlgorithmArg &
5048 136 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
5049 : const char *helpMessage)
5050 : {
5051 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
5052 272 : pValue)
5053 0 : .SetAutoCompleteFunction([this](const std::string &)
5054 272 : { return AutoCompleteArrayName(); });
5055 : }
5056 :
5057 : /************************************************************************/
5058 : /* GDALAlgorithm::AutoCompleteArrayName() */
5059 : /************************************************************************/
5060 :
5061 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
5062 : {
5063 2 : std::vector<std::string> ret;
5064 4 : std::string osDSName;
5065 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
5066 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
5067 : {
5068 2 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
5069 2 : if (!inputDatasets.empty())
5070 : {
5071 2 : osDSName = inputDatasets[0].GetName();
5072 : }
5073 : }
5074 0 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
5075 : {
5076 0 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
5077 0 : osDSName = inputDataset.GetName();
5078 : }
5079 :
5080 2 : if (!osDSName.empty())
5081 : {
5082 4 : CPLStringList aosAllowedDrivers;
5083 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
5084 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
5085 : aosAllowedDrivers =
5086 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
5087 :
5088 4 : CPLStringList aosOpenOptions;
5089 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
5090 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
5091 : aosOpenOptions =
5092 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
5093 :
5094 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5095 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
5096 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
5097 : {
5098 2 : if (auto poRG = poDS->GetRootGroup())
5099 : {
5100 1 : ret = poRG->GetMDArrayFullNamesRecursive();
5101 : }
5102 : }
5103 : }
5104 :
5105 4 : return ret;
5106 : }
5107 :
5108 : /************************************************************************/
5109 : /* GDALAlgorithm::AddMemorySizeArg() */
5110 : /************************************************************************/
5111 :
5112 : GDALInConstructionAlgorithmArg &
5113 227 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
5114 : const std::string &optionName,
5115 : const char *helpMessage)
5116 : {
5117 454 : return AddArg(optionName, 0, helpMessage, pStrValue)
5118 227 : .SetDefault(*pStrValue)
5119 : .AddValidationAction(
5120 139 : [this, pValue, pStrValue]()
5121 : {
5122 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
5123 : GIntBig nBytes;
5124 : bool bUnitSpecified;
5125 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
5126 47 : &bUnitSpecified) != CE_None)
5127 : {
5128 2 : return false;
5129 : }
5130 45 : if (!bUnitSpecified)
5131 : {
5132 1 : ReportError(CE_Failure, CPLE_AppDefined,
5133 : "Memory size must have a unit or be a "
5134 : "percentage of usable RAM (2GB, 5%%, etc.)");
5135 1 : return false;
5136 : }
5137 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5138 : {
5139 : // -1 to please CoverityScan
5140 : if (static_cast<std::uint64_t>(nBytes) >
5141 : std::numeric_limits<size_t>::max() - 1U)
5142 : {
5143 : ReportError(CE_Failure, CPLE_AppDefined,
5144 : "Memory size %s is too large.",
5145 : pStrValue->c_str());
5146 : return false;
5147 : }
5148 : }
5149 :
5150 44 : *pValue = static_cast<size_t>(nBytes);
5151 44 : return true;
5152 454 : });
5153 : }
5154 :
5155 : /************************************************************************/
5156 : /* GDALAlgorithm::AddOutputLayerNameArg() */
5157 : /************************************************************************/
5158 :
5159 : GDALInConstructionAlgorithmArg &
5160 404 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5161 : const char *helpMessage)
5162 : {
5163 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5164 404 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5165 : }
5166 :
5167 : /************************************************************************/
5168 : /* GDALAlgorithm::AddLayerNameArg() */
5169 : /************************************************************************/
5170 :
5171 : GDALInConstructionAlgorithmArg &
5172 910 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5173 : const char *helpMessage)
5174 : {
5175 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5176 910 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5177 : }
5178 :
5179 : /************************************************************************/
5180 : /* GDALAlgorithm::AddGeometryTypeArg() */
5181 : /************************************************************************/
5182 :
5183 : GDALInConstructionAlgorithmArg &
5184 489 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5185 : {
5186 : return AddArg("geometry-type", 0,
5187 978 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5188 : .SetAutoCompleteFunction(
5189 3 : [](const std::string ¤tValue)
5190 : {
5191 3 : std::vector<std::string> oRet;
5192 51 : for (const char *type :
5193 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5194 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5195 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5196 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5197 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5198 : {
5199 68 : if (currentValue.empty() ||
5200 17 : STARTS_WITH(type, currentValue.c_str()))
5201 : {
5202 35 : oRet.push_back(type);
5203 35 : oRet.push_back(std::string(type).append("Z"));
5204 35 : oRet.push_back(std::string(type).append("M"));
5205 35 : oRet.push_back(std::string(type).append("ZM"));
5206 : }
5207 : }
5208 3 : return oRet;
5209 978 : })
5210 : .AddValidationAction(
5211 123 : [this, pValue]()
5212 : {
5213 112 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5214 120 : wkbUnknown &&
5215 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5216 : {
5217 3 : ReportError(CE_Failure, CPLE_AppDefined,
5218 : "Invalid geometry type '%s'", pValue->c_str());
5219 3 : return false;
5220 : }
5221 109 : return true;
5222 978 : });
5223 : }
5224 :
5225 : /************************************************************************/
5226 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
5227 : /************************************************************************/
5228 :
5229 : /* static */
5230 3402 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5231 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5232 : {
5233 3402 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5234 : datasetArg.GetType() == GAAT_DATASET_LIST);
5235 :
5236 : layerArg.SetAutoCompleteFunction(
5237 18 : [&datasetArg](const std::string ¤tValue)
5238 : {
5239 6 : std::vector<std::string> ret;
5240 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5241 6 : GDALArgDatasetValue *dsVal = nullptr;
5242 6 : if (datasetArg.GetType() == GAAT_DATASET)
5243 : {
5244 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5245 : }
5246 : else
5247 : {
5248 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5249 6 : if (val.size() == 1)
5250 : {
5251 6 : dsVal = &val[0];
5252 : }
5253 : }
5254 6 : if (dsVal && !dsVal->GetName().empty())
5255 : {
5256 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5257 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5258 6 : if (poDS)
5259 : {
5260 12 : for (auto &&poLayer : poDS->GetLayers())
5261 : {
5262 6 : if (currentValue == poLayer->GetDescription())
5263 : {
5264 1 : ret.clear();
5265 1 : ret.push_back(poLayer->GetDescription());
5266 1 : break;
5267 : }
5268 5 : ret.push_back(poLayer->GetDescription());
5269 : }
5270 : }
5271 : }
5272 12 : return ret;
5273 3402 : });
5274 3402 : }
5275 :
5276 : /************************************************************************/
5277 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5278 : /************************************************************************/
5279 :
5280 804 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5281 : GDALInConstructionAlgorithmArg &fieldArg,
5282 : const GDALAlgorithmArg *layerNameArg, bool attributeFields,
5283 : bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg,
5284 : const std::vector<std::string> &extraValues,
5285 : std::function<bool(const OGRFieldDefn *)> filterFn)
5286 : {
5287 :
5288 : fieldArg.SetAutoCompleteFunction(
5289 11 : [&datasetArg, layerNameArg, attributeFields, geometryFields,
5290 : extraValues,
5291 804 : filterFn = std::move(filterFn)](const std::string ¤tValue)
5292 : {
5293 22 : std::set<std::string> ret{};
5294 11 : if (!datasetArg.empty())
5295 : {
5296 18 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5297 :
5298 : const auto getLayerFields =
5299 7 : [&ret, ¤tValue, attributeFields, geometryFields,
5300 87 : &extraValues, &filterFn](const OGRLayer *poLayer)
5301 : {
5302 7 : const auto poDefn = poLayer->GetLayerDefn();
5303 7 : if (attributeFields)
5304 : {
5305 27 : for (const auto poFieldDefn : poDefn->GetFields())
5306 : {
5307 20 : if (filterFn && !filterFn(poFieldDefn))
5308 : {
5309 1 : continue;
5310 : }
5311 :
5312 19 : const char *fieldName = poFieldDefn->GetNameRef();
5313 :
5314 19 : if (currentValue == fieldName)
5315 : {
5316 0 : ret.clear();
5317 0 : ret.insert(fieldName);
5318 0 : break;
5319 : }
5320 19 : ret.insert(fieldName);
5321 : }
5322 : }
5323 7 : if (geometryFields)
5324 : {
5325 2 : for (const auto poFieldDefn : poDefn->GetGeomFields())
5326 : {
5327 1 : const char *fieldName = poFieldDefn->GetNameRef();
5328 1 : if (fieldName[0] == 0)
5329 1 : fieldName = OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME;
5330 1 : if (currentValue == fieldName)
5331 : {
5332 0 : ret.clear();
5333 0 : ret.insert(fieldName);
5334 0 : break;
5335 : }
5336 1 : ret.insert(fieldName);
5337 : }
5338 : }
5339 8 : for (const auto &value : extraValues)
5340 : {
5341 1 : if (currentValue == value)
5342 : {
5343 0 : ret.clear();
5344 0 : ret.insert(value);
5345 0 : break;
5346 : }
5347 1 : ret.insert(value);
5348 : }
5349 7 : };
5350 :
5351 9 : const GDALArgDatasetValue &dsVal = datasetArg[0];
5352 :
5353 9 : if (!dsVal.GetName().empty())
5354 : {
5355 : auto poDS = std::unique_ptr<GDALDataset>(
5356 9 : GDALDataset::Open(dsVal.GetName().c_str(),
5357 18 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5358 9 : if (poDS)
5359 : {
5360 18 : std::vector<std::string> layerNames;
5361 9 : if (layerNameArg && layerNameArg->IsExplicitlySet())
5362 : {
5363 4 : if (layerNameArg->GetType() == GAAT_STRING_LIST)
5364 : {
5365 : layerNames =
5366 : layerNameArg
5367 2 : ->Get<std::vector<std::string>>();
5368 : }
5369 2 : else if (layerNameArg->GetType() == GAAT_STRING)
5370 : {
5371 2 : layerNames.push_back(
5372 2 : layerNameArg->Get<std::string>());
5373 : }
5374 : }
5375 9 : if (layerNames.empty())
5376 : {
5377 : // Loop through all layers
5378 10 : for (const auto *poLayer : poDS->GetLayers())
5379 : {
5380 5 : getLayerFields(poLayer);
5381 : }
5382 : }
5383 : else
5384 : {
5385 8 : for (const std::string &layerName : layerNames)
5386 : {
5387 : const auto poLayer =
5388 4 : poDS->GetLayerByName(layerName.c_str());
5389 4 : if (poLayer)
5390 : {
5391 2 : getLayerFields(poLayer);
5392 : }
5393 : }
5394 : }
5395 : }
5396 : }
5397 : }
5398 11 : std::vector<std::string> retVector(ret.begin(), ret.end());
5399 22 : return retVector;
5400 1608 : });
5401 804 : }
5402 :
5403 : /************************************************************************/
5404 : /* GDALAlgorithm::AddFieldNameArg() */
5405 : /************************************************************************/
5406 :
5407 : GDALInConstructionAlgorithmArg &
5408 138 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5409 : {
5410 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5411 138 : pValue);
5412 : }
5413 :
5414 : /************************************************************************/
5415 : /* GDALAlgorithm::ParseFieldDefinition() */
5416 : /************************************************************************/
5417 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5418 : OGRFieldDefn *poFieldDefn,
5419 : std::string *posError)
5420 : {
5421 : static const std::regex re(
5422 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5423 134 : std::smatch match;
5424 67 : if (std::regex_match(posStrDef, match, re))
5425 : {
5426 132 : const std::string name = match[1];
5427 132 : const std::string type = match[2];
5428 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5429 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5430 66 : poFieldDefn->SetName(name.c_str());
5431 :
5432 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5433 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5434 : {
5435 1 : if (posError)
5436 1 : *posError = "Unsupported field type: " + type;
5437 :
5438 1 : return false;
5439 : }
5440 65 : poFieldDefn->SetType(typeEnum);
5441 65 : poFieldDefn->SetWidth(width);
5442 65 : poFieldDefn->SetPrecision(precision);
5443 65 : return true;
5444 : }
5445 :
5446 1 : if (posError)
5447 : *posError = "Invalid field definition format. Expected "
5448 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5449 :
5450 1 : return false;
5451 : }
5452 :
5453 : /************************************************************************/
5454 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5455 : /************************************************************************/
5456 :
5457 : GDALInConstructionAlgorithmArg &
5458 132 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5459 : std::vector<OGRFieldDefn> *pFieldDefns,
5460 : const char *helpMessage)
5461 : {
5462 : auto &arg =
5463 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5464 264 : pValues)
5465 264 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5466 132 : .SetPackedValuesAllowed(true)
5467 132 : .SetRepeatedArgAllowed(true);
5468 :
5469 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5470 : {
5471 65 : pFieldDefns->clear();
5472 130 : for (const auto &strValue : *pValues)
5473 : {
5474 67 : OGRFieldDefn fieldDefn("", OFTString);
5475 67 : std::string error;
5476 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5477 : &error))
5478 : {
5479 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5480 2 : return false;
5481 : }
5482 : // Check uniqueness of field names
5483 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5484 : {
5485 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5486 : fieldDefn.GetNameRef()))
5487 : {
5488 0 : ReportError(CE_Failure, CPLE_AppDefined,
5489 : "Duplicate field name: '%s'",
5490 : fieldDefn.GetNameRef());
5491 0 : return false;
5492 : }
5493 : }
5494 65 : pFieldDefns->push_back(fieldDefn);
5495 : }
5496 63 : return true;
5497 132 : };
5498 :
5499 132 : arg.AddValidationAction(std::move(validationFunction));
5500 :
5501 132 : return arg;
5502 : }
5503 :
5504 : /************************************************************************/
5505 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5506 : /************************************************************************/
5507 :
5508 276 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5509 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5510 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5511 : {
5512 : auto &arg =
5513 552 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5514 828 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5515 : .SetAutoCompleteFunction(
5516 1 : [](const std::string ¤tValue)
5517 : {
5518 1 : std::vector<std::string> oRet;
5519 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5520 : {
5521 : const char *pszSubType =
5522 5 : OGRFieldDefn::GetFieldSubTypeName(
5523 : static_cast<OGRFieldSubType>(i));
5524 5 : if (pszSubType != nullptr)
5525 : {
5526 5 : if (currentValue.empty() ||
5527 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5528 : {
5529 5 : oRet.push_back(pszSubType);
5530 : }
5531 : }
5532 : }
5533 :
5534 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5535 : {
5536 : // Skip deprecated
5537 14 : if (static_cast<OGRFieldType>(i) ==
5538 13 : OGRFieldType::OFTWideString ||
5539 : static_cast<OGRFieldType>(i) ==
5540 : OGRFieldType::OFTWideStringList)
5541 2 : continue;
5542 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5543 : static_cast<OGRFieldType>(i));
5544 12 : if (pszType != nullptr)
5545 : {
5546 12 : if (currentValue.empty() ||
5547 0 : STARTS_WITH(pszType, currentValue.c_str()))
5548 : {
5549 12 : oRet.push_back(pszType);
5550 : }
5551 : }
5552 : }
5553 1 : return oRet;
5554 276 : });
5555 :
5556 : auto validationFunction =
5557 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5558 : {
5559 120 : bool isValid{true};
5560 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5561 :
5562 : // String is returned for unknown types
5563 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5564 : {
5565 16 : isValid = false;
5566 : }
5567 :
5568 120 : *pSubtypeValue =
5569 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5570 :
5571 120 : if (*pSubtypeValue != OFSTNone)
5572 : {
5573 15 : isValid = true;
5574 15 : switch (*pSubtypeValue)
5575 : {
5576 6 : case OFSTBoolean:
5577 : case OFSTInt16:
5578 : {
5579 6 : *pTypeValue = OFTInteger;
5580 6 : break;
5581 : }
5582 3 : case OFSTFloat32:
5583 : {
5584 3 : *pTypeValue = OFTReal;
5585 3 : break;
5586 : }
5587 6 : default:
5588 : {
5589 6 : *pTypeValue = OFTString;
5590 6 : break;
5591 : }
5592 : }
5593 : }
5594 :
5595 120 : if (!isValid)
5596 : {
5597 2 : ReportError(CE_Failure, CPLE_AppDefined,
5598 : "Invalid value for argument '%s': '%s'",
5599 1 : arg.GetName().c_str(), pStrValue->c_str());
5600 : }
5601 :
5602 120 : return isValid;
5603 276 : };
5604 :
5605 276 : if (!pStrValue->empty())
5606 : {
5607 0 : arg.SetDefault(*pStrValue);
5608 0 : validationFunction();
5609 : }
5610 :
5611 276 : arg.AddValidationAction(std::move(validationFunction));
5612 :
5613 276 : return arg;
5614 : }
5615 :
5616 : /************************************************************************/
5617 : /* GDALAlgorithm::ValidateBandArg() */
5618 : /************************************************************************/
5619 :
5620 4920 : bool GDALAlgorithm::ValidateBandArg() const
5621 : {
5622 4920 : bool ret = true;
5623 4920 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5624 4920 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5625 1768 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5626 344 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5627 6682 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5628 175 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5629 : {
5630 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5631 : {
5632 99 : if (nBand > poDS->GetRasterCount())
5633 : {
5634 5 : ReportError(CE_Failure, CPLE_AppDefined,
5635 : "Value of 'band' should be greater or equal than "
5636 : "1 and less or equal than %d.",
5637 : poDS->GetRasterCount());
5638 5 : return false;
5639 : }
5640 94 : return true;
5641 118 : };
5642 :
5643 : const auto ValidateForOneDataset =
5644 356 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5645 : {
5646 113 : bool l_ret = true;
5647 113 : if (bandArg->GetType() == GAAT_INTEGER)
5648 : {
5649 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5650 : }
5651 89 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5652 : {
5653 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5654 : {
5655 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5656 : }
5657 : }
5658 113 : return l_ret;
5659 118 : };
5660 :
5661 118 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5662 : {
5663 : auto poDS =
5664 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5665 6 : if (poDS && !ValidateForOneDataset(poDS))
5666 2 : ret = false;
5667 : }
5668 : else
5669 : {
5670 112 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5671 111 : for (auto &datasetValue :
5672 334 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5673 : {
5674 111 : auto poDS = datasetValue.GetDatasetRef();
5675 111 : if (poDS && !ValidateForOneDataset(poDS))
5676 3 : ret = false;
5677 : }
5678 : }
5679 : }
5680 4920 : return ret;
5681 : }
5682 :
5683 : /************************************************************************/
5684 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5685 : /************************************************************************/
5686 :
5687 3955 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5688 : {
5689 3955 : return ValidateBandArg();
5690 : }
5691 :
5692 : /************************************************************************/
5693 : /* GDALAlgorithm::AddBandArg() */
5694 : /************************************************************************/
5695 :
5696 : GDALInConstructionAlgorithmArg &
5697 1728 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5698 : {
5699 2202 : AddValidationAction([this]() { return ValidateBandArg(); });
5700 :
5701 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5702 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5703 3456 : pValue)
5704 : .AddValidationAction(
5705 34 : [pValue]()
5706 : {
5707 34 : if (*pValue <= 0)
5708 : {
5709 1 : CPLError(CE_Failure, CPLE_AppDefined,
5710 : "Value of 'band' should greater or equal to 1.");
5711 1 : return false;
5712 : }
5713 33 : return true;
5714 3456 : });
5715 : }
5716 :
5717 : /************************************************************************/
5718 : /* GDALAlgorithm::AddBandArg() */
5719 : /************************************************************************/
5720 :
5721 : GDALInConstructionAlgorithmArg &
5722 879 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5723 : {
5724 1370 : AddValidationAction([this]() { return ValidateBandArg(); });
5725 :
5726 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5727 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5728 1758 : pValue)
5729 : .AddValidationAction(
5730 126 : [pValue]()
5731 : {
5732 397 : for (int val : *pValue)
5733 : {
5734 272 : if (val <= 0)
5735 : {
5736 1 : CPLError(CE_Failure, CPLE_AppDefined,
5737 : "Value of 'band' should greater or equal "
5738 : "to 1.");
5739 1 : return false;
5740 : }
5741 : }
5742 125 : return true;
5743 1758 : });
5744 : }
5745 :
5746 : /************************************************************************/
5747 : /* ParseAndValidateKeyValue() */
5748 : /************************************************************************/
5749 :
5750 589 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5751 : {
5752 555 : const auto Validate = [this, &arg](const std::string &val)
5753 : {
5754 550 : if (val.find('=') == std::string::npos)
5755 : {
5756 5 : ReportError(
5757 : CE_Failure, CPLE_AppDefined,
5758 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5759 5 : arg.GetName().c_str());
5760 5 : return false;
5761 : }
5762 :
5763 545 : return true;
5764 589 : };
5765 :
5766 589 : if (arg.GetType() == GAAT_STRING)
5767 : {
5768 0 : return Validate(arg.Get<std::string>());
5769 : }
5770 589 : else if (arg.GetType() == GAAT_STRING_LIST)
5771 : {
5772 589 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5773 589 : if (vals.size() == 1)
5774 : {
5775 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5776 968 : std::vector<std::string> newVals;
5777 968 : std::string curToken;
5778 484 : bool canSplitOnComma = true;
5779 484 : char lastSep = 0;
5780 484 : bool inString = false;
5781 484 : bool equalFoundInLastToken = false;
5782 7315 : for (char c : vals[0])
5783 : {
5784 6835 : if (!inString && c == ',')
5785 : {
5786 10 : if (lastSep != '=' || !equalFoundInLastToken)
5787 : {
5788 2 : canSplitOnComma = false;
5789 2 : break;
5790 : }
5791 8 : lastSep = c;
5792 8 : newVals.push_back(curToken);
5793 8 : curToken.clear();
5794 8 : equalFoundInLastToken = false;
5795 : }
5796 6825 : else if (!inString && c == '=')
5797 : {
5798 483 : if (lastSep == '=')
5799 : {
5800 2 : canSplitOnComma = false;
5801 2 : break;
5802 : }
5803 481 : equalFoundInLastToken = true;
5804 481 : lastSep = c;
5805 481 : curToken += c;
5806 : }
5807 6342 : else if (c == '"')
5808 : {
5809 4 : inString = !inString;
5810 4 : curToken += c;
5811 : }
5812 : else
5813 : {
5814 6338 : curToken += c;
5815 : }
5816 : }
5817 484 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5818 : {
5819 471 : if (!curToken.empty())
5820 471 : newVals.emplace_back(std::move(curToken));
5821 471 : vals = std::move(newVals);
5822 : }
5823 : }
5824 :
5825 1134 : for (const auto &val : vals)
5826 : {
5827 550 : if (!Validate(val))
5828 5 : return false;
5829 : }
5830 : }
5831 :
5832 584 : return true;
5833 : }
5834 :
5835 : /************************************************************************/
5836 : /* IsGDALGOutput() */
5837 : /************************************************************************/
5838 :
5839 2675 : bool GDALAlgorithm::IsGDALGOutput() const
5840 : {
5841 2675 : bool isGDALGOutput = false;
5842 2675 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5843 2675 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5844 4620 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5845 1945 : outputArg->IsExplicitlySet())
5846 : {
5847 3813 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5848 1894 : outputFormatArg->IsExplicitlySet())
5849 : {
5850 : const auto &val =
5851 1186 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5852 1186 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5853 : }
5854 : else
5855 : {
5856 : const auto &filename =
5857 733 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5858 733 : isGDALGOutput =
5859 1437 : filename.GetName().size() > strlen(".gdalg.json") &&
5860 704 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5861 : strlen(".gdalg.json"),
5862 : ".gdalg.json");
5863 : }
5864 : }
5865 2675 : return isGDALGOutput;
5866 : }
5867 :
5868 : /************************************************************************/
5869 : /* ProcessGDALGOutput() */
5870 : /************************************************************************/
5871 :
5872 2797 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5873 : {
5874 2797 : if (!SupportsStreamedOutput())
5875 732 : return ProcessGDALGOutputRet::NOT_GDALG;
5876 :
5877 2065 : if (IsGDALGOutput())
5878 : {
5879 12 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5880 : const auto &filename =
5881 12 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5882 : VSIStatBufL sStat;
5883 12 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5884 : {
5885 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5886 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5887 : {
5888 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5889 : {
5890 0 : CPLError(CE_Failure, CPLE_AppDefined,
5891 : "File '%s' already exists. Specify the "
5892 : "--overwrite option to overwrite it.",
5893 : filename.c_str());
5894 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5895 : }
5896 : }
5897 : }
5898 :
5899 24 : std::string osCommandLine;
5900 :
5901 48 : for (const auto &path : GDALAlgorithm::m_callPath)
5902 : {
5903 36 : if (!osCommandLine.empty())
5904 24 : osCommandLine += ' ';
5905 36 : osCommandLine += path;
5906 : }
5907 :
5908 278 : for (const auto &arg : GetArgs())
5909 : {
5910 296 : if (arg->IsExplicitlySet() &&
5911 48 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5912 35 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5913 313 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5914 17 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5915 : {
5916 16 : osCommandLine += ' ';
5917 16 : std::string strArg;
5918 16 : if (!arg->Serialize(strArg))
5919 : {
5920 0 : CPLError(CE_Failure, CPLE_AppDefined,
5921 : "Cannot serialize argument %s",
5922 0 : arg->GetName().c_str());
5923 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5924 : }
5925 16 : osCommandLine += strArg;
5926 : }
5927 : }
5928 :
5929 12 : osCommandLine += " --output-format stream --output streamed_dataset";
5930 :
5931 12 : std::string outStringUnused;
5932 12 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5933 12 : ? ProcessGDALGOutputRet::GDALG_OK
5934 12 : : ProcessGDALGOutputRet::GDALG_ERROR;
5935 : }
5936 :
5937 2053 : return ProcessGDALGOutputRet::NOT_GDALG;
5938 : }
5939 :
5940 : /************************************************************************/
5941 : /* GDALAlgorithm::SaveGDALG() */
5942 : /************************************************************************/
5943 :
5944 24 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5945 : std::string &outString,
5946 : const std::string &commandLine)
5947 : {
5948 48 : CPLJSONDocument oDoc;
5949 24 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5950 24 : oDoc.GetRoot().Add("command_line", commandLine);
5951 24 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5952 :
5953 24 : if (!filename.empty())
5954 23 : return oDoc.Save(filename);
5955 :
5956 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5957 1 : return true;
5958 : }
5959 :
5960 : /************************************************************************/
5961 : /* GDALAlgorithm::AddCreationOptionsArg() */
5962 : /************************************************************************/
5963 :
5964 : GDALInConstructionAlgorithmArg &
5965 9027 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5966 : const char *helpMessage)
5967 : {
5968 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5969 18054 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5970 18054 : .AddAlias("co")
5971 18054 : .SetMetaVar("<KEY>=<VALUE>")
5972 9027 : .SetPackedValuesAllowed(false);
5973 292 : arg.AddValidationAction([this, &arg]()
5974 9319 : { return ParseAndValidateKeyValue(arg); });
5975 :
5976 : arg.SetAutoCompleteFunction(
5977 51 : [this](const std::string ¤tValue)
5978 : {
5979 17 : std::vector<std::string> oRet;
5980 :
5981 17 : int datasetType =
5982 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5983 17 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5984 17 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5985 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5986 : {
5987 17 : datasetType = outputArg->GetDatasetType();
5988 : }
5989 :
5990 17 : const char *pszMDCreationOptionList =
5991 : (datasetType == GDAL_OF_MULTIDIM_RASTER)
5992 17 : ? GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST
5993 : : GDAL_DMD_CREATIONOPTIONLIST;
5994 :
5995 17 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5996 34 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5997 17 : outputFormat->IsExplicitlySet())
5998 : {
5999 14 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
6000 7 : outputFormat->Get<std::string>().c_str());
6001 7 : if (poDriver)
6002 : {
6003 7 : AddOptionsSuggestions(
6004 7 : poDriver->GetMetadataItem(pszMDCreationOptionList),
6005 : datasetType, currentValue, oRet);
6006 : }
6007 7 : return oRet;
6008 : }
6009 :
6010 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
6011 : {
6012 10 : auto poDM = GetGDALDriverManager();
6013 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6014 10 : const auto &osDSName = datasetValue.GetName();
6015 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6016 10 : if (!osExt.empty())
6017 : {
6018 10 : std::set<std::string> oVisitedExtensions;
6019 721 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
6020 : {
6021 718 : auto poDriver = poDM->GetDriver(i);
6022 2154 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
6023 718 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
6024 216 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
6025 1436 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
6026 216 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
6027 0 : poDriver->GetMetadataItem(
6028 0 : GDAL_DCAP_MULTIDIM_RASTER)))
6029 : {
6030 : const char *pszExtensions =
6031 502 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6032 502 : if (pszExtensions)
6033 : {
6034 : const CPLStringList aosExts(
6035 326 : CSLTokenizeString2(pszExtensions, " ", 0));
6036 722 : for (const char *pszExt : cpl::Iterate(aosExts))
6037 : {
6038 422 : if (EQUAL(pszExt, osExt.c_str()) &&
6039 16 : !cpl::contains(oVisitedExtensions,
6040 : pszExt))
6041 : {
6042 10 : oVisitedExtensions.insert(pszExt);
6043 10 : if (AddOptionsSuggestions(
6044 : poDriver->GetMetadataItem(
6045 10 : pszMDCreationOptionList),
6046 : datasetType, currentValue,
6047 : oRet))
6048 : {
6049 7 : return oRet;
6050 : }
6051 3 : break;
6052 : }
6053 : }
6054 : }
6055 : }
6056 : }
6057 : }
6058 : }
6059 :
6060 3 : return oRet;
6061 9027 : });
6062 :
6063 9027 : return arg;
6064 : }
6065 :
6066 : /************************************************************************/
6067 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
6068 : /************************************************************************/
6069 :
6070 : GDALInConstructionAlgorithmArg &
6071 4302 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
6072 : const char *helpMessage)
6073 : {
6074 : auto &arg =
6075 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
6076 8604 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
6077 8604 : .AddAlias("lco")
6078 8604 : .SetMetaVar("<KEY>=<VALUE>")
6079 4302 : .SetPackedValuesAllowed(false);
6080 76 : arg.AddValidationAction([this, &arg]()
6081 4378 : { return ParseAndValidateKeyValue(arg); });
6082 :
6083 : arg.SetAutoCompleteFunction(
6084 5 : [this](const std::string ¤tValue)
6085 : {
6086 2 : std::vector<std::string> oRet;
6087 :
6088 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6089 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
6090 2 : outputFormat->IsExplicitlySet())
6091 : {
6092 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
6093 1 : outputFormat->Get<std::string>().c_str());
6094 1 : if (poDriver)
6095 : {
6096 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
6097 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6098 : GDAL_OF_VECTOR, currentValue, oRet);
6099 : }
6100 1 : return oRet;
6101 : }
6102 :
6103 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
6104 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
6105 : {
6106 1 : auto poDM = GetGDALDriverManager();
6107 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6108 1 : const auto &osDSName = datasetValue.GetName();
6109 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6110 1 : if (!osExt.empty())
6111 : {
6112 1 : std::set<std::string> oVisitedExtensions;
6113 231 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
6114 : {
6115 230 : auto poDriver = poDM->GetDriver(i);
6116 230 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
6117 : {
6118 : const char *pszExtensions =
6119 91 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6120 91 : if (pszExtensions)
6121 : {
6122 : const CPLStringList aosExts(
6123 62 : CSLTokenizeString2(pszExtensions, " ", 0));
6124 156 : for (const char *pszExt : cpl::Iterate(aosExts))
6125 : {
6126 96 : if (EQUAL(pszExt, osExt.c_str()) &&
6127 1 : !cpl::contains(oVisitedExtensions,
6128 : pszExt))
6129 : {
6130 1 : oVisitedExtensions.insert(pszExt);
6131 1 : if (AddOptionsSuggestions(
6132 : poDriver->GetMetadataItem(
6133 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6134 : GDAL_OF_VECTOR, currentValue,
6135 : oRet))
6136 : {
6137 0 : return oRet;
6138 : }
6139 1 : break;
6140 : }
6141 : }
6142 : }
6143 : }
6144 : }
6145 : }
6146 : }
6147 :
6148 1 : return oRet;
6149 4302 : });
6150 :
6151 4302 : return arg;
6152 : }
6153 :
6154 : /************************************************************************/
6155 : /* GDALAlgorithm::AddBBOXArg() */
6156 : /************************************************************************/
6157 :
6158 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
6159 : GDALInConstructionAlgorithmArg &
6160 1985 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
6161 : {
6162 : auto &arg = AddArg("bbox", 0,
6163 : MsgOrDefault(helpMessage,
6164 : _("Bounding box as xmin,ymin,xmax,ymax")),
6165 3970 : pValue)
6166 1985 : .SetRepeatedArgAllowed(false)
6167 1985 : .SetMinCount(4)
6168 1985 : .SetMaxCount(4)
6169 1985 : .SetDisplayHintAboutRepetition(false);
6170 : arg.AddValidationAction(
6171 247 : [&arg]()
6172 : {
6173 247 : const auto &val = arg.Get<std::vector<double>>();
6174 247 : CPLAssert(val.size() == 4);
6175 247 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
6176 : {
6177 5 : CPLError(CE_Failure, CPLE_AppDefined,
6178 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
6179 : "xmin <= xmax and ymin <= ymax");
6180 5 : return false;
6181 : }
6182 242 : return true;
6183 1985 : });
6184 1985 : return arg;
6185 : }
6186 :
6187 : /************************************************************************/
6188 : /* GDALAlgorithm::AddActiveLayerArg() */
6189 : /************************************************************************/
6190 :
6191 : GDALInConstructionAlgorithmArg &
6192 1962 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6193 : {
6194 : return AddArg("active-layer", 0,
6195 : MsgOrDefault(helpMessage,
6196 : _("Set active layer (if not specified, all)")),
6197 1962 : pValue);
6198 : }
6199 :
6200 : /************************************************************************/
6201 : /* GDALAlgorithm::AddNumThreadsArg() */
6202 : /************************************************************************/
6203 :
6204 : GDALInConstructionAlgorithmArg &
6205 730 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6206 : const char *helpMessage)
6207 : {
6208 : auto &arg =
6209 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6210 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6211 730 : pStrValue);
6212 :
6213 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6214 1460 : _("Number of jobs (read-only, hidden argument)"), pValue)
6215 730 : .SetHidden();
6216 :
6217 2742 : auto lambda = [this, &arg, pValue, pStrValue]
6218 : {
6219 914 : bool bOK = false;
6220 914 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6221 : const int nLimit = std::clamp(
6222 914 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6223 1828 : CPLGetNumCPUs());
6224 : const int nNumThreads =
6225 914 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
6226 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6227 914 : if (bOK)
6228 : {
6229 914 : *pValue = nNumThreads;
6230 : }
6231 : else
6232 : {
6233 0 : ReportError(CE_Failure, CPLE_IllegalArg,
6234 : "Invalid value for '%s' argument",
6235 0 : arg.GetName().c_str());
6236 : }
6237 914 : return bOK;
6238 730 : };
6239 730 : if (!pStrValue->empty())
6240 : {
6241 683 : arg.SetDefault(*pStrValue);
6242 683 : lambda();
6243 : }
6244 730 : arg.AddValidationAction(std::move(lambda));
6245 730 : return arg;
6246 : }
6247 :
6248 : /************************************************************************/
6249 : /* GDALAlgorithm::AddAbsolutePathArg() */
6250 : /************************************************************************/
6251 :
6252 : GDALInConstructionAlgorithmArg &
6253 631 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6254 : {
6255 : return AddArg(
6256 : "absolute-path", 0,
6257 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6258 : "should be stored as an absolute path")),
6259 631 : pValue);
6260 : }
6261 :
6262 : /************************************************************************/
6263 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
6264 : /************************************************************************/
6265 :
6266 : GDALInConstructionAlgorithmArg &
6267 139 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6268 : const char *helpMessage)
6269 : {
6270 :
6271 : const auto pixelFunctionNames =
6272 139 : VRTDerivedRasterBand::GetPixelFunctionNames();
6273 : return AddArg(
6274 : "pixel-function", 0,
6275 : MsgOrDefault(
6276 : helpMessage,
6277 : _("Specify a pixel function to calculate output value from "
6278 : "overlapping inputs")),
6279 278 : pValue)
6280 278 : .SetChoices(pixelFunctionNames);
6281 : }
6282 :
6283 : /************************************************************************/
6284 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
6285 : /************************************************************************/
6286 :
6287 : GDALInConstructionAlgorithmArg &
6288 139 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6289 : const char *helpMessage)
6290 : {
6291 : auto &pixelFunctionArgArg =
6292 : AddArg("pixel-function-arg", 0,
6293 : MsgOrDefault(
6294 : helpMessage,
6295 : _("Specify argument(s) to pass to the pixel function")),
6296 278 : pValue)
6297 278 : .SetMetaVar("<NAME>=<VALUE>")
6298 139 : .SetRepeatedArgAllowed(true);
6299 : pixelFunctionArgArg.AddValidationAction(
6300 7 : [this, &pixelFunctionArgArg]()
6301 146 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6302 :
6303 : pixelFunctionArgArg.SetAutoCompleteFunction(
6304 12 : [this](const std::string ¤tValue)
6305 : {
6306 12 : std::string pixelFunction;
6307 6 : const auto pixelFunctionArg = GetArg("pixel-function");
6308 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6309 : {
6310 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
6311 : }
6312 :
6313 6 : std::vector<std::string> ret;
6314 :
6315 6 : if (!pixelFunction.empty())
6316 : {
6317 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6318 : pixelFunction.c_str());
6319 5 : if (!pair)
6320 : {
6321 1 : ret.push_back("**");
6322 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6323 1 : ret.push_back(std::string("\xC2\xA0"
6324 : "Invalid pixel function name"));
6325 : }
6326 4 : else if (pair->second.find("Argument name=") ==
6327 : std::string::npos)
6328 : {
6329 1 : ret.push_back("**");
6330 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6331 1 : ret.push_back(
6332 2 : std::string(
6333 : "\xC2\xA0"
6334 : "No pixel function arguments for pixel function '")
6335 1 : .append(pixelFunction)
6336 1 : .append("'"));
6337 : }
6338 : else
6339 : {
6340 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6341 : ret);
6342 : }
6343 : }
6344 :
6345 12 : return ret;
6346 139 : });
6347 :
6348 139 : return pixelFunctionArgArg;
6349 : }
6350 :
6351 : /************************************************************************/
6352 : /* GDALAlgorithm::AddProgressArg() */
6353 : /************************************************************************/
6354 :
6355 10937 : void GDALAlgorithm::AddProgressArg(bool hidden)
6356 : {
6357 : auto &arg =
6358 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6359 21874 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6360 10937 : .SetAvailableInPipelineStep(false)
6361 21874 : .SetCategory(GAAC_COMMON)
6362 10937 : .AddAction([this]() { m_progressBarRequested = false; });
6363 10937 : if (hidden)
6364 2157 : arg.SetHidden();
6365 :
6366 21874 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6367 10937 : .SetAvailableInPipelineStep(false)
6368 10937 : .SetHidden();
6369 10937 : }
6370 :
6371 : /************************************************************************/
6372 : /* GDALAlgorithm::Run() */
6373 : /************************************************************************/
6374 :
6375 5392 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6376 : {
6377 5392 : WarnIfDeprecated();
6378 :
6379 5392 : if (m_selectedSubAlg)
6380 : {
6381 464 : if (m_calledFromCommandLine)
6382 276 : m_selectedSubAlg->m_calledFromCommandLine = true;
6383 464 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6384 : }
6385 :
6386 4928 : if (m_helpRequested || m_helpDocRequested)
6387 : {
6388 19 : if (m_calledFromCommandLine)
6389 19 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6390 19 : return true;
6391 : }
6392 :
6393 4909 : if (m_JSONUsageRequested)
6394 : {
6395 3 : if (m_calledFromCommandLine)
6396 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6397 3 : return true;
6398 : }
6399 :
6400 4906 : if (!ValidateArguments())
6401 127 : return false;
6402 :
6403 4779 : if (m_alreadyRun)
6404 : {
6405 3 : ReportError(CE_Failure, CPLE_AppDefined,
6406 : "Run() can be called only once per algorithm instance");
6407 3 : return false;
6408 : }
6409 4776 : m_alreadyRun = true;
6410 :
6411 4776 : switch (ProcessGDALGOutput())
6412 : {
6413 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6414 0 : return false;
6415 :
6416 12 : case ProcessGDALGOutputRet::GDALG_OK:
6417 12 : return true;
6418 :
6419 4764 : case ProcessGDALGOutputRet::NOT_GDALG:
6420 4764 : break;
6421 : }
6422 :
6423 4764 : if (m_executionForStreamOutput)
6424 : {
6425 98 : if (!CheckSafeForStreamOutput())
6426 : {
6427 4 : return false;
6428 : }
6429 : }
6430 :
6431 4760 : return RunImpl(pfnProgress, pProgressData);
6432 : }
6433 :
6434 : /************************************************************************/
6435 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6436 : /************************************************************************/
6437 :
6438 50 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6439 : {
6440 50 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6441 50 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6442 : {
6443 50 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6444 50 : if (!EQUAL(val.c_str(), "stream"))
6445 : {
6446 : // For security reasons, to avoid that reading a .gdalg.json file
6447 : // writes a file on the file system.
6448 4 : ReportError(
6449 : CE_Failure, CPLE_NotSupported,
6450 : "in streamed execution, --format stream should be used");
6451 4 : return false;
6452 : }
6453 : }
6454 46 : return true;
6455 : }
6456 :
6457 : /************************************************************************/
6458 : /* GDALAlgorithm::Finalize() */
6459 : /************************************************************************/
6460 :
6461 2066 : bool GDALAlgorithm::Finalize()
6462 : {
6463 2066 : bool ret = true;
6464 2066 : if (m_selectedSubAlg)
6465 282 : ret = m_selectedSubAlg->Finalize();
6466 :
6467 37617 : for (auto &arg : m_args)
6468 : {
6469 35551 : if (arg->GetType() == GAAT_DATASET)
6470 : {
6471 1622 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6472 : }
6473 33929 : else if (arg->GetType() == GAAT_DATASET_LIST)
6474 : {
6475 3256 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6476 : {
6477 1530 : ret = ds.Close() && ret;
6478 : }
6479 : }
6480 : }
6481 2066 : return ret;
6482 : }
6483 :
6484 : /************************************************************************/
6485 : /* GDALAlgorithm::GetArgNamesForCLI() */
6486 : /************************************************************************/
6487 :
6488 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6489 735 : GDALAlgorithm::GetArgNamesForCLI() const
6490 : {
6491 1470 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6492 :
6493 735 : size_t maxOptLen = 0;
6494 9349 : for (const auto &arg : m_args)
6495 : {
6496 8614 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6497 1814 : continue;
6498 6800 : std::string opt;
6499 6800 : bool addComma = false;
6500 6800 : if (!arg->GetShortName().empty())
6501 : {
6502 1443 : opt += '-';
6503 1443 : opt += arg->GetShortName();
6504 1443 : addComma = true;
6505 : }
6506 6800 : for (char alias : arg->GetShortNameAliases())
6507 : {
6508 0 : if (addComma)
6509 0 : opt += ", ";
6510 0 : opt += "-";
6511 0 : opt += alias;
6512 0 : addComma = true;
6513 : }
6514 7554 : for (const std::string &alias : arg->GetAliases())
6515 : {
6516 754 : if (addComma)
6517 326 : opt += ", ";
6518 754 : opt += "--";
6519 754 : opt += alias;
6520 754 : addComma = true;
6521 : }
6522 6800 : if (!arg->GetName().empty())
6523 : {
6524 6800 : if (addComma)
6525 1871 : opt += ", ";
6526 6800 : opt += "--";
6527 6800 : opt += arg->GetName();
6528 : }
6529 6800 : const auto &metaVar = arg->GetMetaVar();
6530 6800 : if (!metaVar.empty())
6531 : {
6532 4253 : opt += ' ';
6533 4253 : if (metaVar.front() != '<')
6534 3083 : opt += '<';
6535 4253 : opt += metaVar;
6536 4253 : if (metaVar.back() != '>')
6537 3077 : opt += '>';
6538 : }
6539 6800 : maxOptLen = std::max(maxOptLen, opt.size());
6540 6800 : options.emplace_back(arg.get(), opt);
6541 : }
6542 :
6543 1470 : return std::make_pair(std::move(options), maxOptLen);
6544 : }
6545 :
6546 : /************************************************************************/
6547 : /* GDALAlgorithm::GetUsageForCLI() */
6548 : /************************************************************************/
6549 :
6550 : std::string
6551 437 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6552 : const UsageOptions &usageOptions) const
6553 : {
6554 437 : if (m_selectedSubAlg)
6555 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6556 :
6557 860 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6558 860 : std::string osPath;
6559 867 : for (const std::string &s : m_callPath)
6560 : {
6561 437 : if (!osPath.empty())
6562 53 : osPath += ' ';
6563 437 : osPath += s;
6564 : }
6565 430 : osRet += ' ';
6566 430 : osRet += osPath;
6567 :
6568 430 : bool hasNonPositionals = false;
6569 5425 : for (const auto &arg : m_args)
6570 : {
6571 4995 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6572 3615 : hasNonPositionals = true;
6573 : }
6574 :
6575 430 : if (HasSubAlgorithms())
6576 : {
6577 10 : if (m_callPath.size() == 1)
6578 : {
6579 9 : osRet += " <COMMAND>";
6580 9 : if (hasNonPositionals)
6581 9 : osRet += " [OPTIONS]";
6582 9 : if (usageOptions.isPipelineStep)
6583 : {
6584 5 : const size_t nLenFirstLine = osRet.size();
6585 5 : osRet += '\n';
6586 5 : osRet.append(nLenFirstLine, '-');
6587 5 : osRet += '\n';
6588 : }
6589 9 : osRet += "\nwhere <COMMAND> is one of:\n";
6590 : }
6591 : else
6592 : {
6593 1 : osRet += " <SUBCOMMAND>";
6594 1 : if (hasNonPositionals)
6595 1 : osRet += " [OPTIONS]";
6596 1 : if (usageOptions.isPipelineStep)
6597 : {
6598 0 : const size_t nLenFirstLine = osRet.size();
6599 0 : osRet += '\n';
6600 0 : osRet.append(nLenFirstLine, '-');
6601 0 : osRet += '\n';
6602 : }
6603 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6604 : }
6605 10 : size_t maxNameLen = 0;
6606 62 : for (const auto &subAlgName : GetSubAlgorithmNames())
6607 : {
6608 52 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6609 : }
6610 62 : for (const auto &subAlgName : GetSubAlgorithmNames())
6611 : {
6612 104 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6613 52 : if (subAlg && !subAlg->IsHidden())
6614 : {
6615 52 : const std::string &name(subAlg->GetName());
6616 52 : osRet += " - ";
6617 52 : osRet += name;
6618 52 : osRet += ": ";
6619 52 : osRet.append(maxNameLen - name.size(), ' ');
6620 52 : osRet += subAlg->GetDescription();
6621 52 : if (!subAlg->m_aliases.empty())
6622 : {
6623 8 : bool first = true;
6624 8 : for (const auto &alias : subAlg->GetAliases())
6625 : {
6626 8 : if (alias ==
6627 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6628 8 : break;
6629 0 : if (first)
6630 0 : osRet += " (alias: ";
6631 : else
6632 0 : osRet += ", ";
6633 0 : osRet += alias;
6634 0 : first = false;
6635 : }
6636 8 : if (!first)
6637 : {
6638 0 : osRet += ')';
6639 : }
6640 : }
6641 52 : osRet += '\n';
6642 : }
6643 : }
6644 :
6645 10 : if (shortUsage && hasNonPositionals)
6646 : {
6647 3 : osRet += "\nTry '";
6648 3 : osRet += osPath;
6649 3 : osRet += " --help' for help.\n";
6650 : }
6651 : }
6652 : else
6653 : {
6654 420 : if (!m_args.empty())
6655 : {
6656 420 : if (hasNonPositionals)
6657 420 : osRet += " [OPTIONS]";
6658 615 : for (const auto *arg : m_positionalArgs)
6659 : {
6660 277 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6661 82 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6662 : {
6663 : const bool optional =
6664 203 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6665 30 : arg->GetName() == "pipeline"));
6666 173 : osRet += ' ';
6667 173 : if (optional)
6668 25 : osRet += '[';
6669 173 : const std::string &metavar = arg->GetMetaVar();
6670 173 : if (!metavar.empty() && metavar[0] == '<')
6671 : {
6672 4 : osRet += metavar;
6673 : }
6674 : else
6675 : {
6676 169 : osRet += '<';
6677 169 : osRet += metavar;
6678 169 : osRet += '>';
6679 : }
6680 215 : if (arg->GetType() == GAAT_DATASET_LIST &&
6681 42 : arg->GetMaxCount() > 1)
6682 : {
6683 28 : osRet += "...";
6684 : }
6685 173 : if (optional)
6686 25 : osRet += ']';
6687 : }
6688 : }
6689 : }
6690 :
6691 420 : const size_t nLenFirstLine = osRet.size();
6692 420 : osRet += '\n';
6693 420 : if (usageOptions.isPipelineStep)
6694 : {
6695 330 : osRet.append(nLenFirstLine, '-');
6696 330 : osRet += '\n';
6697 : }
6698 :
6699 420 : if (shortUsage)
6700 : {
6701 23 : osRet += "Try '";
6702 23 : osRet += osPath;
6703 23 : osRet += " --help' for help.\n";
6704 23 : return osRet;
6705 : }
6706 :
6707 397 : osRet += '\n';
6708 397 : osRet += m_description;
6709 397 : osRet += '\n';
6710 : }
6711 :
6712 407 : if (!m_args.empty() && !shortUsage)
6713 : {
6714 808 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6715 : size_t maxOptLen;
6716 404 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6717 404 : if (usageOptions.maxOptLen)
6718 331 : maxOptLen = usageOptions.maxOptLen;
6719 :
6720 808 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6721 404 : if (m_arbitraryLongNameArgsAllowed)
6722 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6723 :
6724 : const auto OutputArg =
6725 2592 : [this, maxOptLen, &osRet,
6726 25751 : &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6727 : {
6728 2592 : osRet += " ";
6729 2592 : osRet += opt;
6730 2592 : osRet += " ";
6731 2592 : osRet.append(maxOptLen - opt.size(), ' ');
6732 2592 : osRet += arg->GetDescription();
6733 :
6734 2592 : const auto &choices = arg->GetChoices();
6735 2592 : if (!choices.empty())
6736 : {
6737 237 : osRet += ". ";
6738 237 : osRet += arg->GetMetaVar();
6739 237 : osRet += '=';
6740 237 : bool firstChoice = true;
6741 1739 : for (const auto &choice : choices)
6742 : {
6743 1502 : if (!firstChoice)
6744 1265 : osRet += '|';
6745 1502 : osRet += choice;
6746 1502 : firstChoice = false;
6747 : }
6748 : }
6749 :
6750 5105 : if (arg->GetType() == GAAT_DATASET ||
6751 2513 : arg->GetType() == GAAT_DATASET_LIST)
6752 : {
6753 157 : if (arg->IsOutput() &&
6754 157 : arg->GetDatasetInputFlags() == GADV_NAME &&
6755 9 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6756 : {
6757 9 : osRet += " (created by algorithm)";
6758 : }
6759 : }
6760 :
6761 2592 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6762 : {
6763 198 : osRet += " (default: ";
6764 198 : osRet += arg->GetDefault<std::string>();
6765 198 : osRet += ')';
6766 : }
6767 2394 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6768 : {
6769 70 : if (arg->GetDefault<bool>())
6770 0 : osRet += " (default: true)";
6771 : }
6772 2324 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6773 : {
6774 84 : osRet += " (default: ";
6775 84 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6776 84 : osRet += ')';
6777 : }
6778 2240 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6779 : {
6780 49 : osRet += " (default: ";
6781 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6782 49 : osRet += ')';
6783 : }
6784 2642 : else if (arg->GetType() == GAAT_STRING_LIST &&
6785 451 : arg->HasDefaultValue())
6786 : {
6787 : const auto &defaultVal =
6788 17 : arg->GetDefault<std::vector<std::string>>();
6789 17 : if (defaultVal.size() == 1)
6790 : {
6791 17 : osRet += " (default: ";
6792 17 : osRet += defaultVal[0];
6793 17 : osRet += ')';
6794 : }
6795 : }
6796 2197 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6797 23 : arg->HasDefaultValue())
6798 : {
6799 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6800 0 : if (defaultVal.size() == 1)
6801 : {
6802 0 : osRet += " (default: ";
6803 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6804 0 : osRet += ')';
6805 : }
6806 : }
6807 2174 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6808 : {
6809 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6810 0 : if (defaultVal.size() == 1)
6811 : {
6812 0 : osRet += " (default: ";
6813 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6814 0 : osRet += ')';
6815 : }
6816 : }
6817 :
6818 2592 : if (arg->GetDisplayHintAboutRepetition())
6819 : {
6820 2621 : if (arg->GetMinCount() > 0 &&
6821 92 : arg->GetMinCount() == arg->GetMaxCount())
6822 : {
6823 18 : if (arg->GetMinCount() != 1)
6824 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6825 : }
6826 2585 : else if (arg->GetMinCount() > 0 &&
6827 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6828 : {
6829 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6830 8 : arg->GetMaxCount());
6831 : }
6832 2503 : else if (arg->GetMinCount() > 0)
6833 : {
6834 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6835 : }
6836 2437 : else if (arg->GetMaxCount() > 1)
6837 : {
6838 432 : osRet += " [may be repeated]";
6839 : }
6840 : }
6841 :
6842 2592 : if (arg->IsRequired())
6843 : {
6844 180 : osRet += " [required]";
6845 : }
6846 :
6847 2841 : if (!arg->IsAvailableInPipelineStep() &&
6848 249 : !usageOptions.isPipelineStep)
6849 : {
6850 29 : osRet += " [not available in pipelines]";
6851 : }
6852 :
6853 2592 : osRet += '\n';
6854 :
6855 2592 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6856 2592 : if (!mutualExclusionGroup.empty())
6857 : {
6858 550 : std::string otherArgs;
6859 5159 : for (const auto &otherArg : m_args)
6860 : {
6861 8963 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6862 4079 : otherArg.get() == arg)
6863 1080 : continue;
6864 3804 : if (otherArg->GetMutualExclusionGroup() ==
6865 : mutualExclusionGroup)
6866 : {
6867 372 : if (!otherArgs.empty())
6868 101 : otherArgs += ", ";
6869 372 : otherArgs += "--";
6870 372 : otherArgs += otherArg->GetName();
6871 : }
6872 : }
6873 275 : if (!otherArgs.empty())
6874 : {
6875 271 : osRet += " ";
6876 271 : osRet += " ";
6877 271 : osRet.append(maxOptLen, ' ');
6878 271 : osRet += "Mutually exclusive with ";
6879 271 : osRet += otherArgs;
6880 271 : osRet += '\n';
6881 : }
6882 : }
6883 :
6884 : // Check dependency
6885 5184 : std::string dependencyArgs;
6886 :
6887 32 : for (const auto &dependencyArgumentName :
6888 2656 : GetArgDependencies(arg->GetName()))
6889 : {
6890 32 : const auto otherArg{GetArg(dependencyArgumentName)};
6891 32 : if (otherArg != nullptr)
6892 : {
6893 32 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6894 : otherArg == arg)
6895 : {
6896 0 : continue;
6897 : }
6898 :
6899 32 : if (!dependencyArgs.empty())
6900 : {
6901 3 : dependencyArgs += ", ";
6902 : }
6903 :
6904 32 : dependencyArgs += "--";
6905 32 : dependencyArgs += otherArg->GetName();
6906 : }
6907 : else
6908 : {
6909 0 : CPLError(CE_Warning, CPLE_AppDefined,
6910 : "Argument '%s' depends on unknown argument '%s'",
6911 0 : arg->GetName().c_str(),
6912 : dependencyArgumentName.c_str());
6913 : }
6914 : }
6915 :
6916 2592 : if (!dependencyArgs.empty())
6917 : {
6918 29 : osRet += " ";
6919 29 : osRet += " ";
6920 29 : osRet.append(maxOptLen, ' ');
6921 29 : osRet += "Depends on ";
6922 29 : osRet += dependencyArgs;
6923 29 : osRet += '\n';
6924 : }
6925 2592 : };
6926 :
6927 404 : if (!m_positionalArgs.empty())
6928 : {
6929 155 : osRet += "\nPositional arguments:\n";
6930 1663 : for (const auto &[arg, opt] : options)
6931 : {
6932 1508 : if (arg->IsPositional())
6933 140 : OutputArg(arg, opt);
6934 : }
6935 : }
6936 :
6937 404 : if (hasNonPositionals)
6938 : {
6939 404 : bool hasCommon = false;
6940 404 : bool hasBase = false;
6941 404 : bool hasAdvanced = false;
6942 404 : bool hasEsoteric = false;
6943 808 : std::vector<std::string> categories;
6944 4005 : for (const auto &iter : options)
6945 : {
6946 3601 : const auto &arg = iter.first;
6947 3601 : if (!arg->IsPositional())
6948 : {
6949 3461 : const auto &category = arg->GetCategory();
6950 3461 : if (category == GAAC_COMMON)
6951 : {
6952 1231 : hasCommon = true;
6953 : }
6954 2230 : else if (category == GAAC_BASE)
6955 : {
6956 1958 : hasBase = true;
6957 : }
6958 272 : else if (category == GAAC_ADVANCED)
6959 : {
6960 210 : hasAdvanced = true;
6961 : }
6962 62 : else if (category == GAAC_ESOTERIC)
6963 : {
6964 29 : hasEsoteric = true;
6965 : }
6966 33 : else if (std::find(categories.begin(), categories.end(),
6967 33 : category) == categories.end())
6968 : {
6969 9 : categories.push_back(category);
6970 : }
6971 : }
6972 : }
6973 404 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6974 71 : categories.insert(categories.begin(), GAAC_ADVANCED);
6975 404 : if (hasBase)
6976 357 : categories.insert(categories.begin(), GAAC_BASE);
6977 404 : if (hasCommon && !usageOptions.isPipelineStep)
6978 69 : categories.insert(categories.begin(), GAAC_COMMON);
6979 404 : if (hasEsoteric)
6980 11 : categories.push_back(GAAC_ESOTERIC);
6981 :
6982 921 : for (const auto &category : categories)
6983 : {
6984 517 : osRet += "\n";
6985 517 : if (category != GAAC_BASE)
6986 : {
6987 160 : osRet += category;
6988 160 : osRet += ' ';
6989 : }
6990 517 : osRet += "Options:\n";
6991 5689 : for (const auto &[arg, opt] : options)
6992 : {
6993 5172 : if (!arg->IsPositional() && arg->GetCategory() == category)
6994 2452 : OutputArg(arg, opt);
6995 : }
6996 517 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6997 : {
6998 2 : osRet += " ";
6999 2 : osRet += userProvidedOpt;
7000 2 : osRet += " ";
7001 2 : if (userProvidedOpt.size() < maxOptLen)
7002 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
7003 2 : osRet += "Argument provided by user";
7004 2 : osRet += '\n';
7005 : }
7006 : }
7007 : }
7008 : }
7009 :
7010 407 : if (!m_longDescription.empty())
7011 : {
7012 7 : osRet += '\n';
7013 7 : osRet += m_longDescription;
7014 7 : osRet += '\n';
7015 : }
7016 :
7017 407 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
7018 : {
7019 392 : if (!m_helpURL.empty())
7020 : {
7021 392 : osRet += "\nFor more details, consult ";
7022 392 : osRet += GetHelpFullURL();
7023 392 : osRet += '\n';
7024 : }
7025 392 : osRet += GetUsageForCLIEnd();
7026 : }
7027 :
7028 407 : return osRet;
7029 : }
7030 :
7031 : /************************************************************************/
7032 : /* GDALAlgorithm::GetUsageForCLIEnd() */
7033 : /************************************************************************/
7034 :
7035 : //! @cond Doxygen_Suppress
7036 399 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
7037 : {
7038 399 : std::string osRet;
7039 :
7040 399 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
7041 : {
7042 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
7043 : "alternative interface to GDAL and OGR command line "
7044 : "utilities.\nThe project reserves the right to modify, "
7045 : "rename, reorganize, and change the behavior of the utility\n"
7046 : "until it is officially frozen in a future feature release of "
7047 14 : "GDAL.\n";
7048 : }
7049 399 : return osRet;
7050 : }
7051 :
7052 : //! @endcond
7053 :
7054 : /************************************************************************/
7055 : /* GDALAlgorithm::GetUsageAsJSON() */
7056 : /************************************************************************/
7057 :
7058 603 : std::string GDALAlgorithm::GetUsageAsJSON() const
7059 : {
7060 1206 : CPLJSONDocument oDoc;
7061 1206 : auto oRoot = oDoc.GetRoot();
7062 :
7063 603 : if (m_displayInJSONUsage)
7064 : {
7065 601 : oRoot.Add("name", m_name);
7066 601 : CPLJSONArray jFullPath;
7067 1247 : for (const std::string &s : m_callPath)
7068 : {
7069 646 : jFullPath.Add(s);
7070 : }
7071 601 : oRoot.Add("full_path", jFullPath);
7072 : }
7073 :
7074 603 : oRoot.Add("description", m_description);
7075 603 : if (!m_helpURL.empty())
7076 : {
7077 600 : oRoot.Add("short_url", m_helpURL);
7078 600 : oRoot.Add("url", GetHelpFullURL());
7079 : }
7080 :
7081 1206 : CPLJSONArray jSubAlgorithms;
7082 815 : for (const auto &subAlgName : GetSubAlgorithmNames())
7083 : {
7084 424 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7085 212 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
7086 : {
7087 210 : CPLJSONDocument oSubDoc;
7088 210 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
7089 210 : jSubAlgorithms.Add(oSubDoc.GetRoot());
7090 : }
7091 : }
7092 603 : oRoot.Add("sub_algorithms", jSubAlgorithms);
7093 :
7094 603 : if (m_arbitraryLongNameArgsAllowed)
7095 : {
7096 1 : oRoot.Add("user_provided_arguments_allowed", true);
7097 : }
7098 :
7099 11494 : const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
7100 : {
7101 5747 : CPLJSONObject jArg;
7102 5747 : jArg.Add("name", arg->GetName());
7103 5747 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
7104 5747 : jArg.Add("description", arg->GetDescription());
7105 :
7106 5747 : const auto &metaVar = arg->GetMetaVar();
7107 5747 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
7108 : {
7109 1737 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
7110 1737 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
7111 : std::string::npos)
7112 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
7113 : else
7114 927 : jArg.Add("metavar", metaVar);
7115 : }
7116 :
7117 5747 : if (!arg->IsAvailableInPipelineStep())
7118 : {
7119 1682 : jArg.Add("available_in_pipeline_step", false);
7120 : }
7121 :
7122 5747 : const auto &choices = arg->GetChoices();
7123 5747 : if (!choices.empty())
7124 : {
7125 431 : CPLJSONArray jChoices;
7126 3539 : for (const auto &choice : choices)
7127 3108 : jChoices.Add(choice);
7128 431 : jArg.Add("choices", jChoices);
7129 : }
7130 5747 : if (arg->HasDefaultValue())
7131 : {
7132 1240 : switch (arg->GetType())
7133 : {
7134 440 : case GAAT_BOOLEAN:
7135 440 : jArg.Add("default", arg->GetDefault<bool>());
7136 440 : break;
7137 378 : case GAAT_STRING:
7138 378 : jArg.Add("default", arg->GetDefault<std::string>());
7139 378 : break;
7140 210 : case GAAT_INTEGER:
7141 210 : jArg.Add("default", arg->GetDefault<int>());
7142 210 : break;
7143 178 : case GAAT_REAL:
7144 178 : jArg.Add("default", arg->GetDefault<double>());
7145 178 : break;
7146 32 : case GAAT_STRING_LIST:
7147 : {
7148 : const auto &val =
7149 32 : arg->GetDefault<std::vector<std::string>>();
7150 32 : if (val.size() == 1)
7151 : {
7152 31 : jArg.Add("default", val[0]);
7153 : }
7154 : else
7155 : {
7156 1 : CPLJSONArray jArr;
7157 3 : for (const auto &s : val)
7158 : {
7159 2 : jArr.Add(s);
7160 : }
7161 1 : jArg.Add("default", jArr);
7162 : }
7163 32 : break;
7164 : }
7165 1 : case GAAT_INTEGER_LIST:
7166 : {
7167 1 : const auto &val = arg->GetDefault<std::vector<int>>();
7168 1 : if (val.size() == 1)
7169 : {
7170 0 : jArg.Add("default", val[0]);
7171 : }
7172 : else
7173 : {
7174 1 : CPLJSONArray jArr;
7175 3 : for (int i : val)
7176 : {
7177 2 : jArr.Add(i);
7178 : }
7179 1 : jArg.Add("default", jArr);
7180 : }
7181 1 : break;
7182 : }
7183 1 : case GAAT_REAL_LIST:
7184 : {
7185 1 : const auto &val = arg->GetDefault<std::vector<double>>();
7186 1 : if (val.size() == 1)
7187 : {
7188 0 : jArg.Add("default", val[0]);
7189 : }
7190 : else
7191 : {
7192 1 : CPLJSONArray jArr;
7193 3 : for (double d : val)
7194 : {
7195 2 : jArr.Add(d);
7196 : }
7197 1 : jArg.Add("default", jArr);
7198 : }
7199 1 : break;
7200 : }
7201 0 : case GAAT_DATASET:
7202 : case GAAT_DATASET_LIST:
7203 0 : CPLError(CE_Warning, CPLE_AppDefined,
7204 : "Unhandled default value for arg %s",
7205 0 : arg->GetName().c_str());
7206 0 : break;
7207 : }
7208 : }
7209 :
7210 5747 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7211 5747 : if (!std::isnan(minVal))
7212 : {
7213 697 : if (arg->GetType() == GAAT_INTEGER ||
7214 269 : arg->GetType() == GAAT_INTEGER_LIST)
7215 175 : jArg.Add("min_value", static_cast<int>(minVal));
7216 : else
7217 253 : jArg.Add("min_value", minVal);
7218 428 : jArg.Add("min_value_is_included", minValIsIncluded);
7219 : }
7220 :
7221 5747 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7222 5747 : if (!std::isnan(maxVal))
7223 : {
7224 199 : if (arg->GetType() == GAAT_INTEGER ||
7225 82 : arg->GetType() == GAAT_INTEGER_LIST)
7226 35 : jArg.Add("max_value", static_cast<int>(maxVal));
7227 : else
7228 82 : jArg.Add("max_value", maxVal);
7229 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
7230 : }
7231 :
7232 5747 : jArg.Add("required", arg->IsRequired());
7233 5747 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7234 : {
7235 1611 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7236 1611 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7237 1611 : jArg.Add("min_count", arg->GetMinCount());
7238 1611 : jArg.Add("max_count", arg->GetMaxCount());
7239 : }
7240 :
7241 : // Process dependencies
7242 5747 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7243 5747 : if (!mutualDependencyGroup.empty())
7244 : {
7245 32 : jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7246 : }
7247 :
7248 11494 : CPLJSONArray jDependencies;
7249 50 : for (const auto &dependencyArgumentName :
7250 5847 : GetArgDependencies(arg->GetName()))
7251 : {
7252 50 : jDependencies.Add(dependencyArgumentName);
7253 : }
7254 :
7255 5747 : if (jDependencies.Size() > 0)
7256 : {
7257 50 : jArg.Add("depends_on", jDependencies);
7258 : }
7259 :
7260 5747 : jArg.Add("category", arg->GetCategory());
7261 :
7262 11222 : if (arg->GetType() == GAAT_DATASET ||
7263 5475 : arg->GetType() == GAAT_DATASET_LIST)
7264 : {
7265 : {
7266 489 : CPLJSONArray jAr;
7267 489 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
7268 324 : jAr.Add("raster");
7269 489 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7270 198 : jAr.Add("vector");
7271 489 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7272 41 : jAr.Add("multidim_raster");
7273 489 : jArg.Add("dataset_type", jAr);
7274 : }
7275 :
7276 662 : const auto GetFlags = [](int flags)
7277 : {
7278 662 : CPLJSONArray jAr;
7279 662 : if (flags & GADV_NAME)
7280 489 : jAr.Add("name");
7281 662 : if (flags & GADV_OBJECT)
7282 621 : jAr.Add("dataset");
7283 662 : return jAr;
7284 : };
7285 :
7286 489 : if (arg->IsInput())
7287 : {
7288 489 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7289 : }
7290 489 : if (arg->IsOutput())
7291 : {
7292 173 : jArg.Add("output_flags",
7293 346 : GetFlags(arg->GetDatasetOutputFlags()));
7294 : }
7295 : }
7296 :
7297 5747 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7298 5747 : if (!mutualExclusionGroup.empty())
7299 : {
7300 752 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7301 : }
7302 :
7303 11494 : const auto &metadata = arg->GetMetadata();
7304 5747 : if (!metadata.empty())
7305 : {
7306 465 : CPLJSONObject jMetadata;
7307 969 : for (const auto &[key, values] : metadata)
7308 : {
7309 1008 : CPLJSONArray jValue;
7310 1219 : for (const auto &value : values)
7311 715 : jValue.Add(value);
7312 504 : jMetadata.Add(key, jValue);
7313 : }
7314 465 : jArg.Add("metadata", jMetadata);
7315 : }
7316 :
7317 11494 : return jArg;
7318 603 : };
7319 :
7320 : {
7321 603 : CPLJSONArray jArgs;
7322 9509 : for (const auto &arg : m_args)
7323 : {
7324 8906 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7325 5499 : jArgs.Add(ProcessArg(arg.get()));
7326 : }
7327 603 : oRoot.Add("input_arguments", jArgs);
7328 : }
7329 :
7330 : {
7331 603 : CPLJSONArray jArgs;
7332 9509 : for (const auto &arg : m_args)
7333 : {
7334 8906 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7335 75 : jArgs.Add(ProcessArg(arg.get()));
7336 : }
7337 603 : oRoot.Add("output_arguments", jArgs);
7338 : }
7339 :
7340 : {
7341 603 : CPLJSONArray jArgs;
7342 9509 : for (const auto &arg : m_args)
7343 : {
7344 8906 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7345 173 : jArgs.Add(ProcessArg(arg.get()));
7346 : }
7347 603 : oRoot.Add("input_output_arguments", jArgs);
7348 : }
7349 :
7350 603 : if (m_supportsStreamedOutput)
7351 : {
7352 130 : oRoot.Add("supports_streamed_output", true);
7353 : }
7354 :
7355 1206 : return oDoc.SaveAsString();
7356 : }
7357 :
7358 : /************************************************************************/
7359 : /* GDALAlgorithm::GetAutoComplete() */
7360 : /************************************************************************/
7361 :
7362 : std::vector<std::string>
7363 295 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7364 : bool lastWordIsComplete, bool showAllOptions)
7365 : {
7366 590 : std::vector<std::string> ret;
7367 :
7368 : // Get inner-most algorithm
7369 295 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
7370 295 : GDALAlgorithm *curAlg = this;
7371 580 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7372 : {
7373 : auto subAlg = curAlg->InstantiateSubAlgorithm(
7374 431 : args.front(), /* suggestionAllowed = */ false);
7375 431 : if (!subAlg)
7376 145 : break;
7377 286 : if (args.size() == 1 && !lastWordIsComplete)
7378 : {
7379 5 : int nCount = 0;
7380 118 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7381 : {
7382 113 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7383 6 : nCount++;
7384 : }
7385 5 : if (nCount >= 2)
7386 : {
7387 11 : for (const std::string &subAlgName :
7388 23 : curAlg->GetSubAlgorithmNames())
7389 : {
7390 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7391 11 : if (subAlg && !subAlg->IsHidden())
7392 11 : ret.push_back(subAlg->GetName());
7393 : }
7394 1 : return ret;
7395 : }
7396 : }
7397 285 : showAllOptions = false;
7398 285 : args.erase(args.begin());
7399 285 : curAlgHolder = std::move(subAlg);
7400 285 : curAlg = curAlgHolder.get();
7401 : }
7402 294 : if (curAlg != this)
7403 : {
7404 155 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7405 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
7406 155 : /* showAllOptions = */ false);
7407 : }
7408 :
7409 278 : std::string option;
7410 278 : std::string value;
7411 139 : ExtractLastOptionAndValue(args, option, value);
7412 :
7413 170 : if (option.empty() && !args.empty() && !args.back().empty() &&
7414 31 : args.back()[0] == '-')
7415 : {
7416 28 : const auto &lastArg = args.back();
7417 : // List available options
7418 419 : for (const auto &arg : GetArgs())
7419 : {
7420 721 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7421 655 : (!showAllOptions &&
7422 894 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7423 542 : arg->GetName() == "version" ||
7424 271 : arg->GetName() == "json-usage")))
7425 : {
7426 142 : continue;
7427 : }
7428 249 : if (!arg->GetShortName().empty())
7429 : {
7430 153 : std::string str = std::string("-").append(arg->GetShortName());
7431 51 : if (lastArg == str)
7432 0 : ret.push_back(std::move(str));
7433 : }
7434 249 : if (lastArg != "-" && lastArg != "--")
7435 : {
7436 54 : for (const std::string &alias : arg->GetAliases())
7437 : {
7438 48 : std::string str = std::string("--").append(alias);
7439 16 : if (cpl::starts_with(str, lastArg))
7440 3 : ret.push_back(std::move(str));
7441 : }
7442 : }
7443 249 : if (!arg->GetName().empty())
7444 : {
7445 747 : std::string str = std::string("--").append(arg->GetName());
7446 249 : if (cpl::starts_with(str, lastArg))
7447 213 : ret.push_back(std::move(str));
7448 : }
7449 : }
7450 28 : std::sort(ret.begin(), ret.end());
7451 : }
7452 111 : else if (!option.empty())
7453 : {
7454 : // List possible choices for current option
7455 104 : auto arg = GetArg(option);
7456 104 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7457 : {
7458 104 : ret = arg->GetChoices();
7459 104 : if (ret.empty())
7460 : {
7461 : {
7462 99 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7463 99 : SetParseForAutoCompletion();
7464 99 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7465 : }
7466 99 : ret = arg->GetAutoCompleteChoices(value);
7467 : }
7468 : else
7469 : {
7470 5 : std::sort(ret.begin(), ret.end());
7471 : }
7472 104 : if (!ret.empty() && ret.back() == value)
7473 : {
7474 2 : ret.clear();
7475 : }
7476 102 : else if (ret.empty())
7477 : {
7478 13 : ret.push_back("**");
7479 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7480 26 : ret.push_back(std::string("\xC2\xA0"
7481 : "description: ")
7482 13 : .append(arg->GetDescription()));
7483 : }
7484 : }
7485 : }
7486 : else
7487 : {
7488 : // List possible sub-algorithms
7489 70 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7490 : {
7491 126 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7492 63 : if (subAlg && !subAlg->IsHidden())
7493 63 : ret.push_back(subAlg->GetName());
7494 : }
7495 7 : if (!ret.empty())
7496 : {
7497 3 : std::sort(ret.begin(), ret.end());
7498 : }
7499 :
7500 : // Try filenames
7501 7 : if (ret.empty() && !args.empty())
7502 : {
7503 : {
7504 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7505 3 : SetParseForAutoCompletion();
7506 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7507 : }
7508 :
7509 3 : const std::string &lastArg = args.back();
7510 3 : GDALAlgorithmArg *arg = nullptr;
7511 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7512 21 : "like", "source", "destination"})
7513 : {
7514 18 : if (!arg)
7515 : {
7516 3 : auto newArg = GetArg(name);
7517 3 : if (newArg)
7518 : {
7519 3 : if (!newArg->IsExplicitlySet())
7520 : {
7521 0 : arg = newArg;
7522 : }
7523 6 : else if (newArg->GetType() == GAAT_STRING ||
7524 5 : newArg->GetType() == GAAT_STRING_LIST ||
7525 8 : newArg->GetType() == GAAT_DATASET ||
7526 2 : newArg->GetType() == GAAT_DATASET_LIST)
7527 : {
7528 : VSIStatBufL sStat;
7529 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7530 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7531 : {
7532 3 : arg = newArg;
7533 : }
7534 : }
7535 : }
7536 : }
7537 : }
7538 3 : if (arg)
7539 : {
7540 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7541 : }
7542 : }
7543 : }
7544 :
7545 139 : return ret;
7546 : }
7547 :
7548 : /************************************************************************/
7549 : /* GDALAlgorithm::GetFieldIndices() */
7550 : /************************************************************************/
7551 :
7552 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7553 : OGRLayerH hLayer, std::vector<int> &indices)
7554 : {
7555 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7556 :
7557 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7558 :
7559 44 : if (names.size() == 1 && names[0] == "ALL")
7560 : {
7561 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7562 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7563 : {
7564 16 : indices.push_back(i);
7565 : }
7566 : }
7567 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7568 : {
7569 6 : std::set<int> fieldsAdded;
7570 14 : for (const std::string &osFieldName : names)
7571 : {
7572 :
7573 : const int nIdx =
7574 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7575 :
7576 10 : if (nIdx < 0)
7577 : {
7578 2 : CPLError(CE_Failure, CPLE_AppDefined,
7579 : "Field '%s' does not exist in layer '%s'",
7580 2 : osFieldName.c_str(), layer.GetName());
7581 2 : return false;
7582 : }
7583 :
7584 8 : if (fieldsAdded.insert(nIdx).second)
7585 : {
7586 7 : indices.push_back(nIdx);
7587 : }
7588 : }
7589 : }
7590 :
7591 42 : return true;
7592 : }
7593 :
7594 : /************************************************************************/
7595 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7596 : /************************************************************************/
7597 :
7598 139 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7599 : std::string &option,
7600 : std::string &value) const
7601 : {
7602 139 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7603 : {
7604 97 : const auto nPosEqual = args.back().find('=');
7605 97 : if (nPosEqual == std::string::npos)
7606 : {
7607 : // Deal with "gdal ... --option"
7608 78 : if (GetArg(args.back()))
7609 : {
7610 50 : option = args.back();
7611 50 : args.pop_back();
7612 : }
7613 : }
7614 : else
7615 : {
7616 : // Deal with "gdal ... --option=<value>"
7617 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7618 : {
7619 19 : option = args.back().substr(0, nPosEqual);
7620 19 : value = args.back().substr(nPosEqual + 1);
7621 19 : args.pop_back();
7622 : }
7623 : }
7624 : }
7625 78 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7626 36 : args[args.size() - 2][0] == '-')
7627 : {
7628 : // Deal with "gdal ... --option <value>"
7629 35 : auto arg = GetArg(args[args.size() - 2]);
7630 35 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7631 : {
7632 35 : option = args[args.size() - 2];
7633 35 : value = args.back();
7634 35 : args.pop_back();
7635 : }
7636 : }
7637 :
7638 139 : const auto IsKeyValueOption = [](const std::string &osStr)
7639 : {
7640 382 : return osStr == "--co" || osStr == "--creation-option" ||
7641 357 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7642 380 : osStr == "--oo" || osStr == "--open-option";
7643 : };
7644 :
7645 139 : if (IsKeyValueOption(option))
7646 : {
7647 23 : const auto nPosEqual = value.find('=');
7648 23 : if (nPosEqual != std::string::npos)
7649 : {
7650 11 : value.resize(nPosEqual);
7651 : }
7652 : }
7653 139 : }
7654 :
7655 : /************************************************************************/
7656 : /* GDALAlgorithm::GetArgDependencies() */
7657 : /************************************************************************/
7658 :
7659 : std::vector<std::string>
7660 8348 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
7661 : {
7662 8348 : const auto arg = GetArg(osName, false);
7663 8348 : if (!arg)
7664 : {
7665 0 : ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
7666 : osName.c_str());
7667 0 : return {};
7668 : }
7669 16696 : std::vector<std::string> dependencies = arg->GetDirectDependencies();
7670 8348 : if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7671 8348 : !mutualDependencyGroup.empty())
7672 : {
7673 896 : for (const auto &otherArg : m_args)
7674 : {
7675 1627 : if (otherArg.get() == arg ||
7676 786 : mutualDependencyGroup.compare(
7677 786 : otherArg->GetMutualDependencyGroup()) != 0)
7678 783 : continue;
7679 58 : dependencies.push_back(otherArg->GetName());
7680 : }
7681 : }
7682 8348 : return dependencies;
7683 : }
7684 :
7685 : //! @cond Doxygen_Suppress
7686 :
7687 : /************************************************************************/
7688 : /* GDALContainerAlgorithm::RunImpl() */
7689 : /************************************************************************/
7690 :
7691 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7692 : {
7693 0 : return false;
7694 : }
7695 :
7696 : //! @endcond
7697 :
7698 : /************************************************************************/
7699 : /* GDALAlgorithmRelease() */
7700 : /************************************************************************/
7701 :
7702 : /** Release a handle to an algorithm.
7703 : *
7704 : * @since 3.11
7705 : */
7706 13927 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7707 : {
7708 13927 : delete hAlg;
7709 13927 : }
7710 :
7711 : /************************************************************************/
7712 : /* GDALAlgorithmGetName() */
7713 : /************************************************************************/
7714 :
7715 : /** Return the algorithm name.
7716 : *
7717 : * @param hAlg Handle to an algorithm. Must NOT be null.
7718 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7719 : * be freed.
7720 : * @since 3.11
7721 : */
7722 6304 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7723 : {
7724 6304 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7725 6304 : return hAlg->ptr->GetName().c_str();
7726 : }
7727 :
7728 : /************************************************************************/
7729 : /* GDALAlgorithmGetDescription() */
7730 : /************************************************************************/
7731 :
7732 : /** Return the algorithm (short) description.
7733 : *
7734 : * @param hAlg Handle to an algorithm. Must NOT be null.
7735 : * @return algorithm description whose lifetime is bound to hAlg and which must
7736 : * not be freed.
7737 : * @since 3.11
7738 : */
7739 6070 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7740 : {
7741 6070 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7742 6070 : return hAlg->ptr->GetDescription().c_str();
7743 : }
7744 :
7745 : /************************************************************************/
7746 : /* GDALAlgorithmGetLongDescription() */
7747 : /************************************************************************/
7748 :
7749 : /** Return the algorithm (longer) description.
7750 : *
7751 : * @param hAlg Handle to an algorithm. Must NOT be null.
7752 : * @return algorithm description whose lifetime is bound to hAlg and which must
7753 : * not be freed.
7754 : * @since 3.11
7755 : */
7756 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7757 : {
7758 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7759 2 : return hAlg->ptr->GetLongDescription().c_str();
7760 : }
7761 :
7762 : /************************************************************************/
7763 : /* GDALAlgorithmGetHelpFullURL() */
7764 : /************************************************************************/
7765 :
7766 : /** Return the algorithm full URL.
7767 : *
7768 : * @param hAlg Handle to an algorithm. Must NOT be null.
7769 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7770 : * not be freed.
7771 : * @since 3.11
7772 : */
7773 5332 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7774 : {
7775 5332 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7776 5332 : return hAlg->ptr->GetHelpFullURL().c_str();
7777 : }
7778 :
7779 : /************************************************************************/
7780 : /* GDALAlgorithmHasSubAlgorithms() */
7781 : /************************************************************************/
7782 :
7783 : /** Return whether the algorithm has sub-algorithms.
7784 : *
7785 : * @param hAlg Handle to an algorithm. Must NOT be null.
7786 : * @since 3.11
7787 : */
7788 10221 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7789 : {
7790 10221 : VALIDATE_POINTER1(hAlg, __func__, false);
7791 10221 : return hAlg->ptr->HasSubAlgorithms();
7792 : }
7793 :
7794 : /************************************************************************/
7795 : /* GDALAlgorithmGetSubAlgorithmNames() */
7796 : /************************************************************************/
7797 :
7798 : /** Get the names of registered algorithms.
7799 : *
7800 : * @param hAlg Handle to an algorithm. Must NOT be null.
7801 : * @return a NULL terminated list of names, which must be destroyed with
7802 : * CSLDestroy()
7803 : * @since 3.11
7804 : */
7805 940 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7806 : {
7807 940 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7808 940 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7809 : }
7810 :
7811 : /************************************************************************/
7812 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7813 : /************************************************************************/
7814 :
7815 : /** Instantiate an algorithm by its name (or its alias).
7816 : *
7817 : * @param hAlg Handle to an algorithm. Must NOT be null.
7818 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7819 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7820 : * or NULL if the algorithm does not exist or another error occurred.
7821 : * @since 3.11
7822 : */
7823 9592 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7824 : const char *pszSubAlgName)
7825 : {
7826 9592 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7827 9592 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7828 19184 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7829 : return subAlg
7830 19184 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7831 19184 : : nullptr;
7832 : }
7833 :
7834 : /************************************************************************/
7835 : /* GDALAlgorithmParseCommandLineArguments() */
7836 : /************************************************************************/
7837 :
7838 : /** Parse a command line argument, which does not include the algorithm
7839 : * name, to set the value of corresponding arguments.
7840 : *
7841 : * @param hAlg Handle to an algorithm. Must NOT be null.
7842 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7843 : * @return true if successful, false otherwise
7844 : * @since 3.11
7845 : */
7846 :
7847 371 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7848 : CSLConstList papszArgs)
7849 : {
7850 371 : VALIDATE_POINTER1(hAlg, __func__, false);
7851 371 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7852 : }
7853 :
7854 : /************************************************************************/
7855 : /* GDALAlgorithmGetActualAlgorithm() */
7856 : /************************************************************************/
7857 :
7858 : /** Return the actual algorithm that is going to be invoked, when the
7859 : * current algorithm has sub-algorithms.
7860 : *
7861 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7862 : *
7863 : * Note that the lifetime of the returned algorithm does not exceed the one of
7864 : * the hAlg instance that owns it.
7865 : *
7866 : * @param hAlg Handle to an algorithm. Must NOT be null.
7867 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7868 : * @since 3.11
7869 : */
7870 1030 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7871 : {
7872 1030 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7873 1030 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7874 : }
7875 :
7876 : /************************************************************************/
7877 : /* GDALAlgorithmRun() */
7878 : /************************************************************************/
7879 :
7880 : /** Execute the algorithm, starting with ValidateArguments() and then
7881 : * calling RunImpl().
7882 : *
7883 : * This function must be called at most once per instance.
7884 : *
7885 : * @param hAlg Handle to an algorithm. Must NOT be null.
7886 : * @param pfnProgress Progress callback. May be null.
7887 : * @param pProgressData Progress callback user data. May be null.
7888 : * @return true if successful, false otherwise
7889 : * @since 3.11
7890 : */
7891 :
7892 3123 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7893 : void *pProgressData)
7894 : {
7895 3123 : VALIDATE_POINTER1(hAlg, __func__, false);
7896 3123 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7897 : }
7898 :
7899 : /************************************************************************/
7900 : /* GDALAlgorithmFinalize() */
7901 : /************************************************************************/
7902 :
7903 : /** Complete any pending actions, and return the final status.
7904 : * This is typically useful for algorithm that generate an output dataset.
7905 : *
7906 : * Note that this function does *NOT* release memory associated with the
7907 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7908 : *
7909 : * @param hAlg Handle to an algorithm. Must NOT be null.
7910 : * @return true if successful, false otherwise
7911 : * @since 3.11
7912 : */
7913 :
7914 1054 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7915 : {
7916 1054 : VALIDATE_POINTER1(hAlg, __func__, false);
7917 1054 : return hAlg->ptr->Finalize();
7918 : }
7919 :
7920 : /************************************************************************/
7921 : /* GDALAlgorithmGetUsageAsJSON() */
7922 : /************************************************************************/
7923 :
7924 : /** Return the usage of the algorithm as a JSON-serialized string.
7925 : *
7926 : * This can be used to dynamically generate interfaces to algorithms.
7927 : *
7928 : * @param hAlg Handle to an algorithm. Must NOT be null.
7929 : * @return a string that must be freed with CPLFree()
7930 : * @since 3.11
7931 : */
7932 6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7933 : {
7934 6 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7935 6 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7936 : }
7937 :
7938 : /************************************************************************/
7939 : /* GDALAlgorithmGetArgNames() */
7940 : /************************************************************************/
7941 :
7942 : /** Return the list of available argument names.
7943 : *
7944 : * @param hAlg Handle to an algorithm. Must NOT be null.
7945 : * @return a NULL terminated list of names, which must be destroyed with
7946 : * CSLDestroy()
7947 : * @since 3.11
7948 : */
7949 16650 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7950 : {
7951 16650 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7952 33300 : CPLStringList list;
7953 370882 : for (const auto &arg : hAlg->ptr->GetArgs())
7954 354232 : list.AddString(arg->GetName().c_str());
7955 16650 : return list.StealList();
7956 : }
7957 :
7958 : /************************************************************************/
7959 : /* GDALAlgorithmGetArg() */
7960 : /************************************************************************/
7961 :
7962 : /** Return an argument from its name.
7963 : *
7964 : * The lifetime of the returned object does not exceed the one of hAlg.
7965 : *
7966 : * @param hAlg Handle to an algorithm. Must NOT be null.
7967 : * @param pszArgName Argument name. Must NOT be null.
7968 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7969 : * or nullptr in case of error
7970 : * @since 3.11
7971 : */
7972 355400 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7973 : const char *pszArgName)
7974 : {
7975 355400 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7976 355400 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7977 710800 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7978 355400 : /* isConst = */ true);
7979 355400 : if (!arg)
7980 3 : return nullptr;
7981 355397 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7982 : }
7983 :
7984 : /************************************************************************/
7985 : /* GDALAlgorithmGetArgNonConst() */
7986 : /************************************************************************/
7987 :
7988 : /** Return an argument from its name, possibly allowing creation of user-provided
7989 : * argument if the algorithm allow it.
7990 : *
7991 : * The lifetime of the returned object does not exceed the one of hAlg.
7992 : *
7993 : * @param hAlg Handle to an algorithm. Must NOT be null.
7994 : * @param pszArgName Argument name. Must NOT be null.
7995 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7996 : * or nullptr in case of error
7997 : * @since 3.12
7998 : */
7999 11027 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
8000 : const char *pszArgName)
8001 : {
8002 11027 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
8003 11027 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
8004 22054 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
8005 11027 : /* isConst = */ false);
8006 11027 : if (!arg)
8007 2 : return nullptr;
8008 11025 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
8009 : }
8010 :
8011 : /************************************************************************/
8012 : /* GDALAlgorithmGetArgDependencies() */
8013 : /************************************************************************/
8014 :
8015 : /** Return the list of argument names the specified argument depends on.
8016 : *
8017 : * This includes both regular dependencies and mutual dependencies.
8018 : *
8019 : * @param hAlg Handle to an algorithm. Must NOT be null.
8020 : * @param pszArgName Argument name. Must NOT be null.
8021 : * @return a NULL terminated list of names, which must be destroyed with
8022 : * CSLDestroy()
8023 : * @since 3.11
8024 : */
8025 7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
8026 : const char *pszArgName)
8027 : {
8028 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
8029 7 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
8030 7 : return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
8031 : }
8032 :
8033 : /************************************************************************/
8034 : /* GDALAlgorithmArgRelease() */
8035 : /************************************************************************/
8036 :
8037 : /** Release a handle to an argument.
8038 : *
8039 : * @since 3.11
8040 : */
8041 366422 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
8042 : {
8043 366422 : delete hArg;
8044 366422 : }
8045 :
8046 : /************************************************************************/
8047 : /* GDALAlgorithmArgGetName() */
8048 : /************************************************************************/
8049 :
8050 : /** Return the name of an argument.
8051 : *
8052 : * @param hArg Handle to an argument. Must NOT be null.
8053 : * @return argument name whose lifetime is bound to hArg and which must not
8054 : * be freed.
8055 : * @since 3.11
8056 : */
8057 20268 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
8058 : {
8059 20268 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8060 20268 : return hArg->ptr->GetName().c_str();
8061 : }
8062 :
8063 : /************************************************************************/
8064 : /* GDALAlgorithmArgGetType() */
8065 : /************************************************************************/
8066 :
8067 : /** Get the type of an argument
8068 : *
8069 : * @param hArg Handle to an argument. Must NOT be null.
8070 : * @since 3.11
8071 : */
8072 444154 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
8073 : {
8074 444154 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
8075 444154 : return hArg->ptr->GetType();
8076 : }
8077 :
8078 : /************************************************************************/
8079 : /* GDALAlgorithmArgGetDescription() */
8080 : /************************************************************************/
8081 :
8082 : /** Return the description of an argument.
8083 : *
8084 : * @param hArg Handle to an argument. Must NOT be null.
8085 : * @return argument description whose lifetime is bound to hArg and which must not
8086 : * be freed.
8087 : * @since 3.11
8088 : */
8089 87987 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
8090 : {
8091 87987 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8092 87987 : return hArg->ptr->GetDescription().c_str();
8093 : }
8094 :
8095 : /************************************************************************/
8096 : /* GDALAlgorithmArgGetShortName() */
8097 : /************************************************************************/
8098 :
8099 : /** Return the short name, or empty string if there is none
8100 : *
8101 : * @param hArg Handle to an argument. Must NOT be null.
8102 : * @return short name whose lifetime is bound to hArg and which must not
8103 : * be freed.
8104 : * @since 3.11
8105 : */
8106 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
8107 : {
8108 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8109 1 : return hArg->ptr->GetShortName().c_str();
8110 : }
8111 :
8112 : /************************************************************************/
8113 : /* GDALAlgorithmArgGetAliases() */
8114 : /************************************************************************/
8115 :
8116 : /** Return the aliases (potentially none)
8117 : *
8118 : * @param hArg Handle to an argument. Must NOT be null.
8119 : * @return a NULL terminated list of names, which must be destroyed with
8120 : * CSLDestroy()
8121 :
8122 : * @since 3.11
8123 : */
8124 166051 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
8125 : {
8126 166051 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8127 166051 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
8128 : }
8129 :
8130 : /************************************************************************/
8131 : /* GDALAlgorithmArgGetMetaVar() */
8132 : /************************************************************************/
8133 :
8134 : /** Return the "meta-var" hint.
8135 : *
8136 : * By default, the meta-var value is the long name of the argument in
8137 : * upper case.
8138 : *
8139 : * @param hArg Handle to an argument. Must NOT be null.
8140 : * @return meta-var hint whose lifetime is bound to hArg and which must not
8141 : * be freed.
8142 : * @since 3.11
8143 : */
8144 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
8145 : {
8146 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8147 1 : return hArg->ptr->GetMetaVar().c_str();
8148 : }
8149 :
8150 : /************************************************************************/
8151 : /* GDALAlgorithmArgGetCategory() */
8152 : /************************************************************************/
8153 :
8154 : /** Return the argument category
8155 : *
8156 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
8157 : *
8158 : * @param hArg Handle to an argument. Must NOT be null.
8159 : * @return category whose lifetime is bound to hArg and which must not
8160 : * be freed.
8161 : * @since 3.11
8162 : */
8163 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
8164 : {
8165 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8166 1 : return hArg->ptr->GetCategory().c_str();
8167 : }
8168 :
8169 : /************************************************************************/
8170 : /* GDALAlgorithmArgIsPositional() */
8171 : /************************************************************************/
8172 :
8173 : /** Return if the argument is a positional one.
8174 : *
8175 : * @param hArg Handle to an argument. Must NOT be null.
8176 : * @since 3.11
8177 : */
8178 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
8179 : {
8180 1 : VALIDATE_POINTER1(hArg, __func__, false);
8181 1 : return hArg->ptr->IsPositional();
8182 : }
8183 :
8184 : /************************************************************************/
8185 : /* GDALAlgorithmArgIsRequired() */
8186 : /************************************************************************/
8187 :
8188 : /** Return whether the argument is required. Defaults to false.
8189 : *
8190 : * @param hArg Handle to an argument. Must NOT be null.
8191 : * @since 3.11
8192 : */
8193 166051 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
8194 : {
8195 166051 : VALIDATE_POINTER1(hArg, __func__, false);
8196 166051 : return hArg->ptr->IsRequired();
8197 : }
8198 :
8199 : /************************************************************************/
8200 : /* GDALAlgorithmArgGetMinCount() */
8201 : /************************************************************************/
8202 :
8203 : /** Return the minimum number of values for the argument.
8204 : *
8205 : * Defaults to 0.
8206 : * Only applies to list type of arguments.
8207 : *
8208 : * @param hArg Handle to an argument. Must NOT be null.
8209 : * @since 3.11
8210 : */
8211 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
8212 : {
8213 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8214 1 : return hArg->ptr->GetMinCount();
8215 : }
8216 :
8217 : /************************************************************************/
8218 : /* GDALAlgorithmArgGetMaxCount() */
8219 : /************************************************************************/
8220 :
8221 : /** Return the maximum number of values for the argument.
8222 : *
8223 : * Defaults to 1 for scalar types, and INT_MAX for list types.
8224 : * Only applies to list type of arguments.
8225 : *
8226 : * @param hArg Handle to an argument. Must NOT be null.
8227 : * @since 3.11
8228 : */
8229 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8230 : {
8231 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8232 1 : return hArg->ptr->GetMaxCount();
8233 : }
8234 :
8235 : /************************************************************************/
8236 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
8237 : /************************************************************************/
8238 :
8239 : /** Return whether, for list type of arguments, several values, space
8240 : * separated, may be specified. That is "--foo=bar,baz".
8241 : * The default is true.
8242 : *
8243 : * @param hArg Handle to an argument. Must NOT be null.
8244 : * @since 3.11
8245 : */
8246 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8247 : {
8248 1 : VALIDATE_POINTER1(hArg, __func__, false);
8249 1 : return hArg->ptr->GetPackedValuesAllowed();
8250 : }
8251 :
8252 : /************************************************************************/
8253 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
8254 : /************************************************************************/
8255 :
8256 : /** Return whether, for list type of arguments, the argument may be
8257 : * repeated. That is "--foo=bar --foo=baz".
8258 : * The default is true.
8259 : *
8260 : * @param hArg Handle to an argument. Must NOT be null.
8261 : * @since 3.11
8262 : */
8263 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8264 : {
8265 1 : VALIDATE_POINTER1(hArg, __func__, false);
8266 1 : return hArg->ptr->GetRepeatedArgAllowed();
8267 : }
8268 :
8269 : /************************************************************************/
8270 : /* GDALAlgorithmArgGetChoices() */
8271 : /************************************************************************/
8272 :
8273 : /** Return the allowed values (as strings) for the argument.
8274 : *
8275 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8276 : *
8277 : * @param hArg Handle to an argument. Must NOT be null.
8278 : * @return a NULL terminated list of names, which must be destroyed with
8279 : * CSLDestroy()
8280 :
8281 : * @since 3.11
8282 : */
8283 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8284 : {
8285 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8286 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
8287 : }
8288 :
8289 : /************************************************************************/
8290 : /* GDALAlgorithmArgGetMetadataItem() */
8291 : /************************************************************************/
8292 :
8293 : /** Return the values of the metadata item of an argument.
8294 : *
8295 : * @param hArg Handle to an argument. Must NOT be null.
8296 : * @param pszItem Name of the item. Must NOT be null.
8297 : * @return a NULL terminated list of values, which must be destroyed with
8298 : * CSLDestroy()
8299 :
8300 : * @since 3.11
8301 : */
8302 85 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8303 : const char *pszItem)
8304 : {
8305 85 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8306 85 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
8307 85 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8308 85 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8309 : }
8310 :
8311 : /************************************************************************/
8312 : /* GDALAlgorithmArgIsExplicitlySet() */
8313 : /************************************************************************/
8314 :
8315 : /** Return whether the argument value has been explicitly set with Set()
8316 : *
8317 : * @param hArg Handle to an argument. Must NOT be null.
8318 : * @since 3.11
8319 : */
8320 790 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8321 : {
8322 790 : VALIDATE_POINTER1(hArg, __func__, false);
8323 790 : return hArg->ptr->IsExplicitlySet();
8324 : }
8325 :
8326 : /************************************************************************/
8327 : /* GDALAlgorithmArgHasDefaultValue() */
8328 : /************************************************************************/
8329 :
8330 : /** Return if the argument has a declared default value.
8331 : *
8332 : * @param hArg Handle to an argument. Must NOT be null.
8333 : * @since 3.11
8334 : */
8335 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8336 : {
8337 2 : VALIDATE_POINTER1(hArg, __func__, false);
8338 2 : return hArg->ptr->HasDefaultValue();
8339 : }
8340 :
8341 : /************************************************************************/
8342 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
8343 : /************************************************************************/
8344 :
8345 : /** Return the argument default value as a integer.
8346 : *
8347 : * Must only be called on arguments whose type is GAAT_BOOLEAN
8348 : *
8349 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8350 : * argument has a default value.
8351 : *
8352 : * @param hArg Handle to an argument. Must NOT be null.
8353 : * @since 3.12
8354 : */
8355 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8356 : {
8357 3 : VALIDATE_POINTER1(hArg, __func__, false);
8358 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8359 : {
8360 1 : CPLError(CE_Failure, CPLE_AppDefined,
8361 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8362 : __func__);
8363 1 : return false;
8364 : }
8365 2 : return hArg->ptr->GetDefault<bool>();
8366 : }
8367 :
8368 : /************************************************************************/
8369 : /* GDALAlgorithmArgGetDefaultAsString() */
8370 : /************************************************************************/
8371 :
8372 : /** Return the argument default value as a string.
8373 : *
8374 : * Must only be called on arguments whose type is GAAT_STRING.
8375 : *
8376 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8377 : * argument has a default value.
8378 : *
8379 : * @param hArg Handle to an argument. Must NOT be null.
8380 : * @return string whose lifetime is bound to hArg and which must not
8381 : * be freed.
8382 : * @since 3.11
8383 : */
8384 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8385 : {
8386 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8387 3 : if (hArg->ptr->GetType() != GAAT_STRING)
8388 : {
8389 2 : CPLError(CE_Failure, CPLE_AppDefined,
8390 : "%s must only be called on arguments of type GAAT_STRING",
8391 : __func__);
8392 2 : return nullptr;
8393 : }
8394 1 : return hArg->ptr->GetDefault<std::string>().c_str();
8395 : }
8396 :
8397 : /************************************************************************/
8398 : /* GDALAlgorithmArgGetDefaultAsInteger() */
8399 : /************************************************************************/
8400 :
8401 : /** Return the argument default value as a integer.
8402 : *
8403 : * Must only be called on arguments whose type is GAAT_INTEGER
8404 : *
8405 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8406 : * argument has a default value.
8407 : *
8408 : * @param hArg Handle to an argument. Must NOT be null.
8409 : * @since 3.12
8410 : */
8411 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8412 : {
8413 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8414 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8415 : {
8416 2 : CPLError(CE_Failure, CPLE_AppDefined,
8417 : "%s must only be called on arguments of type GAAT_INTEGER",
8418 : __func__);
8419 2 : return 0;
8420 : }
8421 1 : return hArg->ptr->GetDefault<int>();
8422 : }
8423 :
8424 : /************************************************************************/
8425 : /* GDALAlgorithmArgGetDefaultAsDouble() */
8426 : /************************************************************************/
8427 :
8428 : /** Return the argument default value as a double.
8429 : *
8430 : * Must only be called on arguments whose type is GAAT_REAL
8431 : *
8432 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8433 : * argument has a default value.
8434 : *
8435 : * @param hArg Handle to an argument. Must NOT be null.
8436 : * @since 3.12
8437 : */
8438 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8439 : {
8440 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8441 3 : if (hArg->ptr->GetType() != GAAT_REAL)
8442 : {
8443 2 : CPLError(CE_Failure, CPLE_AppDefined,
8444 : "%s must only be called on arguments of type GAAT_REAL",
8445 : __func__);
8446 2 : return 0;
8447 : }
8448 1 : return hArg->ptr->GetDefault<double>();
8449 : }
8450 :
8451 : /************************************************************************/
8452 : /* GDALAlgorithmArgGetDefaultAsStringList() */
8453 : /************************************************************************/
8454 :
8455 : /** Return the argument default value as a string list.
8456 : *
8457 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8458 : *
8459 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8460 : * argument has a default value.
8461 : *
8462 : * @param hArg Handle to an argument. Must NOT be null.
8463 : * @return a NULL terminated list of names, which must be destroyed with
8464 : * CSLDestroy()
8465 :
8466 : * @since 3.12
8467 : */
8468 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8469 : {
8470 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8471 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8472 : {
8473 2 : CPLError(CE_Failure, CPLE_AppDefined,
8474 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8475 : __func__);
8476 2 : return nullptr;
8477 : }
8478 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8479 1 : .StealList();
8480 : }
8481 :
8482 : /************************************************************************/
8483 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8484 : /************************************************************************/
8485 :
8486 : /** Return the argument default value as a integer list.
8487 : *
8488 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8489 : *
8490 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8491 : * argument has a default value.
8492 : *
8493 : * @param hArg Handle to an argument. Must NOT be null.
8494 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8495 : * @since 3.12
8496 : */
8497 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8498 : size_t *pnCount)
8499 : {
8500 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8501 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8502 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8503 : {
8504 2 : CPLError(
8505 : CE_Failure, CPLE_AppDefined,
8506 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8507 : __func__);
8508 2 : *pnCount = 0;
8509 2 : return nullptr;
8510 : }
8511 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8512 1 : *pnCount = val.size();
8513 1 : return val.data();
8514 : }
8515 :
8516 : /************************************************************************/
8517 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8518 : /************************************************************************/
8519 :
8520 : /** Return the argument default value as a real list.
8521 : *
8522 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8523 : *
8524 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8525 : * argument has a default value.
8526 : *
8527 : * @param hArg Handle to an argument. Must NOT be null.
8528 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8529 : * @since 3.12
8530 : */
8531 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8532 : size_t *pnCount)
8533 : {
8534 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8535 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8536 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8537 : {
8538 2 : CPLError(CE_Failure, CPLE_AppDefined,
8539 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8540 : __func__);
8541 2 : *pnCount = 0;
8542 2 : return nullptr;
8543 : }
8544 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8545 1 : *pnCount = val.size();
8546 1 : return val.data();
8547 : }
8548 :
8549 : /************************************************************************/
8550 : /* GDALAlgorithmArgIsHidden() */
8551 : /************************************************************************/
8552 :
8553 : /** Return whether the argument is hidden (for GDAL internal use)
8554 : *
8555 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8556 : * GDALAlgorithmArgIsHiddenForAPI().
8557 : *
8558 : * @param hArg Handle to an argument. Must NOT be null.
8559 : * @since 3.12
8560 : */
8561 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8562 : {
8563 1 : VALIDATE_POINTER1(hArg, __func__, false);
8564 1 : return hArg->ptr->IsHidden();
8565 : }
8566 :
8567 : /************************************************************************/
8568 : /* GDALAlgorithmArgIsHiddenForCLI() */
8569 : /************************************************************************/
8570 :
8571 : /** Return whether the argument must not be mentioned in CLI usage.
8572 : *
8573 : * For example, "output-value" for "gdal raster info", which is only
8574 : * meant when the algorithm is used from a non-CLI context.
8575 : *
8576 : * @param hArg Handle to an argument. Must NOT be null.
8577 : * @since 3.11
8578 : */
8579 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8580 : {
8581 1 : VALIDATE_POINTER1(hArg, __func__, false);
8582 1 : return hArg->ptr->IsHiddenForCLI();
8583 : }
8584 :
8585 : /************************************************************************/
8586 : /* GDALAlgorithmArgIsHiddenForAPI() */
8587 : /************************************************************************/
8588 :
8589 : /** Return whether the argument must not be mentioned in the context of an
8590 : * API use.
8591 : * Said otherwise, if it is only for CLI usage.
8592 : *
8593 : * For example "--help"
8594 : *
8595 : * @param hArg Handle to an argument. Must NOT be null.
8596 : * @since 3.12
8597 : */
8598 229314 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8599 : {
8600 229314 : VALIDATE_POINTER1(hArg, __func__, false);
8601 229314 : return hArg->ptr->IsHiddenForAPI();
8602 : }
8603 :
8604 : /************************************************************************/
8605 : /* GDALAlgorithmArgIsOnlyForCLI() */
8606 : /************************************************************************/
8607 :
8608 : /** Return whether the argument must not be mentioned in the context of an
8609 : * API use.
8610 : * Said otherwise, if it is only for CLI usage.
8611 : *
8612 : * For example "--help"
8613 : *
8614 : * @param hArg Handle to an argument. Must NOT be null.
8615 : * @since 3.11
8616 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8617 : */
8618 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8619 : {
8620 0 : VALIDATE_POINTER1(hArg, __func__, false);
8621 0 : return hArg->ptr->IsHiddenForAPI();
8622 : }
8623 :
8624 : /************************************************************************/
8625 : /* GDALAlgorithmArgIsAvailableInPipelineStep() */
8626 : /************************************************************************/
8627 :
8628 : /** Return whether the argument is available in a pipeline step.
8629 : *
8630 : * If false, it is only available in standalone mode.
8631 : *
8632 : * @param hArg Handle to an argument. Must NOT be null.
8633 : * @since 3.13
8634 : */
8635 2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8636 : {
8637 2 : VALIDATE_POINTER1(hArg, __func__, false);
8638 2 : return hArg->ptr->IsAvailableInPipelineStep();
8639 : }
8640 :
8641 : /************************************************************************/
8642 : /* GDALAlgorithmArgIsInput() */
8643 : /************************************************************************/
8644 :
8645 : /** Indicate whether the value of the argument is read-only during the
8646 : * execution of the algorithm.
8647 : *
8648 : * Default is true.
8649 : *
8650 : * @param hArg Handle to an argument. Must NOT be null.
8651 : * @since 3.11
8652 : */
8653 226567 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8654 : {
8655 226567 : VALIDATE_POINTER1(hArg, __func__, false);
8656 226567 : return hArg->ptr->IsInput();
8657 : }
8658 :
8659 : /************************************************************************/
8660 : /* GDALAlgorithmArgIsOutput() */
8661 : /************************************************************************/
8662 :
8663 : /** Return whether (at least part of) the value of the argument is set
8664 : * during the execution of the algorithm.
8665 : *
8666 : * For example, "output-value" for "gdal raster info"
8667 : * Default is false.
8668 : * An argument may return both IsInput() and IsOutput() as true.
8669 : * For example the "gdal raster convert" algorithm consumes the dataset
8670 : * name of its "output" argument, and sets the dataset object during its
8671 : * execution.
8672 : *
8673 : * @param hArg Handle to an argument. Must NOT be null.
8674 : * @since 3.11
8675 : */
8676 127641 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8677 : {
8678 127641 : VALIDATE_POINTER1(hArg, __func__, false);
8679 127641 : return hArg->ptr->IsOutput();
8680 : }
8681 :
8682 : /************************************************************************/
8683 : /* GDALAlgorithmArgGetDatasetType() */
8684 : /************************************************************************/
8685 :
8686 : /** Get which type of dataset is allowed / generated.
8687 : *
8688 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8689 : * GDAL_OF_MULTIDIM_RASTER.
8690 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8691 : *
8692 : * @param hArg Handle to an argument. Must NOT be null.
8693 : * @since 3.11
8694 : */
8695 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8696 : {
8697 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8698 2 : return hArg->ptr->GetDatasetType();
8699 : }
8700 :
8701 : /************************************************************************/
8702 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8703 : /************************************************************************/
8704 :
8705 : /** Indicates which components among name and dataset are accepted as
8706 : * input, when this argument serves as an input.
8707 : *
8708 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8709 : * input.
8710 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8711 : * accepted as input.
8712 : * If both bits are set, the algorithm can accept either a name or a dataset
8713 : * object.
8714 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8715 : *
8716 : * @param hArg Handle to an argument. Must NOT be null.
8717 : * @return string whose lifetime is bound to hAlg and which must not
8718 : * be freed.
8719 : * @since 3.11
8720 : */
8721 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8722 : {
8723 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8724 2 : return hArg->ptr->GetDatasetInputFlags();
8725 : }
8726 :
8727 : /************************************************************************/
8728 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8729 : /************************************************************************/
8730 :
8731 : /** Indicates which components among name and dataset are modified,
8732 : * when this argument serves as an output.
8733 : *
8734 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8735 : * output (that is the algorithm will generate the name. Rarely used).
8736 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8737 : * generated as output, and available for use after the algorithm has
8738 : * completed.
8739 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8740 : *
8741 : * @param hArg Handle to an argument. Must NOT be null.
8742 : * @return string whose lifetime is bound to hAlg and which must not
8743 : * be freed.
8744 : * @since 3.11
8745 : */
8746 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8747 : {
8748 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8749 2 : return hArg->ptr->GetDatasetOutputFlags();
8750 : }
8751 :
8752 : /************************************************************************/
8753 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8754 : /************************************************************************/
8755 :
8756 : /** Return the name of the mutual exclusion group to which this argument
8757 : * belongs to.
8758 : *
8759 : * Or empty string if it does not belong to any exclusion group.
8760 : *
8761 : * @param hArg Handle to an argument. Must NOT be null.
8762 : * @return string whose lifetime is bound to hArg and which must not
8763 : * be freed.
8764 : * @since 3.11
8765 : */
8766 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8767 : {
8768 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8769 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8770 : }
8771 :
8772 : /************************************************************************/
8773 : /* GDALAlgorithmArgGetMutualDependencyGroup() */
8774 : /************************************************************************/
8775 :
8776 : /** Return the name of the mutual dependency group to which this argument
8777 : * belongs to.
8778 : *
8779 : * Or empty string if it does not belong to any dependency group.
8780 : *
8781 : * @param hArg Handle to an argument. Must NOT be null.
8782 : * @return string whose lifetime is bound to hArg and which must not
8783 : * be freed.
8784 : * @since 3.13
8785 : */
8786 5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8787 : {
8788 5 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8789 5 : return hArg->ptr->GetMutualDependencyGroup().c_str();
8790 : }
8791 :
8792 : /************************************************************************/
8793 : /* GDALAlgorithmArgGetDirectDependencies() */
8794 : /************************************************************************/
8795 :
8796 : /** Return the list of names of arguments that this argument depends on.
8797 : *
8798 : * This is not necessarily a symmetric relationship.
8799 : * If argument A depends on argument B, it doesn't mean that B depends on A.
8800 : * Mutual dependency groups are a special case of dependencies,
8801 : * where all arguments of the group depend on each other and are not
8802 : * returned by this method.
8803 : *
8804 : * @param hArg Handle to an argument. Must NOT be null.
8805 : * @return a NULL terminated list of names, which must be destroyed with
8806 : * CSLDestroy()
8807 : * @since 3.13
8808 : */
8809 7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8810 : {
8811 7 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8812 7 : return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8813 : }
8814 :
8815 : /************************************************************************/
8816 : /* GDALAlgorithmArgGetAsBoolean() */
8817 : /************************************************************************/
8818 :
8819 : /** Return the argument value as a boolean.
8820 : *
8821 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8822 : *
8823 : * @param hArg Handle to an argument. Must NOT be null.
8824 : * @since 3.11
8825 : */
8826 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8827 : {
8828 8 : VALIDATE_POINTER1(hArg, __func__, false);
8829 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8830 : {
8831 1 : CPLError(CE_Failure, CPLE_AppDefined,
8832 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8833 : __func__);
8834 1 : return false;
8835 : }
8836 7 : return hArg->ptr->Get<bool>();
8837 : }
8838 :
8839 : /************************************************************************/
8840 : /* GDALAlgorithmArgGetAsString() */
8841 : /************************************************************************/
8842 :
8843 : /** Return the argument value as a string.
8844 : *
8845 : * Must only be called on arguments whose type is GAAT_STRING.
8846 : *
8847 : * @param hArg Handle to an argument. Must NOT be null.
8848 : * @return string whose lifetime is bound to hArg and which must not
8849 : * be freed.
8850 : * @since 3.11
8851 : */
8852 441 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8853 : {
8854 441 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8855 441 : if (hArg->ptr->GetType() != GAAT_STRING)
8856 : {
8857 1 : CPLError(CE_Failure, CPLE_AppDefined,
8858 : "%s must only be called on arguments of type GAAT_STRING",
8859 : __func__);
8860 1 : return nullptr;
8861 : }
8862 440 : return hArg->ptr->Get<std::string>().c_str();
8863 : }
8864 :
8865 : /************************************************************************/
8866 : /* GDALAlgorithmArgGetAsDatasetValue() */
8867 : /************************************************************************/
8868 :
8869 : /** Return the argument value as a GDALArgDatasetValueH.
8870 : *
8871 : * Must only be called on arguments whose type is GAAT_DATASET
8872 : *
8873 : * @param hArg Handle to an argument. Must NOT be null.
8874 : * @return handle to a GDALArgDatasetValue that must be released with
8875 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8876 : * the one of hArg.
8877 : * @since 3.11
8878 : */
8879 3515 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8880 : {
8881 3515 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8882 3515 : if (hArg->ptr->GetType() != GAAT_DATASET)
8883 : {
8884 1 : CPLError(CE_Failure, CPLE_AppDefined,
8885 : "%s must only be called on arguments of type GAAT_DATASET",
8886 : __func__);
8887 1 : return nullptr;
8888 : }
8889 3514 : return std::make_unique<GDALArgDatasetValueHS>(
8890 7028 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8891 3514 : .release();
8892 : }
8893 :
8894 : /************************************************************************/
8895 : /* GDALAlgorithmArgGetAsInteger() */
8896 : /************************************************************************/
8897 :
8898 : /** Return the argument value as a integer.
8899 : *
8900 : * Must only be called on arguments whose type is GAAT_INTEGER
8901 : *
8902 : * @param hArg Handle to an argument. Must NOT be null.
8903 : * @since 3.11
8904 : */
8905 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8906 : {
8907 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8908 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8909 : {
8910 1 : CPLError(CE_Failure, CPLE_AppDefined,
8911 : "%s must only be called on arguments of type GAAT_INTEGER",
8912 : __func__);
8913 1 : return 0;
8914 : }
8915 25 : return hArg->ptr->Get<int>();
8916 : }
8917 :
8918 : /************************************************************************/
8919 : /* GDALAlgorithmArgGetAsDouble() */
8920 : /************************************************************************/
8921 :
8922 : /** Return the argument value as a double.
8923 : *
8924 : * Must only be called on arguments whose type is GAAT_REAL
8925 : *
8926 : * @param hArg Handle to an argument. Must NOT be null.
8927 : * @since 3.11
8928 : */
8929 16 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8930 : {
8931 16 : VALIDATE_POINTER1(hArg, __func__, 0);
8932 16 : if (hArg->ptr->GetType() != GAAT_REAL)
8933 : {
8934 1 : CPLError(CE_Failure, CPLE_AppDefined,
8935 : "%s must only be called on arguments of type GAAT_REAL",
8936 : __func__);
8937 1 : return 0;
8938 : }
8939 15 : return hArg->ptr->Get<double>();
8940 : }
8941 :
8942 : /************************************************************************/
8943 : /* GDALAlgorithmArgGetAsStringList() */
8944 : /************************************************************************/
8945 :
8946 : /** Return the argument value as a string list.
8947 : *
8948 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8949 : *
8950 : * @param hArg Handle to an argument. Must NOT be null.
8951 : * @return a NULL terminated list of names, which must be destroyed with
8952 : * CSLDestroy()
8953 :
8954 : * @since 3.11
8955 : */
8956 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8957 : {
8958 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8959 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8960 : {
8961 1 : CPLError(CE_Failure, CPLE_AppDefined,
8962 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8963 : __func__);
8964 1 : return nullptr;
8965 : }
8966 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8967 3 : .StealList();
8968 : }
8969 :
8970 : /************************************************************************/
8971 : /* GDALAlgorithmArgGetAsIntegerList() */
8972 : /************************************************************************/
8973 :
8974 : /** Return the argument value as a integer list.
8975 : *
8976 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8977 : *
8978 : * @param hArg Handle to an argument. Must NOT be null.
8979 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8980 : * @since 3.11
8981 : */
8982 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8983 : size_t *pnCount)
8984 : {
8985 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8986 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8987 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8988 : {
8989 1 : CPLError(
8990 : CE_Failure, CPLE_AppDefined,
8991 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8992 : __func__);
8993 1 : *pnCount = 0;
8994 1 : return nullptr;
8995 : }
8996 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8997 7 : *pnCount = val.size();
8998 7 : return val.data();
8999 : }
9000 :
9001 : /************************************************************************/
9002 : /* GDALAlgorithmArgGetAsDoubleList() */
9003 : /************************************************************************/
9004 :
9005 : /** Return the argument value as a real list.
9006 : *
9007 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
9008 : *
9009 : * @param hArg Handle to an argument. Must NOT be null.
9010 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
9011 : * @since 3.11
9012 : */
9013 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
9014 : size_t *pnCount)
9015 : {
9016 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
9017 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
9018 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
9019 : {
9020 1 : CPLError(CE_Failure, CPLE_AppDefined,
9021 : "%s must only be called on arguments of type GAAT_REAL_LIST",
9022 : __func__);
9023 1 : *pnCount = 0;
9024 1 : return nullptr;
9025 : }
9026 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
9027 7 : *pnCount = val.size();
9028 7 : return val.data();
9029 : }
9030 :
9031 : /************************************************************************/
9032 : /* GDALAlgorithmArgSetAsBoolean() */
9033 : /************************************************************************/
9034 :
9035 : /** Set the value for a GAAT_BOOLEAN argument.
9036 : *
9037 : * It cannot be called several times for a given argument.
9038 : * Validation checks and other actions are run.
9039 : *
9040 : * @param hArg Handle to an argument. Must NOT be null.
9041 : * @param value value.
9042 : * @return true if success.
9043 : * @since 3.11
9044 : */
9045 :
9046 778 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
9047 : {
9048 778 : VALIDATE_POINTER1(hArg, __func__, false);
9049 778 : return hArg->ptr->Set(value);
9050 : }
9051 :
9052 : /************************************************************************/
9053 : /* GDALAlgorithmArgSetAsString() */
9054 : /************************************************************************/
9055 :
9056 : /** Set the value for a GAAT_STRING argument.
9057 : *
9058 : * It cannot be called several times for a given argument.
9059 : * Validation checks and other actions are run.
9060 : *
9061 : * @param hArg Handle to an argument. Must NOT be null.
9062 : * @param value value (may be null)
9063 : * @return true if success.
9064 : * @since 3.11
9065 : */
9066 :
9067 3481 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
9068 : {
9069 3481 : VALIDATE_POINTER1(hArg, __func__, false);
9070 3481 : return hArg->ptr->Set(value ? value : "");
9071 : }
9072 :
9073 : /************************************************************************/
9074 : /* GDALAlgorithmArgSetAsInteger() */
9075 : /************************************************************************/
9076 :
9077 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
9078 : *
9079 : * It cannot be called several times for a given argument.
9080 : * Validation checks and other actions are run.
9081 : *
9082 : * @param hArg Handle to an argument. Must NOT be null.
9083 : * @param value value.
9084 : * @return true if success.
9085 : * @since 3.11
9086 : */
9087 :
9088 495 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
9089 : {
9090 495 : VALIDATE_POINTER1(hArg, __func__, false);
9091 495 : return hArg->ptr->Set(value);
9092 : }
9093 :
9094 : /************************************************************************/
9095 : /* GDALAlgorithmArgSetAsDouble() */
9096 : /************************************************************************/
9097 :
9098 : /** Set the value for a GAAT_REAL argument.
9099 : *
9100 : * It cannot be called several times for a given argument.
9101 : * Validation checks and other actions are run.
9102 : *
9103 : * @param hArg Handle to an argument. Must NOT be null.
9104 : * @param value value.
9105 : * @return true if success.
9106 : * @since 3.11
9107 : */
9108 :
9109 313 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
9110 : {
9111 313 : VALIDATE_POINTER1(hArg, __func__, false);
9112 313 : return hArg->ptr->Set(value);
9113 : }
9114 :
9115 : /************************************************************************/
9116 : /* GDALAlgorithmArgSetAsDatasetValue() */
9117 : /************************************************************************/
9118 :
9119 : /** Set the value for a GAAT_DATASET argument.
9120 : *
9121 : * It cannot be called several times for a given argument.
9122 : * Validation checks and other actions are run.
9123 : *
9124 : * @param hArg Handle to an argument. Must NOT be null.
9125 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
9126 : * @return true if success.
9127 : * @since 3.11
9128 : */
9129 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
9130 : GDALArgDatasetValueH value)
9131 : {
9132 2 : VALIDATE_POINTER1(hArg, __func__, false);
9133 2 : VALIDATE_POINTER1(value, __func__, false);
9134 2 : return hArg->ptr->SetFrom(*(value->ptr));
9135 : }
9136 :
9137 : /************************************************************************/
9138 : /* GDALAlgorithmArgSetDataset() */
9139 : /************************************************************************/
9140 :
9141 : /** Set dataset object, increasing its reference counter.
9142 : *
9143 : * @param hArg Handle to an argument. Must NOT be null.
9144 : * @param hDS Dataset object. May be null.
9145 : * @return true if success.
9146 : * @since 3.11
9147 : */
9148 :
9149 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
9150 : {
9151 2 : VALIDATE_POINTER1(hArg, __func__, false);
9152 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
9153 : }
9154 :
9155 : /************************************************************************/
9156 : /* GDALAlgorithmArgSetAsStringList() */
9157 : /************************************************************************/
9158 :
9159 : /** Set the value for a GAAT_STRING_LIST argument.
9160 : *
9161 : * It cannot be called several times for a given argument.
9162 : * Validation checks and other actions are run.
9163 : *
9164 : * @param hArg Handle to an argument. Must NOT be null.
9165 : * @param value value as a NULL terminated list (may be null)
9166 : * @return true if success.
9167 : * @since 3.11
9168 : */
9169 :
9170 975 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
9171 : {
9172 975 : VALIDATE_POINTER1(hArg, __func__, false);
9173 975 : return hArg->ptr->Set(
9174 1950 : static_cast<std::vector<std::string>>(CPLStringList(value)));
9175 : }
9176 :
9177 : /************************************************************************/
9178 : /* GDALAlgorithmArgSetAsIntegerList() */
9179 : /************************************************************************/
9180 :
9181 : /** Set the value for a GAAT_INTEGER_LIST argument.
9182 : *
9183 : * It cannot be called several times for a given argument.
9184 : * Validation checks and other actions are run.
9185 : *
9186 : * @param hArg Handle to an argument. Must NOT be null.
9187 : * @param nCount Number of values in pnValues.
9188 : * @param pnValues Pointer to an array of integer values of size nCount.
9189 : * @return true if success.
9190 : * @since 3.11
9191 : */
9192 65 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
9193 : const int *pnValues)
9194 : {
9195 65 : VALIDATE_POINTER1(hArg, __func__, false);
9196 65 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
9197 : }
9198 :
9199 : /************************************************************************/
9200 : /* GDALAlgorithmArgSetAsDoubleList() */
9201 : /************************************************************************/
9202 :
9203 : /** Set the value for a GAAT_REAL_LIST argument.
9204 : *
9205 : * It cannot be called several times for a given argument.
9206 : * Validation checks and other actions are run.
9207 : *
9208 : * @param hArg Handle to an argument. Must NOT be null.
9209 : * @param nCount Number of values in pnValues.
9210 : * @param pnValues Pointer to an array of double values of size nCount.
9211 : * @return true if success.
9212 : * @since 3.11
9213 : */
9214 240 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
9215 : const double *pnValues)
9216 : {
9217 240 : VALIDATE_POINTER1(hArg, __func__, false);
9218 240 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
9219 : }
9220 :
9221 : /************************************************************************/
9222 : /* GDALAlgorithmArgSetDatasets() */
9223 : /************************************************************************/
9224 :
9225 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9226 : *
9227 : * @param hArg Handle to an argument. Must NOT be null.
9228 : * @param nCount Number of values in pnValues.
9229 : * @param pahDS Pointer to an array of dataset of size nCount.
9230 : * @return true if success.
9231 : * @since 3.11
9232 : */
9233 :
9234 1495 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9235 : GDALDatasetH *pahDS)
9236 : {
9237 1495 : VALIDATE_POINTER1(hArg, __func__, false);
9238 2990 : std::vector<GDALArgDatasetValue> values;
9239 3016 : for (size_t i = 0; i < nCount; ++i)
9240 : {
9241 1521 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9242 : }
9243 1495 : return hArg->ptr->Set(std::move(values));
9244 : }
9245 :
9246 : /************************************************************************/
9247 : /* GDALAlgorithmArgSetDatasetNames() */
9248 : /************************************************************************/
9249 :
9250 : /** Set dataset names to a GAAT_DATASET_LIST argument.
9251 : *
9252 : * @param hArg Handle to an argument. Must NOT be null.
9253 : * @param names Dataset names as a NULL terminated list (may be null)
9254 : * @return true if success.
9255 : * @since 3.11
9256 : */
9257 :
9258 850 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9259 : {
9260 850 : VALIDATE_POINTER1(hArg, __func__, false);
9261 1700 : std::vector<GDALArgDatasetValue> values;
9262 1773 : for (size_t i = 0; names[i]; ++i)
9263 : {
9264 923 : values.emplace_back(names[i]);
9265 : }
9266 850 : return hArg->ptr->Set(std::move(values));
9267 : }
9268 :
9269 : /************************************************************************/
9270 : /* GDALArgDatasetValueCreate() */
9271 : /************************************************************************/
9272 :
9273 : /** Instantiate an empty GDALArgDatasetValue
9274 : *
9275 : * @return new handle to free with GDALArgDatasetValueRelease()
9276 : * @since 3.11
9277 : */
9278 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
9279 : {
9280 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
9281 : }
9282 :
9283 : /************************************************************************/
9284 : /* GDALArgDatasetValueRelease() */
9285 : /************************************************************************/
9286 :
9287 : /** Release a handle to a GDALArgDatasetValue
9288 : *
9289 : * @since 3.11
9290 : */
9291 3515 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9292 : {
9293 3515 : delete hValue;
9294 3515 : }
9295 :
9296 : /************************************************************************/
9297 : /* GDALArgDatasetValueGetName() */
9298 : /************************************************************************/
9299 :
9300 : /** Return the name component of the GDALArgDatasetValue
9301 : *
9302 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9303 : * @return string whose lifetime is bound to hAlg and which must not
9304 : * be freed.
9305 : * @since 3.11
9306 : */
9307 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9308 : {
9309 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9310 1 : return hValue->ptr->GetName().c_str();
9311 : }
9312 :
9313 : /************************************************************************/
9314 : /* GDALArgDatasetValueGetDatasetRef() */
9315 : /************************************************************************/
9316 :
9317 : /** Return the dataset component of the GDALArgDatasetValue.
9318 : *
9319 : * This does not modify the reference counter, hence the lifetime of the
9320 : * returned object is not guaranteed to exceed the one of hValue.
9321 : *
9322 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9323 : * @since 3.11
9324 : */
9325 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9326 : {
9327 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9328 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9329 : }
9330 :
9331 : /************************************************************************/
9332 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
9333 : /************************************************************************/
9334 :
9335 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
9336 : * reference count if not null. Once done with the dataset, the caller should
9337 : * call GDALReleaseDataset().
9338 : *
9339 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9340 : * @since 3.11
9341 : */
9342 : GDALDatasetH
9343 1184 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9344 : {
9345 1184 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9346 1184 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9347 : }
9348 :
9349 : /************************************************************************/
9350 : /* GDALArgDatasetValueSetName() */
9351 : /************************************************************************/
9352 :
9353 : /** Set dataset name
9354 : *
9355 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9356 : * @param pszName Dataset name. May be null.
9357 : * @since 3.11
9358 : */
9359 :
9360 1510 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9361 : const char *pszName)
9362 : {
9363 1510 : VALIDATE_POINTER0(hValue, __func__);
9364 1510 : hValue->ptr->Set(pszName ? pszName : "");
9365 : }
9366 :
9367 : /************************************************************************/
9368 : /* GDALArgDatasetValueSetDataset() */
9369 : /************************************************************************/
9370 :
9371 : /** Set dataset object, increasing its reference counter.
9372 : *
9373 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9374 : * @param hDS Dataset object. May be null.
9375 : * @since 3.11
9376 : */
9377 :
9378 806 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9379 : GDALDatasetH hDS)
9380 : {
9381 806 : VALIDATE_POINTER0(hValue, __func__);
9382 806 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9383 : }
|