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 <string_view>
37 :
38 : #ifndef _
39 : #define _(x) (x)
40 : #endif
41 :
42 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
43 :
44 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
45 :
46 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
47 :
48 : //! @cond Doxygen_Suppress
49 : struct GDALAlgorithmArgHS
50 : {
51 : GDALAlgorithmArg *ptr = nullptr;
52 :
53 329763 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
54 : {
55 329763 : }
56 : };
57 :
58 : //! @endcond
59 :
60 : //! @cond Doxygen_Suppress
61 : struct GDALArgDatasetValueHS
62 : {
63 : GDALArgDatasetValue val{};
64 : GDALArgDatasetValue *ptr = nullptr;
65 :
66 1 : GDALArgDatasetValueHS() : ptr(&val)
67 : {
68 1 : }
69 :
70 2920 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
71 : {
72 2920 : }
73 :
74 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
75 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
76 : };
77 :
78 : //! @endcond
79 :
80 : /************************************************************************/
81 : /* GDALAlgorithmArgTypeIsList() */
82 : /************************************************************************/
83 :
84 387018 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
85 : {
86 387018 : switch (type)
87 : {
88 254554 : case GAAT_BOOLEAN:
89 : case GAAT_STRING:
90 : case GAAT_INTEGER:
91 : case GAAT_REAL:
92 : case GAAT_DATASET:
93 254554 : break;
94 :
95 132464 : case GAAT_STRING_LIST:
96 : case GAAT_INTEGER_LIST:
97 : case GAAT_REAL_LIST:
98 : case GAAT_DATASET_LIST:
99 132464 : return true;
100 : }
101 :
102 254554 : return false;
103 : }
104 :
105 : /************************************************************************/
106 : /* GDALAlgorithmArgTypeName() */
107 : /************************************************************************/
108 :
109 5240 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
110 : {
111 5240 : switch (type)
112 : {
113 1312 : case GAAT_BOOLEAN:
114 1312 : break;
115 1399 : case GAAT_STRING:
116 1399 : return "string";
117 370 : case GAAT_INTEGER:
118 370 : return "integer";
119 472 : case GAAT_REAL:
120 472 : return "real";
121 242 : case GAAT_DATASET:
122 242 : return "dataset";
123 938 : case GAAT_STRING_LIST:
124 938 : return "string_list";
125 92 : case GAAT_INTEGER_LIST:
126 92 : return "integer_list";
127 218 : case GAAT_REAL_LIST:
128 218 : return "real_list";
129 197 : case GAAT_DATASET_LIST:
130 197 : return "dataset_list";
131 : }
132 :
133 1312 : return "boolean";
134 : }
135 :
136 : /************************************************************************/
137 : /* GDALAlgorithmArgDatasetTypeName() */
138 : /************************************************************************/
139 :
140 21283 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
141 : {
142 21283 : std::string ret;
143 21283 : if ((type & GDAL_OF_RASTER) != 0)
144 11939 : ret = "raster";
145 21283 : if ((type & GDAL_OF_VECTOR) != 0)
146 : {
147 10131 : if (!ret.empty())
148 : {
149 1174 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
150 257 : ret += ", ";
151 : else
152 917 : ret += " or ";
153 : }
154 10131 : ret += "vector";
155 : }
156 21283 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
157 : {
158 570 : if (!ret.empty())
159 : {
160 316 : ret += " or ";
161 : }
162 570 : ret += "multidimensional raster";
163 : }
164 21283 : return ret;
165 : }
166 :
167 : /************************************************************************/
168 : /* GDALAlgorithmArgDecl() */
169 : /************************************************************************/
170 :
171 : // cppcheck-suppress uninitMemberVar
172 310490 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
173 : char chShortName,
174 : const std::string &description,
175 310490 : GDALAlgorithmArgType type)
176 : : m_longName(longName),
177 310490 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
178 : m_description(description), m_type(type),
179 620980 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
180 310490 : .toupper()),
181 931470 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
182 : {
183 310490 : if (m_type == GAAT_BOOLEAN)
184 : {
185 129258 : m_defaultValue = false;
186 : }
187 310490 : }
188 :
189 : /************************************************************************/
190 : /* GDALAlgorithmArgDecl::SetMinCount() */
191 : /************************************************************************/
192 :
193 17612 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
194 : {
195 17612 : if (!GDALAlgorithmArgTypeIsList(m_type))
196 : {
197 1 : CPLError(CE_Failure, CPLE_NotSupported,
198 : "SetMinCount() illegal on scalar argument '%s'",
199 1 : GetName().c_str());
200 : }
201 : else
202 : {
203 17611 : m_minCount = count;
204 : }
205 17612 : return *this;
206 : }
207 :
208 : /************************************************************************/
209 : /* GDALAlgorithmArgDecl::SetMaxCount() */
210 : /************************************************************************/
211 :
212 16649 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
213 : {
214 16649 : if (!GDALAlgorithmArgTypeIsList(m_type))
215 : {
216 1 : CPLError(CE_Failure, CPLE_NotSupported,
217 : "SetMaxCount() illegal on scalar argument '%s'",
218 1 : GetName().c_str());
219 : }
220 : else
221 : {
222 16648 : m_maxCount = count;
223 : }
224 16649 : return *this;
225 : }
226 :
227 : /************************************************************************/
228 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
229 : /************************************************************************/
230 :
231 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
232 :
233 : /************************************************************************/
234 : /* GDALAlgorithmArg::Set() */
235 : /************************************************************************/
236 :
237 1124 : bool GDALAlgorithmArg::Set(bool value)
238 : {
239 1124 : if (m_decl.GetType() != GAAT_BOOLEAN)
240 : {
241 14 : CPLError(
242 : CE_Failure, CPLE_AppDefined,
243 : "Calling Set(bool) on argument '%s' of type %s is not supported",
244 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
245 7 : return false;
246 : }
247 1117 : return SetInternal(value);
248 : }
249 :
250 3675 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
251 : {
252 3711 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
253 36 : value.front() == '@')
254 : {
255 2 : GByte *pabyData = nullptr;
256 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
257 2 : 10 * 1024 * 1024))
258 : {
259 : // Remove UTF-8 BOM
260 1 : size_t offset = 0;
261 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
262 1 : pabyData[2] == 0xBF)
263 : {
264 1 : offset = 3;
265 : }
266 1 : value = reinterpret_cast<const char *>(pabyData + offset);
267 1 : VSIFree(pabyData);
268 : }
269 : else
270 : {
271 1 : return false;
272 : }
273 : }
274 :
275 3674 : if (m_decl.IsRemoveSQLCommentsEnabled())
276 35 : value = CPLRemoveSQLComments(value);
277 :
278 3674 : return true;
279 : }
280 :
281 3696 : bool GDALAlgorithmArg::Set(const std::string &value)
282 : {
283 3696 : switch (m_decl.GetType())
284 : {
285 9 : case GAAT_BOOLEAN:
286 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
287 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
288 : {
289 4 : return Set(true);
290 : }
291 5 : else if (EQUAL(value.c_str(), "0") ||
292 4 : EQUAL(value.c_str(), "FALSE") ||
293 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
294 : {
295 4 : return Set(false);
296 : }
297 1 : break;
298 :
299 8 : case GAAT_INTEGER:
300 : case GAAT_INTEGER_LIST:
301 : {
302 8 : errno = 0;
303 8 : char *endptr = nullptr;
304 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
305 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
306 5 : endptr == value.c_str() + value.size())
307 : {
308 3 : if (m_decl.GetType() == GAAT_INTEGER)
309 3 : return Set(static_cast<int>(v));
310 : else
311 1 : return Set(std::vector<int>{static_cast<int>(v)});
312 : }
313 5 : break;
314 : }
315 :
316 5 : case GAAT_REAL:
317 : case GAAT_REAL_LIST:
318 : {
319 5 : char *endptr = nullptr;
320 5 : const double v = CPLStrtod(value.c_str(), &endptr);
321 5 : if (endptr == value.c_str() + value.size())
322 : {
323 3 : if (m_decl.GetType() == GAAT_REAL)
324 3 : return Set(v);
325 : else
326 1 : return Set(std::vector<double>{v});
327 : }
328 2 : break;
329 : }
330 :
331 3658 : case GAAT_STRING:
332 3658 : break;
333 :
334 1 : case GAAT_STRING_LIST:
335 2 : return Set(std::vector<std::string>{value});
336 :
337 15 : case GAAT_DATASET:
338 15 : return SetDatasetName(value);
339 :
340 0 : case GAAT_DATASET_LIST:
341 : {
342 0 : std::vector<GDALArgDatasetValue> v;
343 0 : v.resize(1);
344 0 : v[0].Set(value);
345 0 : return Set(std::move(v));
346 : }
347 : }
348 :
349 3666 : if (m_decl.GetType() != GAAT_STRING)
350 : {
351 16 : CPLError(CE_Failure, CPLE_AppDefined,
352 : "Calling Set(std::string) on argument '%s' of type %s is not "
353 : "supported",
354 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
355 8 : return false;
356 : }
357 :
358 3658 : std::string newValue(value);
359 3658 : return ProcessString(newValue) && SetInternal(newValue);
360 : }
361 :
362 840 : bool GDALAlgorithmArg::Set(int value)
363 : {
364 840 : if (m_decl.GetType() == GAAT_BOOLEAN)
365 : {
366 3 : if (value == 1)
367 1 : return Set(true);
368 2 : else if (value == 0)
369 1 : return Set(false);
370 : }
371 837 : else if (m_decl.GetType() == GAAT_REAL)
372 : {
373 3 : return Set(static_cast<double>(value));
374 : }
375 834 : else if (m_decl.GetType() == GAAT_STRING)
376 : {
377 2 : return Set(std::to_string(value));
378 : }
379 832 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
380 : {
381 1 : return Set(std::vector<int>{value});
382 : }
383 831 : else if (m_decl.GetType() == GAAT_REAL_LIST)
384 : {
385 1 : return Set(std::vector<double>{static_cast<double>(value)});
386 : }
387 830 : else if (m_decl.GetType() == GAAT_STRING_LIST)
388 : {
389 2 : return Set(std::vector<std::string>{std::to_string(value)});
390 : }
391 :
392 830 : if (m_decl.GetType() != GAAT_INTEGER)
393 : {
394 2 : CPLError(
395 : CE_Failure, CPLE_AppDefined,
396 : "Calling Set(int) on argument '%s' of type %s is not supported",
397 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
398 1 : return false;
399 : }
400 829 : return SetInternal(value);
401 : }
402 :
403 281 : bool GDALAlgorithmArg::Set(double value)
404 : {
405 284 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
406 284 : value <= INT_MAX && static_cast<int>(value) == value)
407 : {
408 2 : return Set(static_cast<int>(value));
409 : }
410 279 : else if (m_decl.GetType() == GAAT_STRING)
411 : {
412 2 : return Set(std::to_string(value));
413 : }
414 279 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
415 279 : value <= INT_MAX && static_cast<int>(value) == value)
416 : {
417 1 : return Set(std::vector<int>{static_cast<int>(value)});
418 : }
419 276 : else if (m_decl.GetType() == GAAT_REAL_LIST)
420 : {
421 0 : return Set(std::vector<double>{value});
422 : }
423 276 : else if (m_decl.GetType() == GAAT_STRING_LIST)
424 : {
425 2 : return Set(std::vector<std::string>{std::to_string(value)});
426 : }
427 275 : else if (m_decl.GetType() != GAAT_REAL)
428 : {
429 6 : CPLError(
430 : CE_Failure, CPLE_AppDefined,
431 : "Calling Set(double) on argument '%s' of type %s is not supported",
432 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
433 3 : return false;
434 : }
435 272 : return SetInternal(value);
436 : }
437 :
438 5720 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
439 : {
440 5723 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
441 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
442 : {
443 3 : CPLError(
444 : CE_Failure, CPLE_AppDefined,
445 : "Dataset object '%s' is created by algorithm and cannot be set "
446 : "as an input.",
447 3 : arg->GetName().c_str());
448 3 : return false;
449 : }
450 5717 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
451 : {
452 2 : CPLError(CE_Failure, CPLE_AppDefined,
453 : "A dataset cannot be set as an input argument of '%s'.",
454 2 : arg->GetName().c_str());
455 2 : return false;
456 : }
457 :
458 5715 : return true;
459 : }
460 :
461 9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
462 : {
463 9 : if (m_decl.GetType() != GAAT_DATASET)
464 : {
465 2 : CPLError(CE_Failure, CPLE_AppDefined,
466 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
467 : "is not supported",
468 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
469 1 : return false;
470 : }
471 8 : if (!CheckCanSetDatasetObject(this))
472 2 : return false;
473 6 : m_explicitlySet = true;
474 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
475 6 : val.Set(ds);
476 6 : return RunAllActions();
477 : }
478 :
479 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
480 : {
481 3 : if (m_decl.GetType() != GAAT_DATASET)
482 : {
483 2 : CPLError(CE_Failure, CPLE_AppDefined,
484 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
485 : "is not supported",
486 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
487 1 : return false;
488 : }
489 2 : if (!CheckCanSetDatasetObject(this))
490 1 : return false;
491 1 : m_explicitlySet = true;
492 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
493 1 : val.Set(std::move(ds));
494 1 : return RunAllActions();
495 : }
496 :
497 535 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
498 : {
499 535 : if (m_decl.GetType() != GAAT_DATASET)
500 : {
501 2 : CPLError(CE_Failure, CPLE_AppDefined,
502 : "Calling SetDatasetName() on argument '%s' of type %s is "
503 : "not supported",
504 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
505 1 : return false;
506 : }
507 534 : m_explicitlySet = true;
508 534 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
509 534 : return RunAllActions();
510 : }
511 :
512 927 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
513 : {
514 927 : if (m_decl.GetType() != GAAT_DATASET)
515 : {
516 2 : CPLError(CE_Failure, CPLE_AppDefined,
517 : "Calling SetFrom() on argument '%s' of type %s is "
518 : "not supported",
519 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
520 1 : return false;
521 : }
522 926 : if (!CheckCanSetDatasetObject(this))
523 1 : return false;
524 925 : m_explicitlySet = true;
525 925 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
526 925 : return RunAllActions();
527 : }
528 :
529 901 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
530 : {
531 901 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
532 : {
533 3 : std::vector<int> v_i;
534 4 : for (const std::string &s : value)
535 : {
536 3 : errno = 0;
537 3 : char *endptr = nullptr;
538 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
539 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
540 2 : endptr == s.c_str() + s.size())
541 : {
542 1 : v_i.push_back(static_cast<int>(v));
543 : }
544 : else
545 : {
546 2 : break;
547 : }
548 : }
549 3 : if (v_i.size() == value.size())
550 1 : return Set(v_i);
551 : }
552 898 : else if (m_decl.GetType() == GAAT_REAL_LIST)
553 : {
554 2 : std::vector<double> v_d;
555 3 : for (const std::string &s : value)
556 : {
557 2 : char *endptr = nullptr;
558 2 : const double v = CPLStrtod(s.c_str(), &endptr);
559 2 : if (endptr == s.c_str() + s.size())
560 : {
561 1 : v_d.push_back(v);
562 : }
563 : else
564 : {
565 1 : break;
566 : }
567 : }
568 2 : if (v_d.size() == value.size())
569 1 : return Set(v_d);
570 : }
571 1790 : else if ((m_decl.GetType() == GAAT_INTEGER ||
572 1787 : m_decl.GetType() == GAAT_REAL ||
573 2685 : m_decl.GetType() == GAAT_STRING) &&
574 5 : value.size() == 1)
575 : {
576 4 : return Set(value[0]);
577 : }
578 892 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
579 : {
580 30 : std::vector<GDALArgDatasetValue> dsVector;
581 46 : for (const std::string &s : value)
582 31 : dsVector.emplace_back(s);
583 15 : return Set(std::move(dsVector));
584 : }
585 :
586 880 : if (m_decl.GetType() != GAAT_STRING_LIST)
587 : {
588 10 : CPLError(CE_Failure, CPLE_AppDefined,
589 : "Calling Set(const std::vector<std::string> &) on argument "
590 : "'%s' of type %s is not supported",
591 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
592 5 : return false;
593 : }
594 :
595 1736 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
596 861 : m_decl.IsRemoveSQLCommentsEnabled())
597 : {
598 28 : std::vector<std::string> newValue(value);
599 31 : for (auto &s : newValue)
600 : {
601 17 : if (!ProcessString(s))
602 0 : return false;
603 : }
604 14 : return SetInternal(newValue);
605 : }
606 : else
607 : {
608 861 : return SetInternal(value);
609 : }
610 : }
611 :
612 167 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
613 : {
614 167 : if (m_decl.GetType() == GAAT_REAL_LIST)
615 : {
616 2 : std::vector<double> v_d;
617 2 : for (int i : value)
618 1 : v_d.push_back(i);
619 1 : return Set(v_d);
620 : }
621 166 : else if (m_decl.GetType() == GAAT_STRING_LIST)
622 : {
623 2 : std::vector<std::string> v_s;
624 3 : for (int i : value)
625 2 : v_s.push_back(std::to_string(i));
626 1 : return Set(v_s);
627 : }
628 328 : else if ((m_decl.GetType() == GAAT_INTEGER ||
629 324 : m_decl.GetType() == GAAT_REAL ||
630 491 : m_decl.GetType() == GAAT_STRING) &&
631 5 : value.size() == 1)
632 : {
633 3 : return Set(value[0]);
634 : }
635 :
636 162 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
637 : {
638 6 : CPLError(CE_Failure, CPLE_AppDefined,
639 : "Calling Set(const std::vector<int> &) on argument '%s' of "
640 : "type %s is not supported",
641 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
642 3 : return false;
643 : }
644 159 : return SetInternal(value);
645 : }
646 :
647 259 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
648 : {
649 259 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
650 : {
651 2 : std::vector<int> v_i;
652 3 : for (double d : value)
653 : {
654 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
655 : {
656 1 : v_i.push_back(static_cast<int>(d));
657 : }
658 : else
659 : {
660 : break;
661 : }
662 : }
663 2 : if (v_i.size() == value.size())
664 1 : return Set(v_i);
665 : }
666 257 : else if (m_decl.GetType() == GAAT_STRING_LIST)
667 : {
668 2 : std::vector<std::string> v_s;
669 3 : for (double d : value)
670 2 : v_s.push_back(std::to_string(d));
671 1 : return Set(v_s);
672 : }
673 511 : else if ((m_decl.GetType() == GAAT_INTEGER ||
674 509 : m_decl.GetType() == GAAT_REAL ||
675 766 : m_decl.GetType() == GAAT_STRING) &&
676 3 : value.size() == 1)
677 : {
678 3 : return Set(value[0]);
679 : }
680 :
681 254 : if (m_decl.GetType() != GAAT_REAL_LIST)
682 : {
683 4 : CPLError(CE_Failure, CPLE_AppDefined,
684 : "Calling Set(const std::vector<double> &) on argument '%s' of "
685 : "type %s is not supported",
686 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
687 2 : return false;
688 : }
689 252 : return SetInternal(value);
690 : }
691 :
692 3223 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
693 : {
694 3223 : if (m_decl.GetType() != GAAT_DATASET_LIST)
695 : {
696 2 : CPLError(CE_Failure, CPLE_AppDefined,
697 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
698 : "argument '%s' of type %s is not supported",
699 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
700 1 : return false;
701 : }
702 3222 : m_explicitlySet = true;
703 3222 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
704 3222 : return RunAllActions();
705 : }
706 :
707 : GDALAlgorithmArg &
708 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
709 : {
710 0 : Set(std::move(value));
711 0 : return *this;
712 : }
713 :
714 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
715 : {
716 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
717 1 : return Set(value.exportToWkt(apszOptions));
718 : }
719 :
720 4003 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
721 : {
722 4003 : if (m_decl.GetType() != other.GetType())
723 : {
724 2 : CPLError(CE_Failure, CPLE_AppDefined,
725 : "Calling SetFrom() on argument '%s' of type %s whereas "
726 : "other argument type is %s is not supported",
727 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
728 : GDALAlgorithmArgTypeName(other.GetType()));
729 1 : return false;
730 : }
731 :
732 4002 : switch (m_decl.GetType())
733 : {
734 85 : case GAAT_BOOLEAN:
735 85 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
736 85 : break;
737 771 : case GAAT_STRING:
738 1542 : *std::get<std::string *>(m_value) =
739 771 : *std::get<std::string *>(other.m_value);
740 771 : break;
741 6 : case GAAT_INTEGER:
742 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
743 6 : break;
744 1 : case GAAT_REAL:
745 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
746 1 : break;
747 921 : case GAAT_DATASET:
748 921 : return SetFrom(other.Get<GDALArgDatasetValue>());
749 38 : case GAAT_STRING_LIST:
750 76 : *std::get<std::vector<std::string> *>(m_value) =
751 38 : *std::get<std::vector<std::string> *>(other.m_value);
752 38 : break;
753 1 : case GAAT_INTEGER_LIST:
754 2 : *std::get<std::vector<int> *>(m_value) =
755 1 : *std::get<std::vector<int> *>(other.m_value);
756 1 : break;
757 1 : case GAAT_REAL_LIST:
758 2 : *std::get<std::vector<double> *>(m_value) =
759 1 : *std::get<std::vector<double> *>(other.m_value);
760 1 : break;
761 2178 : case GAAT_DATASET_LIST:
762 : {
763 2178 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
764 2183 : for (const auto &val :
765 6544 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
766 : {
767 4366 : GDALArgDatasetValue v;
768 2183 : v.SetFrom(val);
769 2183 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
770 2183 : ->push_back(std::move(v));
771 : }
772 2178 : break;
773 : }
774 : }
775 3081 : m_explicitlySet = true;
776 3081 : return RunAllActions();
777 : }
778 :
779 : /************************************************************************/
780 : /* GDALAlgorithmArg::RunAllActions() */
781 : /************************************************************************/
782 :
783 14930 : bool GDALAlgorithmArg::RunAllActions()
784 : {
785 14930 : if (!RunValidationActions())
786 128 : return false;
787 14802 : RunActions();
788 14802 : return true;
789 : }
790 :
791 : /************************************************************************/
792 : /* GDALAlgorithmArg::RunActions() */
793 : /************************************************************************/
794 :
795 14803 : void GDALAlgorithmArg::RunActions()
796 : {
797 15091 : for (const auto &f : m_actions)
798 288 : f();
799 14803 : }
800 :
801 : /************************************************************************/
802 : /* GDALAlgorithmArg::ValidateChoice() */
803 : /************************************************************************/
804 :
805 : // Returns the canonical value if matching a valid choice, or empty string
806 : // otherwise.
807 2498 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
808 : {
809 14887 : for (const std::string &choice : GetChoices())
810 : {
811 14769 : if (EQUAL(value.c_str(), choice.c_str()))
812 : {
813 2380 : return choice;
814 : }
815 : }
816 :
817 190 : for (const std::string &choice : GetHiddenChoices())
818 : {
819 172 : if (EQUAL(value.c_str(), choice.c_str()))
820 : {
821 100 : return choice;
822 : }
823 : }
824 :
825 36 : std::string expected;
826 220 : for (const auto &choice : GetChoices())
827 : {
828 202 : if (!expected.empty())
829 184 : expected += ", ";
830 202 : expected += '\'';
831 202 : expected += choice;
832 202 : expected += '\'';
833 : }
834 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
835 : {
836 6 : return "?";
837 : }
838 24 : CPLError(CE_Failure, CPLE_IllegalArg,
839 : "Invalid value '%s' for string argument '%s'. Should be "
840 : "one among %s.",
841 12 : value.c_str(), GetName().c_str(), expected.c_str());
842 12 : return std::string();
843 : }
844 :
845 : /************************************************************************/
846 : /* GDALAlgorithmArg::ValidateIntRange() */
847 : /************************************************************************/
848 :
849 2647 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
850 : {
851 2647 : bool ret = true;
852 :
853 2647 : const auto [minVal, minValIsIncluded] = GetMinValue();
854 2647 : if (!std::isnan(minVal))
855 : {
856 2024 : if (minValIsIncluded && val < minVal)
857 : {
858 3 : CPLError(CE_Failure, CPLE_IllegalArg,
859 : "Value of argument '%s' is %d, but should be >= %d",
860 3 : GetName().c_str(), val, static_cast<int>(minVal));
861 3 : ret = false;
862 : }
863 2021 : else if (!minValIsIncluded && val <= minVal)
864 : {
865 1 : CPLError(CE_Failure, CPLE_IllegalArg,
866 : "Value of argument '%s' is %d, but should be > %d",
867 1 : GetName().c_str(), val, static_cast<int>(minVal));
868 1 : ret = false;
869 : }
870 : }
871 :
872 2647 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
873 2647 : if (!std::isnan(maxVal))
874 : {
875 :
876 382 : if (maxValIsIncluded && val > maxVal)
877 : {
878 1 : CPLError(CE_Failure, CPLE_IllegalArg,
879 : "Value of argument '%s' is %d, but should be <= %d",
880 1 : GetName().c_str(), val, static_cast<int>(maxVal));
881 1 : ret = false;
882 : }
883 381 : else if (!maxValIsIncluded && val >= maxVal)
884 : {
885 1 : CPLError(CE_Failure, CPLE_IllegalArg,
886 : "Value of argument '%s' is %d, but should be < %d",
887 1 : GetName().c_str(), val, static_cast<int>(maxVal));
888 1 : ret = false;
889 : }
890 : }
891 :
892 2647 : return ret;
893 : }
894 :
895 : /************************************************************************/
896 : /* GDALAlgorithmArg::ValidateRealRange() */
897 : /************************************************************************/
898 :
899 2074 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
900 : {
901 2074 : bool ret = true;
902 :
903 2074 : const auto [minVal, minValIsIncluded] = GetMinValue();
904 2074 : if (!std::isnan(minVal))
905 : {
906 201 : if (minValIsIncluded && !(val >= minVal))
907 : {
908 11 : CPLError(CE_Failure, CPLE_IllegalArg,
909 : "Value of argument '%s' is %g, but should be >= %g",
910 11 : GetName().c_str(), val, minVal);
911 11 : ret = false;
912 : }
913 190 : else if (!minValIsIncluded && !(val > minVal))
914 : {
915 4 : CPLError(CE_Failure, CPLE_IllegalArg,
916 : "Value of argument '%s' is %g, but should be > %g",
917 4 : GetName().c_str(), val, minVal);
918 4 : ret = false;
919 : }
920 : }
921 :
922 2074 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
923 2074 : if (!std::isnan(maxVal))
924 : {
925 :
926 46 : if (maxValIsIncluded && !(val <= maxVal))
927 : {
928 2 : CPLError(CE_Failure, CPLE_IllegalArg,
929 : "Value of argument '%s' is %g, but should be <= %g",
930 2 : GetName().c_str(), val, maxVal);
931 2 : ret = false;
932 : }
933 44 : else if (!maxValIsIncluded && !(val < maxVal))
934 : {
935 1 : CPLError(CE_Failure, CPLE_IllegalArg,
936 : "Value of argument '%s' is %g, but should be < %g",
937 1 : GetName().c_str(), val, maxVal);
938 1 : ret = false;
939 : }
940 : }
941 :
942 2074 : return ret;
943 : }
944 :
945 : /************************************************************************/
946 : /* GDALAlgorithmArg::RunValidationActions() */
947 : /************************************************************************/
948 :
949 33063 : bool GDALAlgorithmArg::RunValidationActions()
950 : {
951 33063 : bool ret = true;
952 :
953 33063 : if (GetType() == GAAT_STRING && !GetChoices().empty())
954 : {
955 1621 : auto &val = Get<std::string>();
956 3242 : std::string validVal = ValidateChoice(val);
957 1621 : if (validVal.empty())
958 7 : ret = false;
959 : else
960 1614 : val = std::move(validVal);
961 : }
962 31442 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
963 : {
964 633 : auto &values = Get<std::vector<std::string>>();
965 1510 : for (std::string &val : values)
966 : {
967 1754 : std::string validVal = ValidateChoice(val);
968 877 : if (validVal.empty())
969 5 : ret = false;
970 : else
971 872 : val = std::move(validVal);
972 : }
973 : }
974 :
975 : const auto CheckMinCharCount =
976 942 : [this, &ret](const std::string &val, int nMinCharCount)
977 : {
978 930 : if (val.size() < static_cast<size_t>(nMinCharCount))
979 : {
980 12 : CPLError(CE_Failure, CPLE_IllegalArg,
981 : "Value of argument '%s' is '%s', but should have at least "
982 : "%d character%s",
983 6 : GetName().c_str(), val.c_str(), nMinCharCount,
984 : nMinCharCount > 1 ? "s" : "");
985 6 : ret = false;
986 : }
987 33993 : };
988 :
989 : const auto CheckMaxCharCount =
990 11535 : [this, &ret](const std::string &val, int nMaxCharCount)
991 : {
992 11533 : if (val.size() > static_cast<size_t>(nMaxCharCount))
993 : {
994 2 : CPLError(
995 : CE_Failure, CPLE_IllegalArg,
996 : "Value of argument '%s' is '%s', but should have no more than "
997 : "%d character%s",
998 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
999 : nMaxCharCount > 1 ? "s" : "");
1000 1 : ret = false;
1001 : }
1002 44596 : };
1003 :
1004 33063 : if (GetType() == GAAT_STRING)
1005 : {
1006 9187 : const auto &val = Get<std::string>();
1007 9187 : const int nMinCharCount = GetMinCharCount();
1008 9187 : if (nMinCharCount > 0)
1009 : {
1010 848 : CheckMinCharCount(val, nMinCharCount);
1011 : }
1012 :
1013 9187 : const int nMaxCharCount = GetMaxCharCount();
1014 9187 : CheckMaxCharCount(val, nMaxCharCount);
1015 : }
1016 23876 : else if (GetType() == GAAT_STRING_LIST)
1017 : {
1018 1930 : const int nMinCharCount = GetMinCharCount();
1019 1930 : const int nMaxCharCount = GetMaxCharCount();
1020 4276 : for (const auto &val : Get<std::vector<std::string>>())
1021 : {
1022 2346 : if (nMinCharCount > 0)
1023 82 : CheckMinCharCount(val, nMinCharCount);
1024 2346 : CheckMaxCharCount(val, nMaxCharCount);
1025 : }
1026 : }
1027 21946 : else if (GetType() == GAAT_INTEGER)
1028 : {
1029 1966 : ret = ValidateIntRange(Get<int>()) && ret;
1030 : }
1031 19980 : else if (GetType() == GAAT_INTEGER_LIST)
1032 : {
1033 1015 : for (int v : Get<std::vector<int>>())
1034 681 : ret = ValidateIntRange(v) && ret;
1035 : }
1036 19646 : else if (GetType() == GAAT_REAL)
1037 : {
1038 530 : ret = ValidateRealRange(Get<double>()) && ret;
1039 : }
1040 19116 : else if (GetType() == GAAT_REAL_LIST)
1041 : {
1042 2102 : for (double v : Get<std::vector<double>>())
1043 1544 : ret = ValidateRealRange(v) && ret;
1044 : }
1045 :
1046 33063 : if (GDALAlgorithmArgTypeIsList(GetType()))
1047 : {
1048 13406 : int valueCount = 0;
1049 13406 : if (GetType() == GAAT_STRING_LIST)
1050 : {
1051 1930 : valueCount =
1052 1930 : static_cast<int>(Get<std::vector<std::string>>().size());
1053 : }
1054 11476 : else if (GetType() == GAAT_INTEGER_LIST)
1055 : {
1056 334 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1057 : }
1058 11142 : else if (GetType() == GAAT_REAL_LIST)
1059 : {
1060 558 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1061 : }
1062 10584 : else if (GetType() == GAAT_DATASET_LIST)
1063 : {
1064 10584 : valueCount = static_cast<int>(
1065 10584 : Get<std::vector<GDALArgDatasetValue>>().size());
1066 : }
1067 :
1068 13406 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1069 : {
1070 14 : ReportError(CE_Failure, CPLE_AppDefined,
1071 : "%d value%s been specified for argument '%s', "
1072 : "whereas exactly %d %s expected.",
1073 : valueCount, valueCount > 1 ? "s have" : " has",
1074 7 : GetName().c_str(), GetMinCount(),
1075 7 : GetMinCount() > 1 ? "were" : "was");
1076 7 : ret = false;
1077 : }
1078 13399 : else if (valueCount < GetMinCount())
1079 : {
1080 6 : ReportError(CE_Failure, CPLE_AppDefined,
1081 : "Only %d value%s been specified for argument '%s', "
1082 : "whereas at least %d %s expected.",
1083 : valueCount, valueCount > 1 ? "s have" : " has",
1084 3 : GetName().c_str(), GetMinCount(),
1085 3 : GetMinCount() > 1 ? "were" : "was");
1086 3 : ret = false;
1087 : }
1088 13396 : else if (valueCount > GetMaxCount())
1089 : {
1090 2 : ReportError(CE_Failure, CPLE_AppDefined,
1091 : "%d value%s been specified for argument '%s', "
1092 : "whereas at most %d %s expected.",
1093 : valueCount, valueCount > 1 ? "s have" : " has",
1094 1 : GetName().c_str(), GetMaxCount(),
1095 1 : GetMaxCount() > 1 ? "were" : "was");
1096 1 : ret = false;
1097 : }
1098 : }
1099 :
1100 33063 : if (ret)
1101 : {
1102 39274 : for (const auto &f : m_validationActions)
1103 : {
1104 6265 : if (!f())
1105 81 : ret = false;
1106 : }
1107 : }
1108 :
1109 33063 : return ret;
1110 : }
1111 :
1112 : /************************************************************************/
1113 : /* GDALAlgorithmArg::ReportError() */
1114 : /************************************************************************/
1115 :
1116 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1117 : const char *fmt, ...) const
1118 : {
1119 : va_list args;
1120 11 : va_start(args, fmt);
1121 11 : if (m_owner)
1122 : {
1123 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1124 22 : CPLString().vPrintf(fmt, args).c_str());
1125 : }
1126 : else
1127 : {
1128 0 : CPLError(eErrClass, err_no, "%s",
1129 0 : CPLString().vPrintf(fmt, args).c_str());
1130 : }
1131 11 : va_end(args);
1132 11 : }
1133 :
1134 : /************************************************************************/
1135 : /* GDALAlgorithmArg::GetEscapedString() */
1136 : /************************************************************************/
1137 :
1138 : /* static */
1139 130 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1140 : {
1141 142 : if (s.find_first_of("\" \\,") != std::string::npos &&
1142 6 : !(s.size() > 4 &&
1143 6 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1144 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1145 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1146 : {
1147 8 : return std::string("\"")
1148 : .append(
1149 8 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1150 4 : .append("\"");
1151 : }
1152 : else
1153 : {
1154 126 : return s;
1155 : }
1156 : }
1157 :
1158 : /************************************************************************/
1159 : /* GDALAlgorithmArg::Serialize() */
1160 : /************************************************************************/
1161 :
1162 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1163 : bool absolutePath) const
1164 : {
1165 39 : serializedArg.clear();
1166 :
1167 39 : if (!IsExplicitlySet())
1168 : {
1169 0 : return false;
1170 : }
1171 :
1172 78 : std::string ret = "--";
1173 39 : ret += GetName();
1174 39 : if (GetType() == GAAT_BOOLEAN)
1175 : {
1176 0 : serializedArg = std::move(ret);
1177 0 : return true;
1178 : }
1179 :
1180 5 : const auto AddListValueSeparator = [this, &ret]()
1181 : {
1182 1 : if (GetPackedValuesAllowed())
1183 : {
1184 0 : ret += ',';
1185 : }
1186 : else
1187 : {
1188 1 : ret += " --";
1189 1 : ret += GetName();
1190 1 : ret += ' ';
1191 : }
1192 40 : };
1193 :
1194 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1195 : {
1196 : VSIStatBufL sStat;
1197 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1198 0 : !CPLIsFilenameRelative(filename.c_str()))
1199 0 : return filename;
1200 0 : char *pszCWD = CPLGetCurrentDir();
1201 0 : if (!pszCWD)
1202 0 : return filename;
1203 : const auto absPath =
1204 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1205 0 : CPLFree(pszCWD);
1206 0 : return absPath;
1207 : };
1208 :
1209 39 : ret += ' ';
1210 39 : switch (GetType())
1211 : {
1212 0 : case GAAT_BOOLEAN:
1213 0 : break;
1214 8 : case GAAT_STRING:
1215 : {
1216 8 : const auto &val = Get<std::string>();
1217 8 : ret += GetEscapedString(val);
1218 8 : break;
1219 : }
1220 0 : case GAAT_INTEGER:
1221 : {
1222 0 : ret += CPLSPrintf("%d", Get<int>());
1223 0 : break;
1224 : }
1225 0 : case GAAT_REAL:
1226 : {
1227 0 : ret += CPLSPrintf("%.17g", Get<double>());
1228 0 : break;
1229 : }
1230 2 : case GAAT_DATASET:
1231 : {
1232 2 : const auto &val = Get<GDALArgDatasetValue>();
1233 2 : const auto &str = val.GetName();
1234 2 : if (str.empty())
1235 : {
1236 0 : return false;
1237 : }
1238 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1239 2 : break;
1240 : }
1241 4 : case GAAT_STRING_LIST:
1242 : {
1243 4 : const auto &vals = Get<std::vector<std::string>>();
1244 8 : for (size_t i = 0; i < vals.size(); ++i)
1245 : {
1246 4 : if (i > 0)
1247 1 : AddListValueSeparator();
1248 4 : ret += GetEscapedString(vals[i]);
1249 : }
1250 4 : break;
1251 : }
1252 0 : case GAAT_INTEGER_LIST:
1253 : {
1254 0 : const auto &vals = Get<std::vector<int>>();
1255 0 : for (size_t i = 0; i < vals.size(); ++i)
1256 : {
1257 0 : if (i > 0)
1258 0 : AddListValueSeparator();
1259 0 : ret += CPLSPrintf("%d", vals[i]);
1260 : }
1261 0 : break;
1262 : }
1263 0 : case GAAT_REAL_LIST:
1264 : {
1265 0 : const auto &vals = Get<std::vector<double>>();
1266 0 : for (size_t i = 0; i < vals.size(); ++i)
1267 : {
1268 0 : if (i > 0)
1269 0 : AddListValueSeparator();
1270 0 : ret += CPLSPrintf("%.17g", vals[i]);
1271 : }
1272 0 : break;
1273 : }
1274 25 : case GAAT_DATASET_LIST:
1275 : {
1276 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1277 49 : for (size_t i = 0; i < vals.size(); ++i)
1278 : {
1279 25 : if (i > 0)
1280 0 : AddListValueSeparator();
1281 25 : const auto &val = vals[i];
1282 25 : const auto &str = val.GetName();
1283 25 : if (str.empty())
1284 : {
1285 1 : return false;
1286 : }
1287 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1288 24 : : str);
1289 : }
1290 24 : break;
1291 : }
1292 : }
1293 :
1294 38 : serializedArg = std::move(ret);
1295 38 : return true;
1296 : }
1297 :
1298 : /************************************************************************/
1299 : /* ~GDALInConstructionAlgorithmArg() */
1300 : /************************************************************************/
1301 :
1302 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1303 :
1304 : /************************************************************************/
1305 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1306 : /************************************************************************/
1307 :
1308 : GDALInConstructionAlgorithmArg &
1309 59604 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1310 : {
1311 59604 : m_decl.AddAlias(alias);
1312 59604 : if (m_owner)
1313 59604 : m_owner->AddAliasFor(this, alias);
1314 59604 : return *this;
1315 : }
1316 :
1317 : /************************************************************************/
1318 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1319 : /************************************************************************/
1320 :
1321 : GDALInConstructionAlgorithmArg &
1322 12556 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1323 : {
1324 12556 : m_decl.AddHiddenAlias(alias);
1325 12556 : if (m_owner)
1326 12556 : m_owner->AddAliasFor(this, alias);
1327 12556 : return *this;
1328 : }
1329 :
1330 : /************************************************************************/
1331 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1332 : /************************************************************************/
1333 :
1334 : GDALInConstructionAlgorithmArg &
1335 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1336 : {
1337 48 : m_decl.AddShortNameAlias(shortNameAlias);
1338 48 : if (m_owner)
1339 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1340 48 : return *this;
1341 : }
1342 :
1343 : /************************************************************************/
1344 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1345 : /************************************************************************/
1346 :
1347 20522 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1348 : {
1349 20522 : m_decl.SetPositional();
1350 20522 : if (m_owner)
1351 20522 : m_owner->SetPositional(this);
1352 20522 : return *this;
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1357 : /************************************************************************/
1358 :
1359 1254 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1360 2508 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1361 1254 : m_nameSet(true)
1362 : {
1363 1254 : if (m_poDS)
1364 1254 : m_poDS->Reference();
1365 1254 : }
1366 :
1367 : /************************************************************************/
1368 : /* GDALArgDatasetValue::Set() */
1369 : /************************************************************************/
1370 :
1371 2086 : void GDALArgDatasetValue::Set(const std::string &name)
1372 : {
1373 2086 : Close();
1374 2086 : m_name = name;
1375 2086 : m_nameSet = true;
1376 2086 : if (m_ownerArg)
1377 2081 : m_ownerArg->NotifyValueSet();
1378 2086 : }
1379 :
1380 : /************************************************************************/
1381 : /* GDALArgDatasetValue::Set() */
1382 : /************************************************************************/
1383 :
1384 1827 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1385 : {
1386 1827 : Close();
1387 1827 : m_poDS = poDS.release();
1388 1827 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1389 1827 : m_nameSet = true;
1390 1827 : if (m_ownerArg)
1391 1708 : m_ownerArg->NotifyValueSet();
1392 1827 : }
1393 :
1394 : /************************************************************************/
1395 : /* GDALArgDatasetValue::Set() */
1396 : /************************************************************************/
1397 :
1398 7370 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1399 : {
1400 7370 : Close();
1401 7370 : m_poDS = poDS;
1402 7370 : if (m_poDS)
1403 6560 : m_poDS->Reference();
1404 7370 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1405 7370 : m_nameSet = true;
1406 7370 : if (m_ownerArg)
1407 3075 : m_ownerArg->NotifyValueSet();
1408 7370 : }
1409 :
1410 : /************************************************************************/
1411 : /* GDALArgDatasetValue::SetFrom() */
1412 : /************************************************************************/
1413 :
1414 3108 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1415 : {
1416 3108 : Close();
1417 3108 : m_name = other.m_name;
1418 3108 : m_nameSet = other.m_nameSet;
1419 3108 : m_poDS = other.m_poDS;
1420 3108 : if (m_poDS)
1421 2180 : m_poDS->Reference();
1422 3108 : }
1423 :
1424 : /************************************************************************/
1425 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1426 : /************************************************************************/
1427 :
1428 29400 : GDALArgDatasetValue::~GDALArgDatasetValue()
1429 : {
1430 29400 : Close();
1431 29400 : }
1432 :
1433 : /************************************************************************/
1434 : /* GDALArgDatasetValue::Close() */
1435 : /************************************************************************/
1436 :
1437 48695 : bool GDALArgDatasetValue::Close()
1438 : {
1439 48695 : bool ret = true;
1440 48695 : if (m_poDS && m_poDS->Dereference() == 0)
1441 : {
1442 3111 : ret = m_poDS->Close() == CE_None;
1443 3111 : delete m_poDS;
1444 : }
1445 48695 : m_poDS = nullptr;
1446 48695 : return ret;
1447 : }
1448 :
1449 : /************************************************************************/
1450 : /* GDALArgDatasetValue::operator=() */
1451 : /************************************************************************/
1452 :
1453 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1454 : {
1455 2 : Close();
1456 2 : m_poDS = other.m_poDS;
1457 2 : m_name = other.m_name;
1458 2 : m_nameSet = other.m_nameSet;
1459 2 : other.m_poDS = nullptr;
1460 2 : other.m_name.clear();
1461 2 : other.m_nameSet = false;
1462 2 : return *this;
1463 : }
1464 :
1465 : /************************************************************************/
1466 : /* GDALArgDatasetValue::GetDataset() */
1467 : /************************************************************************/
1468 :
1469 990 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1470 : {
1471 990 : if (m_poDS)
1472 990 : m_poDS->Reference();
1473 990 : return m_poDS;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1478 : /************************************************************************/
1479 :
1480 2924 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1481 2924 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1482 : {
1483 2924 : other.m_poDS = nullptr;
1484 2924 : other.m_name.clear();
1485 2924 : }
1486 :
1487 : /************************************************************************/
1488 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1489 : /************************************************************************/
1490 :
1491 3062 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1492 : bool noneAllowed, const std::vector<std::string> &specialValues)
1493 : {
1494 3062 : if (GetType() != GAAT_STRING)
1495 : {
1496 1 : CPLError(CE_Failure, CPLE_AppDefined,
1497 : "SetIsCRSArg() can only be called on a String argument");
1498 1 : return *this;
1499 : }
1500 : AddValidationAction(
1501 546 : [this, noneAllowed, specialValues]()
1502 : {
1503 : const std::string &osVal =
1504 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1505 270 : ->Get<std::string>();
1506 270 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1507 0 : return true;
1508 :
1509 527 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1510 257 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1511 527 : specialValues.end())
1512 : {
1513 249 : OGRSpatialReference oSRS;
1514 249 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1515 : {
1516 6 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1517 : "Invalid value for '%s' argument",
1518 6 : GetName().c_str());
1519 6 : return false;
1520 : }
1521 : }
1522 264 : return true;
1523 3061 : });
1524 :
1525 : SetAutoCompleteFunction(
1526 40 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1527 : {
1528 10 : bool bIsRaster = false;
1529 10 : OGREnvelope sDatasetLongLatEnv;
1530 20 : std::string osCelestialBodyName;
1531 10 : if (GetName() == "dst-crs")
1532 : {
1533 10 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1534 10 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1535 : {
1536 : auto &val =
1537 10 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1538 10 : if (val.size() == 1)
1539 : {
1540 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1541 : auto poDS = std::unique_ptr<GDALDataset>(
1542 4 : GDALDataset::Open(val[0].GetName().c_str()));
1543 2 : if (poDS)
1544 : {
1545 2 : bIsRaster = poDS->GetRasterCount() != 0;
1546 2 : if (auto poCRS = poDS->GetSpatialRef())
1547 : {
1548 : const char *pszCelestialBodyName =
1549 2 : poCRS->GetCelestialBodyName();
1550 2 : if (pszCelestialBodyName)
1551 2 : osCelestialBodyName = pszCelestialBodyName;
1552 :
1553 2 : if (!pszCelestialBodyName ||
1554 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1555 : {
1556 0 : OGRSpatialReference oLongLat;
1557 0 : oLongLat.CopyGeogCSFrom(poCRS);
1558 0 : oLongLat.SetAxisMappingStrategy(
1559 : OAMS_TRADITIONAL_GIS_ORDER);
1560 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1561 0 : &oLongLat);
1562 : }
1563 : else
1564 : {
1565 2 : poDS->GetExtentWGS84LongLat(
1566 2 : &sDatasetLongLatEnv);
1567 : }
1568 : }
1569 : }
1570 : }
1571 : }
1572 : }
1573 :
1574 : const auto IsCRSCompatible =
1575 42959 : [bIsRaster, &sDatasetLongLatEnv,
1576 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1577 : {
1578 42959 : if (!sDatasetLongLatEnv.IsInit())
1579 30685 : return true;
1580 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1581 11834 : !(bIsRaster &&
1582 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1583 11652 : crsInfo->dfWestLongitudeDeg <
1584 11652 : crsInfo->dfEastLongitudeDeg &&
1585 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1586 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1587 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1588 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1589 329 : ((!osCelestialBodyName.empty() &&
1590 658 : crsInfo->pszCelestialBodyName &&
1591 329 : osCelestialBodyName ==
1592 329 : crsInfo->pszCelestialBodyName) ||
1593 0 : (osCelestialBodyName.empty() &&
1594 12274 : !crsInfo->pszCelestialBodyName));
1595 10 : };
1596 :
1597 10 : std::vector<std::string> oRet;
1598 10 : if (noneAllowed)
1599 0 : oRet.push_back("none");
1600 10 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1601 10 : if (!currentValue.empty())
1602 : {
1603 : const CPLStringList aosTokens(
1604 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1605 7 : int nCount = 0;
1606 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1607 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1608 : nullptr, &nCount),
1609 14 : OSRDestroyCRSInfoList);
1610 14 : std::string osCode;
1611 :
1612 14 : std::vector<const OSRCRSInfo *> candidates;
1613 46270 : for (int i = 0; i < nCount; ++i)
1614 : {
1615 46263 : const auto *entry = (pCRSList.get())[i];
1616 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1617 : {
1618 49425 : if (aosTokens.size() == 1 ||
1619 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1620 : {
1621 12666 : if (candidates.empty())
1622 7 : osCode = entry->pszCode;
1623 12666 : candidates.push_back(entry);
1624 : }
1625 : }
1626 : }
1627 7 : if (candidates.size() == 1)
1628 : {
1629 1 : oRet.push_back(std::move(osCode));
1630 : }
1631 : else
1632 : {
1633 6 : if (sDatasetLongLatEnv.IsInit())
1634 : {
1635 2 : std::sort(
1636 : candidates.begin(), candidates.end(),
1637 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1638 : {
1639 2999 : const double dfXa =
1640 2999 : a->dfWestLongitudeDeg >
1641 2999 : a->dfEastLongitudeDeg
1642 2999 : ? a->dfWestLongitudeDeg -
1643 0 : a->dfEastLongitudeDeg
1644 2999 : : (180 - a->dfWestLongitudeDeg) +
1645 2999 : (a->dfEastLongitudeDeg - -180);
1646 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1647 2999 : a->dfSouthLatitudeDeg;
1648 2999 : const double dfXb =
1649 2999 : b->dfWestLongitudeDeg >
1650 2999 : b->dfEastLongitudeDeg
1651 2999 : ? b->dfWestLongitudeDeg -
1652 0 : b->dfEastLongitudeDeg
1653 2999 : : (180 - b->dfWestLongitudeDeg) +
1654 2999 : (b->dfEastLongitudeDeg - -180);
1655 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1656 2999 : b->dfSouthLatitudeDeg;
1657 2999 : const double diffArea =
1658 2999 : dfXa * dfYa - dfXb * dfYb;
1659 2999 : if (diffArea < 0)
1660 279 : return true;
1661 2720 : if (diffArea == 0)
1662 : {
1663 2506 : if (std::string_view(a->pszName) ==
1664 2506 : b->pszName)
1665 : {
1666 57 : if (a->eType ==
1667 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1668 13 : b->eType !=
1669 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1670 13 : return true;
1671 44 : if (a->eType ==
1672 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1673 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1674 9 : return true;
1675 35 : return false;
1676 : }
1677 4898 : return std::string_view(a->pszCode) <
1678 4898 : b->pszCode;
1679 : }
1680 214 : return false;
1681 : });
1682 : }
1683 :
1684 12671 : for (const auto *entry : candidates)
1685 : {
1686 25330 : std::string val = std::string(entry->pszCode)
1687 12665 : .append(" -- ")
1688 25330 : .append(entry->pszName);
1689 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1690 1294 : val.append(" (geographic 2D)");
1691 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1692 446 : val.append(" (geographic 3D)");
1693 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1694 397 : val.append(" (geocentric)");
1695 12665 : oRet.push_back(std::move(val));
1696 : }
1697 : }
1698 : }
1699 10 : if (currentValue.empty() || oRet.empty())
1700 : {
1701 : const CPLStringList aosAuthorities(
1702 6 : OSRGetAuthorityListFromDatabase());
1703 18 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1704 : {
1705 15 : int nCount = 0;
1706 15 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1707 : pszAuth, nullptr, &nCount));
1708 15 : if (nCount)
1709 12 : oRet.push_back(std::string(pszAuth).append(":"));
1710 : }
1711 : }
1712 20 : return oRet;
1713 3061 : });
1714 :
1715 3061 : return *this;
1716 : }
1717 :
1718 : /************************************************************************/
1719 : /* GDALAlgorithm::GDALAlgorithm() */
1720 : /************************************************************************/
1721 :
1722 20161 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1723 : const std::string &description,
1724 20161 : const std::string &helpURL)
1725 : : m_name(name), m_description(description), m_helpURL(helpURL),
1726 40169 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1727 20161 : ? "https://gdal.org" + m_helpURL
1728 60114 : : m_helpURL)
1729 : {
1730 : auto &helpArg =
1731 : AddArg("help", 'h', _("Display help message and exit"),
1732 40322 : &m_helpRequested)
1733 20161 : .SetHiddenForAPI()
1734 40322 : .SetCategory(GAAC_COMMON)
1735 14 : .AddAction([this]()
1736 20161 : { m_specialActionRequested = m_calledFromCommandLine; });
1737 : auto &helpDocArg =
1738 : AddArg("help-doc", 0,
1739 : _("Display help message for use by documentation"),
1740 40322 : &m_helpDocRequested)
1741 20161 : .SetHidden()
1742 12 : .AddAction([this]()
1743 20161 : { m_specialActionRequested = m_calledFromCommandLine; });
1744 : auto &jsonUsageArg =
1745 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1746 40322 : &m_JSONUsageRequested)
1747 20161 : .SetHiddenForAPI()
1748 40322 : .SetCategory(GAAC_COMMON)
1749 4 : .AddAction([this]()
1750 20161 : { m_specialActionRequested = m_calledFromCommandLine; });
1751 40322 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1752 40322 : .SetMetaVar("<KEY>=<VALUE>")
1753 20161 : .SetHiddenForAPI()
1754 40322 : .SetCategory(GAAC_COMMON)
1755 : .AddAction(
1756 2 : [this]()
1757 : {
1758 2 : ReportError(
1759 : CE_Warning, CPLE_AppDefined,
1760 : "Configuration options passed with the 'config' argument "
1761 : "are ignored");
1762 20161 : });
1763 :
1764 20161 : AddValidationAction(
1765 12154 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1766 : {
1767 6272 : if (!m_calledFromCommandLine && m_specialActionRequested)
1768 : {
1769 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1770 : {
1771 0 : if (arg->IsExplicitlySet())
1772 : {
1773 0 : ReportError(CE_Failure, CPLE_AppDefined,
1774 : "'%s' argument only available when called "
1775 : "from command line",
1776 0 : arg->GetName().c_str());
1777 0 : return false;
1778 : }
1779 : }
1780 : }
1781 6272 : return true;
1782 : });
1783 20161 : }
1784 :
1785 : /************************************************************************/
1786 : /* GDALAlgorithm::~GDALAlgorithm() */
1787 : /************************************************************************/
1788 :
1789 : GDALAlgorithm::~GDALAlgorithm() = default;
1790 :
1791 : /************************************************************************/
1792 : /* GDALAlgorithm::ParseArgument() */
1793 : /************************************************************************/
1794 :
1795 2959 : bool GDALAlgorithm::ParseArgument(
1796 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1797 : std::map<
1798 : GDALAlgorithmArg *,
1799 : std::variant<std::vector<std::string>, std::vector<int>,
1800 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1801 : &inConstructionValues)
1802 : {
1803 2959 : const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1804 2959 : if (arg->IsExplicitlySet() && !isListArg)
1805 : {
1806 : // Hack for "gdal info" to be able to pass an opened raster dataset
1807 : // by "gdal raster info" to the "gdal vector info" algorithm.
1808 3 : if (arg->SkipIfAlreadySet())
1809 : {
1810 1 : arg->SetSkipIfAlreadySet(false);
1811 1 : return true;
1812 : }
1813 :
1814 2 : ReportError(CE_Failure, CPLE_IllegalArg,
1815 : "Argument '%s' has already been specified.", name.c_str());
1816 2 : return false;
1817 : }
1818 :
1819 3022 : if (!arg->GetRepeatedArgAllowed() &&
1820 66 : cpl::contains(inConstructionValues, arg))
1821 : {
1822 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1823 : "Argument '%s' has already been specified.", name.c_str());
1824 1 : return false;
1825 : }
1826 :
1827 2955 : switch (arg->GetType())
1828 : {
1829 304 : case GAAT_BOOLEAN:
1830 : {
1831 304 : if (value.empty() || value == "true")
1832 302 : return arg->Set(true);
1833 2 : else if (value == "false")
1834 1 : return arg->Set(false);
1835 : else
1836 : {
1837 1 : ReportError(
1838 : CE_Failure, CPLE_IllegalArg,
1839 : "Invalid value '%s' for boolean argument '%s'. Should be "
1840 : "'true' or 'false'.",
1841 : value.c_str(), name.c_str());
1842 1 : return false;
1843 : }
1844 : }
1845 :
1846 736 : case GAAT_STRING:
1847 : {
1848 736 : return arg->Set(value);
1849 : }
1850 :
1851 349 : case GAAT_INTEGER:
1852 : {
1853 349 : errno = 0;
1854 349 : char *endptr = nullptr;
1855 349 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1856 348 : if (errno == 0 && endptr &&
1857 697 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1858 : val <= INT_MAX)
1859 : {
1860 346 : return arg->Set(static_cast<int>(val));
1861 : }
1862 : else
1863 : {
1864 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1865 : "Expected integer value for argument '%s', "
1866 : "but got '%s'.",
1867 : name.c_str(), value.c_str());
1868 3 : return false;
1869 : }
1870 : }
1871 :
1872 32 : case GAAT_REAL:
1873 : {
1874 32 : char *endptr = nullptr;
1875 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
1876 32 : if (endptr != value.c_str() + value.size())
1877 : {
1878 1 : ReportError(
1879 : CE_Failure, CPLE_IllegalArg,
1880 : "Expected real value for argument '%s', but got '%s'.",
1881 : name.c_str(), value.c_str());
1882 1 : return false;
1883 : }
1884 31 : return arg->Set(dfValue);
1885 : }
1886 :
1887 518 : case GAAT_DATASET:
1888 : {
1889 518 : return arg->SetDatasetName(value);
1890 : }
1891 :
1892 251 : case GAAT_STRING_LIST:
1893 : {
1894 : const CPLStringList aosTokens(
1895 251 : arg->GetPackedValuesAllowed()
1896 161 : ? CSLTokenizeString2(value.c_str(), ",",
1897 : CSLT_HONOURSTRINGS |
1898 : CSLT_PRESERVEQUOTES)
1899 663 : : CSLAddString(nullptr, value.c_str()));
1900 251 : if (!cpl::contains(inConstructionValues, arg))
1901 : {
1902 227 : inConstructionValues[arg] = std::vector<std::string>();
1903 : }
1904 : auto &valueVector =
1905 251 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
1906 529 : for (const char *v : aosTokens)
1907 : {
1908 278 : valueVector.push_back(v);
1909 : }
1910 251 : break;
1911 : }
1912 :
1913 60 : case GAAT_INTEGER_LIST:
1914 : {
1915 : const CPLStringList aosTokens(
1916 60 : arg->GetPackedValuesAllowed()
1917 60 : ? CSLTokenizeString2(
1918 : value.c_str(), ",",
1919 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1920 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1921 120 : : CSLAddString(nullptr, value.c_str()));
1922 60 : if (!cpl::contains(inConstructionValues, arg))
1923 : {
1924 56 : inConstructionValues[arg] = std::vector<int>();
1925 : }
1926 : auto &valueVector =
1927 60 : std::get<std::vector<int>>(inConstructionValues[arg]);
1928 185 : for (const char *v : aosTokens)
1929 : {
1930 131 : errno = 0;
1931 131 : char *endptr = nullptr;
1932 131 : const auto val = std::strtol(v, &endptr, 10);
1933 131 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
1934 127 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
1935 : {
1936 125 : valueVector.push_back(static_cast<int>(val));
1937 : }
1938 : else
1939 : {
1940 6 : ReportError(
1941 : CE_Failure, CPLE_IllegalArg,
1942 : "Expected list of integer value for argument '%s', "
1943 : "but got '%s'.",
1944 : name.c_str(), value.c_str());
1945 6 : return false;
1946 : }
1947 : }
1948 54 : break;
1949 : }
1950 :
1951 95 : case GAAT_REAL_LIST:
1952 : {
1953 : const CPLStringList aosTokens(
1954 95 : arg->GetPackedValuesAllowed()
1955 95 : ? CSLTokenizeString2(
1956 : value.c_str(), ",",
1957 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1958 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1959 190 : : CSLAddString(nullptr, value.c_str()));
1960 95 : if (!cpl::contains(inConstructionValues, arg))
1961 : {
1962 93 : inConstructionValues[arg] = std::vector<double>();
1963 : }
1964 : auto &valueVector =
1965 95 : std::get<std::vector<double>>(inConstructionValues[arg]);
1966 385 : for (const char *v : aosTokens)
1967 : {
1968 294 : char *endptr = nullptr;
1969 294 : double dfValue = CPLStrtod(v, &endptr);
1970 294 : if (strlen(v) == 0 || endptr != v + strlen(v))
1971 : {
1972 4 : ReportError(
1973 : CE_Failure, CPLE_IllegalArg,
1974 : "Expected list of real value for argument '%s', "
1975 : "but got '%s'.",
1976 : name.c_str(), value.c_str());
1977 4 : return false;
1978 : }
1979 290 : valueVector.push_back(dfValue);
1980 : }
1981 91 : break;
1982 : }
1983 :
1984 610 : case GAAT_DATASET_LIST:
1985 : {
1986 610 : if (!cpl::contains(inConstructionValues, arg))
1987 : {
1988 598 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
1989 : }
1990 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1991 610 : inConstructionValues[arg]);
1992 610 : if (!value.empty() && value[0] == '{' && value.back() == '}')
1993 : {
1994 12 : valueVector.push_back(GDALArgDatasetValue(value));
1995 : }
1996 : else
1997 : {
1998 : const CPLStringList aosTokens(
1999 598 : arg->GetPackedValuesAllowed()
2000 4 : ? CSLTokenizeString2(value.c_str(), ",",
2001 : CSLT_HONOURSTRINGS |
2002 : CSLT_STRIPLEADSPACES)
2003 1200 : : CSLAddString(nullptr, value.c_str()));
2004 1196 : for (const char *v : aosTokens)
2005 : {
2006 598 : valueVector.push_back(GDALArgDatasetValue(v));
2007 : }
2008 : }
2009 610 : break;
2010 : }
2011 : }
2012 :
2013 1006 : return true;
2014 : }
2015 :
2016 : /************************************************************************/
2017 : /* GDALAlgorithm::ParseCommandLineArguments() */
2018 : /************************************************************************/
2019 :
2020 1943 : bool GDALAlgorithm::ParseCommandLineArguments(
2021 : const std::vector<std::string> &args)
2022 : {
2023 1943 : if (m_parsedSubStringAlreadyCalled)
2024 : {
2025 6 : ReportError(CE_Failure, CPLE_AppDefined,
2026 : "ParseCommandLineArguments() can only be called once per "
2027 : "instance.");
2028 6 : return false;
2029 : }
2030 1937 : m_parsedSubStringAlreadyCalled = true;
2031 :
2032 : // AWS like syntax supported too (not advertized)
2033 1937 : if (args.size() == 1 && args[0] == "help")
2034 : {
2035 1 : auto arg = GetArg("help");
2036 1 : assert(arg);
2037 1 : arg->Set(true);
2038 1 : arg->RunActions();
2039 1 : return true;
2040 : }
2041 :
2042 1936 : if (HasSubAlgorithms())
2043 : {
2044 421 : if (args.empty())
2045 : {
2046 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2047 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2048 2 : return false;
2049 : }
2050 419 : if (!args[0].empty() && args[0][0] == '-')
2051 : {
2052 : // go on argument parsing
2053 : }
2054 : else
2055 : {
2056 416 : const auto nCounter = CPLGetErrorCounter();
2057 416 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2058 416 : if (m_selectedSubAlgHolder)
2059 : {
2060 413 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2061 413 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2062 413 : m_referencePath);
2063 413 : m_selectedSubAlg->m_executionForStreamOutput =
2064 413 : m_executionForStreamOutput;
2065 413 : m_selectedSubAlg->m_calledFromCommandLine =
2066 413 : m_calledFromCommandLine;
2067 413 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2068 413 : m_skipValidationInParseCommandLine;
2069 413 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2070 826 : std::vector<std::string>(args.begin() + 1, args.end()));
2071 413 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2072 413 : return bRet;
2073 : }
2074 : else
2075 : {
2076 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2077 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2078 : {
2079 2 : ReportError(CE_Failure, CPLE_AppDefined,
2080 2 : "Unknown command: '%s'", args[0].c_str());
2081 : }
2082 3 : return false;
2083 : }
2084 : }
2085 : }
2086 :
2087 : std::map<
2088 : GDALAlgorithmArg *,
2089 : std::variant<std::vector<std::string>, std::vector<int>,
2090 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2091 3036 : inConstructionValues;
2092 :
2093 3036 : std::vector<std::string> lArgs(args);
2094 1518 : bool helpValueRequested = false;
2095 4482 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2096 : {
2097 3063 : const auto &strArg = lArgs[i];
2098 3063 : GDALAlgorithmArg *arg = nullptr;
2099 3063 : std::string name;
2100 3063 : std::string value;
2101 3063 : bool hasValue = false;
2102 3063 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2103 5 : helpValueRequested = true;
2104 3063 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2105 : {
2106 1962 : const auto equalPos = strArg.find('=');
2107 3924 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2108 1962 : : strArg;
2109 1962 : const std::string nameWithoutDash = name.substr(2);
2110 1962 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2111 2015 : if (m_arbitraryLongNameArgsAllowed &&
2112 2015 : iterArg == m_mapLongNameToArg.end())
2113 : {
2114 16 : GetArg(nameWithoutDash);
2115 16 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2116 : }
2117 1962 : if (iterArg == m_mapLongNameToArg.end())
2118 : {
2119 : const std::string bestCandidate =
2120 27 : GetSuggestionForArgumentName(nameWithoutDash);
2121 27 : if (!bestCandidate.empty())
2122 : {
2123 2 : ReportError(CE_Failure, CPLE_IllegalArg,
2124 : "Option '%s' is unknown. Do you mean '--%s'?",
2125 : name.c_str(), bestCandidate.c_str());
2126 : }
2127 : else
2128 : {
2129 25 : ReportError(CE_Failure, CPLE_IllegalArg,
2130 : "Option '%s' is unknown.", name.c_str());
2131 : }
2132 27 : return false;
2133 : }
2134 1935 : arg = iterArg->second;
2135 1935 : if (equalPos != std::string::npos)
2136 : {
2137 417 : hasValue = true;
2138 417 : value = strArg.substr(equalPos + 1);
2139 : }
2140 : }
2141 1175 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2142 74 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2143 : {
2144 143 : for (size_t j = 1; j < strArg.size(); ++j)
2145 : {
2146 74 : name.clear();
2147 74 : name += strArg[j];
2148 74 : const auto iterArg = m_mapShortNameToArg.find(name);
2149 74 : if (iterArg == m_mapShortNameToArg.end())
2150 : {
2151 5 : const std::string nameWithoutDash = strArg.substr(1);
2152 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2153 10 : m_mapLongNameToArg.end())
2154 : {
2155 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2156 : "Short name option '%s' is unknown. Do you "
2157 : "mean '--%s' (with leading double dash) ?",
2158 : name.c_str(), nameWithoutDash.c_str());
2159 : }
2160 : else
2161 : {
2162 : const std::string bestCandidate =
2163 8 : GetSuggestionForArgumentName(nameWithoutDash);
2164 4 : if (!bestCandidate.empty())
2165 : {
2166 1 : ReportError(
2167 : CE_Failure, CPLE_IllegalArg,
2168 : "Short name option '%s' is unknown. Do you "
2169 : "mean '--%s' (with leading double dash) ?",
2170 : name.c_str(), bestCandidate.c_str());
2171 : }
2172 : else
2173 : {
2174 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2175 : "Short name option '%s' is unknown.",
2176 : name.c_str());
2177 : }
2178 : }
2179 5 : return false;
2180 : }
2181 69 : arg = iterArg->second;
2182 69 : if (strArg.size() > 2)
2183 : {
2184 0 : if (arg->GetType() != GAAT_BOOLEAN)
2185 : {
2186 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2187 : "Invalid argument '%s'. Option '%s' is not "
2188 : "a boolean option.",
2189 : strArg.c_str(), name.c_str());
2190 0 : return false;
2191 : }
2192 :
2193 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2194 0 : return false;
2195 : }
2196 : }
2197 69 : if (strArg.size() > 2)
2198 : {
2199 0 : lArgs.erase(lArgs.begin() + i);
2200 0 : continue;
2201 : }
2202 : }
2203 : else
2204 : {
2205 1027 : ++i;
2206 1027 : continue;
2207 : }
2208 2004 : CPLAssert(arg);
2209 :
2210 2004 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2211 : {
2212 305 : if (!hasValue)
2213 : {
2214 302 : hasValue = true;
2215 302 : value = "true";
2216 : }
2217 : }
2218 :
2219 2004 : if (!hasValue)
2220 : {
2221 1285 : if (i + 1 == lArgs.size())
2222 : {
2223 29 : if (m_parseForAutoCompletion)
2224 : {
2225 23 : lArgs.erase(lArgs.begin() + i);
2226 23 : break;
2227 : }
2228 6 : ReportError(
2229 : CE_Failure, CPLE_IllegalArg,
2230 : "Expected value for argument '%s', but ran short of tokens",
2231 : name.c_str());
2232 6 : return false;
2233 : }
2234 1256 : value = lArgs[i + 1];
2235 1256 : lArgs.erase(lArgs.begin() + i + 1);
2236 : }
2237 :
2238 1975 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2239 38 : return false;
2240 :
2241 1937 : lArgs.erase(lArgs.begin() + i);
2242 : }
2243 :
2244 1442 : if (m_specialActionRequested)
2245 : {
2246 23 : return true;
2247 : }
2248 :
2249 2351 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2250 : {
2251 2322 : for (auto &[arg, value] : inConstructionValues)
2252 : {
2253 956 : if (arg->GetType() == GAAT_STRING_LIST)
2254 : {
2255 222 : if (!arg->Set(std::get<std::vector<std::string>>(
2256 222 : inConstructionValues[arg])))
2257 : {
2258 29 : return false;
2259 : }
2260 : }
2261 734 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2262 : {
2263 51 : if (!arg->Set(
2264 51 : std::get<std::vector<int>>(inConstructionValues[arg])))
2265 : {
2266 3 : return false;
2267 : }
2268 : }
2269 683 : else if (arg->GetType() == GAAT_REAL_LIST)
2270 : {
2271 89 : if (!arg->Set(std::get<std::vector<double>>(
2272 89 : inConstructionValues[arg])))
2273 : {
2274 8 : return false;
2275 : }
2276 : }
2277 594 : else if (arg->GetType() == GAAT_DATASET_LIST)
2278 : {
2279 594 : if (!arg->Set(
2280 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2281 594 : inConstructionValues[arg]))))
2282 : {
2283 1 : return false;
2284 : }
2285 : }
2286 : }
2287 1366 : return true;
2288 1419 : };
2289 :
2290 : // Process positional arguments that have not been set through their
2291 : // option name.
2292 1419 : size_t i = 0;
2293 1419 : size_t iCurPosArg = 0;
2294 :
2295 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2296 1440 : if (m_positionalArgs.size() == 3 &&
2297 22 : (m_positionalArgs[0]->IsRequired() ||
2298 21 : m_positionalArgs[0]->GetMinCount() == 1) &&
2299 40 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2300 27 : (m_positionalArgs[1]->IsRequired() ||
2301 27 : m_positionalArgs[1]->GetMinCount() == 1) &&
2302 : /* Second argument may have several occurrences */
2303 40 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2304 20 : (m_positionalArgs[2]->IsRequired() ||
2305 20 : m_positionalArgs[2]->GetMinCount() == 1) &&
2306 20 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2307 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2308 1449 : !m_positionalArgs[1]->IsExplicitlySet() &&
2309 9 : !m_positionalArgs[2]->IsExplicitlySet())
2310 : {
2311 7 : if (lArgs.size() - i < 3)
2312 : {
2313 1 : ReportError(CE_Failure, CPLE_AppDefined,
2314 : "Not enough positional values.");
2315 1 : return false;
2316 : }
2317 12 : bool ok = ParseArgument(m_positionalArgs[0],
2318 6 : m_positionalArgs[0]->GetName().c_str(),
2319 6 : lArgs[i], inConstructionValues);
2320 6 : if (ok)
2321 : {
2322 5 : ++i;
2323 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2324 : {
2325 12 : ok = ParseArgument(m_positionalArgs[1],
2326 6 : m_positionalArgs[1]->GetName().c_str(),
2327 6 : lArgs[i], inConstructionValues);
2328 : }
2329 : }
2330 6 : if (ok)
2331 : {
2332 10 : ok = ParseArgument(m_positionalArgs[2],
2333 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2334 : inConstructionValues);
2335 5 : ++i;
2336 : }
2337 6 : if (!ok)
2338 : {
2339 3 : ProcessInConstructionValues();
2340 3 : return false;
2341 : }
2342 : }
2343 :
2344 486 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2345 565 : !m_positionalArgs[0]->IsExplicitlySet() &&
2346 2208 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2347 112 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2348 56 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2349 : {
2350 56 : ++iCurPosArg;
2351 : }
2352 :
2353 2358 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2354 : {
2355 950 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2356 953 : while (arg->IsExplicitlySet())
2357 : {
2358 4 : ++iCurPosArg;
2359 4 : if (iCurPosArg == m_positionalArgs.size())
2360 1 : break;
2361 3 : arg = m_positionalArgs[iCurPosArg];
2362 : }
2363 950 : if (iCurPosArg == m_positionalArgs.size())
2364 : {
2365 1 : break;
2366 : }
2367 1464 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2368 515 : arg->GetMinCount() != arg->GetMaxCount())
2369 : {
2370 102 : if (iCurPosArg == 0)
2371 : {
2372 66 : size_t nCountAtEnd = 0;
2373 91 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2374 : {
2375 27 : const auto *otherArg = m_positionalArgs[j];
2376 27 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2377 : {
2378 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2379 : {
2380 2 : ReportError(
2381 : CE_Failure, CPLE_AppDefined,
2382 : "Ambiguity in definition of positional "
2383 : "argument "
2384 : "'%s' given it has a varying number of values, "
2385 : "but follows argument '%s' which also has a "
2386 : "varying number of values",
2387 1 : otherArg->GetName().c_str(),
2388 1 : arg->GetName().c_str());
2389 1 : ProcessInConstructionValues();
2390 1 : return false;
2391 : }
2392 3 : nCountAtEnd += otherArg->GetMinCount();
2393 : }
2394 : else
2395 : {
2396 23 : if (!otherArg->IsRequired())
2397 : {
2398 2 : ReportError(
2399 : CE_Failure, CPLE_AppDefined,
2400 : "Ambiguity in definition of positional "
2401 : "argument "
2402 : "'%s', given it is not required but follows "
2403 : "argument '%s' which has a varying number of "
2404 : "values",
2405 1 : otherArg->GetName().c_str(),
2406 1 : arg->GetName().c_str());
2407 1 : ProcessInConstructionValues();
2408 1 : return false;
2409 : }
2410 22 : nCountAtEnd++;
2411 : }
2412 : }
2413 64 : if (lArgs.size() < nCountAtEnd)
2414 : {
2415 1 : ReportError(CE_Failure, CPLE_AppDefined,
2416 : "Not enough positional values.");
2417 1 : ProcessInConstructionValues();
2418 1 : return false;
2419 : }
2420 133 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2421 : {
2422 70 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2423 : inConstructionValues))
2424 : {
2425 0 : ProcessInConstructionValues();
2426 0 : return false;
2427 : }
2428 : }
2429 : }
2430 36 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2431 : {
2432 82 : for (; i < lArgs.size(); ++i)
2433 : {
2434 47 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2435 : inConstructionValues))
2436 : {
2437 0 : ProcessInConstructionValues();
2438 0 : return false;
2439 : }
2440 : }
2441 : }
2442 : else
2443 : {
2444 1 : ReportError(CE_Failure, CPLE_AppDefined,
2445 : "Ambiguity in definition of positional arguments: "
2446 : "arguments with varying number of values must be "
2447 : "first or last one.");
2448 1 : return false;
2449 : }
2450 : }
2451 : else
2452 : {
2453 847 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2454 : {
2455 1 : ReportError(CE_Failure, CPLE_AppDefined,
2456 : "Not enough positional values.");
2457 1 : return false;
2458 : }
2459 846 : const size_t iMax = i + arg->GetMaxCount();
2460 1695 : for (; i < iMax; ++i)
2461 : {
2462 850 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2463 : inConstructionValues))
2464 : {
2465 1 : ProcessInConstructionValues();
2466 1 : return false;
2467 : }
2468 : }
2469 : }
2470 943 : ++iCurPosArg;
2471 : }
2472 :
2473 1409 : if (i < lArgs.size())
2474 : {
2475 21 : ReportError(CE_Failure, CPLE_AppDefined,
2476 : "Positional values starting at '%s' are not expected.",
2477 21 : lArgs[i].c_str());
2478 21 : return false;
2479 : }
2480 :
2481 1388 : if (!ProcessInConstructionValues())
2482 : {
2483 28 : return false;
2484 : }
2485 :
2486 : // Skip to first unset positional argument.
2487 2349 : while (iCurPosArg < m_positionalArgs.size() &&
2488 532 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2489 : {
2490 457 : ++iCurPosArg;
2491 : }
2492 : // Check if this positional argument is required.
2493 1434 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2494 74 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2495 47 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2496 27 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2497 : {
2498 65 : ReportError(CE_Failure, CPLE_AppDefined,
2499 : "Positional arguments starting at '%s' have not been "
2500 : "specified.",
2501 65 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2502 65 : return false;
2503 : }
2504 :
2505 1295 : if (m_calledFromCommandLine)
2506 : {
2507 4762 : for (auto &arg : m_args)
2508 : {
2509 6280 : if (arg->IsExplicitlySet() &&
2510 1020 : ((arg->GetType() == GAAT_STRING &&
2511 1017 : arg->Get<std::string>() == "?") ||
2512 935 : (arg->GetType() == GAAT_STRING_LIST &&
2513 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2514 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2515 : {
2516 : {
2517 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2518 5 : ValidateArguments();
2519 : }
2520 :
2521 5 : auto choices = arg->GetChoices();
2522 5 : if (choices.empty())
2523 2 : choices = arg->GetAutoCompleteChoices(std::string());
2524 5 : if (!choices.empty())
2525 : {
2526 5 : if (choices.size() == 1)
2527 : {
2528 4 : ReportError(
2529 : CE_Failure, CPLE_AppDefined,
2530 : "Single potential value for argument '%s' is '%s'",
2531 4 : arg->GetName().c_str(), choices.front().c_str());
2532 : }
2533 : else
2534 : {
2535 6 : std::string msg("Potential values for argument '");
2536 3 : msg += arg->GetName();
2537 3 : msg += "' are:";
2538 45 : for (const auto &v : choices)
2539 : {
2540 42 : msg += "\n- ";
2541 42 : msg += v;
2542 : }
2543 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2544 : msg.c_str());
2545 : }
2546 5 : return false;
2547 : }
2548 : }
2549 : }
2550 : }
2551 :
2552 1290 : return m_skipValidationInParseCommandLine || ValidateArguments();
2553 : }
2554 :
2555 : /************************************************************************/
2556 : /* GDALAlgorithm::ReportError() */
2557 : /************************************************************************/
2558 :
2559 : //! @cond Doxygen_Suppress
2560 807 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2561 : const char *fmt, ...) const
2562 : {
2563 : va_list args;
2564 807 : va_start(args, fmt);
2565 807 : CPLError(eErrClass, err_no, "%s",
2566 807 : std::string(m_name)
2567 807 : .append(": ")
2568 1614 : .append(CPLString().vPrintf(fmt, args))
2569 : .c_str());
2570 807 : va_end(args);
2571 807 : }
2572 :
2573 : //! @endcond
2574 :
2575 : /************************************************************************/
2576 : /* GDALAlgorithm::ProcessDatasetArg() */
2577 : /************************************************************************/
2578 :
2579 8939 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2580 : GDALAlgorithm *algForOutput)
2581 : {
2582 8939 : bool ret = true;
2583 :
2584 8939 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2585 8939 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2586 8939 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2587 8939 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2588 : const bool overwrite =
2589 14936 : (arg->IsOutput() && overwriteArg &&
2590 14936 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2591 8939 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2592 17878 : auto &val = [arg]() -> GDALArgDatasetValue &
2593 : {
2594 8939 : if (arg->GetType() == GAAT_DATASET_LIST)
2595 4979 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2596 : else
2597 3960 : return arg->Get<GDALArgDatasetValue>();
2598 8939 : }();
2599 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2600 14030 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2601 14038 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2602 8 : !overwrite;
2603 8939 : if (!val.GetDatasetRef() && !val.IsNameSet())
2604 : {
2605 3 : ReportError(CE_Failure, CPLE_AppDefined,
2606 : "Argument '%s' has no dataset object or dataset name.",
2607 3 : arg->GetName().c_str());
2608 3 : ret = false;
2609 : }
2610 8936 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2611 : {
2612 1 : return false;
2613 : }
2614 199 : else if (m_inputDatasetCanBeOmitted &&
2615 9134 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2616 8 : !arg->IsOutput())
2617 : {
2618 8 : return true;
2619 : }
2620 13054 : else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2621 4127 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2622 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2623 : {
2624 1241 : int flags = arg->GetDatasetType();
2625 1241 : bool assignToOutputArg = false;
2626 :
2627 : // Check if input and output parameters point to the same
2628 : // filename (for vector datasets)
2629 2305 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2630 2305 : outputArg && outputArg->GetType() == GAAT_DATASET)
2631 : {
2632 60 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2633 117 : if (!outputVal.GetDatasetRef() &&
2634 117 : outputVal.GetName() == val.GetName() &&
2635 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2636 : {
2637 2 : assignToOutputArg = true;
2638 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2639 : }
2640 58 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2641 : {
2642 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2643 : }
2644 : }
2645 :
2646 1241 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2647 1173 : flags |= GDAL_OF_VERBOSE_ERROR;
2648 1241 : if ((arg == outputArg || !outputArg) && update)
2649 70 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2650 :
2651 1241 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2652 : const bool readOnly =
2653 1283 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2654 42 : readOnlyArg->Get<bool>());
2655 1241 : if (readOnly)
2656 12 : flags &= ~GDAL_OF_UPDATE;
2657 :
2658 2482 : CPLStringList aosOpenOptions;
2659 2482 : CPLStringList aosAllowedDrivers;
2660 1241 : if (arg->IsInput())
2661 : {
2662 1241 : if (arg == outputArg)
2663 : {
2664 68 : if (update && !overwrite)
2665 : {
2666 68 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2667 68 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2668 16 : aosOpenOptions = CPLStringList(
2669 16 : ooArg->Get<std::vector<std::string>>());
2670 : }
2671 : }
2672 : else
2673 : {
2674 1173 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2675 1173 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2676 : aosOpenOptions =
2677 1129 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2678 :
2679 1173 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2680 1173 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2681 : aosAllowedDrivers =
2682 1087 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2683 : }
2684 : }
2685 :
2686 2482 : std::string osDatasetName = val.GetName();
2687 1241 : if (!m_referencePath.empty())
2688 : {
2689 42 : osDatasetName = GDALDataset::BuildFilename(
2690 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2691 : }
2692 1241 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2693 0 : osDatasetName = "/vsistdin/";
2694 :
2695 : // Handle special case of overview delete in GTiff which would fail
2696 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2697 126 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2698 1369 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2699 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2700 : {
2701 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2702 : GDALDriverH hDrv =
2703 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2704 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2705 : {
2706 : // Cleaning does not break COG layout
2707 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2708 : }
2709 : }
2710 :
2711 1241 : auto oIter = m_oMapDatasetNameToDataset.find(osDatasetName.c_str());
2712 : GDALDataset *poDS;
2713 : {
2714 : // The PostGISRaster may emit an error message, that is not
2715 : // relevant, if it is the vector driver that was intended
2716 1241 : std::unique_ptr<CPLErrorStateBackuper> poBackuper;
2717 1241 : if (cpl::starts_with(osDatasetName, "PG:") &&
2718 0 : (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
2719 : {
2720 0 : poBackuper = std::make_unique<CPLErrorStateBackuper>(
2721 0 : CPLQuietErrorHandler);
2722 : }
2723 :
2724 1241 : CPL_IGNORE_RET_VAL(poBackuper);
2725 1241 : poDS = oIter != m_oMapDatasetNameToDataset.end()
2726 1241 : ? oIter->second
2727 1238 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2728 1238 : aosAllowedDrivers.List(),
2729 1238 : aosOpenOptions.List());
2730 :
2731 : // Retry with PostGIS vector driver
2732 56 : if (!poDS && poBackuper &&
2733 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2734 1297 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2735 : {
2736 0 : poBackuper.reset();
2737 0 : poDS = GDALDataset::Open(
2738 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2739 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2740 : }
2741 : }
2742 :
2743 1241 : if (poDS)
2744 : {
2745 1185 : if (oIter != m_oMapDatasetNameToDataset.end())
2746 : {
2747 3 : if (arg->GetType() == GAAT_DATASET)
2748 3 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2749 3 : poDS->Reference();
2750 3 : m_oMapDatasetNameToDataset.erase(oIter);
2751 : }
2752 :
2753 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2754 : // where the PG: dataset will be first opened with the PostGISRaster
2755 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2756 1590 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2757 1668 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2758 78 : aosOpenOptions.empty())
2759 : {
2760 74 : auto poDrv = poDS->GetDriver();
2761 74 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2762 : {
2763 : // Retry with PostgreSQL (vector) driver
2764 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2765 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2766 0 : if (poTmpDS)
2767 : {
2768 0 : poDS->ReleaseRef();
2769 0 : poDS = poTmpDS.release();
2770 : }
2771 : }
2772 : }
2773 :
2774 1185 : if (assignToOutputArg)
2775 : {
2776 : // Avoid opening twice the same datasource if it is both
2777 : // the input and output.
2778 : // Known to cause problems with at least FGdb, SQLite
2779 : // and GPKG drivers. See #4270
2780 : // Restrict to those 3 drivers. For example it is known
2781 : // to break with the PG driver due to the way it
2782 : // manages transactions.
2783 2 : auto poDriver = poDS->GetDriver();
2784 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2785 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
2786 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
2787 : {
2788 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2789 : }
2790 : }
2791 1185 : val.SetDatasetOpenedByAlgorithm();
2792 1185 : val.Set(poDS);
2793 1185 : poDS->ReleaseRef();
2794 : }
2795 : else
2796 : {
2797 56 : ret = false;
2798 : }
2799 : }
2800 :
2801 : // Deal with overwriting the output dataset
2802 8930 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2803 : {
2804 2886 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2805 : const bool hasAppendArg =
2806 2886 : appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2807 2886 : const bool append = (hasAppendArg && appendArg->Get<bool>());
2808 2886 : if (!append)
2809 : {
2810 : // If outputting to MEM, do not try to erase a real file of the same name!
2811 : const auto outputFormatArg =
2812 2878 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2813 8604 : if (!(outputFormatArg &&
2814 2863 : outputFormatArg->GetType() == GAAT_STRING &&
2815 2863 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2816 1838 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2817 962 : "stream") ||
2818 962 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2819 : "Memory"))))
2820 : {
2821 977 : const char *pszType = "";
2822 977 : GDALDriver *poDriver = nullptr;
2823 1911 : if (!val.GetName().empty() &&
2824 934 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
2825 : &poDriver))
2826 : {
2827 71 : if (!overwrite)
2828 : {
2829 62 : std::string options;
2830 31 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
2831 : {
2832 8 : options += "--";
2833 8 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
2834 : }
2835 31 : if (hasAppendArg)
2836 : {
2837 22 : if (!options.empty())
2838 8 : options += '/';
2839 22 : options += "--";
2840 22 : options += GDAL_ARG_NAME_APPEND;
2841 : }
2842 31 : if (hasUpdateArg)
2843 : {
2844 12 : if (!options.empty())
2845 9 : options += '/';
2846 12 : options += "--";
2847 12 : options += GDAL_ARG_NAME_UPDATE;
2848 : }
2849 :
2850 31 : if (poDriver)
2851 : {
2852 62 : const char *pszPrefix = poDriver->GetMetadataItem(
2853 31 : GDAL_DMD_CONNECTION_PREFIX);
2854 31 : if (pszPrefix &&
2855 0 : STARTS_WITH_CI(val.GetName().c_str(),
2856 : pszPrefix))
2857 : {
2858 0 : bool bExists = false;
2859 : {
2860 : CPLErrorStateBackuper oBackuper(
2861 0 : CPLQuietErrorHandler);
2862 0 : bExists = std::unique_ptr<GDALDataset>(
2863 : GDALDataset::Open(
2864 0 : val.GetName().c_str())) !=
2865 : nullptr;
2866 : }
2867 0 : if (bExists)
2868 : {
2869 0 : if (!options.empty())
2870 0 : options = " You may specify the " +
2871 0 : options + " option.";
2872 0 : ReportError(CE_Failure, CPLE_AppDefined,
2873 : "%s '%s' already exists.%s",
2874 0 : pszType, val.GetName().c_str(),
2875 : options.c_str());
2876 0 : return false;
2877 : }
2878 :
2879 0 : return true;
2880 : }
2881 : }
2882 :
2883 31 : if (!options.empty())
2884 25 : options = '/' + options;
2885 62 : ReportError(
2886 : CE_Failure, CPLE_AppDefined,
2887 : "%s '%s' already exists. You may specify the "
2888 : "--overwrite%s option.",
2889 31 : pszType, val.GetName().c_str(), options.c_str());
2890 31 : return false;
2891 : }
2892 40 : else if (EQUAL(pszType, "File"))
2893 : {
2894 1 : VSIUnlink(val.GetName().c_str());
2895 : }
2896 39 : else if (EQUAL(pszType, "Directory"))
2897 : {
2898 : // We don't want the user to accidentally erase a non-GDAL dataset
2899 1 : ReportError(CE_Failure, CPLE_AppDefined,
2900 : "Directory '%s' already exists, but is not "
2901 : "recognized as a valid GDAL dataset. "
2902 : "Please manually delete it before retrying",
2903 1 : val.GetName().c_str());
2904 1 : return false;
2905 : }
2906 38 : else if (poDriver)
2907 : {
2908 76 : CPLStringList aosDrivers;
2909 38 : aosDrivers.AddString(poDriver->GetDescription());
2910 76 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2911 38 : GDALDriver::QuietDelete(val.GetName().c_str(),
2912 38 : aosDrivers.List());
2913 : }
2914 : }
2915 : }
2916 : }
2917 : }
2918 :
2919 : // If outputting to stdout, automatically turn off progress bar
2920 8898 : if (arg == outputArg && val.GetName() == "/vsistdout/")
2921 : {
2922 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
2923 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
2924 5 : quietArg->Set(true);
2925 : }
2926 :
2927 8898 : return ret;
2928 : }
2929 :
2930 : /************************************************************************/
2931 : /* GDALAlgorithm::ValidateArguments() */
2932 : /************************************************************************/
2933 :
2934 6276 : bool GDALAlgorithm::ValidateArguments()
2935 : {
2936 6276 : if (m_selectedSubAlg)
2937 3 : return m_selectedSubAlg->ValidateArguments();
2938 :
2939 6273 : if (m_specialActionRequested)
2940 1 : return true;
2941 :
2942 6272 : m_arbitraryLongNameArgsAllowed = false;
2943 :
2944 : // If only --output=format=MEM/stream is specified and not --output,
2945 : // then set empty name for --output.
2946 6272 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2947 6272 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2948 3747 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
2949 2539 : !outputArg->IsExplicitlySet() &&
2950 353 : outputFormatArg->GetType() == GAAT_STRING &&
2951 353 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2952 583 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
2953 10347 : outputArg->GetType() == GAAT_DATASET &&
2954 328 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
2955 : {
2956 328 : outputArg->Get<GDALArgDatasetValue>().Set("");
2957 : }
2958 :
2959 : // The method may emit several errors if several constraints are not met.
2960 6272 : bool ret = true;
2961 6272 : std::map<std::string, std::string> mutualExclusionGroupUsed;
2962 118089 : for (auto &arg : m_args)
2963 : {
2964 : // Check mutually exclusive arguments
2965 111817 : if (arg->IsExplicitlySet())
2966 : {
2967 18133 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
2968 18133 : if (!mutualExclusionGroup.empty())
2969 : {
2970 : auto oIter =
2971 697 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
2972 697 : if (oIter != mutualExclusionGroupUsed.end())
2973 : {
2974 11 : ret = false;
2975 22 : ReportError(
2976 : CE_Failure, CPLE_AppDefined,
2977 : "Argument '%s' is mutually exclusive with '%s'.",
2978 22 : arg->GetName().c_str(), oIter->second.c_str());
2979 : }
2980 : else
2981 : {
2982 686 : mutualExclusionGroupUsed[mutualExclusionGroup] =
2983 1372 : arg->GetName();
2984 : }
2985 : }
2986 : }
2987 :
2988 111969 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
2989 152 : !arg->HasDefaultValue())
2990 : {
2991 152 : bool emitError = true;
2992 152 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
2993 152 : if (!mutualExclusionGroup.empty())
2994 : {
2995 1765 : for (const auto &otherArg : m_args)
2996 : {
2997 1751 : if (otherArg->GetMutualExclusionGroup() ==
2998 1856 : mutualExclusionGroup &&
2999 105 : otherArg->IsExplicitlySet())
3000 : {
3001 74 : emitError = false;
3002 74 : break;
3003 : }
3004 : }
3005 : }
3006 226 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3007 46 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3008 56 : (arg->GetType() == GAAT_DATASET ||
3009 28 : arg->GetType() == GAAT_DATASET_LIST)))
3010 : {
3011 50 : ReportError(CE_Failure, CPLE_AppDefined,
3012 : "Required argument '%s' has not been specified.",
3013 50 : arg->GetName().c_str());
3014 50 : ret = false;
3015 : }
3016 : }
3017 111665 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3018 : {
3019 3960 : if (!ProcessDatasetArg(arg.get(), this))
3020 46 : ret = false;
3021 : }
3022 :
3023 117001 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
3024 5184 : arg->AutoOpenDataset())
3025 : {
3026 4717 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3027 4717 : if (listVal.size() == 1)
3028 : {
3029 4715 : if (!ProcessDatasetArg(arg.get(), this))
3030 36 : ret = false;
3031 : }
3032 : else
3033 : {
3034 6 : for (auto &val : listVal)
3035 : {
3036 4 : if (!val.GetDatasetRef() && val.GetName().empty())
3037 : {
3038 0 : ReportError(CE_Failure, CPLE_AppDefined,
3039 : "Argument '%s' has no dataset object or "
3040 : "dataset name.",
3041 0 : arg->GetName().c_str());
3042 0 : ret = false;
3043 : }
3044 4 : else if (!val.GetDatasetRef())
3045 : {
3046 : int flags =
3047 4 : arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3048 :
3049 8 : CPLStringList aosOpenOptions;
3050 8 : CPLStringList aosAllowedDrivers;
3051 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3052 : {
3053 : const auto ooArg =
3054 4 : GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3055 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3056 : {
3057 4 : aosOpenOptions = CPLStringList(
3058 4 : ooArg->Get<std::vector<std::string>>());
3059 : }
3060 :
3061 : const auto ifArg =
3062 4 : GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3063 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3064 : {
3065 4 : aosAllowedDrivers = CPLStringList(
3066 4 : ifArg->Get<std::vector<std::string>>());
3067 : }
3068 :
3069 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3070 0 : if (updateArg &&
3071 4 : updateArg->GetType() == GAAT_BOOLEAN &&
3072 0 : updateArg->Get<bool>())
3073 : {
3074 0 : flags |= GDAL_OF_UPDATE;
3075 : }
3076 : }
3077 :
3078 : auto poDS = std::unique_ptr<GDALDataset>(
3079 4 : GDALDataset::Open(val.GetName().c_str(), flags,
3080 4 : aosAllowedDrivers.List(),
3081 12 : aosOpenOptions.List()));
3082 4 : if (poDS)
3083 : {
3084 3 : val.Set(std::move(poDS));
3085 : }
3086 : else
3087 : {
3088 1 : ret = false;
3089 : }
3090 : }
3091 : }
3092 : }
3093 : }
3094 :
3095 111817 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3096 : {
3097 6 : ret = false;
3098 : }
3099 : }
3100 :
3101 26689 : for (const auto &f : m_validationActions)
3102 : {
3103 20417 : if (!f())
3104 73 : ret = false;
3105 : }
3106 :
3107 6272 : return ret;
3108 : }
3109 :
3110 : /************************************************************************/
3111 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3112 : /************************************************************************/
3113 :
3114 : std::unique_ptr<GDALAlgorithm>
3115 9464 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3116 : bool suggestionAllowed) const
3117 : {
3118 9464 : auto ret = m_subAlgRegistry.Instantiate(name);
3119 18928 : auto childCallPath = m_callPath;
3120 9464 : childCallPath.push_back(name);
3121 9464 : if (!ret)
3122 : {
3123 766 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3124 766 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3125 : }
3126 9464 : if (ret)
3127 : {
3128 9334 : ret->SetCallPath(childCallPath);
3129 : }
3130 130 : else if (suggestionAllowed)
3131 : {
3132 52 : std::string bestCandidate;
3133 26 : size_t bestDistance = std::numeric_limits<size_t>::max();
3134 448 : for (const std::string &candidate : GetSubAlgorithmNames())
3135 : {
3136 : const size_t distance =
3137 422 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3138 : /* transpositionAllowed = */ true);
3139 422 : if (distance < bestDistance)
3140 : {
3141 65 : bestCandidate = candidate;
3142 65 : bestDistance = distance;
3143 : }
3144 357 : else if (distance == bestDistance)
3145 : {
3146 43 : bestCandidate.clear();
3147 : }
3148 : }
3149 26 : if (!bestCandidate.empty() && bestDistance <= 2)
3150 : {
3151 4 : CPLError(CE_Failure, CPLE_AppDefined,
3152 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3153 : name.c_str(), bestCandidate.c_str());
3154 : }
3155 : }
3156 18928 : return ret;
3157 : }
3158 :
3159 : /************************************************************************/
3160 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3161 : /************************************************************************/
3162 :
3163 : std::string
3164 37 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3165 : {
3166 37 : if (osName.size() >= 3)
3167 : {
3168 34 : std::string bestCandidate;
3169 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3170 689 : for (const auto &[key, value] : m_mapLongNameToArg)
3171 : {
3172 655 : CPL_IGNORE_RET_VAL(value);
3173 655 : const size_t distance = CPLLevenshteinDistance(
3174 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3175 655 : if (distance < bestDistance)
3176 : {
3177 88 : bestCandidate = key;
3178 88 : bestDistance = distance;
3179 : }
3180 567 : else if (distance == bestDistance)
3181 : {
3182 77 : bestCandidate.clear();
3183 : }
3184 : }
3185 49 : if (!bestCandidate.empty() &&
3186 15 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3187 : {
3188 5 : return bestCandidate;
3189 : }
3190 : }
3191 32 : return std::string();
3192 : }
3193 :
3194 : /************************************************************************/
3195 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3196 : /************************************************************************/
3197 :
3198 : /* static */
3199 22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3200 : {
3201 66 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3202 66 : osName == GDAL_ARG_NAME_OVERWRITE ||
3203 44 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3204 : }
3205 :
3206 : /************************************************************************/
3207 : /* GDALAlgorithm::HasOutputString() */
3208 : /************************************************************************/
3209 :
3210 67 : bool GDALAlgorithm::HasOutputString() const
3211 : {
3212 67 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3213 67 : return outputStringArg && outputStringArg->IsOutput();
3214 : }
3215 :
3216 : /************************************************************************/
3217 : /* GDALAlgorithm::GetArg() */
3218 : /************************************************************************/
3219 :
3220 430708 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3221 : bool suggestionAllowed, bool isConst)
3222 : {
3223 430708 : const auto nPos = osName.find_first_not_of('-');
3224 430708 : if (nPos == std::string::npos)
3225 23 : return nullptr;
3226 861370 : std::string osKey = osName.substr(nPos);
3227 : {
3228 430685 : const auto oIter = m_mapLongNameToArg.find(osKey);
3229 430685 : if (oIter != m_mapLongNameToArg.end())
3230 404606 : return oIter->second;
3231 : }
3232 : {
3233 26079 : const auto oIter = m_mapShortNameToArg.find(osKey);
3234 26079 : if (oIter != m_mapShortNameToArg.end())
3235 6 : return oIter->second;
3236 : }
3237 :
3238 26073 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3239 : {
3240 22 : const auto nDotPos = osKey.find('.');
3241 : const std::string osKeyEnd =
3242 22 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3243 22 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3244 : {
3245 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3246 0 : std::make_unique<bool>());
3247 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3248 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3249 0 : .SetUserProvided();
3250 : }
3251 : else
3252 : {
3253 44 : const std::string osKeyInit = osKey;
3254 22 : if (osKey == "oo")
3255 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3256 22 : else if (osKey == "co")
3257 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3258 22 : else if (osKey == "of")
3259 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3260 22 : else if (osKey == "if")
3261 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3262 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3263 22 : std::make_unique<std::string>());
3264 : auto &arg =
3265 44 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3266 44 : m_arbitraryLongNameArgsValuesStr.back().get())
3267 22 : .SetUserProvided();
3268 22 : if (osKey != osKeyInit)
3269 0 : arg.AddAlias(osKeyInit);
3270 : }
3271 22 : const auto oIter = m_mapLongNameToArg.find(osKey);
3272 22 : CPLAssert(oIter != m_mapLongNameToArg.end());
3273 22 : return oIter->second;
3274 : }
3275 :
3276 26051 : if (suggestionAllowed)
3277 : {
3278 12 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3279 6 : if (!bestCandidate.empty())
3280 : {
3281 2 : CPLError(CE_Failure, CPLE_AppDefined,
3282 : "Argument '%s' is unknown. Do you mean '%s'?",
3283 : osName.c_str(), bestCandidate.c_str());
3284 : }
3285 : }
3286 :
3287 26051 : return nullptr;
3288 : }
3289 :
3290 : /************************************************************************/
3291 : /* GDALAlgorithm::AddAliasFor() */
3292 : /************************************************************************/
3293 :
3294 : //! @cond Doxygen_Suppress
3295 72160 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3296 : const std::string &alias)
3297 : {
3298 72160 : if (cpl::contains(m_mapLongNameToArg, alias))
3299 : {
3300 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3301 : alias.c_str());
3302 : }
3303 : else
3304 : {
3305 72159 : m_mapLongNameToArg[alias] = arg;
3306 : }
3307 72160 : }
3308 :
3309 : //! @endcond
3310 :
3311 : /************************************************************************/
3312 : /* GDALAlgorithm::AddShortNameAliasFor() */
3313 : /************************************************************************/
3314 :
3315 : //! @cond Doxygen_Suppress
3316 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3317 : char shortNameAlias)
3318 : {
3319 96 : std::string alias;
3320 48 : alias += shortNameAlias;
3321 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3322 : {
3323 0 : ReportError(CE_Failure, CPLE_AppDefined,
3324 : "Short name '%s' already declared.", alias.c_str());
3325 : }
3326 : else
3327 : {
3328 48 : m_mapShortNameToArg[alias] = arg;
3329 : }
3330 48 : }
3331 :
3332 : //! @endcond
3333 :
3334 : /************************************************************************/
3335 : /* GDALAlgorithm::SetPositional() */
3336 : /************************************************************************/
3337 :
3338 : //! @cond Doxygen_Suppress
3339 20522 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3340 : {
3341 20522 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3342 : arg) == m_positionalArgs.end());
3343 20522 : m_positionalArgs.push_back(arg);
3344 20522 : }
3345 :
3346 : //! @endcond
3347 :
3348 : /************************************************************************/
3349 : /* GDALAlgorithm::HasSubAlgorithms() */
3350 : /************************************************************************/
3351 :
3352 11955 : bool GDALAlgorithm::HasSubAlgorithms() const
3353 : {
3354 11955 : if (!m_subAlgRegistry.empty())
3355 3062 : return true;
3356 8893 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3357 17786 : .GetDeclaredSubAlgorithmNames(m_callPath)
3358 8893 : .empty();
3359 : }
3360 :
3361 : /************************************************************************/
3362 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3363 : /************************************************************************/
3364 :
3365 1270 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3366 : {
3367 1270 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3368 1270 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3369 2540 : .GetDeclaredSubAlgorithmNames(m_callPath);
3370 1270 : ret.insert(ret.end(), other.begin(), other.end());
3371 1270 : if (!other.empty())
3372 369 : std::sort(ret.begin(), ret.end());
3373 2540 : return ret;
3374 : }
3375 :
3376 : /************************************************************************/
3377 : /* GDALAlgorithm::AddArg() */
3378 : /************************************************************************/
3379 :
3380 : GDALInConstructionAlgorithmArg &
3381 290299 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3382 : {
3383 290299 : auto argRaw = arg.get();
3384 290299 : const auto &longName = argRaw->GetName();
3385 290299 : if (!longName.empty())
3386 : {
3387 290286 : if (longName[0] == '-')
3388 : {
3389 1 : ReportError(CE_Failure, CPLE_AppDefined,
3390 : "Long name '%s' should not start with '-'",
3391 : longName.c_str());
3392 : }
3393 290286 : if (longName.find('=') != std::string::npos)
3394 : {
3395 1 : ReportError(CE_Failure, CPLE_AppDefined,
3396 : "Long name '%s' should not contain a '=' character",
3397 : longName.c_str());
3398 : }
3399 290286 : if (cpl::contains(m_mapLongNameToArg, longName))
3400 : {
3401 1 : ReportError(CE_Failure, CPLE_AppDefined,
3402 : "Long name '%s' already declared", longName.c_str());
3403 : }
3404 290286 : m_mapLongNameToArg[longName] = argRaw;
3405 : }
3406 290299 : const auto &shortName = argRaw->GetShortName();
3407 290299 : if (!shortName.empty())
3408 : {
3409 141102 : if (shortName.size() != 1 ||
3410 70551 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3411 64 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3412 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3413 : {
3414 1 : ReportError(CE_Failure, CPLE_AppDefined,
3415 : "Short name '%s' should be a single letter or digit",
3416 : shortName.c_str());
3417 : }
3418 70551 : if (cpl::contains(m_mapShortNameToArg, shortName))
3419 : {
3420 1 : ReportError(CE_Failure, CPLE_AppDefined,
3421 : "Short name '%s' already declared", shortName.c_str());
3422 : }
3423 70551 : m_mapShortNameToArg[shortName] = argRaw;
3424 : }
3425 290299 : m_args.emplace_back(std::move(arg));
3426 : return *(
3427 290299 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3428 : }
3429 :
3430 : GDALInConstructionAlgorithmArg &
3431 129254 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3432 : const std::string &helpMessage, bool *pValue)
3433 : {
3434 129254 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3435 : this,
3436 258508 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3437 258508 : pValue));
3438 : }
3439 :
3440 : GDALInConstructionAlgorithmArg &
3441 46567 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3442 : const std::string &helpMessage, std::string *pValue)
3443 : {
3444 46567 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3445 : this,
3446 93134 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3447 93134 : pValue));
3448 : }
3449 :
3450 : GDALInConstructionAlgorithmArg &
3451 11712 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3452 : const std::string &helpMessage, int *pValue)
3453 : {
3454 11712 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3455 : this,
3456 23424 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3457 23424 : pValue));
3458 : }
3459 :
3460 : GDALInConstructionAlgorithmArg &
3461 10041 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3462 : const std::string &helpMessage, double *pValue)
3463 : {
3464 10041 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3465 : this,
3466 20082 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3467 20082 : pValue));
3468 : }
3469 :
3470 : GDALInConstructionAlgorithmArg &
3471 10948 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3472 : const std::string &helpMessage,
3473 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3474 : {
3475 21896 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3476 : this,
3477 21896 : GDALAlgorithmArgDecl(longName, chShortName,
3478 : helpMessage, GAAT_DATASET),
3479 10948 : pValue))
3480 10948 : .SetDatasetType(type);
3481 10948 : pValue->SetOwnerArgument(&arg);
3482 10948 : return arg;
3483 : }
3484 :
3485 : GDALInConstructionAlgorithmArg &
3486 61386 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3487 : const std::string &helpMessage,
3488 : std::vector<std::string> *pValue)
3489 : {
3490 61386 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3491 : this,
3492 122772 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3493 : GAAT_STRING_LIST),
3494 122772 : pValue));
3495 : }
3496 :
3497 : GDALInConstructionAlgorithmArg &
3498 2154 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3499 : const std::string &helpMessage, std::vector<int> *pValue)
3500 : {
3501 2154 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3502 : this,
3503 4308 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3504 : GAAT_INTEGER_LIST),
3505 4308 : pValue));
3506 : }
3507 :
3508 : GDALInConstructionAlgorithmArg &
3509 5041 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3510 : const std::string &helpMessage,
3511 : std::vector<double> *pValue)
3512 : {
3513 5041 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3514 : this,
3515 10082 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3516 : GAAT_REAL_LIST),
3517 10082 : pValue));
3518 : }
3519 :
3520 : GDALInConstructionAlgorithmArg &
3521 13196 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3522 : const std::string &helpMessage,
3523 : std::vector<GDALArgDatasetValue> *pValue,
3524 : GDALArgDatasetType type)
3525 : {
3526 26392 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3527 : this,
3528 26392 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3529 : GAAT_DATASET_LIST),
3530 13196 : pValue))
3531 26392 : .SetDatasetType(type);
3532 : }
3533 :
3534 : /************************************************************************/
3535 : /* MsgOrDefault() */
3536 : /************************************************************************/
3537 :
3538 97873 : inline const char *MsgOrDefault(const char *helpMessage,
3539 : const char *defaultMessage)
3540 : {
3541 97873 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3542 : }
3543 :
3544 : /************************************************************************/
3545 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3546 : /************************************************************************/
3547 :
3548 : /* static */
3549 15855 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3550 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3551 : {
3552 : arg.SetAutoCompleteFunction(
3553 7 : [&arg,
3554 2408 : type](const std::string ¤tValue) -> std::vector<std::string>
3555 : {
3556 14 : std::vector<std::string> oRet;
3557 :
3558 7 : if (arg.IsHidden())
3559 0 : return oRet;
3560 :
3561 : {
3562 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3563 : VSIStatBufL sStat;
3564 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3565 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3566 : {
3567 0 : return oRet;
3568 : }
3569 : }
3570 :
3571 7 : auto poDM = GetGDALDriverManager();
3572 14 : std::set<std::string> oExtensions;
3573 7 : if (type)
3574 : {
3575 1350 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3576 : {
3577 1344 : auto poDriver = poDM->GetDriver(i);
3578 3808 : if (((type & GDAL_OF_RASTER) != 0 &&
3579 1120 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3580 569 : ((type & GDAL_OF_VECTOR) != 0 &&
3581 2824 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3582 481 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3583 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3584 : {
3585 : const char *pszExtensions =
3586 863 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3587 863 : if (pszExtensions)
3588 : {
3589 : const CPLStringList aosExts(
3590 1142 : CSLTokenizeString2(pszExtensions, " ", 0));
3591 1291 : for (const char *pszExt : cpl::Iterate(aosExts))
3592 720 : oExtensions.insert(CPLString(pszExt).tolower());
3593 : }
3594 : }
3595 : }
3596 : }
3597 :
3598 14 : std::string osDir;
3599 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3600 14 : std::string osPrefix;
3601 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3602 : {
3603 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3604 : {
3605 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3606 : {
3607 2 : osPrefix = pszPrefix;
3608 2 : break;
3609 : }
3610 : }
3611 3 : if (osPrefix.empty())
3612 1 : return aosVSIPrefixes;
3613 2 : if (currentValue == osPrefix)
3614 1 : osDir = osPrefix;
3615 : }
3616 6 : if (osDir.empty())
3617 : {
3618 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3619 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3620 0 : osDir = std::move(osPrefix);
3621 : }
3622 :
3623 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3624 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3625 6 : if (currentValue.empty())
3626 1 : osDir.clear();
3627 : const std::string currentFilename =
3628 12 : CPLGetFilename(currentValue.c_str());
3629 6 : if (psDir)
3630 : {
3631 438 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3632 : {
3633 433 : if ((currentFilename.empty() ||
3634 216 : STARTS_WITH(psEntry->pszName,
3635 218 : currentFilename.c_str())) &&
3636 218 : strcmp(psEntry->pszName, ".") != 0 &&
3637 1301 : strcmp(psEntry->pszName, "..") != 0 &&
3638 218 : (oExtensions.empty() ||
3639 217 : !strstr(psEntry->pszName, ".aux.xml")))
3640 : {
3641 862 : if (oExtensions.empty() ||
3642 215 : cpl::contains(
3643 : oExtensions,
3644 431 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3645 646 : .tolower()) ||
3646 184 : VSI_ISDIR(psEntry->nMode))
3647 : {
3648 72 : std::string osVal;
3649 36 : if (osDir.empty() || osDir == ".")
3650 4 : osVal = psEntry->pszName;
3651 : else
3652 64 : osVal = CPLFormFilenameSafe(
3653 64 : osDir.c_str(), psEntry->pszName, nullptr);
3654 36 : if (VSI_ISDIR(psEntry->nMode))
3655 4 : osVal += osSep;
3656 36 : oRet.push_back(std::move(osVal));
3657 : }
3658 : }
3659 433 : }
3660 5 : VSICloseDir(psDir);
3661 : }
3662 6 : return oRet;
3663 15855 : });
3664 15855 : }
3665 :
3666 : /************************************************************************/
3667 : /* GDALAlgorithm::AddInputDatasetArg() */
3668 : /************************************************************************/
3669 :
3670 594 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3671 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3672 : bool positionalAndRequired, const char *helpMessage)
3673 : {
3674 : auto &arg = AddArg(
3675 : GDAL_ARG_NAME_INPUT, 'i',
3676 : MsgOrDefault(helpMessage,
3677 : CPLSPrintf("Input %s dataset",
3678 594 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3679 1188 : pValue, type);
3680 594 : if (positionalAndRequired)
3681 587 : arg.SetPositional().SetRequired();
3682 :
3683 594 : SetAutoCompleteFunctionForFilename(arg, type);
3684 :
3685 594 : AddValidationAction(
3686 130 : [pValue]()
3687 : {
3688 129 : if (pValue->GetName() == "-")
3689 1 : pValue->Set("/vsistdin/");
3690 129 : return true;
3691 : });
3692 :
3693 594 : return arg;
3694 : }
3695 :
3696 : /************************************************************************/
3697 : /* GDALAlgorithm::AddInputDatasetArg() */
3698 : /************************************************************************/
3699 :
3700 12755 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3701 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
3702 : bool positionalAndRequired, const char *helpMessage)
3703 : {
3704 : auto &arg =
3705 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
3706 : MsgOrDefault(
3707 : helpMessage,
3708 : CPLSPrintf("Input %s datasets",
3709 12755 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3710 38265 : pValue, type)
3711 12755 : .SetPackedValuesAllowed(false);
3712 12755 : if (positionalAndRequired)
3713 1625 : arg.SetPositional().SetRequired();
3714 :
3715 12755 : SetAutoCompleteFunctionForFilename(arg, type);
3716 :
3717 12755 : AddValidationAction(
3718 5805 : [pValue]()
3719 : {
3720 11080 : for (auto &val : *pValue)
3721 : {
3722 5275 : if (val.GetName() == "-")
3723 1 : val.Set("/vsistdin/");
3724 : }
3725 5805 : return true;
3726 : });
3727 12755 : return arg;
3728 : }
3729 :
3730 : /************************************************************************/
3731 : /* GDALAlgorithm::AddOutputDatasetArg() */
3732 : /************************************************************************/
3733 :
3734 7927 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
3735 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3736 : bool positionalAndRequired, const char *helpMessage)
3737 : {
3738 : auto &arg =
3739 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
3740 : MsgOrDefault(
3741 : helpMessage,
3742 : CPLSPrintf("Output %s dataset",
3743 7927 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3744 23781 : pValue, type)
3745 7927 : .SetIsInput(true)
3746 7927 : .SetIsOutput(true)
3747 7927 : .SetDatasetInputFlags(GADV_NAME)
3748 7927 : .SetDatasetOutputFlags(GADV_OBJECT);
3749 7927 : if (positionalAndRequired)
3750 4261 : arg.SetPositional().SetRequired();
3751 :
3752 7927 : AddValidationAction(
3753 10827 : [this, &arg, pValue]()
3754 : {
3755 3374 : if (pValue->GetName() == "-")
3756 4 : pValue->Set("/vsistdout/");
3757 :
3758 3374 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3759 3330 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
3760 5408 : (!outputFormatArg->IsExplicitlySet() ||
3761 8782 : outputFormatArg->Get<std::string>().empty()) &&
3762 1252 : arg.IsExplicitlySet())
3763 : {
3764 : const auto vrtCompatible =
3765 930 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3766 182 : if (vrtCompatible && !vrtCompatible->empty() &&
3767 1112 : vrtCompatible->front() == "false" &&
3768 1021 : EQUAL(
3769 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
3770 : "VRT"))
3771 : {
3772 6 : ReportError(
3773 : CE_Failure, CPLE_NotSupported,
3774 : "VRT output is not supported.%s",
3775 6 : outputFormatArg->GetDescription().find("GDALG") !=
3776 : std::string::npos
3777 : ? " Consider using the GDALG driver instead (files "
3778 : "with .gdalg.json extension)"
3779 : : "");
3780 6 : return false;
3781 : }
3782 924 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
3783 1825 : EQUAL(pValue->GetName()
3784 : .substr(pValue->GetName().size() -
3785 : strlen(".gdalg.json"))
3786 : .c_str(),
3787 2749 : ".gdalg.json") &&
3788 27 : outputFormatArg->GetDescription().find("GDALG") ==
3789 : std::string::npos)
3790 : {
3791 0 : ReportError(CE_Failure, CPLE_NotSupported,
3792 : "GDALG output is not supported");
3793 0 : return false;
3794 : }
3795 : }
3796 3368 : return true;
3797 : });
3798 :
3799 7927 : return arg;
3800 : }
3801 :
3802 : /************************************************************************/
3803 : /* GDALAlgorithm::AddOverwriteArg() */
3804 : /************************************************************************/
3805 :
3806 : GDALInConstructionAlgorithmArg &
3807 7851 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
3808 : {
3809 : return AddArg(GDAL_ARG_NAME_OVERWRITE, 0,
3810 : MsgOrDefault(
3811 : helpMessage,
3812 : _("Whether overwriting existing output is allowed")),
3813 15702 : pValue)
3814 15702 : .SetDefault(false);
3815 : }
3816 :
3817 : /************************************************************************/
3818 : /* GDALAlgorithm::AddOverwriteLayerArg() */
3819 : /************************************************************************/
3820 :
3821 : GDALInConstructionAlgorithmArg &
3822 3155 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
3823 : {
3824 3155 : AddValidationAction(
3825 1381 : [this]
3826 : {
3827 1380 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3828 1380 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3829 : {
3830 1 : ReportError(CE_Failure, CPLE_AppDefined,
3831 : "--update argument must exist for "
3832 : "--overwrite-layer, even if hidden");
3833 1 : return false;
3834 : }
3835 1379 : return true;
3836 : });
3837 : return AddArg(GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
3838 : MsgOrDefault(
3839 : helpMessage,
3840 : _("Whether overwriting existing output is allowed")),
3841 6310 : pValue)
3842 3155 : .SetDefault(false)
3843 : .AddAction(
3844 17 : [this]
3845 : {
3846 17 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3847 17 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3848 : {
3849 17 : updateArg->Set(true);
3850 : }
3851 6327 : });
3852 : }
3853 :
3854 : /************************************************************************/
3855 : /* GDALAlgorithm::AddUpdateArg() */
3856 : /************************************************************************/
3857 :
3858 : GDALInConstructionAlgorithmArg &
3859 3706 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
3860 : {
3861 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
3862 : MsgOrDefault(
3863 : helpMessage,
3864 : _("Whether to open existing dataset in update mode")),
3865 7412 : pValue)
3866 7412 : .SetDefault(false);
3867 : }
3868 :
3869 : /************************************************************************/
3870 : /* GDALAlgorithm::AddAppendLayerArg() */
3871 : /************************************************************************/
3872 :
3873 : GDALInConstructionAlgorithmArg &
3874 3058 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
3875 : {
3876 3058 : AddValidationAction(
3877 1384 : [this]
3878 : {
3879 1383 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3880 1383 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3881 : {
3882 1 : ReportError(CE_Failure, CPLE_AppDefined,
3883 : "--update argument must exist for --append, even "
3884 : "if hidden");
3885 1 : return false;
3886 : }
3887 1382 : return true;
3888 : });
3889 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
3890 : MsgOrDefault(
3891 : helpMessage,
3892 : _("Whether appending to existing layer is allowed")),
3893 6116 : pValue)
3894 3058 : .SetDefault(false)
3895 : .AddAction(
3896 21 : [this]
3897 : {
3898 21 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3899 21 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3900 : {
3901 21 : updateArg->Set(true);
3902 : }
3903 6137 : });
3904 : }
3905 :
3906 : /************************************************************************/
3907 : /* GDALAlgorithm::AddOptionsSuggestions() */
3908 : /************************************************************************/
3909 :
3910 : /* static */
3911 29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
3912 : const std::string ¤tValue,
3913 : std::vector<std::string> &oRet)
3914 : {
3915 29 : if (!pszXML)
3916 0 : return false;
3917 58 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
3918 29 : if (!poTree)
3919 0 : return false;
3920 :
3921 58 : std::string typedOptionName = currentValue;
3922 29 : const auto posEqual = typedOptionName.find('=');
3923 58 : std::string typedValue;
3924 29 : if (posEqual != 0 && posEqual != std::string::npos)
3925 : {
3926 2 : typedValue = currentValue.substr(posEqual + 1);
3927 2 : typedOptionName.resize(posEqual);
3928 : }
3929 :
3930 405 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
3931 376 : psChild = psChild->psNext)
3932 : {
3933 389 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
3934 402 : if (pszName && typedOptionName == pszName &&
3935 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
3936 2 : strcmp(psChild->pszValue, "Argument") == 0))
3937 : {
3938 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
3939 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
3940 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
3941 13 : if (EQUAL(pszType, "string-select"))
3942 : {
3943 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
3944 85 : psChild2 = psChild2->psNext)
3945 : {
3946 85 : if (EQUAL(psChild2->pszValue, "Value"))
3947 : {
3948 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
3949 : }
3950 : }
3951 : }
3952 8 : else if (EQUAL(pszType, "boolean"))
3953 : {
3954 3 : if (typedValue == "YES" || typedValue == "NO")
3955 : {
3956 1 : oRet.push_back(currentValue);
3957 1 : return true;
3958 : }
3959 2 : oRet.push_back("NO");
3960 2 : oRet.push_back("YES");
3961 : }
3962 5 : else if (EQUAL(pszType, "int"))
3963 : {
3964 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
3965 2 : atoi(pszMax) - atoi(pszMin) < 25)
3966 : {
3967 1 : const int nMax = atoi(pszMax);
3968 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
3969 12 : oRet.push_back(std::to_string(i));
3970 : }
3971 : }
3972 :
3973 12 : if (oRet.empty())
3974 : {
3975 4 : if (pszMin && pszMax)
3976 : {
3977 1 : oRet.push_back(std::string("##"));
3978 2 : oRet.push_back(std::string("validity range: [")
3979 1 : .append(pszMin)
3980 1 : .append(",")
3981 1 : .append(pszMax)
3982 1 : .append("]"));
3983 : }
3984 3 : else if (pszMin)
3985 : {
3986 1 : oRet.push_back(std::string("##"));
3987 1 : oRet.push_back(
3988 1 : std::string("validity range: >= ").append(pszMin));
3989 : }
3990 2 : else if (pszMax)
3991 : {
3992 1 : oRet.push_back(std::string("##"));
3993 1 : oRet.push_back(
3994 1 : std::string("validity range: <= ").append(pszMax));
3995 : }
3996 1 : else if (const char *pszDescription =
3997 1 : CPLGetXMLValue(psChild, "description", nullptr))
3998 : {
3999 1 : oRet.push_back(std::string("##"));
4000 2 : oRet.push_back(std::string("type: ")
4001 1 : .append(pszType)
4002 1 : .append(", description: ")
4003 1 : .append(pszDescription));
4004 : }
4005 : }
4006 :
4007 12 : return true;
4008 : }
4009 : }
4010 :
4011 319 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4012 303 : psChild = psChild->psNext)
4013 : {
4014 303 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4015 303 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4016 5 : strcmp(psChild->pszValue, "Argument") == 0))
4017 : {
4018 300 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4019 300 : if (!pszScope ||
4020 40 : (EQUAL(pszScope, "raster") &&
4021 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4022 20 : (EQUAL(pszScope, "vector") &&
4023 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4024 : {
4025 280 : oRet.push_back(std::string(pszName).append("="));
4026 : }
4027 : }
4028 : }
4029 :
4030 16 : return false;
4031 : }
4032 :
4033 : /************************************************************************/
4034 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4035 : /************************************************************************/
4036 :
4037 : //! @cond Doxygen_Suppress
4038 : std::vector<std::string>
4039 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4040 : {
4041 2 : std::vector<std::string> oRet;
4042 :
4043 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4044 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4045 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4046 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4047 : {
4048 2 : datasetType = inputArg->GetDatasetType();
4049 : }
4050 :
4051 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4052 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4053 2 : inputFormat->IsExplicitlySet())
4054 : {
4055 : const auto &aosAllowedDrivers =
4056 1 : inputFormat->Get<std::vector<std::string>>();
4057 1 : if (aosAllowedDrivers.size() == 1)
4058 : {
4059 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4060 1 : aosAllowedDrivers[0].c_str());
4061 1 : if (poDriver)
4062 : {
4063 1 : AddOptionsSuggestions(
4064 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4065 : datasetType, currentValue, oRet);
4066 : }
4067 1 : return oRet;
4068 : }
4069 : }
4070 :
4071 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4072 363 : &oRet](const GDALArgDatasetValue &datasetValue)
4073 : {
4074 1 : auto poDM = GetGDALDriverManager();
4075 :
4076 1 : const auto &osDSName = datasetValue.GetName();
4077 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4078 1 : if (!osExt.empty())
4079 : {
4080 1 : std::set<std::string> oVisitedExtensions;
4081 225 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4082 : {
4083 224 : auto poDriver = poDM->GetDriver(i);
4084 672 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4085 224 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4086 69 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4087 448 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4088 69 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4089 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4090 : {
4091 : const char *pszExtensions =
4092 155 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4093 155 : if (pszExtensions)
4094 : {
4095 : const CPLStringList aosExts(
4096 102 : CSLTokenizeString2(pszExtensions, " ", 0));
4097 225 : for (const char *pszExt : cpl::Iterate(aosExts))
4098 : {
4099 127 : if (EQUAL(pszExt, osExt.c_str()) &&
4100 3 : !cpl::contains(oVisitedExtensions, pszExt))
4101 : {
4102 1 : oVisitedExtensions.insert(pszExt);
4103 1 : if (AddOptionsSuggestions(
4104 : poDriver->GetMetadataItem(
4105 1 : GDAL_DMD_OPENOPTIONLIST),
4106 : datasetType, currentValue, oRet))
4107 : {
4108 0 : return;
4109 : }
4110 1 : break;
4111 : }
4112 : }
4113 : }
4114 : }
4115 : }
4116 : }
4117 1 : };
4118 :
4119 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4120 : {
4121 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4122 0 : AddSuggestions(datasetValue);
4123 : }
4124 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4125 : {
4126 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4127 1 : if (datasetValues.size() == 1)
4128 1 : AddSuggestions(datasetValues[0]);
4129 : }
4130 :
4131 1 : return oRet;
4132 : }
4133 :
4134 : //! @endcond
4135 :
4136 : /************************************************************************/
4137 : /* GDALAlgorithm::AddOpenOptionsArg() */
4138 : /************************************************************************/
4139 :
4140 : GDALInConstructionAlgorithmArg &
4141 8637 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4142 : const char *helpMessage)
4143 : {
4144 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4145 17274 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4146 17274 : .AddAlias("oo")
4147 17274 : .SetMetaVar("<KEY>=<VALUE>")
4148 8637 : .SetPackedValuesAllowed(false)
4149 8637 : .SetCategory(GAAC_ADVANCED);
4150 :
4151 21 : arg.AddValidationAction([this, &arg]()
4152 8658 : { return ParseAndValidateKeyValue(arg); });
4153 :
4154 : arg.SetAutoCompleteFunction(
4155 2 : [this](const std::string ¤tValue)
4156 8639 : { return OpenOptionCompleteFunction(currentValue); });
4157 :
4158 8637 : return arg;
4159 : }
4160 :
4161 : /************************************************************************/
4162 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4163 : /************************************************************************/
4164 :
4165 : GDALInConstructionAlgorithmArg &
4166 2926 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4167 : const char *helpMessage)
4168 : {
4169 : auto &arg =
4170 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4171 5852 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4172 5852 : .AddAlias("output-oo")
4173 5852 : .SetMetaVar("<KEY>=<VALUE>")
4174 2926 : .SetPackedValuesAllowed(false)
4175 2926 : .SetCategory(GAAC_ADVANCED);
4176 :
4177 0 : arg.AddValidationAction([this, &arg]()
4178 2926 : { return ParseAndValidateKeyValue(arg); });
4179 :
4180 : arg.SetAutoCompleteFunction(
4181 0 : [this](const std::string ¤tValue)
4182 2926 : { return OpenOptionCompleteFunction(currentValue); });
4183 :
4184 2926 : return arg;
4185 : }
4186 :
4187 : /************************************************************************/
4188 : /* ValidateFormat() */
4189 : /************************************************************************/
4190 :
4191 4432 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4192 : bool bStreamAllowed,
4193 : bool bGDALGAllowed) const
4194 : {
4195 4432 : if (arg.GetChoices().empty())
4196 : {
4197 : const auto Validate =
4198 19019 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4199 : {
4200 4327 : if (const auto extraFormats =
4201 4327 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4202 : {
4203 60 : for (const auto &extraFormat : *extraFormats)
4204 : {
4205 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4206 14 : return true;
4207 : }
4208 : }
4209 :
4210 4313 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4211 1713 : return true;
4212 :
4213 2606 : if (EQUAL(val.c_str(), "GDALG") &&
4214 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4215 : {
4216 2 : if (bGDALGAllowed)
4217 : {
4218 2 : return true;
4219 : }
4220 : else
4221 : {
4222 0 : ReportError(CE_Failure, CPLE_NotSupported,
4223 : "GDALG output is not supported.");
4224 0 : return false;
4225 : }
4226 : }
4227 :
4228 : const auto vrtCompatible =
4229 2598 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4230 440 : if (vrtCompatible && !vrtCompatible->empty() &&
4231 3038 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4232 : {
4233 7 : ReportError(CE_Failure, CPLE_NotSupported,
4234 : "VRT output is not supported.%s",
4235 : bGDALGAllowed
4236 : ? " Consider using the GDALG driver instead "
4237 : "(files with .gdalg.json extension)."
4238 : : "");
4239 7 : return false;
4240 : }
4241 :
4242 : const auto allowedFormats =
4243 2591 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4244 2612 : if (allowedFormats && !allowedFormats->empty() &&
4245 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4246 2612 : val) != allowedFormats->end())
4247 : {
4248 9 : return true;
4249 : }
4250 :
4251 : const auto excludedFormats =
4252 2582 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4253 2594 : if (excludedFormats && !excludedFormats->empty() &&
4254 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4255 2594 : val) != excludedFormats->end())
4256 : {
4257 0 : ReportError(CE_Failure, CPLE_NotSupported,
4258 : "%s output is not supported.", val.c_str());
4259 0 : return false;
4260 : }
4261 :
4262 2582 : auto hDriver = GDALGetDriverByName(val.c_str());
4263 2582 : if (!hDriver)
4264 : {
4265 : auto poMissingDriver =
4266 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4267 4 : if (poMissingDriver)
4268 : {
4269 : const std::string msg =
4270 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4271 0 : ReportError(CE_Failure, CPLE_AppDefined,
4272 : "Invalid value for argument '%s'. Driver '%s' "
4273 : "not found but is known. However plugin %s",
4274 0 : arg.GetName().c_str(), val.c_str(),
4275 : msg.c_str());
4276 : }
4277 : else
4278 : {
4279 8 : ReportError(CE_Failure, CPLE_AppDefined,
4280 : "Invalid value for argument '%s'. Driver '%s' "
4281 : "does not exist.",
4282 4 : arg.GetName().c_str(), val.c_str());
4283 : }
4284 4 : return false;
4285 : }
4286 :
4287 2578 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4288 2578 : if (caps)
4289 : {
4290 7746 : for (const std::string &cap : *caps)
4291 : {
4292 : const char *pszVal =
4293 5193 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4294 5193 : if (!(pszVal && pszVal[0]))
4295 : {
4296 1539 : if (cap == GDAL_DCAP_CREATECOPY &&
4297 0 : std::find(caps->begin(), caps->end(),
4298 768 : GDAL_DCAP_RASTER) != caps->end() &&
4299 768 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4300 1539 : nullptr) &&
4301 768 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4302 : nullptr))
4303 : {
4304 : // if it supports Create, it supports CreateCopy
4305 : }
4306 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4307 : {
4308 2 : ReportError(
4309 : CE_Failure, CPLE_AppDefined,
4310 : "Invalid value for argument '%s'. Driver '%s' "
4311 : "does "
4312 : "not advertise any file format extension.",
4313 1 : arg.GetName().c_str(), val.c_str());
4314 3 : return false;
4315 : }
4316 : else
4317 : {
4318 2 : if (cap == GDAL_DCAP_CREATE)
4319 : {
4320 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4321 1 : if (updateArg &&
4322 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4323 1 : updateArg->IsExplicitlySet())
4324 : {
4325 0 : continue;
4326 : }
4327 :
4328 2 : ReportError(
4329 : CE_Failure, CPLE_AppDefined,
4330 : "Invalid value for argument '%s'. "
4331 : "Driver '%s' does not have write support.",
4332 1 : arg.GetName().c_str(), val.c_str());
4333 1 : return false;
4334 : }
4335 : else
4336 : {
4337 2 : ReportError(
4338 : CE_Failure, CPLE_AppDefined,
4339 : "Invalid value for argument '%s'. Driver "
4340 : "'%s' "
4341 : "does "
4342 : "not expose the required '%s' capability.",
4343 1 : arg.GetName().c_str(), val.c_str(),
4344 : cap.c_str());
4345 1 : return false;
4346 : }
4347 : }
4348 : }
4349 : }
4350 : }
4351 2575 : return true;
4352 4330 : };
4353 :
4354 4330 : if (arg.GetType() == GAAT_STRING)
4355 : {
4356 4320 : return Validate(arg.Get<std::string>());
4357 : }
4358 12 : else if (arg.GetType() == GAAT_STRING_LIST)
4359 : {
4360 19 : for (const auto &val : arg.Get<std::vector<std::string>>())
4361 : {
4362 9 : if (!Validate(val))
4363 2 : return false;
4364 : }
4365 : }
4366 : }
4367 :
4368 112 : return true;
4369 : }
4370 :
4371 : /************************************************************************/
4372 : /* FormatAutoCompleteFunction() */
4373 : /************************************************************************/
4374 :
4375 : /* static */
4376 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4377 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4378 : {
4379 7 : std::vector<std::string> res;
4380 7 : auto poDM = GetGDALDriverManager();
4381 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4382 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4383 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4384 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4385 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4386 0 : res = std::move(*extraFormats);
4387 1574 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4388 : {
4389 1567 : auto poDriver = poDM->GetDriver(i);
4390 :
4391 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4392 1567 : vrtCompatible->front() == "false" &&
4393 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4394 : {
4395 : // do nothing
4396 : }
4397 1567 : else if (allowedFormats && !allowedFormats->empty() &&
4398 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4399 1567 : poDriver->GetDescription()) != allowedFormats->end())
4400 : {
4401 0 : res.push_back(poDriver->GetDescription());
4402 : }
4403 1567 : else if (excludedFormats && !excludedFormats->empty() &&
4404 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4405 0 : poDriver->GetDescription()) !=
4406 1567 : excludedFormats->end())
4407 : {
4408 0 : continue;
4409 : }
4410 1567 : else if (caps)
4411 : {
4412 1567 : bool ok = true;
4413 3109 : for (const std::string &cap : *caps)
4414 : {
4415 2341 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4416 : {
4417 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4418 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4419 : {
4420 0 : ok = false;
4421 0 : break;
4422 : }
4423 : }
4424 2341 : else if (const char *pszVal =
4425 2341 : poDriver->GetMetadataItem(cap.c_str());
4426 1470 : pszVal && pszVal[0])
4427 : {
4428 : }
4429 1259 : else if (cap == GDAL_DCAP_CREATECOPY &&
4430 0 : (std::find(caps->begin(), caps->end(),
4431 388 : GDAL_DCAP_RASTER) != caps->end() &&
4432 1647 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4433 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4434 : {
4435 : // if it supports Create, it supports CreateCopy
4436 : }
4437 : else
4438 : {
4439 799 : ok = false;
4440 799 : break;
4441 : }
4442 : }
4443 1567 : if (ok)
4444 : {
4445 768 : res.push_back(poDriver->GetDescription());
4446 : }
4447 : }
4448 : }
4449 7 : if (bGDALGAllowed)
4450 4 : res.push_back("GDALG");
4451 7 : return res;
4452 : }
4453 :
4454 : /************************************************************************/
4455 : /* GDALAlgorithm::AddInputFormatsArg() */
4456 : /************************************************************************/
4457 :
4458 : GDALInConstructionAlgorithmArg &
4459 8427 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4460 : const char *helpMessage)
4461 : {
4462 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4463 16854 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4464 16854 : .AddAlias("if")
4465 8427 : .SetCategory(GAAC_ADVANCED);
4466 12 : arg.AddValidationAction([this, &arg]()
4467 8439 : { return ValidateFormat(arg, false, false); });
4468 : arg.SetAutoCompleteFunction(
4469 1 : [&arg](const std::string &)
4470 8428 : { return FormatAutoCompleteFunction(arg, false, false); });
4471 8427 : return arg;
4472 : }
4473 :
4474 : /************************************************************************/
4475 : /* GDALAlgorithm::AddOutputFormatArg() */
4476 : /************************************************************************/
4477 :
4478 : GDALInConstructionAlgorithmArg &
4479 8911 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4480 : bool bGDALGAllowed, const char *helpMessage)
4481 : {
4482 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4483 : MsgOrDefault(helpMessage,
4484 : bGDALGAllowed
4485 : ? _("Output format (\"GDALG\" allowed)")
4486 : : _("Output format")),
4487 17822 : pValue)
4488 17822 : .AddAlias("of")
4489 8911 : .AddAlias("format");
4490 : arg.AddValidationAction(
4491 4416 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4492 13327 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4493 : arg.SetAutoCompleteFunction(
4494 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4495 : {
4496 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4497 4 : bGDALGAllowed);
4498 8911 : });
4499 8911 : return arg;
4500 : }
4501 :
4502 : /************************************************************************/
4503 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4504 : /************************************************************************/
4505 : GDALInConstructionAlgorithmArg &
4506 1719 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4507 : const char *helpMessage)
4508 : {
4509 : auto &arg =
4510 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4511 3438 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4512 3438 : .AddAlias("ot")
4513 3438 : .AddAlias("datatype")
4514 5157 : .AddMetadataItem("type", {"GDALDataType"})
4515 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4516 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4517 1719 : "Float32", "Float64", "CFloat32", "CFloat64")
4518 1719 : .SetHiddenChoices("Byte");
4519 1719 : return arg;
4520 : }
4521 :
4522 : /************************************************************************/
4523 : /* GDALAlgorithm::AddNodataArg() */
4524 : /************************************************************************/
4525 :
4526 : GDALInConstructionAlgorithmArg &
4527 587 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4528 : const std::string &optionName,
4529 : const char *helpMessage)
4530 : {
4531 : auto &arg = AddArg(
4532 : optionName, 0,
4533 : MsgOrDefault(helpMessage,
4534 : noneAllowed
4535 : ? _("Assign a specified nodata value to output bands "
4536 : "('none', numeric value, 'nan', 'inf', '-inf')")
4537 : : _("Assign a specified nodata value to output bands "
4538 : "(numeric value, 'nan', 'inf', '-inf')")),
4539 587 : pValue);
4540 : arg.AddValidationAction(
4541 356 : [this, pValue, noneAllowed, optionName]()
4542 : {
4543 77 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4544 : {
4545 67 : char *endptr = nullptr;
4546 67 : CPLStrtod(pValue->c_str(), &endptr);
4547 67 : if (endptr != pValue->c_str() + pValue->size())
4548 : {
4549 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4550 : "Value of '%s' should be %sa "
4551 : "numeric value, 'nan', 'inf' or '-inf'",
4552 : optionName.c_str(),
4553 : noneAllowed ? "'none', " : "");
4554 1 : return false;
4555 : }
4556 : }
4557 76 : return true;
4558 587 : });
4559 587 : return arg;
4560 : }
4561 :
4562 : /************************************************************************/
4563 : /* GDALAlgorithm::AddOutputStringArg() */
4564 : /************************************************************************/
4565 :
4566 : GDALInConstructionAlgorithmArg &
4567 5271 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4568 : {
4569 : return AddArg(
4570 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4571 : MsgOrDefault(helpMessage,
4572 : _("Output string, in which the result is placed")),
4573 10542 : pValue)
4574 5271 : .SetHiddenForCLI()
4575 5271 : .SetIsInput(false)
4576 10542 : .SetIsOutput(true);
4577 : }
4578 :
4579 : /************************************************************************/
4580 : /* GDALAlgorithm::AddStdoutArg() */
4581 : /************************************************************************/
4582 :
4583 : GDALInConstructionAlgorithmArg &
4584 1365 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4585 : {
4586 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4587 : MsgOrDefault(helpMessage,
4588 : _("Directly output on stdout. If enabled, "
4589 : "output-string will be empty")),
4590 2730 : pValue)
4591 2730 : .SetHidden();
4592 : }
4593 :
4594 : /************************************************************************/
4595 : /* GDALAlgorithm::AddLayerNameArg() */
4596 : /************************************************************************/
4597 :
4598 : GDALInConstructionAlgorithmArg &
4599 202 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4600 : {
4601 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4602 202 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4603 : }
4604 :
4605 : /************************************************************************/
4606 : /* GDALAlgorithm::AddArrayNameArg() */
4607 : /************************************************************************/
4608 :
4609 : GDALInConstructionAlgorithmArg &
4610 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
4611 : {
4612 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
4613 100 : pValue)
4614 2 : .SetAutoCompleteFunction([this](const std::string &)
4615 102 : { return AutoCompleteArrayName(); });
4616 : }
4617 :
4618 : /************************************************************************/
4619 : /* GDALAlgorithm::AddArrayNameArg() */
4620 : /************************************************************************/
4621 :
4622 : GDALInConstructionAlgorithmArg &
4623 73 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
4624 : const char *helpMessage)
4625 : {
4626 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
4627 146 : pValue)
4628 0 : .SetAutoCompleteFunction([this](const std::string &)
4629 146 : { return AutoCompleteArrayName(); });
4630 : }
4631 :
4632 : /************************************************************************/
4633 : /* GDALAlgorithm::AutoCompleteArrayName() */
4634 : /************************************************************************/
4635 :
4636 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
4637 : {
4638 2 : std::vector<std::string> ret;
4639 4 : std::string osDSName;
4640 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4641 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4642 : {
4643 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4644 0 : if (!inputDatasets.empty())
4645 : {
4646 0 : osDSName = inputDatasets[0].GetName();
4647 : }
4648 : }
4649 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
4650 : {
4651 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
4652 2 : osDSName = inputDataset.GetName();
4653 : }
4654 :
4655 2 : if (!osDSName.empty())
4656 : {
4657 4 : CPLStringList aosAllowedDrivers;
4658 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4659 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
4660 : aosAllowedDrivers =
4661 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
4662 :
4663 4 : CPLStringList aosOpenOptions;
4664 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
4665 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
4666 : aosOpenOptions =
4667 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
4668 :
4669 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4670 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
4671 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
4672 : {
4673 2 : if (auto poRG = poDS->GetRootGroup())
4674 : {
4675 1 : ret = poRG->GetMDArrayFullNamesRecursive();
4676 : }
4677 : }
4678 : }
4679 :
4680 4 : return ret;
4681 : }
4682 :
4683 : /************************************************************************/
4684 : /* GDALAlgorithm::AddMemorySizeArg() */
4685 : /************************************************************************/
4686 :
4687 : GDALInConstructionAlgorithmArg &
4688 210 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
4689 : const std::string &optionName,
4690 : const char *helpMessage)
4691 : {
4692 420 : return AddArg(optionName, 0, helpMessage, pStrValue)
4693 210 : .SetDefault(*pStrValue)
4694 : .AddValidationAction(
4695 139 : [this, pValue, pStrValue]()
4696 : {
4697 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
4698 : GIntBig nBytes;
4699 : bool bUnitSpecified;
4700 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
4701 47 : &bUnitSpecified) != CE_None)
4702 : {
4703 2 : return false;
4704 : }
4705 45 : if (!bUnitSpecified)
4706 : {
4707 1 : ReportError(CE_Failure, CPLE_AppDefined,
4708 : "Memory size must have a unit or be a "
4709 : "percentage of usable RAM (2GB, 5%%, etc.)");
4710 1 : return false;
4711 : }
4712 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
4713 : {
4714 : // -1 to please CoverityScan
4715 : if (static_cast<std::uint64_t>(nBytes) >
4716 : std::numeric_limits<size_t>::max() - 1U)
4717 : {
4718 : ReportError(CE_Failure, CPLE_AppDefined,
4719 : "Memory size %s is too large.",
4720 : pStrValue->c_str());
4721 : return false;
4722 : }
4723 : }
4724 :
4725 44 : *pValue = static_cast<size_t>(nBytes);
4726 44 : return true;
4727 420 : });
4728 : }
4729 :
4730 : /************************************************************************/
4731 : /* GDALAlgorithm::AddOutputLayerNameArg() */
4732 : /************************************************************************/
4733 :
4734 : GDALInConstructionAlgorithmArg &
4735 474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
4736 : const char *helpMessage)
4737 : {
4738 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
4739 474 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
4740 : }
4741 :
4742 : /************************************************************************/
4743 : /* GDALAlgorithm::AddLayerNameArg() */
4744 : /************************************************************************/
4745 :
4746 : GDALInConstructionAlgorithmArg &
4747 801 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
4748 : const char *helpMessage)
4749 : {
4750 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4751 801 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4752 : }
4753 :
4754 : /************************************************************************/
4755 : /* GDALAlgorithm::AddGeometryTypeArg() */
4756 : /************************************************************************/
4757 :
4758 : GDALInConstructionAlgorithmArg &
4759 286 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
4760 : {
4761 : return AddArg("geometry-type", 0,
4762 572 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
4763 : .SetAutoCompleteFunction(
4764 3 : [](const std::string ¤tValue)
4765 : {
4766 3 : std::vector<std::string> oRet;
4767 51 : for (const char *type :
4768 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
4769 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
4770 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
4771 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
4772 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
4773 : {
4774 68 : if (currentValue.empty() ||
4775 17 : STARTS_WITH(type, currentValue.c_str()))
4776 : {
4777 35 : oRet.push_back(type);
4778 35 : oRet.push_back(std::string(type).append("Z"));
4779 35 : oRet.push_back(std::string(type).append("M"));
4780 35 : oRet.push_back(std::string(type).append("ZM"));
4781 : }
4782 : }
4783 3 : return oRet;
4784 572 : })
4785 : .AddValidationAction(
4786 53 : [this, pValue]()
4787 : {
4788 46 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
4789 51 : wkbUnknown &&
4790 5 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
4791 : {
4792 2 : ReportError(CE_Failure, CPLE_AppDefined,
4793 : "Invalid geometry type '%s'", pValue->c_str());
4794 2 : return false;
4795 : }
4796 44 : return true;
4797 572 : });
4798 : }
4799 :
4800 : /************************************************************************/
4801 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
4802 : /************************************************************************/
4803 :
4804 : /* static */
4805 2586 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
4806 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
4807 : {
4808 2586 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
4809 : datasetArg.GetType() == GAAT_DATASET_LIST);
4810 :
4811 : layerArg.SetAutoCompleteFunction(
4812 18 : [&datasetArg](const std::string ¤tValue)
4813 : {
4814 6 : std::vector<std::string> ret;
4815 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4816 6 : GDALArgDatasetValue *dsVal = nullptr;
4817 6 : if (datasetArg.GetType() == GAAT_DATASET)
4818 : {
4819 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
4820 : }
4821 : else
4822 : {
4823 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
4824 6 : if (val.size() == 1)
4825 : {
4826 6 : dsVal = &val[0];
4827 : }
4828 : }
4829 6 : if (dsVal && !dsVal->GetName().empty())
4830 : {
4831 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4832 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
4833 6 : if (poDS)
4834 : {
4835 12 : for (auto &&poLayer : poDS->GetLayers())
4836 : {
4837 6 : if (currentValue == poLayer->GetDescription())
4838 : {
4839 1 : ret.clear();
4840 1 : ret.push_back(poLayer->GetDescription());
4841 1 : break;
4842 : }
4843 5 : ret.push_back(poLayer->GetDescription());
4844 : }
4845 : }
4846 : }
4847 12 : return ret;
4848 2586 : });
4849 2586 : }
4850 :
4851 : /************************************************************************/
4852 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
4853 : /************************************************************************/
4854 :
4855 131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
4856 : GDALInConstructionAlgorithmArg &fieldArg,
4857 : GDALInConstructionAlgorithmArg &layerNameArg,
4858 : std::vector<GDALArgDatasetValue> &datasetArg)
4859 : {
4860 :
4861 : fieldArg.SetAutoCompleteFunction(
4862 12 : [&datasetArg, &layerNameArg](const std::string ¤tValue)
4863 : {
4864 8 : std::set<std::string> ret;
4865 4 : if (!datasetArg.empty())
4866 : {
4867 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4868 :
4869 14 : auto getLayerFields = [&ret, ¤tValue](OGRLayer *poLayer)
4870 : {
4871 2 : auto poDefn = poLayer->GetLayerDefn();
4872 2 : const int nFieldCount = poDefn->GetFieldCount();
4873 8 : for (int iField = 0; iField < nFieldCount; iField++)
4874 : {
4875 : const char *fieldName =
4876 6 : poDefn->GetFieldDefn(iField)->GetNameRef();
4877 6 : if (currentValue == fieldName)
4878 : {
4879 0 : ret.clear();
4880 0 : ret.insert(fieldName);
4881 0 : break;
4882 : }
4883 6 : ret.insert(fieldName);
4884 : }
4885 2 : };
4886 :
4887 2 : GDALArgDatasetValue &dsVal = datasetArg[0];
4888 :
4889 2 : if (!dsVal.GetName().empty())
4890 : {
4891 : auto poDS = std::unique_ptr<GDALDataset>(
4892 2 : GDALDataset::Open(dsVal.GetName().c_str(),
4893 4 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
4894 2 : if (poDS)
4895 : {
4896 2 : const auto &layerName = layerNameArg.Get<std::string>();
4897 2 : if (layerName.empty())
4898 : {
4899 : // Loop through all layers
4900 4 : for (auto &&poLayer : poDS->GetLayers())
4901 : {
4902 2 : getLayerFields(poLayer);
4903 : }
4904 : }
4905 : else
4906 : {
4907 0 : const auto poLayer = poDS->GetLayerByName(
4908 0 : layerNameArg.Get<std::string>().c_str());
4909 0 : if (poLayer)
4910 : {
4911 0 : getLayerFields(poLayer);
4912 : }
4913 : }
4914 : }
4915 : }
4916 : }
4917 4 : std::vector<std::string> retVector(ret.begin(), ret.end());
4918 8 : return retVector;
4919 131 : });
4920 131 : }
4921 :
4922 : /************************************************************************/
4923 : /* GDALAlgorithm::AddFieldNameArg() */
4924 : /************************************************************************/
4925 :
4926 : GDALInConstructionAlgorithmArg &
4927 131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
4928 : {
4929 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
4930 131 : pValue);
4931 : }
4932 :
4933 : /************************************************************************/
4934 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
4935 : /************************************************************************/
4936 :
4937 262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
4938 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
4939 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
4940 : {
4941 : auto &arg =
4942 524 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
4943 786 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
4944 : .SetAutoCompleteFunction(
4945 1 : [](const std::string ¤tValue)
4946 : {
4947 1 : std::vector<std::string> oRet;
4948 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
4949 : {
4950 : const char *pszSubType =
4951 5 : OGRFieldDefn::GetFieldSubTypeName(
4952 : static_cast<OGRFieldSubType>(i));
4953 5 : if (pszSubType != nullptr)
4954 : {
4955 5 : if (currentValue.empty() ||
4956 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
4957 : {
4958 5 : oRet.push_back(pszSubType);
4959 : }
4960 : }
4961 : }
4962 :
4963 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
4964 : {
4965 : // Skip deprecated
4966 14 : if (static_cast<OGRFieldType>(i) ==
4967 13 : OGRFieldType::OFTWideString ||
4968 : static_cast<OGRFieldType>(i) ==
4969 : OGRFieldType::OFTWideStringList)
4970 2 : continue;
4971 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
4972 : static_cast<OGRFieldType>(i));
4973 12 : if (pszType != nullptr)
4974 : {
4975 12 : if (currentValue.empty() ||
4976 0 : STARTS_WITH(pszType, currentValue.c_str()))
4977 : {
4978 12 : oRet.push_back(pszType);
4979 : }
4980 : }
4981 : }
4982 1 : return oRet;
4983 262 : });
4984 :
4985 : auto validationFunction =
4986 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
4987 : {
4988 120 : bool isValid{true};
4989 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
4990 :
4991 : // String is returned for unknown types
4992 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
4993 : {
4994 16 : isValid = false;
4995 : }
4996 :
4997 120 : *pSubtypeValue =
4998 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
4999 :
5000 120 : if (*pSubtypeValue != OFSTNone)
5001 : {
5002 15 : isValid = true;
5003 15 : switch (*pSubtypeValue)
5004 : {
5005 6 : case OFSTBoolean:
5006 : case OFSTInt16:
5007 : {
5008 6 : *pTypeValue = OFTInteger;
5009 6 : break;
5010 : }
5011 3 : case OFSTFloat32:
5012 : {
5013 3 : *pTypeValue = OFTReal;
5014 3 : break;
5015 : }
5016 6 : default:
5017 : {
5018 6 : *pTypeValue = OFTString;
5019 6 : break;
5020 : }
5021 : }
5022 : }
5023 :
5024 120 : if (!isValid)
5025 : {
5026 2 : ReportError(CE_Failure, CPLE_AppDefined,
5027 : "Invalid value for argument '%s': '%s'",
5028 1 : arg.GetName().c_str(), pStrValue->c_str());
5029 : }
5030 :
5031 120 : return isValid;
5032 262 : };
5033 :
5034 262 : if (!pStrValue->empty())
5035 : {
5036 0 : arg.SetDefault(*pStrValue);
5037 0 : validationFunction();
5038 : }
5039 :
5040 262 : arg.AddValidationAction(std::move(validationFunction));
5041 :
5042 262 : return arg;
5043 : }
5044 :
5045 : /************************************************************************/
5046 : /* GDALAlgorithm::ValidateBandArg() */
5047 : /************************************************************************/
5048 :
5049 3747 : bool GDALAlgorithm::ValidateBandArg() const
5050 : {
5051 3747 : bool ret = true;
5052 3747 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5053 3747 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5054 1404 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5055 280 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5056 5145 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5057 143 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5058 : {
5059 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5060 : {
5061 99 : if (nBand > poDS->GetRasterCount())
5062 : {
5063 5 : ReportError(CE_Failure, CPLE_AppDefined,
5064 : "Value of 'band' should be greater or equal than "
5065 : "1 and less or equal than %d.",
5066 : poDS->GetRasterCount());
5067 5 : return false;
5068 : }
5069 94 : return true;
5070 86 : };
5071 :
5072 : const auto ValidateForOneDataset =
5073 292 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5074 : {
5075 81 : bool l_ret = true;
5076 81 : if (bandArg->GetType() == GAAT_INTEGER)
5077 : {
5078 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5079 : }
5080 57 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5081 : {
5082 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5083 : {
5084 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5085 : }
5086 : }
5087 81 : return l_ret;
5088 86 : };
5089 :
5090 86 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5091 : {
5092 : auto poDS =
5093 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5094 6 : if (poDS && !ValidateForOneDataset(poDS))
5095 2 : ret = false;
5096 : }
5097 : else
5098 : {
5099 80 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5100 79 : for (auto &datasetValue :
5101 238 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5102 : {
5103 79 : auto poDS = datasetValue.GetDatasetRef();
5104 79 : if (poDS && !ValidateForOneDataset(poDS))
5105 3 : ret = false;
5106 : }
5107 : }
5108 : }
5109 3747 : return ret;
5110 : }
5111 :
5112 : /************************************************************************/
5113 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5114 : /************************************************************************/
5115 :
5116 2973 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5117 : {
5118 2973 : return ValidateBandArg();
5119 : }
5120 :
5121 : /************************************************************************/
5122 : /* GDALAlgorithm::AddBandArg() */
5123 : /************************************************************************/
5124 :
5125 : GDALInConstructionAlgorithmArg &
5126 1445 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5127 : {
5128 1762 : AddValidationAction([this]() { return ValidateBandArg(); });
5129 :
5130 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5131 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5132 2890 : pValue)
5133 : .AddValidationAction(
5134 34 : [pValue]()
5135 : {
5136 34 : if (*pValue <= 0)
5137 : {
5138 1 : CPLError(CE_Failure, CPLE_AppDefined,
5139 : "Value of 'band' should greater or equal to 1.");
5140 1 : return false;
5141 : }
5142 33 : return true;
5143 2890 : });
5144 : }
5145 :
5146 : /************************************************************************/
5147 : /* GDALAlgorithm::AddBandArg() */
5148 : /************************************************************************/
5149 :
5150 : GDALInConstructionAlgorithmArg &
5151 829 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5152 : {
5153 1286 : AddValidationAction([this]() { return ValidateBandArg(); });
5154 :
5155 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5156 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5157 1658 : pValue)
5158 : .AddValidationAction(
5159 126 : [pValue]()
5160 : {
5161 397 : for (int val : *pValue)
5162 : {
5163 272 : if (val <= 0)
5164 : {
5165 1 : CPLError(CE_Failure, CPLE_AppDefined,
5166 : "Value of 'band' should greater or equal "
5167 : "to 1.");
5168 1 : return false;
5169 : }
5170 : }
5171 125 : return true;
5172 1658 : });
5173 : }
5174 :
5175 : /************************************************************************/
5176 : /* ParseAndValidateKeyValue() */
5177 : /************************************************************************/
5178 :
5179 396 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5180 : {
5181 400 : const auto Validate = [this, &arg](const std::string &val)
5182 : {
5183 395 : if (val.find('=') == std::string::npos)
5184 : {
5185 5 : ReportError(
5186 : CE_Failure, CPLE_AppDefined,
5187 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5188 5 : arg.GetName().c_str());
5189 5 : return false;
5190 : }
5191 :
5192 390 : return true;
5193 396 : };
5194 :
5195 396 : if (arg.GetType() == GAAT_STRING)
5196 : {
5197 0 : return Validate(arg.Get<std::string>());
5198 : }
5199 396 : else if (arg.GetType() == GAAT_STRING_LIST)
5200 : {
5201 396 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5202 396 : if (vals.size() == 1)
5203 : {
5204 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5205 722 : std::vector<std::string> newVals;
5206 722 : std::string curToken;
5207 361 : bool canSplitOnComma = true;
5208 361 : char lastSep = 0;
5209 361 : bool inString = false;
5210 361 : bool equalFoundInLastToken = false;
5211 5178 : for (char c : vals[0])
5212 : {
5213 4821 : if (!inString && c == ',')
5214 : {
5215 10 : if (lastSep != '=' || !equalFoundInLastToken)
5216 : {
5217 2 : canSplitOnComma = false;
5218 2 : break;
5219 : }
5220 8 : lastSep = c;
5221 8 : newVals.push_back(curToken);
5222 8 : curToken.clear();
5223 8 : equalFoundInLastToken = false;
5224 : }
5225 4811 : else if (!inString && c == '=')
5226 : {
5227 360 : if (lastSep == '=')
5228 : {
5229 2 : canSplitOnComma = false;
5230 2 : break;
5231 : }
5232 358 : equalFoundInLastToken = true;
5233 358 : lastSep = c;
5234 358 : curToken += c;
5235 : }
5236 4451 : else if (c == '"')
5237 : {
5238 4 : inString = !inString;
5239 4 : curToken += c;
5240 : }
5241 : else
5242 : {
5243 4447 : curToken += c;
5244 : }
5245 : }
5246 361 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5247 : {
5248 348 : if (!curToken.empty())
5249 348 : newVals.emplace_back(std::move(curToken));
5250 348 : vals = std::move(newVals);
5251 : }
5252 : }
5253 :
5254 786 : for (const auto &val : vals)
5255 : {
5256 395 : if (!Validate(val))
5257 5 : return false;
5258 : }
5259 : }
5260 :
5261 391 : return true;
5262 : }
5263 :
5264 : /************************************************************************/
5265 : /* IsGDALGOutput() */
5266 : /************************************************************************/
5267 :
5268 2000 : bool GDALAlgorithm::IsGDALGOutput() const
5269 : {
5270 2000 : bool isGDALGOutput = false;
5271 2000 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5272 2000 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5273 3410 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5274 1410 : outputArg->IsExplicitlySet())
5275 : {
5276 2767 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5277 1371 : outputFormatArg->IsExplicitlySet())
5278 : {
5279 : const auto &val =
5280 917 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5281 917 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5282 : }
5283 : else
5284 : {
5285 : const auto &filename =
5286 479 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5287 479 : isGDALGOutput =
5288 931 : filename.GetName().size() > strlen(".gdalg.json") &&
5289 452 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5290 : strlen(".gdalg.json"),
5291 : ".gdalg.json");
5292 : }
5293 : }
5294 2000 : return isGDALGOutput;
5295 : }
5296 :
5297 : /************************************************************************/
5298 : /* ProcessGDALGOutput() */
5299 : /************************************************************************/
5300 :
5301 2332 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5302 : {
5303 2332 : if (!SupportsStreamedOutput())
5304 774 : return ProcessGDALGOutputRet::NOT_GDALG;
5305 :
5306 1558 : if (IsGDALGOutput())
5307 : {
5308 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5309 : const auto &filename =
5310 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5311 : VSIStatBufL sStat;
5312 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5313 : {
5314 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5315 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5316 : {
5317 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5318 : {
5319 0 : CPLError(CE_Failure, CPLE_AppDefined,
5320 : "File '%s' already exists. Specify the "
5321 : "--overwrite option to overwrite it.",
5322 : filename.c_str());
5323 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5324 : }
5325 : }
5326 : }
5327 :
5328 22 : std::string osCommandLine;
5329 :
5330 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5331 : {
5332 33 : if (!osCommandLine.empty())
5333 22 : osCommandLine += ' ';
5334 33 : osCommandLine += path;
5335 : }
5336 :
5337 250 : for (const auto &arg : GetArgs())
5338 : {
5339 265 : if (arg->IsExplicitlySet() &&
5340 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5341 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5342 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5343 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5344 : {
5345 14 : osCommandLine += ' ';
5346 14 : std::string strArg;
5347 14 : if (!arg->Serialize(strArg))
5348 : {
5349 0 : CPLError(CE_Failure, CPLE_AppDefined,
5350 : "Cannot serialize argument %s",
5351 0 : arg->GetName().c_str());
5352 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5353 : }
5354 14 : osCommandLine += strArg;
5355 : }
5356 : }
5357 :
5358 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5359 :
5360 11 : std::string outStringUnused;
5361 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5362 11 : ? ProcessGDALGOutputRet::GDALG_OK
5363 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5364 : }
5365 :
5366 1547 : return ProcessGDALGOutputRet::NOT_GDALG;
5367 : }
5368 :
5369 : /************************************************************************/
5370 : /* GDALAlgorithm::SaveGDALG() */
5371 : /************************************************************************/
5372 :
5373 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5374 : std::string &outString,
5375 : const std::string &commandLine)
5376 : {
5377 44 : CPLJSONDocument oDoc;
5378 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5379 22 : oDoc.GetRoot().Add("command_line", commandLine);
5380 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5381 :
5382 22 : if (!filename.empty())
5383 21 : return oDoc.Save(filename);
5384 :
5385 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5386 1 : return true;
5387 : }
5388 :
5389 : /************************************************************************/
5390 : /* GDALAlgorithm::AddCreationOptionsArg() */
5391 : /************************************************************************/
5392 :
5393 : GDALInConstructionAlgorithmArg &
5394 7774 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5395 : const char *helpMessage)
5396 : {
5397 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5398 15548 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5399 15548 : .AddAlias("co")
5400 15548 : .SetMetaVar("<KEY>=<VALUE>")
5401 7774 : .SetPackedValuesAllowed(false);
5402 142 : arg.AddValidationAction([this, &arg]()
5403 7916 : { return ParseAndValidateKeyValue(arg); });
5404 :
5405 : arg.SetAutoCompleteFunction(
5406 48 : [this](const std::string ¤tValue)
5407 : {
5408 16 : std::vector<std::string> oRet;
5409 :
5410 16 : int datasetType =
5411 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5412 16 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5413 16 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5414 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5415 : {
5416 16 : datasetType = outputArg->GetDatasetType();
5417 : }
5418 :
5419 16 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5420 32 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5421 16 : outputFormat->IsExplicitlySet())
5422 : {
5423 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5424 6 : outputFormat->Get<std::string>().c_str());
5425 6 : if (poDriver)
5426 : {
5427 6 : AddOptionsSuggestions(
5428 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5429 : datasetType, currentValue, oRet);
5430 : }
5431 6 : return oRet;
5432 : }
5433 :
5434 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5435 : {
5436 10 : auto poDM = GetGDALDriverManager();
5437 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5438 10 : const auto &osDSName = datasetValue.GetName();
5439 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5440 10 : if (!osExt.empty())
5441 : {
5442 10 : std::set<std::string> oVisitedExtensions;
5443 703 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5444 : {
5445 700 : auto poDriver = poDM->GetDriver(i);
5446 2100 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5447 700 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5448 207 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5449 1400 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5450 207 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5451 0 : poDriver->GetMetadataItem(
5452 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5453 : {
5454 : const char *pszExtensions =
5455 493 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5456 493 : if (pszExtensions)
5457 : {
5458 : const CPLStringList aosExts(
5459 320 : CSLTokenizeString2(pszExtensions, " ", 0));
5460 710 : for (const char *pszExt : cpl::Iterate(aosExts))
5461 : {
5462 416 : if (EQUAL(pszExt, osExt.c_str()) &&
5463 16 : !cpl::contains(oVisitedExtensions,
5464 : pszExt))
5465 : {
5466 10 : oVisitedExtensions.insert(pszExt);
5467 10 : if (AddOptionsSuggestions(
5468 : poDriver->GetMetadataItem(
5469 10 : GDAL_DMD_CREATIONOPTIONLIST),
5470 : datasetType, currentValue,
5471 : oRet))
5472 : {
5473 7 : return oRet;
5474 : }
5475 3 : break;
5476 : }
5477 : }
5478 : }
5479 : }
5480 : }
5481 : }
5482 : }
5483 :
5484 3 : return oRet;
5485 7774 : });
5486 :
5487 7774 : return arg;
5488 : }
5489 :
5490 : /************************************************************************/
5491 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
5492 : /************************************************************************/
5493 :
5494 : GDALInConstructionAlgorithmArg &
5495 3621 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
5496 : const char *helpMessage)
5497 : {
5498 : auto &arg =
5499 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
5500 7242 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
5501 7242 : .AddAlias("lco")
5502 7242 : .SetMetaVar("<KEY>=<VALUE>")
5503 3621 : .SetPackedValuesAllowed(false);
5504 72 : arg.AddValidationAction([this, &arg]()
5505 3693 : { return ParseAndValidateKeyValue(arg); });
5506 :
5507 : arg.SetAutoCompleteFunction(
5508 5 : [this](const std::string ¤tValue)
5509 : {
5510 2 : std::vector<std::string> oRet;
5511 :
5512 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5513 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5514 2 : outputFormat->IsExplicitlySet())
5515 : {
5516 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5517 1 : outputFormat->Get<std::string>().c_str());
5518 1 : if (poDriver)
5519 : {
5520 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
5521 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5522 : GDAL_OF_VECTOR, currentValue, oRet);
5523 : }
5524 1 : return oRet;
5525 : }
5526 :
5527 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5528 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5529 : {
5530 1 : auto poDM = GetGDALDriverManager();
5531 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5532 1 : const auto &osDSName = datasetValue.GetName();
5533 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5534 1 : if (!osExt.empty())
5535 : {
5536 1 : std::set<std::string> oVisitedExtensions;
5537 225 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5538 : {
5539 224 : auto poDriver = poDM->GetDriver(i);
5540 224 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
5541 : {
5542 : const char *pszExtensions =
5543 88 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5544 88 : if (pszExtensions)
5545 : {
5546 : const CPLStringList aosExts(
5547 61 : CSLTokenizeString2(pszExtensions, " ", 0));
5548 154 : for (const char *pszExt : cpl::Iterate(aosExts))
5549 : {
5550 95 : if (EQUAL(pszExt, osExt.c_str()) &&
5551 1 : !cpl::contains(oVisitedExtensions,
5552 : pszExt))
5553 : {
5554 1 : oVisitedExtensions.insert(pszExt);
5555 1 : if (AddOptionsSuggestions(
5556 : poDriver->GetMetadataItem(
5557 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5558 : GDAL_OF_VECTOR, currentValue,
5559 : oRet))
5560 : {
5561 0 : return oRet;
5562 : }
5563 1 : break;
5564 : }
5565 : }
5566 : }
5567 : }
5568 : }
5569 : }
5570 : }
5571 :
5572 1 : return oRet;
5573 3621 : });
5574 :
5575 3621 : return arg;
5576 : }
5577 :
5578 : /************************************************************************/
5579 : /* GDALAlgorithm::AddBBOXArg() */
5580 : /************************************************************************/
5581 :
5582 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
5583 : GDALInConstructionAlgorithmArg &
5584 1776 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
5585 : {
5586 : auto &arg = AddArg("bbox", 0,
5587 : MsgOrDefault(helpMessage,
5588 : _("Bounding box as xmin,ymin,xmax,ymax")),
5589 3552 : pValue)
5590 1776 : .SetRepeatedArgAllowed(false)
5591 1776 : .SetMinCount(4)
5592 1776 : .SetMaxCount(4)
5593 1776 : .SetDisplayHintAboutRepetition(false);
5594 : arg.AddValidationAction(
5595 162 : [&arg]()
5596 : {
5597 162 : const auto &val = arg.Get<std::vector<double>>();
5598 162 : CPLAssert(val.size() == 4);
5599 162 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
5600 : {
5601 5 : CPLError(CE_Failure, CPLE_AppDefined,
5602 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
5603 : "xmin <= xmax and ymin <= ymax");
5604 5 : return false;
5605 : }
5606 157 : return true;
5607 1776 : });
5608 1776 : return arg;
5609 : }
5610 :
5611 : /************************************************************************/
5612 : /* GDALAlgorithm::AddActiveLayerArg() */
5613 : /************************************************************************/
5614 :
5615 : GDALInConstructionAlgorithmArg &
5616 1691 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
5617 : {
5618 : return AddArg("active-layer", 0,
5619 : MsgOrDefault(helpMessage,
5620 : _("Set active layer (if not specified, all)")),
5621 1691 : pValue);
5622 : }
5623 :
5624 : /************************************************************************/
5625 : /* GDALAlgorithm::AddNumThreadsArg() */
5626 : /************************************************************************/
5627 :
5628 : GDALInConstructionAlgorithmArg &
5629 676 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
5630 : const char *helpMessage)
5631 : {
5632 : auto &arg =
5633 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
5634 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
5635 676 : pStrValue);
5636 :
5637 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
5638 1352 : _("Number of jobs (read-only, hidden argument)"), pValue)
5639 676 : .SetHidden();
5640 :
5641 2583 : auto lambda = [this, &arg, pValue, pStrValue]
5642 : {
5643 861 : bool bOK = false;
5644 861 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
5645 : const int nLimit = std::clamp(
5646 861 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
5647 1722 : CPLGetNumCPUs());
5648 : const int nNumThreads =
5649 861 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
5650 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
5651 861 : if (bOK)
5652 : {
5653 861 : *pValue = nNumThreads;
5654 : }
5655 : else
5656 : {
5657 0 : ReportError(CE_Failure, CPLE_IllegalArg,
5658 : "Invalid value for '%s' argument",
5659 0 : arg.GetName().c_str());
5660 : }
5661 861 : return bOK;
5662 676 : };
5663 676 : if (!pStrValue->empty())
5664 : {
5665 630 : arg.SetDefault(*pStrValue);
5666 630 : lambda();
5667 : }
5668 676 : arg.AddValidationAction(std::move(lambda));
5669 676 : return arg;
5670 : }
5671 :
5672 : /************************************************************************/
5673 : /* GDALAlgorithm::AddAbsolutePathArg() */
5674 : /************************************************************************/
5675 :
5676 : GDALInConstructionAlgorithmArg &
5677 619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
5678 : {
5679 : return AddArg(
5680 : "absolute-path", 0,
5681 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
5682 : "should be stored as an absolute path")),
5683 619 : pValue);
5684 : }
5685 :
5686 : /************************************************************************/
5687 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
5688 : /************************************************************************/
5689 :
5690 : GDALInConstructionAlgorithmArg &
5691 137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
5692 : const char *helpMessage)
5693 : {
5694 :
5695 : const auto pixelFunctionNames =
5696 137 : VRTDerivedRasterBand::GetPixelFunctionNames();
5697 : return AddArg(
5698 : "pixel-function", 0,
5699 : MsgOrDefault(
5700 : helpMessage,
5701 : _("Specify a pixel function to calculate output value from "
5702 : "overlapping inputs")),
5703 274 : pValue)
5704 274 : .SetChoices(pixelFunctionNames);
5705 : }
5706 :
5707 : /************************************************************************/
5708 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
5709 : /************************************************************************/
5710 :
5711 : GDALInConstructionAlgorithmArg &
5712 137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
5713 : const char *helpMessage)
5714 : {
5715 : auto &pixelFunctionArgArg =
5716 : AddArg("pixel-function-arg", 0,
5717 : MsgOrDefault(
5718 : helpMessage,
5719 : _("Specify argument(s) to pass to the pixel function")),
5720 274 : pValue)
5721 274 : .SetMetaVar("<NAME>=<VALUE>")
5722 137 : .SetRepeatedArgAllowed(true);
5723 : pixelFunctionArgArg.AddValidationAction(
5724 7 : [this, &pixelFunctionArgArg]()
5725 144 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
5726 :
5727 : pixelFunctionArgArg.SetAutoCompleteFunction(
5728 12 : [this](const std::string ¤tValue)
5729 : {
5730 12 : std::string pixelFunction;
5731 6 : const auto pixelFunctionArg = GetArg("pixel-function");
5732 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
5733 : {
5734 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
5735 : }
5736 :
5737 6 : std::vector<std::string> ret;
5738 :
5739 6 : if (!pixelFunction.empty())
5740 : {
5741 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
5742 : pixelFunction.c_str());
5743 5 : if (!pair)
5744 : {
5745 1 : ret.push_back("**");
5746 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5747 1 : ret.push_back(std::string("\xC2\xA0"
5748 : "Invalid pixel function name"));
5749 : }
5750 4 : else if (pair->second.find("Argument name=") ==
5751 : std::string::npos)
5752 : {
5753 1 : ret.push_back("**");
5754 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5755 1 : ret.push_back(
5756 2 : std::string(
5757 : "\xC2\xA0"
5758 : "No pixel function arguments for pixel function '")
5759 1 : .append(pixelFunction)
5760 1 : .append("'"));
5761 : }
5762 : else
5763 : {
5764 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
5765 : ret);
5766 : }
5767 : }
5768 :
5769 12 : return ret;
5770 137 : });
5771 :
5772 137 : return pixelFunctionArgArg;
5773 : }
5774 :
5775 : /************************************************************************/
5776 : /* GDALAlgorithm::AddProgressArg() */
5777 : /************************************************************************/
5778 :
5779 7736 : void GDALAlgorithm::AddProgressArg()
5780 : {
5781 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
5782 15472 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
5783 15472 : .SetCategory(GAAC_COMMON)
5784 7736 : .AddAction([this]() { m_progressBarRequested = false; });
5785 :
5786 15472 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
5787 7736 : .SetHidden();
5788 7736 : }
5789 :
5790 : /************************************************************************/
5791 : /* GDALAlgorithm::Run() */
5792 : /************************************************************************/
5793 :
5794 4301 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
5795 : {
5796 4301 : WarnIfDeprecated();
5797 :
5798 4301 : if (m_selectedSubAlg)
5799 : {
5800 386 : if (m_calledFromCommandLine)
5801 231 : m_selectedSubAlg->m_calledFromCommandLine = true;
5802 386 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
5803 : }
5804 :
5805 3915 : if (m_helpRequested || m_helpDocRequested)
5806 : {
5807 16 : if (m_calledFromCommandLine)
5808 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
5809 16 : return true;
5810 : }
5811 :
5812 3899 : if (m_JSONUsageRequested)
5813 : {
5814 3 : if (m_calledFromCommandLine)
5815 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
5816 3 : return true;
5817 : }
5818 :
5819 3896 : if (!ValidateArguments())
5820 101 : return false;
5821 :
5822 3795 : switch (ProcessGDALGOutput())
5823 : {
5824 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
5825 0 : return false;
5826 :
5827 11 : case ProcessGDALGOutputRet::GDALG_OK:
5828 11 : return true;
5829 :
5830 3784 : case ProcessGDALGOutputRet::NOT_GDALG:
5831 3784 : break;
5832 : }
5833 :
5834 3784 : if (m_executionForStreamOutput)
5835 : {
5836 82 : if (!CheckSafeForStreamOutput())
5837 : {
5838 4 : return false;
5839 : }
5840 : }
5841 :
5842 3780 : return RunImpl(pfnProgress, pProgressData);
5843 : }
5844 :
5845 : /************************************************************************/
5846 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
5847 : /************************************************************************/
5848 :
5849 37 : bool GDALAlgorithm::CheckSafeForStreamOutput()
5850 : {
5851 37 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5852 37 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
5853 : {
5854 37 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5855 37 : if (!EQUAL(val.c_str(), "stream"))
5856 : {
5857 : // For security reasons, to avoid that reading a .gdalg.json file
5858 : // writes a file on the file system.
5859 4 : ReportError(
5860 : CE_Failure, CPLE_NotSupported,
5861 : "in streamed execution, --format stream should be used");
5862 4 : return false;
5863 : }
5864 : }
5865 33 : return true;
5866 : }
5867 :
5868 : /************************************************************************/
5869 : /* GDALAlgorithm::Finalize() */
5870 : /************************************************************************/
5871 :
5872 1601 : bool GDALAlgorithm::Finalize()
5873 : {
5874 1601 : bool ret = true;
5875 1601 : if (m_selectedSubAlg)
5876 237 : ret = m_selectedSubAlg->Finalize();
5877 :
5878 29202 : for (auto &arg : m_args)
5879 : {
5880 27601 : if (arg->GetType() == GAAT_DATASET)
5881 : {
5882 1291 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
5883 : }
5884 26310 : else if (arg->GetType() == GAAT_DATASET_LIST)
5885 : {
5886 2457 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
5887 : {
5888 1164 : ret = ds.Close() && ret;
5889 : }
5890 : }
5891 : }
5892 1601 : return ret;
5893 : }
5894 :
5895 : /************************************************************************/
5896 : /* GDALAlgorithm::GetArgNamesForCLI() */
5897 : /************************************************************************/
5898 :
5899 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
5900 673 : GDALAlgorithm::GetArgNamesForCLI() const
5901 : {
5902 1346 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5903 :
5904 673 : size_t maxOptLen = 0;
5905 8272 : for (const auto &arg : m_args)
5906 : {
5907 7599 : if (arg->IsHidden() || arg->IsHiddenForCLI())
5908 1637 : continue;
5909 5962 : std::string opt;
5910 5962 : bool addComma = false;
5911 5962 : if (!arg->GetShortName().empty())
5912 : {
5913 1366 : opt += '-';
5914 1366 : opt += arg->GetShortName();
5915 1366 : addComma = true;
5916 : }
5917 5962 : for (char alias : arg->GetShortNameAliases())
5918 : {
5919 0 : if (addComma)
5920 0 : opt += ", ";
5921 0 : opt += "-";
5922 0 : opt += alias;
5923 0 : addComma = true;
5924 : }
5925 6622 : for (const std::string &alias : arg->GetAliases())
5926 : {
5927 660 : if (addComma)
5928 275 : opt += ", ";
5929 660 : opt += "--";
5930 660 : opt += alias;
5931 660 : addComma = true;
5932 : }
5933 5962 : if (!arg->GetName().empty())
5934 : {
5935 5962 : if (addComma)
5936 1751 : opt += ", ";
5937 5962 : opt += "--";
5938 5962 : opt += arg->GetName();
5939 : }
5940 5962 : const auto &metaVar = arg->GetMetaVar();
5941 5962 : if (!metaVar.empty())
5942 : {
5943 3702 : opt += ' ';
5944 3702 : if (metaVar.front() != '<')
5945 2647 : opt += '<';
5946 3702 : opt += metaVar;
5947 3702 : if (metaVar.back() != '>')
5948 2663 : opt += '>';
5949 : }
5950 5962 : maxOptLen = std::max(maxOptLen, opt.size());
5951 5962 : options.emplace_back(arg.get(), opt);
5952 : }
5953 :
5954 1346 : return std::make_pair(std::move(options), maxOptLen);
5955 : }
5956 :
5957 : /************************************************************************/
5958 : /* GDALAlgorithm::GetUsageForCLI() */
5959 : /************************************************************************/
5960 :
5961 : std::string
5962 400 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
5963 : const UsageOptions &usageOptions) const
5964 : {
5965 400 : if (m_selectedSubAlg)
5966 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
5967 :
5968 786 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
5969 786 : std::string osPath;
5970 791 : for (const std::string &s : m_callPath)
5971 : {
5972 398 : if (!osPath.empty())
5973 49 : osPath += ' ';
5974 398 : osPath += s;
5975 : }
5976 393 : osRet += ' ';
5977 393 : osRet += osPath;
5978 :
5979 393 : bool hasNonPositionals = false;
5980 4795 : for (const auto &arg : m_args)
5981 : {
5982 4402 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
5983 3147 : hasNonPositionals = true;
5984 : }
5985 :
5986 393 : if (HasSubAlgorithms())
5987 : {
5988 9 : if (m_callPath.size() == 1)
5989 : {
5990 8 : osRet += " <COMMAND>";
5991 8 : if (hasNonPositionals)
5992 8 : osRet += " [OPTIONS]";
5993 8 : if (usageOptions.isPipelineStep)
5994 : {
5995 5 : const size_t nLenFirstLine = osRet.size();
5996 5 : osRet += '\n';
5997 5 : osRet.append(nLenFirstLine, '-');
5998 5 : osRet += '\n';
5999 : }
6000 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6001 : }
6002 : else
6003 : {
6004 1 : osRet += " <SUBCOMMAND>";
6005 1 : if (hasNonPositionals)
6006 1 : osRet += " [OPTIONS]";
6007 1 : if (usageOptions.isPipelineStep)
6008 : {
6009 0 : const size_t nLenFirstLine = osRet.size();
6010 0 : osRet += '\n';
6011 0 : osRet.append(nLenFirstLine, '-');
6012 0 : osRet += '\n';
6013 : }
6014 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6015 : }
6016 9 : size_t maxNameLen = 0;
6017 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6018 : {
6019 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6020 : }
6021 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6022 : {
6023 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6024 43 : if (subAlg && !subAlg->IsHidden())
6025 : {
6026 43 : const std::string &name(subAlg->GetName());
6027 43 : osRet += " - ";
6028 43 : osRet += name;
6029 43 : osRet += ": ";
6030 43 : osRet.append(maxNameLen - name.size(), ' ');
6031 43 : osRet += subAlg->GetDescription();
6032 43 : if (!subAlg->m_aliases.empty())
6033 : {
6034 6 : bool first = true;
6035 6 : for (const auto &alias : subAlg->GetAliases())
6036 : {
6037 6 : if (alias ==
6038 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6039 6 : break;
6040 0 : if (first)
6041 0 : osRet += " (alias: ";
6042 : else
6043 0 : osRet += ", ";
6044 0 : osRet += alias;
6045 0 : first = false;
6046 : }
6047 6 : if (!first)
6048 : {
6049 0 : osRet += ')';
6050 : }
6051 : }
6052 43 : osRet += '\n';
6053 : }
6054 : }
6055 :
6056 9 : if (shortUsage && hasNonPositionals)
6057 : {
6058 2 : osRet += "\nTry '";
6059 2 : osRet += osPath;
6060 2 : osRet += " --help' for help.\n";
6061 : }
6062 : }
6063 : else
6064 : {
6065 384 : if (!m_args.empty())
6066 : {
6067 384 : if (hasNonPositionals)
6068 384 : osRet += " [OPTIONS]";
6069 563 : for (const auto *arg : m_positionalArgs)
6070 : {
6071 247 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6072 68 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6073 : {
6074 : const bool optional =
6075 195 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6076 28 : arg->GetName() == "pipeline"));
6077 167 : osRet += ' ';
6078 167 : if (optional)
6079 24 : osRet += '[';
6080 167 : const std::string &metavar = arg->GetMetaVar();
6081 167 : if (!metavar.empty() && metavar[0] == '<')
6082 : {
6083 4 : osRet += metavar;
6084 : }
6085 : else
6086 : {
6087 163 : osRet += '<';
6088 163 : osRet += metavar;
6089 163 : osRet += '>';
6090 : }
6091 209 : if (arg->GetType() == GAAT_DATASET_LIST &&
6092 42 : arg->GetMaxCount() > 1)
6093 : {
6094 28 : osRet += "...";
6095 : }
6096 167 : if (optional)
6097 24 : osRet += ']';
6098 : }
6099 : }
6100 : }
6101 :
6102 384 : const size_t nLenFirstLine = osRet.size();
6103 384 : osRet += '\n';
6104 384 : if (usageOptions.isPipelineStep)
6105 : {
6106 301 : osRet.append(nLenFirstLine, '-');
6107 301 : osRet += '\n';
6108 : }
6109 :
6110 384 : if (shortUsage)
6111 : {
6112 21 : osRet += "Try '";
6113 21 : osRet += osPath;
6114 21 : osRet += " --help' for help.\n";
6115 21 : return osRet;
6116 : }
6117 :
6118 363 : osRet += '\n';
6119 363 : osRet += m_description;
6120 363 : osRet += '\n';
6121 : }
6122 :
6123 372 : if (!m_args.empty() && !shortUsage)
6124 : {
6125 740 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6126 : size_t maxOptLen;
6127 370 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6128 370 : if (usageOptions.maxOptLen)
6129 303 : maxOptLen = usageOptions.maxOptLen;
6130 :
6131 740 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6132 370 : if (m_arbitraryLongNameArgsAllowed)
6133 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6134 :
6135 : const auto OutputArg =
6136 2198 : [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
6137 19285 : const std::string &opt)
6138 : {
6139 2198 : osRet += " ";
6140 2198 : osRet += opt;
6141 2198 : osRet += " ";
6142 2198 : osRet.append(maxOptLen - opt.size(), ' ');
6143 2198 : osRet += arg->GetDescription();
6144 :
6145 2198 : const auto &choices = arg->GetChoices();
6146 2198 : if (!choices.empty())
6147 : {
6148 207 : osRet += ". ";
6149 207 : osRet += arg->GetMetaVar();
6150 207 : osRet += '=';
6151 207 : bool firstChoice = true;
6152 1659 : for (const auto &choice : choices)
6153 : {
6154 1452 : if (!firstChoice)
6155 1245 : osRet += '|';
6156 1452 : osRet += choice;
6157 1452 : firstChoice = false;
6158 : }
6159 : }
6160 :
6161 4334 : if (arg->GetType() == GAAT_DATASET ||
6162 2136 : arg->GetType() == GAAT_DATASET_LIST)
6163 : {
6164 124 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
6165 17 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6166 : {
6167 9 : osRet += " (created by algorithm)";
6168 : }
6169 : }
6170 :
6171 2198 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6172 : {
6173 171 : osRet += " (default: ";
6174 171 : osRet += arg->GetDefault<std::string>();
6175 171 : osRet += ')';
6176 : }
6177 2027 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6178 : {
6179 50 : if (arg->GetDefault<bool>())
6180 0 : osRet += " (default: true)";
6181 : }
6182 1977 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6183 : {
6184 76 : osRet += " (default: ";
6185 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6186 76 : osRet += ')';
6187 : }
6188 1901 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6189 : {
6190 49 : osRet += " (default: ";
6191 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6192 49 : osRet += ')';
6193 : }
6194 2209 : else if (arg->GetType() == GAAT_STRING_LIST &&
6195 357 : arg->HasDefaultValue())
6196 : {
6197 : const auto &defaultVal =
6198 9 : arg->GetDefault<std::vector<std::string>>();
6199 9 : if (defaultVal.size() == 1)
6200 : {
6201 9 : osRet += " (default: ";
6202 9 : osRet += defaultVal[0];
6203 9 : osRet += ')';
6204 : }
6205 : }
6206 1870 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6207 27 : arg->HasDefaultValue())
6208 : {
6209 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6210 0 : if (defaultVal.size() == 1)
6211 : {
6212 0 : osRet += " (default: ";
6213 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6214 0 : osRet += ')';
6215 : }
6216 : }
6217 1843 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6218 : {
6219 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6220 0 : if (defaultVal.size() == 1)
6221 : {
6222 0 : osRet += " (default: ";
6223 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6224 0 : osRet += ')';
6225 : }
6226 : }
6227 :
6228 2198 : if (arg->GetDisplayHintAboutRepetition())
6229 : {
6230 2231 : if (arg->GetMinCount() > 0 &&
6231 92 : arg->GetMinCount() == arg->GetMaxCount())
6232 : {
6233 18 : if (arg->GetMinCount() != 1)
6234 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6235 : }
6236 2195 : else if (arg->GetMinCount() > 0 &&
6237 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6238 : {
6239 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6240 8 : arg->GetMaxCount());
6241 : }
6242 2113 : else if (arg->GetMinCount() > 0)
6243 : {
6244 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6245 : }
6246 2047 : else if (arg->GetMaxCount() > 1)
6247 : {
6248 345 : osRet += " [may be repeated]";
6249 : }
6250 : }
6251 :
6252 2198 : if (arg->IsRequired())
6253 : {
6254 166 : osRet += " [required]";
6255 : }
6256 :
6257 2198 : osRet += '\n';
6258 :
6259 2198 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6260 2198 : if (!mutualExclusionGroup.empty())
6261 : {
6262 370 : std::string otherArgs;
6263 3468 : for (const auto &otherArg : m_args)
6264 : {
6265 5962 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6266 2679 : otherArg.get() == arg)
6267 789 : continue;
6268 2494 : if (otherArg->GetMutualExclusionGroup() ==
6269 : mutualExclusionGroup)
6270 : {
6271 242 : if (!otherArgs.empty())
6272 62 : otherArgs += ", ";
6273 242 : otherArgs += "--";
6274 242 : otherArgs += otherArg->GetName();
6275 : }
6276 : }
6277 185 : if (!otherArgs.empty())
6278 : {
6279 180 : osRet += " ";
6280 180 : osRet += " ";
6281 180 : osRet.append(maxOptLen, ' ');
6282 180 : osRet += "Mutually exclusive with ";
6283 180 : osRet += otherArgs;
6284 180 : osRet += '\n';
6285 : }
6286 : }
6287 2198 : };
6288 :
6289 370 : if (!m_positionalArgs.empty())
6290 : {
6291 141 : osRet += "\nPositional arguments:\n";
6292 1475 : for (const auto &[arg, opt] : options)
6293 : {
6294 1334 : if (arg->IsPositional())
6295 137 : OutputArg(arg, opt);
6296 : }
6297 : }
6298 :
6299 370 : if (hasNonPositionals)
6300 : {
6301 370 : bool hasCommon = false;
6302 370 : bool hasBase = false;
6303 370 : bool hasAdvanced = false;
6304 370 : bool hasEsoteric = false;
6305 740 : std::vector<std::string> categories;
6306 3515 : for (const auto &iter : options)
6307 : {
6308 3145 : const auto &arg = iter.first;
6309 3145 : if (!arg->IsPositional())
6310 : {
6311 3008 : const auto &category = arg->GetCategory();
6312 3008 : if (category == GAAC_COMMON)
6313 : {
6314 1153 : hasCommon = true;
6315 : }
6316 1855 : else if (category == GAAC_BASE)
6317 : {
6318 1648 : hasBase = true;
6319 : }
6320 207 : else if (category == GAAC_ADVANCED)
6321 : {
6322 159 : hasAdvanced = true;
6323 : }
6324 48 : else if (category == GAAC_ESOTERIC)
6325 : {
6326 15 : hasEsoteric = true;
6327 : }
6328 33 : else if (std::find(categories.begin(), categories.end(),
6329 33 : category) == categories.end())
6330 : {
6331 9 : categories.push_back(category);
6332 : }
6333 : }
6334 : }
6335 370 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6336 62 : categories.insert(categories.begin(), GAAC_ADVANCED);
6337 370 : if (hasBase)
6338 324 : categories.insert(categories.begin(), GAAC_BASE);
6339 370 : if (hasCommon && !usageOptions.isPipelineStep)
6340 64 : categories.insert(categories.begin(), GAAC_COMMON);
6341 370 : if (hasEsoteric)
6342 5 : categories.push_back(GAAC_ESOTERIC);
6343 :
6344 834 : for (const auto &category : categories)
6345 : {
6346 464 : osRet += "\n";
6347 464 : if (category != GAAC_BASE)
6348 : {
6349 140 : osRet += category;
6350 140 : osRet += ' ';
6351 : }
6352 464 : osRet += "Options:\n";
6353 4876 : for (const auto &[arg, opt] : options)
6354 : {
6355 4412 : if (!arg->IsPositional() && arg->GetCategory() == category)
6356 2061 : OutputArg(arg, opt);
6357 : }
6358 464 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6359 : {
6360 2 : osRet += " ";
6361 2 : osRet += userProvidedOpt;
6362 2 : osRet += " ";
6363 2 : if (userProvidedOpt.size() < maxOptLen)
6364 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6365 2 : osRet += "Argument provided by user";
6366 2 : osRet += '\n';
6367 : }
6368 : }
6369 : }
6370 : }
6371 :
6372 372 : if (!m_longDescription.empty())
6373 : {
6374 6 : osRet += '\n';
6375 6 : osRet += m_longDescription;
6376 6 : osRet += '\n';
6377 : }
6378 :
6379 372 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6380 : {
6381 359 : if (!m_helpURL.empty())
6382 : {
6383 359 : osRet += "\nFor more details, consult ";
6384 359 : osRet += GetHelpFullURL();
6385 359 : osRet += '\n';
6386 : }
6387 359 : osRet += GetUsageForCLIEnd();
6388 : }
6389 :
6390 372 : return osRet;
6391 : }
6392 :
6393 : /************************************************************************/
6394 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6395 : /************************************************************************/
6396 :
6397 : //! @cond Doxygen_Suppress
6398 366 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6399 : {
6400 366 : std::string osRet;
6401 :
6402 366 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6403 : {
6404 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6405 : "alternative interface to GDAL and OGR command line "
6406 : "utilities.\nThe project reserves the right to modify, "
6407 : "rename, reorganize, and change the behavior of the utility\n"
6408 : "until it is officially frozen in a future feature release of "
6409 13 : "GDAL.\n";
6410 : }
6411 366 : return osRet;
6412 : }
6413 :
6414 : //! @endcond
6415 :
6416 : /************************************************************************/
6417 : /* GDALAlgorithm::GetUsageAsJSON() */
6418 : /************************************************************************/
6419 :
6420 548 : std::string GDALAlgorithm::GetUsageAsJSON() const
6421 : {
6422 1096 : CPLJSONDocument oDoc;
6423 1096 : auto oRoot = oDoc.GetRoot();
6424 :
6425 548 : if (m_displayInJSONUsage)
6426 : {
6427 546 : oRoot.Add("name", m_name);
6428 546 : CPLJSONArray jFullPath;
6429 1130 : for (const std::string &s : m_callPath)
6430 : {
6431 584 : jFullPath.Add(s);
6432 : }
6433 546 : oRoot.Add("full_path", jFullPath);
6434 : }
6435 :
6436 548 : oRoot.Add("description", m_description);
6437 548 : if (!m_helpURL.empty())
6438 : {
6439 547 : oRoot.Add("short_url", m_helpURL);
6440 547 : oRoot.Add("url", GetHelpFullURL());
6441 : }
6442 :
6443 1096 : CPLJSONArray jSubAlgorithms;
6444 743 : for (const auto &subAlgName : GetSubAlgorithmNames())
6445 : {
6446 390 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6447 195 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
6448 : {
6449 193 : CPLJSONDocument oSubDoc;
6450 193 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
6451 193 : jSubAlgorithms.Add(oSubDoc.GetRoot());
6452 : }
6453 : }
6454 548 : oRoot.Add("sub_algorithms", jSubAlgorithms);
6455 :
6456 548 : if (m_arbitraryLongNameArgsAllowed)
6457 : {
6458 1 : oRoot.Add("user_provided_arguments_allowed", true);
6459 : }
6460 :
6461 5195 : const auto ProcessArg = [](const GDALAlgorithmArg *arg)
6462 : {
6463 5195 : CPLJSONObject jArg;
6464 5195 : jArg.Add("name", arg->GetName());
6465 5195 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
6466 5195 : jArg.Add("description", arg->GetDescription());
6467 :
6468 5195 : const auto &metaVar = arg->GetMetaVar();
6469 5195 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
6470 : {
6471 1544 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
6472 1544 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
6473 : std::string::npos)
6474 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
6475 : else
6476 793 : jArg.Add("metavar", metaVar);
6477 : }
6478 :
6479 5195 : const auto &choices = arg->GetChoices();
6480 5195 : if (!choices.empty())
6481 : {
6482 388 : CPLJSONArray jChoices;
6483 3396 : for (const auto &choice : choices)
6484 3008 : jChoices.Add(choice);
6485 388 : jArg.Add("choices", jChoices);
6486 : }
6487 5195 : if (arg->HasDefaultValue())
6488 : {
6489 1138 : switch (arg->GetType())
6490 : {
6491 399 : case GAAT_BOOLEAN:
6492 399 : jArg.Add("default", arg->GetDefault<bool>());
6493 399 : break;
6494 342 : case GAAT_STRING:
6495 342 : jArg.Add("default", arg->GetDefault<std::string>());
6496 342 : break;
6497 199 : case GAAT_INTEGER:
6498 199 : jArg.Add("default", arg->GetDefault<int>());
6499 199 : break;
6500 178 : case GAAT_REAL:
6501 178 : jArg.Add("default", arg->GetDefault<double>());
6502 178 : break;
6503 18 : case GAAT_STRING_LIST:
6504 : {
6505 : const auto &val =
6506 18 : arg->GetDefault<std::vector<std::string>>();
6507 18 : if (val.size() == 1)
6508 : {
6509 17 : jArg.Add("default", val[0]);
6510 : }
6511 : else
6512 : {
6513 1 : CPLJSONArray jArr;
6514 3 : for (const auto &s : val)
6515 : {
6516 2 : jArr.Add(s);
6517 : }
6518 1 : jArg.Add("default", jArr);
6519 : }
6520 18 : break;
6521 : }
6522 1 : case GAAT_INTEGER_LIST:
6523 : {
6524 1 : const auto &val = arg->GetDefault<std::vector<int>>();
6525 1 : if (val.size() == 1)
6526 : {
6527 0 : jArg.Add("default", val[0]);
6528 : }
6529 : else
6530 : {
6531 1 : CPLJSONArray jArr;
6532 3 : for (int i : val)
6533 : {
6534 2 : jArr.Add(i);
6535 : }
6536 1 : jArg.Add("default", jArr);
6537 : }
6538 1 : break;
6539 : }
6540 1 : case GAAT_REAL_LIST:
6541 : {
6542 1 : const auto &val = arg->GetDefault<std::vector<double>>();
6543 1 : if (val.size() == 1)
6544 : {
6545 0 : jArg.Add("default", val[0]);
6546 : }
6547 : else
6548 : {
6549 1 : CPLJSONArray jArr;
6550 3 : for (double d : val)
6551 : {
6552 2 : jArr.Add(d);
6553 : }
6554 1 : jArg.Add("default", jArr);
6555 : }
6556 1 : break;
6557 : }
6558 0 : case GAAT_DATASET:
6559 : case GAAT_DATASET_LIST:
6560 0 : CPLError(CE_Warning, CPLE_AppDefined,
6561 : "Unhandled default value for arg %s",
6562 0 : arg->GetName().c_str());
6563 0 : break;
6564 : }
6565 : }
6566 :
6567 5195 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
6568 5195 : if (!std::isnan(minVal))
6569 : {
6570 668 : if (arg->GetType() == GAAT_INTEGER ||
6571 260 : arg->GetType() == GAAT_INTEGER_LIST)
6572 171 : jArg.Add("min_value", static_cast<int>(minVal));
6573 : else
6574 237 : jArg.Add("min_value", minVal);
6575 408 : jArg.Add("min_value_is_included", minValIsIncluded);
6576 : }
6577 :
6578 5195 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
6579 5195 : if (!std::isnan(maxVal))
6580 : {
6581 199 : if (arg->GetType() == GAAT_INTEGER ||
6582 82 : arg->GetType() == GAAT_INTEGER_LIST)
6583 35 : jArg.Add("max_value", static_cast<int>(maxVal));
6584 : else
6585 82 : jArg.Add("max_value", maxVal);
6586 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
6587 : }
6588 :
6589 5195 : jArg.Add("required", arg->IsRequired());
6590 5195 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
6591 : {
6592 1429 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
6593 1429 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
6594 1429 : jArg.Add("min_count", arg->GetMinCount());
6595 1429 : jArg.Add("max_count", arg->GetMaxCount());
6596 : }
6597 5195 : jArg.Add("category", arg->GetCategory());
6598 :
6599 10149 : if (arg->GetType() == GAAT_DATASET ||
6600 4954 : arg->GetType() == GAAT_DATASET_LIST)
6601 : {
6602 : {
6603 436 : CPLJSONArray jAr;
6604 436 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
6605 305 : jAr.Add("raster");
6606 436 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
6607 167 : jAr.Add("vector");
6608 436 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
6609 28 : jAr.Add("multidim_raster");
6610 436 : jArg.Add("dataset_type", jAr);
6611 : }
6612 :
6613 596 : const auto GetFlags = [](int flags)
6614 : {
6615 596 : CPLJSONArray jAr;
6616 596 : if (flags & GADV_NAME)
6617 436 : jAr.Add("name");
6618 596 : if (flags & GADV_OBJECT)
6619 554 : jAr.Add("dataset");
6620 596 : return jAr;
6621 : };
6622 :
6623 436 : if (arg->IsInput())
6624 : {
6625 436 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
6626 : }
6627 436 : if (arg->IsOutput())
6628 : {
6629 160 : jArg.Add("output_flags",
6630 320 : GetFlags(arg->GetDatasetOutputFlags()));
6631 : }
6632 : }
6633 :
6634 5195 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6635 5195 : if (!mutualExclusionGroup.empty())
6636 : {
6637 639 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
6638 : }
6639 :
6640 10390 : const auto &metadata = arg->GetMetadata();
6641 5195 : if (!metadata.empty())
6642 : {
6643 429 : CPLJSONObject jMetadata;
6644 897 : for (const auto &[key, values] : metadata)
6645 : {
6646 936 : CPLJSONArray jValue;
6647 1134 : for (const auto &value : values)
6648 666 : jValue.Add(value);
6649 468 : jMetadata.Add(key, jValue);
6650 : }
6651 429 : jArg.Add("metadata", jMetadata);
6652 : }
6653 :
6654 10390 : return jArg;
6655 : };
6656 :
6657 : {
6658 548 : CPLJSONArray jArgs;
6659 8606 : for (const auto &arg : m_args)
6660 : {
6661 8058 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
6662 4985 : jArgs.Add(ProcessArg(arg.get()));
6663 : }
6664 548 : oRoot.Add("input_arguments", jArgs);
6665 : }
6666 :
6667 : {
6668 548 : CPLJSONArray jArgs;
6669 8606 : for (const auto &arg : m_args)
6670 : {
6671 8058 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
6672 50 : jArgs.Add(ProcessArg(arg.get()));
6673 : }
6674 548 : oRoot.Add("output_arguments", jArgs);
6675 : }
6676 :
6677 : {
6678 548 : CPLJSONArray jArgs;
6679 8606 : for (const auto &arg : m_args)
6680 : {
6681 8058 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
6682 160 : jArgs.Add(ProcessArg(arg.get()));
6683 : }
6684 548 : oRoot.Add("input_output_arguments", jArgs);
6685 : }
6686 :
6687 548 : if (m_supportsStreamedOutput)
6688 : {
6689 109 : oRoot.Add("supports_streamed_output", true);
6690 : }
6691 :
6692 1096 : return oDoc.SaveAsString();
6693 : }
6694 :
6695 : /************************************************************************/
6696 : /* GDALAlgorithm::GetAutoComplete() */
6697 : /************************************************************************/
6698 :
6699 : std::vector<std::string>
6700 236 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
6701 : bool lastWordIsComplete, bool showAllOptions)
6702 : {
6703 472 : std::vector<std::string> ret;
6704 :
6705 : // Get inner-most algorithm
6706 236 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
6707 236 : GDALAlgorithm *curAlg = this;
6708 467 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
6709 : {
6710 : auto subAlg = curAlg->InstantiateSubAlgorithm(
6711 336 : args.front(), /* suggestionAllowed = */ false);
6712 336 : if (!subAlg)
6713 104 : break;
6714 232 : if (args.size() == 1 && !lastWordIsComplete)
6715 : {
6716 5 : int nCount = 0;
6717 113 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
6718 : {
6719 108 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
6720 6 : nCount++;
6721 : }
6722 5 : if (nCount >= 2)
6723 : {
6724 11 : for (const std::string &subAlgName :
6725 23 : curAlg->GetSubAlgorithmNames())
6726 : {
6727 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
6728 11 : if (subAlg && !subAlg->IsHidden())
6729 11 : ret.push_back(subAlg->GetName());
6730 : }
6731 1 : return ret;
6732 : }
6733 : }
6734 231 : showAllOptions = false;
6735 231 : args.erase(args.begin());
6736 231 : curAlgHolder = std::move(subAlg);
6737 231 : curAlg = curAlgHolder.get();
6738 : }
6739 235 : if (curAlg != this)
6740 : {
6741 126 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
6742 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
6743 126 : /* showAllOptions = */ false);
6744 : }
6745 :
6746 218 : std::string option;
6747 218 : std::string value;
6748 109 : ExtractLastOptionAndValue(args, option, value);
6749 :
6750 136 : if (option.empty() && !args.empty() && !args.back().empty() &&
6751 27 : args.back()[0] == '-')
6752 : {
6753 24 : const auto &lastArg = args.back();
6754 : // List available options
6755 337 : for (const auto &arg : GetArgs())
6756 : {
6757 579 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
6758 527 : (!showAllOptions &&
6759 714 : (arg->GetName() == "help" || arg->GetName() == "config" ||
6760 430 : arg->GetName() == "version" ||
6761 215 : arg->GetName() == "json-usage")))
6762 : {
6763 116 : continue;
6764 : }
6765 197 : if (!arg->GetShortName().empty())
6766 : {
6767 126 : std::string str = std::string("-").append(arg->GetShortName());
6768 42 : if (lastArg == str)
6769 0 : ret.push_back(std::move(str));
6770 : }
6771 197 : if (lastArg != "-" && lastArg != "--")
6772 : {
6773 52 : for (const std::string &alias : arg->GetAliases())
6774 : {
6775 48 : std::string str = std::string("--").append(alias);
6776 16 : if (cpl::starts_with(str, lastArg))
6777 3 : ret.push_back(std::move(str));
6778 : }
6779 : }
6780 197 : if (!arg->GetName().empty())
6781 : {
6782 591 : std::string str = std::string("--").append(arg->GetName());
6783 197 : if (cpl::starts_with(str, lastArg))
6784 163 : ret.push_back(std::move(str));
6785 : }
6786 : }
6787 24 : std::sort(ret.begin(), ret.end());
6788 : }
6789 85 : else if (!option.empty())
6790 : {
6791 : // List possible choices for current option
6792 79 : auto arg = GetArg(option);
6793 79 : if (arg && arg->GetType() != GAAT_BOOLEAN)
6794 : {
6795 79 : ret = arg->GetChoices();
6796 79 : if (ret.empty())
6797 : {
6798 : {
6799 74 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6800 74 : SetParseForAutoCompletion();
6801 74 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6802 : }
6803 74 : ret = arg->GetAutoCompleteChoices(value);
6804 : }
6805 : else
6806 : {
6807 5 : std::sort(ret.begin(), ret.end());
6808 : }
6809 79 : if (!ret.empty() && ret.back() == value)
6810 : {
6811 2 : ret.clear();
6812 : }
6813 77 : else if (ret.empty())
6814 : {
6815 9 : ret.push_back("**");
6816 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6817 18 : ret.push_back(std::string("\xC2\xA0"
6818 : "description: ")
6819 9 : .append(arg->GetDescription()));
6820 : }
6821 : }
6822 : }
6823 : else
6824 : {
6825 : // List possible sub-algorithms
6826 59 : for (const std::string &subAlgName : GetSubAlgorithmNames())
6827 : {
6828 106 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6829 53 : if (subAlg && !subAlg->IsHidden())
6830 53 : ret.push_back(subAlg->GetName());
6831 : }
6832 6 : if (!ret.empty())
6833 : {
6834 2 : std::sort(ret.begin(), ret.end());
6835 : }
6836 :
6837 : // Try filenames
6838 6 : if (ret.empty() && !args.empty())
6839 : {
6840 : {
6841 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6842 3 : SetParseForAutoCompletion();
6843 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6844 : }
6845 :
6846 3 : const std::string &lastArg = args.back();
6847 3 : GDALAlgorithmArg *arg = nullptr;
6848 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
6849 21 : "like", "source", "destination"})
6850 : {
6851 18 : if (!arg)
6852 : {
6853 3 : auto newArg = GetArg(name);
6854 3 : if (newArg)
6855 : {
6856 3 : if (!newArg->IsExplicitlySet())
6857 : {
6858 0 : arg = newArg;
6859 : }
6860 6 : else if (newArg->GetType() == GAAT_STRING ||
6861 5 : newArg->GetType() == GAAT_STRING_LIST ||
6862 8 : newArg->GetType() == GAAT_DATASET ||
6863 2 : newArg->GetType() == GAAT_DATASET_LIST)
6864 : {
6865 : VSIStatBufL sStat;
6866 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
6867 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
6868 : {
6869 3 : arg = newArg;
6870 : }
6871 : }
6872 : }
6873 : }
6874 : }
6875 3 : if (arg)
6876 : {
6877 3 : ret = arg->GetAutoCompleteChoices(lastArg);
6878 : }
6879 : }
6880 : }
6881 :
6882 109 : return ret;
6883 : }
6884 :
6885 : /************************************************************************/
6886 : /* GDALAlgorithm::GetFieldIndices() */
6887 : /************************************************************************/
6888 :
6889 43 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
6890 : OGRLayerH hLayer, std::vector<int> &indices)
6891 : {
6892 43 : VALIDATE_POINTER1(hLayer, __func__, false);
6893 :
6894 43 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
6895 :
6896 43 : if (names.size() == 1 && names[0] == "ALL")
6897 : {
6898 11 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
6899 27 : for (int i = 0; i < nSrcFieldCount; ++i)
6900 : {
6901 16 : indices.push_back(i);
6902 : }
6903 : }
6904 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
6905 : {
6906 6 : std::set<int> fieldsAdded;
6907 14 : for (const std::string &osFieldName : names)
6908 : {
6909 :
6910 : const int nIdx =
6911 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
6912 :
6913 10 : if (nIdx < 0)
6914 : {
6915 2 : CPLError(CE_Failure, CPLE_AppDefined,
6916 : "Field '%s' does not exist in layer '%s'",
6917 2 : osFieldName.c_str(), layer.GetName());
6918 2 : return false;
6919 : }
6920 :
6921 8 : if (fieldsAdded.insert(nIdx).second)
6922 : {
6923 7 : indices.push_back(nIdx);
6924 : }
6925 : }
6926 : }
6927 :
6928 41 : return true;
6929 : }
6930 :
6931 : /************************************************************************/
6932 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
6933 : /************************************************************************/
6934 :
6935 109 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
6936 : std::string &option,
6937 : std::string &value) const
6938 : {
6939 109 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
6940 : {
6941 80 : const auto nPosEqual = args.back().find('=');
6942 80 : if (nPosEqual == std::string::npos)
6943 : {
6944 : // Deal with "gdal ... --option"
6945 61 : if (GetArg(args.back()))
6946 : {
6947 37 : option = args.back();
6948 37 : args.pop_back();
6949 : }
6950 : }
6951 : else
6952 : {
6953 : // Deal with "gdal ... --option=<value>"
6954 19 : if (GetArg(args.back().substr(0, nPosEqual)))
6955 : {
6956 19 : option = args.back().substr(0, nPosEqual);
6957 19 : value = args.back().substr(nPosEqual + 1);
6958 19 : args.pop_back();
6959 : }
6960 : }
6961 : }
6962 53 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
6963 24 : args[args.size() - 2][0] == '-')
6964 : {
6965 : // Deal with "gdal ... --option <value>"
6966 23 : auto arg = GetArg(args[args.size() - 2]);
6967 23 : if (arg && arg->GetType() != GAAT_BOOLEAN)
6968 : {
6969 23 : option = args[args.size() - 2];
6970 23 : value = args.back();
6971 23 : args.pop_back();
6972 : }
6973 : }
6974 :
6975 109 : const auto IsKeyValueOption = [](const std::string &osStr)
6976 : {
6977 293 : return osStr == "--co" || osStr == "--creation-option" ||
6978 270 : osStr == "--lco" || osStr == "--layer-creation-option" ||
6979 291 : osStr == "--oo" || osStr == "--open-option";
6980 : };
6981 :
6982 109 : if (IsKeyValueOption(option))
6983 : {
6984 22 : const auto nPosEqual = value.find('=');
6985 22 : if (nPosEqual != std::string::npos)
6986 : {
6987 11 : value.resize(nPosEqual);
6988 : }
6989 : }
6990 109 : }
6991 :
6992 : //! @cond Doxygen_Suppress
6993 :
6994 : /************************************************************************/
6995 : /* GDALContainerAlgorithm::RunImpl() */
6996 : /************************************************************************/
6997 :
6998 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
6999 : {
7000 0 : return false;
7001 : }
7002 :
7003 : //! @endcond
7004 :
7005 : /************************************************************************/
7006 : /* GDALAlgorithmRelease() */
7007 : /************************************************************************/
7008 :
7009 : /** Release a handle to an algorithm.
7010 : *
7011 : * @since 3.11
7012 : */
7013 11698 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7014 : {
7015 11698 : delete hAlg;
7016 11698 : }
7017 :
7018 : /************************************************************************/
7019 : /* GDALAlgorithmGetName() */
7020 : /************************************************************************/
7021 :
7022 : /** Return the algorithm name.
7023 : *
7024 : * @param hAlg Handle to an algorithm. Must NOT be null.
7025 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7026 : * be freed.
7027 : * @since 3.11
7028 : */
7029 5490 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7030 : {
7031 5490 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7032 5490 : return hAlg->ptr->GetName().c_str();
7033 : }
7034 :
7035 : /************************************************************************/
7036 : /* GDALAlgorithmGetDescription() */
7037 : /************************************************************************/
7038 :
7039 : /** Return the algorithm (short) description.
7040 : *
7041 : * @param hAlg Handle to an algorithm. Must NOT be null.
7042 : * @return algorithm description whose lifetime is bound to hAlg and which must
7043 : * not be freed.
7044 : * @since 3.11
7045 : */
7046 5414 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7047 : {
7048 5414 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7049 5414 : return hAlg->ptr->GetDescription().c_str();
7050 : }
7051 :
7052 : /************************************************************************/
7053 : /* GDALAlgorithmGetLongDescription() */
7054 : /************************************************************************/
7055 :
7056 : /** Return the algorithm (longer) description.
7057 : *
7058 : * @param hAlg Handle to an algorithm. Must NOT be null.
7059 : * @return algorithm description whose lifetime is bound to hAlg and which must
7060 : * not be freed.
7061 : * @since 3.11
7062 : */
7063 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7064 : {
7065 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7066 2 : return hAlg->ptr->GetLongDescription().c_str();
7067 : }
7068 :
7069 : /************************************************************************/
7070 : /* GDALAlgorithmGetHelpFullURL() */
7071 : /************************************************************************/
7072 :
7073 : /** Return the algorithm full URL.
7074 : *
7075 : * @param hAlg Handle to an algorithm. Must NOT be null.
7076 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7077 : * not be freed.
7078 : * @since 3.11
7079 : */
7080 4799 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7081 : {
7082 4799 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7083 4799 : return hAlg->ptr->GetHelpFullURL().c_str();
7084 : }
7085 :
7086 : /************************************************************************/
7087 : /* GDALAlgorithmHasSubAlgorithms() */
7088 : /************************************************************************/
7089 :
7090 : /** Return whether the algorithm has sub-algorithms.
7091 : *
7092 : * @param hAlg Handle to an algorithm. Must NOT be null.
7093 : * @since 3.11
7094 : */
7095 8680 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7096 : {
7097 8680 : VALIDATE_POINTER1(hAlg, __func__, false);
7098 8680 : return hAlg->ptr->HasSubAlgorithms();
7099 : }
7100 :
7101 : /************************************************************************/
7102 : /* GDALAlgorithmGetSubAlgorithmNames() */
7103 : /************************************************************************/
7104 :
7105 : /** Get the names of registered algorithms.
7106 : *
7107 : * @param hAlg Handle to an algorithm. Must NOT be null.
7108 : * @return a NULL terminated list of names, which must be destroyed with
7109 : * CSLDestroy()
7110 : * @since 3.11
7111 : */
7112 665 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7113 : {
7114 665 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7115 665 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7116 : }
7117 :
7118 : /************************************************************************/
7119 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7120 : /************************************************************************/
7121 :
7122 : /** Instantiate an algorithm by its name (or its alias).
7123 : *
7124 : * @param hAlg Handle to an algorithm. Must NOT be null.
7125 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7126 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7127 : * or NULL if the algorithm does not exist or another error occurred.
7128 : * @since 3.11
7129 : */
7130 8155 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7131 : const char *pszSubAlgName)
7132 : {
7133 8155 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7134 8155 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7135 16310 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7136 : return subAlg
7137 16310 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7138 16310 : : nullptr;
7139 : }
7140 :
7141 : /************************************************************************/
7142 : /* GDALAlgorithmParseCommandLineArguments() */
7143 : /************************************************************************/
7144 :
7145 : /** Parse a command line argument, which does not include the algorithm
7146 : * name, to set the value of corresponding arguments.
7147 : *
7148 : * @param hAlg Handle to an algorithm. Must NOT be null.
7149 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7150 : * @return true if successful, false otherwise
7151 : * @since 3.11
7152 : */
7153 :
7154 350 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7155 : CSLConstList papszArgs)
7156 : {
7157 350 : VALIDATE_POINTER1(hAlg, __func__, false);
7158 350 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7159 : }
7160 :
7161 : /************************************************************************/
7162 : /* GDALAlgorithmGetActualAlgorithm() */
7163 : /************************************************************************/
7164 :
7165 : /** Return the actual algorithm that is going to be invoked, when the
7166 : * current algorithm has sub-algorithms.
7167 : *
7168 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7169 : *
7170 : * Note that the lifetime of the returned algorithm does not exceed the one of
7171 : * the hAlg instance that owns it.
7172 : *
7173 : * @param hAlg Handle to an algorithm. Must NOT be null.
7174 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7175 : * @since 3.11
7176 : */
7177 881 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7178 : {
7179 881 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7180 881 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7181 : }
7182 :
7183 : /************************************************************************/
7184 : /* GDALAlgorithmRun() */
7185 : /************************************************************************/
7186 :
7187 : /** Execute the algorithm, starting with ValidateArguments() and then
7188 : * calling RunImpl().
7189 : *
7190 : * @param hAlg Handle to an algorithm. Must NOT be null.
7191 : * @param pfnProgress Progress callback. May be null.
7192 : * @param pProgressData Progress callback user data. May be null.
7193 : * @return true if successful, false otherwise
7194 : * @since 3.11
7195 : */
7196 :
7197 2512 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7198 : void *pProgressData)
7199 : {
7200 2512 : VALIDATE_POINTER1(hAlg, __func__, false);
7201 2512 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7202 : }
7203 :
7204 : /************************************************************************/
7205 : /* GDALAlgorithmFinalize() */
7206 : /************************************************************************/
7207 :
7208 : /** Complete any pending actions, and return the final status.
7209 : * This is typically useful for algorithm that generate an output dataset.
7210 : *
7211 : * Note that this function does *NOT* release memory associated with the
7212 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7213 : *
7214 : * @param hAlg Handle to an algorithm. Must NOT be null.
7215 : * @return true if successful, false otherwise
7216 : * @since 3.11
7217 : */
7218 :
7219 861 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7220 : {
7221 861 : VALIDATE_POINTER1(hAlg, __func__, false);
7222 861 : return hAlg->ptr->Finalize();
7223 : }
7224 :
7225 : /************************************************************************/
7226 : /* GDALAlgorithmGetUsageAsJSON() */
7227 : /************************************************************************/
7228 :
7229 : /** Return the usage of the algorithm as a JSON-serialized string.
7230 : *
7231 : * This can be used to dynamically generate interfaces to algorithms.
7232 : *
7233 : * @param hAlg Handle to an algorithm. Must NOT be null.
7234 : * @return a string that must be freed with CPLFree()
7235 : * @since 3.11
7236 : */
7237 4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7238 : {
7239 4 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7240 4 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7241 : }
7242 :
7243 : /************************************************************************/
7244 : /* GDALAlgorithmGetArgNames() */
7245 : /************************************************************************/
7246 :
7247 : /** Return the list of available argument names.
7248 : *
7249 : * @param hAlg Handle to an algorithm. Must NOT be null.
7250 : * @return a NULL terminated list of names, which must be destroyed with
7251 : * CSLDestroy()
7252 : * @since 3.11
7253 : */
7254 14899 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7255 : {
7256 14899 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7257 29798 : CPLStringList list;
7258 334858 : for (const auto &arg : hAlg->ptr->GetArgs())
7259 319959 : list.AddString(arg->GetName().c_str());
7260 14899 : return list.StealList();
7261 : }
7262 :
7263 : /************************************************************************/
7264 : /* GDALAlgorithmGetArg() */
7265 : /************************************************************************/
7266 :
7267 : /** Return an argument from its name.
7268 : *
7269 : * The lifetime of the returned object does not exceed the one of hAlg.
7270 : *
7271 : * @param hAlg Handle to an algorithm. Must NOT be null.
7272 : * @param pszArgName Argument name. Must NOT be null.
7273 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7274 : * or nullptr in case of error
7275 : * @since 3.11
7276 : */
7277 320828 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7278 : const char *pszArgName)
7279 : {
7280 320828 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7281 320828 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7282 641656 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7283 320828 : /* isConst = */ true);
7284 320828 : if (!arg)
7285 3 : return nullptr;
7286 320825 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7287 : }
7288 :
7289 : /************************************************************************/
7290 : /* GDALAlgorithmGetArgNonConst() */
7291 : /************************************************************************/
7292 :
7293 : /** Return an argument from its name, possibly allowing creation of user-provided
7294 : * argument if the algorithm allow it.
7295 : *
7296 : * The lifetime of the returned object does not exceed the one of hAlg.
7297 : *
7298 : * @param hAlg Handle to an algorithm. Must NOT be null.
7299 : * @param pszArgName Argument name. Must NOT be null.
7300 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7301 : * or nullptr in case of error
7302 : * @since 3.12
7303 : */
7304 8939 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7305 : const char *pszArgName)
7306 : {
7307 8939 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7308 8939 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7309 17878 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7310 8939 : /* isConst = */ false);
7311 8939 : if (!arg)
7312 1 : return nullptr;
7313 8938 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7314 : }
7315 :
7316 : /************************************************************************/
7317 : /* GDALAlgorithmArgRelease() */
7318 : /************************************************************************/
7319 :
7320 : /** Release a handle to an argument.
7321 : *
7322 : * @since 3.11
7323 : */
7324 329763 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7325 : {
7326 329763 : delete hArg;
7327 329763 : }
7328 :
7329 : /************************************************************************/
7330 : /* GDALAlgorithmArgGetName() */
7331 : /************************************************************************/
7332 :
7333 : /** Return the name of an argument.
7334 : *
7335 : * @param hArg Handle to an argument. Must NOT be null.
7336 : * @return argument name whose lifetime is bound to hArg and which must not
7337 : * be freed.
7338 : * @since 3.11
7339 : */
7340 17962 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7341 : {
7342 17962 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7343 17962 : return hArg->ptr->GetName().c_str();
7344 : }
7345 :
7346 : /************************************************************************/
7347 : /* GDALAlgorithmArgGetType() */
7348 : /************************************************************************/
7349 :
7350 : /** Get the type of an argument
7351 : *
7352 : * @param hArg Handle to an argument. Must NOT be null.
7353 : * @since 3.11
7354 : */
7355 408662 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
7356 : {
7357 408662 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
7358 408662 : return hArg->ptr->GetType();
7359 : }
7360 :
7361 : /************************************************************************/
7362 : /* GDALAlgorithmArgGetDescription() */
7363 : /************************************************************************/
7364 :
7365 : /** Return the description of an argument.
7366 : *
7367 : * @param hArg Handle to an argument. Must NOT be null.
7368 : * @return argument description whose lifetime is bound to hArg and which must not
7369 : * be freed.
7370 : * @since 3.11
7371 : */
7372 81837 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
7373 : {
7374 81837 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7375 81837 : return hArg->ptr->GetDescription().c_str();
7376 : }
7377 :
7378 : /************************************************************************/
7379 : /* GDALAlgorithmArgGetShortName() */
7380 : /************************************************************************/
7381 :
7382 : /** Return the short name, or empty string if there is none
7383 : *
7384 : * @param hArg Handle to an argument. Must NOT be null.
7385 : * @return short name whose lifetime is bound to hArg and which must not
7386 : * be freed.
7387 : * @since 3.11
7388 : */
7389 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
7390 : {
7391 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7392 1 : return hArg->ptr->GetShortName().c_str();
7393 : }
7394 :
7395 : /************************************************************************/
7396 : /* GDALAlgorithmArgGetAliases() */
7397 : /************************************************************************/
7398 :
7399 : /** Return the aliases (potentially none)
7400 : *
7401 : * @param hArg Handle to an argument. Must NOT be null.
7402 : * @return a NULL terminated list of names, which must be destroyed with
7403 : * CSLDestroy()
7404 :
7405 : * @since 3.11
7406 : */
7407 1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
7408 : {
7409 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7410 1 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
7411 : }
7412 :
7413 : /************************************************************************/
7414 : /* GDALAlgorithmArgGetMetaVar() */
7415 : /************************************************************************/
7416 :
7417 : /** Return the "meta-var" hint.
7418 : *
7419 : * By default, the meta-var value is the long name of the argument in
7420 : * upper case.
7421 : *
7422 : * @param hArg Handle to an argument. Must NOT be null.
7423 : * @return meta-var hint whose lifetime is bound to hArg and which must not
7424 : * be freed.
7425 : * @since 3.11
7426 : */
7427 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
7428 : {
7429 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7430 1 : return hArg->ptr->GetMetaVar().c_str();
7431 : }
7432 :
7433 : /************************************************************************/
7434 : /* GDALAlgorithmArgGetCategory() */
7435 : /************************************************************************/
7436 :
7437 : /** Return the argument category
7438 : *
7439 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
7440 : *
7441 : * @param hArg Handle to an argument. Must NOT be null.
7442 : * @return category whose lifetime is bound to hArg and which must not
7443 : * be freed.
7444 : * @since 3.11
7445 : */
7446 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
7447 : {
7448 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7449 1 : return hArg->ptr->GetCategory().c_str();
7450 : }
7451 :
7452 : /************************************************************************/
7453 : /* GDALAlgorithmArgIsPositional() */
7454 : /************************************************************************/
7455 :
7456 : /** Return if the argument is a positional one.
7457 : *
7458 : * @param hArg Handle to an argument. Must NOT be null.
7459 : * @since 3.11
7460 : */
7461 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
7462 : {
7463 1 : VALIDATE_POINTER1(hArg, __func__, false);
7464 1 : return hArg->ptr->IsPositional();
7465 : }
7466 :
7467 : /************************************************************************/
7468 : /* GDALAlgorithmArgIsRequired() */
7469 : /************************************************************************/
7470 :
7471 : /** Return whether the argument is required. Defaults to false.
7472 : *
7473 : * @param hArg Handle to an argument. Must NOT be null.
7474 : * @since 3.11
7475 : */
7476 154981 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
7477 : {
7478 154981 : VALIDATE_POINTER1(hArg, __func__, false);
7479 154981 : return hArg->ptr->IsRequired();
7480 : }
7481 :
7482 : /************************************************************************/
7483 : /* GDALAlgorithmArgGetMinCount() */
7484 : /************************************************************************/
7485 :
7486 : /** Return the minimum number of values for the argument.
7487 : *
7488 : * Defaults to 0.
7489 : * Only applies to list type of arguments.
7490 : *
7491 : * @param hArg Handle to an argument. Must NOT be null.
7492 : * @since 3.11
7493 : */
7494 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
7495 : {
7496 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7497 1 : return hArg->ptr->GetMinCount();
7498 : }
7499 :
7500 : /************************************************************************/
7501 : /* GDALAlgorithmArgGetMaxCount() */
7502 : /************************************************************************/
7503 :
7504 : /** Return the maximum number of values for the argument.
7505 : *
7506 : * Defaults to 1 for scalar types, and INT_MAX for list types.
7507 : * Only applies to list type of arguments.
7508 : *
7509 : * @param hArg Handle to an argument. Must NOT be null.
7510 : * @since 3.11
7511 : */
7512 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
7513 : {
7514 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7515 1 : return hArg->ptr->GetMaxCount();
7516 : }
7517 :
7518 : /************************************************************************/
7519 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
7520 : /************************************************************************/
7521 :
7522 : /** Return whether, for list type of arguments, several values, space
7523 : * separated, may be specified. That is "--foo=bar,baz".
7524 : * The default is true.
7525 : *
7526 : * @param hArg Handle to an argument. Must NOT be null.
7527 : * @since 3.11
7528 : */
7529 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
7530 : {
7531 1 : VALIDATE_POINTER1(hArg, __func__, false);
7532 1 : return hArg->ptr->GetPackedValuesAllowed();
7533 : }
7534 :
7535 : /************************************************************************/
7536 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
7537 : /************************************************************************/
7538 :
7539 : /** Return whether, for list type of arguments, the argument may be
7540 : * repeated. That is "--foo=bar --foo=baz".
7541 : * The default is true.
7542 : *
7543 : * @param hArg Handle to an argument. Must NOT be null.
7544 : * @since 3.11
7545 : */
7546 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
7547 : {
7548 1 : VALIDATE_POINTER1(hArg, __func__, false);
7549 1 : return hArg->ptr->GetRepeatedArgAllowed();
7550 : }
7551 :
7552 : /************************************************************************/
7553 : /* GDALAlgorithmArgGetChoices() */
7554 : /************************************************************************/
7555 :
7556 : /** Return the allowed values (as strings) for the argument.
7557 : *
7558 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
7559 : *
7560 : * @param hArg Handle to an argument. Must NOT be null.
7561 : * @return a NULL terminated list of names, which must be destroyed with
7562 : * CSLDestroy()
7563 :
7564 : * @since 3.11
7565 : */
7566 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
7567 : {
7568 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7569 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
7570 : }
7571 :
7572 : /************************************************************************/
7573 : /* GDALAlgorithmArgGetMetadataItem() */
7574 : /************************************************************************/
7575 :
7576 : /** Return the values of the metadata item of an argument.
7577 : *
7578 : * @param hArg Handle to an argument. Must NOT be null.
7579 : * @param pszItem Name of the item. Must NOT be null.
7580 : * @return a NULL terminated list of values, which must be destroyed with
7581 : * CSLDestroy()
7582 :
7583 : * @since 3.11
7584 : */
7585 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
7586 : const char *pszItem)
7587 : {
7588 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7589 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
7590 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
7591 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
7592 : }
7593 :
7594 : /************************************************************************/
7595 : /* GDALAlgorithmArgIsExplicitlySet() */
7596 : /************************************************************************/
7597 :
7598 : /** Return whether the argument value has been explicitly set with Set()
7599 : *
7600 : * @param hArg Handle to an argument. Must NOT be null.
7601 : * @since 3.11
7602 : */
7603 578 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
7604 : {
7605 578 : VALIDATE_POINTER1(hArg, __func__, false);
7606 578 : return hArg->ptr->IsExplicitlySet();
7607 : }
7608 :
7609 : /************************************************************************/
7610 : /* GDALAlgorithmArgHasDefaultValue() */
7611 : /************************************************************************/
7612 :
7613 : /** Return if the argument has a declared default value.
7614 : *
7615 : * @param hArg Handle to an argument. Must NOT be null.
7616 : * @since 3.11
7617 : */
7618 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
7619 : {
7620 2 : VALIDATE_POINTER1(hArg, __func__, false);
7621 2 : return hArg->ptr->HasDefaultValue();
7622 : }
7623 :
7624 : /************************************************************************/
7625 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
7626 : /************************************************************************/
7627 :
7628 : /** Return the argument default value as a integer.
7629 : *
7630 : * Must only be called on arguments whose type is GAAT_BOOLEAN
7631 : *
7632 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7633 : * argument has a default value.
7634 : *
7635 : * @param hArg Handle to an argument. Must NOT be null.
7636 : * @since 3.12
7637 : */
7638 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
7639 : {
7640 3 : VALIDATE_POINTER1(hArg, __func__, false);
7641 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
7642 : {
7643 1 : CPLError(CE_Failure, CPLE_AppDefined,
7644 : "%s must only be called on arguments of type GAAT_BOOLEAN",
7645 : __func__);
7646 1 : return false;
7647 : }
7648 2 : return hArg->ptr->GetDefault<bool>();
7649 : }
7650 :
7651 : /************************************************************************/
7652 : /* GDALAlgorithmArgGetDefaultAsString() */
7653 : /************************************************************************/
7654 :
7655 : /** Return the argument default value as a string.
7656 : *
7657 : * Must only be called on arguments whose type is GAAT_STRING.
7658 : *
7659 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7660 : * argument has a default value.
7661 : *
7662 : * @param hArg Handle to an argument. Must NOT be null.
7663 : * @return string whose lifetime is bound to hArg and which must not
7664 : * be freed.
7665 : * @since 3.11
7666 : */
7667 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
7668 : {
7669 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7670 3 : if (hArg->ptr->GetType() != GAAT_STRING)
7671 : {
7672 2 : CPLError(CE_Failure, CPLE_AppDefined,
7673 : "%s must only be called on arguments of type GAAT_STRING",
7674 : __func__);
7675 2 : return nullptr;
7676 : }
7677 1 : return hArg->ptr->GetDefault<std::string>().c_str();
7678 : }
7679 :
7680 : /************************************************************************/
7681 : /* GDALAlgorithmArgGetDefaultAsInteger() */
7682 : /************************************************************************/
7683 :
7684 : /** Return the argument default value as a integer.
7685 : *
7686 : * Must only be called on arguments whose type is GAAT_INTEGER
7687 : *
7688 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7689 : * argument has a default value.
7690 : *
7691 : * @param hArg Handle to an argument. Must NOT be null.
7692 : * @since 3.12
7693 : */
7694 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
7695 : {
7696 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7697 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
7698 : {
7699 2 : CPLError(CE_Failure, CPLE_AppDefined,
7700 : "%s must only be called on arguments of type GAAT_INTEGER",
7701 : __func__);
7702 2 : return 0;
7703 : }
7704 1 : return hArg->ptr->GetDefault<int>();
7705 : }
7706 :
7707 : /************************************************************************/
7708 : /* GDALAlgorithmArgGetDefaultAsDouble() */
7709 : /************************************************************************/
7710 :
7711 : /** Return the argument default value as a double.
7712 : *
7713 : * Must only be called on arguments whose type is GAAT_REAL
7714 : *
7715 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7716 : * argument has a default value.
7717 : *
7718 : * @param hArg Handle to an argument. Must NOT be null.
7719 : * @since 3.12
7720 : */
7721 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
7722 : {
7723 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7724 3 : if (hArg->ptr->GetType() != GAAT_REAL)
7725 : {
7726 2 : CPLError(CE_Failure, CPLE_AppDefined,
7727 : "%s must only be called on arguments of type GAAT_REAL",
7728 : __func__);
7729 2 : return 0;
7730 : }
7731 1 : return hArg->ptr->GetDefault<double>();
7732 : }
7733 :
7734 : /************************************************************************/
7735 : /* GDALAlgorithmArgGetDefaultAsStringList() */
7736 : /************************************************************************/
7737 :
7738 : /** Return the argument default value as a string list.
7739 : *
7740 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
7741 : *
7742 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7743 : * argument has a default value.
7744 : *
7745 : * @param hArg Handle to an argument. Must NOT be null.
7746 : * @return a NULL terminated list of names, which must be destroyed with
7747 : * CSLDestroy()
7748 :
7749 : * @since 3.12
7750 : */
7751 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
7752 : {
7753 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7754 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
7755 : {
7756 2 : CPLError(CE_Failure, CPLE_AppDefined,
7757 : "%s must only be called on arguments of type GAAT_STRING_LIST",
7758 : __func__);
7759 2 : return nullptr;
7760 : }
7761 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
7762 1 : .StealList();
7763 : }
7764 :
7765 : /************************************************************************/
7766 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
7767 : /************************************************************************/
7768 :
7769 : /** Return the argument default value as a integer list.
7770 : *
7771 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
7772 : *
7773 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7774 : * argument has a default value.
7775 : *
7776 : * @param hArg Handle to an argument. Must NOT be null.
7777 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7778 : * @since 3.12
7779 : */
7780 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
7781 : size_t *pnCount)
7782 : {
7783 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7784 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7785 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
7786 : {
7787 2 : CPLError(
7788 : CE_Failure, CPLE_AppDefined,
7789 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
7790 : __func__);
7791 2 : *pnCount = 0;
7792 2 : return nullptr;
7793 : }
7794 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
7795 1 : *pnCount = val.size();
7796 1 : return val.data();
7797 : }
7798 :
7799 : /************************************************************************/
7800 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
7801 : /************************************************************************/
7802 :
7803 : /** Return the argument default value as a real list.
7804 : *
7805 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
7806 : *
7807 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7808 : * argument has a default value.
7809 : *
7810 : * @param hArg Handle to an argument. Must NOT be null.
7811 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7812 : * @since 3.12
7813 : */
7814 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
7815 : size_t *pnCount)
7816 : {
7817 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7818 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7819 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
7820 : {
7821 2 : CPLError(CE_Failure, CPLE_AppDefined,
7822 : "%s must only be called on arguments of type GAAT_REAL_LIST",
7823 : __func__);
7824 2 : *pnCount = 0;
7825 2 : return nullptr;
7826 : }
7827 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
7828 1 : *pnCount = val.size();
7829 1 : return val.data();
7830 : }
7831 :
7832 : /************************************************************************/
7833 : /* GDALAlgorithmArgIsHidden() */
7834 : /************************************************************************/
7835 :
7836 : /** Return whether the argument is hidden (for GDAL internal use)
7837 : *
7838 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
7839 : * GDALAlgorithmArgIsHiddenForAPI().
7840 : *
7841 : * @param hArg Handle to an argument. Must NOT be null.
7842 : * @since 3.12
7843 : */
7844 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
7845 : {
7846 1 : VALIDATE_POINTER1(hArg, __func__, false);
7847 1 : return hArg->ptr->IsHidden();
7848 : }
7849 :
7850 : /************************************************************************/
7851 : /* GDALAlgorithmArgIsHiddenForCLI() */
7852 : /************************************************************************/
7853 :
7854 : /** Return whether the argument must not be mentioned in CLI usage.
7855 : *
7856 : * For example, "output-value" for "gdal raster info", which is only
7857 : * meant when the algorithm is used from a non-CLI context.
7858 : *
7859 : * @param hArg Handle to an argument. Must NOT be null.
7860 : * @since 3.11
7861 : */
7862 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
7863 : {
7864 1 : VALIDATE_POINTER1(hArg, __func__, false);
7865 1 : return hArg->ptr->IsHiddenForCLI();
7866 : }
7867 :
7868 : /************************************************************************/
7869 : /* GDALAlgorithmArgIsHiddenForAPI() */
7870 : /************************************************************************/
7871 :
7872 : /** Return whether the argument must not be mentioned in the context of an
7873 : * API use.
7874 : * Said otherwise, if it is only for CLI usage.
7875 : *
7876 : * For example "--help"
7877 : *
7878 : * @param hArg Handle to an argument. Must NOT be null.
7879 : * @since 3.12
7880 : */
7881 208855 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
7882 : {
7883 208855 : VALIDATE_POINTER1(hArg, __func__, false);
7884 208855 : return hArg->ptr->IsHiddenForAPI();
7885 : }
7886 :
7887 : /************************************************************************/
7888 : /* GDALAlgorithmArgIsOnlyForCLI() */
7889 : /************************************************************************/
7890 :
7891 : /** Return whether the argument must not be mentioned in the context of an
7892 : * API use.
7893 : * Said otherwise, if it is only for CLI usage.
7894 : *
7895 : * For example "--help"
7896 : *
7897 : * @param hArg Handle to an argument. Must NOT be null.
7898 : * @since 3.11
7899 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
7900 : */
7901 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
7902 : {
7903 0 : VALIDATE_POINTER1(hArg, __func__, false);
7904 0 : return hArg->ptr->IsHiddenForAPI();
7905 : }
7906 :
7907 : /************************************************************************/
7908 : /* GDALAlgorithmArgIsInput() */
7909 : /************************************************************************/
7910 :
7911 : /** Indicate whether the value of the argument is read-only during the
7912 : * execution of the algorithm.
7913 : *
7914 : * Default is true.
7915 : *
7916 : * @param hArg Handle to an argument. Must NOT be null.
7917 : * @since 3.11
7918 : */
7919 205903 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
7920 : {
7921 205903 : VALIDATE_POINTER1(hArg, __func__, false);
7922 205903 : return hArg->ptr->IsInput();
7923 : }
7924 :
7925 : /************************************************************************/
7926 : /* GDALAlgorithmArgIsOutput() */
7927 : /************************************************************************/
7928 :
7929 : /** Return whether (at least part of) the value of the argument is set
7930 : * during the execution of the algorithm.
7931 : *
7932 : * For example, "output-value" for "gdal raster info"
7933 : * Default is false.
7934 : * An argument may return both IsInput() and IsOutput() as true.
7935 : * For example the "gdal raster convert" algorithm consumes the dataset
7936 : * name of its "output" argument, and sets the dataset object during its
7937 : * execution.
7938 : *
7939 : * @param hArg Handle to an argument. Must NOT be null.
7940 : * @since 3.11
7941 : */
7942 114032 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
7943 : {
7944 114032 : VALIDATE_POINTER1(hArg, __func__, false);
7945 114032 : return hArg->ptr->IsOutput();
7946 : }
7947 :
7948 : /************************************************************************/
7949 : /* GDALAlgorithmArgGetDatasetType() */
7950 : /************************************************************************/
7951 :
7952 : /** Get which type of dataset is allowed / generated.
7953 : *
7954 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
7955 : * GDAL_OF_MULTIDIM_RASTER.
7956 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7957 : *
7958 : * @param hArg Handle to an argument. Must NOT be null.
7959 : * @since 3.11
7960 : */
7961 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
7962 : {
7963 2 : VALIDATE_POINTER1(hArg, __func__, 0);
7964 2 : return hArg->ptr->GetDatasetType();
7965 : }
7966 :
7967 : /************************************************************************/
7968 : /* GDALAlgorithmArgGetDatasetInputFlags() */
7969 : /************************************************************************/
7970 :
7971 : /** Indicates which components among name and dataset are accepted as
7972 : * input, when this argument serves as an input.
7973 : *
7974 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
7975 : * input.
7976 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
7977 : * accepted as input.
7978 : * If both bits are set, the algorithm can accept either a name or a dataset
7979 : * object.
7980 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7981 : *
7982 : * @param hArg Handle to an argument. Must NOT be null.
7983 : * @return string whose lifetime is bound to hAlg and which must not
7984 : * be freed.
7985 : * @since 3.11
7986 : */
7987 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
7988 : {
7989 2 : VALIDATE_POINTER1(hArg, __func__, 0);
7990 2 : return hArg->ptr->GetDatasetInputFlags();
7991 : }
7992 :
7993 : /************************************************************************/
7994 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
7995 : /************************************************************************/
7996 :
7997 : /** Indicates which components among name and dataset are modified,
7998 : * when this argument serves as an output.
7999 : *
8000 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8001 : * output (that is the algorithm will generate the name. Rarely used).
8002 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8003 : * generated as output, and available for use after the algorithm has
8004 : * completed.
8005 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8006 : *
8007 : * @param hArg Handle to an argument. Must NOT be null.
8008 : * @return string whose lifetime is bound to hAlg and which must not
8009 : * be freed.
8010 : * @since 3.11
8011 : */
8012 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8013 : {
8014 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8015 2 : return hArg->ptr->GetDatasetOutputFlags();
8016 : }
8017 :
8018 : /************************************************************************/
8019 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8020 : /************************************************************************/
8021 :
8022 : /** Return the name of the mutual exclusion group to which this argument
8023 : * belongs to.
8024 : *
8025 : * Or empty string if it does not belong to any exclusion group.
8026 : *
8027 : * @param hArg Handle to an argument. Must NOT be null.
8028 : * @return string whose lifetime is bound to hArg and which must not
8029 : * be freed.
8030 : * @since 3.11
8031 : */
8032 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8033 : {
8034 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8035 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8036 : }
8037 :
8038 : /************************************************************************/
8039 : /* GDALAlgorithmArgGetAsBoolean() */
8040 : /************************************************************************/
8041 :
8042 : /** Return the argument value as a boolean.
8043 : *
8044 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8045 : *
8046 : * @param hArg Handle to an argument. Must NOT be null.
8047 : * @since 3.11
8048 : */
8049 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8050 : {
8051 8 : VALIDATE_POINTER1(hArg, __func__, false);
8052 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8053 : {
8054 1 : CPLError(CE_Failure, CPLE_AppDefined,
8055 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8056 : __func__);
8057 1 : return false;
8058 : }
8059 7 : return hArg->ptr->Get<bool>();
8060 : }
8061 :
8062 : /************************************************************************/
8063 : /* GDALAlgorithmArgGetAsString() */
8064 : /************************************************************************/
8065 :
8066 : /** Return the argument value as a string.
8067 : *
8068 : * Must only be called on arguments whose type is GAAT_STRING.
8069 : *
8070 : * @param hArg Handle to an argument. Must NOT be null.
8071 : * @return string whose lifetime is bound to hArg and which must not
8072 : * be freed.
8073 : * @since 3.11
8074 : */
8075 345 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8076 : {
8077 345 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8078 345 : if (hArg->ptr->GetType() != GAAT_STRING)
8079 : {
8080 1 : CPLError(CE_Failure, CPLE_AppDefined,
8081 : "%s must only be called on arguments of type GAAT_STRING",
8082 : __func__);
8083 1 : return nullptr;
8084 : }
8085 344 : return hArg->ptr->Get<std::string>().c_str();
8086 : }
8087 :
8088 : /************************************************************************/
8089 : /* GDALAlgorithmArgGetAsDatasetValue() */
8090 : /************************************************************************/
8091 :
8092 : /** Return the argument value as a GDALArgDatasetValueH.
8093 : *
8094 : * Must only be called on arguments whose type is GAAT_DATASET
8095 : *
8096 : * @param hArg Handle to an argument. Must NOT be null.
8097 : * @return handle to a GDALArgDatasetValue that must be released with
8098 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8099 : * the one of hArg.
8100 : * @since 3.11
8101 : */
8102 2921 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8103 : {
8104 2921 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8105 2921 : if (hArg->ptr->GetType() != GAAT_DATASET)
8106 : {
8107 1 : CPLError(CE_Failure, CPLE_AppDefined,
8108 : "%s must only be called on arguments of type GAAT_DATASET",
8109 : __func__);
8110 1 : return nullptr;
8111 : }
8112 2920 : return std::make_unique<GDALArgDatasetValueHS>(
8113 5840 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8114 2920 : .release();
8115 : }
8116 :
8117 : /************************************************************************/
8118 : /* GDALAlgorithmArgGetAsInteger() */
8119 : /************************************************************************/
8120 :
8121 : /** Return the argument value as a integer.
8122 : *
8123 : * Must only be called on arguments whose type is GAAT_INTEGER
8124 : *
8125 : * @param hArg Handle to an argument. Must NOT be null.
8126 : * @since 3.11
8127 : */
8128 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8129 : {
8130 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8131 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8132 : {
8133 1 : CPLError(CE_Failure, CPLE_AppDefined,
8134 : "%s must only be called on arguments of type GAAT_INTEGER",
8135 : __func__);
8136 1 : return 0;
8137 : }
8138 25 : return hArg->ptr->Get<int>();
8139 : }
8140 :
8141 : /************************************************************************/
8142 : /* GDALAlgorithmArgGetAsDouble() */
8143 : /************************************************************************/
8144 :
8145 : /** Return the argument value as a double.
8146 : *
8147 : * Must only be called on arguments whose type is GAAT_REAL
8148 : *
8149 : * @param hArg Handle to an argument. Must NOT be null.
8150 : * @since 3.11
8151 : */
8152 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8153 : {
8154 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8155 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8156 : {
8157 1 : CPLError(CE_Failure, CPLE_AppDefined,
8158 : "%s must only be called on arguments of type GAAT_REAL",
8159 : __func__);
8160 1 : return 0;
8161 : }
8162 7 : return hArg->ptr->Get<double>();
8163 : }
8164 :
8165 : /************************************************************************/
8166 : /* GDALAlgorithmArgGetAsStringList() */
8167 : /************************************************************************/
8168 :
8169 : /** Return the argument value as a string list.
8170 : *
8171 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8172 : *
8173 : * @param hArg Handle to an argument. Must NOT be null.
8174 : * @return a NULL terminated list of names, which must be destroyed with
8175 : * CSLDestroy()
8176 :
8177 : * @since 3.11
8178 : */
8179 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8180 : {
8181 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8182 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8183 : {
8184 1 : CPLError(CE_Failure, CPLE_AppDefined,
8185 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8186 : __func__);
8187 1 : return nullptr;
8188 : }
8189 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8190 3 : .StealList();
8191 : }
8192 :
8193 : /************************************************************************/
8194 : /* GDALAlgorithmArgGetAsIntegerList() */
8195 : /************************************************************************/
8196 :
8197 : /** Return the argument value as a integer list.
8198 : *
8199 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8200 : *
8201 : * @param hArg Handle to an argument. Must NOT be null.
8202 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8203 : * @since 3.11
8204 : */
8205 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8206 : size_t *pnCount)
8207 : {
8208 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8209 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8210 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8211 : {
8212 1 : CPLError(
8213 : CE_Failure, CPLE_AppDefined,
8214 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8215 : __func__);
8216 1 : *pnCount = 0;
8217 1 : return nullptr;
8218 : }
8219 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8220 7 : *pnCount = val.size();
8221 7 : return val.data();
8222 : }
8223 :
8224 : /************************************************************************/
8225 : /* GDALAlgorithmArgGetAsDoubleList() */
8226 : /************************************************************************/
8227 :
8228 : /** Return the argument value as a real list.
8229 : *
8230 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8231 : *
8232 : * @param hArg Handle to an argument. Must NOT be null.
8233 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8234 : * @since 3.11
8235 : */
8236 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8237 : size_t *pnCount)
8238 : {
8239 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8240 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8241 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8242 : {
8243 1 : CPLError(CE_Failure, CPLE_AppDefined,
8244 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8245 : __func__);
8246 1 : *pnCount = 0;
8247 1 : return nullptr;
8248 : }
8249 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8250 7 : *pnCount = val.size();
8251 7 : return val.data();
8252 : }
8253 :
8254 : /************************************************************************/
8255 : /* GDALAlgorithmArgSetAsBoolean() */
8256 : /************************************************************************/
8257 :
8258 : /** Set the value for a GAAT_BOOLEAN argument.
8259 : *
8260 : * It cannot be called several times for a given argument.
8261 : * Validation checks and other actions are run.
8262 : *
8263 : * @param hArg Handle to an argument. Must NOT be null.
8264 : * @param value value.
8265 : * @return true if success.
8266 : * @since 3.11
8267 : */
8268 :
8269 646 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8270 : {
8271 646 : VALIDATE_POINTER1(hArg, __func__, false);
8272 646 : return hArg->ptr->Set(value);
8273 : }
8274 :
8275 : /************************************************************************/
8276 : /* GDALAlgorithmArgSetAsString() */
8277 : /************************************************************************/
8278 :
8279 : /** Set the value for a GAAT_STRING argument.
8280 : *
8281 : * It cannot be called several times for a given argument.
8282 : * Validation checks and other actions are run.
8283 : *
8284 : * @param hArg Handle to an argument. Must NOT be null.
8285 : * @param value value (may be null)
8286 : * @return true if success.
8287 : * @since 3.11
8288 : */
8289 :
8290 2848 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
8291 : {
8292 2848 : VALIDATE_POINTER1(hArg, __func__, false);
8293 2848 : return hArg->ptr->Set(value ? value : "");
8294 : }
8295 :
8296 : /************************************************************************/
8297 : /* GDALAlgorithmArgSetAsInteger() */
8298 : /************************************************************************/
8299 :
8300 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
8301 : *
8302 : * It cannot be called several times for a given argument.
8303 : * Validation checks and other actions are run.
8304 : *
8305 : * @param hArg Handle to an argument. Must NOT be null.
8306 : * @param value value.
8307 : * @return true if success.
8308 : * @since 3.11
8309 : */
8310 :
8311 471 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8312 : {
8313 471 : VALIDATE_POINTER1(hArg, __func__, false);
8314 471 : return hArg->ptr->Set(value);
8315 : }
8316 :
8317 : /************************************************************************/
8318 : /* GDALAlgorithmArgSetAsDouble() */
8319 : /************************************************************************/
8320 :
8321 : /** Set the value for a GAAT_REAL argument.
8322 : *
8323 : * It cannot be called several times for a given argument.
8324 : * Validation checks and other actions are run.
8325 : *
8326 : * @param hArg Handle to an argument. Must NOT be null.
8327 : * @param value value.
8328 : * @return true if success.
8329 : * @since 3.11
8330 : */
8331 :
8332 233 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
8333 : {
8334 233 : VALIDATE_POINTER1(hArg, __func__, false);
8335 233 : return hArg->ptr->Set(value);
8336 : }
8337 :
8338 : /************************************************************************/
8339 : /* GDALAlgorithmArgSetAsDatasetValue() */
8340 : /************************************************************************/
8341 :
8342 : /** Set the value for a GAAT_DATASET argument.
8343 : *
8344 : * It cannot be called several times for a given argument.
8345 : * Validation checks and other actions are run.
8346 : *
8347 : * @param hArg Handle to an argument. Must NOT be null.
8348 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
8349 : * @return true if success.
8350 : * @since 3.11
8351 : */
8352 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
8353 : GDALArgDatasetValueH value)
8354 : {
8355 2 : VALIDATE_POINTER1(hArg, __func__, false);
8356 2 : VALIDATE_POINTER1(value, __func__, false);
8357 2 : return hArg->ptr->SetFrom(*(value->ptr));
8358 : }
8359 :
8360 : /************************************************************************/
8361 : /* GDALAlgorithmArgSetDataset() */
8362 : /************************************************************************/
8363 :
8364 : /** Set dataset object, increasing its reference counter.
8365 : *
8366 : * @param hArg Handle to an argument. Must NOT be null.
8367 : * @param hDS Dataset object. May be null.
8368 : * @return true if success.
8369 : * @since 3.11
8370 : */
8371 :
8372 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
8373 : {
8374 2 : VALIDATE_POINTER1(hArg, __func__, false);
8375 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
8376 : }
8377 :
8378 : /************************************************************************/
8379 : /* GDALAlgorithmArgSetAsStringList() */
8380 : /************************************************************************/
8381 :
8382 : /** Set the value for a GAAT_STRING_LIST argument.
8383 : *
8384 : * It cannot be called several times for a given argument.
8385 : * Validation checks and other actions are run.
8386 : *
8387 : * @param hArg Handle to an argument. Must NOT be null.
8388 : * @param value value as a NULL terminated list (may be null)
8389 : * @return true if success.
8390 : * @since 3.11
8391 : */
8392 :
8393 637 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
8394 : {
8395 637 : VALIDATE_POINTER1(hArg, __func__, false);
8396 637 : return hArg->ptr->Set(
8397 1274 : static_cast<std::vector<std::string>>(CPLStringList(value)));
8398 : }
8399 :
8400 : /************************************************************************/
8401 : /* GDALAlgorithmArgSetAsIntegerList() */
8402 : /************************************************************************/
8403 :
8404 : /** Set the value for a GAAT_INTEGER_LIST argument.
8405 : *
8406 : * It cannot be called several times for a given argument.
8407 : * Validation checks and other actions are run.
8408 : *
8409 : * @param hArg Handle to an argument. Must NOT be null.
8410 : * @param nCount Number of values in pnValues.
8411 : * @param pnValues Pointer to an array of integer values of size nCount.
8412 : * @return true if success.
8413 : * @since 3.11
8414 : */
8415 100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
8416 : const int *pnValues)
8417 : {
8418 100 : VALIDATE_POINTER1(hArg, __func__, false);
8419 100 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
8420 : }
8421 :
8422 : /************************************************************************/
8423 : /* GDALAlgorithmArgSetAsDoubleList() */
8424 : /************************************************************************/
8425 :
8426 : /** Set the value for a GAAT_REAL_LIST argument.
8427 : *
8428 : * It cannot be called several times for a given argument.
8429 : * Validation checks and other actions are run.
8430 : *
8431 : * @param hArg Handle to an argument. Must NOT be null.
8432 : * @param nCount Number of values in pnValues.
8433 : * @param pnValues Pointer to an array of double values of size nCount.
8434 : * @return true if success.
8435 : * @since 3.11
8436 : */
8437 152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
8438 : const double *pnValues)
8439 : {
8440 152 : VALIDATE_POINTER1(hArg, __func__, false);
8441 152 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
8442 : }
8443 :
8444 : /************************************************************************/
8445 : /* GDALAlgorithmArgSetDatasets() */
8446 : /************************************************************************/
8447 :
8448 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
8449 : *
8450 : * @param hArg Handle to an argument. Must NOT be null.
8451 : * @param nCount Number of values in pnValues.
8452 : * @param pahDS Pointer to an array of dataset of size nCount.
8453 : * @return true if success.
8454 : * @since 3.11
8455 : */
8456 :
8457 1227 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
8458 : GDALDatasetH *pahDS)
8459 : {
8460 1227 : VALIDATE_POINTER1(hArg, __func__, false);
8461 2454 : std::vector<GDALArgDatasetValue> values;
8462 2480 : for (size_t i = 0; i < nCount; ++i)
8463 : {
8464 1253 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
8465 : }
8466 1227 : return hArg->ptr->Set(std::move(values));
8467 : }
8468 :
8469 : /************************************************************************/
8470 : /* GDALAlgorithmArgSetDatasetNames() */
8471 : /************************************************************************/
8472 :
8473 : /** Set dataset names to a GAAT_DATASET_LIST argument.
8474 : *
8475 : * @param hArg Handle to an argument. Must NOT be null.
8476 : * @param names Dataset names as a NULL terminated list (may be null)
8477 : * @return true if success.
8478 : * @since 3.11
8479 : */
8480 :
8481 686 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
8482 : {
8483 686 : VALIDATE_POINTER1(hArg, __func__, false);
8484 1372 : std::vector<GDALArgDatasetValue> values;
8485 1434 : for (size_t i = 0; names[i]; ++i)
8486 : {
8487 748 : values.emplace_back(names[i]);
8488 : }
8489 686 : return hArg->ptr->Set(std::move(values));
8490 : }
8491 :
8492 : /************************************************************************/
8493 : /* GDALArgDatasetValueCreate() */
8494 : /************************************************************************/
8495 :
8496 : /** Instantiate an empty GDALArgDatasetValue
8497 : *
8498 : * @return new handle to free with GDALArgDatasetValueRelease()
8499 : * @since 3.11
8500 : */
8501 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
8502 : {
8503 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* GDALArgDatasetValueRelease() */
8508 : /************************************************************************/
8509 :
8510 : /** Release a handle to a GDALArgDatasetValue
8511 : *
8512 : * @since 3.11
8513 : */
8514 2921 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
8515 : {
8516 2921 : delete hValue;
8517 2921 : }
8518 :
8519 : /************************************************************************/
8520 : /* GDALArgDatasetValueGetName() */
8521 : /************************************************************************/
8522 :
8523 : /** Return the name component of the GDALArgDatasetValue
8524 : *
8525 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8526 : * @return string whose lifetime is bound to hAlg and which must not
8527 : * be freed.
8528 : * @since 3.11
8529 : */
8530 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
8531 : {
8532 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8533 1 : return hValue->ptr->GetName().c_str();
8534 : }
8535 :
8536 : /************************************************************************/
8537 : /* GDALArgDatasetValueGetDatasetRef() */
8538 : /************************************************************************/
8539 :
8540 : /** Return the dataset component of the GDALArgDatasetValue.
8541 : *
8542 : * This does not modify the reference counter, hence the lifetime of the
8543 : * returned object is not guaranteed to exceed the one of hValue.
8544 : *
8545 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8546 : * @since 3.11
8547 : */
8548 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
8549 : {
8550 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8551 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
8552 : }
8553 :
8554 : /************************************************************************/
8555 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
8556 : /************************************************************************/
8557 :
8558 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
8559 : * reference count if not null. Once done with the dataset, the caller should
8560 : * call GDALReleaseDataset().
8561 : *
8562 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8563 : * @since 3.11
8564 : */
8565 : GDALDatasetH
8566 990 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
8567 : {
8568 990 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8569 990 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
8570 : }
8571 :
8572 : /************************************************************************/
8573 : /* GDALArgDatasetValueSetName() */
8574 : /************************************************************************/
8575 :
8576 : /** Set dataset name
8577 : *
8578 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8579 : * @param pszName Dataset name. May be null.
8580 : * @since 3.11
8581 : */
8582 :
8583 1213 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
8584 : const char *pszName)
8585 : {
8586 1213 : VALIDATE_POINTER0(hValue, __func__);
8587 1213 : hValue->ptr->Set(pszName ? pszName : "");
8588 : }
8589 :
8590 : /************************************************************************/
8591 : /* GDALArgDatasetValueSetDataset() */
8592 : /************************************************************************/
8593 :
8594 : /** Set dataset object, increasing its reference counter.
8595 : *
8596 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8597 : * @param hDS Dataset object. May be null.
8598 : * @since 3.11
8599 : */
8600 :
8601 708 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
8602 : GDALDatasetH hDS)
8603 : {
8604 708 : VALIDATE_POINTER0(hValue, __func__);
8605 708 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
8606 : }
|