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