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