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