Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: GDALAlgorithm class
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_error.h"
16 : #include "cpl_json.h"
17 : #include "cpl_levenshtein.h"
18 : #include "cpl_minixml.h"
19 : #include "cpl_multiproc.h"
20 :
21 : #include "gdalalgorithm.h"
22 : #include "gdalalg_abstract_pipeline.h"
23 : #include "gdal_priv.h"
24 : #include "gdal_thread_pool.h"
25 : #include "ogrsf_frmts.h"
26 : #include "ogr_spatialref.h"
27 : #include "vrtdataset.h"
28 :
29 : #include <algorithm>
30 : #include <cassert>
31 : #include <cerrno>
32 : #include <cmath>
33 : #include <cstdlib>
34 : #include <limits>
35 : #include <map>
36 : #include <type_traits>
37 : #include <string_view>
38 : #include <regex>
39 :
40 : #ifndef _
41 : #define _(x) (x)
42 : #endif
43 :
44 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
45 :
46 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
47 :
48 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
49 :
50 : //! @cond Doxygen_Suppress
51 : struct GDALAlgorithmArgHS
52 : {
53 : GDALAlgorithmArg *ptr = nullptr;
54 :
55 338973 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
56 : {
57 338973 : }
58 : };
59 :
60 : //! @endcond
61 :
62 : //! @cond Doxygen_Suppress
63 : struct GDALArgDatasetValueHS
64 : {
65 : GDALArgDatasetValue val{};
66 : GDALArgDatasetValue *ptr = nullptr;
67 :
68 1 : GDALArgDatasetValueHS() : ptr(&val)
69 : {
70 1 : }
71 :
72 3166 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
73 : {
74 3166 : }
75 :
76 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
77 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
78 : };
79 :
80 : //! @endcond
81 :
82 : /************************************************************************/
83 : /* GDALAlgorithmArgTypeIsList() */
84 : /************************************************************************/
85 :
86 407419 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
87 : {
88 407419 : switch (type)
89 : {
90 268224 : case GAAT_BOOLEAN:
91 : case GAAT_STRING:
92 : case GAAT_INTEGER:
93 : case GAAT_REAL:
94 : case GAAT_DATASET:
95 268224 : break;
96 :
97 139195 : case GAAT_STRING_LIST:
98 : case GAAT_INTEGER_LIST:
99 : case GAAT_REAL_LIST:
100 : case GAAT_DATASET_LIST:
101 139195 : return true;
102 : }
103 :
104 268224 : return false;
105 : }
106 :
107 : /************************************************************************/
108 : /* GDALAlgorithmArgTypeName() */
109 : /************************************************************************/
110 :
111 5459 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
112 : {
113 5459 : switch (type)
114 : {
115 1355 : case GAAT_BOOLEAN:
116 1355 : break;
117 1469 : case GAAT_STRING:
118 1469 : return "string";
119 372 : case GAAT_INTEGER:
120 372 : return "integer";
121 477 : case GAAT_REAL:
122 477 : return "real";
123 251 : case GAAT_DATASET:
124 251 : return "dataset";
125 1016 : case GAAT_STRING_LIST:
126 1016 : return "string_list";
127 93 : case GAAT_INTEGER_LIST:
128 93 : return "integer_list";
129 221 : case GAAT_REAL_LIST:
130 221 : return "real_list";
131 205 : case GAAT_DATASET_LIST:
132 205 : return "dataset_list";
133 : }
134 :
135 1355 : return "boolean";
136 : }
137 :
138 : /************************************************************************/
139 : /* GDALAlgorithmArgDatasetTypeName() */
140 : /************************************************************************/
141 :
142 22125 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
143 : {
144 22125 : std::string ret;
145 22125 : if ((type & GDAL_OF_RASTER) != 0)
146 12147 : ret = "raster";
147 22125 : if ((type & GDAL_OF_VECTOR) != 0)
148 : {
149 10772 : if (!ret.empty())
150 : {
151 1187 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
152 257 : ret += ", ";
153 : else
154 930 : ret += " or ";
155 : }
156 10772 : ret += "vector";
157 : }
158 22125 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
159 : {
160 576 : if (!ret.empty())
161 : {
162 316 : ret += " or ";
163 : }
164 576 : ret += "multidimensional raster";
165 : }
166 22125 : return ret;
167 : }
168 :
169 : /************************************************************************/
170 : /* GDALAlgorithmArgDecl() */
171 : /************************************************************************/
172 :
173 : // cppcheck-suppress uninitMemberVar
174 327690 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
175 : char chShortName,
176 : const std::string &description,
177 327690 : GDALAlgorithmArgType type)
178 : : m_longName(longName),
179 327690 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
180 : m_description(description), m_type(type),
181 655380 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
182 327690 : .toupper()),
183 983070 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
184 : {
185 327690 : if (m_type == GAAT_BOOLEAN)
186 : {
187 136630 : m_defaultValue = false;
188 : }
189 327690 : }
190 :
191 : /************************************************************************/
192 : /* GDALAlgorithmArgDecl::SetMinCount() */
193 : /************************************************************************/
194 :
195 18068 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
196 : {
197 18068 : if (!GDALAlgorithmArgTypeIsList(m_type))
198 : {
199 1 : CPLError(CE_Failure, CPLE_NotSupported,
200 : "SetMinCount() illegal on scalar argument '%s'",
201 1 : GetName().c_str());
202 : }
203 : else
204 : {
205 18067 : m_minCount = count;
206 : }
207 18068 : return *this;
208 : }
209 :
210 : /************************************************************************/
211 : /* GDALAlgorithmArgDecl::SetMaxCount() */
212 : /************************************************************************/
213 :
214 17100 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
215 : {
216 17100 : if (!GDALAlgorithmArgTypeIsList(m_type))
217 : {
218 1 : CPLError(CE_Failure, CPLE_NotSupported,
219 : "SetMaxCount() illegal on scalar argument '%s'",
220 1 : GetName().c_str());
221 : }
222 : else
223 : {
224 17099 : m_maxCount = count;
225 : }
226 17100 : return *this;
227 : }
228 :
229 : /************************************************************************/
230 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
231 : /************************************************************************/
232 :
233 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
234 :
235 : /************************************************************************/
236 : /* GDALAlgorithmArg::Set() */
237 : /************************************************************************/
238 :
239 1196 : bool GDALAlgorithmArg::Set(bool value)
240 : {
241 1196 : if (m_decl.GetType() != GAAT_BOOLEAN)
242 : {
243 14 : CPLError(
244 : CE_Failure, CPLE_AppDefined,
245 : "Calling Set(bool) on argument '%s' of type %s is not supported",
246 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
247 7 : return false;
248 : }
249 1189 : return SetInternal(value);
250 : }
251 :
252 3963 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
253 : {
254 4008 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
255 45 : value.front() == '@')
256 : {
257 2 : GByte *pabyData = nullptr;
258 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
259 2 : 10 * 1024 * 1024))
260 : {
261 : // Remove UTF-8 BOM
262 1 : size_t offset = 0;
263 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
264 1 : pabyData[2] == 0xBF)
265 : {
266 1 : offset = 3;
267 : }
268 1 : value = reinterpret_cast<const char *>(pabyData + offset);
269 1 : VSIFree(pabyData);
270 : }
271 : else
272 : {
273 1 : return false;
274 : }
275 : }
276 :
277 3962 : if (m_decl.IsRemoveSQLCommentsEnabled())
278 44 : value = CPLRemoveSQLComments(value);
279 :
280 3962 : return true;
281 : }
282 :
283 3979 : bool GDALAlgorithmArg::Set(const std::string &value)
284 : {
285 3979 : switch (m_decl.GetType())
286 : {
287 9 : case GAAT_BOOLEAN:
288 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
289 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
290 : {
291 4 : return Set(true);
292 : }
293 5 : else if (EQUAL(value.c_str(), "0") ||
294 4 : EQUAL(value.c_str(), "FALSE") ||
295 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
296 : {
297 4 : return Set(false);
298 : }
299 1 : break;
300 :
301 8 : case GAAT_INTEGER:
302 : case GAAT_INTEGER_LIST:
303 : {
304 8 : errno = 0;
305 8 : char *endptr = nullptr;
306 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
307 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
308 5 : endptr == value.c_str() + value.size())
309 : {
310 3 : if (m_decl.GetType() == GAAT_INTEGER)
311 3 : return Set(static_cast<int>(v));
312 : else
313 1 : return Set(std::vector<int>{static_cast<int>(v)});
314 : }
315 5 : break;
316 : }
317 :
318 5 : case GAAT_REAL:
319 : case GAAT_REAL_LIST:
320 : {
321 5 : char *endptr = nullptr;
322 5 : const double v = CPLStrtod(value.c_str(), &endptr);
323 5 : if (endptr == value.c_str() + value.size())
324 : {
325 3 : if (m_decl.GetType() == GAAT_REAL)
326 3 : return Set(v);
327 : else
328 1 : return Set(std::vector<double>{v});
329 : }
330 2 : break;
331 : }
332 :
333 3941 : case GAAT_STRING:
334 3941 : break;
335 :
336 1 : case GAAT_STRING_LIST:
337 2 : return Set(std::vector<std::string>{value});
338 :
339 15 : case GAAT_DATASET:
340 15 : return SetDatasetName(value);
341 :
342 0 : case GAAT_DATASET_LIST:
343 : {
344 0 : std::vector<GDALArgDatasetValue> v;
345 0 : v.resize(1);
346 0 : v[0].Set(value);
347 0 : return Set(std::move(v));
348 : }
349 : }
350 :
351 3949 : if (m_decl.GetType() != GAAT_STRING)
352 : {
353 16 : CPLError(CE_Failure, CPLE_AppDefined,
354 : "Calling Set(std::string) on argument '%s' of type %s is not "
355 : "supported",
356 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
357 8 : return false;
358 : }
359 :
360 3941 : std::string newValue(value);
361 3941 : return ProcessString(newValue) && SetInternal(newValue);
362 : }
363 :
364 848 : bool GDALAlgorithmArg::Set(int value)
365 : {
366 848 : if (m_decl.GetType() == GAAT_BOOLEAN)
367 : {
368 3 : if (value == 1)
369 1 : return Set(true);
370 2 : else if (value == 0)
371 1 : return Set(false);
372 : }
373 845 : else if (m_decl.GetType() == GAAT_REAL)
374 : {
375 3 : return Set(static_cast<double>(value));
376 : }
377 842 : else if (m_decl.GetType() == GAAT_STRING)
378 : {
379 2 : return Set(std::to_string(value));
380 : }
381 840 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
382 : {
383 1 : return Set(std::vector<int>{value});
384 : }
385 839 : else if (m_decl.GetType() == GAAT_REAL_LIST)
386 : {
387 1 : return Set(std::vector<double>{static_cast<double>(value)});
388 : }
389 838 : else if (m_decl.GetType() == GAAT_STRING_LIST)
390 : {
391 2 : return Set(std::vector<std::string>{std::to_string(value)});
392 : }
393 :
394 838 : if (m_decl.GetType() != GAAT_INTEGER)
395 : {
396 2 : CPLError(
397 : CE_Failure, CPLE_AppDefined,
398 : "Calling Set(int) on argument '%s' of type %s is not supported",
399 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
400 1 : return false;
401 : }
402 837 : return SetInternal(value);
403 : }
404 :
405 290 : bool GDALAlgorithmArg::Set(double value)
406 : {
407 293 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
408 293 : value <= INT_MAX && static_cast<int>(value) == value)
409 : {
410 2 : return Set(static_cast<int>(value));
411 : }
412 288 : else if (m_decl.GetType() == GAAT_STRING)
413 : {
414 2 : return Set(std::to_string(value));
415 : }
416 288 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
417 288 : value <= INT_MAX && static_cast<int>(value) == value)
418 : {
419 1 : return Set(std::vector<int>{static_cast<int>(value)});
420 : }
421 285 : else if (m_decl.GetType() == GAAT_REAL_LIST)
422 : {
423 0 : return Set(std::vector<double>{value});
424 : }
425 285 : else if (m_decl.GetType() == GAAT_STRING_LIST)
426 : {
427 2 : return Set(std::vector<std::string>{std::to_string(value)});
428 : }
429 284 : else if (m_decl.GetType() != GAAT_REAL)
430 : {
431 6 : CPLError(
432 : CE_Failure, CPLE_AppDefined,
433 : "Calling Set(double) on argument '%s' of type %s is not supported",
434 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
435 3 : return false;
436 : }
437 281 : return SetInternal(value);
438 : }
439 :
440 6127 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
441 : {
442 6130 : if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
443 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
444 : {
445 3 : CPLError(
446 : CE_Failure, CPLE_AppDefined,
447 : "Dataset object '%s' is created by algorithm and cannot be set "
448 : "as an input.",
449 3 : arg->GetName().c_str());
450 3 : return false;
451 : }
452 6124 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
453 : {
454 8 : CPLError(CE_Failure, CPLE_AppDefined,
455 : "Dataset%s '%s' must be provided by name, not as object.",
456 8 : arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
457 4 : return false;
458 : }
459 :
460 6120 : return true;
461 : }
462 :
463 9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
464 : {
465 9 : if (m_decl.GetType() != GAAT_DATASET)
466 : {
467 2 : CPLError(CE_Failure, CPLE_AppDefined,
468 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
469 : "is not supported",
470 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
471 1 : return false;
472 : }
473 8 : if (!CheckCanSetDatasetObject(this))
474 2 : return false;
475 6 : m_explicitlySet = true;
476 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
477 6 : val.Set(ds);
478 6 : return RunAllActions();
479 : }
480 :
481 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
482 : {
483 3 : if (m_decl.GetType() != GAAT_DATASET)
484 : {
485 2 : CPLError(CE_Failure, CPLE_AppDefined,
486 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
487 : "is not supported",
488 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
489 1 : return false;
490 : }
491 2 : if (!CheckCanSetDatasetObject(this))
492 1 : return false;
493 1 : m_explicitlySet = true;
494 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
495 1 : val.Set(std::move(ds));
496 1 : return RunAllActions();
497 : }
498 :
499 557 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
500 : {
501 557 : if (m_decl.GetType() != GAAT_DATASET)
502 : {
503 2 : CPLError(CE_Failure, CPLE_AppDefined,
504 : "Calling SetDatasetName() on argument '%s' of type %s is "
505 : "not supported",
506 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
507 1 : return false;
508 : }
509 556 : m_explicitlySet = true;
510 556 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
511 556 : return RunAllActions();
512 : }
513 :
514 961 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
515 : {
516 961 : if (m_decl.GetType() != GAAT_DATASET)
517 : {
518 2 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "Calling SetFrom() 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 960 : if (!CheckCanSetDatasetObject(this))
525 1 : return false;
526 959 : m_explicitlySet = true;
527 959 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
528 959 : return RunAllActions();
529 : }
530 :
531 1048 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
532 : {
533 1048 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
534 : {
535 3 : std::vector<int> v_i;
536 4 : for (const std::string &s : value)
537 : {
538 3 : errno = 0;
539 3 : char *endptr = nullptr;
540 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
541 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
542 2 : endptr == s.c_str() + s.size())
543 : {
544 1 : v_i.push_back(static_cast<int>(v));
545 : }
546 : else
547 : {
548 2 : break;
549 : }
550 : }
551 3 : if (v_i.size() == value.size())
552 1 : return Set(v_i);
553 : }
554 1045 : else if (m_decl.GetType() == GAAT_REAL_LIST)
555 : {
556 2 : std::vector<double> v_d;
557 3 : for (const std::string &s : value)
558 : {
559 2 : char *endptr = nullptr;
560 2 : const double v = CPLStrtod(s.c_str(), &endptr);
561 2 : if (endptr == s.c_str() + s.size())
562 : {
563 1 : v_d.push_back(v);
564 : }
565 : else
566 : {
567 1 : break;
568 : }
569 : }
570 2 : if (v_d.size() == value.size())
571 1 : return Set(v_d);
572 : }
573 2084 : else if ((m_decl.GetType() == GAAT_INTEGER ||
574 2081 : m_decl.GetType() == GAAT_REAL ||
575 3126 : m_decl.GetType() == GAAT_STRING) &&
576 5 : value.size() == 1)
577 : {
578 4 : return Set(value[0]);
579 : }
580 1039 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
581 : {
582 30 : std::vector<GDALArgDatasetValue> dsVector;
583 46 : for (const std::string &s : value)
584 31 : dsVector.emplace_back(s);
585 15 : return Set(std::move(dsVector));
586 : }
587 :
588 1027 : if (m_decl.GetType() != GAAT_STRING_LIST)
589 : {
590 10 : CPLError(CE_Failure, CPLE_AppDefined,
591 : "Calling Set(const std::vector<std::string> &) on argument "
592 : "'%s' of type %s is not supported",
593 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
594 5 : return false;
595 : }
596 :
597 2025 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
598 1003 : m_decl.IsRemoveSQLCommentsEnabled())
599 : {
600 38 : std::vector<std::string> newValue(value);
601 41 : for (auto &s : newValue)
602 : {
603 22 : if (!ProcessString(s))
604 0 : return false;
605 : }
606 19 : return SetInternal(newValue);
607 : }
608 : else
609 : {
610 1003 : return SetInternal(value);
611 : }
612 : }
613 :
614 172 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
615 : {
616 172 : if (m_decl.GetType() == GAAT_REAL_LIST)
617 : {
618 2 : std::vector<double> v_d;
619 2 : for (int i : value)
620 1 : v_d.push_back(i);
621 1 : return Set(v_d);
622 : }
623 171 : else if (m_decl.GetType() == GAAT_STRING_LIST)
624 : {
625 2 : std::vector<std::string> v_s;
626 3 : for (int i : value)
627 2 : v_s.push_back(std::to_string(i));
628 1 : return Set(v_s);
629 : }
630 338 : else if ((m_decl.GetType() == GAAT_INTEGER ||
631 334 : m_decl.GetType() == GAAT_REAL ||
632 506 : m_decl.GetType() == GAAT_STRING) &&
633 5 : value.size() == 1)
634 : {
635 3 : return Set(value[0]);
636 : }
637 :
638 167 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
639 : {
640 6 : CPLError(CE_Failure, CPLE_AppDefined,
641 : "Calling Set(const std::vector<int> &) on argument '%s' of "
642 : "type %s is not supported",
643 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
644 3 : return false;
645 : }
646 164 : return SetInternal(value);
647 : }
648 :
649 266 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
650 : {
651 266 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
652 : {
653 2 : std::vector<int> v_i;
654 3 : for (double d : value)
655 : {
656 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
657 : {
658 1 : v_i.push_back(static_cast<int>(d));
659 : }
660 : else
661 : {
662 : break;
663 : }
664 : }
665 2 : if (v_i.size() == value.size())
666 1 : return Set(v_i);
667 : }
668 264 : else if (m_decl.GetType() == GAAT_STRING_LIST)
669 : {
670 2 : std::vector<std::string> v_s;
671 3 : for (double d : value)
672 2 : v_s.push_back(std::to_string(d));
673 1 : return Set(v_s);
674 : }
675 525 : else if ((m_decl.GetType() == GAAT_INTEGER ||
676 523 : m_decl.GetType() == GAAT_REAL ||
677 787 : m_decl.GetType() == GAAT_STRING) &&
678 3 : value.size() == 1)
679 : {
680 3 : return Set(value[0]);
681 : }
682 :
683 261 : if (m_decl.GetType() != GAAT_REAL_LIST)
684 : {
685 4 : CPLError(CE_Failure, CPLE_AppDefined,
686 : "Calling Set(const std::vector<double> &) on argument '%s' of "
687 : "type %s is not supported",
688 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
689 2 : return false;
690 : }
691 259 : return SetInternal(value);
692 : }
693 :
694 3379 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
695 : {
696 3379 : if (m_decl.GetType() != GAAT_DATASET_LIST)
697 : {
698 2 : CPLError(CE_Failure, CPLE_AppDefined,
699 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
700 : "argument '%s' of type %s is not supported",
701 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
702 1 : return false;
703 : }
704 3378 : m_explicitlySet = true;
705 3378 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
706 3378 : return RunAllActions();
707 : }
708 :
709 : GDALAlgorithmArg &
710 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
711 : {
712 0 : Set(std::move(value));
713 0 : return *this;
714 : }
715 :
716 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
717 : {
718 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
719 1 : return Set(value.exportToWkt(apszOptions));
720 : }
721 :
722 4153 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
723 : {
724 4153 : if (m_decl.GetType() != other.GetType())
725 : {
726 2 : CPLError(CE_Failure, CPLE_AppDefined,
727 : "Calling SetFrom() on argument '%s' of type %s whereas "
728 : "other argument type is %s is not supported",
729 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
730 : GDALAlgorithmArgTypeName(other.GetType()));
731 1 : return false;
732 : }
733 :
734 4152 : switch (m_decl.GetType())
735 : {
736 90 : case GAAT_BOOLEAN:
737 90 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
738 90 : break;
739 802 : case GAAT_STRING:
740 1604 : *std::get<std::string *>(m_value) =
741 802 : *std::get<std::string *>(other.m_value);
742 802 : break;
743 6 : case GAAT_INTEGER:
744 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
745 6 : break;
746 1 : case GAAT_REAL:
747 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
748 1 : break;
749 955 : case GAAT_DATASET:
750 955 : return SetFrom(other.Get<GDALArgDatasetValue>());
751 39 : case GAAT_STRING_LIST:
752 78 : *std::get<std::vector<std::string> *>(m_value) =
753 39 : *std::get<std::vector<std::string> *>(other.m_value);
754 39 : break;
755 1 : case GAAT_INTEGER_LIST:
756 2 : *std::get<std::vector<int> *>(m_value) =
757 1 : *std::get<std::vector<int> *>(other.m_value);
758 1 : break;
759 1 : case GAAT_REAL_LIST:
760 2 : *std::get<std::vector<double> *>(m_value) =
761 1 : *std::get<std::vector<double> *>(other.m_value);
762 1 : break;
763 2257 : case GAAT_DATASET_LIST:
764 : {
765 2257 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
766 2262 : for (const auto &val :
767 6781 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
768 : {
769 4524 : GDALArgDatasetValue v;
770 2262 : v.SetFrom(val);
771 2262 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
772 2262 : ->push_back(std::move(v));
773 : }
774 2257 : break;
775 : }
776 : }
777 3197 : m_explicitlySet = true;
778 3197 : return RunAllActions();
779 : }
780 :
781 : /************************************************************************/
782 : /* GDALAlgorithmArg::RunAllActions() */
783 : /************************************************************************/
784 :
785 15789 : bool GDALAlgorithmArg::RunAllActions()
786 : {
787 15789 : if (!RunValidationActions())
788 147 : return false;
789 15642 : RunActions();
790 15642 : return true;
791 : }
792 :
793 : /************************************************************************/
794 : /* GDALAlgorithmArg::RunActions() */
795 : /************************************************************************/
796 :
797 15643 : void GDALAlgorithmArg::RunActions()
798 : {
799 15939 : for (const auto &f : m_actions)
800 296 : f();
801 15643 : }
802 :
803 : /************************************************************************/
804 : /* GDALAlgorithmArg::ValidateChoice() */
805 : /************************************************************************/
806 :
807 : // Returns the canonical value if matching a valid choice, or empty string
808 : // otherwise.
809 2574 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
810 : {
811 15272 : for (const std::string &choice : GetChoices())
812 : {
813 15154 : if (EQUAL(value.c_str(), choice.c_str()))
814 : {
815 2456 : return choice;
816 : }
817 : }
818 :
819 190 : for (const std::string &choice : GetHiddenChoices())
820 : {
821 172 : if (EQUAL(value.c_str(), choice.c_str()))
822 : {
823 100 : return choice;
824 : }
825 : }
826 :
827 36 : std::string expected;
828 220 : for (const auto &choice : GetChoices())
829 : {
830 202 : if (!expected.empty())
831 184 : expected += ", ";
832 202 : expected += '\'';
833 202 : expected += choice;
834 202 : expected += '\'';
835 : }
836 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
837 : {
838 6 : return "?";
839 : }
840 24 : CPLError(CE_Failure, CPLE_IllegalArg,
841 : "Invalid value '%s' for string argument '%s'. Should be "
842 : "one among %s.",
843 12 : value.c_str(), GetName().c_str(), expected.c_str());
844 12 : return std::string();
845 : }
846 :
847 : /************************************************************************/
848 : /* GDALAlgorithmArg::ValidateIntRange() */
849 : /************************************************************************/
850 :
851 2680 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
852 : {
853 2680 : bool ret = true;
854 :
855 2680 : const auto [minVal, minValIsIncluded] = GetMinValue();
856 2680 : if (!std::isnan(minVal))
857 : {
858 2030 : if (minValIsIncluded && val < minVal)
859 : {
860 3 : CPLError(CE_Failure, CPLE_IllegalArg,
861 : "Value of argument '%s' is %d, but should be >= %d",
862 3 : GetName().c_str(), val, static_cast<int>(minVal));
863 3 : ret = false;
864 : }
865 2027 : else if (!minValIsIncluded && val <= minVal)
866 : {
867 1 : CPLError(CE_Failure, CPLE_IllegalArg,
868 : "Value of argument '%s' is %d, but should be > %d",
869 1 : GetName().c_str(), val, static_cast<int>(minVal));
870 1 : ret = false;
871 : }
872 : }
873 :
874 2680 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
875 2680 : if (!std::isnan(maxVal))
876 : {
877 :
878 382 : if (maxValIsIncluded && val > maxVal)
879 : {
880 1 : CPLError(CE_Failure, CPLE_IllegalArg,
881 : "Value of argument '%s' is %d, but should be <= %d",
882 1 : GetName().c_str(), val, static_cast<int>(maxVal));
883 1 : ret = false;
884 : }
885 381 : else if (!maxValIsIncluded && val >= maxVal)
886 : {
887 1 : CPLError(CE_Failure, CPLE_IllegalArg,
888 : "Value of argument '%s' is %d, but should be < %d",
889 1 : GetName().c_str(), val, static_cast<int>(maxVal));
890 1 : ret = false;
891 : }
892 : }
893 :
894 2680 : return ret;
895 : }
896 :
897 : /************************************************************************/
898 : /* GDALAlgorithmArg::ValidateRealRange() */
899 : /************************************************************************/
900 :
901 2126 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
902 : {
903 2126 : bool ret = true;
904 :
905 2126 : const auto [minVal, minValIsIncluded] = GetMinValue();
906 2126 : if (!std::isnan(minVal))
907 : {
908 214 : if (minValIsIncluded && !(val >= minVal))
909 : {
910 11 : CPLError(CE_Failure, CPLE_IllegalArg,
911 : "Value of argument '%s' is %g, but should be >= %g",
912 11 : GetName().c_str(), val, minVal);
913 11 : ret = false;
914 : }
915 203 : else if (!minValIsIncluded && !(val > minVal))
916 : {
917 4 : CPLError(CE_Failure, CPLE_IllegalArg,
918 : "Value of argument '%s' is %g, but should be > %g",
919 4 : GetName().c_str(), val, minVal);
920 4 : ret = false;
921 : }
922 : }
923 :
924 2126 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
925 2126 : if (!std::isnan(maxVal))
926 : {
927 :
928 58 : if (maxValIsIncluded && !(val <= maxVal))
929 : {
930 2 : CPLError(CE_Failure, CPLE_IllegalArg,
931 : "Value of argument '%s' is %g, but should be <= %g",
932 2 : GetName().c_str(), val, maxVal);
933 2 : ret = false;
934 : }
935 56 : else if (!maxValIsIncluded && !(val < maxVal))
936 : {
937 1 : CPLError(CE_Failure, CPLE_IllegalArg,
938 : "Value of argument '%s' is %g, but should be < %g",
939 1 : GetName().c_str(), val, maxVal);
940 1 : ret = false;
941 : }
942 : }
943 :
944 2126 : return ret;
945 : }
946 :
947 : /************************************************************************/
948 : /* CheckDuplicateValues() */
949 : /************************************************************************/
950 :
951 : template <class T>
952 92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
953 : const std::vector<T> &values)
954 : {
955 184 : auto tmpValues = values;
956 92 : bool bHasDupValues = false;
957 : if constexpr (std::is_floating_point_v<T>)
958 : {
959 : // Avoid undefined behavior with NaN values
960 4 : std::sort(tmpValues.begin(), tmpValues.end(),
961 21 : [](T a, T b)
962 : {
963 21 : if (std::isnan(a) && !std::isnan(b))
964 3 : return true;
965 18 : if (std::isnan(b))
966 10 : return false;
967 8 : return a < b;
968 : });
969 :
970 : bHasDupValues =
971 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
972 6 : [](T a, T b)
973 : {
974 6 : if (std::isnan(a) && std::isnan(b))
975 1 : return true;
976 5 : return a == b;
977 8 : }) != tmpValues.end();
978 : }
979 : else
980 : {
981 88 : std::sort(tmpValues.begin(), tmpValues.end());
982 88 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
983 176 : tmpValues.end()) != tmpValues.end();
984 : }
985 92 : if (bHasDupValues)
986 : {
987 10 : CPLError(CE_Failure, CPLE_AppDefined,
988 : "'%s' must be a list of unique values.",
989 10 : arg->GetName().c_str());
990 10 : return false;
991 : }
992 82 : return true;
993 : }
994 :
995 : /************************************************************************/
996 : /* GDALAlgorithmArg::RunValidationActions() */
997 : /************************************************************************/
998 :
999 34931 : bool GDALAlgorithmArg::RunValidationActions()
1000 : {
1001 34931 : bool ret = true;
1002 :
1003 34931 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1004 : {
1005 1655 : auto &val = Get<std::string>();
1006 3310 : std::string validVal = ValidateChoice(val);
1007 1655 : if (validVal.empty())
1008 7 : ret = false;
1009 : else
1010 1648 : val = std::move(validVal);
1011 : }
1012 33276 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1013 : {
1014 659 : auto &values = Get<std::vector<std::string>>();
1015 1578 : for (std::string &val : values)
1016 : {
1017 1838 : std::string validVal = ValidateChoice(val);
1018 919 : if (validVal.empty())
1019 5 : ret = false;
1020 : else
1021 914 : val = std::move(validVal);
1022 : }
1023 : }
1024 :
1025 : const auto CheckMinCharCount =
1026 946 : [this, &ret](const std::string &val, int nMinCharCount)
1027 : {
1028 934 : if (val.size() < static_cast<size_t>(nMinCharCount))
1029 : {
1030 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1031 : "Value of argument '%s' is '%s', but should have at least "
1032 : "%d character%s",
1033 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1034 : nMinCharCount > 1 ? "s" : "");
1035 6 : ret = false;
1036 : }
1037 35865 : };
1038 :
1039 : const auto CheckMaxCharCount =
1040 12538 : [this, &ret](const std::string &val, int nMaxCharCount)
1041 : {
1042 12536 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1043 : {
1044 2 : CPLError(
1045 : CE_Failure, CPLE_IllegalArg,
1046 : "Value of argument '%s' is '%s', but should have no more than "
1047 : "%d character%s",
1048 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1049 : nMaxCharCount > 1 ? "s" : "");
1050 1 : ret = false;
1051 : }
1052 47467 : };
1053 :
1054 34931 : switch (GetType())
1055 : {
1056 2700 : case GAAT_BOOLEAN:
1057 2700 : break;
1058 :
1059 9821 : case GAAT_STRING:
1060 : {
1061 9821 : const auto &val = Get<std::string>();
1062 9821 : const int nMinCharCount = GetMinCharCount();
1063 9821 : if (nMinCharCount > 0)
1064 : {
1065 852 : CheckMinCharCount(val, nMinCharCount);
1066 : }
1067 :
1068 9821 : const int nMaxCharCount = GetMaxCharCount();
1069 9821 : CheckMaxCharCount(val, nMaxCharCount);
1070 9821 : break;
1071 : }
1072 :
1073 2210 : case GAAT_STRING_LIST:
1074 : {
1075 2210 : const int nMinCharCount = GetMinCharCount();
1076 2210 : const int nMaxCharCount = GetMaxCharCount();
1077 2210 : const auto &values = Get<std::vector<std::string>>();
1078 4925 : for (const auto &val : values)
1079 : {
1080 2715 : if (nMinCharCount > 0)
1081 82 : CheckMinCharCount(val, nMinCharCount);
1082 2715 : CheckMaxCharCount(val, nMaxCharCount);
1083 : }
1084 :
1085 2284 : if (!GetDuplicateValuesAllowed() &&
1086 74 : !CheckDuplicateValues(this, values))
1087 2 : ret = false;
1088 2210 : break;
1089 : }
1090 :
1091 1982 : case GAAT_INTEGER:
1092 : {
1093 1982 : ret = ValidateIntRange(Get<int>()) && ret;
1094 1982 : break;
1095 : }
1096 :
1097 344 : case GAAT_INTEGER_LIST:
1098 : {
1099 344 : const auto &values = Get<std::vector<int>>();
1100 1042 : for (int v : values)
1101 698 : ret = ValidateIntRange(v) && ret;
1102 :
1103 347 : if (!GetDuplicateValuesAllowed() &&
1104 3 : !CheckDuplicateValues(this, values))
1105 1 : ret = false;
1106 344 : break;
1107 : }
1108 :
1109 547 : case GAAT_REAL:
1110 : {
1111 547 : ret = ValidateRealRange(Get<double>()) && ret;
1112 547 : break;
1113 : }
1114 :
1115 572 : case GAAT_REAL_LIST:
1116 : {
1117 572 : const auto &values = Get<std::vector<double>>();
1118 2151 : for (double v : values)
1119 1579 : ret = ValidateRealRange(v) && ret;
1120 :
1121 576 : if (!GetDuplicateValuesAllowed() &&
1122 4 : !CheckDuplicateValues(this, values))
1123 2 : ret = false;
1124 572 : break;
1125 : }
1126 :
1127 5709 : case GAAT_DATASET:
1128 5709 : break;
1129 :
1130 11046 : case GAAT_DATASET_LIST:
1131 : {
1132 11046 : if (!GetDuplicateValuesAllowed())
1133 : {
1134 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1135 22 : std::vector<std::string> aosValues;
1136 34 : for (const auto &v : values)
1137 : {
1138 23 : const GDALDataset *poDS = v.GetDatasetRef();
1139 23 : if (poDS)
1140 : {
1141 16 : auto poDriver = poDS->GetDriver();
1142 : // The dataset name for a MEM driver is not relevant,
1143 : // so use the pointer address
1144 32 : if ((poDriver &&
1145 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1146 8 : poDS->GetDescription()[0] == 0)
1147 : {
1148 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1149 : }
1150 : else
1151 : {
1152 8 : aosValues.push_back(poDS->GetDescription());
1153 : }
1154 : }
1155 : else
1156 : {
1157 7 : aosValues.push_back(v.GetName());
1158 : }
1159 : }
1160 11 : if (!CheckDuplicateValues(this, aosValues))
1161 5 : ret = false;
1162 : }
1163 11046 : break;
1164 : }
1165 : }
1166 :
1167 34931 : if (GDALAlgorithmArgTypeIsList(GetType()))
1168 : {
1169 14172 : int valueCount = 0;
1170 14172 : if (GetType() == GAAT_STRING_LIST)
1171 : {
1172 2210 : valueCount =
1173 2210 : static_cast<int>(Get<std::vector<std::string>>().size());
1174 : }
1175 11962 : else if (GetType() == GAAT_INTEGER_LIST)
1176 : {
1177 344 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1178 : }
1179 11618 : else if (GetType() == GAAT_REAL_LIST)
1180 : {
1181 572 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1182 : }
1183 11046 : else if (GetType() == GAAT_DATASET_LIST)
1184 : {
1185 11046 : valueCount = static_cast<int>(
1186 11046 : Get<std::vector<GDALArgDatasetValue>>().size());
1187 : }
1188 :
1189 14172 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1190 : {
1191 14 : ReportError(CE_Failure, CPLE_AppDefined,
1192 : "%d value%s been specified for argument '%s', "
1193 : "whereas exactly %d %s expected.",
1194 : valueCount, valueCount > 1 ? "s have" : " has",
1195 7 : GetName().c_str(), GetMinCount(),
1196 7 : GetMinCount() > 1 ? "were" : "was");
1197 7 : ret = false;
1198 : }
1199 14165 : else if (valueCount < GetMinCount())
1200 : {
1201 6 : ReportError(CE_Failure, CPLE_AppDefined,
1202 : "Only %d value%s been specified for argument '%s', "
1203 : "whereas at least %d %s expected.",
1204 : valueCount, valueCount > 1 ? "s have" : " has",
1205 3 : GetName().c_str(), GetMinCount(),
1206 3 : GetMinCount() > 1 ? "were" : "was");
1207 3 : ret = false;
1208 : }
1209 14162 : else if (valueCount > GetMaxCount())
1210 : {
1211 2 : ReportError(CE_Failure, CPLE_AppDefined,
1212 : "%d value%s been specified for argument '%s', "
1213 : "whereas at most %d %s expected.",
1214 : valueCount, valueCount > 1 ? "s have" : " has",
1215 1 : GetName().c_str(), GetMaxCount(),
1216 1 : GetMaxCount() > 1 ? "were" : "was");
1217 1 : ret = false;
1218 : }
1219 : }
1220 :
1221 34931 : if (ret)
1222 : {
1223 41592 : for (const auto &f : m_validationActions)
1224 : {
1225 6725 : if (!f())
1226 92 : ret = false;
1227 : }
1228 : }
1229 :
1230 34931 : return ret;
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* GDALAlgorithmArg::ReportError() */
1235 : /************************************************************************/
1236 :
1237 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1238 : const char *fmt, ...) const
1239 : {
1240 : va_list args;
1241 11 : va_start(args, fmt);
1242 11 : if (m_owner)
1243 : {
1244 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1245 22 : CPLString().vPrintf(fmt, args).c_str());
1246 : }
1247 : else
1248 : {
1249 0 : CPLError(eErrClass, err_no, "%s",
1250 0 : CPLString().vPrintf(fmt, args).c_str());
1251 : }
1252 11 : va_end(args);
1253 11 : }
1254 :
1255 : /************************************************************************/
1256 : /* GDALAlgorithmArg::GetEscapedString() */
1257 : /************************************************************************/
1258 :
1259 : /* static */
1260 134 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1261 : {
1262 146 : if (s.find_first_of("\" \\,") != std::string::npos &&
1263 6 : !(s.size() > 4 &&
1264 6 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1265 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1266 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1267 : {
1268 8 : return std::string("\"")
1269 : .append(
1270 8 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1271 4 : .append("\"");
1272 : }
1273 : else
1274 : {
1275 130 : return s;
1276 : }
1277 : }
1278 :
1279 : /************************************************************************/
1280 : /* GDALAlgorithmArg::Serialize() */
1281 : /************************************************************************/
1282 :
1283 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1284 : bool absolutePath) const
1285 : {
1286 39 : serializedArg.clear();
1287 :
1288 39 : if (!IsExplicitlySet())
1289 : {
1290 0 : return false;
1291 : }
1292 :
1293 78 : std::string ret = "--";
1294 39 : ret += GetName();
1295 39 : if (GetType() == GAAT_BOOLEAN)
1296 : {
1297 0 : serializedArg = std::move(ret);
1298 0 : return true;
1299 : }
1300 :
1301 5 : const auto AddListValueSeparator = [this, &ret]()
1302 : {
1303 1 : if (GetPackedValuesAllowed())
1304 : {
1305 0 : ret += ',';
1306 : }
1307 : else
1308 : {
1309 1 : ret += " --";
1310 1 : ret += GetName();
1311 1 : ret += ' ';
1312 : }
1313 40 : };
1314 :
1315 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1316 : {
1317 : VSIStatBufL sStat;
1318 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1319 0 : !CPLIsFilenameRelative(filename.c_str()))
1320 0 : return filename;
1321 0 : char *pszCWD = CPLGetCurrentDir();
1322 0 : if (!pszCWD)
1323 0 : return filename;
1324 : const auto absPath =
1325 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1326 0 : CPLFree(pszCWD);
1327 0 : return absPath;
1328 : };
1329 :
1330 39 : ret += ' ';
1331 39 : switch (GetType())
1332 : {
1333 0 : case GAAT_BOOLEAN:
1334 0 : break;
1335 8 : case GAAT_STRING:
1336 : {
1337 8 : const auto &val = Get<std::string>();
1338 8 : ret += GetEscapedString(val);
1339 8 : break;
1340 : }
1341 0 : case GAAT_INTEGER:
1342 : {
1343 0 : ret += CPLSPrintf("%d", Get<int>());
1344 0 : break;
1345 : }
1346 0 : case GAAT_REAL:
1347 : {
1348 0 : ret += CPLSPrintf("%.17g", Get<double>());
1349 0 : break;
1350 : }
1351 2 : case GAAT_DATASET:
1352 : {
1353 2 : const auto &val = Get<GDALArgDatasetValue>();
1354 2 : const auto &str = val.GetName();
1355 2 : if (str.empty())
1356 : {
1357 0 : return false;
1358 : }
1359 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1360 2 : break;
1361 : }
1362 4 : case GAAT_STRING_LIST:
1363 : {
1364 4 : const auto &vals = Get<std::vector<std::string>>();
1365 8 : for (size_t i = 0; i < vals.size(); ++i)
1366 : {
1367 4 : if (i > 0)
1368 1 : AddListValueSeparator();
1369 4 : ret += GetEscapedString(vals[i]);
1370 : }
1371 4 : break;
1372 : }
1373 0 : case GAAT_INTEGER_LIST:
1374 : {
1375 0 : const auto &vals = Get<std::vector<int>>();
1376 0 : for (size_t i = 0; i < vals.size(); ++i)
1377 : {
1378 0 : if (i > 0)
1379 0 : AddListValueSeparator();
1380 0 : ret += CPLSPrintf("%d", vals[i]);
1381 : }
1382 0 : break;
1383 : }
1384 0 : case GAAT_REAL_LIST:
1385 : {
1386 0 : const auto &vals = Get<std::vector<double>>();
1387 0 : for (size_t i = 0; i < vals.size(); ++i)
1388 : {
1389 0 : if (i > 0)
1390 0 : AddListValueSeparator();
1391 0 : ret += CPLSPrintf("%.17g", vals[i]);
1392 : }
1393 0 : break;
1394 : }
1395 25 : case GAAT_DATASET_LIST:
1396 : {
1397 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1398 49 : for (size_t i = 0; i < vals.size(); ++i)
1399 : {
1400 25 : if (i > 0)
1401 0 : AddListValueSeparator();
1402 25 : const auto &val = vals[i];
1403 25 : const auto &str = val.GetName();
1404 25 : if (str.empty())
1405 : {
1406 1 : return false;
1407 : }
1408 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1409 24 : : str);
1410 : }
1411 24 : break;
1412 : }
1413 : }
1414 :
1415 38 : serializedArg = std::move(ret);
1416 38 : return true;
1417 : }
1418 :
1419 : /************************************************************************/
1420 : /* ~GDALInConstructionAlgorithmArg() */
1421 : /************************************************************************/
1422 :
1423 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1424 :
1425 : /************************************************************************/
1426 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1427 : /************************************************************************/
1428 :
1429 : GDALInConstructionAlgorithmArg &
1430 62676 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1431 : {
1432 62676 : m_decl.AddAlias(alias);
1433 62676 : if (m_owner)
1434 62676 : m_owner->AddAliasFor(this, alias);
1435 62676 : return *this;
1436 : }
1437 :
1438 : /************************************************************************/
1439 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1440 : /************************************************************************/
1441 :
1442 : GDALInConstructionAlgorithmArg &
1443 16627 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1444 : {
1445 16627 : m_decl.AddHiddenAlias(alias);
1446 16627 : if (m_owner)
1447 16627 : m_owner->AddAliasFor(this, alias);
1448 16627 : return *this;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1453 : /************************************************************************/
1454 :
1455 : GDALInConstructionAlgorithmArg &
1456 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1457 : {
1458 48 : m_decl.AddShortNameAlias(shortNameAlias);
1459 48 : if (m_owner)
1460 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1461 48 : return *this;
1462 : }
1463 :
1464 : /************************************************************************/
1465 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1466 : /************************************************************************/
1467 :
1468 21395 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1469 : {
1470 21395 : m_decl.SetPositional();
1471 21395 : if (m_owner)
1472 21395 : m_owner->SetPositional(this);
1473 21395 : return *this;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1478 : /************************************************************************/
1479 :
1480 1297 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1481 2594 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1482 1297 : m_nameSet(true)
1483 : {
1484 1297 : if (m_poDS)
1485 1297 : m_poDS->Reference();
1486 1297 : }
1487 :
1488 : /************************************************************************/
1489 : /* GDALArgDatasetValue::Set() */
1490 : /************************************************************************/
1491 :
1492 2250 : void GDALArgDatasetValue::Set(const std::string &name)
1493 : {
1494 2250 : Close();
1495 2250 : m_name = name;
1496 2250 : m_nameSet = true;
1497 2250 : if (m_ownerArg)
1498 2245 : m_ownerArg->NotifyValueSet();
1499 2250 : }
1500 :
1501 : /************************************************************************/
1502 : /* GDALArgDatasetValue::Set() */
1503 : /************************************************************************/
1504 :
1505 1954 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1506 : {
1507 1954 : Close();
1508 1954 : m_poDS = poDS.release();
1509 1954 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1510 1954 : m_nameSet = true;
1511 1954 : if (m_ownerArg)
1512 1814 : m_ownerArg->NotifyValueSet();
1513 1954 : }
1514 :
1515 : /************************************************************************/
1516 : /* GDALArgDatasetValue::Set() */
1517 : /************************************************************************/
1518 :
1519 7748 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1520 : {
1521 7748 : Close();
1522 7748 : m_poDS = poDS;
1523 7748 : if (m_poDS)
1524 6904 : m_poDS->Reference();
1525 7748 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1526 7748 : m_nameSet = true;
1527 7748 : if (m_ownerArg)
1528 3255 : m_ownerArg->NotifyValueSet();
1529 7748 : }
1530 :
1531 : /************************************************************************/
1532 : /* GDALArgDatasetValue::SetFrom() */
1533 : /************************************************************************/
1534 :
1535 3221 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1536 : {
1537 3221 : Close();
1538 3221 : m_name = other.m_name;
1539 3221 : m_nameSet = other.m_nameSet;
1540 3221 : m_poDS = other.m_poDS;
1541 3221 : if (m_poDS)
1542 2260 : m_poDS->Reference();
1543 3221 : }
1544 :
1545 : /************************************************************************/
1546 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1547 : /************************************************************************/
1548 :
1549 30942 : GDALArgDatasetValue::~GDALArgDatasetValue()
1550 : {
1551 30942 : Close();
1552 30942 : }
1553 :
1554 : /************************************************************************/
1555 : /* GDALArgDatasetValue::Close() */
1556 : /************************************************************************/
1557 :
1558 51173 : bool GDALArgDatasetValue::Close()
1559 : {
1560 51173 : bool ret = true;
1561 51173 : if (m_poDS && m_poDS->Dereference() == 0)
1562 : {
1563 3235 : ret = m_poDS->Close() == CE_None;
1564 3235 : delete m_poDS;
1565 : }
1566 51173 : m_poDS = nullptr;
1567 51173 : return ret;
1568 : }
1569 :
1570 : /************************************************************************/
1571 : /* GDALArgDatasetValue::operator=() */
1572 : /************************************************************************/
1573 :
1574 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1575 : {
1576 2 : Close();
1577 2 : m_poDS = other.m_poDS;
1578 2 : m_name = other.m_name;
1579 2 : m_nameSet = other.m_nameSet;
1580 2 : other.m_poDS = nullptr;
1581 2 : other.m_name.clear();
1582 2 : other.m_nameSet = false;
1583 2 : return *this;
1584 : }
1585 :
1586 : /************************************************************************/
1587 : /* GDALArgDatasetValue::GetDataset() */
1588 : /************************************************************************/
1589 :
1590 1046 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1591 : {
1592 1046 : if (m_poDS)
1593 1046 : m_poDS->Reference();
1594 1046 : return m_poDS;
1595 : }
1596 :
1597 : /************************************************************************/
1598 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1599 : /************************************************************************/
1600 :
1601 3059 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1602 3059 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1603 : {
1604 3059 : other.m_poDS = nullptr;
1605 3059 : other.m_name.clear();
1606 3059 : }
1607 :
1608 : /************************************************************************/
1609 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1610 : /************************************************************************/
1611 :
1612 3278 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1613 : bool noneAllowed, const std::vector<std::string> &specialValues)
1614 : {
1615 3278 : if (GetType() != GAAT_STRING)
1616 : {
1617 1 : CPLError(CE_Failure, CPLE_AppDefined,
1618 : "SetIsCRSArg() can only be called on a String argument");
1619 1 : return *this;
1620 : }
1621 : AddValidationAction(
1622 707 : [this, noneAllowed, specialValues]()
1623 : {
1624 : const std::string &osVal =
1625 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1626 350 : ->Get<std::string>();
1627 350 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1628 0 : return true;
1629 :
1630 687 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1631 337 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1632 687 : specialValues.end())
1633 : {
1634 329 : OGRSpatialReference oSRS;
1635 329 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1636 : {
1637 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1638 : "Invalid value for '%s' argument",
1639 7 : GetName().c_str());
1640 7 : return false;
1641 : }
1642 : }
1643 343 : return true;
1644 3277 : });
1645 :
1646 : SetAutoCompleteFunction(
1647 40 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1648 : {
1649 10 : bool bIsRaster = false;
1650 10 : OGREnvelope sDatasetLongLatEnv;
1651 20 : std::string osCelestialBodyName;
1652 10 : if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1653 : {
1654 10 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1655 10 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1656 : {
1657 : auto &val =
1658 10 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1659 10 : if (val.size() == 1)
1660 : {
1661 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1662 : auto poDS = std::unique_ptr<GDALDataset>(
1663 4 : GDALDataset::Open(val[0].GetName().c_str()));
1664 2 : if (poDS)
1665 : {
1666 2 : bIsRaster = poDS->GetRasterCount() != 0;
1667 2 : if (auto poCRS = poDS->GetSpatialRef())
1668 : {
1669 : const char *pszCelestialBodyName =
1670 2 : poCRS->GetCelestialBodyName();
1671 2 : if (pszCelestialBodyName)
1672 2 : osCelestialBodyName = pszCelestialBodyName;
1673 :
1674 2 : if (!pszCelestialBodyName ||
1675 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1676 : {
1677 0 : OGRSpatialReference oLongLat;
1678 0 : oLongLat.CopyGeogCSFrom(poCRS);
1679 0 : oLongLat.SetAxisMappingStrategy(
1680 : OAMS_TRADITIONAL_GIS_ORDER);
1681 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1682 0 : &oLongLat);
1683 : }
1684 : else
1685 : {
1686 2 : poDS->GetExtentWGS84LongLat(
1687 2 : &sDatasetLongLatEnv);
1688 : }
1689 : }
1690 : }
1691 : }
1692 : }
1693 : }
1694 :
1695 : const auto IsCRSCompatible =
1696 42959 : [bIsRaster, &sDatasetLongLatEnv,
1697 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1698 : {
1699 42959 : if (!sDatasetLongLatEnv.IsInit())
1700 30685 : return true;
1701 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1702 11834 : !(bIsRaster &&
1703 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1704 11652 : crsInfo->dfWestLongitudeDeg <
1705 11652 : crsInfo->dfEastLongitudeDeg &&
1706 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1707 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1708 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1709 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1710 329 : ((!osCelestialBodyName.empty() &&
1711 658 : crsInfo->pszCelestialBodyName &&
1712 329 : osCelestialBodyName ==
1713 329 : crsInfo->pszCelestialBodyName) ||
1714 0 : (osCelestialBodyName.empty() &&
1715 12274 : !crsInfo->pszCelestialBodyName));
1716 10 : };
1717 :
1718 10 : std::vector<std::string> oRet;
1719 10 : if (noneAllowed)
1720 0 : oRet.push_back("none");
1721 10 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1722 10 : if (!currentValue.empty())
1723 : {
1724 : const CPLStringList aosTokens(
1725 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1726 7 : int nCount = 0;
1727 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1728 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1729 : nullptr, &nCount),
1730 14 : OSRDestroyCRSInfoList);
1731 14 : std::string osCode;
1732 :
1733 14 : std::vector<const OSRCRSInfo *> candidates;
1734 46270 : for (int i = 0; i < nCount; ++i)
1735 : {
1736 46263 : const auto *entry = (pCRSList.get())[i];
1737 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1738 : {
1739 49425 : if (aosTokens.size() == 1 ||
1740 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1741 : {
1742 12666 : if (candidates.empty())
1743 7 : osCode = entry->pszCode;
1744 12666 : candidates.push_back(entry);
1745 : }
1746 : }
1747 : }
1748 7 : if (candidates.size() == 1)
1749 : {
1750 1 : oRet.push_back(std::move(osCode));
1751 : }
1752 : else
1753 : {
1754 6 : if (sDatasetLongLatEnv.IsInit())
1755 : {
1756 2 : std::sort(
1757 : candidates.begin(), candidates.end(),
1758 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1759 : {
1760 2999 : const double dfXa =
1761 2999 : a->dfWestLongitudeDeg >
1762 2999 : a->dfEastLongitudeDeg
1763 2999 : ? a->dfWestLongitudeDeg -
1764 0 : a->dfEastLongitudeDeg
1765 2999 : : (180 - a->dfWestLongitudeDeg) +
1766 2999 : (a->dfEastLongitudeDeg - -180);
1767 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1768 2999 : a->dfSouthLatitudeDeg;
1769 2999 : const double dfXb =
1770 2999 : b->dfWestLongitudeDeg >
1771 2999 : b->dfEastLongitudeDeg
1772 2999 : ? b->dfWestLongitudeDeg -
1773 0 : b->dfEastLongitudeDeg
1774 2999 : : (180 - b->dfWestLongitudeDeg) +
1775 2999 : (b->dfEastLongitudeDeg - -180);
1776 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1777 2999 : b->dfSouthLatitudeDeg;
1778 2999 : const double diffArea =
1779 2999 : dfXa * dfYa - dfXb * dfYb;
1780 2999 : if (diffArea < 0)
1781 279 : return true;
1782 2720 : if (diffArea == 0)
1783 : {
1784 2506 : if (std::string_view(a->pszName) ==
1785 2506 : b->pszName)
1786 : {
1787 57 : if (a->eType ==
1788 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1789 13 : b->eType !=
1790 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1791 13 : return true;
1792 44 : if (a->eType ==
1793 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1794 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1795 9 : return true;
1796 35 : return false;
1797 : }
1798 4898 : return std::string_view(a->pszCode) <
1799 4898 : b->pszCode;
1800 : }
1801 214 : return false;
1802 : });
1803 : }
1804 :
1805 12671 : for (const auto *entry : candidates)
1806 : {
1807 25330 : std::string val = std::string(entry->pszCode)
1808 12665 : .append(" -- ")
1809 25330 : .append(entry->pszName);
1810 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1811 1294 : val.append(" (geographic 2D)");
1812 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1813 446 : val.append(" (geographic 3D)");
1814 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1815 397 : val.append(" (geocentric)");
1816 12665 : oRet.push_back(std::move(val));
1817 : }
1818 : }
1819 : }
1820 10 : if (currentValue.empty() || oRet.empty())
1821 : {
1822 : const CPLStringList aosAuthorities(
1823 6 : OSRGetAuthorityListFromDatabase());
1824 18 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1825 : {
1826 15 : int nCount = 0;
1827 15 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1828 : pszAuth, nullptr, &nCount));
1829 15 : if (nCount)
1830 12 : oRet.push_back(std::string(pszAuth).append(":"));
1831 : }
1832 : }
1833 20 : return oRet;
1834 3277 : });
1835 :
1836 3277 : return *this;
1837 : }
1838 :
1839 : /************************************************************************/
1840 : /* GDALAlgorithm::GDALAlgorithm() */
1841 : /************************************************************************/
1842 :
1843 21448 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1844 : const std::string &description,
1845 21448 : const std::string &helpURL)
1846 : : m_name(name), m_description(description), m_helpURL(helpURL),
1847 42736 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1848 21448 : ? "https://gdal.org" + m_helpURL
1849 63929 : : m_helpURL)
1850 : {
1851 : auto &helpArg =
1852 : AddArg("help", 'h', _("Display help message and exit"),
1853 42896 : &m_helpRequested)
1854 21448 : .SetHiddenForAPI()
1855 42896 : .SetCategory(GAAC_COMMON)
1856 14 : .AddAction([this]()
1857 21448 : { m_specialActionRequested = m_calledFromCommandLine; });
1858 : auto &helpDocArg =
1859 : AddArg("help-doc", 0,
1860 : _("Display help message for use by documentation"),
1861 42896 : &m_helpDocRequested)
1862 21448 : .SetHidden()
1863 12 : .AddAction([this]()
1864 21448 : { m_specialActionRequested = m_calledFromCommandLine; });
1865 : auto &jsonUsageArg =
1866 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1867 42896 : &m_JSONUsageRequested)
1868 21448 : .SetHiddenForAPI()
1869 42896 : .SetCategory(GAAC_COMMON)
1870 4 : .AddAction([this]()
1871 21448 : { m_specialActionRequested = m_calledFromCommandLine; });
1872 42896 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1873 42896 : .SetMetaVar("<KEY>=<VALUE>")
1874 21448 : .SetHiddenForAPI()
1875 42896 : .SetCategory(GAAC_COMMON)
1876 : .AddAction(
1877 2 : [this]()
1878 : {
1879 2 : ReportError(
1880 : CE_Warning, CPLE_AppDefined,
1881 : "Configuration options passed with the 'config' argument "
1882 : "are ignored");
1883 21448 : });
1884 :
1885 21448 : AddValidationAction(
1886 12895 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1887 : {
1888 6644 : if (!m_calledFromCommandLine && m_specialActionRequested)
1889 : {
1890 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1891 : {
1892 0 : if (arg->IsExplicitlySet())
1893 : {
1894 0 : ReportError(CE_Failure, CPLE_AppDefined,
1895 : "'%s' argument only available when called "
1896 : "from command line",
1897 0 : arg->GetName().c_str());
1898 0 : return false;
1899 : }
1900 : }
1901 : }
1902 6644 : return true;
1903 : });
1904 21448 : }
1905 :
1906 : /************************************************************************/
1907 : /* GDALAlgorithm::~GDALAlgorithm() */
1908 : /************************************************************************/
1909 :
1910 : GDALAlgorithm::~GDALAlgorithm() = default;
1911 :
1912 : /************************************************************************/
1913 : /* GDALAlgorithm::ParseArgument() */
1914 : /************************************************************************/
1915 :
1916 3099 : bool GDALAlgorithm::ParseArgument(
1917 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1918 : std::map<
1919 : GDALAlgorithmArg *,
1920 : std::variant<std::vector<std::string>, std::vector<int>,
1921 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1922 : &inConstructionValues)
1923 : {
1924 : const bool isListArg =
1925 3099 : GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1926 3099 : if (arg->IsExplicitlySet() && !isListArg)
1927 : {
1928 : // Hack for "gdal info" to be able to pass an opened raster dataset
1929 : // by "gdal raster info" to the "gdal vector info" algorithm.
1930 4 : if (arg->SkipIfAlreadySet())
1931 : {
1932 1 : arg->SetSkipIfAlreadySet(false);
1933 1 : return true;
1934 : }
1935 :
1936 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1937 : "Argument '%s' has already been specified.", name.c_str());
1938 3 : return false;
1939 : }
1940 :
1941 3163 : if (!arg->GetRepeatedArgAllowed() &&
1942 68 : cpl::contains(inConstructionValues, arg))
1943 : {
1944 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1945 : "Argument '%s' has already been specified.", name.c_str());
1946 1 : return false;
1947 : }
1948 :
1949 3094 : switch (arg->GetType())
1950 : {
1951 320 : case GAAT_BOOLEAN:
1952 : {
1953 320 : if (value.empty() || value == "true")
1954 318 : return arg->Set(true);
1955 2 : else if (value == "false")
1956 1 : return arg->Set(false);
1957 : else
1958 : {
1959 1 : ReportError(
1960 : CE_Failure, CPLE_IllegalArg,
1961 : "Invalid value '%s' for boolean argument '%s'. Should be "
1962 : "'true' or 'false'.",
1963 : value.c_str(), name.c_str());
1964 1 : return false;
1965 : }
1966 : }
1967 :
1968 767 : case GAAT_STRING:
1969 : {
1970 767 : return arg->Set(value);
1971 : }
1972 :
1973 356 : case GAAT_INTEGER:
1974 : {
1975 356 : errno = 0;
1976 356 : char *endptr = nullptr;
1977 356 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1978 355 : if (errno == 0 && endptr &&
1979 711 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1980 : val <= INT_MAX)
1981 : {
1982 353 : return arg->Set(static_cast<int>(val));
1983 : }
1984 : else
1985 : {
1986 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1987 : "Expected integer value for argument '%s', "
1988 : "but got '%s'.",
1989 : name.c_str(), value.c_str());
1990 3 : return false;
1991 : }
1992 : }
1993 :
1994 32 : case GAAT_REAL:
1995 : {
1996 32 : char *endptr = nullptr;
1997 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
1998 32 : if (endptr != value.c_str() + value.size())
1999 : {
2000 1 : ReportError(
2001 : CE_Failure, CPLE_IllegalArg,
2002 : "Expected real value for argument '%s', but got '%s'.",
2003 : name.c_str(), value.c_str());
2004 1 : return false;
2005 : }
2006 31 : return arg->Set(dfValue);
2007 : }
2008 :
2009 540 : case GAAT_DATASET:
2010 : {
2011 540 : return arg->SetDatasetName(value);
2012 : }
2013 :
2014 268 : case GAAT_STRING_LIST:
2015 : {
2016 : const CPLStringList aosTokens(
2017 268 : arg->GetPackedValuesAllowed()
2018 171 : ? CSLTokenizeString2(value.c_str(), ",",
2019 : CSLT_HONOURSTRINGS |
2020 : CSLT_PRESERVEQUOTES)
2021 439 : : CSLAddString(nullptr, value.c_str()));
2022 268 : if (!cpl::contains(inConstructionValues, arg))
2023 : {
2024 244 : inConstructionValues[arg] = std::vector<std::string>();
2025 : }
2026 : auto &valueVector =
2027 268 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2028 568 : for (const char *v : aosTokens)
2029 : {
2030 300 : valueVector.push_back(v);
2031 : }
2032 268 : if (arg->GetMaxCount() == 1)
2033 : {
2034 3 : bool ret = arg->Set(std::move(valueVector));
2035 3 : inConstructionValues.erase(inConstructionValues.find(arg));
2036 3 : return ret;
2037 : }
2038 :
2039 265 : break;
2040 : }
2041 :
2042 65 : case GAAT_INTEGER_LIST:
2043 : {
2044 : const CPLStringList aosTokens(
2045 65 : arg->GetPackedValuesAllowed()
2046 65 : ? CSLTokenizeString2(
2047 : value.c_str(), ",",
2048 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2049 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2050 130 : : CSLAddString(nullptr, value.c_str()));
2051 65 : if (!cpl::contains(inConstructionValues, arg))
2052 : {
2053 61 : inConstructionValues[arg] = std::vector<int>();
2054 : }
2055 : auto &valueVector =
2056 65 : std::get<std::vector<int>>(inConstructionValues[arg]);
2057 199 : for (const char *v : aosTokens)
2058 : {
2059 140 : errno = 0;
2060 140 : char *endptr = nullptr;
2061 140 : const auto val = std::strtol(v, &endptr, 10);
2062 140 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2063 136 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2064 : {
2065 134 : valueVector.push_back(static_cast<int>(val));
2066 : }
2067 : else
2068 : {
2069 6 : ReportError(
2070 : CE_Failure, CPLE_IllegalArg,
2071 : "Expected list of integer value for argument '%s', "
2072 : "but got '%s'.",
2073 : name.c_str(), value.c_str());
2074 6 : return false;
2075 : }
2076 : }
2077 59 : if (arg->GetMaxCount() == 1)
2078 : {
2079 2 : bool ret = arg->Set(std::move(valueVector));
2080 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2081 2 : return ret;
2082 : }
2083 :
2084 57 : break;
2085 : }
2086 :
2087 102 : case GAAT_REAL_LIST:
2088 : {
2089 : const CPLStringList aosTokens(
2090 102 : arg->GetPackedValuesAllowed()
2091 102 : ? CSLTokenizeString2(
2092 : value.c_str(), ",",
2093 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2094 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2095 204 : : CSLAddString(nullptr, value.c_str()));
2096 102 : if (!cpl::contains(inConstructionValues, arg))
2097 : {
2098 100 : inConstructionValues[arg] = std::vector<double>();
2099 : }
2100 : auto &valueVector =
2101 102 : std::get<std::vector<double>>(inConstructionValues[arg]);
2102 410 : for (const char *v : aosTokens)
2103 : {
2104 312 : char *endptr = nullptr;
2105 312 : double dfValue = CPLStrtod(v, &endptr);
2106 312 : if (strlen(v) == 0 || endptr != v + strlen(v))
2107 : {
2108 4 : ReportError(
2109 : CE_Failure, CPLE_IllegalArg,
2110 : "Expected list of real value for argument '%s', "
2111 : "but got '%s'.",
2112 : name.c_str(), value.c_str());
2113 4 : return false;
2114 : }
2115 308 : valueVector.push_back(dfValue);
2116 : }
2117 98 : if (arg->GetMaxCount() == 1)
2118 : {
2119 2 : bool ret = arg->Set(std::move(valueVector));
2120 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2121 2 : return ret;
2122 : }
2123 :
2124 96 : break;
2125 : }
2126 :
2127 644 : case GAAT_DATASET_LIST:
2128 : {
2129 644 : if (!cpl::contains(inConstructionValues, arg))
2130 : {
2131 631 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2132 : }
2133 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2134 644 : inConstructionValues[arg]);
2135 644 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2136 : {
2137 12 : valueVector.push_back(GDALArgDatasetValue(value));
2138 : }
2139 : else
2140 : {
2141 : const CPLStringList aosTokens(
2142 632 : arg->GetPackedValuesAllowed()
2143 6 : ? CSLTokenizeString2(value.c_str(), ",",
2144 : CSLT_HONOURSTRINGS |
2145 : CSLT_STRIPLEADSPACES)
2146 1270 : : CSLAddString(nullptr, value.c_str()));
2147 1267 : for (const char *v : aosTokens)
2148 : {
2149 635 : valueVector.push_back(GDALArgDatasetValue(v));
2150 : }
2151 : }
2152 644 : if (arg->GetMaxCount() == 1)
2153 : {
2154 523 : bool ret = arg->Set(std::move(valueVector));
2155 523 : inConstructionValues.erase(inConstructionValues.find(arg));
2156 523 : return ret;
2157 : }
2158 :
2159 121 : break;
2160 : }
2161 : }
2162 :
2163 539 : return true;
2164 : }
2165 :
2166 : /************************************************************************/
2167 : /* GDALAlgorithm::ParseCommandLineArguments() */
2168 : /************************************************************************/
2169 :
2170 2057 : bool GDALAlgorithm::ParseCommandLineArguments(
2171 : const std::vector<std::string> &args)
2172 : {
2173 2057 : if (m_parsedSubStringAlreadyCalled)
2174 : {
2175 6 : ReportError(CE_Failure, CPLE_AppDefined,
2176 : "ParseCommandLineArguments() can only be called once per "
2177 : "instance.");
2178 6 : return false;
2179 : }
2180 2051 : m_parsedSubStringAlreadyCalled = true;
2181 :
2182 : // AWS like syntax supported too (not advertized)
2183 2051 : if (args.size() == 1 && args[0] == "help")
2184 : {
2185 1 : auto arg = GetArg("help");
2186 1 : assert(arg);
2187 1 : arg->Set(true);
2188 1 : arg->RunActions();
2189 1 : return true;
2190 : }
2191 :
2192 2050 : if (HasSubAlgorithms())
2193 : {
2194 445 : if (args.empty())
2195 : {
2196 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2197 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2198 2 : return false;
2199 : }
2200 443 : if (!args[0].empty() && args[0][0] == '-')
2201 : {
2202 : // go on argument parsing
2203 : }
2204 : else
2205 : {
2206 440 : const auto nCounter = CPLGetErrorCounter();
2207 440 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2208 440 : if (m_selectedSubAlgHolder)
2209 : {
2210 437 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2211 437 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2212 437 : m_referencePath);
2213 437 : m_selectedSubAlg->m_executionForStreamOutput =
2214 437 : m_executionForStreamOutput;
2215 437 : m_selectedSubAlg->m_calledFromCommandLine =
2216 437 : m_calledFromCommandLine;
2217 437 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2218 437 : m_skipValidationInParseCommandLine;
2219 437 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2220 874 : std::vector<std::string>(args.begin() + 1, args.end()));
2221 437 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2222 437 : return bRet;
2223 : }
2224 : else
2225 : {
2226 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2227 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2228 : {
2229 2 : ReportError(CE_Failure, CPLE_AppDefined,
2230 2 : "Unknown command: '%s'", args[0].c_str());
2231 : }
2232 3 : return false;
2233 : }
2234 : }
2235 : }
2236 :
2237 : std::map<
2238 : GDALAlgorithmArg *,
2239 : std::variant<std::vector<std::string>, std::vector<int>,
2240 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2241 3216 : inConstructionValues;
2242 :
2243 3216 : std::vector<std::string> lArgs(args);
2244 1608 : bool helpValueRequested = false;
2245 4711 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2246 : {
2247 3203 : const auto &strArg = lArgs[i];
2248 3203 : GDALAlgorithmArg *arg = nullptr;
2249 3203 : std::string name;
2250 3203 : std::string value;
2251 3203 : bool hasValue = false;
2252 3203 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2253 5 : helpValueRequested = true;
2254 3203 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2255 : {
2256 2044 : const auto equalPos = strArg.find('=');
2257 4088 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2258 2044 : : strArg;
2259 2044 : const std::string nameWithoutDash = name.substr(2);
2260 2044 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2261 2097 : if (m_arbitraryLongNameArgsAllowed &&
2262 2097 : iterArg == m_mapLongNameToArg.end())
2263 : {
2264 16 : GetArg(nameWithoutDash);
2265 16 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2266 : }
2267 2044 : if (iterArg == m_mapLongNameToArg.end())
2268 : {
2269 : const std::string bestCandidate =
2270 26 : GetSuggestionForArgumentName(nameWithoutDash);
2271 26 : if (!bestCandidate.empty())
2272 : {
2273 2 : ReportError(CE_Failure, CPLE_IllegalArg,
2274 : "Option '%s' is unknown. Do you mean '--%s'?",
2275 : name.c_str(), bestCandidate.c_str());
2276 : }
2277 : else
2278 : {
2279 24 : ReportError(CE_Failure, CPLE_IllegalArg,
2280 : "Option '%s' is unknown.", name.c_str());
2281 : }
2282 26 : return false;
2283 : }
2284 2018 : arg = iterArg->second;
2285 2018 : if (equalPos != std::string::npos)
2286 : {
2287 441 : hasValue = true;
2288 441 : value = strArg.substr(equalPos + 1);
2289 : }
2290 : }
2291 1233 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2292 74 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2293 : {
2294 143 : for (size_t j = 1; j < strArg.size(); ++j)
2295 : {
2296 74 : name.clear();
2297 74 : name += strArg[j];
2298 74 : const auto iterArg = m_mapShortNameToArg.find(name);
2299 74 : if (iterArg == m_mapShortNameToArg.end())
2300 : {
2301 5 : const std::string nameWithoutDash = strArg.substr(1);
2302 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2303 10 : m_mapLongNameToArg.end())
2304 : {
2305 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2306 : "Short name option '%s' is unknown. Do you "
2307 : "mean '--%s' (with leading double dash) ?",
2308 : name.c_str(), nameWithoutDash.c_str());
2309 : }
2310 : else
2311 : {
2312 : const std::string bestCandidate =
2313 8 : GetSuggestionForArgumentName(nameWithoutDash);
2314 4 : if (!bestCandidate.empty())
2315 : {
2316 1 : ReportError(
2317 : CE_Failure, CPLE_IllegalArg,
2318 : "Short name option '%s' is unknown. Do you "
2319 : "mean '--%s' (with leading double dash) ?",
2320 : name.c_str(), bestCandidate.c_str());
2321 : }
2322 : else
2323 : {
2324 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2325 : "Short name option '%s' is unknown.",
2326 : name.c_str());
2327 : }
2328 : }
2329 5 : return false;
2330 : }
2331 69 : arg = iterArg->second;
2332 69 : if (strArg.size() > 2)
2333 : {
2334 0 : if (arg->GetType() != GAAT_BOOLEAN)
2335 : {
2336 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2337 : "Invalid argument '%s'. Option '%s' is not "
2338 : "a boolean option.",
2339 : strArg.c_str(), name.c_str());
2340 0 : return false;
2341 : }
2342 :
2343 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2344 0 : return false;
2345 : }
2346 : }
2347 69 : if (strArg.size() > 2)
2348 : {
2349 0 : lArgs.erase(lArgs.begin() + i);
2350 0 : continue;
2351 : }
2352 : }
2353 : else
2354 : {
2355 1085 : ++i;
2356 1085 : continue;
2357 : }
2358 2087 : CPLAssert(arg);
2359 :
2360 2087 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2361 : {
2362 321 : if (!hasValue)
2363 : {
2364 318 : hasValue = true;
2365 318 : value = "true";
2366 : }
2367 : }
2368 :
2369 2087 : if (!hasValue)
2370 : {
2371 1328 : if (i + 1 == lArgs.size())
2372 : {
2373 30 : if (m_parseForAutoCompletion)
2374 : {
2375 24 : lArgs.erase(lArgs.begin() + i);
2376 24 : break;
2377 : }
2378 6 : ReportError(
2379 : CE_Failure, CPLE_IllegalArg,
2380 : "Expected value for argument '%s', but ran short of tokens",
2381 : name.c_str());
2382 6 : return false;
2383 : }
2384 1298 : value = lArgs[i + 1];
2385 1298 : lArgs.erase(lArgs.begin() + i + 1);
2386 : }
2387 :
2388 2057 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2389 39 : return false;
2390 :
2391 2018 : lArgs.erase(lArgs.begin() + i);
2392 : }
2393 :
2394 1532 : if (m_specialActionRequested)
2395 : {
2396 23 : return true;
2397 : }
2398 :
2399 1978 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2400 : {
2401 1944 : for (auto &[arg, value] : inConstructionValues)
2402 : {
2403 493 : if (arg->GetType() == GAAT_STRING_LIST)
2404 : {
2405 237 : if (!arg->Set(std::get<std::vector<std::string>>(
2406 237 : inConstructionValues[arg])))
2407 : {
2408 34 : return false;
2409 : }
2410 : }
2411 256 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2412 : {
2413 54 : if (!arg->Set(
2414 54 : std::get<std::vector<int>>(inConstructionValues[arg])))
2415 : {
2416 4 : return false;
2417 : }
2418 : }
2419 202 : else if (arg->GetType() == GAAT_REAL_LIST)
2420 : {
2421 94 : if (!arg->Set(std::get<std::vector<double>>(
2422 94 : inConstructionValues[arg])))
2423 : {
2424 10 : return false;
2425 : }
2426 : }
2427 108 : else if (arg->GetType() == GAAT_DATASET_LIST)
2428 : {
2429 108 : if (!arg->Set(
2430 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2431 108 : inConstructionValues[arg]))))
2432 : {
2433 2 : return false;
2434 : }
2435 : }
2436 : }
2437 1451 : return true;
2438 1509 : };
2439 :
2440 : // Process positional arguments that have not been set through their
2441 : // option name.
2442 1509 : size_t i = 0;
2443 1509 : size_t iCurPosArg = 0;
2444 :
2445 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2446 1532 : if (m_positionalArgs.size() == 3 &&
2447 24 : (m_positionalArgs[0]->IsRequired() ||
2448 23 : m_positionalArgs[0]->GetMinCount() == 1) &&
2449 44 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2450 29 : (m_positionalArgs[1]->IsRequired() ||
2451 29 : m_positionalArgs[1]->GetMinCount() == 1) &&
2452 : /* Second argument may have several occurrences */
2453 44 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2454 22 : (m_positionalArgs[2]->IsRequired() ||
2455 22 : m_positionalArgs[2]->GetMinCount() == 1) &&
2456 22 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2457 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2458 1541 : !m_positionalArgs[1]->IsExplicitlySet() &&
2459 9 : !m_positionalArgs[2]->IsExplicitlySet())
2460 : {
2461 7 : if (lArgs.size() - i < 3)
2462 : {
2463 1 : ReportError(CE_Failure, CPLE_AppDefined,
2464 : "Not enough positional values.");
2465 1 : return false;
2466 : }
2467 12 : bool ok = ParseArgument(m_positionalArgs[0],
2468 6 : m_positionalArgs[0]->GetName().c_str(),
2469 6 : lArgs[i], inConstructionValues);
2470 6 : if (ok)
2471 : {
2472 5 : ++i;
2473 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2474 : {
2475 12 : ok = ParseArgument(m_positionalArgs[1],
2476 6 : m_positionalArgs[1]->GetName().c_str(),
2477 6 : lArgs[i], inConstructionValues);
2478 : }
2479 : }
2480 6 : if (ok)
2481 : {
2482 10 : ok = ParseArgument(m_positionalArgs[2],
2483 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2484 : inConstructionValues);
2485 5 : ++i;
2486 : }
2487 6 : if (!ok)
2488 : {
2489 3 : ProcessInConstructionValues();
2490 3 : return false;
2491 : }
2492 : }
2493 :
2494 508 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2495 587 : !m_positionalArgs[0]->IsExplicitlySet() &&
2496 2326 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2497 106 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2498 53 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2499 : {
2500 53 : ++iCurPosArg;
2501 : }
2502 :
2503 2505 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2504 : {
2505 1007 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2506 1015 : while (arg->IsExplicitlySet())
2507 : {
2508 9 : ++iCurPosArg;
2509 9 : if (iCurPosArg == m_positionalArgs.size())
2510 1 : break;
2511 8 : arg = m_positionalArgs[iCurPosArg];
2512 : }
2513 1007 : if (iCurPosArg == m_positionalArgs.size())
2514 : {
2515 1 : break;
2516 : }
2517 1550 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2518 544 : arg->GetMinCount() != arg->GetMaxCount())
2519 : {
2520 109 : if (iCurPosArg == 0)
2521 : {
2522 73 : size_t nCountAtEnd = 0;
2523 102 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2524 : {
2525 31 : const auto *otherArg = m_positionalArgs[j];
2526 31 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2527 : {
2528 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2529 : {
2530 2 : ReportError(
2531 : CE_Failure, CPLE_AppDefined,
2532 : "Ambiguity in definition of positional "
2533 : "argument "
2534 : "'%s' given it has a varying number of values, "
2535 : "but follows argument '%s' which also has a "
2536 : "varying number of values",
2537 1 : otherArg->GetName().c_str(),
2538 1 : arg->GetName().c_str());
2539 1 : ProcessInConstructionValues();
2540 1 : return false;
2541 : }
2542 3 : nCountAtEnd += otherArg->GetMinCount();
2543 : }
2544 : else
2545 : {
2546 27 : if (!otherArg->IsRequired())
2547 : {
2548 2 : ReportError(
2549 : CE_Failure, CPLE_AppDefined,
2550 : "Ambiguity in definition of positional "
2551 : "argument "
2552 : "'%s', given it is not required but follows "
2553 : "argument '%s' which has a varying number of "
2554 : "values",
2555 1 : otherArg->GetName().c_str(),
2556 1 : arg->GetName().c_str());
2557 1 : ProcessInConstructionValues();
2558 1 : return false;
2559 : }
2560 26 : nCountAtEnd++;
2561 : }
2562 : }
2563 71 : if (lArgs.size() < nCountAtEnd)
2564 : {
2565 1 : ReportError(CE_Failure, CPLE_AppDefined,
2566 : "Not enough positional values.");
2567 1 : ProcessInConstructionValues();
2568 1 : return false;
2569 : }
2570 148 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2571 : {
2572 78 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2573 : inConstructionValues))
2574 : {
2575 0 : ProcessInConstructionValues();
2576 0 : return false;
2577 : }
2578 : }
2579 : }
2580 36 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2581 : {
2582 82 : for (; i < lArgs.size(); ++i)
2583 : {
2584 47 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2585 : inConstructionValues))
2586 : {
2587 0 : ProcessInConstructionValues();
2588 0 : return false;
2589 : }
2590 : }
2591 : }
2592 : else
2593 : {
2594 1 : ReportError(CE_Failure, CPLE_AppDefined,
2595 : "Ambiguity in definition of positional arguments: "
2596 : "arguments with varying number of values must be "
2597 : "first or last one.");
2598 1 : return false;
2599 : }
2600 : }
2601 : else
2602 : {
2603 897 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2604 : {
2605 1 : ReportError(CE_Failure, CPLE_AppDefined,
2606 : "Not enough positional values.");
2607 1 : return false;
2608 : }
2609 896 : const size_t iMax = i + arg->GetMaxCount();
2610 1795 : for (; i < iMax; ++i)
2611 : {
2612 900 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2613 : inConstructionValues))
2614 : {
2615 1 : ProcessInConstructionValues();
2616 1 : return false;
2617 : }
2618 : }
2619 : }
2620 1000 : ++iCurPosArg;
2621 : }
2622 :
2623 1499 : if (i < lArgs.size())
2624 : {
2625 21 : ReportError(CE_Failure, CPLE_AppDefined,
2626 : "Positional values starting at '%s' are not expected.",
2627 21 : lArgs[i].c_str());
2628 21 : return false;
2629 : }
2630 :
2631 1478 : if (!ProcessInConstructionValues())
2632 : {
2633 33 : return false;
2634 : }
2635 :
2636 : // Skip to first unset positional argument.
2637 2474 : while (iCurPosArg < m_positionalArgs.size() &&
2638 555 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2639 : {
2640 474 : ++iCurPosArg;
2641 : }
2642 : // Check if this positional argument is required.
2643 1525 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2644 80 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2645 48 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2646 32 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2647 : {
2648 69 : ReportError(CE_Failure, CPLE_AppDefined,
2649 : "Positional arguments starting at '%s' have not been "
2650 : "specified.",
2651 69 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2652 69 : return false;
2653 : }
2654 :
2655 1376 : if (m_calledFromCommandLine)
2656 : {
2657 4837 : for (auto &arg : m_args)
2658 : {
2659 6356 : if (arg->IsExplicitlySet() &&
2660 1021 : ((arg->GetType() == GAAT_STRING &&
2661 1018 : arg->Get<std::string>() == "?") ||
2662 936 : (arg->GetType() == GAAT_STRING_LIST &&
2663 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2664 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2665 : {
2666 : {
2667 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2668 5 : ValidateArguments();
2669 : }
2670 :
2671 5 : auto choices = arg->GetChoices();
2672 5 : if (choices.empty())
2673 2 : choices = arg->GetAutoCompleteChoices(std::string());
2674 5 : if (!choices.empty())
2675 : {
2676 5 : if (choices.size() == 1)
2677 : {
2678 4 : ReportError(
2679 : CE_Failure, CPLE_AppDefined,
2680 : "Single potential value for argument '%s' is '%s'",
2681 4 : arg->GetName().c_str(), choices.front().c_str());
2682 : }
2683 : else
2684 : {
2685 6 : std::string msg("Potential values for argument '");
2686 3 : msg += arg->GetName();
2687 3 : msg += "' are:";
2688 45 : for (const auto &v : choices)
2689 : {
2690 42 : msg += "\n- ";
2691 42 : msg += v;
2692 : }
2693 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2694 : msg.c_str());
2695 : }
2696 5 : return false;
2697 : }
2698 : }
2699 : }
2700 : }
2701 :
2702 1371 : return m_skipValidationInParseCommandLine || ValidateArguments();
2703 : }
2704 :
2705 : /************************************************************************/
2706 : /* GDALAlgorithm::ReportError() */
2707 : /************************************************************************/
2708 :
2709 : //! @cond Doxygen_Suppress
2710 883 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2711 : const char *fmt, ...) const
2712 : {
2713 : va_list args;
2714 883 : va_start(args, fmt);
2715 883 : CPLError(eErrClass, err_no, "%s",
2716 883 : std::string(m_name)
2717 883 : .append(": ")
2718 1766 : .append(CPLString().vPrintf(fmt, args))
2719 : .c_str());
2720 883 : va_end(args);
2721 883 : }
2722 :
2723 : //! @endcond
2724 :
2725 : /************************************************************************/
2726 : /* GDALAlgorithm::ProcessDatasetArg() */
2727 : /************************************************************************/
2728 :
2729 9724 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2730 : GDALAlgorithm *algForOutput)
2731 : {
2732 9724 : bool ret = true;
2733 :
2734 9724 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2735 9724 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2736 9724 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2737 :
2738 9724 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2739 9724 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2740 9724 : const bool append = hasAppendArg && appendArg->Get<bool>();
2741 :
2742 9724 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2743 : const bool overwrite =
2744 16057 : (arg->IsOutput() && overwriteArg &&
2745 16057 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2746 :
2747 9724 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2748 19448 : auto &val = [arg]() -> GDALArgDatasetValue &
2749 : {
2750 9724 : if (arg->GetType() == GAAT_DATASET_LIST)
2751 5537 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2752 : else
2753 4187 : return arg->Get<GDALArgDatasetValue>();
2754 9724 : }();
2755 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2756 15360 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2757 15368 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2758 8 : !overwrite;
2759 :
2760 : // Used for nested pipelines
2761 : const auto oIterDatasetNameToDataset =
2762 19445 : val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2763 9724 : : m_oMapDatasetNameToDataset.end();
2764 :
2765 9724 : if (!val.GetDatasetRef() && !val.IsNameSet())
2766 : {
2767 3 : ReportError(CE_Failure, CPLE_AppDefined,
2768 : "Argument '%s' has no dataset object or dataset name.",
2769 3 : arg->GetName().c_str());
2770 3 : ret = false;
2771 : }
2772 9721 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2773 : {
2774 3 : return false;
2775 : }
2776 217 : else if (m_inputDatasetCanBeOmitted &&
2777 9935 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2778 8 : !arg->IsOutput())
2779 : {
2780 8 : return true;
2781 : }
2782 14386 : else if (!val.GetDatasetRef() &&
2783 4976 : (arg->AutoOpenDataset() ||
2784 14686 : oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2785 4376 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2786 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2787 : {
2788 1334 : int flags = arg->GetDatasetType();
2789 1334 : bool assignToOutputArg = false;
2790 :
2791 : // Check if input and output parameters point to the same
2792 : // filename (for vector datasets)
2793 2466 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2794 2466 : outputArg && outputArg->GetType() == GAAT_DATASET)
2795 : {
2796 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2797 121 : if (!outputVal.GetDatasetRef() &&
2798 121 : outputVal.GetName() == val.GetName() &&
2799 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2800 : {
2801 2 : assignToOutputArg = true;
2802 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2803 : }
2804 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2805 : {
2806 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2807 : }
2808 : }
2809 :
2810 1334 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2811 1251 : flags |= GDAL_OF_VERBOSE_ERROR;
2812 1334 : if ((arg == outputArg || !outputArg) && update)
2813 : {
2814 85 : flags |= GDAL_OF_UPDATE;
2815 85 : if (!append)
2816 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2817 : }
2818 :
2819 1334 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2820 : const bool readOnly =
2821 1377 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2822 43 : readOnlyArg->Get<bool>());
2823 1334 : if (readOnly)
2824 12 : flags &= ~GDAL_OF_UPDATE;
2825 :
2826 2668 : CPLStringList aosOpenOptions;
2827 2668 : CPLStringList aosAllowedDrivers;
2828 1334 : if (arg->IsInput())
2829 : {
2830 1334 : if (arg == outputArg)
2831 : {
2832 83 : if (update && !overwrite)
2833 : {
2834 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2835 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2836 31 : aosOpenOptions = CPLStringList(
2837 31 : ooArg->Get<std::vector<std::string>>());
2838 : }
2839 : }
2840 : else
2841 : {
2842 1251 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2843 1251 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2844 : aosOpenOptions =
2845 1200 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2846 :
2847 1251 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2848 1251 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2849 : aosAllowedDrivers =
2850 1157 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2851 : }
2852 : }
2853 :
2854 2668 : std::string osDatasetName = val.GetName();
2855 1334 : if (!m_referencePath.empty())
2856 : {
2857 42 : osDatasetName = GDALDataset::BuildFilename(
2858 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2859 : }
2860 1334 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2861 0 : osDatasetName = "/vsistdin/";
2862 :
2863 : // Handle special case of overview delete in GTiff which would fail
2864 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2865 142 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2866 1478 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2867 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2868 : {
2869 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2870 : GDALDriverH hDrv =
2871 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2872 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2873 : {
2874 : // Cleaning does not break COG layout
2875 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2876 : }
2877 : }
2878 :
2879 : GDALDataset *poDS;
2880 : {
2881 : // The PostGISRaster may emit an error message, that is not
2882 : // relevant, if it is the vector driver that was intended
2883 1334 : std::unique_ptr<CPLErrorStateBackuper> poBackuper;
2884 1334 : if (cpl::starts_with(osDatasetName, "PG:") &&
2885 0 : (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
2886 : {
2887 0 : poBackuper = std::make_unique<CPLErrorStateBackuper>(
2888 0 : CPLQuietErrorHandler);
2889 : }
2890 :
2891 1334 : CPL_IGNORE_RET_VAL(poBackuper);
2892 1334 : poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2893 1334 : ? oIterDatasetNameToDataset->second
2894 1331 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2895 1331 : aosAllowedDrivers.List(),
2896 1331 : aosOpenOptions.List());
2897 :
2898 : // Retry with PostGIS vector driver
2899 59 : if (!poDS && poBackuper &&
2900 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2901 1393 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2902 : {
2903 0 : poBackuper.reset();
2904 0 : poDS = GDALDataset::Open(
2905 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2906 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2907 : }
2908 : }
2909 :
2910 1334 : if (poDS)
2911 : {
2912 1275 : if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2913 : {
2914 3 : if (arg->GetType() == GAAT_DATASET)
2915 3 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2916 3 : poDS->Reference();
2917 3 : m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
2918 : }
2919 :
2920 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2921 : // where the PG: dataset will be first opened with the PostGISRaster
2922 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2923 1739 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2924 1829 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2925 90 : aosOpenOptions.empty())
2926 : {
2927 86 : auto poDrv = poDS->GetDriver();
2928 86 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2929 : {
2930 : // Retry with PostgreSQL (vector) driver
2931 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2932 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2933 0 : if (poTmpDS)
2934 : {
2935 0 : poDS->ReleaseRef();
2936 0 : poDS = poTmpDS.release();
2937 : }
2938 : }
2939 : }
2940 :
2941 1275 : if (assignToOutputArg)
2942 : {
2943 : // Avoid opening twice the same datasource if it is both
2944 : // the input and output.
2945 : // Known to cause problems with at least FGdb, SQLite
2946 : // and GPKG drivers. See #4270
2947 : // Restrict to those 3 drivers. For example it is known
2948 : // to break with the PG driver due to the way it
2949 : // manages transactions.
2950 2 : auto poDriver = poDS->GetDriver();
2951 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2952 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
2953 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
2954 : {
2955 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2956 : }
2957 : }
2958 1275 : val.SetDatasetOpenedByAlgorithm();
2959 1275 : val.Set(poDS);
2960 1275 : poDS->ReleaseRef();
2961 : }
2962 59 : else if (!append)
2963 : {
2964 57 : ret = false;
2965 : }
2966 : }
2967 :
2968 : // Deal with overwriting the output dataset
2969 9713 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2970 : {
2971 3044 : if (!append)
2972 : {
2973 : // If outputting to MEM, do not try to erase a real file of the same name!
2974 : const auto outputFormatArg =
2975 3032 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2976 9058 : if (!(outputFormatArg &&
2977 3013 : outputFormatArg->GetType() == GAAT_STRING &&
2978 3013 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2979 1952 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2980 1026 : "stream") ||
2981 1026 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2982 : "Memory"))))
2983 : {
2984 1045 : const char *pszType = "";
2985 1045 : GDALDriver *poDriver = nullptr;
2986 2044 : if (!val.GetName().empty() &&
2987 999 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
2988 : &poDriver))
2989 : {
2990 78 : if (!overwrite)
2991 : {
2992 68 : std::string options;
2993 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
2994 : {
2995 11 : options += "--";
2996 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
2997 : }
2998 34 : if (hasAppendArg)
2999 : {
3000 22 : if (!options.empty())
3001 8 : options += '/';
3002 22 : options += "--";
3003 22 : options += GDAL_ARG_NAME_APPEND;
3004 : }
3005 34 : if (hasUpdateArg)
3006 : {
3007 15 : if (!options.empty())
3008 12 : options += '/';
3009 15 : options += "--";
3010 15 : options += GDAL_ARG_NAME_UPDATE;
3011 : }
3012 :
3013 34 : if (poDriver)
3014 : {
3015 68 : const char *pszPrefix = poDriver->GetMetadataItem(
3016 34 : GDAL_DMD_CONNECTION_PREFIX);
3017 34 : if (pszPrefix &&
3018 0 : STARTS_WITH_CI(val.GetName().c_str(),
3019 : pszPrefix))
3020 : {
3021 0 : bool bExists = false;
3022 : {
3023 : CPLErrorStateBackuper oBackuper(
3024 0 : CPLQuietErrorHandler);
3025 0 : bExists = std::unique_ptr<GDALDataset>(
3026 : GDALDataset::Open(
3027 0 : val.GetName().c_str())) !=
3028 : nullptr;
3029 : }
3030 0 : if (bExists)
3031 : {
3032 0 : if (!options.empty())
3033 0 : options = " You may specify the " +
3034 0 : options + " option.";
3035 0 : ReportError(CE_Failure, CPLE_AppDefined,
3036 : "%s '%s' already exists.%s",
3037 0 : pszType, val.GetName().c_str(),
3038 : options.c_str());
3039 0 : return false;
3040 : }
3041 :
3042 0 : return true;
3043 : }
3044 : }
3045 :
3046 34 : if (!options.empty())
3047 28 : options = '/' + options;
3048 68 : ReportError(
3049 : CE_Failure, CPLE_AppDefined,
3050 : "%s '%s' already exists. You may specify the "
3051 : "--overwrite%s option.",
3052 34 : pszType, val.GetName().c_str(), options.c_str());
3053 34 : return false;
3054 : }
3055 44 : else if (EQUAL(pszType, "File"))
3056 : {
3057 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3058 : {
3059 0 : ReportError(CE_Failure, CPLE_AppDefined,
3060 : "Deleting %s failed: %s",
3061 0 : val.GetName().c_str(),
3062 0 : VSIStrerror(errno));
3063 0 : return false;
3064 : }
3065 : }
3066 43 : else if (EQUAL(pszType, "Directory"))
3067 : {
3068 : // We don't want the user to accidentally erase a non-GDAL dataset
3069 1 : ReportError(CE_Failure, CPLE_AppDefined,
3070 : "Directory '%s' already exists, but is not "
3071 : "recognized as a valid GDAL dataset. "
3072 : "Please manually delete it before retrying",
3073 1 : val.GetName().c_str());
3074 1 : return false;
3075 : }
3076 42 : else if (poDriver)
3077 : {
3078 : bool bDeleteOK;
3079 : {
3080 : CPLErrorStateBackuper oBackuper(
3081 42 : CPLQuietErrorHandler);
3082 42 : bDeleteOK = (poDriver->Delete(
3083 42 : val.GetName().c_str()) == CE_None);
3084 : }
3085 : VSIStatBufL sStat;
3086 45 : if (!bDeleteOK &&
3087 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3088 : {
3089 3 : if (VSI_ISDIR(sStat.st_mode))
3090 : {
3091 : // We don't want the user to accidentally erase a non-GDAL dataset
3092 0 : ReportError(
3093 : CE_Failure, CPLE_AppDefined,
3094 : "Directory '%s' already exists, but is not "
3095 : "recognized as a valid GDAL dataset. "
3096 : "Please manually delete it before retrying",
3097 0 : val.GetName().c_str());
3098 2 : return false;
3099 : }
3100 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3101 : {
3102 2 : ReportError(CE_Failure, CPLE_AppDefined,
3103 : "Deleting %s failed: %s",
3104 2 : val.GetName().c_str(),
3105 2 : VSIStrerror(errno));
3106 2 : return false;
3107 : }
3108 : }
3109 : }
3110 : }
3111 : }
3112 : }
3113 : }
3114 :
3115 : // If outputting to stdout, automatically turn off progress bar
3116 9676 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3117 : {
3118 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3119 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3120 5 : quietArg->Set(true);
3121 : }
3122 :
3123 9676 : return ret;
3124 : }
3125 :
3126 : /************************************************************************/
3127 : /* GDALAlgorithm::ValidateArguments() */
3128 : /************************************************************************/
3129 :
3130 6648 : bool GDALAlgorithm::ValidateArguments()
3131 : {
3132 6648 : if (m_selectedSubAlg)
3133 3 : return m_selectedSubAlg->ValidateArguments();
3134 :
3135 6645 : if (m_specialActionRequested)
3136 1 : return true;
3137 :
3138 6644 : m_arbitraryLongNameArgsAllowed = false;
3139 :
3140 : // If only --output=format=MEM/stream is specified and not --output,
3141 : // then set empty name for --output.
3142 6644 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3143 6644 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3144 3940 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3145 2638 : !outputArg->IsExplicitlySet() &&
3146 358 : outputFormatArg->GetType() == GAAT_STRING &&
3147 358 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3148 588 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3149 10917 : outputArg->GetType() == GAAT_DATASET &&
3150 333 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3151 : {
3152 333 : outputArg->Get<GDALArgDatasetValue>().Set("");
3153 : }
3154 :
3155 : // The method may emit several errors if several constraints are not met.
3156 6644 : bool ret = true;
3157 13288 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3158 13288 : std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3159 125431 : for (auto &arg : m_args)
3160 : {
3161 : // Check mutually exclusive/dependent arguments
3162 118787 : if (arg->IsExplicitlySet())
3163 : {
3164 :
3165 19142 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3166 19142 : if (!mutualExclusionGroup.empty())
3167 : {
3168 : auto oIter =
3169 759 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3170 759 : if (oIter != mutualExclusionGroupUsed.end())
3171 : {
3172 13 : ret = false;
3173 26 : ReportError(
3174 : CE_Failure, CPLE_AppDefined,
3175 : "Argument '%s' is mutually exclusive with '%s'.",
3176 26 : arg->GetName().c_str(), oIter->second.c_str());
3177 : }
3178 : else
3179 : {
3180 746 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3181 1492 : arg->GetName();
3182 : }
3183 : }
3184 :
3185 19142 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3186 19142 : if (!mutualDependencyGroup.empty())
3187 : {
3188 50 : if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3189 100 : mutualDependencyGroupUsed.end())
3190 : {
3191 87 : mutualDependencyGroupUsed[mutualDependencyGroup] = {
3192 87 : arg->GetName()};
3193 : }
3194 : else
3195 : {
3196 42 : mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3197 21 : arg->GetName());
3198 : }
3199 : }
3200 :
3201 : // Check direct dependencies
3202 19151 : for (const auto &dependency : arg->GetDirectDependencies())
3203 : {
3204 9 : auto depArg = GetArg(dependency);
3205 9 : if (!depArg)
3206 : {
3207 0 : ret = false;
3208 0 : ReportError(CE_Failure, CPLE_AppDefined,
3209 : "Argument '%s' depends on argument '%s' that "
3210 : "is not defined.",
3211 0 : arg->GetName().c_str(), dependency.c_str());
3212 : }
3213 9 : else if (!depArg->IsExplicitlySet())
3214 : {
3215 5 : ret = false;
3216 10 : ReportError(CE_Failure, CPLE_AppDefined,
3217 : "Argument '%s' depends on argument '%s' that "
3218 : "has not been specified.",
3219 5 : arg->GetName().c_str(),
3220 5 : depArg->GetName().c_str());
3221 : }
3222 : }
3223 : }
3224 :
3225 118941 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3226 154 : !arg->HasDefaultValue())
3227 : {
3228 154 : bool emitError = true;
3229 154 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3230 154 : if (!mutualExclusionGroup.empty())
3231 : {
3232 1765 : for (const auto &otherArg : m_args)
3233 : {
3234 1751 : if (otherArg->GetMutualExclusionGroup() ==
3235 1856 : mutualExclusionGroup &&
3236 105 : otherArg->IsExplicitlySet())
3237 : {
3238 74 : emitError = false;
3239 74 : break;
3240 : }
3241 : }
3242 : }
3243 232 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3244 48 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3245 60 : (arg->GetType() == GAAT_DATASET ||
3246 30 : arg->GetType() == GAAT_DATASET_LIST)))
3247 : {
3248 50 : ReportError(CE_Failure, CPLE_AppDefined,
3249 : "Required argument '%s' has not been specified.",
3250 50 : arg->GetName().c_str());
3251 50 : ret = false;
3252 : }
3253 : }
3254 118633 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3255 : {
3256 4187 : if (!ProcessDatasetArg(arg.get(), this))
3257 51 : ret = false;
3258 : }
3259 :
3260 118787 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3261 : {
3262 5411 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3263 5411 : if (listVal.size() == 1)
3264 : {
3265 5257 : if (!ProcessDatasetArg(arg.get(), this))
3266 39 : ret = false;
3267 : }
3268 : else
3269 : {
3270 473 : for (auto &val : listVal)
3271 : {
3272 319 : if (val.GetDatasetRef())
3273 : {
3274 120 : if (!CheckCanSetDatasetObject(arg.get()))
3275 : {
3276 0 : ret = false;
3277 : }
3278 315 : continue;
3279 : }
3280 :
3281 199 : if (val.GetName().empty())
3282 : {
3283 0 : ReportError(CE_Failure, CPLE_AppDefined,
3284 : "Argument '%s' has no dataset object or "
3285 : "dataset name.",
3286 0 : arg->GetName().c_str());
3287 0 : ret = false;
3288 0 : continue;
3289 : }
3290 :
3291 199 : auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3292 199 : if (oIter != m_oMapDatasetNameToDataset.end())
3293 : {
3294 2 : auto poDS = oIter->second;
3295 2 : val.SetDatasetOpenedByAlgorithm();
3296 2 : val.Set(poDS);
3297 2 : m_oMapDatasetNameToDataset.erase(oIter);
3298 2 : continue;
3299 : }
3300 :
3301 197 : if (!arg->AutoOpenDataset())
3302 193 : continue;
3303 :
3304 4 : int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3305 :
3306 8 : CPLStringList aosOpenOptions;
3307 8 : CPLStringList aosAllowedDrivers;
3308 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3309 : {
3310 4 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3311 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3312 : {
3313 4 : aosOpenOptions = CPLStringList(
3314 4 : ooArg->Get<std::vector<std::string>>());
3315 : }
3316 :
3317 4 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3318 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3319 : {
3320 4 : aosAllowedDrivers = CPLStringList(
3321 4 : ifArg->Get<std::vector<std::string>>());
3322 : }
3323 :
3324 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3325 4 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3326 0 : updateArg->Get<bool>())
3327 : {
3328 0 : flags |= GDAL_OF_UPDATE;
3329 : }
3330 : }
3331 :
3332 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3333 4 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3334 12 : aosOpenOptions.List()));
3335 4 : if (poDS)
3336 : {
3337 3 : val.Set(std::move(poDS));
3338 : }
3339 : else
3340 : {
3341 1 : ret = false;
3342 : }
3343 : }
3344 : }
3345 : }
3346 :
3347 118787 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3348 : {
3349 8 : ret = false;
3350 : }
3351 : }
3352 :
3353 : // Check mutual dependency groups
3354 6644 : std::vector<std::string> processedGroups;
3355 : // Loop through group map and check there are not required args in the group that are not set
3356 6673 : for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3357 : {
3358 29 : if (std::find(processedGroups.begin(), processedGroups.end(),
3359 29 : groupName) != processedGroups.end())
3360 0 : continue;
3361 58 : std::vector<std::string> missingArgs;
3362 562 : for (auto &arg : m_args)
3363 : {
3364 533 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3365 598 : if (mutualDependencyGroup == groupName &&
3366 65 : std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3367 598 : argNames.end())
3368 : {
3369 15 : missingArgs.push_back(arg->GetName());
3370 : }
3371 : }
3372 29 : if (!missingArgs.empty())
3373 : {
3374 12 : ret = false;
3375 24 : std::string missingArgsStr;
3376 27 : for (const auto &missingArg : missingArgs)
3377 : {
3378 15 : if (!missingArgsStr.empty())
3379 3 : missingArgsStr += ", ";
3380 15 : missingArgsStr += missingArg;
3381 : }
3382 24 : std::string givenArgsStr;
3383 27 : for (const auto &givenArg : argNames)
3384 : {
3385 15 : if (!givenArgsStr.empty())
3386 3 : givenArgsStr += ", ";
3387 15 : givenArgsStr += givenArg;
3388 : }
3389 12 : ReportError(CE_Failure, CPLE_AppDefined,
3390 : "Argument(s) '%s' require(s) that the following "
3391 : "argument(s) are also specified: %s.",
3392 : givenArgsStr.c_str(), missingArgsStr.c_str());
3393 : }
3394 29 : processedGroups.push_back(groupName);
3395 : }
3396 :
3397 28430 : for (const auto &f : m_validationActions)
3398 : {
3399 21786 : if (!f())
3400 80 : ret = false;
3401 : }
3402 :
3403 6644 : return ret;
3404 : }
3405 :
3406 : /************************************************************************/
3407 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3408 : /************************************************************************/
3409 :
3410 : std::unique_ptr<GDALAlgorithm>
3411 9933 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3412 : bool suggestionAllowed) const
3413 : {
3414 9933 : auto ret = m_subAlgRegistry.Instantiate(name);
3415 19866 : auto childCallPath = m_callPath;
3416 9933 : childCallPath.push_back(name);
3417 9933 : if (!ret)
3418 : {
3419 916 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3420 916 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3421 : }
3422 9933 : if (ret)
3423 : {
3424 9796 : ret->SetCallPath(childCallPath);
3425 : }
3426 137 : else if (suggestionAllowed)
3427 : {
3428 58 : std::string bestCandidate;
3429 29 : size_t bestDistance = std::numeric_limits<size_t>::max();
3430 470 : for (const std::string &candidate : GetSubAlgorithmNames())
3431 : {
3432 : const size_t distance =
3433 441 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3434 : /* transpositionAllowed = */ true);
3435 441 : if (distance < bestDistance)
3436 : {
3437 71 : bestCandidate = candidate;
3438 71 : bestDistance = distance;
3439 : }
3440 370 : else if (distance == bestDistance)
3441 : {
3442 45 : bestCandidate.clear();
3443 : }
3444 : }
3445 29 : if (!bestCandidate.empty() && bestDistance <= 2)
3446 : {
3447 4 : CPLError(CE_Failure, CPLE_AppDefined,
3448 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3449 : name.c_str(), bestCandidate.c_str());
3450 : }
3451 : }
3452 19866 : return ret;
3453 : }
3454 :
3455 : /************************************************************************/
3456 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3457 : /************************************************************************/
3458 :
3459 : std::string
3460 37 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3461 : {
3462 37 : if (osName.size() >= 3)
3463 : {
3464 34 : std::string bestCandidate;
3465 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3466 714 : for (const auto &[key, value] : m_mapLongNameToArg)
3467 : {
3468 680 : CPL_IGNORE_RET_VAL(value);
3469 680 : const size_t distance = CPLLevenshteinDistance(
3470 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3471 680 : if (distance < bestDistance)
3472 : {
3473 87 : bestCandidate = key;
3474 87 : bestDistance = distance;
3475 : }
3476 593 : else if (distance == bestDistance)
3477 : {
3478 83 : bestCandidate.clear();
3479 : }
3480 : }
3481 48 : if (!bestCandidate.empty() &&
3482 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3483 : {
3484 5 : return bestCandidate;
3485 : }
3486 : }
3487 32 : return std::string();
3488 : }
3489 :
3490 : /************************************************************************/
3491 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3492 : /************************************************************************/
3493 :
3494 : /* static */
3495 22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3496 : {
3497 66 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3498 66 : osName == GDAL_ARG_NAME_OVERWRITE ||
3499 44 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3500 : }
3501 :
3502 : /************************************************************************/
3503 : /* GDALAlgorithm::HasOutputString() */
3504 : /************************************************************************/
3505 :
3506 67 : bool GDALAlgorithm::HasOutputString() const
3507 : {
3508 67 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3509 67 : return outputStringArg && outputStringArg->IsOutput();
3510 : }
3511 :
3512 : /************************************************************************/
3513 : /* GDALAlgorithm::GetArg() */
3514 : /************************************************************************/
3515 :
3516 461000 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3517 : bool suggestionAllowed, bool isConst)
3518 : {
3519 461000 : const auto nPos = osName.find_first_not_of('-');
3520 461000 : if (nPos == std::string::npos)
3521 23 : return nullptr;
3522 921954 : std::string osKey = osName.substr(nPos);
3523 : {
3524 460977 : const auto oIter = m_mapLongNameToArg.find(osKey);
3525 460977 : if (oIter != m_mapLongNameToArg.end())
3526 429607 : return oIter->second;
3527 : }
3528 : {
3529 31370 : const auto oIter = m_mapShortNameToArg.find(osKey);
3530 31370 : if (oIter != m_mapShortNameToArg.end())
3531 6 : return oIter->second;
3532 : }
3533 :
3534 31364 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3535 : {
3536 22 : const auto nDotPos = osKey.find('.');
3537 : const std::string osKeyEnd =
3538 22 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3539 22 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3540 : {
3541 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3542 0 : std::make_unique<bool>());
3543 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3544 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3545 0 : .SetUserProvided();
3546 : }
3547 : else
3548 : {
3549 44 : const std::string osKeyInit = osKey;
3550 22 : if (osKey == "oo")
3551 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3552 22 : else if (osKey == "co")
3553 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3554 22 : else if (osKey == "of")
3555 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3556 22 : else if (osKey == "if")
3557 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3558 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3559 22 : std::make_unique<std::string>());
3560 : auto &arg =
3561 44 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3562 44 : m_arbitraryLongNameArgsValuesStr.back().get())
3563 22 : .SetUserProvided();
3564 22 : if (osKey != osKeyInit)
3565 0 : arg.AddAlias(osKeyInit);
3566 : }
3567 22 : const auto oIter = m_mapLongNameToArg.find(osKey);
3568 22 : CPLAssert(oIter != m_mapLongNameToArg.end());
3569 22 : return oIter->second;
3570 : }
3571 :
3572 31342 : if (suggestionAllowed)
3573 : {
3574 14 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3575 7 : if (!bestCandidate.empty())
3576 : {
3577 2 : CPLError(CE_Failure, CPLE_AppDefined,
3578 : "Argument '%s' is unknown. Do you mean '%s'?",
3579 : osName.c_str(), bestCandidate.c_str());
3580 : }
3581 : }
3582 :
3583 31342 : return nullptr;
3584 : }
3585 :
3586 : /************************************************************************/
3587 : /* GDALAlgorithm::AddAliasFor() */
3588 : /************************************************************************/
3589 :
3590 : //! @cond Doxygen_Suppress
3591 79303 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3592 : const std::string &alias)
3593 : {
3594 79303 : if (cpl::contains(m_mapLongNameToArg, alias))
3595 : {
3596 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3597 : alias.c_str());
3598 : }
3599 : else
3600 : {
3601 79302 : m_mapLongNameToArg[alias] = arg;
3602 : }
3603 79303 : }
3604 :
3605 : //! @endcond
3606 :
3607 : /************************************************************************/
3608 : /* GDALAlgorithm::AddShortNameAliasFor() */
3609 : /************************************************************************/
3610 :
3611 : //! @cond Doxygen_Suppress
3612 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3613 : char shortNameAlias)
3614 : {
3615 96 : std::string alias;
3616 48 : alias += shortNameAlias;
3617 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3618 : {
3619 0 : ReportError(CE_Failure, CPLE_AppDefined,
3620 : "Short name '%s' already declared.", alias.c_str());
3621 : }
3622 : else
3623 : {
3624 48 : m_mapShortNameToArg[alias] = arg;
3625 : }
3626 48 : }
3627 :
3628 : //! @endcond
3629 :
3630 : /************************************************************************/
3631 : /* GDALAlgorithm::SetPositional() */
3632 : /************************************************************************/
3633 :
3634 : //! @cond Doxygen_Suppress
3635 21395 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3636 : {
3637 21395 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3638 : arg) == m_positionalArgs.end());
3639 21395 : m_positionalArgs.push_back(arg);
3640 21395 : }
3641 :
3642 : //! @endcond
3643 :
3644 : /************************************************************************/
3645 : /* GDALAlgorithm::HasSubAlgorithms() */
3646 : /************************************************************************/
3647 :
3648 12566 : bool GDALAlgorithm::HasSubAlgorithms() const
3649 : {
3650 12566 : if (!m_subAlgRegistry.empty())
3651 3253 : return true;
3652 9313 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3653 18626 : .GetDeclaredSubAlgorithmNames(m_callPath)
3654 9313 : .empty();
3655 : }
3656 :
3657 : /************************************************************************/
3658 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3659 : /************************************************************************/
3660 :
3661 1330 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3662 : {
3663 1330 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3664 1330 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3665 2660 : .GetDeclaredSubAlgorithmNames(m_callPath);
3666 1330 : ret.insert(ret.end(), other.begin(), other.end());
3667 1330 : if (!other.empty())
3668 415 : std::sort(ret.begin(), ret.end());
3669 2660 : return ret;
3670 : }
3671 :
3672 : /************************************************************************/
3673 : /* GDALAlgorithm::AddArg() */
3674 : /************************************************************************/
3675 :
3676 : GDALInConstructionAlgorithmArg &
3677 306212 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3678 : {
3679 306212 : auto argRaw = arg.get();
3680 306212 : const auto &longName = argRaw->GetName();
3681 306212 : if (!longName.empty())
3682 : {
3683 306199 : if (longName[0] == '-')
3684 : {
3685 1 : ReportError(CE_Failure, CPLE_AppDefined,
3686 : "Long name '%s' should not start with '-'",
3687 : longName.c_str());
3688 : }
3689 306199 : if (longName.find('=') != std::string::npos)
3690 : {
3691 1 : ReportError(CE_Failure, CPLE_AppDefined,
3692 : "Long name '%s' should not contain a '=' character",
3693 : longName.c_str());
3694 : }
3695 306199 : if (cpl::contains(m_mapLongNameToArg, longName))
3696 : {
3697 1 : ReportError(CE_Failure, CPLE_AppDefined,
3698 : "Long name '%s' already declared", longName.c_str());
3699 : }
3700 306199 : m_mapLongNameToArg[longName] = argRaw;
3701 : }
3702 306212 : const auto &shortName = argRaw->GetShortName();
3703 306212 : if (!shortName.empty())
3704 : {
3705 148114 : if (shortName.size() != 1 ||
3706 74057 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3707 65 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3708 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3709 : {
3710 1 : ReportError(CE_Failure, CPLE_AppDefined,
3711 : "Short name '%s' should be a single letter or digit",
3712 : shortName.c_str());
3713 : }
3714 74057 : if (cpl::contains(m_mapShortNameToArg, shortName))
3715 : {
3716 1 : ReportError(CE_Failure, CPLE_AppDefined,
3717 : "Short name '%s' already declared", shortName.c_str());
3718 : }
3719 74057 : m_mapShortNameToArg[shortName] = argRaw;
3720 : }
3721 306212 : m_args.emplace_back(std::move(arg));
3722 : return *(
3723 306212 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3724 : }
3725 :
3726 : GDALInConstructionAlgorithmArg &
3727 136626 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3728 : const std::string &helpMessage, bool *pValue)
3729 : {
3730 136626 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3731 : this,
3732 273252 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3733 273252 : pValue));
3734 : }
3735 :
3736 : GDALInConstructionAlgorithmArg &
3737 49482 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3738 : const std::string &helpMessage, std::string *pValue)
3739 : {
3740 49482 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3741 : this,
3742 98964 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3743 98964 : pValue));
3744 : }
3745 :
3746 : GDALInConstructionAlgorithmArg &
3747 11784 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3748 : const std::string &helpMessage, int *pValue)
3749 : {
3750 11784 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3751 : this,
3752 23568 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3753 23568 : pValue));
3754 : }
3755 :
3756 : GDALInConstructionAlgorithmArg &
3757 10064 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3758 : const std::string &helpMessage, double *pValue)
3759 : {
3760 10064 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3761 : this,
3762 20128 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3763 20128 : pValue));
3764 : }
3765 :
3766 : GDALInConstructionAlgorithmArg &
3767 11605 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3768 : const std::string &helpMessage,
3769 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3770 : {
3771 23210 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3772 : this,
3773 23210 : GDALAlgorithmArgDecl(longName, chShortName,
3774 : helpMessage, GAAT_DATASET),
3775 11605 : pValue))
3776 11605 : .SetDatasetType(type);
3777 11605 : pValue->SetOwnerArgument(&arg);
3778 11605 : return arg;
3779 : }
3780 :
3781 : GDALInConstructionAlgorithmArg &
3782 65610 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3783 : const std::string &helpMessage,
3784 : std::vector<std::string> *pValue)
3785 : {
3786 65610 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3787 : this,
3788 131220 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3789 : GAAT_STRING_LIST),
3790 131220 : pValue));
3791 : }
3792 :
3793 : GDALInConstructionAlgorithmArg &
3794 2206 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3795 : const std::string &helpMessage, std::vector<int> *pValue)
3796 : {
3797 2206 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3798 : this,
3799 4412 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3800 : GAAT_INTEGER_LIST),
3801 4412 : pValue));
3802 : }
3803 :
3804 : GDALInConstructionAlgorithmArg &
3805 5167 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3806 : const std::string &helpMessage,
3807 : std::vector<double> *pValue)
3808 : {
3809 5167 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3810 : this,
3811 10334 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3812 : GAAT_REAL_LIST),
3813 10334 : pValue));
3814 : }
3815 :
3816 : GDALInConstructionAlgorithmArg &
3817 13668 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3818 : const std::string &helpMessage,
3819 : std::vector<GDALArgDatasetValue> *pValue,
3820 : GDALArgDatasetType type)
3821 : {
3822 27336 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3823 : this,
3824 27336 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3825 : GAAT_DATASET_LIST),
3826 13668 : pValue))
3827 27336 : .SetDatasetType(type);
3828 : }
3829 :
3830 : /************************************************************************/
3831 : /* MsgOrDefault() */
3832 : /************************************************************************/
3833 :
3834 102716 : inline const char *MsgOrDefault(const char *helpMessage,
3835 : const char *defaultMessage)
3836 : {
3837 102716 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3838 : }
3839 :
3840 : /************************************************************************/
3841 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3842 : /************************************************************************/
3843 :
3844 : /* static */
3845 16705 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3846 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3847 : {
3848 : arg.SetAutoCompleteFunction(
3849 7 : [&arg,
3850 2460 : type](const std::string ¤tValue) -> std::vector<std::string>
3851 : {
3852 14 : std::vector<std::string> oRet;
3853 :
3854 7 : if (arg.IsHidden())
3855 0 : return oRet;
3856 :
3857 : {
3858 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3859 : VSIStatBufL sStat;
3860 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3861 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3862 : {
3863 0 : return oRet;
3864 : }
3865 : }
3866 :
3867 7 : auto poDM = GetGDALDriverManager();
3868 14 : std::set<std::string> oExtensions;
3869 7 : if (type)
3870 : {
3871 1368 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3872 : {
3873 1362 : auto poDriver = poDM->GetDriver(i);
3874 3859 : if (((type & GDAL_OF_RASTER) != 0 &&
3875 1135 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3876 587 : ((type & GDAL_OF_VECTOR) != 0 &&
3877 2861 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3878 497 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3879 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3880 : {
3881 : const char *pszExtensions =
3882 865 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3883 865 : if (pszExtensions)
3884 : {
3885 : const CPLStringList aosExts(
3886 1142 : CSLTokenizeString2(pszExtensions, " ", 0));
3887 1291 : for (const char *pszExt : cpl::Iterate(aosExts))
3888 720 : oExtensions.insert(CPLString(pszExt).tolower());
3889 : }
3890 : }
3891 : }
3892 : }
3893 :
3894 14 : std::string osDir;
3895 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3896 14 : std::string osPrefix;
3897 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3898 : {
3899 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3900 : {
3901 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3902 : {
3903 2 : osPrefix = pszPrefix;
3904 2 : break;
3905 : }
3906 : }
3907 3 : if (osPrefix.empty())
3908 1 : return aosVSIPrefixes;
3909 2 : if (currentValue == osPrefix)
3910 1 : osDir = osPrefix;
3911 : }
3912 6 : if (osDir.empty())
3913 : {
3914 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3915 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3916 0 : osDir = std::move(osPrefix);
3917 : }
3918 :
3919 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3920 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3921 6 : if (currentValue.empty())
3922 1 : osDir.clear();
3923 : const std::string currentFilename =
3924 12 : CPLGetFilename(currentValue.c_str());
3925 6 : if (psDir)
3926 : {
3927 442 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3928 : {
3929 437 : if ((currentFilename.empty() ||
3930 218 : STARTS_WITH(psEntry->pszName,
3931 220 : currentFilename.c_str())) &&
3932 220 : strcmp(psEntry->pszName, ".") != 0 &&
3933 1313 : strcmp(psEntry->pszName, "..") != 0 &&
3934 220 : (oExtensions.empty() ||
3935 219 : !strstr(psEntry->pszName, ".aux.xml")))
3936 : {
3937 870 : if (oExtensions.empty() ||
3938 217 : cpl::contains(
3939 : oExtensions,
3940 435 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3941 652 : .tolower()) ||
3942 186 : VSI_ISDIR(psEntry->nMode))
3943 : {
3944 72 : std::string osVal;
3945 36 : if (osDir.empty() || osDir == ".")
3946 4 : osVal = psEntry->pszName;
3947 : else
3948 64 : osVal = CPLFormFilenameSafe(
3949 64 : osDir.c_str(), psEntry->pszName, nullptr);
3950 36 : if (VSI_ISDIR(psEntry->nMode))
3951 4 : osVal += osSep;
3952 36 : oRet.push_back(std::move(osVal));
3953 : }
3954 : }
3955 437 : }
3956 5 : VSICloseDir(psDir);
3957 : }
3958 6 : return oRet;
3959 16705 : });
3960 16705 : }
3961 :
3962 : /************************************************************************/
3963 : /* GDALAlgorithm::AddInputDatasetArg() */
3964 : /************************************************************************/
3965 :
3966 704 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3967 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3968 : bool positionalAndRequired, const char *helpMessage)
3969 : {
3970 : auto &arg = AddArg(
3971 : GDAL_ARG_NAME_INPUT, 'i',
3972 : MsgOrDefault(helpMessage,
3973 : CPLSPrintf("Input %s dataset",
3974 704 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3975 1408 : pValue, type);
3976 704 : if (positionalAndRequired)
3977 697 : arg.SetPositional().SetRequired();
3978 :
3979 704 : SetAutoCompleteFunctionForFilename(arg, type);
3980 :
3981 704 : AddValidationAction(
3982 155 : [pValue]()
3983 : {
3984 154 : if (pValue->GetName() == "-")
3985 1 : pValue->Set("/vsistdin/");
3986 154 : return true;
3987 : });
3988 :
3989 704 : return arg;
3990 : }
3991 :
3992 : /************************************************************************/
3993 : /* GDALAlgorithm::AddInputDatasetArg() */
3994 : /************************************************************************/
3995 :
3996 13199 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3997 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
3998 : bool positionalAndRequired, const char *helpMessage)
3999 : {
4000 : auto &arg =
4001 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
4002 : MsgOrDefault(
4003 : helpMessage,
4004 : CPLSPrintf("Input %s datasets",
4005 13199 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4006 39597 : pValue, type)
4007 13199 : .SetPackedValuesAllowed(false);
4008 13199 : if (positionalAndRequired)
4009 1721 : arg.SetPositional().SetRequired();
4010 :
4011 13199 : SetAutoCompleteFunctionForFilename(arg, type);
4012 :
4013 13199 : AddValidationAction(
4014 6106 : [pValue]()
4015 : {
4016 11608 : for (auto &val : *pValue)
4017 : {
4018 5502 : if (val.GetName() == "-")
4019 1 : val.Set("/vsistdin/");
4020 : }
4021 6106 : return true;
4022 : });
4023 13199 : return arg;
4024 : }
4025 :
4026 : /************************************************************************/
4027 : /* GDALAlgorithm::AddOutputDatasetArg() */
4028 : /************************************************************************/
4029 :
4030 8215 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4031 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4032 : bool positionalAndRequired, const char *helpMessage)
4033 : {
4034 : auto &arg =
4035 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4036 : MsgOrDefault(
4037 : helpMessage,
4038 : CPLSPrintf("Output %s dataset",
4039 8215 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4040 24645 : pValue, type)
4041 8215 : .SetIsInput(true)
4042 8215 : .SetIsOutput(true)
4043 8215 : .SetDatasetInputFlags(GADV_NAME)
4044 8215 : .SetDatasetOutputFlags(GADV_OBJECT);
4045 8215 : if (positionalAndRequired)
4046 4290 : arg.SetPositional().SetRequired();
4047 :
4048 8215 : AddValidationAction(
4049 11520 : [this, &arg, pValue]()
4050 : {
4051 3567 : if (pValue->GetName() == "-")
4052 4 : pValue->Set("/vsistdout/");
4053 :
4054 3567 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4055 3515 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4056 5687 : (!outputFormatArg->IsExplicitlySet() ||
4057 9254 : outputFormatArg->Get<std::string>().empty()) &&
4058 1343 : arg.IsExplicitlySet())
4059 : {
4060 : const auto vrtCompatible =
4061 1002 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4062 182 : if (vrtCompatible && !vrtCompatible->empty() &&
4063 1184 : vrtCompatible->front() == "false" &&
4064 1093 : EQUAL(
4065 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4066 : "VRT"))
4067 : {
4068 6 : ReportError(
4069 : CE_Failure, CPLE_NotSupported,
4070 : "VRT output is not supported.%s",
4071 6 : outputFormatArg->GetDescription().find("GDALG") !=
4072 : std::string::npos
4073 : ? " Consider using the GDALG driver instead (files "
4074 : "with .gdalg.json extension)"
4075 : : "");
4076 6 : return false;
4077 : }
4078 996 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4079 1969 : EQUAL(pValue->GetName()
4080 : .substr(pValue->GetName().size() -
4081 : strlen(".gdalg.json"))
4082 : .c_str(),
4083 2965 : ".gdalg.json") &&
4084 27 : outputFormatArg->GetDescription().find("GDALG") ==
4085 : std::string::npos)
4086 : {
4087 0 : ReportError(CE_Failure, CPLE_NotSupported,
4088 : "GDALG output is not supported");
4089 0 : return false;
4090 : }
4091 : }
4092 3561 : return true;
4093 : });
4094 :
4095 8215 : return arg;
4096 : }
4097 :
4098 : /************************************************************************/
4099 : /* GDALAlgorithm::AddOverwriteArg() */
4100 : /************************************************************************/
4101 :
4102 : GDALInConstructionAlgorithmArg &
4103 8125 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4104 : {
4105 : return AddArg(
4106 : GDAL_ARG_NAME_OVERWRITE, 0,
4107 : MsgOrDefault(
4108 : helpMessage,
4109 : _("Whether overwriting existing output dataset is allowed")),
4110 16250 : pValue)
4111 16250 : .SetDefault(false);
4112 : }
4113 :
4114 : /************************************************************************/
4115 : /* GDALAlgorithm::AddOverwriteLayerArg() */
4116 : /************************************************************************/
4117 :
4118 : GDALInConstructionAlgorithmArg &
4119 3402 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4120 : {
4121 3402 : AddValidationAction(
4122 1532 : [this]
4123 : {
4124 1531 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4125 1531 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4126 : {
4127 1 : ReportError(CE_Failure, CPLE_AppDefined,
4128 : "--update argument must exist for "
4129 : "--overwrite-layer, even if hidden");
4130 1 : return false;
4131 : }
4132 1530 : return true;
4133 : });
4134 : return AddArg(
4135 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4136 : MsgOrDefault(
4137 : helpMessage,
4138 : _("Whether overwriting existing output layer is allowed")),
4139 6804 : pValue)
4140 3402 : .SetDefault(false)
4141 : .AddAction(
4142 19 : [this]
4143 : {
4144 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4145 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4146 : {
4147 19 : updateArg->Set(true);
4148 : }
4149 6823 : });
4150 : }
4151 :
4152 : /************************************************************************/
4153 : /* GDALAlgorithm::AddUpdateArg() */
4154 : /************************************************************************/
4155 :
4156 : GDALInConstructionAlgorithmArg &
4157 3953 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4158 : {
4159 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4160 : MsgOrDefault(
4161 : helpMessage,
4162 : _("Whether to open existing dataset in update mode")),
4163 7906 : pValue)
4164 7906 : .SetDefault(false);
4165 : }
4166 :
4167 : /************************************************************************/
4168 : /* GDALAlgorithm::AddAppendLayerArg() */
4169 : /************************************************************************/
4170 :
4171 : GDALInConstructionAlgorithmArg &
4172 3176 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4173 : {
4174 3176 : AddValidationAction(
4175 1487 : [this]
4176 : {
4177 1486 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4178 1486 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4179 : {
4180 1 : ReportError(CE_Failure, CPLE_AppDefined,
4181 : "--update argument must exist for --append, even "
4182 : "if hidden");
4183 1 : return false;
4184 : }
4185 1485 : return true;
4186 : });
4187 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4188 : MsgOrDefault(
4189 : helpMessage,
4190 : _("Whether appending to existing layer is allowed")),
4191 6352 : pValue)
4192 3176 : .SetDefault(false)
4193 : .AddAction(
4194 25 : [this]
4195 : {
4196 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4197 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4198 : {
4199 25 : updateArg->Set(true);
4200 : }
4201 6377 : });
4202 : }
4203 :
4204 : /************************************************************************/
4205 : /* GDALAlgorithm::AddOptionsSuggestions() */
4206 : /************************************************************************/
4207 :
4208 : /* static */
4209 29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4210 : const std::string ¤tValue,
4211 : std::vector<std::string> &oRet)
4212 : {
4213 29 : if (!pszXML)
4214 0 : return false;
4215 58 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4216 29 : if (!poTree)
4217 0 : return false;
4218 :
4219 58 : std::string typedOptionName = currentValue;
4220 29 : const auto posEqual = typedOptionName.find('=');
4221 58 : std::string typedValue;
4222 29 : if (posEqual != 0 && posEqual != std::string::npos)
4223 : {
4224 2 : typedValue = currentValue.substr(posEqual + 1);
4225 2 : typedOptionName.resize(posEqual);
4226 : }
4227 :
4228 405 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4229 376 : psChild = psChild->psNext)
4230 : {
4231 389 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4232 402 : if (pszName && typedOptionName == pszName &&
4233 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4234 2 : strcmp(psChild->pszValue, "Argument") == 0))
4235 : {
4236 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4237 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4238 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4239 13 : if (EQUAL(pszType, "string-select"))
4240 : {
4241 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4242 85 : psChild2 = psChild2->psNext)
4243 : {
4244 85 : if (EQUAL(psChild2->pszValue, "Value"))
4245 : {
4246 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4247 : }
4248 : }
4249 : }
4250 8 : else if (EQUAL(pszType, "boolean"))
4251 : {
4252 3 : if (typedValue == "YES" || typedValue == "NO")
4253 : {
4254 1 : oRet.push_back(currentValue);
4255 1 : return true;
4256 : }
4257 2 : oRet.push_back("NO");
4258 2 : oRet.push_back("YES");
4259 : }
4260 5 : else if (EQUAL(pszType, "int"))
4261 : {
4262 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4263 2 : atoi(pszMax) - atoi(pszMin) < 25)
4264 : {
4265 1 : const int nMax = atoi(pszMax);
4266 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4267 12 : oRet.push_back(std::to_string(i));
4268 : }
4269 : }
4270 :
4271 12 : if (oRet.empty())
4272 : {
4273 4 : if (pszMin && pszMax)
4274 : {
4275 1 : oRet.push_back(std::string("##"));
4276 2 : oRet.push_back(std::string("validity range: [")
4277 1 : .append(pszMin)
4278 1 : .append(",")
4279 1 : .append(pszMax)
4280 1 : .append("]"));
4281 : }
4282 3 : else if (pszMin)
4283 : {
4284 1 : oRet.push_back(std::string("##"));
4285 1 : oRet.push_back(
4286 1 : std::string("validity range: >= ").append(pszMin));
4287 : }
4288 2 : else if (pszMax)
4289 : {
4290 1 : oRet.push_back(std::string("##"));
4291 1 : oRet.push_back(
4292 1 : std::string("validity range: <= ").append(pszMax));
4293 : }
4294 1 : else if (const char *pszDescription =
4295 1 : CPLGetXMLValue(psChild, "description", nullptr))
4296 : {
4297 1 : oRet.push_back(std::string("##"));
4298 2 : oRet.push_back(std::string("type: ")
4299 1 : .append(pszType)
4300 1 : .append(", description: ")
4301 1 : .append(pszDescription));
4302 : }
4303 : }
4304 :
4305 12 : return true;
4306 : }
4307 : }
4308 :
4309 319 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4310 303 : psChild = psChild->psNext)
4311 : {
4312 303 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4313 303 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4314 5 : strcmp(psChild->pszValue, "Argument") == 0))
4315 : {
4316 300 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4317 300 : if (!pszScope ||
4318 40 : (EQUAL(pszScope, "raster") &&
4319 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4320 20 : (EQUAL(pszScope, "vector") &&
4321 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4322 : {
4323 280 : oRet.push_back(std::string(pszName).append("="));
4324 : }
4325 : }
4326 : }
4327 :
4328 16 : return false;
4329 : }
4330 :
4331 : /************************************************************************/
4332 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4333 : /************************************************************************/
4334 :
4335 : //! @cond Doxygen_Suppress
4336 : std::vector<std::string>
4337 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4338 : {
4339 2 : std::vector<std::string> oRet;
4340 :
4341 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4342 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4343 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4344 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4345 : {
4346 2 : datasetType = inputArg->GetDatasetType();
4347 : }
4348 :
4349 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4350 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4351 2 : inputFormat->IsExplicitlySet())
4352 : {
4353 : const auto &aosAllowedDrivers =
4354 1 : inputFormat->Get<std::vector<std::string>>();
4355 1 : if (aosAllowedDrivers.size() == 1)
4356 : {
4357 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4358 1 : aosAllowedDrivers[0].c_str());
4359 1 : if (poDriver)
4360 : {
4361 1 : AddOptionsSuggestions(
4362 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4363 : datasetType, currentValue, oRet);
4364 : }
4365 1 : return oRet;
4366 : }
4367 : }
4368 :
4369 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4370 372 : &oRet](const GDALArgDatasetValue &datasetValue)
4371 : {
4372 1 : auto poDM = GetGDALDriverManager();
4373 :
4374 1 : const auto &osDSName = datasetValue.GetName();
4375 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4376 1 : if (!osExt.empty())
4377 : {
4378 1 : std::set<std::string> oVisitedExtensions;
4379 228 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4380 : {
4381 227 : auto poDriver = poDM->GetDriver(i);
4382 681 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4383 227 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4384 72 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4385 454 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4386 72 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4387 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4388 : {
4389 : const char *pszExtensions =
4390 155 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4391 155 : if (pszExtensions)
4392 : {
4393 : const CPLStringList aosExts(
4394 102 : CSLTokenizeString2(pszExtensions, " ", 0));
4395 225 : for (const char *pszExt : cpl::Iterate(aosExts))
4396 : {
4397 127 : if (EQUAL(pszExt, osExt.c_str()) &&
4398 3 : !cpl::contains(oVisitedExtensions, pszExt))
4399 : {
4400 1 : oVisitedExtensions.insert(pszExt);
4401 1 : if (AddOptionsSuggestions(
4402 : poDriver->GetMetadataItem(
4403 1 : GDAL_DMD_OPENOPTIONLIST),
4404 : datasetType, currentValue, oRet))
4405 : {
4406 0 : return;
4407 : }
4408 1 : break;
4409 : }
4410 : }
4411 : }
4412 : }
4413 : }
4414 : }
4415 1 : };
4416 :
4417 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4418 : {
4419 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4420 0 : AddSuggestions(datasetValue);
4421 : }
4422 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4423 : {
4424 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4425 1 : if (datasetValues.size() == 1)
4426 1 : AddSuggestions(datasetValues[0]);
4427 : }
4428 :
4429 1 : return oRet;
4430 : }
4431 :
4432 : //! @endcond
4433 :
4434 : /************************************************************************/
4435 : /* GDALAlgorithm::AddOpenOptionsArg() */
4436 : /************************************************************************/
4437 :
4438 : GDALInConstructionAlgorithmArg &
4439 9052 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4440 : const char *helpMessage)
4441 : {
4442 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4443 18104 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4444 18104 : .AddAlias("oo")
4445 18104 : .SetMetaVar("<KEY>=<VALUE>")
4446 9052 : .SetPackedValuesAllowed(false)
4447 9052 : .SetCategory(GAAC_ADVANCED);
4448 :
4449 21 : arg.AddValidationAction([this, &arg]()
4450 9073 : { return ParseAndValidateKeyValue(arg); });
4451 :
4452 : arg.SetAutoCompleteFunction(
4453 2 : [this](const std::string ¤tValue)
4454 9054 : { return OpenOptionCompleteFunction(currentValue); });
4455 :
4456 9052 : return arg;
4457 : }
4458 :
4459 : /************************************************************************/
4460 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4461 : /************************************************************************/
4462 :
4463 : GDALInConstructionAlgorithmArg &
4464 3173 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4465 : const char *helpMessage)
4466 : {
4467 : auto &arg =
4468 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4469 6346 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4470 6346 : .AddAlias("output-oo")
4471 6346 : .SetMetaVar("<KEY>=<VALUE>")
4472 3173 : .SetPackedValuesAllowed(false)
4473 3173 : .SetCategory(GAAC_ADVANCED);
4474 :
4475 0 : arg.AddValidationAction([this, &arg]()
4476 3173 : { return ParseAndValidateKeyValue(arg); });
4477 :
4478 : arg.SetAutoCompleteFunction(
4479 0 : [this](const std::string ¤tValue)
4480 3173 : { return OpenOptionCompleteFunction(currentValue); });
4481 :
4482 3173 : return arg;
4483 : }
4484 :
4485 : /************************************************************************/
4486 : /* ValidateFormat() */
4487 : /************************************************************************/
4488 :
4489 4617 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4490 : bool bStreamAllowed,
4491 : bool bGDALGAllowed) const
4492 : {
4493 4617 : if (arg.GetChoices().empty())
4494 : {
4495 : const auto Validate =
4496 19761 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4497 : {
4498 4512 : if (const auto extraFormats =
4499 4512 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4500 : {
4501 60 : for (const auto &extraFormat : *extraFormats)
4502 : {
4503 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4504 14 : return true;
4505 : }
4506 : }
4507 :
4508 4498 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4509 1805 : return true;
4510 :
4511 2699 : if (EQUAL(val.c_str(), "GDALG") &&
4512 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4513 : {
4514 2 : if (bGDALGAllowed)
4515 : {
4516 2 : return true;
4517 : }
4518 : else
4519 : {
4520 0 : ReportError(CE_Failure, CPLE_NotSupported,
4521 : "GDALG output is not supported.");
4522 0 : return false;
4523 : }
4524 : }
4525 :
4526 : const auto vrtCompatible =
4527 2691 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4528 440 : if (vrtCompatible && !vrtCompatible->empty() &&
4529 3131 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4530 : {
4531 7 : ReportError(CE_Failure, CPLE_NotSupported,
4532 : "VRT output is not supported.%s",
4533 : bGDALGAllowed
4534 : ? " Consider using the GDALG driver instead "
4535 : "(files with .gdalg.json extension)."
4536 : : "");
4537 7 : return false;
4538 : }
4539 :
4540 : const auto allowedFormats =
4541 2684 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4542 2705 : if (allowedFormats && !allowedFormats->empty() &&
4543 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4544 2705 : val) != allowedFormats->end())
4545 : {
4546 9 : return true;
4547 : }
4548 :
4549 : const auto excludedFormats =
4550 2675 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4551 2687 : if (excludedFormats && !excludedFormats->empty() &&
4552 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4553 2687 : val) != excludedFormats->end())
4554 : {
4555 0 : ReportError(CE_Failure, CPLE_NotSupported,
4556 : "%s output is not supported.", val.c_str());
4557 0 : return false;
4558 : }
4559 :
4560 2675 : auto hDriver = GDALGetDriverByName(val.c_str());
4561 2675 : if (!hDriver)
4562 : {
4563 : auto poMissingDriver =
4564 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4565 4 : if (poMissingDriver)
4566 : {
4567 : const std::string msg =
4568 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4569 0 : ReportError(CE_Failure, CPLE_AppDefined,
4570 : "Invalid value for argument '%s'. Driver '%s' "
4571 : "not found but is known. However plugin %s",
4572 0 : arg.GetName().c_str(), val.c_str(),
4573 : msg.c_str());
4574 : }
4575 : else
4576 : {
4577 8 : ReportError(CE_Failure, CPLE_AppDefined,
4578 : "Invalid value for argument '%s'. Driver '%s' "
4579 : "does not exist.",
4580 4 : arg.GetName().c_str(), val.c_str());
4581 : }
4582 4 : return false;
4583 : }
4584 :
4585 2671 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4586 2671 : if (caps)
4587 : {
4588 8019 : for (const std::string &cap : *caps)
4589 : {
4590 : const char *pszVal =
4591 5373 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4592 5373 : if (!(pszVal && pszVal[0]))
4593 : {
4594 1569 : if (cap == GDAL_DCAP_CREATECOPY &&
4595 0 : std::find(caps->begin(), caps->end(),
4596 783 : GDAL_DCAP_RASTER) != caps->end() &&
4597 783 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4598 1569 : nullptr) &&
4599 783 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4600 : nullptr))
4601 : {
4602 : // if it supports Create, it supports CreateCopy
4603 : }
4604 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4605 : {
4606 2 : ReportError(
4607 : CE_Failure, CPLE_AppDefined,
4608 : "Invalid value for argument '%s'. Driver '%s' "
4609 : "does "
4610 : "not advertise any file format extension.",
4611 1 : arg.GetName().c_str(), val.c_str());
4612 3 : return false;
4613 : }
4614 : else
4615 : {
4616 2 : if (cap == GDAL_DCAP_CREATE)
4617 : {
4618 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4619 1 : if (updateArg &&
4620 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4621 1 : updateArg->IsExplicitlySet())
4622 : {
4623 0 : continue;
4624 : }
4625 :
4626 2 : ReportError(
4627 : CE_Failure, CPLE_AppDefined,
4628 : "Invalid value for argument '%s'. "
4629 : "Driver '%s' does not have write support.",
4630 1 : arg.GetName().c_str(), val.c_str());
4631 1 : return false;
4632 : }
4633 : else
4634 : {
4635 2 : ReportError(
4636 : CE_Failure, CPLE_AppDefined,
4637 : "Invalid value for argument '%s'. Driver "
4638 : "'%s' "
4639 : "does "
4640 : "not expose the required '%s' capability.",
4641 1 : arg.GetName().c_str(), val.c_str(),
4642 : cap.c_str());
4643 1 : return false;
4644 : }
4645 : }
4646 : }
4647 : }
4648 : }
4649 2668 : return true;
4650 4515 : };
4651 :
4652 4515 : if (arg.GetType() == GAAT_STRING)
4653 : {
4654 4505 : return Validate(arg.Get<std::string>());
4655 : }
4656 12 : else if (arg.GetType() == GAAT_STRING_LIST)
4657 : {
4658 19 : for (const auto &val : arg.Get<std::vector<std::string>>())
4659 : {
4660 9 : if (!Validate(val))
4661 2 : return false;
4662 : }
4663 : }
4664 : }
4665 :
4666 112 : return true;
4667 : }
4668 :
4669 : /************************************************************************/
4670 : /* FormatAutoCompleteFunction() */
4671 : /************************************************************************/
4672 :
4673 : /* static */
4674 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4675 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4676 : {
4677 7 : std::vector<std::string> res;
4678 7 : auto poDM = GetGDALDriverManager();
4679 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4680 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4681 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4682 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4683 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4684 0 : res = std::move(*extraFormats);
4685 1595 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4686 : {
4687 1588 : auto poDriver = poDM->GetDriver(i);
4688 :
4689 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4690 1588 : vrtCompatible->front() == "false" &&
4691 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4692 : {
4693 : // do nothing
4694 : }
4695 1588 : else if (allowedFormats && !allowedFormats->empty() &&
4696 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4697 1588 : poDriver->GetDescription()) != allowedFormats->end())
4698 : {
4699 0 : res.push_back(poDriver->GetDescription());
4700 : }
4701 1588 : else if (excludedFormats && !excludedFormats->empty() &&
4702 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4703 0 : poDriver->GetDescription()) !=
4704 1588 : excludedFormats->end())
4705 : {
4706 0 : continue;
4707 : }
4708 1588 : else if (caps)
4709 : {
4710 1588 : bool ok = true;
4711 3136 : for (const std::string &cap : *caps)
4712 : {
4713 2362 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4714 : {
4715 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4716 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4717 : {
4718 0 : ok = false;
4719 0 : break;
4720 : }
4721 : }
4722 2362 : else if (const char *pszVal =
4723 2362 : poDriver->GetMetadataItem(cap.c_str());
4724 1476 : pszVal && pszVal[0])
4725 : {
4726 : }
4727 1274 : else if (cap == GDAL_DCAP_CREATECOPY &&
4728 0 : (std::find(caps->begin(), caps->end(),
4729 388 : GDAL_DCAP_RASTER) != caps->end() &&
4730 1662 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4731 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4732 : {
4733 : // if it supports Create, it supports CreateCopy
4734 : }
4735 : else
4736 : {
4737 814 : ok = false;
4738 814 : break;
4739 : }
4740 : }
4741 1588 : if (ok)
4742 : {
4743 774 : res.push_back(poDriver->GetDescription());
4744 : }
4745 : }
4746 : }
4747 7 : if (bGDALGAllowed)
4748 4 : res.push_back("GDALG");
4749 7 : return res;
4750 : }
4751 :
4752 : /************************************************************************/
4753 : /* GDALAlgorithm::AddInputFormatsArg() */
4754 : /************************************************************************/
4755 :
4756 : GDALInConstructionAlgorithmArg &
4757 8799 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4758 : const char *helpMessage)
4759 : {
4760 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4761 17598 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4762 17598 : .AddAlias("if")
4763 8799 : .SetCategory(GAAC_ADVANCED);
4764 12 : arg.AddValidationAction([this, &arg]()
4765 8811 : { return ValidateFormat(arg, false, false); });
4766 : arg.SetAutoCompleteFunction(
4767 1 : [&arg](const std::string &)
4768 8800 : { return FormatAutoCompleteFunction(arg, false, false); });
4769 8799 : return arg;
4770 : }
4771 :
4772 : /************************************************************************/
4773 : /* GDALAlgorithm::AddOutputFormatArg() */
4774 : /************************************************************************/
4775 :
4776 : GDALInConstructionAlgorithmArg &
4777 9194 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4778 : bool bGDALGAllowed, const char *helpMessage)
4779 : {
4780 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4781 : MsgOrDefault(helpMessage,
4782 : bGDALGAllowed
4783 : ? _("Output format (\"GDALG\" allowed)")
4784 : : _("Output format")),
4785 18388 : pValue)
4786 18388 : .AddAlias("of")
4787 9194 : .AddAlias("format");
4788 : arg.AddValidationAction(
4789 4601 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4790 13795 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4791 : arg.SetAutoCompleteFunction(
4792 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4793 : {
4794 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4795 4 : bGDALGAllowed);
4796 9194 : });
4797 9194 : return arg;
4798 : }
4799 :
4800 : /************************************************************************/
4801 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4802 : /************************************************************************/
4803 : GDALInConstructionAlgorithmArg &
4804 1738 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4805 : const char *helpMessage)
4806 : {
4807 : auto &arg =
4808 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4809 3476 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4810 3476 : .AddAlias("ot")
4811 3476 : .AddAlias("datatype")
4812 5214 : .AddMetadataItem("type", {"GDALDataType"})
4813 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4814 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4815 1738 : "Float32", "Float64", "CFloat32", "CFloat64")
4816 1738 : .SetHiddenChoices("Byte");
4817 1738 : return arg;
4818 : }
4819 :
4820 : /************************************************************************/
4821 : /* GDALAlgorithm::AddNodataArg() */
4822 : /************************************************************************/
4823 :
4824 : GDALInConstructionAlgorithmArg &
4825 646 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4826 : const std::string &optionName,
4827 : const char *helpMessage)
4828 : {
4829 : auto &arg = AddArg(
4830 : optionName, 0,
4831 : MsgOrDefault(helpMessage,
4832 : noneAllowed
4833 : ? _("Assign a specified nodata value to output bands "
4834 : "('none', numeric value, 'nan', 'inf', '-inf')")
4835 : : _("Assign a specified nodata value to output bands "
4836 : "(numeric value, 'nan', 'inf', '-inf')")),
4837 646 : pValue);
4838 : arg.AddValidationAction(
4839 356 : [this, pValue, noneAllowed, optionName]()
4840 : {
4841 77 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4842 : {
4843 67 : char *endptr = nullptr;
4844 67 : CPLStrtod(pValue->c_str(), &endptr);
4845 67 : if (endptr != pValue->c_str() + pValue->size())
4846 : {
4847 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4848 : "Value of '%s' should be %sa "
4849 : "numeric value, 'nan', 'inf' or '-inf'",
4850 : optionName.c_str(),
4851 : noneAllowed ? "'none', " : "");
4852 1 : return false;
4853 : }
4854 : }
4855 76 : return true;
4856 646 : });
4857 646 : return arg;
4858 : }
4859 :
4860 : /************************************************************************/
4861 : /* GDALAlgorithm::AddOutputStringArg() */
4862 : /************************************************************************/
4863 :
4864 : GDALInConstructionAlgorithmArg &
4865 5888 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4866 : {
4867 : return AddArg(
4868 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4869 : MsgOrDefault(helpMessage,
4870 : _("Output string, in which the result is placed")),
4871 11776 : pValue)
4872 5888 : .SetHiddenForCLI()
4873 5888 : .SetIsInput(false)
4874 11776 : .SetIsOutput(true);
4875 : }
4876 :
4877 : /************************************************************************/
4878 : /* GDALAlgorithm::AddStdoutArg() */
4879 : /************************************************************************/
4880 :
4881 : GDALInConstructionAlgorithmArg &
4882 1470 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4883 : {
4884 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4885 : MsgOrDefault(helpMessage,
4886 : _("Directly output on stdout. If enabled, "
4887 : "output-string will be empty")),
4888 2940 : pValue)
4889 2940 : .SetHidden();
4890 : }
4891 :
4892 : /************************************************************************/
4893 : /* GDALAlgorithm::AddLayerNameArg() */
4894 : /************************************************************************/
4895 :
4896 : GDALInConstructionAlgorithmArg &
4897 207 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4898 : {
4899 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4900 207 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4901 : }
4902 :
4903 : /************************************************************************/
4904 : /* GDALAlgorithm::AddArrayNameArg() */
4905 : /************************************************************************/
4906 :
4907 : GDALInConstructionAlgorithmArg &
4908 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
4909 : {
4910 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
4911 100 : pValue)
4912 2 : .SetAutoCompleteFunction([this](const std::string &)
4913 102 : { return AutoCompleteArrayName(); });
4914 : }
4915 :
4916 : /************************************************************************/
4917 : /* GDALAlgorithm::AddArrayNameArg() */
4918 : /************************************************************************/
4919 :
4920 : GDALInConstructionAlgorithmArg &
4921 76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
4922 : const char *helpMessage)
4923 : {
4924 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
4925 152 : pValue)
4926 0 : .SetAutoCompleteFunction([this](const std::string &)
4927 152 : { return AutoCompleteArrayName(); });
4928 : }
4929 :
4930 : /************************************************************************/
4931 : /* GDALAlgorithm::AutoCompleteArrayName() */
4932 : /************************************************************************/
4933 :
4934 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
4935 : {
4936 2 : std::vector<std::string> ret;
4937 4 : std::string osDSName;
4938 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4939 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4940 : {
4941 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4942 0 : if (!inputDatasets.empty())
4943 : {
4944 0 : osDSName = inputDatasets[0].GetName();
4945 : }
4946 : }
4947 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
4948 : {
4949 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
4950 2 : osDSName = inputDataset.GetName();
4951 : }
4952 :
4953 2 : if (!osDSName.empty())
4954 : {
4955 4 : CPLStringList aosAllowedDrivers;
4956 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4957 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
4958 : aosAllowedDrivers =
4959 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
4960 :
4961 4 : CPLStringList aosOpenOptions;
4962 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
4963 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
4964 : aosOpenOptions =
4965 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
4966 :
4967 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4968 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
4969 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
4970 : {
4971 2 : if (auto poRG = poDS->GetRootGroup())
4972 : {
4973 1 : ret = poRG->GetMDArrayFullNamesRecursive();
4974 : }
4975 : }
4976 : }
4977 :
4978 4 : return ret;
4979 : }
4980 :
4981 : /************************************************************************/
4982 : /* GDALAlgorithm::AddMemorySizeArg() */
4983 : /************************************************************************/
4984 :
4985 : GDALInConstructionAlgorithmArg &
4986 224 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
4987 : const std::string &optionName,
4988 : const char *helpMessage)
4989 : {
4990 448 : return AddArg(optionName, 0, helpMessage, pStrValue)
4991 224 : .SetDefault(*pStrValue)
4992 : .AddValidationAction(
4993 139 : [this, pValue, pStrValue]()
4994 : {
4995 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
4996 : GIntBig nBytes;
4997 : bool bUnitSpecified;
4998 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
4999 47 : &bUnitSpecified) != CE_None)
5000 : {
5001 2 : return false;
5002 : }
5003 45 : if (!bUnitSpecified)
5004 : {
5005 1 : ReportError(CE_Failure, CPLE_AppDefined,
5006 : "Memory size must have a unit or be a "
5007 : "percentage of usable RAM (2GB, 5%%, etc.)");
5008 1 : return false;
5009 : }
5010 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5011 : {
5012 : // -1 to please CoverityScan
5013 : if (static_cast<std::uint64_t>(nBytes) >
5014 : std::numeric_limits<size_t>::max() - 1U)
5015 : {
5016 : ReportError(CE_Failure, CPLE_AppDefined,
5017 : "Memory size %s is too large.",
5018 : pStrValue->c_str());
5019 : return false;
5020 : }
5021 : }
5022 :
5023 44 : *pValue = static_cast<size_t>(nBytes);
5024 44 : return true;
5025 448 : });
5026 : }
5027 :
5028 : /************************************************************************/
5029 : /* GDALAlgorithm::AddOutputLayerNameArg() */
5030 : /************************************************************************/
5031 :
5032 : GDALInConstructionAlgorithmArg &
5033 474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5034 : const char *helpMessage)
5035 : {
5036 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5037 474 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5038 : }
5039 :
5040 : /************************************************************************/
5041 : /* GDALAlgorithm::AddLayerNameArg() */
5042 : /************************************************************************/
5043 :
5044 : GDALInConstructionAlgorithmArg &
5045 882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5046 : const char *helpMessage)
5047 : {
5048 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5049 882 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5050 : }
5051 :
5052 : /************************************************************************/
5053 : /* GDALAlgorithm::AddGeometryTypeArg() */
5054 : /************************************************************************/
5055 :
5056 : GDALInConstructionAlgorithmArg &
5057 422 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5058 : {
5059 : return AddArg("geometry-type", 0,
5060 844 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5061 : .SetAutoCompleteFunction(
5062 3 : [](const std::string ¤tValue)
5063 : {
5064 3 : std::vector<std::string> oRet;
5065 51 : for (const char *type :
5066 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5067 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5068 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5069 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5070 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5071 : {
5072 68 : if (currentValue.empty() ||
5073 17 : STARTS_WITH(type, currentValue.c_str()))
5074 : {
5075 35 : oRet.push_back(type);
5076 35 : oRet.push_back(std::string(type).append("Z"));
5077 35 : oRet.push_back(std::string(type).append("M"));
5078 35 : oRet.push_back(std::string(type).append("ZM"));
5079 : }
5080 : }
5081 3 : return oRet;
5082 844 : })
5083 : .AddValidationAction(
5084 118 : [this, pValue]()
5085 : {
5086 107 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5087 115 : wkbUnknown &&
5088 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5089 : {
5090 3 : ReportError(CE_Failure, CPLE_AppDefined,
5091 : "Invalid geometry type '%s'", pValue->c_str());
5092 3 : return false;
5093 : }
5094 104 : return true;
5095 844 : });
5096 : }
5097 :
5098 : /************************************************************************/
5099 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
5100 : /************************************************************************/
5101 :
5102 : /* static */
5103 2895 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5104 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5105 : {
5106 2895 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5107 : datasetArg.GetType() == GAAT_DATASET_LIST);
5108 :
5109 : layerArg.SetAutoCompleteFunction(
5110 18 : [&datasetArg](const std::string ¤tValue)
5111 : {
5112 6 : std::vector<std::string> ret;
5113 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5114 6 : GDALArgDatasetValue *dsVal = nullptr;
5115 6 : if (datasetArg.GetType() == GAAT_DATASET)
5116 : {
5117 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5118 : }
5119 : else
5120 : {
5121 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5122 6 : if (val.size() == 1)
5123 : {
5124 6 : dsVal = &val[0];
5125 : }
5126 : }
5127 6 : if (dsVal && !dsVal->GetName().empty())
5128 : {
5129 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5130 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5131 6 : if (poDS)
5132 : {
5133 12 : for (auto &&poLayer : poDS->GetLayers())
5134 : {
5135 6 : if (currentValue == poLayer->GetDescription())
5136 : {
5137 1 : ret.clear();
5138 1 : ret.push_back(poLayer->GetDescription());
5139 1 : break;
5140 : }
5141 5 : ret.push_back(poLayer->GetDescription());
5142 : }
5143 : }
5144 : }
5145 12 : return ret;
5146 2895 : });
5147 2895 : }
5148 :
5149 : /************************************************************************/
5150 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5151 : /************************************************************************/
5152 :
5153 131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5154 : GDALInConstructionAlgorithmArg &fieldArg,
5155 : GDALInConstructionAlgorithmArg &layerNameArg,
5156 : std::vector<GDALArgDatasetValue> &datasetArg)
5157 : {
5158 :
5159 : fieldArg.SetAutoCompleteFunction(
5160 12 : [&datasetArg, &layerNameArg](const std::string ¤tValue)
5161 : {
5162 8 : std::set<std::string> ret;
5163 4 : if (!datasetArg.empty())
5164 : {
5165 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5166 :
5167 14 : auto getLayerFields = [&ret, ¤tValue](OGRLayer *poLayer)
5168 : {
5169 2 : auto poDefn = poLayer->GetLayerDefn();
5170 2 : const int nFieldCount = poDefn->GetFieldCount();
5171 8 : for (int iField = 0; iField < nFieldCount; iField++)
5172 : {
5173 : const char *fieldName =
5174 6 : poDefn->GetFieldDefn(iField)->GetNameRef();
5175 6 : if (currentValue == fieldName)
5176 : {
5177 0 : ret.clear();
5178 0 : ret.insert(fieldName);
5179 0 : break;
5180 : }
5181 6 : ret.insert(fieldName);
5182 : }
5183 2 : };
5184 :
5185 2 : GDALArgDatasetValue &dsVal = datasetArg[0];
5186 :
5187 2 : if (!dsVal.GetName().empty())
5188 : {
5189 : auto poDS = std::unique_ptr<GDALDataset>(
5190 2 : GDALDataset::Open(dsVal.GetName().c_str(),
5191 4 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5192 2 : if (poDS)
5193 : {
5194 2 : const auto &layerName = layerNameArg.Get<std::string>();
5195 2 : if (layerName.empty())
5196 : {
5197 : // Loop through all layers
5198 4 : for (auto &&poLayer : poDS->GetLayers())
5199 : {
5200 2 : getLayerFields(poLayer);
5201 : }
5202 : }
5203 : else
5204 : {
5205 0 : const auto poLayer = poDS->GetLayerByName(
5206 0 : layerNameArg.Get<std::string>().c_str());
5207 0 : if (poLayer)
5208 : {
5209 0 : getLayerFields(poLayer);
5210 : }
5211 : }
5212 : }
5213 : }
5214 : }
5215 4 : std::vector<std::string> retVector(ret.begin(), ret.end());
5216 8 : return retVector;
5217 131 : });
5218 131 : }
5219 :
5220 : /************************************************************************/
5221 : /* GDALAlgorithm::AddFieldNameArg() */
5222 : /************************************************************************/
5223 :
5224 : GDALInConstructionAlgorithmArg &
5225 131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5226 : {
5227 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5228 131 : pValue);
5229 : }
5230 :
5231 : /************************************************************************/
5232 : /* GDALAlgorithm::ParseFieldDefinition() */
5233 : /************************************************************************/
5234 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5235 : OGRFieldDefn *poFieldDefn,
5236 : std::string *posError)
5237 : {
5238 : static const std::regex re(
5239 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5240 134 : std::smatch match;
5241 67 : if (std::regex_match(posStrDef, match, re))
5242 : {
5243 132 : const std::string name = match[1];
5244 132 : const std::string type = match[2];
5245 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5246 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5247 66 : poFieldDefn->SetName(name.c_str());
5248 :
5249 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5250 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5251 : {
5252 1 : if (posError)
5253 1 : *posError = "Unsupported field type: " + type;
5254 :
5255 1 : return false;
5256 : }
5257 65 : poFieldDefn->SetType(typeEnum);
5258 65 : poFieldDefn->SetWidth(width);
5259 65 : poFieldDefn->SetPrecision(precision);
5260 65 : return true;
5261 : }
5262 :
5263 1 : if (posError)
5264 : *posError = "Invalid field definition format. Expected "
5265 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5266 :
5267 1 : return false;
5268 : }
5269 :
5270 : /************************************************************************/
5271 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5272 : /************************************************************************/
5273 :
5274 : GDALInConstructionAlgorithmArg &
5275 129 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5276 : std::vector<OGRFieldDefn> *pFieldDefns,
5277 : const char *helpMessage)
5278 : {
5279 : auto &arg =
5280 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5281 258 : pValues)
5282 258 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5283 129 : .SetPackedValuesAllowed(true)
5284 129 : .SetRepeatedArgAllowed(true);
5285 :
5286 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5287 : {
5288 65 : pFieldDefns->clear();
5289 130 : for (const auto &strValue : *pValues)
5290 : {
5291 67 : OGRFieldDefn fieldDefn("", OFTString);
5292 67 : std::string error;
5293 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5294 : &error))
5295 : {
5296 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5297 2 : return false;
5298 : }
5299 : // Check uniqueness of field names
5300 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5301 : {
5302 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5303 : fieldDefn.GetNameRef()))
5304 : {
5305 0 : ReportError(CE_Failure, CPLE_AppDefined,
5306 : "Duplicate field name: '%s'",
5307 : fieldDefn.GetNameRef());
5308 0 : return false;
5309 : }
5310 : }
5311 65 : pFieldDefns->push_back(fieldDefn);
5312 : }
5313 63 : return true;
5314 129 : };
5315 :
5316 129 : arg.AddValidationAction(std::move(validationFunction));
5317 :
5318 129 : return arg;
5319 : }
5320 :
5321 : /************************************************************************/
5322 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5323 : /************************************************************************/
5324 :
5325 262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5326 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5327 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5328 : {
5329 : auto &arg =
5330 524 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5331 786 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5332 : .SetAutoCompleteFunction(
5333 1 : [](const std::string ¤tValue)
5334 : {
5335 1 : std::vector<std::string> oRet;
5336 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5337 : {
5338 : const char *pszSubType =
5339 5 : OGRFieldDefn::GetFieldSubTypeName(
5340 : static_cast<OGRFieldSubType>(i));
5341 5 : if (pszSubType != nullptr)
5342 : {
5343 5 : if (currentValue.empty() ||
5344 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5345 : {
5346 5 : oRet.push_back(pszSubType);
5347 : }
5348 : }
5349 : }
5350 :
5351 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5352 : {
5353 : // Skip deprecated
5354 14 : if (static_cast<OGRFieldType>(i) ==
5355 13 : OGRFieldType::OFTWideString ||
5356 : static_cast<OGRFieldType>(i) ==
5357 : OGRFieldType::OFTWideStringList)
5358 2 : continue;
5359 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5360 : static_cast<OGRFieldType>(i));
5361 12 : if (pszType != nullptr)
5362 : {
5363 12 : if (currentValue.empty() ||
5364 0 : STARTS_WITH(pszType, currentValue.c_str()))
5365 : {
5366 12 : oRet.push_back(pszType);
5367 : }
5368 : }
5369 : }
5370 1 : return oRet;
5371 262 : });
5372 :
5373 : auto validationFunction =
5374 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5375 : {
5376 120 : bool isValid{true};
5377 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5378 :
5379 : // String is returned for unknown types
5380 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5381 : {
5382 16 : isValid = false;
5383 : }
5384 :
5385 120 : *pSubtypeValue =
5386 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5387 :
5388 120 : if (*pSubtypeValue != OFSTNone)
5389 : {
5390 15 : isValid = true;
5391 15 : switch (*pSubtypeValue)
5392 : {
5393 6 : case OFSTBoolean:
5394 : case OFSTInt16:
5395 : {
5396 6 : *pTypeValue = OFTInteger;
5397 6 : break;
5398 : }
5399 3 : case OFSTFloat32:
5400 : {
5401 3 : *pTypeValue = OFTReal;
5402 3 : break;
5403 : }
5404 6 : default:
5405 : {
5406 6 : *pTypeValue = OFTString;
5407 6 : break;
5408 : }
5409 : }
5410 : }
5411 :
5412 120 : if (!isValid)
5413 : {
5414 2 : ReportError(CE_Failure, CPLE_AppDefined,
5415 : "Invalid value for argument '%s': '%s'",
5416 1 : arg.GetName().c_str(), pStrValue->c_str());
5417 : }
5418 :
5419 120 : return isValid;
5420 262 : };
5421 :
5422 262 : if (!pStrValue->empty())
5423 : {
5424 0 : arg.SetDefault(*pStrValue);
5425 0 : validationFunction();
5426 : }
5427 :
5428 262 : arg.AddValidationAction(std::move(validationFunction));
5429 :
5430 262 : return arg;
5431 : }
5432 :
5433 : /************************************************************************/
5434 : /* GDALAlgorithm::ValidateBandArg() */
5435 : /************************************************************************/
5436 :
5437 3953 : bool GDALAlgorithm::ValidateBandArg() const
5438 : {
5439 3953 : bool ret = true;
5440 3953 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5441 3953 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5442 1450 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5443 292 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5444 5397 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5445 149 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5446 : {
5447 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5448 : {
5449 99 : if (nBand > poDS->GetRasterCount())
5450 : {
5451 5 : ReportError(CE_Failure, CPLE_AppDefined,
5452 : "Value of 'band' should be greater or equal than "
5453 : "1 and less or equal than %d.",
5454 : poDS->GetRasterCount());
5455 5 : return false;
5456 : }
5457 94 : return true;
5458 92 : };
5459 :
5460 : const auto ValidateForOneDataset =
5461 304 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5462 : {
5463 87 : bool l_ret = true;
5464 87 : if (bandArg->GetType() == GAAT_INTEGER)
5465 : {
5466 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5467 : }
5468 63 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5469 : {
5470 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5471 : {
5472 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5473 : }
5474 : }
5475 87 : return l_ret;
5476 92 : };
5477 :
5478 92 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5479 : {
5480 : auto poDS =
5481 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5482 6 : if (poDS && !ValidateForOneDataset(poDS))
5483 2 : ret = false;
5484 : }
5485 : else
5486 : {
5487 86 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5488 85 : for (auto &datasetValue :
5489 256 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5490 : {
5491 85 : auto poDS = datasetValue.GetDatasetRef();
5492 85 : if (poDS && !ValidateForOneDataset(poDS))
5493 3 : ret = false;
5494 : }
5495 : }
5496 : }
5497 3953 : return ret;
5498 : }
5499 :
5500 : /************************************************************************/
5501 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5502 : /************************************************************************/
5503 :
5504 3157 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5505 : {
5506 3157 : return ValidateBandArg();
5507 : }
5508 :
5509 : /************************************************************************/
5510 : /* GDALAlgorithm::AddBandArg() */
5511 : /************************************************************************/
5512 :
5513 : GDALInConstructionAlgorithmArg &
5514 1450 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5515 : {
5516 1769 : AddValidationAction([this]() { return ValidateBandArg(); });
5517 :
5518 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5519 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5520 2900 : pValue)
5521 : .AddValidationAction(
5522 34 : [pValue]()
5523 : {
5524 34 : if (*pValue <= 0)
5525 : {
5526 1 : CPLError(CE_Failure, CPLE_AppDefined,
5527 : "Value of 'band' should greater or equal to 1.");
5528 1 : return false;
5529 : }
5530 33 : return true;
5531 2900 : });
5532 : }
5533 :
5534 : /************************************************************************/
5535 : /* GDALAlgorithm::AddBandArg() */
5536 : /************************************************************************/
5537 :
5538 : GDALInConstructionAlgorithmArg &
5539 852 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5540 : {
5541 1329 : AddValidationAction([this]() { return ValidateBandArg(); });
5542 :
5543 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5544 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5545 1704 : pValue)
5546 : .AddValidationAction(
5547 126 : [pValue]()
5548 : {
5549 397 : for (int val : *pValue)
5550 : {
5551 272 : if (val <= 0)
5552 : {
5553 1 : CPLError(CE_Failure, CPLE_AppDefined,
5554 : "Value of 'band' should greater or equal "
5555 : "to 1.");
5556 1 : return false;
5557 : }
5558 : }
5559 125 : return true;
5560 1704 : });
5561 : }
5562 :
5563 : /************************************************************************/
5564 : /* ParseAndValidateKeyValue() */
5565 : /************************************************************************/
5566 :
5567 418 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5568 : {
5569 414 : const auto Validate = [this, &arg](const std::string &val)
5570 : {
5571 409 : if (val.find('=') == std::string::npos)
5572 : {
5573 5 : ReportError(
5574 : CE_Failure, CPLE_AppDefined,
5575 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5576 5 : arg.GetName().c_str());
5577 5 : return false;
5578 : }
5579 :
5580 404 : return true;
5581 418 : };
5582 :
5583 418 : if (arg.GetType() == GAAT_STRING)
5584 : {
5585 0 : return Validate(arg.Get<std::string>());
5586 : }
5587 418 : else if (arg.GetType() == GAAT_STRING_LIST)
5588 : {
5589 418 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5590 418 : if (vals.size() == 1)
5591 : {
5592 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5593 750 : std::vector<std::string> newVals;
5594 750 : std::string curToken;
5595 375 : bool canSplitOnComma = true;
5596 375 : char lastSep = 0;
5597 375 : bool inString = false;
5598 375 : bool equalFoundInLastToken = false;
5599 5408 : for (char c : vals[0])
5600 : {
5601 5037 : if (!inString && c == ',')
5602 : {
5603 10 : if (lastSep != '=' || !equalFoundInLastToken)
5604 : {
5605 2 : canSplitOnComma = false;
5606 2 : break;
5607 : }
5608 8 : lastSep = c;
5609 8 : newVals.push_back(curToken);
5610 8 : curToken.clear();
5611 8 : equalFoundInLastToken = false;
5612 : }
5613 5027 : else if (!inString && c == '=')
5614 : {
5615 374 : if (lastSep == '=')
5616 : {
5617 2 : canSplitOnComma = false;
5618 2 : break;
5619 : }
5620 372 : equalFoundInLastToken = true;
5621 372 : lastSep = c;
5622 372 : curToken += c;
5623 : }
5624 4653 : else if (c == '"')
5625 : {
5626 4 : inString = !inString;
5627 4 : curToken += c;
5628 : }
5629 : else
5630 : {
5631 4649 : curToken += c;
5632 : }
5633 : }
5634 375 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5635 : {
5636 362 : if (!curToken.empty())
5637 362 : newVals.emplace_back(std::move(curToken));
5638 362 : vals = std::move(newVals);
5639 : }
5640 : }
5641 :
5642 822 : for (const auto &val : vals)
5643 : {
5644 409 : if (!Validate(val))
5645 5 : return false;
5646 : }
5647 : }
5648 :
5649 413 : return true;
5650 : }
5651 :
5652 : /************************************************************************/
5653 : /* IsGDALGOutput() */
5654 : /************************************************************************/
5655 :
5656 2092 : bool GDALAlgorithm::IsGDALGOutput() const
5657 : {
5658 2092 : bool isGDALGOutput = false;
5659 2092 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5660 2092 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5661 3587 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5662 1495 : outputArg->IsExplicitlySet())
5663 : {
5664 2933 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5665 1454 : outputFormatArg->IsExplicitlySet())
5666 : {
5667 : const auto &val =
5668 949 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5669 949 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5670 : }
5671 : else
5672 : {
5673 : const auto &filename =
5674 530 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5675 530 : isGDALGOutput =
5676 1033 : filename.GetName().size() > strlen(".gdalg.json") &&
5677 503 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5678 : strlen(".gdalg.json"),
5679 : ".gdalg.json");
5680 : }
5681 : }
5682 2092 : return isGDALGOutput;
5683 : }
5684 :
5685 : /************************************************************************/
5686 : /* ProcessGDALGOutput() */
5687 : /************************************************************************/
5688 :
5689 2437 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5690 : {
5691 2437 : if (!SupportsStreamedOutput())
5692 798 : return ProcessGDALGOutputRet::NOT_GDALG;
5693 :
5694 1639 : if (IsGDALGOutput())
5695 : {
5696 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5697 : const auto &filename =
5698 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5699 : VSIStatBufL sStat;
5700 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5701 : {
5702 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5703 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5704 : {
5705 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5706 : {
5707 0 : CPLError(CE_Failure, CPLE_AppDefined,
5708 : "File '%s' already exists. Specify the "
5709 : "--overwrite option to overwrite it.",
5710 : filename.c_str());
5711 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5712 : }
5713 : }
5714 : }
5715 :
5716 22 : std::string osCommandLine;
5717 :
5718 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5719 : {
5720 33 : if (!osCommandLine.empty())
5721 22 : osCommandLine += ' ';
5722 33 : osCommandLine += path;
5723 : }
5724 :
5725 250 : for (const auto &arg : GetArgs())
5726 : {
5727 265 : if (arg->IsExplicitlySet() &&
5728 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5729 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5730 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5731 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5732 : {
5733 14 : osCommandLine += ' ';
5734 14 : std::string strArg;
5735 14 : if (!arg->Serialize(strArg))
5736 : {
5737 0 : CPLError(CE_Failure, CPLE_AppDefined,
5738 : "Cannot serialize argument %s",
5739 0 : arg->GetName().c_str());
5740 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5741 : }
5742 14 : osCommandLine += strArg;
5743 : }
5744 : }
5745 :
5746 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5747 :
5748 11 : std::string outStringUnused;
5749 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5750 11 : ? ProcessGDALGOutputRet::GDALG_OK
5751 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5752 : }
5753 :
5754 1628 : return ProcessGDALGOutputRet::NOT_GDALG;
5755 : }
5756 :
5757 : /************************************************************************/
5758 : /* GDALAlgorithm::SaveGDALG() */
5759 : /************************************************************************/
5760 :
5761 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5762 : std::string &outString,
5763 : const std::string &commandLine)
5764 : {
5765 44 : CPLJSONDocument oDoc;
5766 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5767 22 : oDoc.GetRoot().Add("command_line", commandLine);
5768 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5769 :
5770 22 : if (!filename.empty())
5771 21 : return oDoc.Save(filename);
5772 :
5773 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5774 1 : return true;
5775 : }
5776 :
5777 : /************************************************************************/
5778 : /* GDALAlgorithm::AddCreationOptionsArg() */
5779 : /************************************************************************/
5780 :
5781 : GDALInConstructionAlgorithmArg &
5782 8048 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5783 : const char *helpMessage)
5784 : {
5785 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5786 16096 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5787 16096 : .AddAlias("co")
5788 16096 : .SetMetaVar("<KEY>=<VALUE>")
5789 8048 : .SetPackedValuesAllowed(false);
5790 160 : arg.AddValidationAction([this, &arg]()
5791 8208 : { return ParseAndValidateKeyValue(arg); });
5792 :
5793 : arg.SetAutoCompleteFunction(
5794 48 : [this](const std::string ¤tValue)
5795 : {
5796 16 : std::vector<std::string> oRet;
5797 :
5798 16 : int datasetType =
5799 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5800 16 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5801 16 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5802 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5803 : {
5804 16 : datasetType = outputArg->GetDatasetType();
5805 : }
5806 :
5807 16 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5808 32 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5809 16 : outputFormat->IsExplicitlySet())
5810 : {
5811 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5812 6 : outputFormat->Get<std::string>().c_str());
5813 6 : if (poDriver)
5814 : {
5815 6 : AddOptionsSuggestions(
5816 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5817 : datasetType, currentValue, oRet);
5818 : }
5819 6 : return oRet;
5820 : }
5821 :
5822 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5823 : {
5824 10 : auto poDM = GetGDALDriverManager();
5825 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5826 10 : const auto &osDSName = datasetValue.GetName();
5827 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5828 10 : if (!osExt.empty())
5829 : {
5830 10 : std::set<std::string> oVisitedExtensions;
5831 712 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5832 : {
5833 709 : auto poDriver = poDM->GetDriver(i);
5834 2127 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5835 709 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5836 216 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5837 1418 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5838 216 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5839 0 : poDriver->GetMetadataItem(
5840 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5841 : {
5842 : const char *pszExtensions =
5843 493 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5844 493 : if (pszExtensions)
5845 : {
5846 : const CPLStringList aosExts(
5847 320 : CSLTokenizeString2(pszExtensions, " ", 0));
5848 710 : for (const char *pszExt : cpl::Iterate(aosExts))
5849 : {
5850 416 : if (EQUAL(pszExt, osExt.c_str()) &&
5851 16 : !cpl::contains(oVisitedExtensions,
5852 : pszExt))
5853 : {
5854 10 : oVisitedExtensions.insert(pszExt);
5855 10 : if (AddOptionsSuggestions(
5856 : poDriver->GetMetadataItem(
5857 10 : GDAL_DMD_CREATIONOPTIONLIST),
5858 : datasetType, currentValue,
5859 : oRet))
5860 : {
5861 7 : return oRet;
5862 : }
5863 3 : break;
5864 : }
5865 : }
5866 : }
5867 : }
5868 : }
5869 : }
5870 : }
5871 :
5872 3 : return oRet;
5873 8048 : });
5874 :
5875 8048 : return arg;
5876 : }
5877 :
5878 : /************************************************************************/
5879 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
5880 : /************************************************************************/
5881 :
5882 : GDALInConstructionAlgorithmArg &
5883 3871 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
5884 : const char *helpMessage)
5885 : {
5886 : auto &arg =
5887 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
5888 7742 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
5889 7742 : .AddAlias("lco")
5890 7742 : .SetMetaVar("<KEY>=<VALUE>")
5891 3871 : .SetPackedValuesAllowed(false);
5892 73 : arg.AddValidationAction([this, &arg]()
5893 3944 : { return ParseAndValidateKeyValue(arg); });
5894 :
5895 : arg.SetAutoCompleteFunction(
5896 5 : [this](const std::string ¤tValue)
5897 : {
5898 2 : std::vector<std::string> oRet;
5899 :
5900 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5901 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5902 2 : outputFormat->IsExplicitlySet())
5903 : {
5904 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5905 1 : outputFormat->Get<std::string>().c_str());
5906 1 : if (poDriver)
5907 : {
5908 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
5909 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5910 : GDAL_OF_VECTOR, currentValue, oRet);
5911 : }
5912 1 : return oRet;
5913 : }
5914 :
5915 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5916 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5917 : {
5918 1 : auto poDM = GetGDALDriverManager();
5919 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5920 1 : const auto &osDSName = datasetValue.GetName();
5921 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5922 1 : if (!osExt.empty())
5923 : {
5924 1 : std::set<std::string> oVisitedExtensions;
5925 228 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5926 : {
5927 227 : auto poDriver = poDM->GetDriver(i);
5928 227 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
5929 : {
5930 : const char *pszExtensions =
5931 90 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5932 90 : if (pszExtensions)
5933 : {
5934 : const CPLStringList aosExts(
5935 61 : CSLTokenizeString2(pszExtensions, " ", 0));
5936 154 : for (const char *pszExt : cpl::Iterate(aosExts))
5937 : {
5938 95 : if (EQUAL(pszExt, osExt.c_str()) &&
5939 1 : !cpl::contains(oVisitedExtensions,
5940 : pszExt))
5941 : {
5942 1 : oVisitedExtensions.insert(pszExt);
5943 1 : if (AddOptionsSuggestions(
5944 : poDriver->GetMetadataItem(
5945 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5946 : GDAL_OF_VECTOR, currentValue,
5947 : oRet))
5948 : {
5949 0 : return oRet;
5950 : }
5951 1 : break;
5952 : }
5953 : }
5954 : }
5955 : }
5956 : }
5957 : }
5958 : }
5959 :
5960 1 : return oRet;
5961 3871 : });
5962 :
5963 3871 : return arg;
5964 : }
5965 :
5966 : /************************************************************************/
5967 : /* GDALAlgorithm::AddBBOXArg() */
5968 : /************************************************************************/
5969 :
5970 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
5971 : GDALInConstructionAlgorithmArg &
5972 1838 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
5973 : {
5974 : auto &arg = AddArg("bbox", 0,
5975 : MsgOrDefault(helpMessage,
5976 : _("Bounding box as xmin,ymin,xmax,ymax")),
5977 3676 : pValue)
5978 1838 : .SetRepeatedArgAllowed(false)
5979 1838 : .SetMinCount(4)
5980 1838 : .SetMaxCount(4)
5981 1838 : .SetDisplayHintAboutRepetition(false);
5982 : arg.AddValidationAction(
5983 165 : [&arg]()
5984 : {
5985 165 : const auto &val = arg.Get<std::vector<double>>();
5986 165 : CPLAssert(val.size() == 4);
5987 165 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
5988 : {
5989 5 : CPLError(CE_Failure, CPLE_AppDefined,
5990 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
5991 : "xmin <= xmax and ymin <= ymax");
5992 5 : return false;
5993 : }
5994 160 : return true;
5995 1838 : });
5996 1838 : return arg;
5997 : }
5998 :
5999 : /************************************************************************/
6000 : /* GDALAlgorithm::AddActiveLayerArg() */
6001 : /************************************************************************/
6002 :
6003 : GDALInConstructionAlgorithmArg &
6004 1719 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6005 : {
6006 : return AddArg("active-layer", 0,
6007 : MsgOrDefault(helpMessage,
6008 : _("Set active layer (if not specified, all)")),
6009 1719 : pValue);
6010 : }
6011 :
6012 : /************************************************************************/
6013 : /* GDALAlgorithm::AddNumThreadsArg() */
6014 : /************************************************************************/
6015 :
6016 : GDALInConstructionAlgorithmArg &
6017 678 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6018 : const char *helpMessage)
6019 : {
6020 : auto &arg =
6021 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6022 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6023 678 : pStrValue);
6024 :
6025 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6026 1356 : _("Number of jobs (read-only, hidden argument)"), pValue)
6027 678 : .SetHidden();
6028 :
6029 2589 : auto lambda = [this, &arg, pValue, pStrValue]
6030 : {
6031 863 : bool bOK = false;
6032 863 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6033 : const int nLimit = std::clamp(
6034 863 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6035 1726 : CPLGetNumCPUs());
6036 : const int nNumThreads =
6037 863 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
6038 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6039 863 : if (bOK)
6040 : {
6041 863 : *pValue = nNumThreads;
6042 : }
6043 : else
6044 : {
6045 0 : ReportError(CE_Failure, CPLE_IllegalArg,
6046 : "Invalid value for '%s' argument",
6047 0 : arg.GetName().c_str());
6048 : }
6049 863 : return bOK;
6050 678 : };
6051 678 : if (!pStrValue->empty())
6052 : {
6053 632 : arg.SetDefault(*pStrValue);
6054 632 : lambda();
6055 : }
6056 678 : arg.AddValidationAction(std::move(lambda));
6057 678 : return arg;
6058 : }
6059 :
6060 : /************************************************************************/
6061 : /* GDALAlgorithm::AddAbsolutePathArg() */
6062 : /************************************************************************/
6063 :
6064 : GDALInConstructionAlgorithmArg &
6065 619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6066 : {
6067 : return AddArg(
6068 : "absolute-path", 0,
6069 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6070 : "should be stored as an absolute path")),
6071 619 : pValue);
6072 : }
6073 :
6074 : /************************************************************************/
6075 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
6076 : /************************************************************************/
6077 :
6078 : GDALInConstructionAlgorithmArg &
6079 137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6080 : const char *helpMessage)
6081 : {
6082 :
6083 : const auto pixelFunctionNames =
6084 137 : VRTDerivedRasterBand::GetPixelFunctionNames();
6085 : return AddArg(
6086 : "pixel-function", 0,
6087 : MsgOrDefault(
6088 : helpMessage,
6089 : _("Specify a pixel function to calculate output value from "
6090 : "overlapping inputs")),
6091 274 : pValue)
6092 274 : .SetChoices(pixelFunctionNames);
6093 : }
6094 :
6095 : /************************************************************************/
6096 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
6097 : /************************************************************************/
6098 :
6099 : GDALInConstructionAlgorithmArg &
6100 137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6101 : const char *helpMessage)
6102 : {
6103 : auto &pixelFunctionArgArg =
6104 : AddArg("pixel-function-arg", 0,
6105 : MsgOrDefault(
6106 : helpMessage,
6107 : _("Specify argument(s) to pass to the pixel function")),
6108 274 : pValue)
6109 274 : .SetMetaVar("<NAME>=<VALUE>")
6110 137 : .SetRepeatedArgAllowed(true);
6111 : pixelFunctionArgArg.AddValidationAction(
6112 7 : [this, &pixelFunctionArgArg]()
6113 144 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6114 :
6115 : pixelFunctionArgArg.SetAutoCompleteFunction(
6116 12 : [this](const std::string ¤tValue)
6117 : {
6118 12 : std::string pixelFunction;
6119 6 : const auto pixelFunctionArg = GetArg("pixel-function");
6120 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6121 : {
6122 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
6123 : }
6124 :
6125 6 : std::vector<std::string> ret;
6126 :
6127 6 : if (!pixelFunction.empty())
6128 : {
6129 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6130 : pixelFunction.c_str());
6131 5 : if (!pair)
6132 : {
6133 1 : ret.push_back("**");
6134 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6135 1 : ret.push_back(std::string("\xC2\xA0"
6136 : "Invalid pixel function name"));
6137 : }
6138 4 : else if (pair->second.find("Argument name=") ==
6139 : std::string::npos)
6140 : {
6141 1 : ret.push_back("**");
6142 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6143 1 : ret.push_back(
6144 2 : std::string(
6145 : "\xC2\xA0"
6146 : "No pixel function arguments for pixel function '")
6147 1 : .append(pixelFunction)
6148 1 : .append("'"));
6149 : }
6150 : else
6151 : {
6152 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6153 : ret);
6154 : }
6155 : }
6156 :
6157 12 : return ret;
6158 137 : });
6159 :
6160 137 : return pixelFunctionArgArg;
6161 : }
6162 :
6163 : /************************************************************************/
6164 : /* GDALAlgorithm::AddProgressArg() */
6165 : /************************************************************************/
6166 :
6167 8165 : void GDALAlgorithm::AddProgressArg()
6168 : {
6169 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6170 16330 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6171 8165 : .SetAvailableInPipelineStep(false)
6172 16330 : .SetCategory(GAAC_COMMON)
6173 8165 : .AddAction([this]() { m_progressBarRequested = false; });
6174 :
6175 16330 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6176 8165 : .SetAvailableInPipelineStep(false)
6177 8165 : .SetHidden();
6178 8165 : }
6179 :
6180 : /************************************************************************/
6181 : /* GDALAlgorithm::Run() */
6182 : /************************************************************************/
6183 :
6184 4545 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6185 : {
6186 4545 : WarnIfDeprecated();
6187 :
6188 4545 : if (m_selectedSubAlg)
6189 : {
6190 411 : if (m_calledFromCommandLine)
6191 233 : m_selectedSubAlg->m_calledFromCommandLine = true;
6192 411 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6193 : }
6194 :
6195 4134 : if (m_helpRequested || m_helpDocRequested)
6196 : {
6197 16 : if (m_calledFromCommandLine)
6198 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6199 16 : return true;
6200 : }
6201 :
6202 4118 : if (m_JSONUsageRequested)
6203 : {
6204 3 : if (m_calledFromCommandLine)
6205 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6206 3 : return true;
6207 : }
6208 :
6209 4115 : if (!ValidateArguments())
6210 124 : return false;
6211 :
6212 3991 : if (m_alreadyRun)
6213 : {
6214 3 : ReportError(CE_Failure, CPLE_AppDefined,
6215 : "Run() can be called only once per algorithm instance");
6216 3 : return false;
6217 : }
6218 3988 : m_alreadyRun = true;
6219 :
6220 3988 : switch (ProcessGDALGOutput())
6221 : {
6222 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6223 0 : return false;
6224 :
6225 11 : case ProcessGDALGOutputRet::GDALG_OK:
6226 11 : return true;
6227 :
6228 3977 : case ProcessGDALGOutputRet::NOT_GDALG:
6229 3977 : break;
6230 : }
6231 :
6232 3977 : if (m_executionForStreamOutput)
6233 : {
6234 93 : if (!CheckSafeForStreamOutput())
6235 : {
6236 4 : return false;
6237 : }
6238 : }
6239 :
6240 3973 : return RunImpl(pfnProgress, pProgressData);
6241 : }
6242 :
6243 : /************************************************************************/
6244 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6245 : /************************************************************************/
6246 :
6247 48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6248 : {
6249 48 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6250 48 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6251 : {
6252 48 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6253 48 : if (!EQUAL(val.c_str(), "stream"))
6254 : {
6255 : // For security reasons, to avoid that reading a .gdalg.json file
6256 : // writes a file on the file system.
6257 4 : ReportError(
6258 : CE_Failure, CPLE_NotSupported,
6259 : "in streamed execution, --format stream should be used");
6260 4 : return false;
6261 : }
6262 : }
6263 44 : return true;
6264 : }
6265 :
6266 : /************************************************************************/
6267 : /* GDALAlgorithm::Finalize() */
6268 : /************************************************************************/
6269 :
6270 1655 : bool GDALAlgorithm::Finalize()
6271 : {
6272 1655 : bool ret = true;
6273 1655 : if (m_selectedSubAlg)
6274 239 : ret = m_selectedSubAlg->Finalize();
6275 :
6276 30272 : for (auto &arg : m_args)
6277 : {
6278 28617 : if (arg->GetType() == GAAT_DATASET)
6279 : {
6280 1326 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6281 : }
6282 27291 : else if (arg->GetType() == GAAT_DATASET_LIST)
6283 : {
6284 2540 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6285 : {
6286 1200 : ret = ds.Close() && ret;
6287 : }
6288 : }
6289 : }
6290 1655 : return ret;
6291 : }
6292 :
6293 : /************************************************************************/
6294 : /* GDALAlgorithm::GetArgNamesForCLI() */
6295 : /************************************************************************/
6296 :
6297 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6298 693 : GDALAlgorithm::GetArgNamesForCLI() const
6299 : {
6300 1386 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6301 :
6302 693 : size_t maxOptLen = 0;
6303 8696 : for (const auto &arg : m_args)
6304 : {
6305 8003 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6306 1705 : continue;
6307 6298 : std::string opt;
6308 6298 : bool addComma = false;
6309 6298 : if (!arg->GetShortName().empty())
6310 : {
6311 1431 : opt += '-';
6312 1431 : opt += arg->GetShortName();
6313 1431 : addComma = true;
6314 : }
6315 6298 : for (char alias : arg->GetShortNameAliases())
6316 : {
6317 0 : if (addComma)
6318 0 : opt += ", ";
6319 0 : opt += "-";
6320 0 : opt += alias;
6321 0 : addComma = true;
6322 : }
6323 7050 : for (const std::string &alias : arg->GetAliases())
6324 : {
6325 752 : if (addComma)
6326 324 : opt += ", ";
6327 752 : opt += "--";
6328 752 : opt += alias;
6329 752 : addComma = true;
6330 : }
6331 6298 : if (!arg->GetName().empty())
6332 : {
6333 6298 : if (addComma)
6334 1859 : opt += ", ";
6335 6298 : opt += "--";
6336 6298 : opt += arg->GetName();
6337 : }
6338 6298 : const auto &metaVar = arg->GetMetaVar();
6339 6298 : if (!metaVar.empty())
6340 : {
6341 3936 : opt += ' ';
6342 3936 : if (metaVar.front() != '<')
6343 2817 : opt += '<';
6344 3936 : opt += metaVar;
6345 3936 : if (metaVar.back() != '>')
6346 2811 : opt += '>';
6347 : }
6348 6298 : maxOptLen = std::max(maxOptLen, opt.size());
6349 6298 : options.emplace_back(arg.get(), opt);
6350 : }
6351 :
6352 1386 : return std::make_pair(std::move(options), maxOptLen);
6353 : }
6354 :
6355 : /************************************************************************/
6356 : /* GDALAlgorithm::GetUsageForCLI() */
6357 : /************************************************************************/
6358 :
6359 : std::string
6360 412 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6361 : const UsageOptions &usageOptions) const
6362 : {
6363 412 : if (m_selectedSubAlg)
6364 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6365 :
6366 810 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6367 810 : std::string osPath;
6368 813 : for (const std::string &s : m_callPath)
6369 : {
6370 408 : if (!osPath.empty())
6371 49 : osPath += ' ';
6372 408 : osPath += s;
6373 : }
6374 405 : osRet += ' ';
6375 405 : osRet += osPath;
6376 :
6377 405 : bool hasNonPositionals = false;
6378 5042 : for (const auto &arg : m_args)
6379 : {
6380 4637 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6381 3338 : hasNonPositionals = true;
6382 : }
6383 :
6384 405 : if (HasSubAlgorithms())
6385 : {
6386 9 : if (m_callPath.size() == 1)
6387 : {
6388 8 : osRet += " <COMMAND>";
6389 8 : if (hasNonPositionals)
6390 8 : osRet += " [OPTIONS]";
6391 8 : if (usageOptions.isPipelineStep)
6392 : {
6393 5 : const size_t nLenFirstLine = osRet.size();
6394 5 : osRet += '\n';
6395 5 : osRet.append(nLenFirstLine, '-');
6396 5 : osRet += '\n';
6397 : }
6398 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6399 : }
6400 : else
6401 : {
6402 1 : osRet += " <SUBCOMMAND>";
6403 1 : if (hasNonPositionals)
6404 1 : osRet += " [OPTIONS]";
6405 1 : if (usageOptions.isPipelineStep)
6406 : {
6407 0 : const size_t nLenFirstLine = osRet.size();
6408 0 : osRet += '\n';
6409 0 : osRet.append(nLenFirstLine, '-');
6410 0 : osRet += '\n';
6411 : }
6412 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6413 : }
6414 9 : size_t maxNameLen = 0;
6415 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6416 : {
6417 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6418 : }
6419 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6420 : {
6421 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6422 43 : if (subAlg && !subAlg->IsHidden())
6423 : {
6424 43 : const std::string &name(subAlg->GetName());
6425 43 : osRet += " - ";
6426 43 : osRet += name;
6427 43 : osRet += ": ";
6428 43 : osRet.append(maxNameLen - name.size(), ' ');
6429 43 : osRet += subAlg->GetDescription();
6430 43 : if (!subAlg->m_aliases.empty())
6431 : {
6432 6 : bool first = true;
6433 6 : for (const auto &alias : subAlg->GetAliases())
6434 : {
6435 6 : if (alias ==
6436 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6437 6 : break;
6438 0 : if (first)
6439 0 : osRet += " (alias: ";
6440 : else
6441 0 : osRet += ", ";
6442 0 : osRet += alias;
6443 0 : first = false;
6444 : }
6445 6 : if (!first)
6446 : {
6447 0 : osRet += ')';
6448 : }
6449 : }
6450 43 : osRet += '\n';
6451 : }
6452 : }
6453 :
6454 9 : if (shortUsage && hasNonPositionals)
6455 : {
6456 2 : osRet += "\nTry '";
6457 2 : osRet += osPath;
6458 2 : osRet += " --help' for help.\n";
6459 : }
6460 : }
6461 : else
6462 : {
6463 396 : if (!m_args.empty())
6464 : {
6465 396 : if (hasNonPositionals)
6466 396 : osRet += " [OPTIONS]";
6467 584 : for (const auto *arg : m_positionalArgs)
6468 : {
6469 261 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6470 73 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6471 : {
6472 : const bool optional =
6473 199 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6474 28 : arg->GetName() == "pipeline"));
6475 171 : osRet += ' ';
6476 171 : if (optional)
6477 25 : osRet += '[';
6478 171 : const std::string &metavar = arg->GetMetaVar();
6479 171 : if (!metavar.empty() && metavar[0] == '<')
6480 : {
6481 4 : osRet += metavar;
6482 : }
6483 : else
6484 : {
6485 167 : osRet += '<';
6486 167 : osRet += metavar;
6487 167 : osRet += '>';
6488 : }
6489 213 : if (arg->GetType() == GAAT_DATASET_LIST &&
6490 42 : arg->GetMaxCount() > 1)
6491 : {
6492 28 : osRet += "...";
6493 : }
6494 171 : if (optional)
6495 25 : osRet += ']';
6496 : }
6497 : }
6498 : }
6499 :
6500 396 : const size_t nLenFirstLine = osRet.size();
6501 396 : osRet += '\n';
6502 396 : if (usageOptions.isPipelineStep)
6503 : {
6504 309 : osRet.append(nLenFirstLine, '-');
6505 309 : osRet += '\n';
6506 : }
6507 :
6508 396 : if (shortUsage)
6509 : {
6510 21 : osRet += "Try '";
6511 21 : osRet += osPath;
6512 21 : osRet += " --help' for help.\n";
6513 21 : return osRet;
6514 : }
6515 :
6516 375 : osRet += '\n';
6517 375 : osRet += m_description;
6518 375 : osRet += '\n';
6519 : }
6520 :
6521 384 : if (!m_args.empty() && !shortUsage)
6522 : {
6523 764 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6524 : size_t maxOptLen;
6525 382 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6526 382 : if (usageOptions.maxOptLen)
6527 311 : maxOptLen = usageOptions.maxOptLen;
6528 :
6529 764 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6530 382 : if (m_arbitraryLongNameArgsAllowed)
6531 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6532 :
6533 : const auto OutputArg =
6534 2368 : [this, maxOptLen, &osRet,
6535 23548 : &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6536 : {
6537 2368 : osRet += " ";
6538 2368 : osRet += opt;
6539 2368 : osRet += " ";
6540 2368 : osRet.append(maxOptLen - opt.size(), ' ');
6541 2368 : osRet += arg->GetDescription();
6542 :
6543 2368 : const auto &choices = arg->GetChoices();
6544 2368 : if (!choices.empty())
6545 : {
6546 224 : osRet += ". ";
6547 224 : osRet += arg->GetMetaVar();
6548 224 : osRet += '=';
6549 224 : bool firstChoice = true;
6550 1725 : for (const auto &choice : choices)
6551 : {
6552 1501 : if (!firstChoice)
6553 1277 : osRet += '|';
6554 1501 : osRet += choice;
6555 1501 : firstChoice = false;
6556 : }
6557 : }
6558 :
6559 4670 : if (arg->GetType() == GAAT_DATASET ||
6560 2302 : arg->GetType() == GAAT_DATASET_LIST)
6561 : {
6562 144 : if (arg->IsOutput() &&
6563 144 : arg->GetDatasetInputFlags() == GADV_NAME &&
6564 9 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6565 : {
6566 9 : osRet += " (created by algorithm)";
6567 : }
6568 : }
6569 :
6570 2368 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6571 : {
6572 190 : osRet += " (default: ";
6573 190 : osRet += arg->GetDefault<std::string>();
6574 190 : osRet += ')';
6575 : }
6576 2178 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6577 : {
6578 66 : if (arg->GetDefault<bool>())
6579 0 : osRet += " (default: true)";
6580 : }
6581 2112 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6582 : {
6583 76 : osRet += " (default: ";
6584 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6585 76 : osRet += ')';
6586 : }
6587 2036 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6588 : {
6589 49 : osRet += " (default: ";
6590 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6591 49 : osRet += ')';
6592 : }
6593 2399 : else if (arg->GetType() == GAAT_STRING_LIST &&
6594 412 : arg->HasDefaultValue())
6595 : {
6596 : const auto &defaultVal =
6597 9 : arg->GetDefault<std::vector<std::string>>();
6598 9 : if (defaultVal.size() == 1)
6599 : {
6600 9 : osRet += " (default: ";
6601 9 : osRet += defaultVal[0];
6602 9 : osRet += ')';
6603 : }
6604 : }
6605 2005 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6606 27 : arg->HasDefaultValue())
6607 : {
6608 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6609 0 : if (defaultVal.size() == 1)
6610 : {
6611 0 : osRet += " (default: ";
6612 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6613 0 : osRet += ')';
6614 : }
6615 : }
6616 1978 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6617 : {
6618 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6619 0 : if (defaultVal.size() == 1)
6620 : {
6621 0 : osRet += " (default: ";
6622 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6623 0 : osRet += ')';
6624 : }
6625 : }
6626 :
6627 2368 : if (arg->GetDisplayHintAboutRepetition())
6628 : {
6629 2401 : if (arg->GetMinCount() > 0 &&
6630 92 : arg->GetMinCount() == arg->GetMaxCount())
6631 : {
6632 18 : if (arg->GetMinCount() != 1)
6633 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6634 : }
6635 2365 : else if (arg->GetMinCount() > 0 &&
6636 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6637 : {
6638 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6639 8 : arg->GetMaxCount());
6640 : }
6641 2283 : else if (arg->GetMinCount() > 0)
6642 : {
6643 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6644 : }
6645 2217 : else if (arg->GetMaxCount() > 1)
6646 : {
6647 401 : osRet += " [may be repeated]";
6648 : }
6649 : }
6650 :
6651 2368 : if (arg->IsRequired())
6652 : {
6653 169 : osRet += " [required]";
6654 : }
6655 :
6656 2622 : if (!arg->IsAvailableInPipelineStep() &&
6657 254 : !usageOptions.isPipelineStep)
6658 : {
6659 28 : osRet += " [not available in pipelines]";
6660 : }
6661 :
6662 2368 : osRet += '\n';
6663 :
6664 2368 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6665 2368 : if (!mutualExclusionGroup.empty())
6666 : {
6667 410 : std::string otherArgs;
6668 3953 : for (const auto &otherArg : m_args)
6669 : {
6670 6840 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6671 3092 : otherArg.get() == arg)
6672 861 : continue;
6673 2887 : if (otherArg->GetMutualExclusionGroup() ==
6674 : mutualExclusionGroup)
6675 : {
6676 278 : if (!otherArgs.empty())
6677 77 : otherArgs += ", ";
6678 278 : otherArgs += "--";
6679 278 : otherArgs += otherArg->GetName();
6680 : }
6681 : }
6682 205 : if (!otherArgs.empty())
6683 : {
6684 201 : osRet += " ";
6685 201 : osRet += " ";
6686 201 : osRet.append(maxOptLen, ' ');
6687 201 : osRet += "Mutually exclusive with ";
6688 201 : osRet += otherArgs;
6689 201 : osRet += '\n';
6690 : }
6691 : }
6692 :
6693 : // Check dependency
6694 4736 : std::string dependencyArgs;
6695 :
6696 31 : for (const auto &dependencyArgumentName :
6697 2430 : GetArgDependencies(arg->GetName()))
6698 : {
6699 31 : const auto otherArg{GetArg(dependencyArgumentName)};
6700 31 : if (otherArg != nullptr)
6701 : {
6702 31 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6703 : otherArg == arg)
6704 : {
6705 0 : continue;
6706 : }
6707 :
6708 31 : if (!dependencyArgs.empty())
6709 : {
6710 3 : dependencyArgs += ", ";
6711 : }
6712 :
6713 31 : dependencyArgs += "--";
6714 31 : dependencyArgs += otherArg->GetName();
6715 : }
6716 : else
6717 : {
6718 0 : CPLError(CE_Warning, CPLE_AppDefined,
6719 : "Argument '%s' depends on unknown argument '%s'",
6720 0 : arg->GetName().c_str(),
6721 : dependencyArgumentName.c_str());
6722 : }
6723 : }
6724 :
6725 2368 : if (!dependencyArgs.empty())
6726 : {
6727 28 : osRet += " ";
6728 28 : osRet += " ";
6729 28 : osRet.append(maxOptLen, ' ');
6730 28 : osRet += "Depends on ";
6731 28 : osRet += dependencyArgs;
6732 28 : osRet += '\n';
6733 : }
6734 2368 : };
6735 :
6736 382 : if (!m_positionalArgs.empty())
6737 : {
6738 150 : osRet += "\nPositional arguments:\n";
6739 1613 : for (const auto &[arg, opt] : options)
6740 : {
6741 1463 : if (arg->IsPositional())
6742 141 : OutputArg(arg, opt);
6743 : }
6744 : }
6745 :
6746 382 : if (hasNonPositionals)
6747 : {
6748 382 : bool hasCommon = false;
6749 382 : bool hasBase = false;
6750 382 : bool hasAdvanced = false;
6751 382 : bool hasEsoteric = false;
6752 764 : std::vector<std::string> categories;
6753 3721 : for (const auto &iter : options)
6754 : {
6755 3339 : const auto &arg = iter.first;
6756 3339 : if (!arg->IsPositional())
6757 : {
6758 3198 : const auto &category = arg->GetCategory();
6759 3198 : if (category == GAAC_COMMON)
6760 : {
6761 1189 : hasCommon = true;
6762 : }
6763 2009 : else if (category == GAAC_BASE)
6764 : {
6765 1769 : hasBase = true;
6766 : }
6767 240 : else if (category == GAAC_ADVANCED)
6768 : {
6769 178 : hasAdvanced = true;
6770 : }
6771 62 : else if (category == GAAC_ESOTERIC)
6772 : {
6773 29 : hasEsoteric = true;
6774 : }
6775 33 : else if (std::find(categories.begin(), categories.end(),
6776 33 : category) == categories.end())
6777 : {
6778 9 : categories.push_back(category);
6779 : }
6780 : }
6781 : }
6782 382 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6783 67 : categories.insert(categories.begin(), GAAC_ADVANCED);
6784 382 : if (hasBase)
6785 336 : categories.insert(categories.begin(), GAAC_BASE);
6786 382 : if (hasCommon && !usageOptions.isPipelineStep)
6787 68 : categories.insert(categories.begin(), GAAC_COMMON);
6788 382 : if (hasEsoteric)
6789 11 : categories.push_back(GAAC_ESOTERIC);
6790 :
6791 873 : for (const auto &category : categories)
6792 : {
6793 491 : osRet += "\n";
6794 491 : if (category != GAAC_BASE)
6795 : {
6796 155 : osRet += category;
6797 155 : osRet += ' ';
6798 : }
6799 491 : osRet += "Options:\n";
6800 5344 : for (const auto &[arg, opt] : options)
6801 : {
6802 4853 : if (!arg->IsPositional() && arg->GetCategory() == category)
6803 2227 : OutputArg(arg, opt);
6804 : }
6805 491 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6806 : {
6807 2 : osRet += " ";
6808 2 : osRet += userProvidedOpt;
6809 2 : osRet += " ";
6810 2 : if (userProvidedOpt.size() < maxOptLen)
6811 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6812 2 : osRet += "Argument provided by user";
6813 2 : osRet += '\n';
6814 : }
6815 : }
6816 : }
6817 : }
6818 :
6819 384 : if (!m_longDescription.empty())
6820 : {
6821 6 : osRet += '\n';
6822 6 : osRet += m_longDescription;
6823 6 : osRet += '\n';
6824 : }
6825 :
6826 384 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6827 : {
6828 371 : if (!m_helpURL.empty())
6829 : {
6830 371 : osRet += "\nFor more details, consult ";
6831 371 : osRet += GetHelpFullURL();
6832 371 : osRet += '\n';
6833 : }
6834 371 : osRet += GetUsageForCLIEnd();
6835 : }
6836 :
6837 384 : return osRet;
6838 : }
6839 :
6840 : /************************************************************************/
6841 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6842 : /************************************************************************/
6843 :
6844 : //! @cond Doxygen_Suppress
6845 378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6846 : {
6847 378 : std::string osRet;
6848 :
6849 378 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6850 : {
6851 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6852 : "alternative interface to GDAL and OGR command line "
6853 : "utilities.\nThe project reserves the right to modify, "
6854 : "rename, reorganize, and change the behavior of the utility\n"
6855 : "until it is officially frozen in a future feature release of "
6856 13 : "GDAL.\n";
6857 : }
6858 378 : return osRet;
6859 : }
6860 :
6861 : //! @endcond
6862 :
6863 : /************************************************************************/
6864 : /* GDALAlgorithm::GetUsageAsJSON() */
6865 : /************************************************************************/
6866 :
6867 563 : std::string GDALAlgorithm::GetUsageAsJSON() const
6868 : {
6869 1126 : CPLJSONDocument oDoc;
6870 1126 : auto oRoot = oDoc.GetRoot();
6871 :
6872 563 : if (m_displayInJSONUsage)
6873 : {
6874 561 : oRoot.Add("name", m_name);
6875 561 : CPLJSONArray jFullPath;
6876 1168 : for (const std::string &s : m_callPath)
6877 : {
6878 607 : jFullPath.Add(s);
6879 : }
6880 561 : oRoot.Add("full_path", jFullPath);
6881 : }
6882 :
6883 563 : oRoot.Add("description", m_description);
6884 563 : if (!m_helpURL.empty())
6885 : {
6886 562 : oRoot.Add("short_url", m_helpURL);
6887 562 : oRoot.Add("url", GetHelpFullURL());
6888 : }
6889 :
6890 1126 : CPLJSONArray jSubAlgorithms;
6891 763 : for (const auto &subAlgName : GetSubAlgorithmNames())
6892 : {
6893 400 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6894 200 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
6895 : {
6896 198 : CPLJSONDocument oSubDoc;
6897 198 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
6898 198 : jSubAlgorithms.Add(oSubDoc.GetRoot());
6899 : }
6900 : }
6901 563 : oRoot.Add("sub_algorithms", jSubAlgorithms);
6902 :
6903 563 : if (m_arbitraryLongNameArgsAllowed)
6904 : {
6905 1 : oRoot.Add("user_provided_arguments_allowed", true);
6906 : }
6907 :
6908 10828 : const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
6909 : {
6910 5414 : CPLJSONObject jArg;
6911 5414 : jArg.Add("name", arg->GetName());
6912 5414 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
6913 5414 : jArg.Add("description", arg->GetDescription());
6914 :
6915 5414 : const auto &metaVar = arg->GetMetaVar();
6916 5414 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
6917 : {
6918 1671 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
6919 1671 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
6920 : std::string::npos)
6921 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
6922 : else
6923 888 : jArg.Add("metavar", metaVar);
6924 : }
6925 :
6926 5414 : if (!arg->IsAvailableInPipelineStep())
6927 : {
6928 1622 : jArg.Add("available_in_pipeline_step", false);
6929 : }
6930 :
6931 5414 : const auto &choices = arg->GetChoices();
6932 5414 : if (!choices.empty())
6933 : {
6934 408 : CPLJSONArray jChoices;
6935 3500 : for (const auto &choice : choices)
6936 3092 : jChoices.Add(choice);
6937 408 : jArg.Add("choices", jChoices);
6938 : }
6939 5414 : if (arg->HasDefaultValue())
6940 : {
6941 1188 : switch (arg->GetType())
6942 : {
6943 424 : case GAAT_BOOLEAN:
6944 424 : jArg.Add("default", arg->GetDefault<bool>());
6945 424 : break;
6946 366 : case GAAT_STRING:
6947 366 : jArg.Add("default", arg->GetDefault<std::string>());
6948 366 : break;
6949 200 : case GAAT_INTEGER:
6950 200 : jArg.Add("default", arg->GetDefault<int>());
6951 200 : break;
6952 178 : case GAAT_REAL:
6953 178 : jArg.Add("default", arg->GetDefault<double>());
6954 178 : break;
6955 18 : case GAAT_STRING_LIST:
6956 : {
6957 : const auto &val =
6958 18 : arg->GetDefault<std::vector<std::string>>();
6959 18 : if (val.size() == 1)
6960 : {
6961 17 : jArg.Add("default", val[0]);
6962 : }
6963 : else
6964 : {
6965 1 : CPLJSONArray jArr;
6966 3 : for (const auto &s : val)
6967 : {
6968 2 : jArr.Add(s);
6969 : }
6970 1 : jArg.Add("default", jArr);
6971 : }
6972 18 : break;
6973 : }
6974 1 : case GAAT_INTEGER_LIST:
6975 : {
6976 1 : const auto &val = arg->GetDefault<std::vector<int>>();
6977 1 : if (val.size() == 1)
6978 : {
6979 0 : jArg.Add("default", val[0]);
6980 : }
6981 : else
6982 : {
6983 1 : CPLJSONArray jArr;
6984 3 : for (int i : val)
6985 : {
6986 2 : jArr.Add(i);
6987 : }
6988 1 : jArg.Add("default", jArr);
6989 : }
6990 1 : break;
6991 : }
6992 1 : case GAAT_REAL_LIST:
6993 : {
6994 1 : const auto &val = arg->GetDefault<std::vector<double>>();
6995 1 : if (val.size() == 1)
6996 : {
6997 0 : jArg.Add("default", val[0]);
6998 : }
6999 : else
7000 : {
7001 1 : CPLJSONArray jArr;
7002 3 : for (double d : val)
7003 : {
7004 2 : jArr.Add(d);
7005 : }
7006 1 : jArg.Add("default", jArr);
7007 : }
7008 1 : break;
7009 : }
7010 0 : case GAAT_DATASET:
7011 : case GAAT_DATASET_LIST:
7012 0 : CPLError(CE_Warning, CPLE_AppDefined,
7013 : "Unhandled default value for arg %s",
7014 0 : arg->GetName().c_str());
7015 0 : break;
7016 : }
7017 : }
7018 :
7019 5414 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7020 5414 : if (!std::isnan(minVal))
7021 : {
7022 671 : if (arg->GetType() == GAAT_INTEGER ||
7023 261 : arg->GetType() == GAAT_INTEGER_LIST)
7024 173 : jArg.Add("min_value", static_cast<int>(minVal));
7025 : else
7026 237 : jArg.Add("min_value", minVal);
7027 410 : jArg.Add("min_value_is_included", minValIsIncluded);
7028 : }
7029 :
7030 5414 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7031 5414 : if (!std::isnan(maxVal))
7032 : {
7033 199 : if (arg->GetType() == GAAT_INTEGER ||
7034 82 : arg->GetType() == GAAT_INTEGER_LIST)
7035 35 : jArg.Add("max_value", static_cast<int>(maxVal));
7036 : else
7037 82 : jArg.Add("max_value", maxVal);
7038 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
7039 : }
7040 :
7041 5414 : jArg.Add("required", arg->IsRequired());
7042 5414 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7043 : {
7044 1519 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7045 1519 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7046 1519 : jArg.Add("min_count", arg->GetMinCount());
7047 1519 : jArg.Add("max_count", arg->GetMaxCount());
7048 : }
7049 :
7050 : // Process dependencies
7051 5414 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7052 5414 : if (!mutualDependencyGroup.empty())
7053 : {
7054 32 : jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7055 : }
7056 :
7057 10828 : CPLJSONArray jDependencies;
7058 48 : for (const auto &dependencyArgumentName :
7059 5510 : GetArgDependencies(arg->GetName()))
7060 : {
7061 48 : jDependencies.Add(dependencyArgumentName);
7062 : }
7063 :
7064 5414 : if (jDependencies.Size() > 0)
7065 : {
7066 48 : jArg.Add("depends_on", jDependencies);
7067 : }
7068 :
7069 5414 : jArg.Add("category", arg->GetCategory());
7070 :
7071 10578 : if (arg->GetType() == GAAT_DATASET ||
7072 5164 : arg->GetType() == GAAT_DATASET_LIST)
7073 : {
7074 : {
7075 453 : CPLJSONArray jAr;
7076 453 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
7077 311 : jAr.Add("raster");
7078 453 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7079 179 : jAr.Add("vector");
7080 453 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7081 28 : jAr.Add("multidim_raster");
7082 453 : jArg.Add("dataset_type", jAr);
7083 : }
7084 :
7085 620 : const auto GetFlags = [](int flags)
7086 : {
7087 620 : CPLJSONArray jAr;
7088 620 : if (flags & GADV_NAME)
7089 453 : jAr.Add("name");
7090 620 : if (flags & GADV_OBJECT)
7091 571 : jAr.Add("dataset");
7092 620 : return jAr;
7093 : };
7094 :
7095 453 : if (arg->IsInput())
7096 : {
7097 453 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7098 : }
7099 453 : if (arg->IsOutput())
7100 : {
7101 167 : jArg.Add("output_flags",
7102 334 : GetFlags(arg->GetDatasetOutputFlags()));
7103 : }
7104 : }
7105 :
7106 5414 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7107 5414 : if (!mutualExclusionGroup.empty())
7108 : {
7109 659 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7110 : }
7111 :
7112 10828 : const auto &metadata = arg->GetMetadata();
7113 5414 : if (!metadata.empty())
7114 : {
7115 450 : CPLJSONObject jMetadata;
7116 939 : for (const auto &[key, values] : metadata)
7117 : {
7118 978 : CPLJSONArray jValue;
7119 1183 : for (const auto &value : values)
7120 694 : jValue.Add(value);
7121 489 : jMetadata.Add(key, jValue);
7122 : }
7123 450 : jArg.Add("metadata", jMetadata);
7124 : }
7125 :
7126 10828 : return jArg;
7127 563 : };
7128 :
7129 : {
7130 563 : CPLJSONArray jArgs;
7131 8913 : for (const auto &arg : m_args)
7132 : {
7133 8350 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7134 5190 : jArgs.Add(ProcessArg(arg.get()));
7135 : }
7136 563 : oRoot.Add("input_arguments", jArgs);
7137 : }
7138 :
7139 : {
7140 563 : CPLJSONArray jArgs;
7141 8913 : for (const auto &arg : m_args)
7142 : {
7143 8350 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7144 57 : jArgs.Add(ProcessArg(arg.get()));
7145 : }
7146 563 : oRoot.Add("output_arguments", jArgs);
7147 : }
7148 :
7149 : {
7150 563 : CPLJSONArray jArgs;
7151 8913 : for (const auto &arg : m_args)
7152 : {
7153 8350 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7154 167 : jArgs.Add(ProcessArg(arg.get()));
7155 : }
7156 563 : oRoot.Add("input_output_arguments", jArgs);
7157 : }
7158 :
7159 563 : if (m_supportsStreamedOutput)
7160 : {
7161 117 : oRoot.Add("supports_streamed_output", true);
7162 : }
7163 :
7164 1126 : return oDoc.SaveAsString();
7165 : }
7166 :
7167 : /************************************************************************/
7168 : /* GDALAlgorithm::GetAutoComplete() */
7169 : /************************************************************************/
7170 :
7171 : std::vector<std::string>
7172 246 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7173 : bool lastWordIsComplete, bool showAllOptions)
7174 : {
7175 492 : std::vector<std::string> ret;
7176 :
7177 : // Get inner-most algorithm
7178 246 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
7179 246 : GDALAlgorithm *curAlg = this;
7180 487 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7181 : {
7182 : auto subAlg = curAlg->InstantiateSubAlgorithm(
7183 350 : args.front(), /* suggestionAllowed = */ false);
7184 350 : if (!subAlg)
7185 108 : break;
7186 242 : if (args.size() == 1 && !lastWordIsComplete)
7187 : {
7188 5 : int nCount = 0;
7189 115 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7190 : {
7191 110 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7192 6 : nCount++;
7193 : }
7194 5 : if (nCount >= 2)
7195 : {
7196 11 : for (const std::string &subAlgName :
7197 23 : curAlg->GetSubAlgorithmNames())
7198 : {
7199 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7200 11 : if (subAlg && !subAlg->IsHidden())
7201 11 : ret.push_back(subAlg->GetName());
7202 : }
7203 1 : return ret;
7204 : }
7205 : }
7206 241 : showAllOptions = false;
7207 241 : args.erase(args.begin());
7208 241 : curAlgHolder = std::move(subAlg);
7209 241 : curAlg = curAlgHolder.get();
7210 : }
7211 245 : if (curAlg != this)
7212 : {
7213 131 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7214 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
7215 131 : /* showAllOptions = */ false);
7216 : }
7217 :
7218 228 : std::string option;
7219 228 : std::string value;
7220 114 : ExtractLastOptionAndValue(args, option, value);
7221 :
7222 141 : if (option.empty() && !args.empty() && !args.back().empty() &&
7223 27 : args.back()[0] == '-')
7224 : {
7225 24 : const auto &lastArg = args.back();
7226 : // List available options
7227 349 : for (const auto &arg : GetArgs())
7228 : {
7229 603 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7230 551 : (!showAllOptions &&
7231 750 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7232 454 : arg->GetName() == "version" ||
7233 227 : arg->GetName() == "json-usage")))
7234 : {
7235 116 : continue;
7236 : }
7237 209 : if (!arg->GetShortName().empty())
7238 : {
7239 126 : std::string str = std::string("-").append(arg->GetShortName());
7240 42 : if (lastArg == str)
7241 0 : ret.push_back(std::move(str));
7242 : }
7243 209 : if (lastArg != "-" && lastArg != "--")
7244 : {
7245 54 : for (const std::string &alias : arg->GetAliases())
7246 : {
7247 48 : std::string str = std::string("--").append(alias);
7248 16 : if (cpl::starts_with(str, lastArg))
7249 3 : ret.push_back(std::move(str));
7250 : }
7251 : }
7252 209 : if (!arg->GetName().empty())
7253 : {
7254 627 : std::string str = std::string("--").append(arg->GetName());
7255 209 : if (cpl::starts_with(str, lastArg))
7256 173 : ret.push_back(std::move(str));
7257 : }
7258 : }
7259 24 : std::sort(ret.begin(), ret.end());
7260 : }
7261 90 : else if (!option.empty())
7262 : {
7263 : // List possible choices for current option
7264 84 : auto arg = GetArg(option);
7265 84 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7266 : {
7267 84 : ret = arg->GetChoices();
7268 84 : if (ret.empty())
7269 : {
7270 : {
7271 79 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7272 79 : SetParseForAutoCompletion();
7273 79 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7274 : }
7275 79 : ret = arg->GetAutoCompleteChoices(value);
7276 : }
7277 : else
7278 : {
7279 5 : std::sort(ret.begin(), ret.end());
7280 : }
7281 84 : if (!ret.empty() && ret.back() == value)
7282 : {
7283 2 : ret.clear();
7284 : }
7285 82 : else if (ret.empty())
7286 : {
7287 10 : ret.push_back("**");
7288 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7289 20 : ret.push_back(std::string("\xC2\xA0"
7290 : "description: ")
7291 10 : .append(arg->GetDescription()));
7292 : }
7293 : }
7294 : }
7295 : else
7296 : {
7297 : // List possible sub-algorithms
7298 59 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7299 : {
7300 106 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7301 53 : if (subAlg && !subAlg->IsHidden())
7302 53 : ret.push_back(subAlg->GetName());
7303 : }
7304 6 : if (!ret.empty())
7305 : {
7306 2 : std::sort(ret.begin(), ret.end());
7307 : }
7308 :
7309 : // Try filenames
7310 6 : if (ret.empty() && !args.empty())
7311 : {
7312 : {
7313 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7314 3 : SetParseForAutoCompletion();
7315 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7316 : }
7317 :
7318 3 : const std::string &lastArg = args.back();
7319 3 : GDALAlgorithmArg *arg = nullptr;
7320 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7321 21 : "like", "source", "destination"})
7322 : {
7323 18 : if (!arg)
7324 : {
7325 3 : auto newArg = GetArg(name);
7326 3 : if (newArg)
7327 : {
7328 3 : if (!newArg->IsExplicitlySet())
7329 : {
7330 0 : arg = newArg;
7331 : }
7332 6 : else if (newArg->GetType() == GAAT_STRING ||
7333 5 : newArg->GetType() == GAAT_STRING_LIST ||
7334 8 : newArg->GetType() == GAAT_DATASET ||
7335 2 : newArg->GetType() == GAAT_DATASET_LIST)
7336 : {
7337 : VSIStatBufL sStat;
7338 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7339 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7340 : {
7341 3 : arg = newArg;
7342 : }
7343 : }
7344 : }
7345 : }
7346 : }
7347 3 : if (arg)
7348 : {
7349 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7350 : }
7351 : }
7352 : }
7353 :
7354 114 : return ret;
7355 : }
7356 :
7357 : /************************************************************************/
7358 : /* GDALAlgorithm::GetFieldIndices() */
7359 : /************************************************************************/
7360 :
7361 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7362 : OGRLayerH hLayer, std::vector<int> &indices)
7363 : {
7364 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7365 :
7366 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7367 :
7368 44 : if (names.size() == 1 && names[0] == "ALL")
7369 : {
7370 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7371 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7372 : {
7373 16 : indices.push_back(i);
7374 : }
7375 : }
7376 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7377 : {
7378 6 : std::set<int> fieldsAdded;
7379 14 : for (const std::string &osFieldName : names)
7380 : {
7381 :
7382 : const int nIdx =
7383 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7384 :
7385 10 : if (nIdx < 0)
7386 : {
7387 2 : CPLError(CE_Failure, CPLE_AppDefined,
7388 : "Field '%s' does not exist in layer '%s'",
7389 2 : osFieldName.c_str(), layer.GetName());
7390 2 : return false;
7391 : }
7392 :
7393 8 : if (fieldsAdded.insert(nIdx).second)
7394 : {
7395 7 : indices.push_back(nIdx);
7396 : }
7397 : }
7398 : }
7399 :
7400 42 : return true;
7401 : }
7402 :
7403 : /************************************************************************/
7404 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7405 : /************************************************************************/
7406 :
7407 114 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7408 : std::string &option,
7409 : std::string &value) const
7410 : {
7411 114 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7412 : {
7413 84 : const auto nPosEqual = args.back().find('=');
7414 84 : if (nPosEqual == std::string::npos)
7415 : {
7416 : // Deal with "gdal ... --option"
7417 65 : if (GetArg(args.back()))
7418 : {
7419 41 : option = args.back();
7420 41 : args.pop_back();
7421 : }
7422 : }
7423 : else
7424 : {
7425 : // Deal with "gdal ... --option=<value>"
7426 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7427 : {
7428 19 : option = args.back().substr(0, nPosEqual);
7429 19 : value = args.back().substr(nPosEqual + 1);
7430 19 : args.pop_back();
7431 : }
7432 : }
7433 : }
7434 55 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7435 25 : args[args.size() - 2][0] == '-')
7436 : {
7437 : // Deal with "gdal ... --option <value>"
7438 24 : auto arg = GetArg(args[args.size() - 2]);
7439 24 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7440 : {
7441 24 : option = args[args.size() - 2];
7442 24 : value = args.back();
7443 24 : args.pop_back();
7444 : }
7445 : }
7446 :
7447 114 : const auto IsKeyValueOption = [](const std::string &osStr)
7448 : {
7449 308 : return osStr == "--co" || osStr == "--creation-option" ||
7450 285 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7451 306 : osStr == "--oo" || osStr == "--open-option";
7452 : };
7453 :
7454 114 : if (IsKeyValueOption(option))
7455 : {
7456 22 : const auto nPosEqual = value.find('=');
7457 22 : if (nPosEqual != std::string::npos)
7458 : {
7459 11 : value.resize(nPosEqual);
7460 : }
7461 : }
7462 114 : }
7463 :
7464 : //! @cond Doxygen_Suppress
7465 :
7466 : /************************************************************************/
7467 : /* GDALContainerAlgorithm::RunImpl() */
7468 : /************************************************************************/
7469 :
7470 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7471 : {
7472 0 : return false;
7473 : }
7474 :
7475 : //! @endcond
7476 :
7477 : /************************************************************************/
7478 : /* GDALAlgorithmRelease() */
7479 : /************************************************************************/
7480 :
7481 : /** Release a handle to an algorithm.
7482 : *
7483 : * @since 3.11
7484 : */
7485 12337 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7486 : {
7487 12337 : delete hAlg;
7488 12337 : }
7489 :
7490 : /************************************************************************/
7491 : /* GDALAlgorithmGetName() */
7492 : /************************************************************************/
7493 :
7494 : /** Return the algorithm name.
7495 : *
7496 : * @param hAlg Handle to an algorithm. Must NOT be null.
7497 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7498 : * be freed.
7499 : * @since 3.11
7500 : */
7501 5699 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7502 : {
7503 5699 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7504 5699 : return hAlg->ptr->GetName().c_str();
7505 : }
7506 :
7507 : /************************************************************************/
7508 : /* GDALAlgorithmGetDescription() */
7509 : /************************************************************************/
7510 :
7511 : /** Return the algorithm (short) description.
7512 : *
7513 : * @param hAlg Handle to an algorithm. Must NOT be null.
7514 : * @return algorithm description whose lifetime is bound to hAlg and which must
7515 : * not be freed.
7516 : * @since 3.11
7517 : */
7518 5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7519 : {
7520 5619 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7521 5619 : return hAlg->ptr->GetDescription().c_str();
7522 : }
7523 :
7524 : /************************************************************************/
7525 : /* GDALAlgorithmGetLongDescription() */
7526 : /************************************************************************/
7527 :
7528 : /** Return the algorithm (longer) description.
7529 : *
7530 : * @param hAlg Handle to an algorithm. Must NOT be null.
7531 : * @return algorithm description whose lifetime is bound to hAlg and which must
7532 : * not be freed.
7533 : * @since 3.11
7534 : */
7535 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7536 : {
7537 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7538 2 : return hAlg->ptr->GetLongDescription().c_str();
7539 : }
7540 :
7541 : /************************************************************************/
7542 : /* GDALAlgorithmGetHelpFullURL() */
7543 : /************************************************************************/
7544 :
7545 : /** Return the algorithm full URL.
7546 : *
7547 : * @param hAlg Handle to an algorithm. Must NOT be null.
7548 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7549 : * not be freed.
7550 : * @since 3.11
7551 : */
7552 4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7553 : {
7554 4963 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7555 4963 : return hAlg->ptr->GetHelpFullURL().c_str();
7556 : }
7557 :
7558 : /************************************************************************/
7559 : /* GDALAlgorithmHasSubAlgorithms() */
7560 : /************************************************************************/
7561 :
7562 : /** Return whether the algorithm has sub-algorithms.
7563 : *
7564 : * @param hAlg Handle to an algorithm. Must NOT be null.
7565 : * @since 3.11
7566 : */
7567 9105 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7568 : {
7569 9105 : VALIDATE_POINTER1(hAlg, __func__, false);
7570 9105 : return hAlg->ptr->HasSubAlgorithms();
7571 : }
7572 :
7573 : /************************************************************************/
7574 : /* GDALAlgorithmGetSubAlgorithmNames() */
7575 : /************************************************************************/
7576 :
7577 : /** Get the names of registered algorithms.
7578 : *
7579 : * @param hAlg Handle to an algorithm. Must NOT be null.
7580 : * @return a NULL terminated list of names, which must be destroyed with
7581 : * CSLDestroy()
7582 : * @since 3.11
7583 : */
7584 707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7585 : {
7586 707 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7587 707 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7588 : }
7589 :
7590 : /************************************************************************/
7591 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7592 : /************************************************************************/
7593 :
7594 : /** Instantiate an algorithm by its name (or its alias).
7595 : *
7596 : * @param hAlg Handle to an algorithm. Must NOT be null.
7597 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7598 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7599 : * or NULL if the algorithm does not exist or another error occurred.
7600 : * @since 3.11
7601 : */
7602 8568 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7603 : const char *pszSubAlgName)
7604 : {
7605 8568 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7606 8568 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7607 17136 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7608 : return subAlg
7609 17136 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7610 17136 : : nullptr;
7611 : }
7612 :
7613 : /************************************************************************/
7614 : /* GDALAlgorithmParseCommandLineArguments() */
7615 : /************************************************************************/
7616 :
7617 : /** Parse a command line argument, which does not include the algorithm
7618 : * name, to set the value of corresponding arguments.
7619 : *
7620 : * @param hAlg Handle to an algorithm. Must NOT be null.
7621 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7622 : * @return true if successful, false otherwise
7623 : * @since 3.11
7624 : */
7625 :
7626 352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7627 : CSLConstList papszArgs)
7628 : {
7629 352 : VALIDATE_POINTER1(hAlg, __func__, false);
7630 352 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7631 : }
7632 :
7633 : /************************************************************************/
7634 : /* GDALAlgorithmGetActualAlgorithm() */
7635 : /************************************************************************/
7636 :
7637 : /** Return the actual algorithm that is going to be invoked, when the
7638 : * current algorithm has sub-algorithms.
7639 : *
7640 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7641 : *
7642 : * Note that the lifetime of the returned algorithm does not exceed the one of
7643 : * the hAlg instance that owns it.
7644 : *
7645 : * @param hAlg Handle to an algorithm. Must NOT be null.
7646 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7647 : * @since 3.11
7648 : */
7649 914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7650 : {
7651 914 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7652 914 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7653 : }
7654 :
7655 : /************************************************************************/
7656 : /* GDALAlgorithmRun() */
7657 : /************************************************************************/
7658 :
7659 : /** Execute the algorithm, starting with ValidateArguments() and then
7660 : * calling RunImpl().
7661 : *
7662 : * This function must be called at most once per instance.
7663 : *
7664 : * @param hAlg Handle to an algorithm. Must NOT be null.
7665 : * @param pfnProgress Progress callback. May be null.
7666 : * @param pProgressData Progress callback user data. May be null.
7667 : * @return true if successful, false otherwise
7668 : * @since 3.11
7669 : */
7670 :
7671 2678 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7672 : void *pProgressData)
7673 : {
7674 2678 : VALIDATE_POINTER1(hAlg, __func__, false);
7675 2678 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7676 : }
7677 :
7678 : /************************************************************************/
7679 : /* GDALAlgorithmFinalize() */
7680 : /************************************************************************/
7681 :
7682 : /** Complete any pending actions, and return the final status.
7683 : * This is typically useful for algorithm that generate an output dataset.
7684 : *
7685 : * Note that this function does *NOT* release memory associated with the
7686 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7687 : *
7688 : * @param hAlg Handle to an algorithm. Must NOT be null.
7689 : * @return true if successful, false otherwise
7690 : * @since 3.11
7691 : */
7692 :
7693 891 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7694 : {
7695 891 : VALIDATE_POINTER1(hAlg, __func__, false);
7696 891 : return hAlg->ptr->Finalize();
7697 : }
7698 :
7699 : /************************************************************************/
7700 : /* GDALAlgorithmGetUsageAsJSON() */
7701 : /************************************************************************/
7702 :
7703 : /** Return the usage of the algorithm as a JSON-serialized string.
7704 : *
7705 : * This can be used to dynamically generate interfaces to algorithms.
7706 : *
7707 : * @param hAlg Handle to an algorithm. Must NOT be null.
7708 : * @return a string that must be freed with CPLFree()
7709 : * @since 3.11
7710 : */
7711 6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7712 : {
7713 6 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7714 6 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7715 : }
7716 :
7717 : /************************************************************************/
7718 : /* GDALAlgorithmGetArgNames() */
7719 : /************************************************************************/
7720 :
7721 : /** Return the list of available argument names.
7722 : *
7723 : * @param hAlg Handle to an algorithm. Must NOT be null.
7724 : * @return a NULL terminated list of names, which must be destroyed with
7725 : * CSLDestroy()
7726 : * @since 3.11
7727 : */
7728 15428 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7729 : {
7730 15428 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7731 30856 : CPLStringList list;
7732 343849 : for (const auto &arg : hAlg->ptr->GetArgs())
7733 328421 : list.AddString(arg->GetName().c_str());
7734 15428 : return list.StealList();
7735 : }
7736 :
7737 : /************************************************************************/
7738 : /* GDALAlgorithmGetArg() */
7739 : /************************************************************************/
7740 :
7741 : /** Return an argument from its name.
7742 : *
7743 : * The lifetime of the returned object does not exceed the one of hAlg.
7744 : *
7745 : * @param hAlg Handle to an algorithm. Must NOT be null.
7746 : * @param pszArgName Argument name. Must NOT be null.
7747 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7748 : * or nullptr in case of error
7749 : * @since 3.11
7750 : */
7751 329331 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7752 : const char *pszArgName)
7753 : {
7754 329331 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7755 329331 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7756 658662 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7757 329331 : /* isConst = */ true);
7758 329331 : if (!arg)
7759 3 : return nullptr;
7760 329328 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7761 : }
7762 :
7763 : /************************************************************************/
7764 : /* GDALAlgorithmGetArgNonConst() */
7765 : /************************************************************************/
7766 :
7767 : /** Return an argument from its name, possibly allowing creation of user-provided
7768 : * argument if the algorithm allow it.
7769 : *
7770 : * The lifetime of the returned object does not exceed the one of hAlg.
7771 : *
7772 : * @param hAlg Handle to an algorithm. Must NOT be null.
7773 : * @param pszArgName Argument name. Must NOT be null.
7774 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7775 : * or nullptr in case of error
7776 : * @since 3.12
7777 : */
7778 9647 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7779 : const char *pszArgName)
7780 : {
7781 9647 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7782 9647 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7783 19294 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7784 9647 : /* isConst = */ false);
7785 9647 : if (!arg)
7786 2 : return nullptr;
7787 9645 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7788 : }
7789 :
7790 : /************************************************************************/
7791 : /* GDALAlgorithmGetArgDependencies() */
7792 : /************************************************************************/
7793 :
7794 : /** Return the list of argument names the specified argument depends on.
7795 : *
7796 : * This includes both regular dependencies and mutual dependencies.
7797 : *
7798 : * @param hAlg Handle to an algorithm. Must NOT be null.
7799 : * @param pszArgName Argument name. Must NOT be null.
7800 : * @return a NULL terminated list of names, which must be destroyed with
7801 : * CSLDestroy()
7802 : * @since 3.11
7803 : */
7804 7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
7805 : const char *pszArgName)
7806 : {
7807 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7808 7 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7809 7 : return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
7810 : }
7811 :
7812 : /************************************************************************/
7813 : /* GDALAlgorithmArgRelease() */
7814 : /************************************************************************/
7815 :
7816 : /** Release a handle to an argument.
7817 : *
7818 : * @since 3.11
7819 : */
7820 338973 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7821 : {
7822 338973 : delete hArg;
7823 338973 : }
7824 :
7825 : /************************************************************************/
7826 : /* GDALAlgorithmArgGetName() */
7827 : /************************************************************************/
7828 :
7829 : /** Return the name of an argument.
7830 : *
7831 : * @param hArg Handle to an argument. Must NOT be null.
7832 : * @return argument name whose lifetime is bound to hArg and which must not
7833 : * be freed.
7834 : * @since 3.11
7835 : */
7836 18824 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7837 : {
7838 18824 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7839 18824 : return hArg->ptr->GetName().c_str();
7840 : }
7841 :
7842 : /************************************************************************/
7843 : /* GDALAlgorithmArgGetType() */
7844 : /************************************************************************/
7845 :
7846 : /** Get the type of an argument
7847 : *
7848 : * @param hArg Handle to an argument. Must NOT be null.
7849 : * @since 3.11
7850 : */
7851 419148 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
7852 : {
7853 419148 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
7854 419148 : return hArg->ptr->GetType();
7855 : }
7856 :
7857 : /************************************************************************/
7858 : /* GDALAlgorithmArgGetDescription() */
7859 : /************************************************************************/
7860 :
7861 : /** Return the description of an argument.
7862 : *
7863 : * @param hArg Handle to an argument. Must NOT be null.
7864 : * @return argument description whose lifetime is bound to hArg and which must not
7865 : * be freed.
7866 : * @since 3.11
7867 : */
7868 83600 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
7869 : {
7870 83600 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7871 83600 : return hArg->ptr->GetDescription().c_str();
7872 : }
7873 :
7874 : /************************************************************************/
7875 : /* GDALAlgorithmArgGetShortName() */
7876 : /************************************************************************/
7877 :
7878 : /** Return the short name, or empty string if there is none
7879 : *
7880 : * @param hArg Handle to an argument. Must NOT be null.
7881 : * @return short name whose lifetime is bound to hArg and which must not
7882 : * be freed.
7883 : * @since 3.11
7884 : */
7885 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
7886 : {
7887 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7888 1 : return hArg->ptr->GetShortName().c_str();
7889 : }
7890 :
7891 : /************************************************************************/
7892 : /* GDALAlgorithmArgGetAliases() */
7893 : /************************************************************************/
7894 :
7895 : /** Return the aliases (potentially none)
7896 : *
7897 : * @param hArg Handle to an argument. Must NOT be null.
7898 : * @return a NULL terminated list of names, which must be destroyed with
7899 : * CSLDestroy()
7900 :
7901 : * @since 3.11
7902 : */
7903 158179 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
7904 : {
7905 158179 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7906 158179 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
7907 : }
7908 :
7909 : /************************************************************************/
7910 : /* GDALAlgorithmArgGetMetaVar() */
7911 : /************************************************************************/
7912 :
7913 : /** Return the "meta-var" hint.
7914 : *
7915 : * By default, the meta-var value is the long name of the argument in
7916 : * upper case.
7917 : *
7918 : * @param hArg Handle to an argument. Must NOT be null.
7919 : * @return meta-var hint whose lifetime is bound to hArg and which must not
7920 : * be freed.
7921 : * @since 3.11
7922 : */
7923 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
7924 : {
7925 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7926 1 : return hArg->ptr->GetMetaVar().c_str();
7927 : }
7928 :
7929 : /************************************************************************/
7930 : /* GDALAlgorithmArgGetCategory() */
7931 : /************************************************************************/
7932 :
7933 : /** Return the argument category
7934 : *
7935 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
7936 : *
7937 : * @param hArg Handle to an argument. Must NOT be null.
7938 : * @return category whose lifetime is bound to hArg and which must not
7939 : * be freed.
7940 : * @since 3.11
7941 : */
7942 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
7943 : {
7944 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7945 1 : return hArg->ptr->GetCategory().c_str();
7946 : }
7947 :
7948 : /************************************************************************/
7949 : /* GDALAlgorithmArgIsPositional() */
7950 : /************************************************************************/
7951 :
7952 : /** Return if the argument is a positional one.
7953 : *
7954 : * @param hArg Handle to an argument. Must NOT be null.
7955 : * @since 3.11
7956 : */
7957 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
7958 : {
7959 1 : VALIDATE_POINTER1(hArg, __func__, false);
7960 1 : return hArg->ptr->IsPositional();
7961 : }
7962 :
7963 : /************************************************************************/
7964 : /* GDALAlgorithmArgIsRequired() */
7965 : /************************************************************************/
7966 :
7967 : /** Return whether the argument is required. Defaults to false.
7968 : *
7969 : * @param hArg Handle to an argument. Must NOT be null.
7970 : * @since 3.11
7971 : */
7972 158179 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
7973 : {
7974 158179 : VALIDATE_POINTER1(hArg, __func__, false);
7975 158179 : return hArg->ptr->IsRequired();
7976 : }
7977 :
7978 : /************************************************************************/
7979 : /* GDALAlgorithmArgGetMinCount() */
7980 : /************************************************************************/
7981 :
7982 : /** Return the minimum number of values for the argument.
7983 : *
7984 : * Defaults to 0.
7985 : * Only applies to list type of arguments.
7986 : *
7987 : * @param hArg Handle to an argument. Must NOT be null.
7988 : * @since 3.11
7989 : */
7990 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
7991 : {
7992 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7993 1 : return hArg->ptr->GetMinCount();
7994 : }
7995 :
7996 : /************************************************************************/
7997 : /* GDALAlgorithmArgGetMaxCount() */
7998 : /************************************************************************/
7999 :
8000 : /** Return the maximum number of values for the argument.
8001 : *
8002 : * Defaults to 1 for scalar types, and INT_MAX for list types.
8003 : * Only applies to list type of arguments.
8004 : *
8005 : * @param hArg Handle to an argument. Must NOT be null.
8006 : * @since 3.11
8007 : */
8008 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8009 : {
8010 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8011 1 : return hArg->ptr->GetMaxCount();
8012 : }
8013 :
8014 : /************************************************************************/
8015 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
8016 : /************************************************************************/
8017 :
8018 : /** Return whether, for list type of arguments, several values, space
8019 : * separated, may be specified. That is "--foo=bar,baz".
8020 : * The default is true.
8021 : *
8022 : * @param hArg Handle to an argument. Must NOT be null.
8023 : * @since 3.11
8024 : */
8025 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8026 : {
8027 1 : VALIDATE_POINTER1(hArg, __func__, false);
8028 1 : return hArg->ptr->GetPackedValuesAllowed();
8029 : }
8030 :
8031 : /************************************************************************/
8032 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
8033 : /************************************************************************/
8034 :
8035 : /** Return whether, for list type of arguments, the argument may be
8036 : * repeated. That is "--foo=bar --foo=baz".
8037 : * The default is true.
8038 : *
8039 : * @param hArg Handle to an argument. Must NOT be null.
8040 : * @since 3.11
8041 : */
8042 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8043 : {
8044 1 : VALIDATE_POINTER1(hArg, __func__, false);
8045 1 : return hArg->ptr->GetRepeatedArgAllowed();
8046 : }
8047 :
8048 : /************************************************************************/
8049 : /* GDALAlgorithmArgGetChoices() */
8050 : /************************************************************************/
8051 :
8052 : /** Return the allowed values (as strings) for the argument.
8053 : *
8054 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8055 : *
8056 : * @param hArg Handle to an argument. Must NOT be null.
8057 : * @return a NULL terminated list of names, which must be destroyed with
8058 : * CSLDestroy()
8059 :
8060 : * @since 3.11
8061 : */
8062 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8063 : {
8064 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8065 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
8066 : }
8067 :
8068 : /************************************************************************/
8069 : /* GDALAlgorithmArgGetMetadataItem() */
8070 : /************************************************************************/
8071 :
8072 : /** Return the values of the metadata item of an argument.
8073 : *
8074 : * @param hArg Handle to an argument. Must NOT be null.
8075 : * @param pszItem Name of the item. Must NOT be null.
8076 : * @return a NULL terminated list of values, which must be destroyed with
8077 : * CSLDestroy()
8078 :
8079 : * @since 3.11
8080 : */
8081 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8082 : const char *pszItem)
8083 : {
8084 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8085 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
8086 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8087 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8088 : }
8089 :
8090 : /************************************************************************/
8091 : /* GDALAlgorithmArgIsExplicitlySet() */
8092 : /************************************************************************/
8093 :
8094 : /** Return whether the argument value has been explicitly set with Set()
8095 : *
8096 : * @param hArg Handle to an argument. Must NOT be null.
8097 : * @since 3.11
8098 : */
8099 626 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8100 : {
8101 626 : VALIDATE_POINTER1(hArg, __func__, false);
8102 626 : return hArg->ptr->IsExplicitlySet();
8103 : }
8104 :
8105 : /************************************************************************/
8106 : /* GDALAlgorithmArgHasDefaultValue() */
8107 : /************************************************************************/
8108 :
8109 : /** Return if the argument has a declared default value.
8110 : *
8111 : * @param hArg Handle to an argument. Must NOT be null.
8112 : * @since 3.11
8113 : */
8114 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8115 : {
8116 2 : VALIDATE_POINTER1(hArg, __func__, false);
8117 2 : return hArg->ptr->HasDefaultValue();
8118 : }
8119 :
8120 : /************************************************************************/
8121 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
8122 : /************************************************************************/
8123 :
8124 : /** Return the argument default value as a integer.
8125 : *
8126 : * Must only be called on arguments whose type is GAAT_BOOLEAN
8127 : *
8128 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8129 : * argument has a default value.
8130 : *
8131 : * @param hArg Handle to an argument. Must NOT be null.
8132 : * @since 3.12
8133 : */
8134 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8135 : {
8136 3 : VALIDATE_POINTER1(hArg, __func__, false);
8137 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8138 : {
8139 1 : CPLError(CE_Failure, CPLE_AppDefined,
8140 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8141 : __func__);
8142 1 : return false;
8143 : }
8144 2 : return hArg->ptr->GetDefault<bool>();
8145 : }
8146 :
8147 : /************************************************************************/
8148 : /* GDALAlgorithmArgGetDefaultAsString() */
8149 : /************************************************************************/
8150 :
8151 : /** Return the argument default value as a string.
8152 : *
8153 : * Must only be called on arguments whose type is GAAT_STRING.
8154 : *
8155 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8156 : * argument has a default value.
8157 : *
8158 : * @param hArg Handle to an argument. Must NOT be null.
8159 : * @return string whose lifetime is bound to hArg and which must not
8160 : * be freed.
8161 : * @since 3.11
8162 : */
8163 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8164 : {
8165 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8166 3 : if (hArg->ptr->GetType() != GAAT_STRING)
8167 : {
8168 2 : CPLError(CE_Failure, CPLE_AppDefined,
8169 : "%s must only be called on arguments of type GAAT_STRING",
8170 : __func__);
8171 2 : return nullptr;
8172 : }
8173 1 : return hArg->ptr->GetDefault<std::string>().c_str();
8174 : }
8175 :
8176 : /************************************************************************/
8177 : /* GDALAlgorithmArgGetDefaultAsInteger() */
8178 : /************************************************************************/
8179 :
8180 : /** Return the argument default value as a integer.
8181 : *
8182 : * Must only be called on arguments whose type is GAAT_INTEGER
8183 : *
8184 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8185 : * argument has a default value.
8186 : *
8187 : * @param hArg Handle to an argument. Must NOT be null.
8188 : * @since 3.12
8189 : */
8190 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8191 : {
8192 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8193 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8194 : {
8195 2 : CPLError(CE_Failure, CPLE_AppDefined,
8196 : "%s must only be called on arguments of type GAAT_INTEGER",
8197 : __func__);
8198 2 : return 0;
8199 : }
8200 1 : return hArg->ptr->GetDefault<int>();
8201 : }
8202 :
8203 : /************************************************************************/
8204 : /* GDALAlgorithmArgGetDefaultAsDouble() */
8205 : /************************************************************************/
8206 :
8207 : /** Return the argument default value as a double.
8208 : *
8209 : * Must only be called on arguments whose type is GAAT_REAL
8210 : *
8211 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8212 : * argument has a default value.
8213 : *
8214 : * @param hArg Handle to an argument. Must NOT be null.
8215 : * @since 3.12
8216 : */
8217 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8218 : {
8219 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8220 3 : if (hArg->ptr->GetType() != GAAT_REAL)
8221 : {
8222 2 : CPLError(CE_Failure, CPLE_AppDefined,
8223 : "%s must only be called on arguments of type GAAT_REAL",
8224 : __func__);
8225 2 : return 0;
8226 : }
8227 1 : return hArg->ptr->GetDefault<double>();
8228 : }
8229 :
8230 : /************************************************************************/
8231 : /* GDALAlgorithmArgGetDefaultAsStringList() */
8232 : /************************************************************************/
8233 :
8234 : /** Return the argument default value as a string list.
8235 : *
8236 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8237 : *
8238 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8239 : * argument has a default value.
8240 : *
8241 : * @param hArg Handle to an argument. Must NOT be null.
8242 : * @return a NULL terminated list of names, which must be destroyed with
8243 : * CSLDestroy()
8244 :
8245 : * @since 3.12
8246 : */
8247 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8248 : {
8249 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8250 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8251 : {
8252 2 : CPLError(CE_Failure, CPLE_AppDefined,
8253 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8254 : __func__);
8255 2 : return nullptr;
8256 : }
8257 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8258 1 : .StealList();
8259 : }
8260 :
8261 : /************************************************************************/
8262 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8263 : /************************************************************************/
8264 :
8265 : /** Return the argument default value as a integer list.
8266 : *
8267 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8268 : *
8269 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8270 : * argument has a default value.
8271 : *
8272 : * @param hArg Handle to an argument. Must NOT be null.
8273 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8274 : * @since 3.12
8275 : */
8276 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8277 : size_t *pnCount)
8278 : {
8279 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8280 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8281 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8282 : {
8283 2 : CPLError(
8284 : CE_Failure, CPLE_AppDefined,
8285 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8286 : __func__);
8287 2 : *pnCount = 0;
8288 2 : return nullptr;
8289 : }
8290 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8291 1 : *pnCount = val.size();
8292 1 : return val.data();
8293 : }
8294 :
8295 : /************************************************************************/
8296 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8297 : /************************************************************************/
8298 :
8299 : /** Return the argument default value as a real list.
8300 : *
8301 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8302 : *
8303 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8304 : * argument has a default value.
8305 : *
8306 : * @param hArg Handle to an argument. Must NOT be null.
8307 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8308 : * @since 3.12
8309 : */
8310 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8311 : size_t *pnCount)
8312 : {
8313 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8314 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8315 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8316 : {
8317 2 : CPLError(CE_Failure, CPLE_AppDefined,
8318 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8319 : __func__);
8320 2 : *pnCount = 0;
8321 2 : return nullptr;
8322 : }
8323 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8324 1 : *pnCount = val.size();
8325 1 : return val.data();
8326 : }
8327 :
8328 : /************************************************************************/
8329 : /* GDALAlgorithmArgIsHidden() */
8330 : /************************************************************************/
8331 :
8332 : /** Return whether the argument is hidden (for GDAL internal use)
8333 : *
8334 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8335 : * GDALAlgorithmArgIsHiddenForAPI().
8336 : *
8337 : * @param hArg Handle to an argument. Must NOT be null.
8338 : * @since 3.12
8339 : */
8340 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8341 : {
8342 1 : VALIDATE_POINTER1(hArg, __func__, false);
8343 1 : return hArg->ptr->IsHidden();
8344 : }
8345 :
8346 : /************************************************************************/
8347 : /* GDALAlgorithmArgIsHiddenForCLI() */
8348 : /************************************************************************/
8349 :
8350 : /** Return whether the argument must not be mentioned in CLI usage.
8351 : *
8352 : * For example, "output-value" for "gdal raster info", which is only
8353 : * meant when the algorithm is used from a non-CLI context.
8354 : *
8355 : * @param hArg Handle to an argument. Must NOT be null.
8356 : * @since 3.11
8357 : */
8358 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8359 : {
8360 1 : VALIDATE_POINTER1(hArg, __func__, false);
8361 1 : return hArg->ptr->IsHiddenForCLI();
8362 : }
8363 :
8364 : /************************************************************************/
8365 : /* GDALAlgorithmArgIsHiddenForAPI() */
8366 : /************************************************************************/
8367 :
8368 : /** Return whether the argument must not be mentioned in the context of an
8369 : * API use.
8370 : * Said otherwise, if it is only for CLI usage.
8371 : *
8372 : * For example "--help"
8373 : *
8374 : * @param hArg Handle to an argument. Must NOT be null.
8375 : * @since 3.12
8376 : */
8377 213775 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8378 : {
8379 213775 : VALIDATE_POINTER1(hArg, __func__, false);
8380 213775 : return hArg->ptr->IsHiddenForAPI();
8381 : }
8382 :
8383 : /************************************************************************/
8384 : /* GDALAlgorithmArgIsOnlyForCLI() */
8385 : /************************************************************************/
8386 :
8387 : /** Return whether the argument must not be mentioned in the context of an
8388 : * API use.
8389 : * Said otherwise, if it is only for CLI usage.
8390 : *
8391 : * For example "--help"
8392 : *
8393 : * @param hArg Handle to an argument. Must NOT be null.
8394 : * @since 3.11
8395 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8396 : */
8397 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8398 : {
8399 0 : VALIDATE_POINTER1(hArg, __func__, false);
8400 0 : return hArg->ptr->IsHiddenForAPI();
8401 : }
8402 :
8403 : /************************************************************************/
8404 : /* GDALAlgorithmArgIsAvailableInPipelineStep() */
8405 : /************************************************************************/
8406 :
8407 : /** Return whether the argument is available in a pipeline step.
8408 : *
8409 : * If false, it is only available in standalone mode.
8410 : *
8411 : * @param hArg Handle to an argument. Must NOT be null.
8412 : * @since 3.13
8413 : */
8414 2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8415 : {
8416 2 : VALIDATE_POINTER1(hArg, __func__, false);
8417 2 : return hArg->ptr->IsAvailableInPipelineStep();
8418 : }
8419 :
8420 : /************************************************************************/
8421 : /* GDALAlgorithmArgIsInput() */
8422 : /************************************************************************/
8423 :
8424 : /** Indicate whether the value of the argument is read-only during the
8425 : * execution of the algorithm.
8426 : *
8427 : * Default is true.
8428 : *
8429 : * @param hArg Handle to an argument. Must NOT be null.
8430 : * @since 3.11
8431 : */
8432 210905 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8433 : {
8434 210905 : VALIDATE_POINTER1(hArg, __func__, false);
8435 210905 : return hArg->ptr->IsInput();
8436 : }
8437 :
8438 : /************************************************************************/
8439 : /* GDALAlgorithmArgIsOutput() */
8440 : /************************************************************************/
8441 :
8442 : /** Return whether (at least part of) the value of the argument is set
8443 : * during the execution of the algorithm.
8444 : *
8445 : * For example, "output-value" for "gdal raster info"
8446 : * Default is false.
8447 : * An argument may return both IsInput() and IsOutput() as true.
8448 : * For example the "gdal raster convert" algorithm consumes the dataset
8449 : * name of its "output" argument, and sets the dataset object during its
8450 : * execution.
8451 : *
8452 : * @param hArg Handle to an argument. Must NOT be null.
8453 : * @since 3.11
8454 : */
8455 117492 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8456 : {
8457 117492 : VALIDATE_POINTER1(hArg, __func__, false);
8458 117492 : return hArg->ptr->IsOutput();
8459 : }
8460 :
8461 : /************************************************************************/
8462 : /* GDALAlgorithmArgGetDatasetType() */
8463 : /************************************************************************/
8464 :
8465 : /** Get which type of dataset is allowed / generated.
8466 : *
8467 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8468 : * GDAL_OF_MULTIDIM_RASTER.
8469 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8470 : *
8471 : * @param hArg Handle to an argument. Must NOT be null.
8472 : * @since 3.11
8473 : */
8474 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8475 : {
8476 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8477 2 : return hArg->ptr->GetDatasetType();
8478 : }
8479 :
8480 : /************************************************************************/
8481 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8482 : /************************************************************************/
8483 :
8484 : /** Indicates which components among name and dataset are accepted as
8485 : * input, when this argument serves as an input.
8486 : *
8487 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8488 : * input.
8489 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8490 : * accepted as input.
8491 : * If both bits are set, the algorithm can accept either a name or a dataset
8492 : * object.
8493 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8494 : *
8495 : * @param hArg Handle to an argument. Must NOT be null.
8496 : * @return string whose lifetime is bound to hAlg and which must not
8497 : * be freed.
8498 : * @since 3.11
8499 : */
8500 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8501 : {
8502 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8503 2 : return hArg->ptr->GetDatasetInputFlags();
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8508 : /************************************************************************/
8509 :
8510 : /** Indicates which components among name and dataset are modified,
8511 : * when this argument serves as an output.
8512 : *
8513 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8514 : * output (that is the algorithm will generate the name. Rarely used).
8515 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8516 : * generated as output, and available for use after the algorithm has
8517 : * completed.
8518 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8519 : *
8520 : * @param hArg Handle to an argument. Must NOT be null.
8521 : * @return string whose lifetime is bound to hAlg and which must not
8522 : * be freed.
8523 : * @since 3.11
8524 : */
8525 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8526 : {
8527 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8528 2 : return hArg->ptr->GetDatasetOutputFlags();
8529 : }
8530 :
8531 : /************************************************************************/
8532 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8533 : /************************************************************************/
8534 :
8535 : /** Return the name of the mutual exclusion group to which this argument
8536 : * belongs to.
8537 : *
8538 : * Or empty string if it does not belong to any exclusion group.
8539 : *
8540 : * @param hArg Handle to an argument. Must NOT be null.
8541 : * @return string whose lifetime is bound to hArg and which must not
8542 : * be freed.
8543 : * @since 3.11
8544 : */
8545 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8546 : {
8547 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8548 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8549 : }
8550 :
8551 : /************************************************************************/
8552 : /* GDALAlgorithmArgGetMutualDependencyGroup() */
8553 : /************************************************************************/
8554 :
8555 : /** Return the name of the mutual dependency group to which this argument
8556 : * belongs to.
8557 : *
8558 : * Or empty string if it does not belong to any dependency group.
8559 : *
8560 : * @param hArg Handle to an argument. Must NOT be null.
8561 : * @return string whose lifetime is bound to hArg and which must not
8562 : * be freed.
8563 : * @since 3.13
8564 : */
8565 5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8566 : {
8567 5 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8568 5 : return hArg->ptr->GetMutualDependencyGroup().c_str();
8569 : }
8570 :
8571 : /************************************************************************/
8572 : /* GDALAlgorithmArgGetDirectDependencies() */
8573 : /************************************************************************/
8574 :
8575 : /** Return the list of names of arguments that this argument depends on.
8576 : *
8577 : * This is not necessarily a symmetric relationship.
8578 : * If argument A depends on argument B, it doesn't mean that B depends on A.
8579 : * Mutual dependency groups are a special case of dependencies,
8580 : * where all arguments of the group depend on each other and are not
8581 : * returned by this method.
8582 : *
8583 : * @param hArg Handle to an argument. Must NOT be null.
8584 : * @return a NULL terminated list of names, which must be destroyed with
8585 : * CSLDestroy()
8586 : * @since 3.13
8587 : */
8588 7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8589 : {
8590 7 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8591 7 : return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8592 : }
8593 :
8594 : /************************************************************************/
8595 : /* GDALAlgorithmArgGetAsBoolean() */
8596 : /************************************************************************/
8597 :
8598 : /** Return the argument value as a boolean.
8599 : *
8600 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8601 : *
8602 : * @param hArg Handle to an argument. Must NOT be null.
8603 : * @since 3.11
8604 : */
8605 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8606 : {
8607 8 : VALIDATE_POINTER1(hArg, __func__, false);
8608 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8609 : {
8610 1 : CPLError(CE_Failure, CPLE_AppDefined,
8611 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8612 : __func__);
8613 1 : return false;
8614 : }
8615 7 : return hArg->ptr->Get<bool>();
8616 : }
8617 :
8618 : /************************************************************************/
8619 : /* GDALAlgorithmArgGetAsString() */
8620 : /************************************************************************/
8621 :
8622 : /** Return the argument value as a string.
8623 : *
8624 : * Must only be called on arguments whose type is GAAT_STRING.
8625 : *
8626 : * @param hArg Handle to an argument. Must NOT be null.
8627 : * @return string whose lifetime is bound to hArg and which must not
8628 : * be freed.
8629 : * @since 3.11
8630 : */
8631 354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8632 : {
8633 354 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8634 354 : if (hArg->ptr->GetType() != GAAT_STRING)
8635 : {
8636 1 : CPLError(CE_Failure, CPLE_AppDefined,
8637 : "%s must only be called on arguments of type GAAT_STRING",
8638 : __func__);
8639 1 : return nullptr;
8640 : }
8641 353 : return hArg->ptr->Get<std::string>().c_str();
8642 : }
8643 :
8644 : /************************************************************************/
8645 : /* GDALAlgorithmArgGetAsDatasetValue() */
8646 : /************************************************************************/
8647 :
8648 : /** Return the argument value as a GDALArgDatasetValueH.
8649 : *
8650 : * Must only be called on arguments whose type is GAAT_DATASET
8651 : *
8652 : * @param hArg Handle to an argument. Must NOT be null.
8653 : * @return handle to a GDALArgDatasetValue that must be released with
8654 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8655 : * the one of hArg.
8656 : * @since 3.11
8657 : */
8658 3167 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8659 : {
8660 3167 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8661 3167 : if (hArg->ptr->GetType() != GAAT_DATASET)
8662 : {
8663 1 : CPLError(CE_Failure, CPLE_AppDefined,
8664 : "%s must only be called on arguments of type GAAT_DATASET",
8665 : __func__);
8666 1 : return nullptr;
8667 : }
8668 3166 : return std::make_unique<GDALArgDatasetValueHS>(
8669 6332 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8670 3166 : .release();
8671 : }
8672 :
8673 : /************************************************************************/
8674 : /* GDALAlgorithmArgGetAsInteger() */
8675 : /************************************************************************/
8676 :
8677 : /** Return the argument value as a integer.
8678 : *
8679 : * Must only be called on arguments whose type is GAAT_INTEGER
8680 : *
8681 : * @param hArg Handle to an argument. Must NOT be null.
8682 : * @since 3.11
8683 : */
8684 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8685 : {
8686 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8687 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8688 : {
8689 1 : CPLError(CE_Failure, CPLE_AppDefined,
8690 : "%s must only be called on arguments of type GAAT_INTEGER",
8691 : __func__);
8692 1 : return 0;
8693 : }
8694 25 : return hArg->ptr->Get<int>();
8695 : }
8696 :
8697 : /************************************************************************/
8698 : /* GDALAlgorithmArgGetAsDouble() */
8699 : /************************************************************************/
8700 :
8701 : /** Return the argument value as a double.
8702 : *
8703 : * Must only be called on arguments whose type is GAAT_REAL
8704 : *
8705 : * @param hArg Handle to an argument. Must NOT be null.
8706 : * @since 3.11
8707 : */
8708 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8709 : {
8710 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8711 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8712 : {
8713 1 : CPLError(CE_Failure, CPLE_AppDefined,
8714 : "%s must only be called on arguments of type GAAT_REAL",
8715 : __func__);
8716 1 : return 0;
8717 : }
8718 7 : return hArg->ptr->Get<double>();
8719 : }
8720 :
8721 : /************************************************************************/
8722 : /* GDALAlgorithmArgGetAsStringList() */
8723 : /************************************************************************/
8724 :
8725 : /** Return the argument value as a string list.
8726 : *
8727 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8728 : *
8729 : * @param hArg Handle to an argument. Must NOT be null.
8730 : * @return a NULL terminated list of names, which must be destroyed with
8731 : * CSLDestroy()
8732 :
8733 : * @since 3.11
8734 : */
8735 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8736 : {
8737 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8738 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8739 : {
8740 1 : CPLError(CE_Failure, CPLE_AppDefined,
8741 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8742 : __func__);
8743 1 : return nullptr;
8744 : }
8745 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8746 3 : .StealList();
8747 : }
8748 :
8749 : /************************************************************************/
8750 : /* GDALAlgorithmArgGetAsIntegerList() */
8751 : /************************************************************************/
8752 :
8753 : /** Return the argument value as a integer list.
8754 : *
8755 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8756 : *
8757 : * @param hArg Handle to an argument. Must NOT be null.
8758 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8759 : * @since 3.11
8760 : */
8761 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8762 : size_t *pnCount)
8763 : {
8764 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8765 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8766 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8767 : {
8768 1 : CPLError(
8769 : CE_Failure, CPLE_AppDefined,
8770 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8771 : __func__);
8772 1 : *pnCount = 0;
8773 1 : return nullptr;
8774 : }
8775 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8776 7 : *pnCount = val.size();
8777 7 : return val.data();
8778 : }
8779 :
8780 : /************************************************************************/
8781 : /* GDALAlgorithmArgGetAsDoubleList() */
8782 : /************************************************************************/
8783 :
8784 : /** Return the argument value as a real list.
8785 : *
8786 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8787 : *
8788 : * @param hArg Handle to an argument. Must NOT be null.
8789 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8790 : * @since 3.11
8791 : */
8792 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8793 : size_t *pnCount)
8794 : {
8795 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8796 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8797 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8798 : {
8799 1 : CPLError(CE_Failure, CPLE_AppDefined,
8800 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8801 : __func__);
8802 1 : *pnCount = 0;
8803 1 : return nullptr;
8804 : }
8805 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8806 7 : *pnCount = val.size();
8807 7 : return val.data();
8808 : }
8809 :
8810 : /************************************************************************/
8811 : /* GDALAlgorithmArgSetAsBoolean() */
8812 : /************************************************************************/
8813 :
8814 : /** Set the value for a GAAT_BOOLEAN argument.
8815 : *
8816 : * It cannot be called several times for a given argument.
8817 : * Validation checks and other actions are run.
8818 : *
8819 : * @param hArg Handle to an argument. Must NOT be null.
8820 : * @param value value.
8821 : * @return true if success.
8822 : * @since 3.11
8823 : */
8824 :
8825 694 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8826 : {
8827 694 : VALIDATE_POINTER1(hArg, __func__, false);
8828 694 : return hArg->ptr->Set(value);
8829 : }
8830 :
8831 : /************************************************************************/
8832 : /* GDALAlgorithmArgSetAsString() */
8833 : /************************************************************************/
8834 :
8835 : /** Set the value for a GAAT_STRING argument.
8836 : *
8837 : * It cannot be called several times for a given argument.
8838 : * Validation checks and other actions are run.
8839 : *
8840 : * @param hArg Handle to an argument. Must NOT be null.
8841 : * @param value value (may be null)
8842 : * @return true if success.
8843 : * @since 3.11
8844 : */
8845 :
8846 3100 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
8847 : {
8848 3100 : VALIDATE_POINTER1(hArg, __func__, false);
8849 3100 : return hArg->ptr->Set(value ? value : "");
8850 : }
8851 :
8852 : /************************************************************************/
8853 : /* GDALAlgorithmArgSetAsInteger() */
8854 : /************************************************************************/
8855 :
8856 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
8857 : *
8858 : * It cannot be called several times for a given argument.
8859 : * Validation checks and other actions are run.
8860 : *
8861 : * @param hArg Handle to an argument. Must NOT be null.
8862 : * @param value value.
8863 : * @return true if success.
8864 : * @since 3.11
8865 : */
8866 :
8867 472 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8868 : {
8869 472 : VALIDATE_POINTER1(hArg, __func__, false);
8870 472 : return hArg->ptr->Set(value);
8871 : }
8872 :
8873 : /************************************************************************/
8874 : /* GDALAlgorithmArgSetAsDouble() */
8875 : /************************************************************************/
8876 :
8877 : /** Set the value for a GAAT_REAL argument.
8878 : *
8879 : * It cannot be called several times for a given argument.
8880 : * Validation checks and other actions are run.
8881 : *
8882 : * @param hArg Handle to an argument. Must NOT be null.
8883 : * @param value value.
8884 : * @return true if success.
8885 : * @since 3.11
8886 : */
8887 :
8888 242 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
8889 : {
8890 242 : VALIDATE_POINTER1(hArg, __func__, false);
8891 242 : return hArg->ptr->Set(value);
8892 : }
8893 :
8894 : /************************************************************************/
8895 : /* GDALAlgorithmArgSetAsDatasetValue() */
8896 : /************************************************************************/
8897 :
8898 : /** Set the value for a GAAT_DATASET argument.
8899 : *
8900 : * It cannot be called several times for a given argument.
8901 : * Validation checks and other actions are run.
8902 : *
8903 : * @param hArg Handle to an argument. Must NOT be null.
8904 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
8905 : * @return true if success.
8906 : * @since 3.11
8907 : */
8908 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
8909 : GDALArgDatasetValueH value)
8910 : {
8911 2 : VALIDATE_POINTER1(hArg, __func__, false);
8912 2 : VALIDATE_POINTER1(value, __func__, false);
8913 2 : return hArg->ptr->SetFrom(*(value->ptr));
8914 : }
8915 :
8916 : /************************************************************************/
8917 : /* GDALAlgorithmArgSetDataset() */
8918 : /************************************************************************/
8919 :
8920 : /** Set dataset object, increasing its reference counter.
8921 : *
8922 : * @param hArg Handle to an argument. Must NOT be null.
8923 : * @param hDS Dataset object. May be null.
8924 : * @return true if success.
8925 : * @since 3.11
8926 : */
8927 :
8928 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
8929 : {
8930 2 : VALIDATE_POINTER1(hArg, __func__, false);
8931 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
8932 : }
8933 :
8934 : /************************************************************************/
8935 : /* GDALAlgorithmArgSetAsStringList() */
8936 : /************************************************************************/
8937 :
8938 : /** Set the value for a GAAT_STRING_LIST argument.
8939 : *
8940 : * It cannot be called several times for a given argument.
8941 : * Validation checks and other actions are run.
8942 : *
8943 : * @param hArg Handle to an argument. Must NOT be null.
8944 : * @param value value as a NULL terminated list (may be null)
8945 : * @return true if success.
8946 : * @since 3.11
8947 : */
8948 :
8949 766 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
8950 : {
8951 766 : VALIDATE_POINTER1(hArg, __func__, false);
8952 766 : return hArg->ptr->Set(
8953 1532 : static_cast<std::vector<std::string>>(CPLStringList(value)));
8954 : }
8955 :
8956 : /************************************************************************/
8957 : /* GDALAlgorithmArgSetAsIntegerList() */
8958 : /************************************************************************/
8959 :
8960 : /** Set the value for a GAAT_INTEGER_LIST argument.
8961 : *
8962 : * It cannot be called several times for a given argument.
8963 : * Validation checks and other actions are run.
8964 : *
8965 : * @param hArg Handle to an argument. Must NOT be null.
8966 : * @param nCount Number of values in pnValues.
8967 : * @param pnValues Pointer to an array of integer values of size nCount.
8968 : * @return true if success.
8969 : * @since 3.11
8970 : */
8971 100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
8972 : const int *pnValues)
8973 : {
8974 100 : VALIDATE_POINTER1(hArg, __func__, false);
8975 100 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
8976 : }
8977 :
8978 : /************************************************************************/
8979 : /* GDALAlgorithmArgSetAsDoubleList() */
8980 : /************************************************************************/
8981 :
8982 : /** Set the value for a GAAT_REAL_LIST argument.
8983 : *
8984 : * It cannot be called several times for a given argument.
8985 : * Validation checks and other actions are run.
8986 : *
8987 : * @param hArg Handle to an argument. Must NOT be null.
8988 : * @param nCount Number of values in pnValues.
8989 : * @param pnValues Pointer to an array of double values of size nCount.
8990 : * @return true if success.
8991 : * @since 3.11
8992 : */
8993 152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
8994 : const double *pnValues)
8995 : {
8996 152 : VALIDATE_POINTER1(hArg, __func__, false);
8997 152 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
8998 : }
8999 :
9000 : /************************************************************************/
9001 : /* GDALAlgorithmArgSetDatasets() */
9002 : /************************************************************************/
9003 :
9004 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9005 : *
9006 : * @param hArg Handle to an argument. Must NOT be null.
9007 : * @param nCount Number of values in pnValues.
9008 : * @param pahDS Pointer to an array of dataset of size nCount.
9009 : * @return true if success.
9010 : * @since 3.11
9011 : */
9012 :
9013 1262 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9014 : GDALDatasetH *pahDS)
9015 : {
9016 1262 : VALIDATE_POINTER1(hArg, __func__, false);
9017 2524 : std::vector<GDALArgDatasetValue> values;
9018 2550 : for (size_t i = 0; i < nCount; ++i)
9019 : {
9020 1288 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9021 : }
9022 1262 : return hArg->ptr->Set(std::move(values));
9023 : }
9024 :
9025 : /************************************************************************/
9026 : /* GDALAlgorithmArgSetDatasetNames() */
9027 : /************************************************************************/
9028 :
9029 : /** Set dataset names to a GAAT_DATASET_LIST argument.
9030 : *
9031 : * @param hArg Handle to an argument. Must NOT be null.
9032 : * @param names Dataset names as a NULL terminated list (may be null)
9033 : * @return true if success.
9034 : * @since 3.11
9035 : */
9036 :
9037 734 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9038 : {
9039 734 : VALIDATE_POINTER1(hArg, __func__, false);
9040 1468 : std::vector<GDALArgDatasetValue> values;
9041 1539 : for (size_t i = 0; names[i]; ++i)
9042 : {
9043 805 : values.emplace_back(names[i]);
9044 : }
9045 734 : return hArg->ptr->Set(std::move(values));
9046 : }
9047 :
9048 : /************************************************************************/
9049 : /* GDALArgDatasetValueCreate() */
9050 : /************************************************************************/
9051 :
9052 : /** Instantiate an empty GDALArgDatasetValue
9053 : *
9054 : * @return new handle to free with GDALArgDatasetValueRelease()
9055 : * @since 3.11
9056 : */
9057 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
9058 : {
9059 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
9060 : }
9061 :
9062 : /************************************************************************/
9063 : /* GDALArgDatasetValueRelease() */
9064 : /************************************************************************/
9065 :
9066 : /** Release a handle to a GDALArgDatasetValue
9067 : *
9068 : * @since 3.11
9069 : */
9070 3167 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9071 : {
9072 3167 : delete hValue;
9073 3167 : }
9074 :
9075 : /************************************************************************/
9076 : /* GDALArgDatasetValueGetName() */
9077 : /************************************************************************/
9078 :
9079 : /** Return the name component of the GDALArgDatasetValue
9080 : *
9081 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9082 : * @return string whose lifetime is bound to hAlg and which must not
9083 : * be freed.
9084 : * @since 3.11
9085 : */
9086 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9087 : {
9088 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9089 1 : return hValue->ptr->GetName().c_str();
9090 : }
9091 :
9092 : /************************************************************************/
9093 : /* GDALArgDatasetValueGetDatasetRef() */
9094 : /************************************************************************/
9095 :
9096 : /** Return the dataset component of the GDALArgDatasetValue.
9097 : *
9098 : * This does not modify the reference counter, hence the lifetime of the
9099 : * returned object is not guaranteed to exceed the one of hValue.
9100 : *
9101 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9102 : * @since 3.11
9103 : */
9104 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9105 : {
9106 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9107 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9108 : }
9109 :
9110 : /************************************************************************/
9111 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
9112 : /************************************************************************/
9113 :
9114 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
9115 : * reference count if not null. Once done with the dataset, the caller should
9116 : * call GDALReleaseDataset().
9117 : *
9118 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9119 : * @since 3.11
9120 : */
9121 : GDALDatasetH
9122 1046 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9123 : {
9124 1046 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9125 1046 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9126 : }
9127 :
9128 : /************************************************************************/
9129 : /* GDALArgDatasetValueSetName() */
9130 : /************************************************************************/
9131 :
9132 : /** Set dataset name
9133 : *
9134 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9135 : * @param pszName Dataset name. May be null.
9136 : * @since 3.11
9137 : */
9138 :
9139 1350 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9140 : const char *pszName)
9141 : {
9142 1350 : VALIDATE_POINTER0(hValue, __func__);
9143 1350 : hValue->ptr->Set(pszName ? pszName : "");
9144 : }
9145 :
9146 : /************************************************************************/
9147 : /* GDALArgDatasetValueSetDataset() */
9148 : /************************************************************************/
9149 :
9150 : /** Set dataset object, increasing its reference counter.
9151 : *
9152 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9153 : * @param hDS Dataset object. May be null.
9154 : * @since 3.11
9155 : */
9156 :
9157 756 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9158 : GDALDatasetH hDS)
9159 : {
9160 756 : VALIDATE_POINTER0(hValue, __func__);
9161 756 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9162 : }
|