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