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_enumerate.h"
16 : #include "cpl_error.h"
17 : #include "cpl_error_internal.h"
18 : #include "cpl_json.h"
19 : #include "cpl_levenshtein.h"
20 : #include "cpl_minixml.h"
21 : #include "cpl_multiproc.h"
22 :
23 : #include "gdalalgorithm.h"
24 : #include "gdalalg_abstract_pipeline.h"
25 : #include "gdal_priv.h"
26 : #include "gdal_thread_pool.h"
27 : #include "memdataset.h"
28 : #include "ogrsf_frmts.h"
29 : #include "ogr_p.h"
30 : #include "ogr_spatialref.h"
31 : #include "vrtdataset.h"
32 :
33 : #include <algorithm>
34 : #include <cassert>
35 : #include <cerrno>
36 : #include <cmath>
37 : #include <cstdlib>
38 : #include <limits>
39 : #include <map>
40 : #include <type_traits>
41 : #include <string_view>
42 : #include <regex>
43 :
44 : #ifndef _
45 : #define _(x) (x)
46 : #endif
47 :
48 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
49 :
50 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
51 :
52 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
53 :
54 : //! @cond Doxygen_Suppress
55 : struct GDALAlgorithmArgHS
56 : {
57 : GDALAlgorithmArg *ptr = nullptr;
58 :
59 359343 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
60 : {
61 359343 : }
62 : };
63 :
64 : //! @endcond
65 :
66 : //! @cond Doxygen_Suppress
67 : struct GDALArgDatasetValueHS
68 : {
69 : GDALArgDatasetValue val{};
70 : GDALArgDatasetValue *ptr = nullptr;
71 :
72 1 : GDALArgDatasetValueHS() : ptr(&val)
73 : {
74 1 : }
75 :
76 3322 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
77 : {
78 3322 : }
79 :
80 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
81 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
82 : };
83 :
84 : //! @endcond
85 :
86 : /************************************************************************/
87 : /* GDALAlgorithmArgTypeIsList() */
88 : /************************************************************************/
89 :
90 455063 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
91 : {
92 455063 : switch (type)
93 : {
94 299857 : case GAAT_BOOLEAN:
95 : case GAAT_STRING:
96 : case GAAT_INTEGER:
97 : case GAAT_REAL:
98 : case GAAT_DATASET:
99 299857 : break;
100 :
101 155206 : case GAAT_STRING_LIST:
102 : case GAAT_INTEGER_LIST:
103 : case GAAT_REAL_LIST:
104 : case GAAT_DATASET_LIST:
105 155206 : return true;
106 : }
107 :
108 299857 : return false;
109 : }
110 :
111 : /************************************************************************/
112 : /* GDALAlgorithmArgTypeName() */
113 : /************************************************************************/
114 :
115 5671 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
116 : {
117 5671 : switch (type)
118 : {
119 1389 : case GAAT_BOOLEAN:
120 1389 : break;
121 1544 : case GAAT_STRING:
122 1544 : return "string";
123 392 : case GAAT_INTEGER:
124 392 : return "integer";
125 477 : case GAAT_REAL:
126 477 : return "real";
127 259 : case GAAT_DATASET:
128 259 : return "dataset";
129 1088 : case GAAT_STRING_LIST:
130 1088 : return "string_list";
131 85 : case GAAT_INTEGER_LIST:
132 85 : return "integer_list";
133 221 : case GAAT_REAL_LIST:
134 221 : return "real_list";
135 216 : case GAAT_DATASET_LIST:
136 216 : return "dataset_list";
137 : }
138 :
139 1389 : return "boolean";
140 : }
141 :
142 : /************************************************************************/
143 : /* GDALAlgorithmArgDatasetTypeName() */
144 : /************************************************************************/
145 :
146 28762 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
147 : {
148 28762 : std::string ret;
149 28762 : if ((type & GDAL_OF_RASTER) != 0)
150 17187 : ret = "raster";
151 28762 : if ((type & GDAL_OF_VECTOR) != 0)
152 : {
153 12265 : if (!ret.empty())
154 : {
155 1802 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
156 294 : ret += ", ";
157 : else
158 1508 : ret += " or ";
159 : }
160 12265 : ret += "vector";
161 : }
162 28762 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
163 : {
164 1331 : if (!ret.empty())
165 : {
166 436 : ret += " or ";
167 : }
168 1331 : ret += "multidimensional raster";
169 : }
170 28762 : return ret;
171 : }
172 :
173 : /************************************************************************/
174 : /* GDALAlgorithmArgDecl() */
175 : /************************************************************************/
176 :
177 : // cppcheck-suppress uninitMemberVar
178 366356 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
179 : char chShortName,
180 : const std::string &description,
181 366356 : GDALAlgorithmArgType type)
182 : : m_longName(longName),
183 366356 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
184 : m_description(description), m_type(type),
185 732712 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
186 366356 : .toupper()),
187 1099070 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
188 : {
189 366356 : if (m_type == GAAT_BOOLEAN)
190 : {
191 155577 : m_defaultValue = false;
192 : }
193 366356 : }
194 :
195 : /************************************************************************/
196 : /* GDALAlgorithmArgDecl::SetMinCount() */
197 : /************************************************************************/
198 :
199 20171 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
200 : {
201 20171 : if (!GDALAlgorithmArgTypeIsList(m_type))
202 : {
203 1 : CPLError(CE_Failure, CPLE_NotSupported,
204 : "SetMinCount() illegal on scalar argument '%s'",
205 1 : GetName().c_str());
206 : }
207 : else
208 : {
209 20170 : m_minCount = count;
210 : }
211 20171 : return *this;
212 : }
213 :
214 : /************************************************************************/
215 : /* GDALAlgorithmArgDecl::SetMaxCount() */
216 : /************************************************************************/
217 :
218 19295 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
219 : {
220 19295 : if (!GDALAlgorithmArgTypeIsList(m_type))
221 : {
222 1 : CPLError(CE_Failure, CPLE_NotSupported,
223 : "SetMaxCount() illegal on scalar argument '%s'",
224 1 : GetName().c_str());
225 : }
226 : else
227 : {
228 19294 : m_maxCount = count;
229 : }
230 19295 : return *this;
231 : }
232 :
233 : /************************************************************************/
234 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
235 : /************************************************************************/
236 :
237 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
238 :
239 : /************************************************************************/
240 : /* GDALAlgorithmArg::Set() */
241 : /************************************************************************/
242 :
243 1303 : bool GDALAlgorithmArg::Set(bool value)
244 : {
245 1303 : if (m_decl.GetType() != GAAT_BOOLEAN)
246 : {
247 14 : CPLError(
248 : CE_Failure, CPLE_AppDefined,
249 : "Calling Set(bool) on argument '%s' of type %s is not supported",
250 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
251 7 : return false;
252 : }
253 1296 : return SetInternal(value);
254 : }
255 :
256 4405 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
257 : {
258 4456 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
259 51 : value.front() == '@')
260 : {
261 2 : GByte *pabyData = nullptr;
262 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
263 2 : 10 * 1024 * 1024))
264 : {
265 : // Remove UTF-8 BOM
266 1 : size_t offset = 0;
267 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
268 1 : pabyData[2] == 0xBF)
269 : {
270 1 : offset = 3;
271 : }
272 1 : value = reinterpret_cast<const char *>(pabyData + offset);
273 1 : VSIFree(pabyData);
274 : }
275 : else
276 : {
277 1 : return false;
278 : }
279 : }
280 :
281 4404 : if (m_decl.IsRemoveSQLCommentsEnabled())
282 50 : value = CPLRemoveSQLComments(value);
283 :
284 4404 : return true;
285 : }
286 :
287 4441 : bool GDALAlgorithmArg::Set(const std::string &value)
288 : {
289 4441 : switch (m_decl.GetType())
290 : {
291 9 : case GAAT_BOOLEAN:
292 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
293 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
294 : {
295 4 : return Set(true);
296 : }
297 5 : else if (EQUAL(value.c_str(), "0") ||
298 4 : EQUAL(value.c_str(), "FALSE") ||
299 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
300 : {
301 4 : return Set(false);
302 : }
303 1 : break;
304 :
305 8 : case GAAT_INTEGER:
306 : case GAAT_INTEGER_LIST:
307 : {
308 8 : errno = 0;
309 8 : char *endptr = nullptr;
310 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
311 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
312 5 : endptr == value.c_str() + value.size())
313 : {
314 3 : if (m_decl.GetType() == GAAT_INTEGER)
315 3 : return Set(static_cast<int>(v));
316 : else
317 1 : return Set(std::vector<int>{static_cast<int>(v)});
318 : }
319 5 : break;
320 : }
321 :
322 5 : case GAAT_REAL:
323 : case GAAT_REAL_LIST:
324 : {
325 5 : char *endptr = nullptr;
326 5 : const double v = CPLStrtod(value.c_str(), &endptr);
327 5 : if (endptr == value.c_str() + value.size())
328 : {
329 3 : if (m_decl.GetType() == GAAT_REAL)
330 3 : return Set(v);
331 : else
332 1 : return Set(std::vector<double>{v});
333 : }
334 2 : break;
335 : }
336 :
337 4383 : case GAAT_STRING:
338 4383 : break;
339 :
340 8 : case GAAT_STRING_LIST:
341 16 : return Set(std::vector<std::string>{value});
342 :
343 28 : case GAAT_DATASET:
344 28 : return SetDatasetName(value);
345 :
346 0 : case GAAT_DATASET_LIST:
347 : {
348 0 : std::vector<GDALArgDatasetValue> v;
349 0 : v.resize(1);
350 0 : v[0].Set(value);
351 0 : return Set(std::move(v));
352 : }
353 : }
354 :
355 4391 : if (m_decl.GetType() != GAAT_STRING)
356 : {
357 16 : CPLError(CE_Failure, CPLE_AppDefined,
358 : "Calling Set(std::string) on argument '%s' of type %s is not "
359 : "supported",
360 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
361 8 : return false;
362 : }
363 :
364 4383 : std::string newValue(value);
365 4383 : return ProcessString(newValue) && SetInternal(newValue);
366 : }
367 :
368 892 : bool GDALAlgorithmArg::Set(int value)
369 : {
370 892 : if (m_decl.GetType() == GAAT_BOOLEAN)
371 : {
372 3 : if (value == 1)
373 1 : return Set(true);
374 2 : else if (value == 0)
375 1 : return Set(false);
376 : }
377 889 : else if (m_decl.GetType() == GAAT_REAL)
378 : {
379 3 : return Set(static_cast<double>(value));
380 : }
381 886 : else if (m_decl.GetType() == GAAT_STRING)
382 : {
383 2 : return Set(std::to_string(value));
384 : }
385 884 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
386 : {
387 1 : return Set(std::vector<int>{value});
388 : }
389 883 : else if (m_decl.GetType() == GAAT_REAL_LIST)
390 : {
391 1 : return Set(std::vector<double>{static_cast<double>(value)});
392 : }
393 882 : else if (m_decl.GetType() == GAAT_STRING_LIST)
394 : {
395 2 : return Set(std::vector<std::string>{std::to_string(value)});
396 : }
397 :
398 882 : if (m_decl.GetType() != GAAT_INTEGER)
399 : {
400 2 : CPLError(
401 : CE_Failure, CPLE_AppDefined,
402 : "Calling Set(int) on argument '%s' of type %s is not supported",
403 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
404 1 : return false;
405 : }
406 881 : return SetInternal(value);
407 : }
408 :
409 315 : bool GDALAlgorithmArg::Set(double value)
410 : {
411 318 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
412 318 : value <= INT_MAX && static_cast<int>(value) == value)
413 : {
414 2 : return Set(static_cast<int>(value));
415 : }
416 313 : else if (m_decl.GetType() == GAAT_STRING)
417 : {
418 2 : return Set(std::to_string(value));
419 : }
420 313 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
421 313 : value <= INT_MAX && static_cast<int>(value) == value)
422 : {
423 1 : return Set(std::vector<int>{static_cast<int>(value)});
424 : }
425 310 : else if (m_decl.GetType() == GAAT_REAL_LIST)
426 : {
427 0 : return Set(std::vector<double>{value});
428 : }
429 310 : else if (m_decl.GetType() == GAAT_STRING_LIST)
430 : {
431 2 : return Set(std::vector<std::string>{std::to_string(value)});
432 : }
433 309 : else if (m_decl.GetType() != GAAT_REAL)
434 : {
435 6 : CPLError(
436 : CE_Failure, CPLE_AppDefined,
437 : "Calling Set(double) on argument '%s' of type %s is not supported",
438 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
439 3 : return false;
440 : }
441 306 : return SetInternal(value);
442 : }
443 :
444 5880 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
445 : {
446 5883 : if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
447 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
448 : {
449 3 : CPLError(
450 : CE_Failure, CPLE_AppDefined,
451 : "Dataset object '%s' is created by algorithm and cannot be set "
452 : "as an input.",
453 3 : arg->GetName().c_str());
454 3 : return false;
455 : }
456 5877 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
457 : {
458 8 : CPLError(CE_Failure, CPLE_AppDefined,
459 : "Dataset%s '%s' must be provided by name, not as object.",
460 8 : arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
461 4 : return false;
462 : }
463 :
464 5873 : return true;
465 : }
466 :
467 33 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
468 : {
469 58 : if (m_decl.GetType() != GAAT_DATASET &&
470 25 : m_decl.GetType() != GAAT_DATASET_LIST)
471 : {
472 2 : CPLError(CE_Failure, CPLE_AppDefined,
473 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
474 : "is not supported",
475 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
476 1 : return false;
477 : }
478 32 : if (!CheckCanSetDatasetObject(this))
479 2 : return false;
480 30 : m_explicitlySet = true;
481 30 : if (m_decl.GetType() == GAAT_DATASET)
482 : {
483 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
484 6 : val.Set(ds);
485 : }
486 : else
487 : {
488 24 : CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
489 24 : auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
490 24 : val.resize(1);
491 24 : val[0].Set(ds);
492 : }
493 30 : return RunAllActions();
494 : }
495 :
496 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
497 : {
498 3 : if (m_decl.GetType() != GAAT_DATASET)
499 : {
500 2 : CPLError(CE_Failure, CPLE_AppDefined,
501 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
502 : "is not supported",
503 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
504 1 : return false;
505 : }
506 2 : if (!CheckCanSetDatasetObject(this))
507 1 : return false;
508 1 : m_explicitlySet = true;
509 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
510 1 : val.Set(std::move(ds));
511 1 : return RunAllActions();
512 : }
513 :
514 628 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
515 : {
516 628 : if (m_decl.GetType() != GAAT_DATASET)
517 : {
518 2 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "Calling SetDatasetName() on argument '%s' of type %s is "
520 : "not supported",
521 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
522 1 : return false;
523 : }
524 627 : m_explicitlySet = true;
525 627 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
526 627 : return RunAllActions();
527 : }
528 :
529 1052 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
530 : {
531 1052 : if (m_decl.GetType() != GAAT_DATASET)
532 : {
533 2 : CPLError(CE_Failure, CPLE_AppDefined,
534 : "Calling SetFrom() on argument '%s' of type %s is "
535 : "not supported",
536 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
537 1 : return false;
538 : }
539 1051 : if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
540 1 : return false;
541 1050 : m_explicitlySet = true;
542 1050 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
543 1050 : return RunAllActions();
544 : }
545 :
546 1241 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
547 : {
548 1241 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
549 : {
550 3 : std::vector<int> v_i;
551 4 : for (const std::string &s : value)
552 : {
553 3 : errno = 0;
554 3 : char *endptr = nullptr;
555 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
556 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
557 2 : endptr == s.c_str() + s.size())
558 : {
559 1 : v_i.push_back(static_cast<int>(v));
560 : }
561 : else
562 : {
563 2 : break;
564 : }
565 : }
566 3 : if (v_i.size() == value.size())
567 1 : return Set(v_i);
568 : }
569 1238 : else if (m_decl.GetType() == GAAT_REAL_LIST)
570 : {
571 2 : std::vector<double> v_d;
572 3 : for (const std::string &s : value)
573 : {
574 2 : char *endptr = nullptr;
575 2 : const double v = CPLStrtod(s.c_str(), &endptr);
576 2 : if (endptr == s.c_str() + s.size())
577 : {
578 1 : v_d.push_back(v);
579 : }
580 : else
581 : {
582 1 : break;
583 : }
584 : }
585 2 : if (v_d.size() == value.size())
586 1 : return Set(v_d);
587 : }
588 2470 : else if ((m_decl.GetType() == GAAT_INTEGER ||
589 2467 : m_decl.GetType() == GAAT_REAL ||
590 3705 : m_decl.GetType() == GAAT_STRING) &&
591 5 : value.size() == 1)
592 : {
593 4 : return Set(value[0]);
594 : }
595 1232 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
596 : {
597 30 : std::vector<GDALArgDatasetValue> dsVector;
598 46 : for (const std::string &s : value)
599 31 : dsVector.emplace_back(s);
600 15 : return Set(std::move(dsVector));
601 : }
602 :
603 1220 : if (m_decl.GetType() != GAAT_STRING_LIST)
604 : {
605 10 : CPLError(CE_Failure, CPLE_AppDefined,
606 : "Calling Set(const std::vector<std::string> &) on argument "
607 : "'%s' of type %s is not supported",
608 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
609 5 : return false;
610 : }
611 :
612 2411 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
613 1196 : m_decl.IsRemoveSQLCommentsEnabled())
614 : {
615 38 : std::vector<std::string> newValue(value);
616 41 : for (auto &s : newValue)
617 : {
618 22 : if (!ProcessString(s))
619 0 : return false;
620 : }
621 19 : return SetInternal(newValue);
622 : }
623 : else
624 : {
625 1196 : return SetInternal(value);
626 : }
627 : }
628 :
629 135 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
630 : {
631 135 : if (m_decl.GetType() == GAAT_REAL_LIST)
632 : {
633 2 : std::vector<double> v_d;
634 2 : for (int i : value)
635 1 : v_d.push_back(i);
636 1 : return Set(v_d);
637 : }
638 134 : else if (m_decl.GetType() == GAAT_STRING_LIST)
639 : {
640 2 : std::vector<std::string> v_s;
641 3 : for (int i : value)
642 2 : v_s.push_back(std::to_string(i));
643 1 : return Set(v_s);
644 : }
645 264 : else if ((m_decl.GetType() == GAAT_INTEGER ||
646 260 : m_decl.GetType() == GAAT_REAL ||
647 395 : m_decl.GetType() == GAAT_STRING) &&
648 5 : value.size() == 1)
649 : {
650 3 : return Set(value[0]);
651 : }
652 :
653 130 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
654 : {
655 6 : CPLError(CE_Failure, CPLE_AppDefined,
656 : "Calling Set(const std::vector<int> &) on argument '%s' of "
657 : "type %s is not supported",
658 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
659 3 : return false;
660 : }
661 127 : return SetInternal(value);
662 : }
663 :
664 357 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
665 : {
666 357 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
667 : {
668 2 : std::vector<int> v_i;
669 3 : for (double d : value)
670 : {
671 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
672 : {
673 1 : v_i.push_back(static_cast<int>(d));
674 : }
675 : else
676 : {
677 : break;
678 : }
679 : }
680 2 : if (v_i.size() == value.size())
681 1 : return Set(v_i);
682 : }
683 355 : else if (m_decl.GetType() == GAAT_STRING_LIST)
684 : {
685 2 : std::vector<std::string> v_s;
686 3 : for (double d : value)
687 2 : v_s.push_back(std::to_string(d));
688 1 : return Set(v_s);
689 : }
690 707 : else if ((m_decl.GetType() == GAAT_INTEGER ||
691 705 : m_decl.GetType() == GAAT_REAL ||
692 1060 : m_decl.GetType() == GAAT_STRING) &&
693 3 : value.size() == 1)
694 : {
695 3 : return Set(value[0]);
696 : }
697 :
698 352 : if (m_decl.GetType() != GAAT_REAL_LIST)
699 : {
700 4 : CPLError(CE_Failure, CPLE_AppDefined,
701 : "Calling Set(const std::vector<double> &) on argument '%s' of "
702 : "type %s is not supported",
703 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
704 2 : return false;
705 : }
706 350 : return SetInternal(value);
707 : }
708 :
709 3776 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
710 : {
711 3776 : if (m_decl.GetType() != GAAT_DATASET_LIST)
712 : {
713 2 : CPLError(CE_Failure, CPLE_AppDefined,
714 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
715 : "argument '%s' of type %s is not supported",
716 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
717 1 : return false;
718 : }
719 3775 : m_explicitlySet = true;
720 3775 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
721 3775 : return RunAllActions();
722 : }
723 :
724 : GDALAlgorithmArg &
725 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
726 : {
727 0 : Set(std::move(value));
728 0 : return *this;
729 : }
730 :
731 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
732 : {
733 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
734 1 : return Set(value.exportToWkt(apszOptions));
735 : }
736 :
737 4550 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
738 : {
739 4550 : if (m_decl.GetType() != other.GetType())
740 : {
741 2 : CPLError(CE_Failure, CPLE_AppDefined,
742 : "Calling SetFrom() on argument '%s' of type %s whereas "
743 : "other argument type is %s is not supported",
744 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
745 : GDALAlgorithmArgTypeName(other.GetType()));
746 1 : return false;
747 : }
748 :
749 4549 : switch (m_decl.GetType())
750 : {
751 98 : case GAAT_BOOLEAN:
752 98 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
753 98 : break;
754 844 : case GAAT_STRING:
755 1688 : *std::get<std::string *>(m_value) =
756 844 : *std::get<std::string *>(other.m_value);
757 844 : break;
758 7 : case GAAT_INTEGER:
759 7 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
760 7 : break;
761 1 : case GAAT_REAL:
762 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
763 1 : break;
764 1046 : case GAAT_DATASET:
765 1046 : return SetFrom(other.Get<GDALArgDatasetValue>());
766 60 : case GAAT_STRING_LIST:
767 120 : *std::get<std::vector<std::string> *>(m_value) =
768 60 : *std::get<std::vector<std::string> *>(other.m_value);
769 60 : break;
770 1 : case GAAT_INTEGER_LIST:
771 2 : *std::get<std::vector<int> *>(m_value) =
772 1 : *std::get<std::vector<int> *>(other.m_value);
773 1 : break;
774 1 : case GAAT_REAL_LIST:
775 2 : *std::get<std::vector<double> *>(m_value) =
776 1 : *std::get<std::vector<double> *>(other.m_value);
777 1 : break;
778 2491 : case GAAT_DATASET_LIST:
779 : {
780 2491 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
781 2497 : for (const auto &val :
782 7485 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
783 : {
784 4994 : GDALArgDatasetValue v;
785 2497 : v.SetFrom(val);
786 2497 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
787 2497 : ->push_back(std::move(v));
788 : }
789 2491 : break;
790 : }
791 : }
792 3503 : m_explicitlySet = true;
793 3503 : return RunAllActions();
794 : }
795 :
796 : /************************************************************************/
797 : /* GDALAlgorithmArg::RunAllActions() */
798 : /************************************************************************/
799 :
800 17543 : bool GDALAlgorithmArg::RunAllActions()
801 : {
802 17543 : if (!RunValidationActions())
803 149 : return false;
804 17394 : RunActions();
805 17394 : return true;
806 : }
807 :
808 : /************************************************************************/
809 : /* GDALAlgorithmArg::RunActions() */
810 : /************************************************************************/
811 :
812 17395 : void GDALAlgorithmArg::RunActions()
813 : {
814 17711 : for (const auto &f : m_actions)
815 316 : f();
816 17395 : }
817 :
818 : /************************************************************************/
819 : /* GDALAlgorithmArg::ValidateChoice() */
820 : /************************************************************************/
821 :
822 : // Returns the canonical value if matching a valid choice, or empty string
823 : // otherwise.
824 2752 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
825 : {
826 15724 : for (const std::string &choice : GetChoices())
827 : {
828 15606 : if (EQUAL(value.c_str(), choice.c_str()))
829 : {
830 2634 : return choice;
831 : }
832 : }
833 :
834 190 : for (const std::string &choice : GetHiddenChoices())
835 : {
836 172 : if (EQUAL(value.c_str(), choice.c_str()))
837 : {
838 100 : return choice;
839 : }
840 : }
841 :
842 36 : std::string expected;
843 222 : for (const auto &choice : GetChoices())
844 : {
845 204 : if (!expected.empty())
846 186 : expected += ", ";
847 204 : expected += '\'';
848 204 : expected += choice;
849 204 : expected += '\'';
850 : }
851 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
852 : {
853 6 : return "?";
854 : }
855 24 : CPLError(CE_Failure, CPLE_IllegalArg,
856 : "Invalid value '%s' for string argument '%s'. Should be "
857 : "one among %s.",
858 12 : value.c_str(), GetName().c_str(), expected.c_str());
859 12 : return std::string();
860 : }
861 :
862 : /************************************************************************/
863 : /* GDALAlgorithmArg::ValidateIntRange() */
864 : /************************************************************************/
865 :
866 2621 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
867 : {
868 2621 : bool ret = true;
869 :
870 2621 : const auto [minVal, minValIsIncluded] = GetMinValue();
871 2621 : if (!std::isnan(minVal))
872 : {
873 1941 : if (minValIsIncluded && val < minVal)
874 : {
875 3 : CPLError(CE_Failure, CPLE_IllegalArg,
876 : "Value of argument '%s' is %d, but should be >= %d",
877 3 : GetName().c_str(), val, static_cast<int>(minVal));
878 3 : ret = false;
879 : }
880 1938 : else if (!minValIsIncluded && val <= minVal)
881 : {
882 1 : CPLError(CE_Failure, CPLE_IllegalArg,
883 : "Value of argument '%s' is %d, but should be > %d",
884 1 : GetName().c_str(), val, static_cast<int>(minVal));
885 1 : ret = false;
886 : }
887 : }
888 :
889 2621 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
890 2621 : if (!std::isnan(maxVal))
891 : {
892 :
893 430 : if (maxValIsIncluded && val > maxVal)
894 : {
895 1 : CPLError(CE_Failure, CPLE_IllegalArg,
896 : "Value of argument '%s' is %d, but should be <= %d",
897 1 : GetName().c_str(), val, static_cast<int>(maxVal));
898 1 : ret = false;
899 : }
900 429 : else if (!maxValIsIncluded && val >= maxVal)
901 : {
902 1 : CPLError(CE_Failure, CPLE_IllegalArg,
903 : "Value of argument '%s' is %d, but should be < %d",
904 1 : GetName().c_str(), val, static_cast<int>(maxVal));
905 1 : ret = false;
906 : }
907 : }
908 :
909 2621 : return ret;
910 : }
911 :
912 : /************************************************************************/
913 : /* GDALAlgorithmArg::ValidateRealRange() */
914 : /************************************************************************/
915 :
916 2657 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
917 : {
918 2657 : bool ret = true;
919 :
920 2657 : const auto [minVal, minValIsIncluded] = GetMinValue();
921 2657 : if (!std::isnan(minVal))
922 : {
923 216 : if (minValIsIncluded && !(val >= minVal))
924 : {
925 11 : CPLError(CE_Failure, CPLE_IllegalArg,
926 : "Value of argument '%s' is %g, but should be >= %g",
927 11 : GetName().c_str(), val, minVal);
928 11 : ret = false;
929 : }
930 205 : else if (!minValIsIncluded && !(val > minVal))
931 : {
932 4 : CPLError(CE_Failure, CPLE_IllegalArg,
933 : "Value of argument '%s' is %g, but should be > %g",
934 4 : GetName().c_str(), val, minVal);
935 4 : ret = false;
936 : }
937 : }
938 :
939 2657 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
940 2657 : if (!std::isnan(maxVal))
941 : {
942 :
943 58 : if (maxValIsIncluded && !(val <= maxVal))
944 : {
945 2 : CPLError(CE_Failure, CPLE_IllegalArg,
946 : "Value of argument '%s' is %g, but should be <= %g",
947 2 : GetName().c_str(), val, maxVal);
948 2 : ret = false;
949 : }
950 56 : else if (!maxValIsIncluded && !(val < maxVal))
951 : {
952 1 : CPLError(CE_Failure, CPLE_IllegalArg,
953 : "Value of argument '%s' is %g, but should be < %g",
954 1 : GetName().c_str(), val, maxVal);
955 1 : ret = false;
956 : }
957 : }
958 :
959 2657 : return ret;
960 : }
961 :
962 : /************************************************************************/
963 : /* CheckDuplicateValues() */
964 : /************************************************************************/
965 :
966 : template <class T>
967 95 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
968 : const std::vector<T> &values)
969 : {
970 190 : auto tmpValues = values;
971 95 : bool bHasDupValues = false;
972 : if constexpr (std::is_floating_point_v<T>)
973 : {
974 : // Avoid undefined behavior with NaN values
975 4 : std::sort(tmpValues.begin(), tmpValues.end(),
976 21 : [](T a, T b)
977 : {
978 21 : if (std::isnan(a) && !std::isnan(b))
979 3 : return true;
980 18 : if (std::isnan(b))
981 10 : return false;
982 8 : return a < b;
983 : });
984 :
985 : bHasDupValues =
986 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
987 6 : [](T a, T b)
988 : {
989 6 : if (std::isnan(a) && std::isnan(b))
990 1 : return true;
991 5 : return a == b;
992 8 : }) != tmpValues.end();
993 : }
994 : else
995 : {
996 91 : std::sort(tmpValues.begin(), tmpValues.end());
997 91 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
998 182 : tmpValues.end()) != tmpValues.end();
999 : }
1000 95 : if (bHasDupValues)
1001 : {
1002 10 : CPLError(CE_Failure, CPLE_AppDefined,
1003 : "'%s' must be a list of unique values.",
1004 10 : arg->GetName().c_str());
1005 10 : return false;
1006 : }
1007 85 : return true;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* GDALAlgorithmArg::RunValidationActions() */
1012 : /************************************************************************/
1013 :
1014 38902 : bool GDALAlgorithmArg::RunValidationActions()
1015 : {
1016 38902 : bool ret = true;
1017 :
1018 38902 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1019 : {
1020 1815 : auto &val = Get<std::string>();
1021 3630 : std::string validVal = ValidateChoice(val);
1022 1815 : if (validVal.empty())
1023 7 : ret = false;
1024 : else
1025 1808 : val = std::move(validVal);
1026 : }
1027 37087 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1028 : {
1029 677 : auto &values = Get<std::vector<std::string>>();
1030 1614 : for (std::string &val : values)
1031 : {
1032 1874 : std::string validVal = ValidateChoice(val);
1033 937 : if (validVal.empty())
1034 5 : ret = false;
1035 : else
1036 932 : val = std::move(validVal);
1037 : }
1038 : }
1039 :
1040 : const auto CheckMinCharCount =
1041 1143 : [this, &ret](const std::string &val, int nMinCharCount)
1042 : {
1043 1131 : if (val.size() < static_cast<size_t>(nMinCharCount))
1044 : {
1045 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1046 : "Value of argument '%s' is '%s', but should have at least "
1047 : "%d character%s",
1048 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1049 : nMinCharCount > 1 ? "s" : "");
1050 6 : ret = false;
1051 : }
1052 40033 : };
1053 :
1054 : const auto CheckMaxCharCount =
1055 14240 : [this, &ret](const std::string &val, int nMaxCharCount)
1056 : {
1057 14238 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1058 : {
1059 2 : CPLError(
1060 : CE_Failure, CPLE_IllegalArg,
1061 : "Value of argument '%s' is '%s', but should have no more than "
1062 : "%d character%s",
1063 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1064 : nMaxCharCount > 1 ? "s" : "");
1065 1 : ret = false;
1066 : }
1067 53140 : };
1068 :
1069 38902 : switch (GetType())
1070 : {
1071 2923 : case GAAT_BOOLEAN:
1072 2923 : break;
1073 :
1074 10885 : case GAAT_STRING:
1075 : {
1076 10885 : const auto &val = Get<std::string>();
1077 10885 : const int nMinCharCount = GetMinCharCount();
1078 10885 : if (nMinCharCount > 0)
1079 : {
1080 1049 : CheckMinCharCount(val, nMinCharCount);
1081 : }
1082 :
1083 10885 : const int nMaxCharCount = GetMaxCharCount();
1084 10885 : CheckMaxCharCount(val, nMaxCharCount);
1085 10885 : break;
1086 : }
1087 :
1088 2662 : case GAAT_STRING_LIST:
1089 : {
1090 2662 : const int nMinCharCount = GetMinCharCount();
1091 2662 : const int nMaxCharCount = GetMaxCharCount();
1092 2662 : const auto &values = Get<std::vector<std::string>>();
1093 6015 : for (const auto &val : values)
1094 : {
1095 3353 : if (nMinCharCount > 0)
1096 82 : CheckMinCharCount(val, nMinCharCount);
1097 3353 : CheckMaxCharCount(val, nMaxCharCount);
1098 : }
1099 :
1100 2739 : if (!GetDuplicateValuesAllowed() &&
1101 77 : !CheckDuplicateValues(this, values))
1102 2 : ret = false;
1103 2662 : break;
1104 : }
1105 :
1106 2073 : case GAAT_INTEGER:
1107 : {
1108 2073 : ret = ValidateIntRange(Get<int>()) && ret;
1109 2073 : break;
1110 : }
1111 :
1112 270 : case GAAT_INTEGER_LIST:
1113 : {
1114 270 : const auto &values = Get<std::vector<int>>();
1115 818 : for (int v : values)
1116 548 : ret = ValidateIntRange(v) && ret;
1117 :
1118 273 : if (!GetDuplicateValuesAllowed() &&
1119 3 : !CheckDuplicateValues(this, values))
1120 1 : ret = false;
1121 270 : break;
1122 : }
1123 :
1124 601 : case GAAT_REAL:
1125 : {
1126 601 : ret = ValidateRealRange(Get<double>()) && ret;
1127 601 : break;
1128 : }
1129 :
1130 757 : case GAAT_REAL_LIST:
1131 : {
1132 757 : const auto &values = Get<std::vector<double>>();
1133 2813 : for (double v : values)
1134 2056 : ret = ValidateRealRange(v) && ret;
1135 :
1136 761 : if (!GetDuplicateValuesAllowed() &&
1137 4 : !CheckDuplicateValues(this, values))
1138 2 : ret = false;
1139 757 : break;
1140 : }
1141 :
1142 6221 : case GAAT_DATASET:
1143 6221 : break;
1144 :
1145 12510 : case GAAT_DATASET_LIST:
1146 : {
1147 12510 : if (!GetDuplicateValuesAllowed())
1148 : {
1149 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1150 22 : std::vector<std::string> aosValues;
1151 34 : for (const auto &v : values)
1152 : {
1153 23 : const GDALDataset *poDS = v.GetDatasetRef();
1154 23 : if (poDS)
1155 : {
1156 16 : auto poDriver = poDS->GetDriver();
1157 : // The dataset name for a MEM driver is not relevant,
1158 : // so use the pointer address
1159 32 : if ((poDriver &&
1160 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1161 8 : poDS->GetDescription()[0] == 0)
1162 : {
1163 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1164 : }
1165 : else
1166 : {
1167 8 : aosValues.push_back(poDS->GetDescription());
1168 : }
1169 : }
1170 : else
1171 : {
1172 7 : aosValues.push_back(v.GetName());
1173 : }
1174 : }
1175 11 : if (!CheckDuplicateValues(this, aosValues))
1176 5 : ret = false;
1177 : }
1178 12510 : break;
1179 : }
1180 : }
1181 :
1182 38902 : if (GDALAlgorithmArgTypeIsList(GetType()))
1183 : {
1184 16199 : int valueCount = 0;
1185 16199 : if (GetType() == GAAT_STRING_LIST)
1186 : {
1187 2662 : valueCount =
1188 2662 : static_cast<int>(Get<std::vector<std::string>>().size());
1189 : }
1190 13537 : else if (GetType() == GAAT_INTEGER_LIST)
1191 : {
1192 270 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1193 : }
1194 13267 : else if (GetType() == GAAT_REAL_LIST)
1195 : {
1196 757 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1197 : }
1198 12510 : else if (GetType() == GAAT_DATASET_LIST)
1199 : {
1200 12510 : valueCount = static_cast<int>(
1201 12510 : Get<std::vector<GDALArgDatasetValue>>().size());
1202 : }
1203 :
1204 16199 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1205 : {
1206 14 : ReportError(CE_Failure, CPLE_AppDefined,
1207 : "%d value%s been specified for argument '%s', "
1208 : "whereas exactly %d %s expected.",
1209 : valueCount, valueCount > 1 ? "s have" : " has",
1210 7 : GetName().c_str(), GetMinCount(),
1211 7 : GetMinCount() > 1 ? "were" : "was");
1212 7 : ret = false;
1213 : }
1214 16192 : else if (valueCount < GetMinCount())
1215 : {
1216 6 : ReportError(CE_Failure, CPLE_AppDefined,
1217 : "Only %d value%s been specified for argument '%s', "
1218 : "whereas at least %d %s expected.",
1219 : valueCount, valueCount > 1 ? "s have" : " has",
1220 3 : GetName().c_str(), GetMinCount(),
1221 3 : GetMinCount() > 1 ? "were" : "was");
1222 3 : ret = false;
1223 : }
1224 16189 : else if (valueCount > GetMaxCount())
1225 : {
1226 2 : ReportError(CE_Failure, CPLE_AppDefined,
1227 : "%d value%s been specified for argument '%s', "
1228 : "whereas at most %d %s expected.",
1229 : valueCount, valueCount > 1 ? "s have" : " has",
1230 1 : GetName().c_str(), GetMaxCount(),
1231 1 : GetMaxCount() > 1 ? "were" : "was");
1232 1 : ret = false;
1233 : }
1234 : }
1235 :
1236 38902 : if (ret)
1237 : {
1238 46468 : for (const auto &f : m_validationActions)
1239 : {
1240 7630 : if (!f())
1241 94 : ret = false;
1242 : }
1243 : }
1244 :
1245 38902 : return ret;
1246 : }
1247 :
1248 : /************************************************************************/
1249 : /* GDALAlgorithmArg::ReportError() */
1250 : /************************************************************************/
1251 :
1252 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1253 : const char *fmt, ...) const
1254 : {
1255 : va_list args;
1256 11 : va_start(args, fmt);
1257 11 : if (m_owner)
1258 : {
1259 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1260 22 : CPLString().vPrintf(fmt, args).c_str());
1261 : }
1262 : else
1263 : {
1264 0 : CPLError(eErrClass, err_no, "%s",
1265 0 : CPLString().vPrintf(fmt, args).c_str());
1266 : }
1267 11 : va_end(args);
1268 11 : }
1269 :
1270 : /************************************************************************/
1271 : /* GDALAlgorithmArg::GetEscapedString() */
1272 : /************************************************************************/
1273 :
1274 : /* static */
1275 197 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1276 : {
1277 215 : if (s.find_first_of("\" \\,") != std::string::npos &&
1278 9 : !(s.size() > 4 &&
1279 9 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1280 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1281 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1282 : {
1283 14 : return std::string("\"")
1284 : .append(
1285 14 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1286 7 : .append("\"");
1287 : }
1288 : else
1289 : {
1290 190 : return s;
1291 : }
1292 : }
1293 :
1294 : /************************************************************************/
1295 : /* GDALAlgorithmArg::Serialize() */
1296 : /************************************************************************/
1297 :
1298 43 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1299 : bool absolutePath) const
1300 : {
1301 43 : serializedArg.clear();
1302 :
1303 43 : if (!IsExplicitlySet())
1304 : {
1305 0 : return false;
1306 : }
1307 :
1308 86 : std::string ret = "--";
1309 43 : ret += GetName();
1310 43 : if (GetType() == GAAT_BOOLEAN)
1311 : {
1312 0 : serializedArg = std::move(ret);
1313 0 : return true;
1314 : }
1315 :
1316 7 : const auto AddListValueSeparator = [this, &ret]()
1317 : {
1318 2 : if (GetPackedValuesAllowed())
1319 : {
1320 1 : ret += ',';
1321 : }
1322 : else
1323 : {
1324 1 : ret += " --";
1325 1 : ret += GetName();
1326 1 : ret += ' ';
1327 : }
1328 45 : };
1329 :
1330 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1331 : {
1332 : VSIStatBufL sStat;
1333 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1334 0 : !CPLIsFilenameRelative(filename.c_str()))
1335 0 : return filename;
1336 0 : char *pszCWD = CPLGetCurrentDir();
1337 0 : if (!pszCWD)
1338 0 : return filename;
1339 : const auto absPath =
1340 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1341 0 : CPLFree(pszCWD);
1342 0 : return absPath;
1343 : };
1344 :
1345 43 : ret += ' ';
1346 43 : switch (GetType())
1347 : {
1348 0 : case GAAT_BOOLEAN:
1349 0 : break;
1350 8 : case GAAT_STRING:
1351 : {
1352 8 : const auto &val = Get<std::string>();
1353 8 : ret += GetEscapedString(val);
1354 8 : break;
1355 : }
1356 1 : case GAAT_INTEGER:
1357 : {
1358 1 : ret += CPLSPrintf("%d", Get<int>());
1359 1 : break;
1360 : }
1361 0 : case GAAT_REAL:
1362 : {
1363 0 : ret += CPLSPrintf("%.17g", Get<double>());
1364 0 : break;
1365 : }
1366 2 : case GAAT_DATASET:
1367 : {
1368 2 : const auto &val = Get<GDALArgDatasetValue>();
1369 2 : const auto &str = val.GetName();
1370 2 : if (str.empty())
1371 : {
1372 0 : return false;
1373 : }
1374 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1375 2 : break;
1376 : }
1377 5 : case GAAT_STRING_LIST:
1378 : {
1379 5 : const auto &vals = Get<std::vector<std::string>>();
1380 11 : for (size_t i = 0; i < vals.size(); ++i)
1381 : {
1382 6 : if (i > 0)
1383 2 : AddListValueSeparator();
1384 6 : ret += GetEscapedString(vals[i]);
1385 : }
1386 5 : break;
1387 : }
1388 0 : case GAAT_INTEGER_LIST:
1389 : {
1390 0 : const auto &vals = Get<std::vector<int>>();
1391 0 : for (size_t i = 0; i < vals.size(); ++i)
1392 : {
1393 0 : if (i > 0)
1394 0 : AddListValueSeparator();
1395 0 : ret += CPLSPrintf("%d", vals[i]);
1396 : }
1397 0 : break;
1398 : }
1399 0 : case GAAT_REAL_LIST:
1400 : {
1401 0 : const auto &vals = Get<std::vector<double>>();
1402 0 : for (size_t i = 0; i < vals.size(); ++i)
1403 : {
1404 0 : if (i > 0)
1405 0 : AddListValueSeparator();
1406 0 : ret += CPLSPrintf("%.17g", vals[i]);
1407 : }
1408 0 : break;
1409 : }
1410 27 : case GAAT_DATASET_LIST:
1411 : {
1412 27 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1413 53 : for (size_t i = 0; i < vals.size(); ++i)
1414 : {
1415 27 : if (i > 0)
1416 0 : AddListValueSeparator();
1417 27 : const auto &val = vals[i];
1418 27 : const auto &str = val.GetName();
1419 27 : if (str.empty())
1420 : {
1421 1 : return false;
1422 : }
1423 52 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1424 26 : : str);
1425 : }
1426 26 : break;
1427 : }
1428 : }
1429 :
1430 42 : serializedArg = std::move(ret);
1431 42 : return true;
1432 : }
1433 :
1434 : /************************************************************************/
1435 : /* ~GDALInConstructionAlgorithmArg() */
1436 : /************************************************************************/
1437 :
1438 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1439 :
1440 : /************************************************************************/
1441 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1442 : /************************************************************************/
1443 :
1444 : GDALInConstructionAlgorithmArg &
1445 67933 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1446 : {
1447 67933 : m_decl.AddAlias(alias);
1448 67933 : if (m_owner)
1449 67933 : m_owner->AddAliasFor(this, alias);
1450 67933 : return *this;
1451 : }
1452 :
1453 : /************************************************************************/
1454 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1455 : /************************************************************************/
1456 :
1457 : GDALInConstructionAlgorithmArg &
1458 18186 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1459 : {
1460 18186 : m_decl.AddHiddenAlias(alias);
1461 18186 : if (m_owner)
1462 18186 : m_owner->AddAliasFor(this, alias);
1463 18186 : return *this;
1464 : }
1465 :
1466 : /************************************************************************/
1467 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1468 : /************************************************************************/
1469 :
1470 : GDALInConstructionAlgorithmArg &
1471 50 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1472 : {
1473 50 : m_decl.AddShortNameAlias(shortNameAlias);
1474 50 : if (m_owner)
1475 50 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1476 50 : return *this;
1477 : }
1478 :
1479 : /************************************************************************/
1480 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1481 : /************************************************************************/
1482 :
1483 23565 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1484 : {
1485 23565 : m_decl.SetPositional();
1486 23565 : if (m_owner)
1487 23565 : m_owner->SetPositional(this);
1488 23565 : return *this;
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1493 : /************************************************************************/
1494 :
1495 1399 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1496 2798 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1497 1399 : m_nameSet(true)
1498 : {
1499 1399 : if (m_poDS)
1500 1399 : m_poDS->Reference();
1501 1399 : }
1502 :
1503 : /************************************************************************/
1504 : /* GDALArgDatasetValue::Set() */
1505 : /************************************************************************/
1506 :
1507 2432 : void GDALArgDatasetValue::Set(const std::string &name)
1508 : {
1509 2432 : Close();
1510 2432 : m_name = name;
1511 2432 : m_nameSet = true;
1512 2432 : if (m_ownerArg)
1513 2426 : m_ownerArg->NotifyValueSet();
1514 2432 : }
1515 :
1516 : /************************************************************************/
1517 : /* GDALArgDatasetValue::Set() */
1518 : /************************************************************************/
1519 :
1520 2236 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1521 : {
1522 2236 : Close();
1523 2236 : m_poDS = poDS.release();
1524 2236 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1525 2236 : m_nameSet = true;
1526 2236 : if (m_ownerArg)
1527 2034 : m_ownerArg->NotifyValueSet();
1528 2236 : }
1529 :
1530 : /************************************************************************/
1531 : /* GDALArgDatasetValue::Set() */
1532 : /************************************************************************/
1533 :
1534 8755 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1535 : {
1536 8755 : Close();
1537 8755 : m_poDS = poDS;
1538 8755 : if (m_poDS)
1539 7832 : m_poDS->Reference();
1540 8755 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1541 8755 : m_nameSet = true;
1542 8755 : if (m_ownerArg)
1543 3510 : m_ownerArg->NotifyValueSet();
1544 8755 : }
1545 :
1546 : /************************************************************************/
1547 : /* GDALArgDatasetValue::SetFrom() */
1548 : /************************************************************************/
1549 :
1550 3547 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1551 : {
1552 3547 : Close();
1553 3547 : m_name = other.m_name;
1554 3547 : m_nameSet = other.m_nameSet;
1555 3547 : m_poDS = other.m_poDS;
1556 3547 : if (m_poDS)
1557 2477 : m_poDS->Reference();
1558 3547 : }
1559 :
1560 : /************************************************************************/
1561 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1562 : /************************************************************************/
1563 :
1564 34213 : GDALArgDatasetValue::~GDALArgDatasetValue()
1565 : {
1566 34213 : Close();
1567 34213 : }
1568 :
1569 : /************************************************************************/
1570 : /* GDALArgDatasetValue::Close() */
1571 : /************************************************************************/
1572 :
1573 57173 : bool GDALArgDatasetValue::Close()
1574 : {
1575 57173 : bool ret = true;
1576 57173 : if (m_poDS && m_poDS->Dereference() == 0)
1577 : {
1578 3665 : ret = m_poDS->Close() == CE_None;
1579 3665 : delete m_poDS;
1580 : }
1581 57173 : m_poDS = nullptr;
1582 57173 : return ret;
1583 : }
1584 :
1585 : /************************************************************************/
1586 : /* GDALArgDatasetValue::operator=() */
1587 : /************************************************************************/
1588 :
1589 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1590 : {
1591 2 : Close();
1592 2 : m_poDS = other.m_poDS;
1593 2 : m_name = other.m_name;
1594 2 : m_nameSet = other.m_nameSet;
1595 2 : other.m_poDS = nullptr;
1596 2 : other.m_name.clear();
1597 2 : other.m_nameSet = false;
1598 2 : return *this;
1599 : }
1600 :
1601 : /************************************************************************/
1602 : /* GDALArgDatasetValue::GetDataset() */
1603 : /************************************************************************/
1604 :
1605 1127 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1606 : {
1607 1127 : if (m_poDS)
1608 1126 : m_poDS->Reference();
1609 1127 : return m_poDS;
1610 : }
1611 :
1612 : /************************************************************************/
1613 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1614 : /************************************************************************/
1615 :
1616 3423 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1617 3423 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1618 : {
1619 3423 : other.m_poDS = nullptr;
1620 3423 : other.m_name.clear();
1621 3423 : }
1622 :
1623 : /************************************************************************/
1624 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1625 : /************************************************************************/
1626 :
1627 3540 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1628 : bool noneAllowed, const std::vector<std::string> &specialValues)
1629 : {
1630 3540 : if (GetType() != GAAT_STRING)
1631 : {
1632 1 : CPLError(CE_Failure, CPLE_AppDefined,
1633 : "SetIsCRSArg() can only be called on a String argument");
1634 1 : return *this;
1635 : }
1636 : AddValidationAction(
1637 895 : [this, noneAllowed, specialValues]()
1638 : {
1639 : const std::string &osVal =
1640 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1641 444 : ->Get<std::string>();
1642 444 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1643 0 : return true;
1644 :
1645 875 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1646 431 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1647 875 : specialValues.end())
1648 : {
1649 423 : OGRSpatialReference oSRS;
1650 423 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1651 : {
1652 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1653 : "Invalid value for '%s' argument",
1654 7 : GetName().c_str());
1655 7 : return false;
1656 : }
1657 : }
1658 437 : return true;
1659 3539 : });
1660 :
1661 : SetAutoCompleteFunction(
1662 44 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1663 : {
1664 11 : bool bIsRaster = false;
1665 11 : OGREnvelope sDatasetLongLatEnv;
1666 22 : std::string osCelestialBodyName;
1667 11 : if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
1668 : {
1669 11 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1670 11 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1671 : {
1672 : auto &val =
1673 11 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1674 11 : if (val.size() == 1)
1675 : {
1676 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1677 : auto poDS = std::unique_ptr<GDALDataset>(
1678 4 : GDALDataset::Open(val[0].GetName().c_str()));
1679 2 : if (poDS)
1680 : {
1681 2 : bIsRaster = poDS->GetRasterCount() != 0;
1682 2 : if (auto poCRS = poDS->GetSpatialRef())
1683 : {
1684 : const char *pszCelestialBodyName =
1685 2 : poCRS->GetCelestialBodyName();
1686 2 : if (pszCelestialBodyName)
1687 2 : osCelestialBodyName = pszCelestialBodyName;
1688 :
1689 2 : if (!pszCelestialBodyName ||
1690 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1691 : {
1692 0 : OGRSpatialReference oLongLat;
1693 0 : oLongLat.CopyGeogCSFrom(poCRS);
1694 0 : oLongLat.SetAxisMappingStrategy(
1695 : OAMS_TRADITIONAL_GIS_ORDER);
1696 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1697 0 : &oLongLat);
1698 : }
1699 : else
1700 : {
1701 2 : poDS->GetExtentWGS84LongLat(
1702 2 : &sDatasetLongLatEnv);
1703 : }
1704 : }
1705 : }
1706 : }
1707 : }
1708 : }
1709 :
1710 : const auto IsCRSCompatible =
1711 42959 : [bIsRaster, &sDatasetLongLatEnv,
1712 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1713 : {
1714 42959 : if (!sDatasetLongLatEnv.IsInit())
1715 30685 : return true;
1716 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1717 11834 : !(bIsRaster &&
1718 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1719 11652 : crsInfo->dfWestLongitudeDeg <
1720 11652 : crsInfo->dfEastLongitudeDeg &&
1721 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1722 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1723 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1724 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1725 329 : ((!osCelestialBodyName.empty() &&
1726 658 : crsInfo->pszCelestialBodyName &&
1727 329 : osCelestialBodyName ==
1728 329 : crsInfo->pszCelestialBodyName) ||
1729 0 : (osCelestialBodyName.empty() &&
1730 12274 : !crsInfo->pszCelestialBodyName));
1731 11 : };
1732 :
1733 11 : std::vector<std::string> oRet;
1734 11 : if (noneAllowed)
1735 0 : oRet.push_back("none");
1736 11 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1737 11 : if (!currentValue.empty())
1738 : {
1739 : const CPLStringList aosTokens(
1740 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1741 7 : int nCount = 0;
1742 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1743 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1744 : nullptr, &nCount),
1745 14 : OSRDestroyCRSInfoList);
1746 14 : std::string osCode;
1747 :
1748 14 : std::vector<const OSRCRSInfo *> candidates;
1749 46270 : for (int i = 0; i < nCount; ++i)
1750 : {
1751 46263 : const auto *entry = (pCRSList.get())[i];
1752 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1753 : {
1754 49425 : if (aosTokens.size() == 1 ||
1755 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1756 : {
1757 12666 : if (candidates.empty())
1758 7 : osCode = entry->pszCode;
1759 12666 : candidates.push_back(entry);
1760 : }
1761 : }
1762 : }
1763 7 : if (candidates.size() == 1)
1764 : {
1765 1 : oRet.push_back(std::move(osCode));
1766 : }
1767 : else
1768 : {
1769 6 : if (sDatasetLongLatEnv.IsInit())
1770 : {
1771 2 : std::sort(
1772 : candidates.begin(), candidates.end(),
1773 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1774 : {
1775 2999 : const double dfXa =
1776 2999 : a->dfWestLongitudeDeg >
1777 2999 : a->dfEastLongitudeDeg
1778 2999 : ? a->dfWestLongitudeDeg -
1779 0 : a->dfEastLongitudeDeg
1780 2999 : : (180 - a->dfWestLongitudeDeg) +
1781 2999 : (a->dfEastLongitudeDeg - -180);
1782 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1783 2999 : a->dfSouthLatitudeDeg;
1784 2999 : const double dfXb =
1785 2999 : b->dfWestLongitudeDeg >
1786 2999 : b->dfEastLongitudeDeg
1787 2999 : ? b->dfWestLongitudeDeg -
1788 0 : b->dfEastLongitudeDeg
1789 2999 : : (180 - b->dfWestLongitudeDeg) +
1790 2999 : (b->dfEastLongitudeDeg - -180);
1791 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1792 2999 : b->dfSouthLatitudeDeg;
1793 2999 : const double diffArea =
1794 2999 : dfXa * dfYa - dfXb * dfYb;
1795 2999 : if (diffArea < 0)
1796 279 : return true;
1797 2720 : if (diffArea == 0)
1798 : {
1799 2506 : if (std::string_view(a->pszName) ==
1800 2506 : b->pszName)
1801 : {
1802 57 : if (a->eType ==
1803 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1804 13 : b->eType !=
1805 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1806 13 : return true;
1807 44 : if (a->eType ==
1808 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1809 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1810 9 : return true;
1811 35 : return false;
1812 : }
1813 4898 : return std::string_view(a->pszCode) <
1814 4898 : b->pszCode;
1815 : }
1816 214 : return false;
1817 : });
1818 : }
1819 :
1820 12671 : for (const auto *entry : candidates)
1821 : {
1822 25330 : std::string val = std::string(entry->pszCode)
1823 12665 : .append(" -- ")
1824 25330 : .append(entry->pszName);
1825 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1826 1294 : val.append(" (geographic 2D)");
1827 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1828 446 : val.append(" (geographic 3D)");
1829 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1830 397 : val.append(" (geocentric)");
1831 12665 : oRet.push_back(std::move(val));
1832 : }
1833 : }
1834 : }
1835 11 : if (currentValue.empty() || oRet.empty())
1836 : {
1837 : const CPLStringList aosAuthorities(
1838 8 : OSRGetAuthorityListFromDatabase());
1839 24 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1840 : {
1841 20 : int nCount = 0;
1842 20 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1843 : pszAuth, nullptr, &nCount));
1844 20 : if (nCount)
1845 16 : oRet.push_back(std::string(pszAuth).append(":"));
1846 : }
1847 : }
1848 22 : return oRet;
1849 3539 : });
1850 :
1851 3539 : return *this;
1852 : }
1853 :
1854 : /************************************************************************/
1855 : /* GDALAlgorithm::GDALAlgorithm() */
1856 : /************************************************************************/
1857 :
1858 24268 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1859 : const std::string &description,
1860 24268 : const std::string &helpURL)
1861 : : m_name(name), m_description(description), m_helpURL(helpURL),
1862 47946 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1863 24268 : ? "https://gdal.org" + m_helpURL
1864 71959 : : m_helpURL)
1865 : {
1866 : auto &helpArg =
1867 : AddArg("help", 'h', _("Display help message and exit"),
1868 48536 : &m_helpRequested)
1869 24268 : .SetHiddenForAPI()
1870 48536 : .SetCategory(GAAC_COMMON)
1871 14 : .AddAction([this]()
1872 24268 : { m_specialActionRequested = m_calledFromCommandLine; });
1873 : auto &helpDocArg =
1874 : AddArg("help-doc", 0,
1875 : _("Display help message for use by documentation"),
1876 48536 : &m_helpDocRequested)
1877 24268 : .SetHidden()
1878 16 : .AddAction([this]()
1879 24268 : { m_specialActionRequested = m_calledFromCommandLine; });
1880 : auto &jsonUsageArg =
1881 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1882 48536 : &m_JSONUsageRequested)
1883 24268 : .SetHiddenForAPI()
1884 48536 : .SetCategory(GAAC_COMMON)
1885 4 : .AddAction([this]()
1886 24268 : { m_specialActionRequested = m_calledFromCommandLine; });
1887 48536 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1888 48536 : .SetMetaVar("<KEY>=<VALUE>")
1889 24268 : .SetHiddenForAPI()
1890 48536 : .SetCategory(GAAC_COMMON)
1891 : .AddAction(
1892 2 : [this]()
1893 : {
1894 2 : ReportError(
1895 : CE_Warning, CPLE_AppDefined,
1896 : "Configuration options passed with the 'config' argument "
1897 : "are ignored");
1898 24268 : });
1899 :
1900 24268 : AddValidationAction(
1901 14910 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1902 : {
1903 7679 : if (!m_calledFromCommandLine && m_specialActionRequested)
1904 : {
1905 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1906 : {
1907 0 : if (arg->IsExplicitlySet())
1908 : {
1909 0 : ReportError(CE_Failure, CPLE_AppDefined,
1910 : "'%s' argument only available when called "
1911 : "from command line",
1912 0 : arg->GetName().c_str());
1913 0 : return false;
1914 : }
1915 : }
1916 : }
1917 7679 : return true;
1918 : });
1919 24268 : }
1920 :
1921 : /************************************************************************/
1922 : /* GDALAlgorithm::~GDALAlgorithm() */
1923 : /************************************************************************/
1924 :
1925 : GDALAlgorithm::~GDALAlgorithm() = default;
1926 :
1927 : /************************************************************************/
1928 : /* GDALAlgorithm::ParseArgument() */
1929 : /************************************************************************/
1930 :
1931 3399 : bool GDALAlgorithm::ParseArgument(
1932 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1933 : std::map<
1934 : GDALAlgorithmArg *,
1935 : std::variant<std::vector<std::string>, std::vector<int>,
1936 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1937 : &inConstructionValues)
1938 : {
1939 : const bool isListArg =
1940 3399 : GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
1941 3399 : if (arg->IsExplicitlySet() && !isListArg)
1942 : {
1943 : // Hack for "gdal info" to be able to pass an opened raster dataset
1944 : // by "gdal raster info" to the "gdal vector info" algorithm.
1945 4 : if (arg->SkipIfAlreadySet())
1946 : {
1947 1 : arg->SetSkipIfAlreadySet(false);
1948 1 : return true;
1949 : }
1950 :
1951 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1952 : "Argument '%s' has already been specified.", name.c_str());
1953 3 : return false;
1954 : }
1955 :
1956 3467 : if (!arg->GetRepeatedArgAllowed() &&
1957 72 : cpl::contains(inConstructionValues, arg))
1958 : {
1959 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1960 : "Argument '%s' has already been specified.", name.c_str());
1961 1 : return false;
1962 : }
1963 :
1964 3394 : switch (arg->GetType())
1965 : {
1966 330 : case GAAT_BOOLEAN:
1967 : {
1968 330 : if (value.empty() || value == "true")
1969 328 : return arg->Set(true);
1970 2 : else if (value == "false")
1971 1 : return arg->Set(false);
1972 : else
1973 : {
1974 1 : ReportError(
1975 : CE_Failure, CPLE_IllegalArg,
1976 : "Invalid value '%s' for boolean argument '%s'. Should be "
1977 : "'true' or 'false'.",
1978 : value.c_str(), name.c_str());
1979 1 : return false;
1980 : }
1981 : }
1982 :
1983 850 : case GAAT_STRING:
1984 : {
1985 850 : return arg->Set(value);
1986 : }
1987 :
1988 359 : case GAAT_INTEGER:
1989 : {
1990 359 : errno = 0;
1991 359 : char *endptr = nullptr;
1992 359 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1993 358 : if (errno == 0 && endptr &&
1994 717 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1995 : val <= INT_MAX)
1996 : {
1997 356 : return arg->Set(static_cast<int>(val));
1998 : }
1999 : else
2000 : {
2001 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2002 : "Expected integer value for argument '%s', "
2003 : "but got '%s'.",
2004 : name.c_str(), value.c_str());
2005 3 : return false;
2006 : }
2007 : }
2008 :
2009 36 : case GAAT_REAL:
2010 : {
2011 36 : char *endptr = nullptr;
2012 36 : double dfValue = CPLStrtod(value.c_str(), &endptr);
2013 36 : if (endptr != value.c_str() + value.size())
2014 : {
2015 1 : ReportError(
2016 : CE_Failure, CPLE_IllegalArg,
2017 : "Expected real value for argument '%s', but got '%s'.",
2018 : name.c_str(), value.c_str());
2019 1 : return false;
2020 : }
2021 35 : return arg->Set(dfValue);
2022 : }
2023 :
2024 598 : case GAAT_DATASET:
2025 : {
2026 598 : return arg->SetDatasetName(value);
2027 : }
2028 :
2029 274 : case GAAT_STRING_LIST:
2030 : {
2031 : const CPLStringList aosTokens(
2032 274 : arg->GetPackedValuesAllowed()
2033 187 : ? CSLTokenizeString2(value.c_str(), ",",
2034 : CSLT_HONOURSTRINGS |
2035 : CSLT_PRESERVEQUOTES)
2036 461 : : CSLAddString(nullptr, value.c_str()));
2037 274 : if (!cpl::contains(inConstructionValues, arg))
2038 : {
2039 250 : inConstructionValues[arg] = std::vector<std::string>();
2040 : }
2041 : auto &valueVector =
2042 274 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2043 586 : for (const char *v : aosTokens)
2044 : {
2045 312 : valueVector.push_back(v);
2046 : }
2047 274 : if (arg->GetMaxCount() == 1)
2048 : {
2049 4 : bool ret = arg->Set(std::move(valueVector));
2050 4 : inConstructionValues.erase(inConstructionValues.find(arg));
2051 4 : return ret;
2052 : }
2053 :
2054 270 : break;
2055 : }
2056 :
2057 63 : case GAAT_INTEGER_LIST:
2058 : {
2059 : const CPLStringList aosTokens(
2060 63 : arg->GetPackedValuesAllowed()
2061 63 : ? CSLTokenizeString2(
2062 : value.c_str(), ",",
2063 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2064 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2065 126 : : CSLAddString(nullptr, value.c_str()));
2066 63 : if (!cpl::contains(inConstructionValues, arg))
2067 : {
2068 59 : inConstructionValues[arg] = std::vector<int>();
2069 : }
2070 : auto &valueVector =
2071 63 : std::get<std::vector<int>>(inConstructionValues[arg]);
2072 191 : for (const char *v : aosTokens)
2073 : {
2074 134 : errno = 0;
2075 134 : char *endptr = nullptr;
2076 134 : const auto val = std::strtol(v, &endptr, 10);
2077 134 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2078 130 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2079 : {
2080 128 : valueVector.push_back(static_cast<int>(val));
2081 : }
2082 : else
2083 : {
2084 6 : ReportError(
2085 : CE_Failure, CPLE_IllegalArg,
2086 : "Expected list of integer value for argument '%s', "
2087 : "but got '%s'.",
2088 : name.c_str(), value.c_str());
2089 6 : return false;
2090 : }
2091 : }
2092 57 : if (arg->GetMaxCount() == 1)
2093 : {
2094 2 : bool ret = arg->Set(std::move(valueVector));
2095 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2096 2 : return ret;
2097 : }
2098 :
2099 55 : break;
2100 : }
2101 :
2102 107 : case GAAT_REAL_LIST:
2103 : {
2104 : const CPLStringList aosTokens(
2105 107 : arg->GetPackedValuesAllowed()
2106 107 : ? CSLTokenizeString2(
2107 : value.c_str(), ",",
2108 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2109 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2110 214 : : CSLAddString(nullptr, value.c_str()));
2111 107 : if (!cpl::contains(inConstructionValues, arg))
2112 : {
2113 105 : inConstructionValues[arg] = std::vector<double>();
2114 : }
2115 : auto &valueVector =
2116 107 : std::get<std::vector<double>>(inConstructionValues[arg]);
2117 432 : for (const char *v : aosTokens)
2118 : {
2119 329 : char *endptr = nullptr;
2120 329 : double dfValue = CPLStrtod(v, &endptr);
2121 329 : if (strlen(v) == 0 || endptr != v + strlen(v))
2122 : {
2123 4 : ReportError(
2124 : CE_Failure, CPLE_IllegalArg,
2125 : "Expected list of real value for argument '%s', "
2126 : "but got '%s'.",
2127 : name.c_str(), value.c_str());
2128 4 : return false;
2129 : }
2130 325 : valueVector.push_back(dfValue);
2131 : }
2132 103 : if (arg->GetMaxCount() == 1)
2133 : {
2134 2 : bool ret = arg->Set(std::move(valueVector));
2135 2 : inConstructionValues.erase(inConstructionValues.find(arg));
2136 2 : return ret;
2137 : }
2138 :
2139 101 : break;
2140 : }
2141 :
2142 777 : case GAAT_DATASET_LIST:
2143 : {
2144 777 : if (!cpl::contains(inConstructionValues, arg))
2145 : {
2146 769 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2147 : }
2148 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2149 777 : inConstructionValues[arg]);
2150 777 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2151 : {
2152 12 : valueVector.push_back(GDALArgDatasetValue(value));
2153 : }
2154 : else
2155 : {
2156 : const CPLStringList aosTokens(
2157 765 : arg->GetPackedValuesAllowed()
2158 6 : ? CSLTokenizeString2(value.c_str(), ",",
2159 : CSLT_HONOURSTRINGS |
2160 : CSLT_STRIPLEADSPACES)
2161 1536 : : CSLAddString(nullptr, value.c_str()));
2162 1533 : for (const char *v : aosTokens)
2163 : {
2164 768 : valueVector.push_back(GDALArgDatasetValue(v));
2165 : }
2166 : }
2167 777 : if (arg->GetMaxCount() == 1)
2168 : {
2169 670 : bool ret = arg->Set(std::move(valueVector));
2170 670 : inConstructionValues.erase(inConstructionValues.find(arg));
2171 670 : return ret;
2172 : }
2173 :
2174 107 : break;
2175 : }
2176 : }
2177 :
2178 533 : return true;
2179 : }
2180 :
2181 : /************************************************************************/
2182 : /* FormatSuggestionsAsString() */
2183 : /************************************************************************/
2184 :
2185 : static std::string
2186 6 : FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
2187 : bool addDashDashPrefix)
2188 : {
2189 6 : std::string ret;
2190 14 : for (auto [i, suggestion] : cpl::enumerate(suggestions))
2191 : {
2192 8 : if (i > 0)
2193 : {
2194 2 : ret += (i + 1 < suggestions.size()) ? ", " : " or ";
2195 : }
2196 8 : ret += '\'';
2197 8 : if (addDashDashPrefix)
2198 6 : ret += "--";
2199 8 : ret += suggestion;
2200 8 : ret += '\'';
2201 : }
2202 6 : return ret;
2203 : }
2204 :
2205 : /************************************************************************/
2206 : /* GDALAlgorithm::ParseCommandLineArguments() */
2207 : /************************************************************************/
2208 :
2209 2334 : bool GDALAlgorithm::ParseCommandLineArguments(
2210 : const std::vector<std::string> &args)
2211 : {
2212 2334 : if (m_parsedSubStringAlreadyCalled)
2213 : {
2214 6 : ReportError(CE_Failure, CPLE_AppDefined,
2215 : "ParseCommandLineArguments() can only be called once per "
2216 : "instance.");
2217 6 : return false;
2218 : }
2219 2328 : m_parsedSubStringAlreadyCalled = true;
2220 :
2221 : // AWS like syntax supported too (not advertized)
2222 2328 : if (args.size() == 1 && args[0] == "help")
2223 : {
2224 1 : auto arg = GetArg("help");
2225 1 : assert(arg);
2226 1 : arg->Set(true);
2227 1 : arg->RunActions();
2228 1 : return true;
2229 : }
2230 :
2231 2327 : if (HasSubAlgorithms())
2232 : {
2233 495 : if (args.empty())
2234 : {
2235 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2236 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2237 2 : return false;
2238 : }
2239 493 : if (!args[0].empty() && args[0][0] == '-')
2240 : {
2241 : // go on argument parsing
2242 : }
2243 : else
2244 : {
2245 490 : const auto nCounter = CPLGetErrorCounter();
2246 490 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2247 490 : if (m_selectedSubAlgHolder)
2248 : {
2249 487 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2250 487 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2251 487 : m_referencePath);
2252 487 : m_selectedSubAlg->m_executionForStreamOutput =
2253 487 : m_executionForStreamOutput;
2254 487 : m_selectedSubAlg->m_calledFromCommandLine =
2255 487 : m_calledFromCommandLine;
2256 487 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2257 487 : m_skipValidationInParseCommandLine;
2258 487 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2259 974 : std::vector<std::string>(args.begin() + 1, args.end()));
2260 487 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2261 487 : return bRet;
2262 : }
2263 : else
2264 : {
2265 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2266 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2267 : {
2268 2 : ReportError(CE_Failure, CPLE_AppDefined,
2269 2 : "Unknown command: '%s'", args[0].c_str());
2270 : }
2271 3 : return false;
2272 : }
2273 : }
2274 : }
2275 :
2276 : std::map<
2277 : GDALAlgorithmArg *,
2278 : std::variant<std::vector<std::string>, std::vector<int>,
2279 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2280 3670 : inConstructionValues;
2281 :
2282 3670 : std::vector<std::string> lArgs(args);
2283 1835 : bool helpValueRequested = false;
2284 5238 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2285 : {
2286 3516 : const auto &strArg = lArgs[i];
2287 3516 : GDALAlgorithmArg *arg = nullptr;
2288 3516 : std::string name;
2289 3516 : std::string value;
2290 3516 : bool hasValue = false;
2291 3516 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2292 5 : helpValueRequested = true;
2293 3516 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2294 : {
2295 2180 : const auto equalPos = strArg.find('=');
2296 4360 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2297 2180 : : strArg;
2298 2180 : const std::string nameWithoutDash = name.substr(2);
2299 2180 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2300 2236 : if (m_arbitraryLongNameArgsAllowed &&
2301 2236 : iterArg == m_mapLongNameToArg.end())
2302 : {
2303 17 : GetArg(nameWithoutDash);
2304 17 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2305 : }
2306 2180 : if (iterArg == m_mapLongNameToArg.end())
2307 : {
2308 : const auto suggestions =
2309 28 : GetSuggestionsForArgumentName(nameWithoutDash);
2310 28 : if (!suggestions.empty())
2311 : {
2312 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2313 : "Option '%s' is unknown. Do you mean %s?",
2314 : name.c_str(),
2315 6 : FormatSuggestionsAsString(
2316 : suggestions, /* addDashDashPrefix = */ true)
2317 : .c_str());
2318 : }
2319 : else
2320 : {
2321 25 : ReportError(CE_Failure, CPLE_IllegalArg,
2322 : "Option '%s' is unknown.", name.c_str());
2323 : }
2324 28 : return false;
2325 : }
2326 2152 : arg = iterArg->second;
2327 2152 : if (equalPos != std::string::npos)
2328 : {
2329 472 : hasValue = true;
2330 472 : value = strArg.substr(equalPos + 1);
2331 : }
2332 : }
2333 1413 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2334 77 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2335 : {
2336 149 : for (size_t j = 1; j < strArg.size(); ++j)
2337 : {
2338 77 : name.clear();
2339 77 : name += strArg[j];
2340 77 : const auto iterArg = m_mapShortNameToArg.find(name);
2341 77 : if (iterArg == m_mapShortNameToArg.end())
2342 : {
2343 5 : const std::string nameWithoutDash = strArg.substr(1);
2344 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2345 10 : m_mapLongNameToArg.end())
2346 : {
2347 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2348 : "Short name option '%s' is unknown. Do you "
2349 : "mean '--%s' (with leading double dash) ?",
2350 : name.c_str(), nameWithoutDash.c_str());
2351 : }
2352 : else
2353 : {
2354 : const auto suggestions =
2355 8 : GetSuggestionsForArgumentName(nameWithoutDash);
2356 4 : if (!suggestions.empty())
2357 : {
2358 1 : ReportError(
2359 : CE_Failure, CPLE_IllegalArg,
2360 : "Short name option '%s' is unknown. Do you "
2361 : "mean %s (with leading double dash) ?",
2362 : name.c_str(),
2363 2 : FormatSuggestionsAsString(
2364 : suggestions, /* addDashDashPrefix = */ true)
2365 : .c_str());
2366 : }
2367 : else
2368 : {
2369 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2370 : "Short name option '%s' is unknown.",
2371 : name.c_str());
2372 : }
2373 : }
2374 5 : return false;
2375 : }
2376 72 : arg = iterArg->second;
2377 72 : if (strArg.size() > 2)
2378 : {
2379 0 : if (arg->GetType() != GAAT_BOOLEAN)
2380 : {
2381 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2382 : "Invalid argument '%s'. Option '%s' is not "
2383 : "a boolean option.",
2384 : strArg.c_str(), name.c_str());
2385 0 : return false;
2386 : }
2387 :
2388 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2389 0 : return false;
2390 : }
2391 : }
2392 72 : if (strArg.size() > 2)
2393 : {
2394 0 : lArgs.erase(lArgs.begin() + i);
2395 0 : continue;
2396 : }
2397 : }
2398 : else
2399 : {
2400 1259 : ++i;
2401 1259 : continue;
2402 : }
2403 2224 : CPLAssert(arg);
2404 :
2405 2224 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2406 : {
2407 331 : if (!hasValue)
2408 : {
2409 328 : hasValue = true;
2410 328 : value = "true";
2411 : }
2412 : }
2413 :
2414 2224 : if (!hasValue)
2415 : {
2416 1424 : if (i + 1 == lArgs.size())
2417 : {
2418 41 : if (m_parseForAutoCompletion)
2419 : {
2420 35 : lArgs.erase(lArgs.begin() + i);
2421 35 : break;
2422 : }
2423 6 : ReportError(
2424 : CE_Failure, CPLE_IllegalArg,
2425 : "Expected value for argument '%s', but ran short of tokens",
2426 : name.c_str());
2427 6 : return false;
2428 : }
2429 1383 : value = lArgs[i + 1];
2430 1383 : lArgs.erase(lArgs.begin() + i + 1);
2431 : }
2432 :
2433 2183 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2434 39 : return false;
2435 :
2436 2144 : lArgs.erase(lArgs.begin() + i);
2437 : }
2438 :
2439 1757 : if (m_specialActionRequested)
2440 : {
2441 26 : return true;
2442 : }
2443 :
2444 2199 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2445 : {
2446 2165 : for (auto &[arg, value] : inConstructionValues)
2447 : {
2448 492 : if (arg->GetType() == GAAT_STRING_LIST)
2449 : {
2450 242 : if (!arg->Set(std::get<std::vector<std::string>>(
2451 242 : inConstructionValues[arg])))
2452 : {
2453 34 : return false;
2454 : }
2455 : }
2456 250 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2457 : {
2458 52 : if (!arg->Set(
2459 52 : std::get<std::vector<int>>(inConstructionValues[arg])))
2460 : {
2461 4 : return false;
2462 : }
2463 : }
2464 198 : else if (arg->GetType() == GAAT_REAL_LIST)
2465 : {
2466 99 : if (!arg->Set(std::get<std::vector<double>>(
2467 99 : inConstructionValues[arg])))
2468 : {
2469 10 : return false;
2470 : }
2471 : }
2472 99 : else if (arg->GetType() == GAAT_DATASET_LIST)
2473 : {
2474 99 : if (!arg->Set(
2475 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2476 99 : inConstructionValues[arg]))))
2477 : {
2478 2 : return false;
2479 : }
2480 : }
2481 : }
2482 1673 : return true;
2483 1731 : };
2484 :
2485 : // Process positional arguments that have not been set through their
2486 : // option name.
2487 1731 : size_t i = 0;
2488 1731 : size_t iCurPosArg = 0;
2489 :
2490 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2491 1754 : if (m_positionalArgs.size() == 3 &&
2492 24 : (m_positionalArgs[0]->IsRequired() ||
2493 23 : m_positionalArgs[0]->GetMinCount() == 1) &&
2494 44 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2495 29 : (m_positionalArgs[1]->IsRequired() ||
2496 29 : m_positionalArgs[1]->GetMinCount() == 1) &&
2497 : /* Second argument may have several occurrences */
2498 44 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2499 31 : (m_positionalArgs[2]->IsRequired() ||
2500 22 : m_positionalArgs[2]->GetMinCount() == 1) &&
2501 13 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2502 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2503 1763 : !m_positionalArgs[1]->IsExplicitlySet() &&
2504 9 : !m_positionalArgs[2]->IsExplicitlySet())
2505 : {
2506 7 : if (lArgs.size() - i < 3)
2507 : {
2508 1 : ReportError(CE_Failure, CPLE_AppDefined,
2509 : "Not enough positional values.");
2510 1 : return false;
2511 : }
2512 12 : bool ok = ParseArgument(m_positionalArgs[0],
2513 6 : m_positionalArgs[0]->GetName().c_str(),
2514 6 : lArgs[i], inConstructionValues);
2515 6 : if (ok)
2516 : {
2517 5 : ++i;
2518 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2519 : {
2520 12 : ok = ParseArgument(m_positionalArgs[1],
2521 6 : m_positionalArgs[1]->GetName().c_str(),
2522 6 : lArgs[i], inConstructionValues);
2523 : }
2524 : }
2525 6 : if (ok)
2526 : {
2527 10 : ok = ParseArgument(m_positionalArgs[2],
2528 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2529 : inConstructionValues);
2530 5 : ++i;
2531 : }
2532 6 : if (!ok)
2533 : {
2534 3 : ProcessInConstructionValues();
2535 3 : return false;
2536 : }
2537 : }
2538 :
2539 577 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2540 638 : !m_positionalArgs[0]->IsExplicitlySet() &&
2541 2624 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2542 72 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2543 36 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2544 : {
2545 36 : ++iCurPosArg;
2546 : }
2547 :
2548 2906 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2549 : {
2550 1186 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2551 1197 : while (arg->IsExplicitlySet())
2552 : {
2553 12 : ++iCurPosArg;
2554 12 : if (iCurPosArg == m_positionalArgs.size())
2555 1 : break;
2556 11 : arg = m_positionalArgs[iCurPosArg];
2557 : }
2558 1186 : if (iCurPosArg == m_positionalArgs.size())
2559 : {
2560 1 : break;
2561 : }
2562 1854 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2563 669 : arg->GetMinCount() != arg->GetMaxCount())
2564 : {
2565 102 : if (iCurPosArg == 0)
2566 : {
2567 80 : size_t nCountAtEnd = 0;
2568 109 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2569 : {
2570 31 : const auto *otherArg = m_positionalArgs[j];
2571 31 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2572 : {
2573 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2574 : {
2575 2 : ReportError(
2576 : CE_Failure, CPLE_AppDefined,
2577 : "Ambiguity in definition of positional "
2578 : "argument "
2579 : "'%s' given it has a varying number of values, "
2580 : "but follows argument '%s' which also has a "
2581 : "varying number of values",
2582 1 : otherArg->GetName().c_str(),
2583 1 : arg->GetName().c_str());
2584 1 : ProcessInConstructionValues();
2585 1 : return false;
2586 : }
2587 3 : nCountAtEnd += otherArg->GetMinCount();
2588 : }
2589 : else
2590 : {
2591 27 : if (!otherArg->IsRequired())
2592 : {
2593 2 : ReportError(
2594 : CE_Failure, CPLE_AppDefined,
2595 : "Ambiguity in definition of positional "
2596 : "argument "
2597 : "'%s', given it is not required but follows "
2598 : "argument '%s' which has a varying number of "
2599 : "values",
2600 1 : otherArg->GetName().c_str(),
2601 1 : arg->GetName().c_str());
2602 1 : ProcessInConstructionValues();
2603 1 : return false;
2604 : }
2605 26 : nCountAtEnd++;
2606 : }
2607 : }
2608 78 : if (lArgs.size() < nCountAtEnd)
2609 : {
2610 1 : ReportError(CE_Failure, CPLE_AppDefined,
2611 : "Not enough positional values.");
2612 1 : ProcessInConstructionValues();
2613 1 : return false;
2614 : }
2615 162 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2616 : {
2617 85 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2618 : inConstructionValues))
2619 : {
2620 0 : ProcessInConstructionValues();
2621 0 : return false;
2622 : }
2623 : }
2624 : }
2625 22 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2626 : {
2627 49 : for (; i < lArgs.size(); ++i)
2628 : {
2629 28 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2630 : inConstructionValues))
2631 : {
2632 0 : ProcessInConstructionValues();
2633 0 : return false;
2634 : }
2635 : }
2636 : }
2637 : else
2638 : {
2639 1 : ReportError(CE_Failure, CPLE_AppDefined,
2640 : "Ambiguity in definition of positional arguments: "
2641 : "arguments with varying number of values must be "
2642 : "first or last one.");
2643 1 : return false;
2644 : }
2645 : }
2646 : else
2647 : {
2648 1083 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2649 : {
2650 1 : ReportError(CE_Failure, CPLE_AppDefined,
2651 : "Not enough positional values.");
2652 1 : return false;
2653 : }
2654 1082 : const size_t iMax = i + arg->GetMaxCount();
2655 2167 : for (; i < iMax; ++i)
2656 : {
2657 1086 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2658 : inConstructionValues))
2659 : {
2660 1 : ProcessInConstructionValues();
2661 1 : return false;
2662 : }
2663 : }
2664 : }
2665 1179 : ++iCurPosArg;
2666 : }
2667 :
2668 1721 : if (i < lArgs.size())
2669 : {
2670 21 : ReportError(CE_Failure, CPLE_AppDefined,
2671 : "Positional values starting at '%s' are not expected.",
2672 21 : lArgs[i].c_str());
2673 21 : return false;
2674 : }
2675 :
2676 1700 : if (!ProcessInConstructionValues())
2677 : {
2678 33 : return false;
2679 : }
2680 :
2681 : // Skip to first unset positional argument.
2682 2744 : while (iCurPosArg < m_positionalArgs.size() &&
2683 588 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2684 : {
2685 489 : ++iCurPosArg;
2686 : }
2687 : // Check if this positional argument is required.
2688 1765 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2689 98 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2690 50 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2691 48 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2692 : {
2693 87 : ReportError(CE_Failure, CPLE_AppDefined,
2694 : "Positional arguments starting at '%s' have not been "
2695 : "specified.",
2696 87 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2697 87 : return false;
2698 : }
2699 :
2700 1580 : if (m_calledFromCommandLine)
2701 : {
2702 5518 : for (auto &arg : m_args)
2703 : {
2704 7134 : if (arg->IsExplicitlySet() &&
2705 1105 : ((arg->GetType() == GAAT_STRING &&
2706 1102 : arg->Get<std::string>() == "?") ||
2707 999 : (arg->GetType() == GAAT_STRING_LIST &&
2708 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2709 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2710 : {
2711 : {
2712 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2713 5 : ValidateArguments();
2714 : }
2715 :
2716 5 : auto choices = arg->GetChoices();
2717 5 : if (choices.empty())
2718 2 : choices = arg->GetAutoCompleteChoices(std::string());
2719 5 : if (!choices.empty())
2720 : {
2721 5 : if (choices.size() == 1)
2722 : {
2723 4 : ReportError(
2724 : CE_Failure, CPLE_AppDefined,
2725 : "Single potential value for argument '%s' is '%s'",
2726 4 : arg->GetName().c_str(), choices.front().c_str());
2727 : }
2728 : else
2729 : {
2730 6 : std::string msg("Potential values for argument '");
2731 3 : msg += arg->GetName();
2732 3 : msg += "' are:";
2733 45 : for (const auto &v : choices)
2734 : {
2735 42 : msg += "\n- ";
2736 42 : msg += v;
2737 : }
2738 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2739 : msg.c_str());
2740 : }
2741 5 : return false;
2742 : }
2743 : }
2744 : }
2745 : }
2746 :
2747 1575 : return m_skipValidationInParseCommandLine || ValidateArguments();
2748 : }
2749 :
2750 : /************************************************************************/
2751 : /* GDALAlgorithm::ReportError() */
2752 : /************************************************************************/
2753 :
2754 : //! @cond Doxygen_Suppress
2755 973 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2756 : const char *fmt, ...) const
2757 : {
2758 : va_list args;
2759 973 : va_start(args, fmt);
2760 973 : CPLError(eErrClass, err_no, "%s",
2761 973 : std::string(m_name)
2762 973 : .append(": ")
2763 1946 : .append(CPLString().vPrintf(fmt, args))
2764 : .c_str());
2765 973 : va_end(args);
2766 973 : }
2767 :
2768 : //! @endcond
2769 :
2770 : /************************************************************************/
2771 : /* GDALAlgorithm::ProcessDatasetArg() */
2772 : /************************************************************************/
2773 :
2774 10981 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2775 : GDALAlgorithm *algForOutput)
2776 : {
2777 10981 : bool ret = true;
2778 :
2779 10981 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2780 10981 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2781 10981 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2782 :
2783 10981 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2784 10981 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2785 10981 : const bool append = hasAppendArg && appendArg->Get<bool>();
2786 :
2787 10981 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2788 : const bool overwrite =
2789 18032 : (arg->IsOutput() && overwriteArg &&
2790 18032 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2791 :
2792 10981 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2793 21962 : auto &val = [arg]() -> GDALArgDatasetValue &
2794 : {
2795 10981 : if (arg->GetType() == GAAT_DATASET_LIST)
2796 6444 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2797 : else
2798 4537 : return arg->Get<GDALArgDatasetValue>();
2799 10981 : }();
2800 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2801 17445 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2802 17453 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2803 8 : !overwrite;
2804 :
2805 : // Used for nested pipelines
2806 : const auto oIterDatasetNameToDataset =
2807 21959 : val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
2808 10981 : : m_oMapDatasetNameToDataset.end();
2809 :
2810 10981 : if (!val.GetDatasetRef() && !val.IsNameSet())
2811 : {
2812 3 : ReportError(CE_Failure, CPLE_AppDefined,
2813 : "Argument '%s' has no dataset object or dataset name.",
2814 3 : arg->GetName().c_str());
2815 3 : ret = false;
2816 : }
2817 10978 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2818 : {
2819 3 : return false;
2820 : }
2821 310 : else if (m_inputDatasetCanBeOmitted &&
2822 11285 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2823 17 : !arg->IsOutput())
2824 : {
2825 17 : return true;
2826 : }
2827 16207 : else if (!val.GetDatasetRef() &&
2828 5570 : (arg->AutoOpenDataset() ||
2829 16528 : oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
2830 4929 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2831 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2832 : {
2833 1527 : int flags = arg->GetDatasetType();
2834 1527 : bool assignToOutputArg = false;
2835 :
2836 : // Check if input and output parameters point to the same
2837 : // filename (for vector datasets)
2838 2832 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2839 2832 : outputArg && outputArg->GetType() == GAAT_DATASET)
2840 : {
2841 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2842 121 : if (!outputVal.GetDatasetRef() &&
2843 121 : outputVal.GetName() == val.GetName() &&
2844 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2845 : {
2846 2 : assignToOutputArg = true;
2847 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2848 : }
2849 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2850 : {
2851 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2852 : }
2853 : }
2854 :
2855 1527 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2856 1444 : flags |= GDAL_OF_VERBOSE_ERROR;
2857 1527 : if ((arg == outputArg || !outputArg) && update)
2858 : {
2859 85 : flags |= GDAL_OF_UPDATE;
2860 85 : if (!append)
2861 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2862 : }
2863 :
2864 1527 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2865 : const bool readOnly =
2866 1571 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2867 44 : readOnlyArg->Get<bool>());
2868 1527 : if (readOnly)
2869 12 : flags &= ~GDAL_OF_UPDATE;
2870 :
2871 3054 : CPLStringList aosOpenOptions;
2872 3054 : CPLStringList aosAllowedDrivers;
2873 1527 : if (arg->IsInput())
2874 : {
2875 1527 : if (arg == outputArg)
2876 : {
2877 83 : if (update && !overwrite)
2878 : {
2879 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2880 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2881 46 : aosOpenOptions = CPLStringList(
2882 46 : ooArg->Get<std::vector<std::string>>());
2883 : }
2884 : }
2885 : else
2886 : {
2887 1444 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2888 1444 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2889 : aosOpenOptions =
2890 1366 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2891 :
2892 1444 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2893 1444 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2894 : aosAllowedDrivers =
2895 1322 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2896 : }
2897 : }
2898 :
2899 3054 : std::string osDatasetName = val.GetName();
2900 1527 : if (!m_referencePath.empty())
2901 : {
2902 46 : osDatasetName = GDALDataset::BuildFilename(
2903 23 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2904 : }
2905 1527 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2906 0 : osDatasetName = "/vsistdin/";
2907 :
2908 : // Handle special case of overview delete in GTiff which would fail
2909 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2910 145 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2911 1674 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2912 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2913 : {
2914 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2915 : GDALDriverH hDrv =
2916 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2917 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2918 : {
2919 : // Cleaning does not break COG layout
2920 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2921 : }
2922 : }
2923 :
2924 : GDALDataset *poDS;
2925 3054 : CPLErrorAccumulator oAccumulator;
2926 : {
2927 3054 : auto oContext = oAccumulator.InstallForCurrentScope();
2928 :
2929 1527 : poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
2930 1527 : ? oIterDatasetNameToDataset->second
2931 1511 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2932 1511 : aosAllowedDrivers.List(),
2933 1511 : aosOpenOptions.List());
2934 :
2935 70 : if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
2936 1597 : !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
2937 : {
2938 40 : auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
2939 80 : osDatasetName.c_str(), nullptr);
2940 40 : if (eErr == OGRERR_NONE)
2941 : {
2942 12 : auto poMemDS = std::make_unique<MEMDataset>();
2943 12 : auto *poLayer = poMemDS->CreateLayer(
2944 : "layer", poWktGeom->getSpatialReference(),
2945 6 : poWktGeom->getGeometryType());
2946 :
2947 6 : auto poFeatureDefn = poLayer->GetLayerDefn();
2948 12 : OGRFeature oFeature(poFeatureDefn);
2949 :
2950 6 : oFeature.SetGeometry(std::move(poWktGeom));
2951 6 : if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
2952 : {
2953 6 : poDS = poMemDS.release();
2954 6 : oAccumulator.ClearErrors();
2955 : }
2956 : }
2957 : }
2958 :
2959 : // Retry with PostGIS vector driver
2960 64 : if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
2961 62 : cpl::starts_with(osDatasetName, "PG:") &&
2962 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2963 1591 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2964 : {
2965 0 : oAccumulator.ClearErrors();
2966 0 : poDS = GDALDataset::Open(
2967 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2968 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2969 : }
2970 : }
2971 1527 : oAccumulator.ReplayErrors();
2972 :
2973 1527 : if (poDS)
2974 : {
2975 1463 : if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
2976 : {
2977 16 : if (arg->GetType() == GAAT_DATASET)
2978 8 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2979 16 : poDS->Reference();
2980 16 : m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
2981 : }
2982 :
2983 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2984 : // where the PG: dataset will be first opened with the PostGISRaster
2985 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2986 2037 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2987 2151 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2988 114 : aosOpenOptions.empty())
2989 : {
2990 110 : auto poDrv = poDS->GetDriver();
2991 110 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2992 : {
2993 : // Retry with PostgreSQL (vector) driver
2994 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2995 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2996 0 : if (poTmpDS)
2997 : {
2998 0 : poDS->ReleaseRef();
2999 0 : poDS = poTmpDS.release();
3000 : }
3001 : }
3002 : }
3003 :
3004 1463 : if (assignToOutputArg)
3005 : {
3006 : // Avoid opening twice the same datasource if it is both
3007 : // the input and output.
3008 : // Known to cause problems with at least FGdb, SQLite
3009 : // and GPKG drivers. See #4270
3010 : // Restrict to those 3 drivers. For example it is known
3011 : // to break with the PG driver due to the way it
3012 : // manages transactions.
3013 2 : auto poDriver = poDS->GetDriver();
3014 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
3015 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
3016 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
3017 : {
3018 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
3019 : }
3020 : }
3021 1463 : val.SetDatasetOpenedByAlgorithm();
3022 1463 : val.Set(poDS);
3023 1463 : poDS->ReleaseRef();
3024 : }
3025 64 : else if (!append)
3026 : {
3027 62 : ret = false;
3028 : }
3029 : }
3030 :
3031 : // Deal with overwriting the output dataset
3032 10961 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
3033 : {
3034 3404 : if (!append)
3035 : {
3036 : // If outputting to MEM, do not try to erase a real file of the same name!
3037 : const auto outputFormatArg =
3038 3392 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3039 10138 : if (!(outputFormatArg &&
3040 3373 : outputFormatArg->GetType() == GAAT_STRING &&
3041 3373 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3042 2225 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3043 1252 : "stream") ||
3044 1252 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
3045 : "Memory"))))
3046 : {
3047 1271 : const char *pszType = "";
3048 1271 : GDALDriver *poDriver = nullptr;
3049 2496 : if (!val.GetName().empty() &&
3050 1225 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
3051 : &poDriver))
3052 : {
3053 79 : if (!overwrite)
3054 : {
3055 68 : std::string options;
3056 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
3057 : {
3058 11 : options += "--";
3059 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
3060 : }
3061 34 : if (hasAppendArg)
3062 : {
3063 22 : if (!options.empty())
3064 8 : options += '/';
3065 22 : options += "--";
3066 22 : options += GDAL_ARG_NAME_APPEND;
3067 : }
3068 34 : if (hasUpdateArg)
3069 : {
3070 15 : if (!options.empty())
3071 12 : options += '/';
3072 15 : options += "--";
3073 15 : options += GDAL_ARG_NAME_UPDATE;
3074 : }
3075 :
3076 34 : if (poDriver)
3077 : {
3078 68 : const char *pszPrefix = poDriver->GetMetadataItem(
3079 34 : GDAL_DMD_CONNECTION_PREFIX);
3080 34 : if (pszPrefix &&
3081 0 : STARTS_WITH_CI(val.GetName().c_str(),
3082 : pszPrefix))
3083 : {
3084 0 : bool bExists = false;
3085 : {
3086 : CPLErrorStateBackuper oBackuper(
3087 0 : CPLQuietErrorHandler);
3088 0 : bExists = std::unique_ptr<GDALDataset>(
3089 : GDALDataset::Open(
3090 0 : val.GetName().c_str())) !=
3091 : nullptr;
3092 : }
3093 0 : if (bExists)
3094 : {
3095 0 : if (!options.empty())
3096 0 : options = " You may specify the " +
3097 0 : options + " option.";
3098 0 : ReportError(CE_Failure, CPLE_AppDefined,
3099 : "%s '%s' already exists.%s",
3100 0 : pszType, val.GetName().c_str(),
3101 : options.c_str());
3102 0 : return false;
3103 : }
3104 :
3105 0 : return true;
3106 : }
3107 : }
3108 :
3109 34 : if (!options.empty())
3110 28 : options = '/' + options;
3111 68 : ReportError(
3112 : CE_Failure, CPLE_AppDefined,
3113 : "%s '%s' already exists. You may specify the "
3114 : "--overwrite%s option.",
3115 34 : pszType, val.GetName().c_str(), options.c_str());
3116 34 : return false;
3117 : }
3118 45 : else if (EQUAL(pszType, "File"))
3119 : {
3120 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3121 : {
3122 0 : ReportError(CE_Failure, CPLE_AppDefined,
3123 : "Deleting %s failed: %s",
3124 0 : val.GetName().c_str(),
3125 0 : VSIStrerror(errno));
3126 0 : return false;
3127 : }
3128 : }
3129 44 : else if (EQUAL(pszType, "Directory"))
3130 : {
3131 : // We don't want the user to accidentally erase a non-GDAL dataset
3132 1 : ReportError(CE_Failure, CPLE_AppDefined,
3133 : "Directory '%s' already exists, but is not "
3134 : "recognized as a valid GDAL dataset. "
3135 : "Please manually delete it before retrying",
3136 1 : val.GetName().c_str());
3137 1 : return false;
3138 : }
3139 43 : else if (poDriver)
3140 : {
3141 : bool bDeleteOK;
3142 : {
3143 : CPLErrorStateBackuper oBackuper(
3144 43 : CPLQuietErrorHandler);
3145 43 : bDeleteOK = (poDriver->Delete(
3146 43 : val.GetName().c_str()) == CE_None);
3147 : }
3148 : VSIStatBufL sStat;
3149 46 : if (!bDeleteOK &&
3150 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3151 : {
3152 3 : if (VSI_ISDIR(sStat.st_mode))
3153 : {
3154 : // We don't want the user to accidentally erase a non-GDAL dataset
3155 0 : ReportError(
3156 : CE_Failure, CPLE_AppDefined,
3157 : "Directory '%s' already exists, but is not "
3158 : "recognized as a valid GDAL dataset. "
3159 : "Please manually delete it before retrying",
3160 0 : val.GetName().c_str());
3161 2 : return false;
3162 : }
3163 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3164 : {
3165 2 : ReportError(CE_Failure, CPLE_AppDefined,
3166 : "Deleting %s failed: %s",
3167 2 : val.GetName().c_str(),
3168 2 : VSIStrerror(errno));
3169 2 : return false;
3170 : }
3171 : }
3172 : }
3173 : }
3174 : }
3175 : }
3176 : }
3177 :
3178 : // If outputting to stdout, automatically turn off progress bar
3179 10924 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3180 : {
3181 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3182 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3183 5 : quietArg->Set(true);
3184 : }
3185 :
3186 10924 : return ret;
3187 : }
3188 :
3189 : /************************************************************************/
3190 : /* GDALAlgorithm::ValidateArguments() */
3191 : /************************************************************************/
3192 :
3193 7683 : bool GDALAlgorithm::ValidateArguments()
3194 : {
3195 7683 : if (m_selectedSubAlg)
3196 3 : return m_selectedSubAlg->ValidateArguments();
3197 :
3198 7680 : if (m_specialActionRequested)
3199 1 : return true;
3200 :
3201 7679 : m_arbitraryLongNameArgsAllowed = false;
3202 :
3203 : // If only --output=format=MEM/stream is specified and not --output,
3204 : // then set empty name for --output.
3205 7679 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3206 7679 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3207 4499 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3208 2849 : !outputArg->IsExplicitlySet() &&
3209 382 : outputFormatArg->GetType() == GAAT_STRING &&
3210 382 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3211 617 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3212 12531 : outputArg->GetType() == GAAT_DATASET &&
3213 353 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3214 : {
3215 353 : outputArg->Get<GDALArgDatasetValue>().Set("");
3216 : }
3217 :
3218 : // The method may emit several errors if several constraints are not met.
3219 7679 : bool ret = true;
3220 15358 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3221 15358 : std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
3222 143165 : for (auto &arg : m_args)
3223 : {
3224 : // Check mutually exclusive/dependent arguments
3225 135486 : if (arg->IsExplicitlySet())
3226 : {
3227 :
3228 21359 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3229 21359 : if (!mutualExclusionGroup.empty())
3230 : {
3231 : auto oIter =
3232 929 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3233 929 : if (oIter != mutualExclusionGroupUsed.end())
3234 : {
3235 13 : ret = false;
3236 26 : ReportError(
3237 : CE_Failure, CPLE_AppDefined,
3238 : "Argument '%s' is mutually exclusive with '%s'.",
3239 26 : arg->GetName().c_str(), oIter->second.c_str());
3240 : }
3241 : else
3242 : {
3243 916 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3244 1832 : arg->GetName();
3245 : }
3246 : }
3247 :
3248 21359 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3249 21359 : if (!mutualDependencyGroup.empty())
3250 : {
3251 78 : if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
3252 156 : mutualDependencyGroupUsed.end())
3253 : {
3254 129 : mutualDependencyGroupUsed[mutualDependencyGroup] = {
3255 129 : arg->GetName()};
3256 : }
3257 : else
3258 : {
3259 70 : mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
3260 35 : arg->GetName());
3261 : }
3262 : }
3263 :
3264 : // Check direct dependencies
3265 21371 : for (const auto &dependency : arg->GetDirectDependencies())
3266 : {
3267 12 : auto depArg = GetArg(dependency);
3268 12 : if (!depArg)
3269 : {
3270 0 : ret = false;
3271 0 : ReportError(CE_Failure, CPLE_AppDefined,
3272 : "Argument '%s' depends on argument '%s' that "
3273 : "is not defined.",
3274 0 : arg->GetName().c_str(), dependency.c_str());
3275 : }
3276 12 : else if (!depArg->IsExplicitlySet())
3277 : {
3278 6 : ret = false;
3279 12 : ReportError(CE_Failure, CPLE_AppDefined,
3280 : "Argument '%s' depends on argument '%s' that "
3281 : "has not been specified.",
3282 6 : arg->GetName().c_str(),
3283 6 : depArg->GetName().c_str());
3284 : }
3285 : }
3286 : }
3287 :
3288 135662 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3289 176 : !arg->HasDefaultValue())
3290 : {
3291 176 : bool emitError = true;
3292 176 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3293 176 : if (!mutualExclusionGroup.empty())
3294 : {
3295 1885 : for (const auto &otherArg : m_args)
3296 : {
3297 1859 : if (otherArg->GetMutualExclusionGroup() ==
3298 1988 : mutualExclusionGroup &&
3299 129 : otherArg->IsExplicitlySet())
3300 : {
3301 74 : emitError = false;
3302 74 : break;
3303 : }
3304 : }
3305 : }
3306 266 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3307 57 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3308 66 : (arg->GetType() == GAAT_DATASET ||
3309 33 : arg->GetType() == GAAT_DATASET_LIST)))
3310 : {
3311 69 : ReportError(CE_Failure, CPLE_AppDefined,
3312 : "Required argument '%s' has not been specified.",
3313 69 : arg->GetName().c_str());
3314 69 : ret = false;
3315 : }
3316 : }
3317 135310 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3318 : {
3319 4537 : if (!ProcessDatasetArg(arg.get(), this))
3320 49 : ret = false;
3321 : }
3322 :
3323 135486 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
3324 : {
3325 6220 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3326 6220 : if (listVal.size() == 1)
3327 : {
3328 6066 : if (!ProcessDatasetArg(arg.get(), this))
3329 42 : ret = false;
3330 : }
3331 : else
3332 : {
3333 473 : for (auto &val : listVal)
3334 : {
3335 319 : if (val.GetDatasetRef())
3336 : {
3337 120 : if (!CheckCanSetDatasetObject(arg.get()))
3338 : {
3339 0 : ret = false;
3340 : }
3341 315 : continue;
3342 : }
3343 :
3344 199 : if (val.GetName().empty())
3345 : {
3346 0 : ReportError(CE_Failure, CPLE_AppDefined,
3347 : "Argument '%s' has no dataset object or "
3348 : "dataset name.",
3349 0 : arg->GetName().c_str());
3350 0 : ret = false;
3351 0 : continue;
3352 : }
3353 :
3354 199 : auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
3355 199 : if (oIter != m_oMapDatasetNameToDataset.end())
3356 : {
3357 2 : auto poDS = oIter->second;
3358 2 : val.SetDatasetOpenedByAlgorithm();
3359 2 : val.Set(poDS);
3360 2 : m_oMapDatasetNameToDataset.erase(oIter);
3361 2 : continue;
3362 : }
3363 :
3364 197 : if (!arg->AutoOpenDataset())
3365 193 : continue;
3366 :
3367 4 : int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3368 :
3369 8 : CPLStringList aosOpenOptions;
3370 8 : CPLStringList aosAllowedDrivers;
3371 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3372 : {
3373 4 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3374 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3375 : {
3376 4 : aosOpenOptions = CPLStringList(
3377 4 : ooArg->Get<std::vector<std::string>>());
3378 : }
3379 :
3380 4 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3381 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3382 : {
3383 4 : aosAllowedDrivers = CPLStringList(
3384 4 : ifArg->Get<std::vector<std::string>>());
3385 : }
3386 :
3387 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3388 4 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
3389 0 : updateArg->Get<bool>())
3390 : {
3391 0 : flags |= GDAL_OF_UPDATE;
3392 : }
3393 : }
3394 :
3395 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
3396 4 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
3397 12 : aosOpenOptions.List()));
3398 4 : if (poDS)
3399 : {
3400 3 : val.Set(std::move(poDS));
3401 : }
3402 : else
3403 : {
3404 1 : ret = false;
3405 : }
3406 : }
3407 : }
3408 : }
3409 :
3410 135486 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3411 : {
3412 8 : ret = false;
3413 : }
3414 : }
3415 :
3416 : // Check mutual dependency groups
3417 7679 : std::vector<std::string> processedGroups;
3418 : // Loop through group map and check there are not required args in the group that are not set
3419 7722 : for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
3420 : {
3421 43 : if (std::find(processedGroups.begin(), processedGroups.end(),
3422 43 : groupName) != processedGroups.end())
3423 0 : continue;
3424 86 : std::vector<std::string> missingArgs;
3425 848 : for (auto &arg : m_args)
3426 : {
3427 805 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
3428 898 : if (mutualDependencyGroup == groupName &&
3429 93 : std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
3430 898 : argNames.end())
3431 : {
3432 15 : missingArgs.push_back(arg->GetName());
3433 : }
3434 : }
3435 43 : if (!missingArgs.empty())
3436 : {
3437 12 : ret = false;
3438 24 : std::string missingArgsStr;
3439 27 : for (const auto &missingArg : missingArgs)
3440 : {
3441 15 : if (!missingArgsStr.empty())
3442 3 : missingArgsStr += ", ";
3443 15 : missingArgsStr += missingArg;
3444 : }
3445 24 : std::string givenArgsStr;
3446 27 : for (const auto &givenArg : argNames)
3447 : {
3448 15 : if (!givenArgsStr.empty())
3449 3 : givenArgsStr += ", ";
3450 15 : givenArgsStr += givenArg;
3451 : }
3452 12 : ReportError(CE_Failure, CPLE_AppDefined,
3453 : "Argument(s) '%s' require(s) that the following "
3454 : "argument(s) are also specified: %s.",
3455 : givenArgsStr.c_str(), missingArgsStr.c_str());
3456 : }
3457 43 : processedGroups.push_back(groupName);
3458 : }
3459 :
3460 32578 : for (const auto &f : m_validationActions)
3461 : {
3462 24899 : if (!f())
3463 81 : ret = false;
3464 : }
3465 :
3466 7679 : return ret;
3467 : }
3468 :
3469 : /************************************************************************/
3470 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3471 : /************************************************************************/
3472 :
3473 : std::unique_ptr<GDALAlgorithm>
3474 10874 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3475 : bool suggestionAllowed) const
3476 : {
3477 10874 : auto ret = m_subAlgRegistry.Instantiate(name);
3478 21748 : auto childCallPath = m_callPath;
3479 10874 : childCallPath.push_back(name);
3480 10874 : if (!ret)
3481 : {
3482 1214 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3483 1214 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3484 : }
3485 10874 : if (ret)
3486 : {
3487 10693 : ret->SetCallPath(childCallPath);
3488 : }
3489 181 : else if (suggestionAllowed)
3490 : {
3491 72 : std::string bestCandidate;
3492 36 : size_t bestDistance = std::numeric_limits<size_t>::max();
3493 525 : for (const std::string &candidate : GetSubAlgorithmNames())
3494 : {
3495 : const size_t distance =
3496 489 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3497 : /* transpositionAllowed = */ true);
3498 489 : if (distance < bestDistance)
3499 : {
3500 83 : bestCandidate = candidate;
3501 83 : bestDistance = distance;
3502 : }
3503 406 : else if (distance == bestDistance)
3504 : {
3505 51 : bestCandidate.clear();
3506 : }
3507 : }
3508 36 : if (!bestCandidate.empty() && bestDistance <= 2)
3509 : {
3510 4 : CPLError(CE_Failure, CPLE_AppDefined,
3511 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3512 : name.c_str(), bestCandidate.c_str());
3513 : }
3514 : }
3515 21748 : return ret;
3516 : }
3517 :
3518 : /************************************************************************/
3519 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3520 : /************************************************************************/
3521 :
3522 : std::string
3523 39 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3524 : {
3525 39 : if (osName.size() >= 3)
3526 : {
3527 34 : std::string bestCandidate;
3528 34 : size_t bestDistance = std::numeric_limits<size_t>::max();
3529 776 : for (const auto &[key, value] : m_mapLongNameToArg)
3530 : {
3531 742 : CPL_IGNORE_RET_VAL(value);
3532 742 : const size_t distance = CPLLevenshteinDistance(
3533 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3534 742 : if (distance < bestDistance)
3535 : {
3536 89 : bestCandidate = key;
3537 89 : bestDistance = distance;
3538 : }
3539 653 : else if (distance == bestDistance)
3540 : {
3541 78 : bestCandidate.clear();
3542 : }
3543 : }
3544 48 : if (!bestCandidate.empty() &&
3545 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3546 : {
3547 5 : return bestCandidate;
3548 : }
3549 : }
3550 34 : return std::string();
3551 : }
3552 :
3553 : /************************************************************************/
3554 : /* GDALAlgorithm::GetSuggestionsForArgumentName() */
3555 : /************************************************************************/
3556 :
3557 : std::vector<std::string>
3558 39 : GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
3559 : {
3560 39 : std::vector<std::string> ret;
3561 78 : std::string suggestion = GetSuggestionForArgumentName(osName);
3562 39 : if (!suggestion.empty())
3563 : {
3564 5 : ret.push_back(std::move(suggestion));
3565 : }
3566 34 : else if (osName.size() >= 3)
3567 : {
3568 : // e.g "crs" for reproject will match "input-crs" and "target-crs"
3569 87 : const std::string dashName = std::string("-").append(osName);
3570 554 : for (const auto &arg : m_args)
3571 : {
3572 525 : if (cpl::ends_with(arg->GetName(), dashName))
3573 : {
3574 3 : ret.push_back(arg->GetName());
3575 : }
3576 : }
3577 : }
3578 78 : return ret;
3579 : }
3580 :
3581 : /************************************************************************/
3582 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3583 : /************************************************************************/
3584 :
3585 : /* static */
3586 23 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3587 : {
3588 69 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3589 69 : osName == GDAL_ARG_NAME_OVERWRITE ||
3590 46 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3591 : }
3592 :
3593 : /************************************************************************/
3594 : /* GDALAlgorithm::HasOutputString() */
3595 : /************************************************************************/
3596 :
3597 74 : bool GDALAlgorithm::HasOutputString() const
3598 : {
3599 74 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3600 74 : return outputStringArg && outputStringArg->IsOutput();
3601 : }
3602 :
3603 : /************************************************************************/
3604 : /* GDALAlgorithm::GetArg() */
3605 : /************************************************************************/
3606 :
3607 497590 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3608 : bool suggestionAllowed, bool isConst)
3609 : {
3610 497590 : const auto nPos = osName.find_first_not_of('-');
3611 497590 : if (nPos == std::string::npos)
3612 27 : return nullptr;
3613 995126 : std::string osKey = osName.substr(nPos);
3614 : {
3615 497563 : const auto oIter = m_mapLongNameToArg.find(osKey);
3616 497563 : if (oIter != m_mapLongNameToArg.end())
3617 461167 : return oIter->second;
3618 : }
3619 : {
3620 36396 : const auto oIter = m_mapShortNameToArg.find(osKey);
3621 36396 : if (oIter != m_mapShortNameToArg.end())
3622 8 : return oIter->second;
3623 : }
3624 :
3625 36388 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3626 : {
3627 23 : const auto nDotPos = osKey.find('.');
3628 : const std::string osKeyEnd =
3629 23 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3630 23 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3631 : {
3632 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3633 0 : std::make_unique<bool>());
3634 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3635 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3636 0 : .SetUserProvided();
3637 : }
3638 : else
3639 : {
3640 46 : const std::string osKeyInit = osKey;
3641 23 : if (osKey == "oo")
3642 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3643 23 : else if (osKey == "co")
3644 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3645 23 : else if (osKey == "of")
3646 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3647 23 : else if (osKey == "if")
3648 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3649 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3650 23 : std::make_unique<std::string>());
3651 : auto &arg =
3652 46 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3653 46 : m_arbitraryLongNameArgsValuesStr.back().get())
3654 23 : .SetUserProvided();
3655 23 : if (osKey != osKeyInit)
3656 0 : arg.AddAlias(osKeyInit);
3657 : }
3658 23 : const auto oIter = m_mapLongNameToArg.find(osKey);
3659 23 : CPLAssert(oIter != m_mapLongNameToArg.end());
3660 23 : return oIter->second;
3661 : }
3662 :
3663 36365 : if (suggestionAllowed)
3664 : {
3665 14 : const auto suggestions = GetSuggestionsForArgumentName(osName);
3666 7 : if (!suggestions.empty())
3667 : {
3668 2 : CPLError(CE_Failure, CPLE_AppDefined,
3669 : "Argument '%s' is unknown. Do you mean %s?",
3670 : osName.c_str(),
3671 4 : FormatSuggestionsAsString(suggestions,
3672 : /* addDashDashPrefix = */ false)
3673 : .c_str());
3674 : }
3675 : }
3676 :
3677 36365 : return nullptr;
3678 : }
3679 :
3680 : /************************************************************************/
3681 : /* GDALAlgorithm::AddAliasFor() */
3682 : /************************************************************************/
3683 :
3684 : //! @cond Doxygen_Suppress
3685 86119 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3686 : const std::string &alias)
3687 : {
3688 86119 : if (cpl::contains(m_mapLongNameToArg, alias))
3689 : {
3690 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3691 : alias.c_str());
3692 : }
3693 : else
3694 : {
3695 86118 : m_mapLongNameToArg[alias] = arg;
3696 : }
3697 86119 : }
3698 :
3699 : //! @endcond
3700 :
3701 : /************************************************************************/
3702 : /* GDALAlgorithm::AddShortNameAliasFor() */
3703 : /************************************************************************/
3704 :
3705 : //! @cond Doxygen_Suppress
3706 50 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3707 : char shortNameAlias)
3708 : {
3709 100 : std::string alias;
3710 50 : alias += shortNameAlias;
3711 50 : if (cpl::contains(m_mapShortNameToArg, alias))
3712 : {
3713 0 : ReportError(CE_Failure, CPLE_AppDefined,
3714 : "Short name '%s' already declared.", alias.c_str());
3715 : }
3716 : else
3717 : {
3718 50 : m_mapShortNameToArg[alias] = arg;
3719 : }
3720 50 : }
3721 :
3722 : //! @endcond
3723 :
3724 : /************************************************************************/
3725 : /* GDALAlgorithm::SetPositional() */
3726 : /************************************************************************/
3727 :
3728 : //! @cond Doxygen_Suppress
3729 23565 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3730 : {
3731 23565 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3732 : arg) == m_positionalArgs.end());
3733 23565 : m_positionalArgs.push_back(arg);
3734 23565 : }
3735 :
3736 : //! @endcond
3737 :
3738 : /************************************************************************/
3739 : /* GDALAlgorithm::HasSubAlgorithms() */
3740 : /************************************************************************/
3741 :
3742 14053 : bool GDALAlgorithm::HasSubAlgorithms() const
3743 : {
3744 14053 : if (!m_subAlgRegistry.empty())
3745 3639 : return true;
3746 10414 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3747 20828 : .GetDeclaredSubAlgorithmNames(m_callPath)
3748 10414 : .empty();
3749 : }
3750 :
3751 : /************************************************************************/
3752 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3753 : /************************************************************************/
3754 :
3755 1599 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3756 : {
3757 1599 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3758 1599 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3759 3198 : .GetDeclaredSubAlgorithmNames(m_callPath);
3760 1599 : ret.insert(ret.end(), other.begin(), other.end());
3761 1599 : if (!other.empty())
3762 521 : std::sort(ret.begin(), ret.end());
3763 3198 : return ret;
3764 : }
3765 :
3766 : /************************************************************************/
3767 : /* GDALAlgorithm::AddArg() */
3768 : /************************************************************************/
3769 :
3770 : GDALInConstructionAlgorithmArg &
3771 342058 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3772 : {
3773 342058 : auto argRaw = arg.get();
3774 342058 : const auto &longName = argRaw->GetName();
3775 342058 : if (!longName.empty())
3776 : {
3777 342045 : if (longName[0] == '-')
3778 : {
3779 1 : ReportError(CE_Failure, CPLE_AppDefined,
3780 : "Long name '%s' should not start with '-'",
3781 : longName.c_str());
3782 : }
3783 342045 : if (longName.find('=') != std::string::npos)
3784 : {
3785 1 : ReportError(CE_Failure, CPLE_AppDefined,
3786 : "Long name '%s' should not contain a '=' character",
3787 : longName.c_str());
3788 : }
3789 342045 : if (cpl::contains(m_mapLongNameToArg, longName))
3790 : {
3791 1 : ReportError(CE_Failure, CPLE_AppDefined,
3792 : "Long name '%s' already declared", longName.c_str());
3793 : }
3794 342045 : m_mapLongNameToArg[longName] = argRaw;
3795 : }
3796 342058 : const auto &shortName = argRaw->GetShortName();
3797 342058 : if (!shortName.empty())
3798 : {
3799 167980 : if (shortName.size() != 1 ||
3800 83990 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3801 66 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3802 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3803 : {
3804 1 : ReportError(CE_Failure, CPLE_AppDefined,
3805 : "Short name '%s' should be a single letter or digit",
3806 : shortName.c_str());
3807 : }
3808 83990 : if (cpl::contains(m_mapShortNameToArg, shortName))
3809 : {
3810 1 : ReportError(CE_Failure, CPLE_AppDefined,
3811 : "Short name '%s' already declared", shortName.c_str());
3812 : }
3813 83990 : m_mapShortNameToArg[shortName] = argRaw;
3814 : }
3815 342058 : m_args.emplace_back(std::move(arg));
3816 : return *(
3817 342058 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3818 : }
3819 :
3820 : GDALInConstructionAlgorithmArg &
3821 155573 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3822 : const std::string &helpMessage, bool *pValue)
3823 : {
3824 155573 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3825 : this,
3826 311146 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3827 311146 : pValue));
3828 : }
3829 :
3830 : GDALInConstructionAlgorithmArg &
3831 54460 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3832 : const std::string &helpMessage, std::string *pValue)
3833 : {
3834 54460 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3835 : this,
3836 108920 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3837 108920 : pValue));
3838 : }
3839 :
3840 : GDALInConstructionAlgorithmArg &
3841 12870 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3842 : const std::string &helpMessage, int *pValue)
3843 : {
3844 12870 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3845 : this,
3846 25740 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3847 25740 : pValue));
3848 : }
3849 :
3850 : GDALInConstructionAlgorithmArg &
3851 10406 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3852 : const std::string &helpMessage, double *pValue)
3853 : {
3854 10406 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3855 : this,
3856 20812 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3857 20812 : pValue));
3858 : }
3859 :
3860 : GDALInConstructionAlgorithmArg &
3861 12756 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3862 : const std::string &helpMessage,
3863 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3864 : {
3865 25512 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3866 : this,
3867 25512 : GDALAlgorithmArgDecl(longName, chShortName,
3868 : helpMessage, GAAT_DATASET),
3869 12756 : pValue))
3870 12756 : .SetDatasetType(type);
3871 12756 : pValue->SetOwnerArgument(&arg);
3872 12756 : return arg;
3873 : }
3874 :
3875 : GDALInConstructionAlgorithmArg &
3876 73113 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3877 : const std::string &helpMessage,
3878 : std::vector<std::string> *pValue)
3879 : {
3880 73113 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3881 : this,
3882 146226 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3883 : GAAT_STRING_LIST),
3884 146226 : pValue));
3885 : }
3886 :
3887 : GDALInConstructionAlgorithmArg &
3888 2139 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3889 : const std::string &helpMessage, std::vector<int> *pValue)
3890 : {
3891 2139 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3892 : this,
3893 4278 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3894 : GAAT_INTEGER_LIST),
3895 4278 : pValue));
3896 : }
3897 :
3898 : GDALInConstructionAlgorithmArg &
3899 5460 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3900 : const std::string &helpMessage,
3901 : std::vector<double> *pValue)
3902 : {
3903 5460 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3904 : this,
3905 10920 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3906 : GAAT_REAL_LIST),
3907 10920 : pValue));
3908 : }
3909 :
3910 : GDALInConstructionAlgorithmArg &
3911 15281 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3912 : const std::string &helpMessage,
3913 : std::vector<GDALArgDatasetValue> *pValue,
3914 : GDALArgDatasetType type)
3915 : {
3916 30562 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3917 : this,
3918 30562 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3919 : GAAT_DATASET_LIST),
3920 15281 : pValue))
3921 30562 : .SetDatasetType(type);
3922 : }
3923 :
3924 : /************************************************************************/
3925 : /* MsgOrDefault() */
3926 : /************************************************************************/
3927 :
3928 112653 : inline const char *MsgOrDefault(const char *helpMessage,
3929 : const char *defaultMessage)
3930 : {
3931 112653 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3932 : }
3933 :
3934 : /************************************************************************/
3935 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3936 : /************************************************************************/
3937 :
3938 : /* static */
3939 18707 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3940 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3941 : {
3942 : arg.SetAutoCompleteFunction(
3943 7 : [&arg,
3944 2483 : type](const std::string ¤tValue) -> std::vector<std::string>
3945 : {
3946 14 : std::vector<std::string> oRet;
3947 :
3948 7 : if (arg.IsHidden())
3949 0 : return oRet;
3950 :
3951 : {
3952 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3953 : VSIStatBufL sStat;
3954 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3955 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3956 : {
3957 0 : return oRet;
3958 : }
3959 : }
3960 :
3961 7 : auto poDM = GetGDALDriverManager();
3962 14 : std::set<std::string> oExtensions;
3963 7 : if (type)
3964 : {
3965 1386 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3966 : {
3967 1380 : auto poDriver = poDM->GetDriver(i);
3968 3910 : if (((type & GDAL_OF_RASTER) != 0 &&
3969 1150 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3970 590 : ((type & GDAL_OF_VECTOR) != 0 &&
3971 2899 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3972 499 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3973 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3974 : {
3975 : const char *pszExtensions =
3976 881 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3977 881 : if (pszExtensions)
3978 : {
3979 : const CPLStringList aosExts(
3980 1164 : CSLTokenizeString2(pszExtensions, " ", 0));
3981 1313 : for (const char *pszExt : cpl::Iterate(aosExts))
3982 731 : oExtensions.insert(CPLString(pszExt).tolower());
3983 : }
3984 : }
3985 : }
3986 : }
3987 :
3988 14 : std::string osDir;
3989 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3990 14 : std::string osPrefix;
3991 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3992 : {
3993 82 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3994 : {
3995 81 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3996 : {
3997 2 : osPrefix = pszPrefix;
3998 2 : break;
3999 : }
4000 : }
4001 3 : if (osPrefix.empty())
4002 1 : return aosVSIPrefixes;
4003 2 : if (currentValue == osPrefix)
4004 1 : osDir = osPrefix;
4005 : }
4006 6 : if (osDir.empty())
4007 : {
4008 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
4009 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
4010 0 : osDir = std::move(osPrefix);
4011 : }
4012 :
4013 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
4014 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
4015 6 : if (currentValue.empty())
4016 1 : osDir.clear();
4017 : const std::string currentFilename =
4018 12 : CPLGetFilename(currentValue.c_str());
4019 6 : if (psDir)
4020 : {
4021 456 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
4022 : {
4023 451 : if ((currentFilename.empty() ||
4024 225 : STARTS_WITH(psEntry->pszName,
4025 227 : currentFilename.c_str())) &&
4026 227 : strcmp(psEntry->pszName, ".") != 0 &&
4027 1355 : strcmp(psEntry->pszName, "..") != 0 &&
4028 227 : (oExtensions.empty() ||
4029 226 : !strstr(psEntry->pszName, ".aux.xml")))
4030 : {
4031 898 : if (oExtensions.empty() ||
4032 224 : cpl::contains(
4033 : oExtensions,
4034 449 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
4035 673 : .tolower()) ||
4036 192 : VSI_ISDIR(psEntry->nMode))
4037 : {
4038 74 : std::string osVal;
4039 37 : if (osDir.empty() || osDir == ".")
4040 4 : osVal = psEntry->pszName;
4041 : else
4042 66 : osVal = CPLFormFilenameSafe(
4043 66 : osDir.c_str(), psEntry->pszName, nullptr);
4044 37 : if (VSI_ISDIR(psEntry->nMode))
4045 4 : osVal += osSep;
4046 37 : oRet.push_back(std::move(osVal));
4047 : }
4048 : }
4049 451 : }
4050 5 : VSICloseDir(psDir);
4051 : }
4052 6 : return oRet;
4053 18707 : });
4054 18707 : }
4055 :
4056 : /************************************************************************/
4057 : /* GDALAlgorithm::AddInputDatasetArg() */
4058 : /************************************************************************/
4059 :
4060 909 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4061 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4062 : bool positionalAndRequired, const char *helpMessage)
4063 : {
4064 : auto &arg = AddArg(
4065 : GDAL_ARG_NAME_INPUT, 'i',
4066 : MsgOrDefault(helpMessage,
4067 : CPLSPrintf("Input %s dataset",
4068 909 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4069 1818 : pValue, type);
4070 909 : if (positionalAndRequired)
4071 902 : arg.SetPositional().SetRequired();
4072 :
4073 909 : SetAutoCompleteFunctionForFilename(arg, type);
4074 :
4075 909 : AddValidationAction(
4076 80 : [pValue]()
4077 : {
4078 79 : if (pValue->GetName() == "-")
4079 1 : pValue->Set("/vsistdin/");
4080 79 : return true;
4081 : });
4082 :
4083 909 : return arg;
4084 : }
4085 :
4086 : /************************************************************************/
4087 : /* GDALAlgorithm::AddInputDatasetArg() */
4088 : /************************************************************************/
4089 :
4090 14812 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
4091 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
4092 : bool positionalAndRequired, const char *helpMessage)
4093 : {
4094 : auto &arg =
4095 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
4096 : MsgOrDefault(
4097 : helpMessage,
4098 : CPLSPrintf("Input %s datasets",
4099 14812 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4100 44436 : pValue, type)
4101 14812 : .SetPackedValuesAllowed(false);
4102 14812 : if (positionalAndRequired)
4103 1693 : arg.SetPositional().SetRequired();
4104 :
4105 14812 : SetAutoCompleteFunctionForFilename(arg, type);
4106 :
4107 14812 : AddValidationAction(
4108 7187 : [pValue]()
4109 : {
4110 13496 : for (auto &val : *pValue)
4111 : {
4112 6309 : if (val.GetName() == "-")
4113 1 : val.Set("/vsistdin/");
4114 : }
4115 7187 : return true;
4116 : });
4117 14812 : return arg;
4118 : }
4119 :
4120 : /************************************************************************/
4121 : /* GDALAlgorithm::AddOutputDatasetArg() */
4122 : /************************************************************************/
4123 :
4124 8965 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
4125 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
4126 : bool positionalAndRequired, const char *helpMessage)
4127 : {
4128 : auto &arg =
4129 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
4130 : MsgOrDefault(
4131 : helpMessage,
4132 : CPLSPrintf("Output %s dataset",
4133 8965 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
4134 26895 : pValue, type)
4135 8965 : .SetIsInput(true)
4136 8965 : .SetIsOutput(true)
4137 8965 : .SetDatasetInputFlags(GADV_NAME)
4138 8965 : .SetDatasetOutputFlags(GADV_OBJECT);
4139 8965 : if (positionalAndRequired)
4140 4484 : arg.SetPositional().SetRequired();
4141 :
4142 8965 : AddValidationAction(
4143 13507 : [this, &arg, pValue]()
4144 : {
4145 4081 : if (pValue->GetName() == "-")
4146 4 : pValue->Set("/vsistdout/");
4147 :
4148 4081 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
4149 4029 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
4150 6364 : (!outputFormatArg->IsExplicitlySet() ||
4151 10445 : outputFormatArg->Get<std::string>().empty()) &&
4152 1694 : arg.IsExplicitlySet())
4153 : {
4154 : const auto vrtCompatible =
4155 1203 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4156 192 : if (vrtCompatible && !vrtCompatible->empty() &&
4157 1395 : vrtCompatible->front() == "false" &&
4158 1299 : EQUAL(
4159 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
4160 : "VRT"))
4161 : {
4162 6 : ReportError(
4163 : CE_Failure, CPLE_NotSupported,
4164 : "VRT output is not supported.%s",
4165 6 : outputFormatArg->GetDescription().find("GDALG") !=
4166 : std::string::npos
4167 : ? " Consider using the GDALG driver instead (files "
4168 : "with .gdalg.json extension)"
4169 : : "");
4170 6 : return false;
4171 : }
4172 1197 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
4173 2371 : EQUAL(pValue->GetName()
4174 : .substr(pValue->GetName().size() -
4175 : strlen(".gdalg.json"))
4176 : .c_str(),
4177 3568 : ".gdalg.json") &&
4178 28 : outputFormatArg->GetDescription().find("GDALG") ==
4179 : std::string::npos)
4180 : {
4181 0 : ReportError(CE_Failure, CPLE_NotSupported,
4182 : "GDALG output is not supported");
4183 0 : return false;
4184 : }
4185 : }
4186 4075 : return true;
4187 : });
4188 :
4189 8965 : return arg;
4190 : }
4191 :
4192 : /************************************************************************/
4193 : /* GDALAlgorithm::AddOverwriteArg() */
4194 : /************************************************************************/
4195 :
4196 : GDALInConstructionAlgorithmArg &
4197 8833 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
4198 : {
4199 : return AddArg(
4200 : GDAL_ARG_NAME_OVERWRITE, 0,
4201 : MsgOrDefault(
4202 : helpMessage,
4203 : _("Whether overwriting existing output dataset is allowed")),
4204 17666 : pValue)
4205 17666 : .SetDefault(false);
4206 : }
4207 :
4208 : /************************************************************************/
4209 : /* GDALAlgorithm::AddOverwriteLayerArg() */
4210 : /************************************************************************/
4211 :
4212 : GDALInConstructionAlgorithmArg &
4213 3669 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
4214 : {
4215 3669 : AddValidationAction(
4216 1707 : [this]
4217 : {
4218 1706 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4219 1706 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4220 : {
4221 1 : ReportError(CE_Failure, CPLE_AppDefined,
4222 : "--update argument must exist for "
4223 : "--overwrite-layer, even if hidden");
4224 1 : return false;
4225 : }
4226 1705 : return true;
4227 : });
4228 : return AddArg(
4229 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4230 : MsgOrDefault(
4231 : helpMessage,
4232 : _("Whether overwriting existing output layer is allowed")),
4233 7338 : pValue)
4234 3669 : .SetDefault(false)
4235 : .AddAction(
4236 19 : [this]
4237 : {
4238 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4239 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4240 : {
4241 19 : updateArg->Set(true);
4242 : }
4243 7357 : });
4244 : }
4245 :
4246 : /************************************************************************/
4247 : /* GDALAlgorithm::AddUpdateArg() */
4248 : /************************************************************************/
4249 :
4250 : GDALInConstructionAlgorithmArg &
4251 4238 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4252 : {
4253 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4254 : MsgOrDefault(
4255 : helpMessage,
4256 : _("Whether to open existing dataset in update mode")),
4257 8476 : pValue)
4258 8476 : .SetDefault(false);
4259 : }
4260 :
4261 : /************************************************************************/
4262 : /* GDALAlgorithm::AddAppendLayerArg() */
4263 : /************************************************************************/
4264 :
4265 : GDALInConstructionAlgorithmArg &
4266 3440 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4267 : {
4268 3440 : AddValidationAction(
4269 1662 : [this]
4270 : {
4271 1661 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4272 1661 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4273 : {
4274 1 : ReportError(CE_Failure, CPLE_AppDefined,
4275 : "--update argument must exist for --append, even "
4276 : "if hidden");
4277 1 : return false;
4278 : }
4279 1660 : return true;
4280 : });
4281 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4282 : MsgOrDefault(
4283 : helpMessage,
4284 : _("Whether appending to existing layer is allowed")),
4285 6880 : pValue)
4286 3440 : .SetDefault(false)
4287 : .AddAction(
4288 25 : [this]
4289 : {
4290 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4291 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4292 : {
4293 25 : updateArg->Set(true);
4294 : }
4295 6905 : });
4296 : }
4297 :
4298 : /************************************************************************/
4299 : /* GDALAlgorithm::AddOptionsSuggestions() */
4300 : /************************************************************************/
4301 :
4302 : /* static */
4303 30 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4304 : const std::string ¤tValue,
4305 : std::vector<std::string> &oRet)
4306 : {
4307 30 : if (!pszXML)
4308 0 : return false;
4309 60 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4310 30 : if (!poTree)
4311 0 : return false;
4312 :
4313 60 : std::string typedOptionName = currentValue;
4314 30 : const auto posEqual = typedOptionName.find('=');
4315 60 : std::string typedValue;
4316 30 : if (posEqual != 0 && posEqual != std::string::npos)
4317 : {
4318 2 : typedValue = currentValue.substr(posEqual + 1);
4319 2 : typedOptionName.resize(posEqual);
4320 : }
4321 :
4322 453 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4323 423 : psChild = psChild->psNext)
4324 : {
4325 436 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4326 449 : if (pszName && typedOptionName == pszName &&
4327 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4328 2 : strcmp(psChild->pszValue, "Argument") == 0))
4329 : {
4330 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4331 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4332 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4333 13 : if (EQUAL(pszType, "string-select"))
4334 : {
4335 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4336 85 : psChild2 = psChild2->psNext)
4337 : {
4338 85 : if (EQUAL(psChild2->pszValue, "Value"))
4339 : {
4340 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4341 : }
4342 : }
4343 : }
4344 8 : else if (EQUAL(pszType, "boolean"))
4345 : {
4346 3 : if (typedValue == "YES" || typedValue == "NO")
4347 : {
4348 1 : oRet.push_back(currentValue);
4349 1 : return true;
4350 : }
4351 2 : oRet.push_back("NO");
4352 2 : oRet.push_back("YES");
4353 : }
4354 5 : else if (EQUAL(pszType, "int"))
4355 : {
4356 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4357 2 : atoi(pszMax) - atoi(pszMin) < 25)
4358 : {
4359 1 : const int nMax = atoi(pszMax);
4360 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4361 12 : oRet.push_back(std::to_string(i));
4362 : }
4363 : }
4364 :
4365 12 : if (oRet.empty())
4366 : {
4367 4 : if (pszMin && pszMax)
4368 : {
4369 1 : oRet.push_back(std::string("##"));
4370 2 : oRet.push_back(std::string("validity range: [")
4371 1 : .append(pszMin)
4372 1 : .append(",")
4373 1 : .append(pszMax)
4374 1 : .append("]"));
4375 : }
4376 3 : else if (pszMin)
4377 : {
4378 1 : oRet.push_back(std::string("##"));
4379 1 : oRet.push_back(
4380 1 : std::string("validity range: >= ").append(pszMin));
4381 : }
4382 2 : else if (pszMax)
4383 : {
4384 1 : oRet.push_back(std::string("##"));
4385 1 : oRet.push_back(
4386 1 : std::string("validity range: <= ").append(pszMax));
4387 : }
4388 1 : else if (const char *pszDescription =
4389 1 : CPLGetXMLValue(psChild, "description", nullptr))
4390 : {
4391 1 : oRet.push_back(std::string("##"));
4392 2 : oRet.push_back(std::string("type: ")
4393 1 : .append(pszType)
4394 1 : .append(", description: ")
4395 1 : .append(pszDescription));
4396 : }
4397 : }
4398 :
4399 12 : return true;
4400 : }
4401 : }
4402 :
4403 367 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4404 350 : psChild = psChild->psNext)
4405 : {
4406 350 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4407 350 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4408 5 : strcmp(psChild->pszValue, "Argument") == 0))
4409 : {
4410 347 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4411 347 : if (!pszScope ||
4412 40 : (EQUAL(pszScope, "raster") &&
4413 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4414 20 : (EQUAL(pszScope, "vector") &&
4415 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4416 : {
4417 327 : oRet.push_back(std::string(pszName).append("="));
4418 : }
4419 : }
4420 : }
4421 :
4422 17 : return false;
4423 : }
4424 :
4425 : /************************************************************************/
4426 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4427 : /************************************************************************/
4428 :
4429 : //! @cond Doxygen_Suppress
4430 : std::vector<std::string>
4431 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4432 : {
4433 2 : std::vector<std::string> oRet;
4434 :
4435 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4436 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4437 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4438 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4439 : {
4440 2 : datasetType = inputArg->GetDatasetType();
4441 : }
4442 :
4443 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4444 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4445 2 : inputFormat->IsExplicitlySet())
4446 : {
4447 : const auto &aosAllowedDrivers =
4448 1 : inputFormat->Get<std::vector<std::string>>();
4449 1 : if (aosAllowedDrivers.size() == 1)
4450 : {
4451 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4452 1 : aosAllowedDrivers[0].c_str());
4453 1 : if (poDriver)
4454 : {
4455 1 : AddOptionsSuggestions(
4456 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4457 : datasetType, currentValue, oRet);
4458 : }
4459 1 : return oRet;
4460 : }
4461 : }
4462 :
4463 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4464 375 : &oRet](const GDALArgDatasetValue &datasetValue)
4465 : {
4466 1 : auto poDM = GetGDALDriverManager();
4467 :
4468 1 : const auto &osDSName = datasetValue.GetName();
4469 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4470 1 : if (!osExt.empty())
4471 : {
4472 1 : std::set<std::string> oVisitedExtensions;
4473 231 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4474 : {
4475 230 : auto poDriver = poDM->GetDriver(i);
4476 690 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4477 230 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4478 72 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4479 460 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4480 72 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4481 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4482 : {
4483 : const char *pszExtensions =
4484 158 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4485 158 : if (pszExtensions)
4486 : {
4487 : const CPLStringList aosExts(
4488 104 : CSLTokenizeString2(pszExtensions, " ", 0));
4489 229 : for (const char *pszExt : cpl::Iterate(aosExts))
4490 : {
4491 129 : if (EQUAL(pszExt, osExt.c_str()) &&
4492 3 : !cpl::contains(oVisitedExtensions, pszExt))
4493 : {
4494 1 : oVisitedExtensions.insert(pszExt);
4495 1 : if (AddOptionsSuggestions(
4496 : poDriver->GetMetadataItem(
4497 1 : GDAL_DMD_OPENOPTIONLIST),
4498 : datasetType, currentValue, oRet))
4499 : {
4500 0 : return;
4501 : }
4502 1 : break;
4503 : }
4504 : }
4505 : }
4506 : }
4507 : }
4508 : }
4509 1 : };
4510 :
4511 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4512 : {
4513 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4514 0 : AddSuggestions(datasetValue);
4515 : }
4516 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4517 : {
4518 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4519 1 : if (datasetValues.size() == 1)
4520 1 : AddSuggestions(datasetValues[0]);
4521 : }
4522 :
4523 1 : return oRet;
4524 : }
4525 :
4526 : //! @endcond
4527 :
4528 : /************************************************************************/
4529 : /* GDALAlgorithm::AddOpenOptionsArg() */
4530 : /************************************************************************/
4531 :
4532 : GDALInConstructionAlgorithmArg &
4533 9851 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4534 : const char *helpMessage)
4535 : {
4536 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4537 19702 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4538 19702 : .AddAlias("oo")
4539 19702 : .SetMetaVar("<KEY>=<VALUE>")
4540 9851 : .SetPackedValuesAllowed(false)
4541 9851 : .SetCategory(GAAC_ADVANCED);
4542 :
4543 31 : arg.AddValidationAction([this, &arg]()
4544 9882 : { return ParseAndValidateKeyValue(arg); });
4545 :
4546 : arg.SetAutoCompleteFunction(
4547 2 : [this](const std::string ¤tValue)
4548 9853 : { return OpenOptionCompleteFunction(currentValue); });
4549 :
4550 9851 : return arg;
4551 : }
4552 :
4553 : /************************************************************************/
4554 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4555 : /************************************************************************/
4556 :
4557 : GDALInConstructionAlgorithmArg &
4558 3517 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4559 : const char *helpMessage)
4560 : {
4561 : auto &arg =
4562 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4563 7034 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4564 7034 : .AddAlias("output-oo")
4565 7034 : .SetMetaVar("<KEY>=<VALUE>")
4566 3517 : .SetPackedValuesAllowed(false)
4567 3517 : .SetCategory(GAAC_ADVANCED);
4568 :
4569 0 : arg.AddValidationAction([this, &arg]()
4570 3517 : { return ParseAndValidateKeyValue(arg); });
4571 :
4572 : arg.SetAutoCompleteFunction(
4573 0 : [this](const std::string ¤tValue)
4574 3517 : { return OpenOptionCompleteFunction(currentValue); });
4575 :
4576 3517 : return arg;
4577 : }
4578 :
4579 : /************************************************************************/
4580 : /* ValidateFormat() */
4581 : /************************************************************************/
4582 :
4583 4973 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4584 : bool bStreamAllowed,
4585 : bool bGDALGAllowed) const
4586 : {
4587 4973 : if (arg.GetChoices().empty())
4588 : {
4589 : const auto Validate =
4590 21455 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4591 : {
4592 4852 : if (const auto extraFormats =
4593 4852 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4594 : {
4595 60 : for (const auto &extraFormat : *extraFormats)
4596 : {
4597 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4598 14 : return true;
4599 : }
4600 : }
4601 :
4602 4838 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4603 1889 : return true;
4604 :
4605 2957 : if (EQUAL(val.c_str(), "GDALG") &&
4606 8 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4607 : {
4608 4 : if (bGDALGAllowed)
4609 : {
4610 4 : return true;
4611 : }
4612 : else
4613 : {
4614 0 : ReportError(CE_Failure, CPLE_NotSupported,
4615 : "GDALG output is not supported.");
4616 0 : return false;
4617 : }
4618 : }
4619 :
4620 : const auto vrtCompatible =
4621 2945 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4622 540 : if (vrtCompatible && !vrtCompatible->empty() &&
4623 3485 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4624 : {
4625 7 : ReportError(CE_Failure, CPLE_NotSupported,
4626 : "VRT output is not supported.%s",
4627 : bGDALGAllowed
4628 : ? " Consider using the GDALG driver instead "
4629 : "(files with .gdalg.json extension)."
4630 : : "");
4631 7 : return false;
4632 : }
4633 :
4634 : const auto allowedFormats =
4635 2938 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4636 2991 : if (allowedFormats && !allowedFormats->empty() &&
4637 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4638 2991 : val) != allowedFormats->end())
4639 : {
4640 12 : return true;
4641 : }
4642 :
4643 : const auto excludedFormats =
4644 2926 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4645 2973 : if (excludedFormats && !excludedFormats->empty() &&
4646 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4647 2973 : val) != excludedFormats->end())
4648 : {
4649 0 : ReportError(CE_Failure, CPLE_NotSupported,
4650 : "%s output is not supported.", val.c_str());
4651 0 : return false;
4652 : }
4653 :
4654 2926 : auto hDriver = GDALGetDriverByName(val.c_str());
4655 2926 : if (!hDriver)
4656 : {
4657 : auto poMissingDriver =
4658 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4659 4 : if (poMissingDriver)
4660 : {
4661 : const std::string msg =
4662 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4663 0 : ReportError(CE_Failure, CPLE_AppDefined,
4664 : "Invalid value for argument '%s'. Driver '%s' "
4665 : "not found but is known. However plugin %s",
4666 0 : arg.GetName().c_str(), val.c_str(),
4667 : msg.c_str());
4668 : }
4669 : else
4670 : {
4671 8 : ReportError(CE_Failure, CPLE_AppDefined,
4672 : "Invalid value for argument '%s'. Driver '%s' "
4673 : "does not exist.",
4674 4 : arg.GetName().c_str(), val.c_str());
4675 : }
4676 4 : return false;
4677 : }
4678 :
4679 2922 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4680 2922 : if (caps)
4681 : {
4682 8858 : for (const std::string &cap : *caps)
4683 : {
4684 : const char *pszVal =
4685 5967 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4686 5967 : if (!(pszVal && pszVal[0]))
4687 : {
4688 1651 : if (cap == GDAL_DCAP_CREATECOPY &&
4689 0 : std::find(caps->begin(), caps->end(),
4690 824 : GDAL_DCAP_RASTER) != caps->end() &&
4691 824 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4692 1651 : nullptr) &&
4693 824 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4694 : nullptr))
4695 : {
4696 : // if it supports Create, it supports CreateCopy
4697 : }
4698 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4699 : {
4700 2 : ReportError(
4701 : CE_Failure, CPLE_AppDefined,
4702 : "Invalid value for argument '%s'. Driver '%s' "
4703 : "does "
4704 : "not advertise any file format extension.",
4705 1 : arg.GetName().c_str(), val.c_str());
4706 3 : return false;
4707 : }
4708 : else
4709 : {
4710 2 : if (cap == GDAL_DCAP_CREATE)
4711 : {
4712 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4713 1 : if (updateArg &&
4714 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4715 1 : updateArg->IsExplicitlySet())
4716 : {
4717 0 : continue;
4718 : }
4719 :
4720 2 : ReportError(
4721 : CE_Failure, CPLE_AppDefined,
4722 : "Invalid value for argument '%s'. "
4723 : "Driver '%s' does not have write support.",
4724 1 : arg.GetName().c_str(), val.c_str());
4725 1 : return false;
4726 : }
4727 : else
4728 : {
4729 2 : ReportError(
4730 : CE_Failure, CPLE_AppDefined,
4731 : "Invalid value for argument '%s'. Driver "
4732 : "'%s' "
4733 : "does "
4734 : "not expose the required '%s' capability.",
4735 1 : arg.GetName().c_str(), val.c_str(),
4736 : cap.c_str());
4737 1 : return false;
4738 : }
4739 : }
4740 : }
4741 : }
4742 : }
4743 2919 : return true;
4744 4855 : };
4745 :
4746 4855 : if (arg.GetType() == GAAT_STRING)
4747 : {
4748 4842 : return Validate(arg.Get<std::string>());
4749 : }
4750 15 : else if (arg.GetType() == GAAT_STRING_LIST)
4751 : {
4752 25 : for (const auto &val : arg.Get<std::vector<std::string>>())
4753 : {
4754 12 : if (!Validate(val))
4755 2 : return false;
4756 : }
4757 : }
4758 : }
4759 :
4760 131 : return true;
4761 : }
4762 :
4763 : /************************************************************************/
4764 : /* FormatAutoCompleteFunction() */
4765 : /************************************************************************/
4766 :
4767 : /* static */
4768 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4769 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4770 : {
4771 7 : std::vector<std::string> res;
4772 7 : auto poDM = GetGDALDriverManager();
4773 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4774 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4775 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4776 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4777 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4778 0 : res = std::move(*extraFormats);
4779 1616 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4780 : {
4781 1609 : auto poDriver = poDM->GetDriver(i);
4782 :
4783 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4784 1609 : vrtCompatible->front() == "false" &&
4785 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4786 : {
4787 : // do nothing
4788 : }
4789 1609 : else if (allowedFormats && !allowedFormats->empty() &&
4790 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4791 1609 : poDriver->GetDescription()) != allowedFormats->end())
4792 : {
4793 0 : res.push_back(poDriver->GetDescription());
4794 : }
4795 1609 : else if (excludedFormats && !excludedFormats->empty() &&
4796 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4797 0 : poDriver->GetDescription()) !=
4798 1609 : excludedFormats->end())
4799 : {
4800 0 : continue;
4801 : }
4802 1609 : else if (caps)
4803 : {
4804 1609 : bool ok = true;
4805 3183 : for (const std::string &cap : *caps)
4806 : {
4807 2398 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4808 : {
4809 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4810 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4811 : {
4812 0 : ok = false;
4813 0 : break;
4814 : }
4815 : }
4816 2398 : else if (const char *pszVal =
4817 2398 : poDriver->GetMetadataItem(cap.c_str());
4818 1502 : pszVal && pszVal[0])
4819 : {
4820 : }
4821 1292 : else if (cap == GDAL_DCAP_CREATECOPY &&
4822 0 : (std::find(caps->begin(), caps->end(),
4823 396 : GDAL_DCAP_RASTER) != caps->end() &&
4824 1688 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4825 396 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4826 : {
4827 : // if it supports Create, it supports CreateCopy
4828 : }
4829 : else
4830 : {
4831 824 : ok = false;
4832 824 : break;
4833 : }
4834 : }
4835 1609 : if (ok)
4836 : {
4837 785 : res.push_back(poDriver->GetDescription());
4838 : }
4839 : }
4840 : }
4841 7 : if (bGDALGAllowed)
4842 4 : res.push_back("GDALG");
4843 7 : return res;
4844 : }
4845 :
4846 : /************************************************************************/
4847 : /* GDALAlgorithm::AddInputFormatsArg() */
4848 : /************************************************************************/
4849 :
4850 : GDALInConstructionAlgorithmArg &
4851 9632 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4852 : const char *helpMessage)
4853 : {
4854 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4855 19264 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4856 19264 : .AddAlias("if")
4857 9632 : .SetCategory(GAAC_ADVANCED);
4858 15 : arg.AddValidationAction([this, &arg]()
4859 9647 : { return ValidateFormat(arg, false, false); });
4860 : arg.SetAutoCompleteFunction(
4861 1 : [&arg](const std::string &)
4862 9633 : { return FormatAutoCompleteFunction(arg, false, false); });
4863 9632 : return arg;
4864 : }
4865 :
4866 : /************************************************************************/
4867 : /* GDALAlgorithm::AddOutputFormatArg() */
4868 : /************************************************************************/
4869 :
4870 : GDALInConstructionAlgorithmArg &
4871 10019 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4872 : bool bGDALGAllowed, const char *helpMessage)
4873 : {
4874 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4875 : MsgOrDefault(helpMessage,
4876 : bGDALGAllowed
4877 : ? _("Output format (\"GDALG\" allowed)")
4878 : : _("Output format")),
4879 20038 : pValue)
4880 20038 : .AddAlias("of")
4881 10019 : .AddAlias("format");
4882 : arg.AddValidationAction(
4883 4954 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4884 14973 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4885 : arg.SetAutoCompleteFunction(
4886 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4887 : {
4888 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4889 4 : bGDALGAllowed);
4890 10019 : });
4891 10019 : return arg;
4892 : }
4893 :
4894 : /************************************************************************/
4895 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4896 : /************************************************************************/
4897 : GDALInConstructionAlgorithmArg &
4898 1835 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4899 : const char *helpMessage)
4900 : {
4901 : auto &arg =
4902 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4903 3670 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4904 3670 : .AddAlias("ot")
4905 3670 : .AddAlias("datatype")
4906 5505 : .AddMetadataItem("type", {"GDALDataType"})
4907 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4908 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4909 1835 : "Float32", "Float64", "CFloat32", "CFloat64")
4910 1835 : .SetHiddenChoices("Byte");
4911 1835 : return arg;
4912 : }
4913 :
4914 : /************************************************************************/
4915 : /* GDALAlgorithm::AddNodataArg() */
4916 : /************************************************************************/
4917 :
4918 : GDALInConstructionAlgorithmArg &
4919 707 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4920 : const std::string &optionName,
4921 : const char *helpMessage)
4922 : {
4923 : auto &arg = AddArg(
4924 : optionName, 0,
4925 : MsgOrDefault(helpMessage,
4926 : noneAllowed
4927 : ? _("Assign a specified nodata value to output bands "
4928 : "('none', numeric value, 'nan', 'inf', '-inf')")
4929 : : _("Assign a specified nodata value to output bands "
4930 : "(numeric value, 'nan', 'inf', '-inf')")),
4931 707 : pValue);
4932 : arg.AddValidationAction(
4933 496 : [this, pValue, noneAllowed, optionName]()
4934 : {
4935 105 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4936 : {
4937 95 : char *endptr = nullptr;
4938 95 : CPLStrtod(pValue->c_str(), &endptr);
4939 95 : if (endptr != pValue->c_str() + pValue->size())
4940 : {
4941 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4942 : "Value of '%s' should be %sa "
4943 : "numeric value, 'nan', 'inf' or '-inf'",
4944 : optionName.c_str(),
4945 : noneAllowed ? "'none', " : "");
4946 1 : return false;
4947 : }
4948 : }
4949 104 : return true;
4950 707 : });
4951 707 : return arg;
4952 : }
4953 :
4954 : /************************************************************************/
4955 : /* GDALAlgorithm::AddOutputStringArg() */
4956 : /************************************************************************/
4957 :
4958 : GDALInConstructionAlgorithmArg &
4959 6784 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4960 : {
4961 : return AddArg(
4962 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4963 : MsgOrDefault(helpMessage,
4964 : _("Output string, in which the result is placed")),
4965 13568 : pValue)
4966 6784 : .SetHiddenForCLI()
4967 6784 : .SetIsInput(false)
4968 13568 : .SetIsOutput(true);
4969 : }
4970 :
4971 : /************************************************************************/
4972 : /* GDALAlgorithm::AddStdoutArg() */
4973 : /************************************************************************/
4974 :
4975 : GDALInConstructionAlgorithmArg &
4976 1680 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4977 : {
4978 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4979 : MsgOrDefault(helpMessage,
4980 : _("Directly output on stdout. If enabled, "
4981 : "output-string will be empty")),
4982 3360 : pValue)
4983 3360 : .SetHidden();
4984 : }
4985 :
4986 : /************************************************************************/
4987 : /* GDALAlgorithm::AddLayerNameArg() */
4988 : /************************************************************************/
4989 :
4990 : GDALInConstructionAlgorithmArg &
4991 220 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4992 : {
4993 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4994 220 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4995 : }
4996 :
4997 : /************************************************************************/
4998 : /* GDALAlgorithm::AddArrayNameArg() */
4999 : /************************************************************************/
5000 :
5001 : GDALInConstructionAlgorithmArg &
5002 59 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
5003 : {
5004 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
5005 118 : pValue)
5006 2 : .SetAutoCompleteFunction([this](const std::string &)
5007 120 : { return AutoCompleteArrayName(); });
5008 : }
5009 :
5010 : /************************************************************************/
5011 : /* GDALAlgorithm::AddArrayNameArg() */
5012 : /************************************************************************/
5013 :
5014 : GDALInConstructionAlgorithmArg &
5015 136 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
5016 : const char *helpMessage)
5017 : {
5018 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
5019 272 : pValue)
5020 0 : .SetAutoCompleteFunction([this](const std::string &)
5021 272 : { return AutoCompleteArrayName(); });
5022 : }
5023 :
5024 : /************************************************************************/
5025 : /* GDALAlgorithm::AutoCompleteArrayName() */
5026 : /************************************************************************/
5027 :
5028 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
5029 : {
5030 2 : std::vector<std::string> ret;
5031 4 : std::string osDSName;
5032 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
5033 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
5034 : {
5035 2 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
5036 2 : if (!inputDatasets.empty())
5037 : {
5038 2 : osDSName = inputDatasets[0].GetName();
5039 : }
5040 : }
5041 0 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
5042 : {
5043 0 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
5044 0 : osDSName = inputDataset.GetName();
5045 : }
5046 :
5047 2 : if (!osDSName.empty())
5048 : {
5049 4 : CPLStringList aosAllowedDrivers;
5050 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
5051 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
5052 : aosAllowedDrivers =
5053 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
5054 :
5055 4 : CPLStringList aosOpenOptions;
5056 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
5057 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
5058 : aosOpenOptions =
5059 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
5060 :
5061 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5062 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
5063 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
5064 : {
5065 2 : if (auto poRG = poDS->GetRootGroup())
5066 : {
5067 1 : ret = poRG->GetMDArrayFullNamesRecursive();
5068 : }
5069 : }
5070 : }
5071 :
5072 4 : return ret;
5073 : }
5074 :
5075 : /************************************************************************/
5076 : /* GDALAlgorithm::AddMemorySizeArg() */
5077 : /************************************************************************/
5078 :
5079 : GDALInConstructionAlgorithmArg &
5080 227 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
5081 : const std::string &optionName,
5082 : const char *helpMessage)
5083 : {
5084 454 : return AddArg(optionName, 0, helpMessage, pStrValue)
5085 227 : .SetDefault(*pStrValue)
5086 : .AddValidationAction(
5087 139 : [this, pValue, pStrValue]()
5088 : {
5089 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
5090 : GIntBig nBytes;
5091 : bool bUnitSpecified;
5092 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
5093 47 : &bUnitSpecified) != CE_None)
5094 : {
5095 2 : return false;
5096 : }
5097 45 : if (!bUnitSpecified)
5098 : {
5099 1 : ReportError(CE_Failure, CPLE_AppDefined,
5100 : "Memory size must have a unit or be a "
5101 : "percentage of usable RAM (2GB, 5%%, etc.)");
5102 1 : return false;
5103 : }
5104 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
5105 : {
5106 : // -1 to please CoverityScan
5107 : if (static_cast<std::uint64_t>(nBytes) >
5108 : std::numeric_limits<size_t>::max() - 1U)
5109 : {
5110 : ReportError(CE_Failure, CPLE_AppDefined,
5111 : "Memory size %s is too large.",
5112 : pStrValue->c_str());
5113 : return false;
5114 : }
5115 : }
5116 :
5117 44 : *pValue = static_cast<size_t>(nBytes);
5118 44 : return true;
5119 454 : });
5120 : }
5121 :
5122 : /************************************************************************/
5123 : /* GDALAlgorithm::AddOutputLayerNameArg() */
5124 : /************************************************************************/
5125 :
5126 : GDALInConstructionAlgorithmArg &
5127 404 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
5128 : const char *helpMessage)
5129 : {
5130 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
5131 404 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
5132 : }
5133 :
5134 : /************************************************************************/
5135 : /* GDALAlgorithm::AddLayerNameArg() */
5136 : /************************************************************************/
5137 :
5138 : GDALInConstructionAlgorithmArg &
5139 920 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
5140 : const char *helpMessage)
5141 : {
5142 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
5143 920 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
5144 : }
5145 :
5146 : /************************************************************************/
5147 : /* GDALAlgorithm::AddGeometryTypeArg() */
5148 : /************************************************************************/
5149 :
5150 : GDALInConstructionAlgorithmArg &
5151 484 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
5152 : {
5153 : return AddArg("geometry-type", 0,
5154 968 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
5155 : .SetAutoCompleteFunction(
5156 3 : [](const std::string ¤tValue)
5157 : {
5158 3 : std::vector<std::string> oRet;
5159 51 : for (const char *type :
5160 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
5161 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
5162 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
5163 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
5164 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
5165 : {
5166 68 : if (currentValue.empty() ||
5167 17 : STARTS_WITH(type, currentValue.c_str()))
5168 : {
5169 35 : oRet.push_back(type);
5170 35 : oRet.push_back(std::string(type).append("Z"));
5171 35 : oRet.push_back(std::string(type).append("M"));
5172 35 : oRet.push_back(std::string(type).append("ZM"));
5173 : }
5174 : }
5175 3 : return oRet;
5176 968 : })
5177 : .AddValidationAction(
5178 121 : [this, pValue]()
5179 : {
5180 110 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
5181 118 : wkbUnknown &&
5182 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
5183 : {
5184 3 : ReportError(CE_Failure, CPLE_AppDefined,
5185 : "Invalid geometry type '%s'", pValue->c_str());
5186 3 : return false;
5187 : }
5188 107 : return true;
5189 968 : });
5190 : }
5191 :
5192 : /************************************************************************/
5193 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
5194 : /************************************************************************/
5195 :
5196 : /* static */
5197 3195 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
5198 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
5199 : {
5200 3195 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
5201 : datasetArg.GetType() == GAAT_DATASET_LIST);
5202 :
5203 : layerArg.SetAutoCompleteFunction(
5204 18 : [&datasetArg](const std::string ¤tValue)
5205 : {
5206 6 : std::vector<std::string> ret;
5207 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5208 6 : GDALArgDatasetValue *dsVal = nullptr;
5209 6 : if (datasetArg.GetType() == GAAT_DATASET)
5210 : {
5211 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
5212 : }
5213 : else
5214 : {
5215 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
5216 6 : if (val.size() == 1)
5217 : {
5218 6 : dsVal = &val[0];
5219 : }
5220 : }
5221 6 : if (dsVal && !dsVal->GetName().empty())
5222 : {
5223 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
5224 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
5225 6 : if (poDS)
5226 : {
5227 12 : for (auto &&poLayer : poDS->GetLayers())
5228 : {
5229 6 : if (currentValue == poLayer->GetDescription())
5230 : {
5231 1 : ret.clear();
5232 1 : ret.push_back(poLayer->GetDescription());
5233 1 : break;
5234 : }
5235 5 : ret.push_back(poLayer->GetDescription());
5236 : }
5237 : }
5238 : }
5239 12 : return ret;
5240 3195 : });
5241 3195 : }
5242 :
5243 : /************************************************************************/
5244 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5245 : /************************************************************************/
5246 :
5247 585 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5248 : GDALInConstructionAlgorithmArg &fieldArg,
5249 : const GDALAlgorithmArg *layerNameArg, bool attributeFields,
5250 : bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg,
5251 : const std::vector<std::string> &extraValues,
5252 : std::function<bool(const OGRFieldDefn *)> filterFn)
5253 : {
5254 :
5255 : fieldArg.SetAutoCompleteFunction(
5256 11 : [&datasetArg, layerNameArg, attributeFields, geometryFields,
5257 : extraValues,
5258 585 : filterFn = std::move(filterFn)](const std::string ¤tValue)
5259 : {
5260 22 : std::set<std::string> ret{};
5261 11 : if (!datasetArg.empty())
5262 : {
5263 18 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5264 :
5265 : const auto getLayerFields =
5266 7 : [&ret, ¤tValue, attributeFields, geometryFields,
5267 87 : &extraValues, &filterFn](const OGRLayer *poLayer)
5268 : {
5269 7 : const auto poDefn = poLayer->GetLayerDefn();
5270 7 : if (attributeFields)
5271 : {
5272 27 : for (const auto poFieldDefn : poDefn->GetFields())
5273 : {
5274 20 : if (filterFn && !filterFn(poFieldDefn))
5275 : {
5276 1 : continue;
5277 : }
5278 :
5279 19 : const char *fieldName = poFieldDefn->GetNameRef();
5280 :
5281 19 : if (currentValue == fieldName)
5282 : {
5283 0 : ret.clear();
5284 0 : ret.insert(fieldName);
5285 0 : break;
5286 : }
5287 19 : ret.insert(fieldName);
5288 : }
5289 : }
5290 7 : if (geometryFields)
5291 : {
5292 2 : for (const auto poFieldDefn : poDefn->GetGeomFields())
5293 : {
5294 1 : const char *fieldName = poFieldDefn->GetNameRef();
5295 1 : if (fieldName[0] == 0)
5296 1 : fieldName = OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME;
5297 1 : if (currentValue == fieldName)
5298 : {
5299 0 : ret.clear();
5300 0 : ret.insert(fieldName);
5301 0 : break;
5302 : }
5303 1 : ret.insert(fieldName);
5304 : }
5305 : }
5306 8 : for (const auto &value : extraValues)
5307 : {
5308 1 : if (currentValue == value)
5309 : {
5310 0 : ret.clear();
5311 0 : ret.insert(value);
5312 0 : break;
5313 : }
5314 1 : ret.insert(value);
5315 : }
5316 7 : };
5317 :
5318 9 : const GDALArgDatasetValue &dsVal = datasetArg[0];
5319 :
5320 9 : if (!dsVal.GetName().empty())
5321 : {
5322 : auto poDS = std::unique_ptr<GDALDataset>(
5323 9 : GDALDataset::Open(dsVal.GetName().c_str(),
5324 18 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5325 9 : if (poDS)
5326 : {
5327 18 : std::vector<std::string> layerNames;
5328 9 : if (layerNameArg && layerNameArg->IsExplicitlySet())
5329 : {
5330 4 : if (layerNameArg->GetType() == GAAT_STRING_LIST)
5331 : {
5332 : layerNames =
5333 : layerNameArg
5334 2 : ->Get<std::vector<std::string>>();
5335 : }
5336 2 : else if (layerNameArg->GetType() == GAAT_STRING)
5337 : {
5338 2 : layerNames.push_back(
5339 2 : layerNameArg->Get<std::string>());
5340 : }
5341 : }
5342 9 : if (layerNames.empty())
5343 : {
5344 : // Loop through all layers
5345 10 : for (const auto *poLayer : poDS->GetLayers())
5346 : {
5347 5 : getLayerFields(poLayer);
5348 : }
5349 : }
5350 : else
5351 : {
5352 8 : for (const std::string &layerName : layerNames)
5353 : {
5354 : const auto poLayer =
5355 4 : poDS->GetLayerByName(layerName.c_str());
5356 4 : if (poLayer)
5357 : {
5358 2 : getLayerFields(poLayer);
5359 : }
5360 : }
5361 : }
5362 : }
5363 : }
5364 : }
5365 11 : std::vector<std::string> retVector(ret.begin(), ret.end());
5366 22 : return retVector;
5367 1170 : });
5368 585 : }
5369 :
5370 : /************************************************************************/
5371 : /* GDALAlgorithm::AddFieldNameArg() */
5372 : /************************************************************************/
5373 :
5374 : GDALInConstructionAlgorithmArg &
5375 138 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5376 : {
5377 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5378 138 : pValue);
5379 : }
5380 :
5381 : /************************************************************************/
5382 : /* GDALAlgorithm::ParseFieldDefinition() */
5383 : /************************************************************************/
5384 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5385 : OGRFieldDefn *poFieldDefn,
5386 : std::string *posError)
5387 : {
5388 : static const std::regex re(
5389 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5390 134 : std::smatch match;
5391 67 : if (std::regex_match(posStrDef, match, re))
5392 : {
5393 132 : const std::string name = match[1];
5394 132 : const std::string type = match[2];
5395 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5396 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5397 66 : poFieldDefn->SetName(name.c_str());
5398 :
5399 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5400 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5401 : {
5402 1 : if (posError)
5403 1 : *posError = "Unsupported field type: " + type;
5404 :
5405 1 : return false;
5406 : }
5407 65 : poFieldDefn->SetType(typeEnum);
5408 65 : poFieldDefn->SetWidth(width);
5409 65 : poFieldDefn->SetPrecision(precision);
5410 65 : return true;
5411 : }
5412 :
5413 1 : if (posError)
5414 : *posError = "Invalid field definition format. Expected "
5415 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5416 :
5417 1 : return false;
5418 : }
5419 :
5420 : /************************************************************************/
5421 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5422 : /************************************************************************/
5423 :
5424 : GDALInConstructionAlgorithmArg &
5425 132 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5426 : std::vector<OGRFieldDefn> *pFieldDefns,
5427 : const char *helpMessage)
5428 : {
5429 : auto &arg =
5430 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5431 264 : pValues)
5432 264 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5433 132 : .SetPackedValuesAllowed(true)
5434 132 : .SetRepeatedArgAllowed(true);
5435 :
5436 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5437 : {
5438 65 : pFieldDefns->clear();
5439 130 : for (const auto &strValue : *pValues)
5440 : {
5441 67 : OGRFieldDefn fieldDefn("", OFTString);
5442 67 : std::string error;
5443 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5444 : &error))
5445 : {
5446 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5447 2 : return false;
5448 : }
5449 : // Check uniqueness of field names
5450 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5451 : {
5452 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5453 : fieldDefn.GetNameRef()))
5454 : {
5455 0 : ReportError(CE_Failure, CPLE_AppDefined,
5456 : "Duplicate field name: '%s'",
5457 : fieldDefn.GetNameRef());
5458 0 : return false;
5459 : }
5460 : }
5461 65 : pFieldDefns->push_back(fieldDefn);
5462 : }
5463 63 : return true;
5464 132 : };
5465 :
5466 132 : arg.AddValidationAction(std::move(validationFunction));
5467 :
5468 132 : return arg;
5469 : }
5470 :
5471 : /************************************************************************/
5472 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5473 : /************************************************************************/
5474 :
5475 276 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5476 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5477 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5478 : {
5479 : auto &arg =
5480 552 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5481 828 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5482 : .SetAutoCompleteFunction(
5483 1 : [](const std::string ¤tValue)
5484 : {
5485 1 : std::vector<std::string> oRet;
5486 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5487 : {
5488 : const char *pszSubType =
5489 5 : OGRFieldDefn::GetFieldSubTypeName(
5490 : static_cast<OGRFieldSubType>(i));
5491 5 : if (pszSubType != nullptr)
5492 : {
5493 5 : if (currentValue.empty() ||
5494 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5495 : {
5496 5 : oRet.push_back(pszSubType);
5497 : }
5498 : }
5499 : }
5500 :
5501 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5502 : {
5503 : // Skip deprecated
5504 14 : if (static_cast<OGRFieldType>(i) ==
5505 13 : OGRFieldType::OFTWideString ||
5506 : static_cast<OGRFieldType>(i) ==
5507 : OGRFieldType::OFTWideStringList)
5508 2 : continue;
5509 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5510 : static_cast<OGRFieldType>(i));
5511 12 : if (pszType != nullptr)
5512 : {
5513 12 : if (currentValue.empty() ||
5514 0 : STARTS_WITH(pszType, currentValue.c_str()))
5515 : {
5516 12 : oRet.push_back(pszType);
5517 : }
5518 : }
5519 : }
5520 1 : return oRet;
5521 276 : });
5522 :
5523 : auto validationFunction =
5524 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5525 : {
5526 120 : bool isValid{true};
5527 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5528 :
5529 : // String is returned for unknown types
5530 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5531 : {
5532 16 : isValid = false;
5533 : }
5534 :
5535 120 : *pSubtypeValue =
5536 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5537 :
5538 120 : if (*pSubtypeValue != OFSTNone)
5539 : {
5540 15 : isValid = true;
5541 15 : switch (*pSubtypeValue)
5542 : {
5543 6 : case OFSTBoolean:
5544 : case OFSTInt16:
5545 : {
5546 6 : *pTypeValue = OFTInteger;
5547 6 : break;
5548 : }
5549 3 : case OFSTFloat32:
5550 : {
5551 3 : *pTypeValue = OFTReal;
5552 3 : break;
5553 : }
5554 6 : default:
5555 : {
5556 6 : *pTypeValue = OFTString;
5557 6 : break;
5558 : }
5559 : }
5560 : }
5561 :
5562 120 : if (!isValid)
5563 : {
5564 2 : ReportError(CE_Failure, CPLE_AppDefined,
5565 : "Invalid value for argument '%s': '%s'",
5566 1 : arg.GetName().c_str(), pStrValue->c_str());
5567 : }
5568 :
5569 120 : return isValid;
5570 276 : };
5571 :
5572 276 : if (!pStrValue->empty())
5573 : {
5574 0 : arg.SetDefault(*pStrValue);
5575 0 : validationFunction();
5576 : }
5577 :
5578 276 : arg.AddValidationAction(std::move(validationFunction));
5579 :
5580 276 : return arg;
5581 : }
5582 :
5583 : /************************************************************************/
5584 : /* GDALAlgorithm::ValidateBandArg() */
5585 : /************************************************************************/
5586 :
5587 4569 : bool GDALAlgorithm::ValidateBandArg() const
5588 : {
5589 4569 : bool ret = true;
5590 4569 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5591 4569 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5592 1689 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5593 292 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5594 6252 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5595 149 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5596 : {
5597 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5598 : {
5599 99 : if (nBand > poDS->GetRasterCount())
5600 : {
5601 5 : ReportError(CE_Failure, CPLE_AppDefined,
5602 : "Value of 'band' should be greater or equal than "
5603 : "1 and less or equal than %d.",
5604 : poDS->GetRasterCount());
5605 5 : return false;
5606 : }
5607 94 : return true;
5608 92 : };
5609 :
5610 : const auto ValidateForOneDataset =
5611 304 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5612 : {
5613 87 : bool l_ret = true;
5614 87 : if (bandArg->GetType() == GAAT_INTEGER)
5615 : {
5616 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5617 : }
5618 63 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5619 : {
5620 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5621 : {
5622 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5623 : }
5624 : }
5625 87 : return l_ret;
5626 92 : };
5627 :
5628 92 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5629 : {
5630 : auto poDS =
5631 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5632 6 : if (poDS && !ValidateForOneDataset(poDS))
5633 2 : ret = false;
5634 : }
5635 : else
5636 : {
5637 86 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5638 85 : for (auto &datasetValue :
5639 256 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5640 : {
5641 85 : auto poDS = datasetValue.GetDatasetRef();
5642 85 : if (poDS && !ValidateForOneDataset(poDS))
5643 3 : ret = false;
5644 : }
5645 : }
5646 : }
5647 4569 : return ret;
5648 : }
5649 :
5650 : /************************************************************************/
5651 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5652 : /************************************************************************/
5653 :
5654 3619 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5655 : {
5656 3619 : return ValidateBandArg();
5657 : }
5658 :
5659 : /************************************************************************/
5660 : /* GDALAlgorithm::AddBandArg() */
5661 : /************************************************************************/
5662 :
5663 : GDALInConstructionAlgorithmArg &
5664 1711 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5665 : {
5666 2173 : AddValidationAction([this]() { return ValidateBandArg(); });
5667 :
5668 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5669 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5670 3422 : pValue)
5671 : .AddValidationAction(
5672 34 : [pValue]()
5673 : {
5674 34 : if (*pValue <= 0)
5675 : {
5676 1 : CPLError(CE_Failure, CPLE_AppDefined,
5677 : "Value of 'band' should greater or equal to 1.");
5678 1 : return false;
5679 : }
5680 33 : return true;
5681 3422 : });
5682 : }
5683 :
5684 : /************************************************************************/
5685 : /* GDALAlgorithm::AddBandArg() */
5686 : /************************************************************************/
5687 :
5688 : GDALInConstructionAlgorithmArg &
5689 876 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5690 : {
5691 1364 : AddValidationAction([this]() { return ValidateBandArg(); });
5692 :
5693 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5694 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5695 1752 : pValue)
5696 : .AddValidationAction(
5697 126 : [pValue]()
5698 : {
5699 397 : for (int val : *pValue)
5700 : {
5701 272 : if (val <= 0)
5702 : {
5703 1 : CPLError(CE_Failure, CPLE_AppDefined,
5704 : "Value of 'band' should greater or equal "
5705 : "to 1.");
5706 1 : return false;
5707 : }
5708 : }
5709 125 : return true;
5710 1752 : });
5711 : }
5712 :
5713 : /************************************************************************/
5714 : /* ParseAndValidateKeyValue() */
5715 : /************************************************************************/
5716 :
5717 589 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5718 : {
5719 553 : const auto Validate = [this, &arg](const std::string &val)
5720 : {
5721 548 : if (val.find('=') == std::string::npos)
5722 : {
5723 5 : ReportError(
5724 : CE_Failure, CPLE_AppDefined,
5725 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5726 5 : arg.GetName().c_str());
5727 5 : return false;
5728 : }
5729 :
5730 543 : return true;
5731 589 : };
5732 :
5733 589 : if (arg.GetType() == GAAT_STRING)
5734 : {
5735 0 : return Validate(arg.Get<std::string>());
5736 : }
5737 589 : else if (arg.GetType() == GAAT_STRING_LIST)
5738 : {
5739 589 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5740 589 : if (vals.size() == 1)
5741 : {
5742 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5743 964 : std::vector<std::string> newVals;
5744 964 : std::string curToken;
5745 482 : bool canSplitOnComma = true;
5746 482 : char lastSep = 0;
5747 482 : bool inString = false;
5748 482 : bool equalFoundInLastToken = false;
5749 7268 : for (char c : vals[0])
5750 : {
5751 6790 : if (!inString && c == ',')
5752 : {
5753 10 : if (lastSep != '=' || !equalFoundInLastToken)
5754 : {
5755 2 : canSplitOnComma = false;
5756 2 : break;
5757 : }
5758 8 : lastSep = c;
5759 8 : newVals.push_back(curToken);
5760 8 : curToken.clear();
5761 8 : equalFoundInLastToken = false;
5762 : }
5763 6780 : else if (!inString && c == '=')
5764 : {
5765 481 : if (lastSep == '=')
5766 : {
5767 2 : canSplitOnComma = false;
5768 2 : break;
5769 : }
5770 479 : equalFoundInLastToken = true;
5771 479 : lastSep = c;
5772 479 : curToken += c;
5773 : }
5774 6299 : else if (c == '"')
5775 : {
5776 4 : inString = !inString;
5777 4 : curToken += c;
5778 : }
5779 : else
5780 : {
5781 6295 : curToken += c;
5782 : }
5783 : }
5784 482 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5785 : {
5786 469 : if (!curToken.empty())
5787 469 : newVals.emplace_back(std::move(curToken));
5788 469 : vals = std::move(newVals);
5789 : }
5790 : }
5791 :
5792 1132 : for (const auto &val : vals)
5793 : {
5794 548 : if (!Validate(val))
5795 5 : return false;
5796 : }
5797 : }
5798 :
5799 584 : return true;
5800 : }
5801 :
5802 : /************************************************************************/
5803 : /* IsGDALGOutput() */
5804 : /************************************************************************/
5805 :
5806 2486 : bool GDALAlgorithm::IsGDALGOutput() const
5807 : {
5808 2486 : bool isGDALGOutput = false;
5809 2486 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5810 2486 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5811 4322 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5812 1836 : outputArg->IsExplicitlySet())
5813 : {
5814 3595 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5815 1785 : outputFormatArg->IsExplicitlySet())
5816 : {
5817 : const auto &val =
5818 1095 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5819 1095 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5820 : }
5821 : else
5822 : {
5823 : const auto &filename =
5824 715 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5825 715 : isGDALGOutput =
5826 1401 : filename.GetName().size() > strlen(".gdalg.json") &&
5827 686 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5828 : strlen(".gdalg.json"),
5829 : ".gdalg.json");
5830 : }
5831 : }
5832 2486 : return isGDALGOutput;
5833 : }
5834 :
5835 : /************************************************************************/
5836 : /* ProcessGDALGOutput() */
5837 : /************************************************************************/
5838 :
5839 2623 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5840 : {
5841 2623 : if (!SupportsStreamedOutput())
5842 730 : return ProcessGDALGOutputRet::NOT_GDALG;
5843 :
5844 1893 : if (IsGDALGOutput())
5845 : {
5846 12 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5847 : const auto &filename =
5848 12 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5849 : VSIStatBufL sStat;
5850 12 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5851 : {
5852 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5853 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5854 : {
5855 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5856 : {
5857 0 : CPLError(CE_Failure, CPLE_AppDefined,
5858 : "File '%s' already exists. Specify the "
5859 : "--overwrite option to overwrite it.",
5860 : filename.c_str());
5861 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5862 : }
5863 : }
5864 : }
5865 :
5866 24 : std::string osCommandLine;
5867 :
5868 48 : for (const auto &path : GDALAlgorithm::m_callPath)
5869 : {
5870 36 : if (!osCommandLine.empty())
5871 24 : osCommandLine += ' ';
5872 36 : osCommandLine += path;
5873 : }
5874 :
5875 278 : for (const auto &arg : GetArgs())
5876 : {
5877 296 : if (arg->IsExplicitlySet() &&
5878 48 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5879 35 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5880 313 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5881 17 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5882 : {
5883 16 : osCommandLine += ' ';
5884 16 : std::string strArg;
5885 16 : if (!arg->Serialize(strArg))
5886 : {
5887 0 : CPLError(CE_Failure, CPLE_AppDefined,
5888 : "Cannot serialize argument %s",
5889 0 : arg->GetName().c_str());
5890 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5891 : }
5892 16 : osCommandLine += strArg;
5893 : }
5894 : }
5895 :
5896 12 : osCommandLine += " --output-format stream --output streamed_dataset";
5897 :
5898 12 : std::string outStringUnused;
5899 12 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5900 12 : ? ProcessGDALGOutputRet::GDALG_OK
5901 12 : : ProcessGDALGOutputRet::GDALG_ERROR;
5902 : }
5903 :
5904 1881 : return ProcessGDALGOutputRet::NOT_GDALG;
5905 : }
5906 :
5907 : /************************************************************************/
5908 : /* GDALAlgorithm::SaveGDALG() */
5909 : /************************************************************************/
5910 :
5911 24 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5912 : std::string &outString,
5913 : const std::string &commandLine)
5914 : {
5915 48 : CPLJSONDocument oDoc;
5916 24 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5917 24 : oDoc.GetRoot().Add("command_line", commandLine);
5918 24 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5919 :
5920 24 : if (!filename.empty())
5921 23 : return oDoc.Save(filename);
5922 :
5923 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5924 1 : return true;
5925 : }
5926 :
5927 : /************************************************************************/
5928 : /* GDALAlgorithm::AddCreationOptionsArg() */
5929 : /************************************************************************/
5930 :
5931 : GDALInConstructionAlgorithmArg &
5932 8696 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5933 : const char *helpMessage)
5934 : {
5935 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5936 17392 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5937 17392 : .AddAlias("co")
5938 17392 : .SetMetaVar("<KEY>=<VALUE>")
5939 8696 : .SetPackedValuesAllowed(false);
5940 293 : arg.AddValidationAction([this, &arg]()
5941 8989 : { return ParseAndValidateKeyValue(arg); });
5942 :
5943 : arg.SetAutoCompleteFunction(
5944 51 : [this](const std::string ¤tValue)
5945 : {
5946 17 : std::vector<std::string> oRet;
5947 :
5948 17 : int datasetType =
5949 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5950 17 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5951 17 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5952 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5953 : {
5954 17 : datasetType = outputArg->GetDatasetType();
5955 : }
5956 :
5957 17 : const char *pszMDCreationOptionList =
5958 : (datasetType == GDAL_OF_MULTIDIM_RASTER)
5959 17 : ? GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST
5960 : : GDAL_DMD_CREATIONOPTIONLIST;
5961 :
5962 17 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5963 34 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5964 17 : outputFormat->IsExplicitlySet())
5965 : {
5966 14 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5967 7 : outputFormat->Get<std::string>().c_str());
5968 7 : if (poDriver)
5969 : {
5970 7 : AddOptionsSuggestions(
5971 7 : poDriver->GetMetadataItem(pszMDCreationOptionList),
5972 : datasetType, currentValue, oRet);
5973 : }
5974 7 : return oRet;
5975 : }
5976 :
5977 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5978 : {
5979 10 : auto poDM = GetGDALDriverManager();
5980 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5981 10 : const auto &osDSName = datasetValue.GetName();
5982 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5983 10 : if (!osExt.empty())
5984 : {
5985 10 : std::set<std::string> oVisitedExtensions;
5986 721 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5987 : {
5988 718 : auto poDriver = poDM->GetDriver(i);
5989 2154 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5990 718 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5991 216 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5992 1436 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5993 216 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5994 0 : poDriver->GetMetadataItem(
5995 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5996 : {
5997 : const char *pszExtensions =
5998 502 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5999 502 : if (pszExtensions)
6000 : {
6001 : const CPLStringList aosExts(
6002 326 : CSLTokenizeString2(pszExtensions, " ", 0));
6003 722 : for (const char *pszExt : cpl::Iterate(aosExts))
6004 : {
6005 422 : if (EQUAL(pszExt, osExt.c_str()) &&
6006 16 : !cpl::contains(oVisitedExtensions,
6007 : pszExt))
6008 : {
6009 10 : oVisitedExtensions.insert(pszExt);
6010 10 : if (AddOptionsSuggestions(
6011 : poDriver->GetMetadataItem(
6012 10 : pszMDCreationOptionList),
6013 : datasetType, currentValue,
6014 : oRet))
6015 : {
6016 7 : return oRet;
6017 : }
6018 3 : break;
6019 : }
6020 : }
6021 : }
6022 : }
6023 : }
6024 : }
6025 : }
6026 :
6027 3 : return oRet;
6028 8696 : });
6029 :
6030 8696 : return arg;
6031 : }
6032 :
6033 : /************************************************************************/
6034 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
6035 : /************************************************************************/
6036 :
6037 : GDALInConstructionAlgorithmArg &
6038 4158 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
6039 : const char *helpMessage)
6040 : {
6041 : auto &arg =
6042 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
6043 8316 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
6044 8316 : .AddAlias("lco")
6045 8316 : .SetMetaVar("<KEY>=<VALUE>")
6046 4158 : .SetPackedValuesAllowed(false);
6047 76 : arg.AddValidationAction([this, &arg]()
6048 4234 : { return ParseAndValidateKeyValue(arg); });
6049 :
6050 : arg.SetAutoCompleteFunction(
6051 5 : [this](const std::string ¤tValue)
6052 : {
6053 2 : std::vector<std::string> oRet;
6054 :
6055 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6056 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
6057 2 : outputFormat->IsExplicitlySet())
6058 : {
6059 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
6060 1 : outputFormat->Get<std::string>().c_str());
6061 1 : if (poDriver)
6062 : {
6063 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
6064 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6065 : GDAL_OF_VECTOR, currentValue, oRet);
6066 : }
6067 1 : return oRet;
6068 : }
6069 :
6070 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
6071 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
6072 : {
6073 1 : auto poDM = GetGDALDriverManager();
6074 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
6075 1 : const auto &osDSName = datasetValue.GetName();
6076 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
6077 1 : if (!osExt.empty())
6078 : {
6079 1 : std::set<std::string> oVisitedExtensions;
6080 231 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
6081 : {
6082 230 : auto poDriver = poDM->GetDriver(i);
6083 230 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
6084 : {
6085 : const char *pszExtensions =
6086 91 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
6087 91 : if (pszExtensions)
6088 : {
6089 : const CPLStringList aosExts(
6090 62 : CSLTokenizeString2(pszExtensions, " ", 0));
6091 156 : for (const char *pszExt : cpl::Iterate(aosExts))
6092 : {
6093 96 : if (EQUAL(pszExt, osExt.c_str()) &&
6094 1 : !cpl::contains(oVisitedExtensions,
6095 : pszExt))
6096 : {
6097 1 : oVisitedExtensions.insert(pszExt);
6098 1 : if (AddOptionsSuggestions(
6099 : poDriver->GetMetadataItem(
6100 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
6101 : GDAL_OF_VECTOR, currentValue,
6102 : oRet))
6103 : {
6104 0 : return oRet;
6105 : }
6106 1 : break;
6107 : }
6108 : }
6109 : }
6110 : }
6111 : }
6112 : }
6113 : }
6114 :
6115 1 : return oRet;
6116 4158 : });
6117 :
6118 4158 : return arg;
6119 : }
6120 :
6121 : /************************************************************************/
6122 : /* GDALAlgorithm::AddBBOXArg() */
6123 : /************************************************************************/
6124 :
6125 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
6126 : GDALInConstructionAlgorithmArg &
6127 1979 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
6128 : {
6129 : auto &arg = AddArg("bbox", 0,
6130 : MsgOrDefault(helpMessage,
6131 : _("Bounding box as xmin,ymin,xmax,ymax")),
6132 3958 : pValue)
6133 1979 : .SetRepeatedArgAllowed(false)
6134 1979 : .SetMinCount(4)
6135 1979 : .SetMaxCount(4)
6136 1979 : .SetDisplayHintAboutRepetition(false);
6137 : arg.AddValidationAction(
6138 241 : [&arg]()
6139 : {
6140 241 : const auto &val = arg.Get<std::vector<double>>();
6141 241 : CPLAssert(val.size() == 4);
6142 241 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
6143 : {
6144 5 : CPLError(CE_Failure, CPLE_AppDefined,
6145 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
6146 : "xmin <= xmax and ymin <= ymax");
6147 5 : return false;
6148 : }
6149 236 : return true;
6150 1979 : });
6151 1979 : return arg;
6152 : }
6153 :
6154 : /************************************************************************/
6155 : /* GDALAlgorithm::AddActiveLayerArg() */
6156 : /************************************************************************/
6157 :
6158 : GDALInConstructionAlgorithmArg &
6159 1936 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
6160 : {
6161 : return AddArg("active-layer", 0,
6162 : MsgOrDefault(helpMessage,
6163 : _("Set active layer (if not specified, all)")),
6164 1936 : pValue);
6165 : }
6166 :
6167 : /************************************************************************/
6168 : /* GDALAlgorithm::AddNumThreadsArg() */
6169 : /************************************************************************/
6170 :
6171 : GDALInConstructionAlgorithmArg &
6172 728 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
6173 : const char *helpMessage)
6174 : {
6175 : auto &arg =
6176 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
6177 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
6178 728 : pStrValue);
6179 :
6180 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
6181 1456 : _("Number of jobs (read-only, hidden argument)"), pValue)
6182 728 : .SetHidden();
6183 :
6184 2736 : auto lambda = [this, &arg, pValue, pStrValue]
6185 : {
6186 912 : bool bOK = false;
6187 912 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
6188 : const int nLimit = std::clamp(
6189 912 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
6190 1824 : CPLGetNumCPUs());
6191 : const int nNumThreads =
6192 912 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
6193 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
6194 912 : if (bOK)
6195 : {
6196 912 : *pValue = nNumThreads;
6197 : }
6198 : else
6199 : {
6200 0 : ReportError(CE_Failure, CPLE_IllegalArg,
6201 : "Invalid value for '%s' argument",
6202 0 : arg.GetName().c_str());
6203 : }
6204 912 : return bOK;
6205 728 : };
6206 728 : if (!pStrValue->empty())
6207 : {
6208 681 : arg.SetDefault(*pStrValue);
6209 681 : lambda();
6210 : }
6211 728 : arg.AddValidationAction(std::move(lambda));
6212 728 : return arg;
6213 : }
6214 :
6215 : /************************************************************************/
6216 : /* GDALAlgorithm::AddAbsolutePathArg() */
6217 : /************************************************************************/
6218 :
6219 : GDALInConstructionAlgorithmArg &
6220 631 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
6221 : {
6222 : return AddArg(
6223 : "absolute-path", 0,
6224 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
6225 : "should be stored as an absolute path")),
6226 631 : pValue);
6227 : }
6228 :
6229 : /************************************************************************/
6230 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
6231 : /************************************************************************/
6232 :
6233 : GDALInConstructionAlgorithmArg &
6234 139 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
6235 : const char *helpMessage)
6236 : {
6237 :
6238 : const auto pixelFunctionNames =
6239 139 : VRTDerivedRasterBand::GetPixelFunctionNames();
6240 : return AddArg(
6241 : "pixel-function", 0,
6242 : MsgOrDefault(
6243 : helpMessage,
6244 : _("Specify a pixel function to calculate output value from "
6245 : "overlapping inputs")),
6246 278 : pValue)
6247 278 : .SetChoices(pixelFunctionNames);
6248 : }
6249 :
6250 : /************************************************************************/
6251 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
6252 : /************************************************************************/
6253 :
6254 : GDALInConstructionAlgorithmArg &
6255 139 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
6256 : const char *helpMessage)
6257 : {
6258 : auto &pixelFunctionArgArg =
6259 : AddArg("pixel-function-arg", 0,
6260 : MsgOrDefault(
6261 : helpMessage,
6262 : _("Specify argument(s) to pass to the pixel function")),
6263 278 : pValue)
6264 278 : .SetMetaVar("<NAME>=<VALUE>")
6265 139 : .SetRepeatedArgAllowed(true);
6266 : pixelFunctionArgArg.AddValidationAction(
6267 7 : [this, &pixelFunctionArgArg]()
6268 146 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
6269 :
6270 : pixelFunctionArgArg.SetAutoCompleteFunction(
6271 12 : [this](const std::string ¤tValue)
6272 : {
6273 12 : std::string pixelFunction;
6274 6 : const auto pixelFunctionArg = GetArg("pixel-function");
6275 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
6276 : {
6277 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
6278 : }
6279 :
6280 6 : std::vector<std::string> ret;
6281 :
6282 6 : if (!pixelFunction.empty())
6283 : {
6284 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
6285 : pixelFunction.c_str());
6286 5 : if (!pair)
6287 : {
6288 1 : ret.push_back("**");
6289 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6290 1 : ret.push_back(std::string("\xC2\xA0"
6291 : "Invalid pixel function name"));
6292 : }
6293 4 : else if (pair->second.find("Argument name=") ==
6294 : std::string::npos)
6295 : {
6296 1 : ret.push_back("**");
6297 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6298 1 : ret.push_back(
6299 2 : std::string(
6300 : "\xC2\xA0"
6301 : "No pixel function arguments for pixel function '")
6302 1 : .append(pixelFunction)
6303 1 : .append("'"));
6304 : }
6305 : else
6306 : {
6307 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6308 : ret);
6309 : }
6310 : }
6311 :
6312 12 : return ret;
6313 139 : });
6314 :
6315 139 : return pixelFunctionArgArg;
6316 : }
6317 :
6318 : /************************************************************************/
6319 : /* GDALAlgorithm::AddProgressArg() */
6320 : /************************************************************************/
6321 :
6322 10660 : void GDALAlgorithm::AddProgressArg(bool hidden)
6323 : {
6324 : auto &arg =
6325 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6326 21320 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6327 10660 : .SetAvailableInPipelineStep(false)
6328 21320 : .SetCategory(GAAC_COMMON)
6329 10660 : .AddAction([this]() { m_progressBarRequested = false; });
6330 10660 : if (hidden)
6331 2156 : arg.SetHidden();
6332 :
6333 21320 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6334 10660 : .SetAvailableInPipelineStep(false)
6335 10660 : .SetHidden();
6336 10660 : }
6337 :
6338 : /************************************************************************/
6339 : /* GDALAlgorithm::Run() */
6340 : /************************************************************************/
6341 :
6342 5046 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6343 : {
6344 5046 : WarnIfDeprecated();
6345 :
6346 5046 : if (m_selectedSubAlg)
6347 : {
6348 464 : if (m_calledFromCommandLine)
6349 276 : m_selectedSubAlg->m_calledFromCommandLine = true;
6350 464 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6351 : }
6352 :
6353 4582 : if (m_helpRequested || m_helpDocRequested)
6354 : {
6355 19 : if (m_calledFromCommandLine)
6356 19 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6357 19 : return true;
6358 : }
6359 :
6360 4563 : if (m_JSONUsageRequested)
6361 : {
6362 3 : if (m_calledFromCommandLine)
6363 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6364 3 : return true;
6365 : }
6366 :
6367 4560 : if (!ValidateArguments())
6368 125 : return false;
6369 :
6370 4435 : if (m_alreadyRun)
6371 : {
6372 3 : ReportError(CE_Failure, CPLE_AppDefined,
6373 : "Run() can be called only once per algorithm instance");
6374 3 : return false;
6375 : }
6376 4432 : m_alreadyRun = true;
6377 :
6378 4432 : switch (ProcessGDALGOutput())
6379 : {
6380 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6381 0 : return false;
6382 :
6383 12 : case ProcessGDALGOutputRet::GDALG_OK:
6384 12 : return true;
6385 :
6386 4420 : case ProcessGDALGOutputRet::NOT_GDALG:
6387 4420 : break;
6388 : }
6389 :
6390 4420 : if (m_executionForStreamOutput)
6391 : {
6392 98 : if (!CheckSafeForStreamOutput())
6393 : {
6394 4 : return false;
6395 : }
6396 : }
6397 :
6398 4416 : return RunImpl(pfnProgress, pProgressData);
6399 : }
6400 :
6401 : /************************************************************************/
6402 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6403 : /************************************************************************/
6404 :
6405 50 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6406 : {
6407 50 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6408 50 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6409 : {
6410 50 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6411 50 : if (!EQUAL(val.c_str(), "stream"))
6412 : {
6413 : // For security reasons, to avoid that reading a .gdalg.json file
6414 : // writes a file on the file system.
6415 4 : ReportError(
6416 : CE_Failure, CPLE_NotSupported,
6417 : "in streamed execution, --format stream should be used");
6418 4 : return false;
6419 : }
6420 : }
6421 46 : return true;
6422 : }
6423 :
6424 : /************************************************************************/
6425 : /* GDALAlgorithm::Finalize() */
6426 : /************************************************************************/
6427 :
6428 1965 : bool GDALAlgorithm::Finalize()
6429 : {
6430 1965 : bool ret = true;
6431 1965 : if (m_selectedSubAlg)
6432 282 : ret = m_selectedSubAlg->Finalize();
6433 :
6434 35497 : for (auto &arg : m_args)
6435 : {
6436 33532 : if (arg->GetType() == GAAT_DATASET)
6437 : {
6438 1495 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6439 : }
6440 32037 : else if (arg->GetType() == GAAT_DATASET_LIST)
6441 : {
6442 3059 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6443 : {
6444 1434 : ret = ds.Close() && ret;
6445 : }
6446 : }
6447 : }
6448 1965 : return ret;
6449 : }
6450 :
6451 : /************************************************************************/
6452 : /* GDALAlgorithm::GetArgNamesForCLI() */
6453 : /************************************************************************/
6454 :
6455 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6456 719 : GDALAlgorithm::GetArgNamesForCLI() const
6457 : {
6458 1438 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6459 :
6460 719 : size_t maxOptLen = 0;
6461 9097 : for (const auto &arg : m_args)
6462 : {
6463 8378 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6464 1757 : continue;
6465 6621 : std::string opt;
6466 6621 : bool addComma = false;
6467 6621 : if (!arg->GetShortName().empty())
6468 : {
6469 1427 : opt += '-';
6470 1427 : opt += arg->GetShortName();
6471 1427 : addComma = true;
6472 : }
6473 6621 : for (char alias : arg->GetShortNameAliases())
6474 : {
6475 0 : if (addComma)
6476 0 : opt += ", ";
6477 0 : opt += "-";
6478 0 : opt += alias;
6479 0 : addComma = true;
6480 : }
6481 7375 : for (const std::string &alias : arg->GetAliases())
6482 : {
6483 754 : if (addComma)
6484 326 : opt += ", ";
6485 754 : opt += "--";
6486 754 : opt += alias;
6487 754 : addComma = true;
6488 : }
6489 6621 : if (!arg->GetName().empty())
6490 : {
6491 6621 : if (addComma)
6492 1855 : opt += ", ";
6493 6621 : opt += "--";
6494 6621 : opt += arg->GetName();
6495 : }
6496 6621 : const auto &metaVar = arg->GetMetaVar();
6497 6621 : if (!metaVar.empty())
6498 : {
6499 4154 : opt += ' ';
6500 4154 : if (metaVar.front() != '<')
6501 3000 : opt += '<';
6502 4154 : opt += metaVar;
6503 4154 : if (metaVar.back() != '>')
6504 2994 : opt += '>';
6505 : }
6506 6621 : maxOptLen = std::max(maxOptLen, opt.size());
6507 6621 : options.emplace_back(arg.get(), opt);
6508 : }
6509 :
6510 1438 : return std::make_pair(std::move(options), maxOptLen);
6511 : }
6512 :
6513 : /************************************************************************/
6514 : /* GDALAlgorithm::GetUsageForCLI() */
6515 : /************************************************************************/
6516 :
6517 : std::string
6518 429 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6519 : const UsageOptions &usageOptions) const
6520 : {
6521 429 : if (m_selectedSubAlg)
6522 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6523 :
6524 844 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6525 844 : std::string osPath;
6526 851 : for (const std::string &s : m_callPath)
6527 : {
6528 429 : if (!osPath.empty())
6529 53 : osPath += ' ';
6530 429 : osPath += s;
6531 : }
6532 422 : osRet += ' ';
6533 422 : osRet += osPath;
6534 :
6535 422 : bool hasNonPositionals = false;
6536 5298 : for (const auto &arg : m_args)
6537 : {
6538 4876 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6539 3524 : hasNonPositionals = true;
6540 : }
6541 :
6542 422 : if (HasSubAlgorithms())
6543 : {
6544 10 : if (m_callPath.size() == 1)
6545 : {
6546 9 : osRet += " <COMMAND>";
6547 9 : if (hasNonPositionals)
6548 9 : osRet += " [OPTIONS]";
6549 9 : if (usageOptions.isPipelineStep)
6550 : {
6551 5 : const size_t nLenFirstLine = osRet.size();
6552 5 : osRet += '\n';
6553 5 : osRet.append(nLenFirstLine, '-');
6554 5 : osRet += '\n';
6555 : }
6556 9 : osRet += "\nwhere <COMMAND> is one of:\n";
6557 : }
6558 : else
6559 : {
6560 1 : osRet += " <SUBCOMMAND>";
6561 1 : if (hasNonPositionals)
6562 1 : osRet += " [OPTIONS]";
6563 1 : if (usageOptions.isPipelineStep)
6564 : {
6565 0 : const size_t nLenFirstLine = osRet.size();
6566 0 : osRet += '\n';
6567 0 : osRet.append(nLenFirstLine, '-');
6568 0 : osRet += '\n';
6569 : }
6570 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6571 : }
6572 10 : size_t maxNameLen = 0;
6573 62 : for (const auto &subAlgName : GetSubAlgorithmNames())
6574 : {
6575 52 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6576 : }
6577 62 : for (const auto &subAlgName : GetSubAlgorithmNames())
6578 : {
6579 104 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6580 52 : if (subAlg && !subAlg->IsHidden())
6581 : {
6582 52 : const std::string &name(subAlg->GetName());
6583 52 : osRet += " - ";
6584 52 : osRet += name;
6585 52 : osRet += ": ";
6586 52 : osRet.append(maxNameLen - name.size(), ' ');
6587 52 : osRet += subAlg->GetDescription();
6588 52 : if (!subAlg->m_aliases.empty())
6589 : {
6590 8 : bool first = true;
6591 8 : for (const auto &alias : subAlg->GetAliases())
6592 : {
6593 8 : if (alias ==
6594 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6595 8 : break;
6596 0 : if (first)
6597 0 : osRet += " (alias: ";
6598 : else
6599 0 : osRet += ", ";
6600 0 : osRet += alias;
6601 0 : first = false;
6602 : }
6603 8 : if (!first)
6604 : {
6605 0 : osRet += ')';
6606 : }
6607 : }
6608 52 : osRet += '\n';
6609 : }
6610 : }
6611 :
6612 10 : if (shortUsage && hasNonPositionals)
6613 : {
6614 3 : osRet += "\nTry '";
6615 3 : osRet += osPath;
6616 3 : osRet += " --help' for help.\n";
6617 : }
6618 : }
6619 : else
6620 : {
6621 412 : if (!m_args.empty())
6622 : {
6623 412 : if (hasNonPositionals)
6624 412 : osRet += " [OPTIONS]";
6625 603 : for (const auto *arg : m_positionalArgs)
6626 : {
6627 268 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6628 77 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6629 : {
6630 : const bool optional =
6631 204 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6632 30 : arg->GetName() == "pipeline"));
6633 174 : osRet += ' ';
6634 174 : if (optional)
6635 30 : osRet += '[';
6636 174 : const std::string &metavar = arg->GetMetaVar();
6637 174 : if (!metavar.empty() && metavar[0] == '<')
6638 : {
6639 4 : osRet += metavar;
6640 : }
6641 : else
6642 : {
6643 170 : osRet += '<';
6644 170 : osRet += metavar;
6645 170 : osRet += '>';
6646 : }
6647 216 : if (arg->GetType() == GAAT_DATASET_LIST &&
6648 42 : arg->GetMaxCount() > 1)
6649 : {
6650 28 : osRet += "...";
6651 : }
6652 174 : if (optional)
6653 30 : osRet += ']';
6654 : }
6655 : }
6656 : }
6657 :
6658 412 : const size_t nLenFirstLine = osRet.size();
6659 412 : osRet += '\n';
6660 412 : if (usageOptions.isPipelineStep)
6661 : {
6662 322 : osRet.append(nLenFirstLine, '-');
6663 322 : osRet += '\n';
6664 : }
6665 :
6666 412 : if (shortUsage)
6667 : {
6668 23 : osRet += "Try '";
6669 23 : osRet += osPath;
6670 23 : osRet += " --help' for help.\n";
6671 23 : return osRet;
6672 : }
6673 :
6674 389 : osRet += '\n';
6675 389 : osRet += m_description;
6676 389 : osRet += '\n';
6677 : }
6678 :
6679 399 : if (!m_args.empty() && !shortUsage)
6680 : {
6681 792 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6682 : size_t maxOptLen;
6683 396 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6684 396 : if (usageOptions.maxOptLen)
6685 323 : maxOptLen = usageOptions.maxOptLen;
6686 :
6687 792 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6688 396 : if (m_arbitraryLongNameArgsAllowed)
6689 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6690 :
6691 : const auto OutputArg =
6692 2526 : [this, maxOptLen, &osRet,
6693 25286 : &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
6694 : {
6695 2526 : osRet += " ";
6696 2526 : osRet += opt;
6697 2526 : osRet += " ";
6698 2526 : osRet.append(maxOptLen - opt.size(), ' ');
6699 2526 : osRet += arg->GetDescription();
6700 :
6701 2526 : const auto &choices = arg->GetChoices();
6702 2526 : if (!choices.empty())
6703 : {
6704 237 : osRet += ". ";
6705 237 : osRet += arg->GetMetaVar();
6706 237 : osRet += '=';
6707 237 : bool firstChoice = true;
6708 1800 : for (const auto &choice : choices)
6709 : {
6710 1563 : if (!firstChoice)
6711 1326 : osRet += '|';
6712 1563 : osRet += choice;
6713 1563 : firstChoice = false;
6714 : }
6715 : }
6716 :
6717 4982 : if (arg->GetType() == GAAT_DATASET ||
6718 2456 : arg->GetType() == GAAT_DATASET_LIST)
6719 : {
6720 148 : if (arg->IsOutput() &&
6721 148 : arg->GetDatasetInputFlags() == GADV_NAME &&
6722 9 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6723 : {
6724 9 : osRet += " (created by algorithm)";
6725 : }
6726 : }
6727 :
6728 2526 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6729 : {
6730 198 : osRet += " (default: ";
6731 198 : osRet += arg->GetDefault<std::string>();
6732 198 : osRet += ')';
6733 : }
6734 2328 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6735 : {
6736 70 : if (arg->GetDefault<bool>())
6737 0 : osRet += " (default: true)";
6738 : }
6739 2258 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6740 : {
6741 84 : osRet += " (default: ";
6742 84 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6743 84 : osRet += ')';
6744 : }
6745 2174 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6746 : {
6747 49 : osRet += " (default: ";
6748 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6749 49 : osRet += ')';
6750 : }
6751 2571 : else if (arg->GetType() == GAAT_STRING_LIST &&
6752 446 : arg->HasDefaultValue())
6753 : {
6754 : const auto &defaultVal =
6755 17 : arg->GetDefault<std::vector<std::string>>();
6756 17 : if (defaultVal.size() == 1)
6757 : {
6758 17 : osRet += " (default: ";
6759 17 : osRet += defaultVal[0];
6760 17 : osRet += ')';
6761 : }
6762 : }
6763 2131 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6764 23 : arg->HasDefaultValue())
6765 : {
6766 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6767 0 : if (defaultVal.size() == 1)
6768 : {
6769 0 : osRet += " (default: ";
6770 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6771 0 : osRet += ')';
6772 : }
6773 : }
6774 2108 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6775 : {
6776 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6777 0 : if (defaultVal.size() == 1)
6778 : {
6779 0 : osRet += " (default: ";
6780 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6781 0 : osRet += ')';
6782 : }
6783 : }
6784 :
6785 2526 : if (arg->GetDisplayHintAboutRepetition())
6786 : {
6787 2555 : if (arg->GetMinCount() > 0 &&
6788 92 : arg->GetMinCount() == arg->GetMaxCount())
6789 : {
6790 18 : if (arg->GetMinCount() != 1)
6791 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6792 : }
6793 2519 : else if (arg->GetMinCount() > 0 &&
6794 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6795 : {
6796 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6797 8 : arg->GetMaxCount());
6798 : }
6799 2437 : else if (arg->GetMinCount() > 0)
6800 : {
6801 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6802 : }
6803 2371 : else if (arg->GetMaxCount() > 1)
6804 : {
6805 427 : osRet += " [may be repeated]";
6806 : }
6807 : }
6808 :
6809 2526 : if (arg->IsRequired())
6810 : {
6811 172 : osRet += " [required]";
6812 : }
6813 :
6814 2775 : if (!arg->IsAvailableInPipelineStep() &&
6815 249 : !usageOptions.isPipelineStep)
6816 : {
6817 29 : osRet += " [not available in pipelines]";
6818 : }
6819 :
6820 2526 : osRet += '\n';
6821 :
6822 2526 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6823 2526 : if (!mutualExclusionGroup.empty())
6824 : {
6825 518 : std::string otherArgs;
6826 4943 : for (const auto &otherArg : m_args)
6827 : {
6828 8605 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6829 3921 : otherArg.get() == arg)
6830 1022 : continue;
6831 3662 : if (otherArg->GetMutualExclusionGroup() ==
6832 : mutualExclusionGroup)
6833 : {
6834 356 : if (!otherArgs.empty())
6835 101 : otherArgs += ", ";
6836 356 : otherArgs += "--";
6837 356 : otherArgs += otherArg->GetName();
6838 : }
6839 : }
6840 259 : if (!otherArgs.empty())
6841 : {
6842 255 : osRet += " ";
6843 255 : osRet += " ";
6844 255 : osRet.append(maxOptLen, ' ');
6845 255 : osRet += "Mutually exclusive with ";
6846 255 : osRet += otherArgs;
6847 255 : osRet += '\n';
6848 : }
6849 : }
6850 :
6851 : // Check dependency
6852 5052 : std::string dependencyArgs;
6853 :
6854 32 : for (const auto &dependencyArgumentName :
6855 2590 : GetArgDependencies(arg->GetName()))
6856 : {
6857 32 : const auto otherArg{GetArg(dependencyArgumentName)};
6858 32 : if (otherArg != nullptr)
6859 : {
6860 32 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6861 : otherArg == arg)
6862 : {
6863 0 : continue;
6864 : }
6865 :
6866 32 : if (!dependencyArgs.empty())
6867 : {
6868 3 : dependencyArgs += ", ";
6869 : }
6870 :
6871 32 : dependencyArgs += "--";
6872 32 : dependencyArgs += otherArg->GetName();
6873 : }
6874 : else
6875 : {
6876 0 : CPLError(CE_Warning, CPLE_AppDefined,
6877 : "Argument '%s' depends on unknown argument '%s'",
6878 0 : arg->GetName().c_str(),
6879 : dependencyArgumentName.c_str());
6880 : }
6881 : }
6882 :
6883 2526 : if (!dependencyArgs.empty())
6884 : {
6885 29 : osRet += " ";
6886 29 : osRet += " ";
6887 29 : osRet.append(maxOptLen, ' ');
6888 29 : osRet += "Depends on ";
6889 29 : osRet += dependencyArgs;
6890 29 : osRet += '\n';
6891 : }
6892 2526 : };
6893 :
6894 396 : if (!m_positionalArgs.empty())
6895 : {
6896 151 : osRet += "\nPositional arguments:\n";
6897 1614 : for (const auto &[arg, opt] : options)
6898 : {
6899 1463 : if (arg->IsPositional())
6900 141 : OutputArg(arg, opt);
6901 : }
6902 : }
6903 :
6904 396 : if (hasNonPositionals)
6905 : {
6906 396 : bool hasCommon = false;
6907 396 : bool hasBase = false;
6908 396 : bool hasAdvanced = false;
6909 396 : bool hasEsoteric = false;
6910 792 : std::vector<std::string> categories;
6911 3907 : for (const auto &iter : options)
6912 : {
6913 3511 : const auto &arg = iter.first;
6914 3511 : if (!arg->IsPositional())
6915 : {
6916 3370 : const auto &category = arg->GetCategory();
6917 3370 : if (category == GAAC_COMMON)
6918 : {
6919 1207 : hasCommon = true;
6920 : }
6921 2163 : else if (category == GAAC_BASE)
6922 : {
6923 1891 : hasBase = true;
6924 : }
6925 272 : else if (category == GAAC_ADVANCED)
6926 : {
6927 210 : hasAdvanced = true;
6928 : }
6929 62 : else if (category == GAAC_ESOTERIC)
6930 : {
6931 29 : hasEsoteric = true;
6932 : }
6933 33 : else if (std::find(categories.begin(), categories.end(),
6934 33 : category) == categories.end())
6935 : {
6936 9 : categories.push_back(category);
6937 : }
6938 : }
6939 : }
6940 396 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6941 71 : categories.insert(categories.begin(), GAAC_ADVANCED);
6942 396 : if (hasBase)
6943 349 : categories.insert(categories.begin(), GAAC_BASE);
6944 396 : if (hasCommon && !usageOptions.isPipelineStep)
6945 69 : categories.insert(categories.begin(), GAAC_COMMON);
6946 396 : if (hasEsoteric)
6947 11 : categories.push_back(GAAC_ESOTERIC);
6948 :
6949 905 : for (const auto &category : categories)
6950 : {
6951 509 : osRet += "\n";
6952 509 : if (category != GAAC_BASE)
6953 : {
6954 160 : osRet += category;
6955 160 : osRet += ' ';
6956 : }
6957 509 : osRet += "Options:\n";
6958 5590 : for (const auto &[arg, opt] : options)
6959 : {
6960 5081 : if (!arg->IsPositional() && arg->GetCategory() == category)
6961 2385 : OutputArg(arg, opt);
6962 : }
6963 509 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6964 : {
6965 2 : osRet += " ";
6966 2 : osRet += userProvidedOpt;
6967 2 : osRet += " ";
6968 2 : if (userProvidedOpt.size() < maxOptLen)
6969 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6970 2 : osRet += "Argument provided by user";
6971 2 : osRet += '\n';
6972 : }
6973 : }
6974 : }
6975 : }
6976 :
6977 399 : if (!m_longDescription.empty())
6978 : {
6979 7 : osRet += '\n';
6980 7 : osRet += m_longDescription;
6981 7 : osRet += '\n';
6982 : }
6983 :
6984 399 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6985 : {
6986 384 : if (!m_helpURL.empty())
6987 : {
6988 384 : osRet += "\nFor more details, consult ";
6989 384 : osRet += GetHelpFullURL();
6990 384 : osRet += '\n';
6991 : }
6992 384 : osRet += GetUsageForCLIEnd();
6993 : }
6994 :
6995 399 : return osRet;
6996 : }
6997 :
6998 : /************************************************************************/
6999 : /* GDALAlgorithm::GetUsageForCLIEnd() */
7000 : /************************************************************************/
7001 :
7002 : //! @cond Doxygen_Suppress
7003 391 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
7004 : {
7005 391 : std::string osRet;
7006 :
7007 391 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
7008 : {
7009 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
7010 : "alternative interface to GDAL and OGR command line "
7011 : "utilities.\nThe project reserves the right to modify, "
7012 : "rename, reorganize, and change the behavior of the utility\n"
7013 : "until it is officially frozen in a future feature release of "
7014 14 : "GDAL.\n";
7015 : }
7016 391 : return osRet;
7017 : }
7018 :
7019 : //! @endcond
7020 :
7021 : /************************************************************************/
7022 : /* GDALAlgorithm::GetUsageAsJSON() */
7023 : /************************************************************************/
7024 :
7025 591 : std::string GDALAlgorithm::GetUsageAsJSON() const
7026 : {
7027 1182 : CPLJSONDocument oDoc;
7028 1182 : auto oRoot = oDoc.GetRoot();
7029 :
7030 591 : if (m_displayInJSONUsage)
7031 : {
7032 589 : oRoot.Add("name", m_name);
7033 589 : CPLJSONArray jFullPath;
7034 1226 : for (const std::string &s : m_callPath)
7035 : {
7036 637 : jFullPath.Add(s);
7037 : }
7038 589 : oRoot.Add("full_path", jFullPath);
7039 : }
7040 :
7041 591 : oRoot.Add("description", m_description);
7042 591 : if (!m_helpURL.empty())
7043 : {
7044 588 : oRoot.Add("short_url", m_helpURL);
7045 588 : oRoot.Add("url", GetHelpFullURL());
7046 : }
7047 :
7048 1182 : CPLJSONArray jSubAlgorithms;
7049 800 : for (const auto &subAlgName : GetSubAlgorithmNames())
7050 : {
7051 418 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7052 209 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
7053 : {
7054 207 : CPLJSONDocument oSubDoc;
7055 207 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
7056 207 : jSubAlgorithms.Add(oSubDoc.GetRoot());
7057 : }
7058 : }
7059 591 : oRoot.Add("sub_algorithms", jSubAlgorithms);
7060 :
7061 591 : if (m_arbitraryLongNameArgsAllowed)
7062 : {
7063 1 : oRoot.Add("user_provided_arguments_allowed", true);
7064 : }
7065 :
7066 11252 : const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
7067 : {
7068 5626 : CPLJSONObject jArg;
7069 5626 : jArg.Add("name", arg->GetName());
7070 5626 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
7071 5626 : jArg.Add("description", arg->GetDescription());
7072 :
7073 5626 : const auto &metaVar = arg->GetMetaVar();
7074 5626 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
7075 : {
7076 1715 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
7077 1715 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
7078 : std::string::npos)
7079 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
7080 : else
7081 910 : jArg.Add("metavar", metaVar);
7082 : }
7083 :
7084 5626 : if (!arg->IsAvailableInPipelineStep())
7085 : {
7086 1659 : jArg.Add("available_in_pipeline_step", false);
7087 : }
7088 :
7089 5626 : const auto &choices = arg->GetChoices();
7090 5626 : if (!choices.empty())
7091 : {
7092 431 : CPLJSONArray jChoices;
7093 3647 : for (const auto &choice : choices)
7094 3216 : jChoices.Add(choice);
7095 431 : jArg.Add("choices", jChoices);
7096 : }
7097 5626 : if (arg->HasDefaultValue())
7098 : {
7099 1236 : switch (arg->GetType())
7100 : {
7101 436 : case GAAT_BOOLEAN:
7102 436 : jArg.Add("default", arg->GetDefault<bool>());
7103 436 : break;
7104 378 : case GAAT_STRING:
7105 378 : jArg.Add("default", arg->GetDefault<std::string>());
7106 378 : break;
7107 210 : case GAAT_INTEGER:
7108 210 : jArg.Add("default", arg->GetDefault<int>());
7109 210 : break;
7110 178 : case GAAT_REAL:
7111 178 : jArg.Add("default", arg->GetDefault<double>());
7112 178 : break;
7113 32 : case GAAT_STRING_LIST:
7114 : {
7115 : const auto &val =
7116 32 : arg->GetDefault<std::vector<std::string>>();
7117 32 : if (val.size() == 1)
7118 : {
7119 31 : jArg.Add("default", val[0]);
7120 : }
7121 : else
7122 : {
7123 1 : CPLJSONArray jArr;
7124 3 : for (const auto &s : val)
7125 : {
7126 2 : jArr.Add(s);
7127 : }
7128 1 : jArg.Add("default", jArr);
7129 : }
7130 32 : break;
7131 : }
7132 1 : case GAAT_INTEGER_LIST:
7133 : {
7134 1 : const auto &val = arg->GetDefault<std::vector<int>>();
7135 1 : if (val.size() == 1)
7136 : {
7137 0 : jArg.Add("default", val[0]);
7138 : }
7139 : else
7140 : {
7141 1 : CPLJSONArray jArr;
7142 3 : for (int i : val)
7143 : {
7144 2 : jArr.Add(i);
7145 : }
7146 1 : jArg.Add("default", jArr);
7147 : }
7148 1 : break;
7149 : }
7150 1 : case GAAT_REAL_LIST:
7151 : {
7152 1 : const auto &val = arg->GetDefault<std::vector<double>>();
7153 1 : if (val.size() == 1)
7154 : {
7155 0 : jArg.Add("default", val[0]);
7156 : }
7157 : else
7158 : {
7159 1 : CPLJSONArray jArr;
7160 3 : for (double d : val)
7161 : {
7162 2 : jArr.Add(d);
7163 : }
7164 1 : jArg.Add("default", jArr);
7165 : }
7166 1 : break;
7167 : }
7168 0 : case GAAT_DATASET:
7169 : case GAAT_DATASET_LIST:
7170 0 : CPLError(CE_Warning, CPLE_AppDefined,
7171 : "Unhandled default value for arg %s",
7172 0 : arg->GetName().c_str());
7173 0 : break;
7174 : }
7175 : }
7176 :
7177 5626 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
7178 5626 : if (!std::isnan(minVal))
7179 : {
7180 697 : if (arg->GetType() == GAAT_INTEGER ||
7181 269 : arg->GetType() == GAAT_INTEGER_LIST)
7182 175 : jArg.Add("min_value", static_cast<int>(minVal));
7183 : else
7184 253 : jArg.Add("min_value", minVal);
7185 428 : jArg.Add("min_value_is_included", minValIsIncluded);
7186 : }
7187 :
7188 5626 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
7189 5626 : if (!std::isnan(maxVal))
7190 : {
7191 199 : if (arg->GetType() == GAAT_INTEGER ||
7192 82 : arg->GetType() == GAAT_INTEGER_LIST)
7193 35 : jArg.Add("max_value", static_cast<int>(maxVal));
7194 : else
7195 82 : jArg.Add("max_value", maxVal);
7196 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
7197 : }
7198 :
7199 5626 : jArg.Add("required", arg->IsRequired());
7200 5626 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
7201 : {
7202 1594 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
7203 1594 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
7204 1594 : jArg.Add("min_count", arg->GetMinCount());
7205 1594 : jArg.Add("max_count", arg->GetMaxCount());
7206 : }
7207 :
7208 : // Process dependencies
7209 5626 : const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7210 5626 : if (!mutualDependencyGroup.empty())
7211 : {
7212 32 : jArg.Add("mutual_dependency_group", mutualDependencyGroup);
7213 : }
7214 :
7215 11252 : CPLJSONArray jDependencies;
7216 50 : for (const auto &dependencyArgumentName :
7217 5726 : GetArgDependencies(arg->GetName()))
7218 : {
7219 50 : jDependencies.Add(dependencyArgumentName);
7220 : }
7221 :
7222 5626 : if (jDependencies.Size() > 0)
7223 : {
7224 50 : jArg.Add("depends_on", jDependencies);
7225 : }
7226 :
7227 5626 : jArg.Add("category", arg->GetCategory());
7228 :
7229 10994 : if (arg->GetType() == GAAT_DATASET ||
7230 5368 : arg->GetType() == GAAT_DATASET_LIST)
7231 : {
7232 : {
7233 472 : CPLJSONArray jAr;
7234 472 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
7235 313 : jAr.Add("raster");
7236 472 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
7237 185 : jAr.Add("vector");
7238 472 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
7239 41 : jAr.Add("multidim_raster");
7240 472 : jArg.Add("dataset_type", jAr);
7241 : }
7242 :
7243 643 : const auto GetFlags = [](int flags)
7244 : {
7245 643 : CPLJSONArray jAr;
7246 643 : if (flags & GADV_NAME)
7247 472 : jAr.Add("name");
7248 643 : if (flags & GADV_OBJECT)
7249 595 : jAr.Add("dataset");
7250 643 : return jAr;
7251 : };
7252 :
7253 472 : if (arg->IsInput())
7254 : {
7255 472 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
7256 : }
7257 472 : if (arg->IsOutput())
7258 : {
7259 171 : jArg.Add("output_flags",
7260 342 : GetFlags(arg->GetDatasetOutputFlags()));
7261 : }
7262 : }
7263 :
7264 5626 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
7265 5626 : if (!mutualExclusionGroup.empty())
7266 : {
7267 728 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
7268 : }
7269 :
7270 11252 : const auto &metadata = arg->GetMetadata();
7271 5626 : if (!metadata.empty())
7272 : {
7273 460 : CPLJSONObject jMetadata;
7274 959 : for (const auto &[key, values] : metadata)
7275 : {
7276 998 : CPLJSONArray jValue;
7277 1204 : for (const auto &value : values)
7278 705 : jValue.Add(value);
7279 499 : jMetadata.Add(key, jValue);
7280 : }
7281 460 : jArg.Add("metadata", jMetadata);
7282 : }
7283 :
7284 11252 : return jArg;
7285 591 : };
7286 :
7287 : {
7288 591 : CPLJSONArray jArgs;
7289 9311 : for (const auto &arg : m_args)
7290 : {
7291 8720 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
7292 5390 : jArgs.Add(ProcessArg(arg.get()));
7293 : }
7294 591 : oRoot.Add("input_arguments", jArgs);
7295 : }
7296 :
7297 : {
7298 591 : CPLJSONArray jArgs;
7299 9311 : for (const auto &arg : m_args)
7300 : {
7301 8720 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
7302 65 : jArgs.Add(ProcessArg(arg.get()));
7303 : }
7304 591 : oRoot.Add("output_arguments", jArgs);
7305 : }
7306 :
7307 : {
7308 591 : CPLJSONArray jArgs;
7309 9311 : for (const auto &arg : m_args)
7310 : {
7311 8720 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
7312 171 : jArgs.Add(ProcessArg(arg.get()));
7313 : }
7314 591 : oRoot.Add("input_output_arguments", jArgs);
7315 : }
7316 :
7317 591 : if (m_supportsStreamedOutput)
7318 : {
7319 127 : oRoot.Add("supports_streamed_output", true);
7320 : }
7321 :
7322 1182 : return oDoc.SaveAsString();
7323 : }
7324 :
7325 : /************************************************************************/
7326 : /* GDALAlgorithm::GetAutoComplete() */
7327 : /************************************************************************/
7328 :
7329 : std::vector<std::string>
7330 295 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
7331 : bool lastWordIsComplete, bool showAllOptions)
7332 : {
7333 590 : std::vector<std::string> ret;
7334 :
7335 : // Get inner-most algorithm
7336 295 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
7337 295 : GDALAlgorithm *curAlg = this;
7338 580 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
7339 : {
7340 : auto subAlg = curAlg->InstantiateSubAlgorithm(
7341 431 : args.front(), /* suggestionAllowed = */ false);
7342 431 : if (!subAlg)
7343 145 : break;
7344 286 : if (args.size() == 1 && !lastWordIsComplete)
7345 : {
7346 5 : int nCount = 0;
7347 116 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
7348 : {
7349 111 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
7350 6 : nCount++;
7351 : }
7352 5 : if (nCount >= 2)
7353 : {
7354 11 : for (const std::string &subAlgName :
7355 23 : curAlg->GetSubAlgorithmNames())
7356 : {
7357 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
7358 11 : if (subAlg && !subAlg->IsHidden())
7359 11 : ret.push_back(subAlg->GetName());
7360 : }
7361 1 : return ret;
7362 : }
7363 : }
7364 285 : showAllOptions = false;
7365 285 : args.erase(args.begin());
7366 285 : curAlgHolder = std::move(subAlg);
7367 285 : curAlg = curAlgHolder.get();
7368 : }
7369 294 : if (curAlg != this)
7370 : {
7371 155 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
7372 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
7373 155 : /* showAllOptions = */ false);
7374 : }
7375 :
7376 278 : std::string option;
7377 278 : std::string value;
7378 139 : ExtractLastOptionAndValue(args, option, value);
7379 :
7380 170 : if (option.empty() && !args.empty() && !args.back().empty() &&
7381 31 : args.back()[0] == '-')
7382 : {
7383 28 : const auto &lastArg = args.back();
7384 : // List available options
7385 413 : for (const auto &arg : GetArgs())
7386 : {
7387 709 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7388 643 : (!showAllOptions &&
7389 876 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7390 530 : arg->GetName() == "version" ||
7391 265 : arg->GetName() == "json-usage")))
7392 : {
7393 142 : continue;
7394 : }
7395 243 : if (!arg->GetShortName().empty())
7396 : {
7397 153 : std::string str = std::string("-").append(arg->GetShortName());
7398 51 : if (lastArg == str)
7399 0 : ret.push_back(std::move(str));
7400 : }
7401 243 : if (lastArg != "-" && lastArg != "--")
7402 : {
7403 54 : for (const std::string &alias : arg->GetAliases())
7404 : {
7405 48 : std::string str = std::string("--").append(alias);
7406 16 : if (cpl::starts_with(str, lastArg))
7407 3 : ret.push_back(std::move(str));
7408 : }
7409 : }
7410 243 : if (!arg->GetName().empty())
7411 : {
7412 729 : std::string str = std::string("--").append(arg->GetName());
7413 243 : if (cpl::starts_with(str, lastArg))
7414 207 : ret.push_back(std::move(str));
7415 : }
7416 : }
7417 28 : std::sort(ret.begin(), ret.end());
7418 : }
7419 111 : else if (!option.empty())
7420 : {
7421 : // List possible choices for current option
7422 104 : auto arg = GetArg(option);
7423 104 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7424 : {
7425 104 : ret = arg->GetChoices();
7426 104 : if (ret.empty())
7427 : {
7428 : {
7429 99 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7430 99 : SetParseForAutoCompletion();
7431 99 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7432 : }
7433 99 : ret = arg->GetAutoCompleteChoices(value);
7434 : }
7435 : else
7436 : {
7437 5 : std::sort(ret.begin(), ret.end());
7438 : }
7439 104 : if (!ret.empty() && ret.back() == value)
7440 : {
7441 2 : ret.clear();
7442 : }
7443 102 : else if (ret.empty())
7444 : {
7445 13 : ret.push_back("**");
7446 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7447 26 : ret.push_back(std::string("\xC2\xA0"
7448 : "description: ")
7449 13 : .append(arg->GetDescription()));
7450 : }
7451 : }
7452 : }
7453 : else
7454 : {
7455 : // List possible sub-algorithms
7456 69 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7457 : {
7458 124 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7459 62 : if (subAlg && !subAlg->IsHidden())
7460 62 : ret.push_back(subAlg->GetName());
7461 : }
7462 7 : if (!ret.empty())
7463 : {
7464 3 : std::sort(ret.begin(), ret.end());
7465 : }
7466 :
7467 : // Try filenames
7468 7 : if (ret.empty() && !args.empty())
7469 : {
7470 : {
7471 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7472 3 : SetParseForAutoCompletion();
7473 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7474 : }
7475 :
7476 3 : const std::string &lastArg = args.back();
7477 3 : GDALAlgorithmArg *arg = nullptr;
7478 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7479 21 : "like", "source", "destination"})
7480 : {
7481 18 : if (!arg)
7482 : {
7483 3 : auto newArg = GetArg(name);
7484 3 : if (newArg)
7485 : {
7486 3 : if (!newArg->IsExplicitlySet())
7487 : {
7488 0 : arg = newArg;
7489 : }
7490 6 : else if (newArg->GetType() == GAAT_STRING ||
7491 5 : newArg->GetType() == GAAT_STRING_LIST ||
7492 8 : newArg->GetType() == GAAT_DATASET ||
7493 2 : newArg->GetType() == GAAT_DATASET_LIST)
7494 : {
7495 : VSIStatBufL sStat;
7496 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7497 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7498 : {
7499 3 : arg = newArg;
7500 : }
7501 : }
7502 : }
7503 : }
7504 : }
7505 3 : if (arg)
7506 : {
7507 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7508 : }
7509 : }
7510 : }
7511 :
7512 139 : return ret;
7513 : }
7514 :
7515 : /************************************************************************/
7516 : /* GDALAlgorithm::GetFieldIndices() */
7517 : /************************************************************************/
7518 :
7519 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7520 : OGRLayerH hLayer, std::vector<int> &indices)
7521 : {
7522 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7523 :
7524 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7525 :
7526 44 : if (names.size() == 1 && names[0] == "ALL")
7527 : {
7528 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7529 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7530 : {
7531 16 : indices.push_back(i);
7532 : }
7533 : }
7534 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7535 : {
7536 6 : std::set<int> fieldsAdded;
7537 14 : for (const std::string &osFieldName : names)
7538 : {
7539 :
7540 : const int nIdx =
7541 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7542 :
7543 10 : if (nIdx < 0)
7544 : {
7545 2 : CPLError(CE_Failure, CPLE_AppDefined,
7546 : "Field '%s' does not exist in layer '%s'",
7547 2 : osFieldName.c_str(), layer.GetName());
7548 2 : return false;
7549 : }
7550 :
7551 8 : if (fieldsAdded.insert(nIdx).second)
7552 : {
7553 7 : indices.push_back(nIdx);
7554 : }
7555 : }
7556 : }
7557 :
7558 42 : return true;
7559 : }
7560 :
7561 : /************************************************************************/
7562 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7563 : /************************************************************************/
7564 :
7565 139 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7566 : std::string &option,
7567 : std::string &value) const
7568 : {
7569 139 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7570 : {
7571 97 : const auto nPosEqual = args.back().find('=');
7572 97 : if (nPosEqual == std::string::npos)
7573 : {
7574 : // Deal with "gdal ... --option"
7575 78 : if (GetArg(args.back()))
7576 : {
7577 50 : option = args.back();
7578 50 : args.pop_back();
7579 : }
7580 : }
7581 : else
7582 : {
7583 : // Deal with "gdal ... --option=<value>"
7584 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7585 : {
7586 19 : option = args.back().substr(0, nPosEqual);
7587 19 : value = args.back().substr(nPosEqual + 1);
7588 19 : args.pop_back();
7589 : }
7590 : }
7591 : }
7592 78 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7593 36 : args[args.size() - 2][0] == '-')
7594 : {
7595 : // Deal with "gdal ... --option <value>"
7596 35 : auto arg = GetArg(args[args.size() - 2]);
7597 35 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7598 : {
7599 35 : option = args[args.size() - 2];
7600 35 : value = args.back();
7601 35 : args.pop_back();
7602 : }
7603 : }
7604 :
7605 139 : const auto IsKeyValueOption = [](const std::string &osStr)
7606 : {
7607 382 : return osStr == "--co" || osStr == "--creation-option" ||
7608 357 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7609 380 : osStr == "--oo" || osStr == "--open-option";
7610 : };
7611 :
7612 139 : if (IsKeyValueOption(option))
7613 : {
7614 23 : const auto nPosEqual = value.find('=');
7615 23 : if (nPosEqual != std::string::npos)
7616 : {
7617 11 : value.resize(nPosEqual);
7618 : }
7619 : }
7620 139 : }
7621 :
7622 : /************************************************************************/
7623 : /* GDALAlgorithm::GetArgDependencies() */
7624 : /************************************************************************/
7625 :
7626 : std::vector<std::string>
7627 8161 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
7628 : {
7629 8161 : const auto arg = GetArg(osName, false);
7630 8161 : if (!arg)
7631 : {
7632 0 : ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
7633 : osName.c_str());
7634 0 : return {};
7635 : }
7636 16322 : std::vector<std::string> dependencies = arg->GetDirectDependencies();
7637 8161 : if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
7638 8161 : !mutualDependencyGroup.empty())
7639 : {
7640 896 : for (const auto &otherArg : m_args)
7641 : {
7642 1627 : if (otherArg.get() == arg ||
7643 786 : mutualDependencyGroup.compare(
7644 786 : otherArg->GetMutualDependencyGroup()) != 0)
7645 783 : continue;
7646 58 : dependencies.push_back(otherArg->GetName());
7647 : }
7648 : }
7649 8161 : return dependencies;
7650 : }
7651 :
7652 : //! @cond Doxygen_Suppress
7653 :
7654 : /************************************************************************/
7655 : /* GDALContainerAlgorithm::RunImpl() */
7656 : /************************************************************************/
7657 :
7658 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7659 : {
7660 0 : return false;
7661 : }
7662 :
7663 : //! @endcond
7664 :
7665 : /************************************************************************/
7666 : /* GDALAlgorithmRelease() */
7667 : /************************************************************************/
7668 :
7669 : /** Release a handle to an algorithm.
7670 : *
7671 : * @since 3.11
7672 : */
7673 13388 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7674 : {
7675 13388 : delete hAlg;
7676 13388 : }
7677 :
7678 : /************************************************************************/
7679 : /* GDALAlgorithmGetName() */
7680 : /************************************************************************/
7681 :
7682 : /** Return the algorithm name.
7683 : *
7684 : * @param hAlg Handle to an algorithm. Must NOT be null.
7685 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7686 : * be freed.
7687 : * @since 3.11
7688 : */
7689 6220 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7690 : {
7691 6220 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7692 6220 : return hAlg->ptr->GetName().c_str();
7693 : }
7694 :
7695 : /************************************************************************/
7696 : /* GDALAlgorithmGetDescription() */
7697 : /************************************************************************/
7698 :
7699 : /** Return the algorithm (short) description.
7700 : *
7701 : * @param hAlg Handle to an algorithm. Must NOT be null.
7702 : * @return algorithm description whose lifetime is bound to hAlg and which must
7703 : * not be freed.
7704 : * @since 3.11
7705 : */
7706 5988 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7707 : {
7708 5988 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7709 5988 : return hAlg->ptr->GetDescription().c_str();
7710 : }
7711 :
7712 : /************************************************************************/
7713 : /* GDALAlgorithmGetLongDescription() */
7714 : /************************************************************************/
7715 :
7716 : /** Return the algorithm (longer) description.
7717 : *
7718 : * @param hAlg Handle to an algorithm. Must NOT be null.
7719 : * @return algorithm description whose lifetime is bound to hAlg and which must
7720 : * not be freed.
7721 : * @since 3.11
7722 : */
7723 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7724 : {
7725 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7726 2 : return hAlg->ptr->GetLongDescription().c_str();
7727 : }
7728 :
7729 : /************************************************************************/
7730 : /* GDALAlgorithmGetHelpFullURL() */
7731 : /************************************************************************/
7732 :
7733 : /** Return the algorithm full URL.
7734 : *
7735 : * @param hAlg Handle to an algorithm. Must NOT be null.
7736 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7737 : * not be freed.
7738 : * @since 3.11
7739 : */
7740 5250 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7741 : {
7742 5250 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7743 5250 : return hAlg->ptr->GetHelpFullURL().c_str();
7744 : }
7745 :
7746 : /************************************************************************/
7747 : /* GDALAlgorithmHasSubAlgorithms() */
7748 : /************************************************************************/
7749 :
7750 : /** Return whether the algorithm has sub-algorithms.
7751 : *
7752 : * @param hAlg Handle to an algorithm. Must NOT be null.
7753 : * @since 3.11
7754 : */
7755 9892 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7756 : {
7757 9892 : VALIDATE_POINTER1(hAlg, __func__, false);
7758 9892 : return hAlg->ptr->HasSubAlgorithms();
7759 : }
7760 :
7761 : /************************************************************************/
7762 : /* GDALAlgorithmGetSubAlgorithmNames() */
7763 : /************************************************************************/
7764 :
7765 : /** Get the names of registered algorithms.
7766 : *
7767 : * @param hAlg Handle to an algorithm. Must NOT be null.
7768 : * @return a NULL terminated list of names, which must be destroyed with
7769 : * CSLDestroy()
7770 : * @since 3.11
7771 : */
7772 938 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7773 : {
7774 938 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7775 938 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7776 : }
7777 :
7778 : /************************************************************************/
7779 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7780 : /************************************************************************/
7781 :
7782 : /** Instantiate an algorithm by its name (or its alias).
7783 : *
7784 : * @param hAlg Handle to an algorithm. Must NOT be null.
7785 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7786 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7787 : * or NULL if the algorithm does not exist or another error occurred.
7788 : * @since 3.11
7789 : */
7790 9320 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7791 : const char *pszSubAlgName)
7792 : {
7793 9320 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7794 9320 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7795 18640 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7796 : return subAlg
7797 18640 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7798 18640 : : nullptr;
7799 : }
7800 :
7801 : /************************************************************************/
7802 : /* GDALAlgorithmParseCommandLineArguments() */
7803 : /************************************************************************/
7804 :
7805 : /** Parse a command line argument, which does not include the algorithm
7806 : * name, to set the value of corresponding arguments.
7807 : *
7808 : * @param hAlg Handle to an algorithm. Must NOT be null.
7809 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7810 : * @return true if successful, false otherwise
7811 : * @since 3.11
7812 : */
7813 :
7814 356 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7815 : CSLConstList papszArgs)
7816 : {
7817 356 : VALIDATE_POINTER1(hAlg, __func__, false);
7818 356 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7819 : }
7820 :
7821 : /************************************************************************/
7822 : /* GDALAlgorithmGetActualAlgorithm() */
7823 : /************************************************************************/
7824 :
7825 : /** Return the actual algorithm that is going to be invoked, when the
7826 : * current algorithm has sub-algorithms.
7827 : *
7828 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7829 : *
7830 : * Note that the lifetime of the returned algorithm does not exceed the one of
7831 : * the hAlg instance that owns it.
7832 : *
7833 : * @param hAlg Handle to an algorithm. Must NOT be null.
7834 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7835 : * @since 3.11
7836 : */
7837 952 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7838 : {
7839 952 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7840 952 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7841 : }
7842 :
7843 : /************************************************************************/
7844 : /* GDALAlgorithmRun() */
7845 : /************************************************************************/
7846 :
7847 : /** Execute the algorithm, starting with ValidateArguments() and then
7848 : * calling RunImpl().
7849 : *
7850 : * This function must be called at most once per instance.
7851 : *
7852 : * @param hAlg Handle to an algorithm. Must NOT be null.
7853 : * @param pfnProgress Progress callback. May be null.
7854 : * @param pProgressData Progress callback user data. May be null.
7855 : * @return true if successful, false otherwise
7856 : * @since 3.11
7857 : */
7858 :
7859 2939 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7860 : void *pProgressData)
7861 : {
7862 2939 : VALIDATE_POINTER1(hAlg, __func__, false);
7863 2939 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7864 : }
7865 :
7866 : /************************************************************************/
7867 : /* GDALAlgorithmFinalize() */
7868 : /************************************************************************/
7869 :
7870 : /** Complete any pending actions, and return the final status.
7871 : * This is typically useful for algorithm that generate an output dataset.
7872 : *
7873 : * Note that this function does *NOT* release memory associated with the
7874 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7875 : *
7876 : * @param hAlg Handle to an algorithm. Must NOT be null.
7877 : * @return true if successful, false otherwise
7878 : * @since 3.11
7879 : */
7880 :
7881 971 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7882 : {
7883 971 : VALIDATE_POINTER1(hAlg, __func__, false);
7884 971 : return hAlg->ptr->Finalize();
7885 : }
7886 :
7887 : /************************************************************************/
7888 : /* GDALAlgorithmGetUsageAsJSON() */
7889 : /************************************************************************/
7890 :
7891 : /** Return the usage of the algorithm as a JSON-serialized string.
7892 : *
7893 : * This can be used to dynamically generate interfaces to algorithms.
7894 : *
7895 : * @param hAlg Handle to an algorithm. Must NOT be null.
7896 : * @return a string that must be freed with CPLFree()
7897 : * @since 3.11
7898 : */
7899 6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7900 : {
7901 6 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7902 6 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7903 : }
7904 :
7905 : /************************************************************************/
7906 : /* GDALAlgorithmGetArgNames() */
7907 : /************************************************************************/
7908 :
7909 : /** Return the list of available argument names.
7910 : *
7911 : * @param hAlg Handle to an algorithm. Must NOT be null.
7912 : * @return a NULL terminated list of names, which must be destroyed with
7913 : * CSLDestroy()
7914 : * @since 3.11
7915 : */
7916 16354 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7917 : {
7918 16354 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7919 32708 : CPLStringList list;
7920 364164 : for (const auto &arg : hAlg->ptr->GetArgs())
7921 347810 : list.AddString(arg->GetName().c_str());
7922 16354 : return list.StealList();
7923 : }
7924 :
7925 : /************************************************************************/
7926 : /* GDALAlgorithmGetArg() */
7927 : /************************************************************************/
7928 :
7929 : /** Return an argument from its name.
7930 : *
7931 : * The lifetime of the returned object does not exceed the one of hAlg.
7932 : *
7933 : * @param hAlg Handle to an algorithm. Must NOT be null.
7934 : * @param pszArgName Argument name. Must NOT be null.
7935 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7936 : * or nullptr in case of error
7937 : * @since 3.11
7938 : */
7939 348886 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7940 : const char *pszArgName)
7941 : {
7942 348886 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7943 348886 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7944 697772 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7945 348886 : /* isConst = */ true);
7946 348886 : if (!arg)
7947 3 : return nullptr;
7948 348883 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7949 : }
7950 :
7951 : /************************************************************************/
7952 : /* GDALAlgorithmGetArgNonConst() */
7953 : /************************************************************************/
7954 :
7955 : /** Return an argument from its name, possibly allowing creation of user-provided
7956 : * argument if the algorithm allow it.
7957 : *
7958 : * The lifetime of the returned object does not exceed the one of hAlg.
7959 : *
7960 : * @param hAlg Handle to an algorithm. Must NOT be null.
7961 : * @param pszArgName Argument name. Must NOT be null.
7962 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7963 : * or nullptr in case of error
7964 : * @since 3.12
7965 : */
7966 10462 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7967 : const char *pszArgName)
7968 : {
7969 10462 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7970 10462 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7971 20924 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7972 10462 : /* isConst = */ false);
7973 10462 : if (!arg)
7974 2 : return nullptr;
7975 10460 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7976 : }
7977 :
7978 : /************************************************************************/
7979 : /* GDALAlgorithmGetArgDependencies() */
7980 : /************************************************************************/
7981 :
7982 : /** Return the list of argument names the specified argument depends on.
7983 : *
7984 : * This includes both regular dependencies and mutual dependencies.
7985 : *
7986 : * @param hAlg Handle to an algorithm. Must NOT be null.
7987 : * @param pszArgName Argument name. Must NOT be null.
7988 : * @return a NULL terminated list of names, which must be destroyed with
7989 : * CSLDestroy()
7990 : * @since 3.11
7991 : */
7992 7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
7993 : const char *pszArgName)
7994 : {
7995 7 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7996 7 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7997 7 : return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
7998 : }
7999 :
8000 : /************************************************************************/
8001 : /* GDALAlgorithmArgRelease() */
8002 : /************************************************************************/
8003 :
8004 : /** Release a handle to an argument.
8005 : *
8006 : * @since 3.11
8007 : */
8008 359343 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
8009 : {
8010 359343 : delete hArg;
8011 359343 : }
8012 :
8013 : /************************************************************************/
8014 : /* GDALAlgorithmArgGetName() */
8015 : /************************************************************************/
8016 :
8017 : /** Return the name of an argument.
8018 : *
8019 : * @param hArg Handle to an argument. Must NOT be null.
8020 : * @return argument name whose lifetime is bound to hArg and which must not
8021 : * be freed.
8022 : * @since 3.11
8023 : */
8024 19913 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
8025 : {
8026 19913 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8027 19913 : return hArg->ptr->GetName().c_str();
8028 : }
8029 :
8030 : /************************************************************************/
8031 : /* GDALAlgorithmArgGetType() */
8032 : /************************************************************************/
8033 :
8034 : /** Get the type of an argument
8035 : *
8036 : * @param hArg Handle to an argument. Must NOT be null.
8037 : * @since 3.11
8038 : */
8039 436431 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
8040 : {
8041 436431 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
8042 436431 : return hArg->ptr->GetType();
8043 : }
8044 :
8045 : /************************************************************************/
8046 : /* GDALAlgorithmArgGetDescription() */
8047 : /************************************************************************/
8048 :
8049 : /** Return the description of an argument.
8050 : *
8051 : * @param hArg Handle to an argument. Must NOT be null.
8052 : * @return argument description whose lifetime is bound to hArg and which must not
8053 : * be freed.
8054 : * @since 3.11
8055 : */
8056 86593 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
8057 : {
8058 86593 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8059 86593 : return hArg->ptr->GetDescription().c_str();
8060 : }
8061 :
8062 : /************************************************************************/
8063 : /* GDALAlgorithmArgGetShortName() */
8064 : /************************************************************************/
8065 :
8066 : /** Return the short name, or empty string if there is none
8067 : *
8068 : * @param hArg Handle to an argument. Must NOT be null.
8069 : * @return short name whose lifetime is bound to hArg and which must not
8070 : * be freed.
8071 : * @since 3.11
8072 : */
8073 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
8074 : {
8075 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8076 1 : return hArg->ptr->GetShortName().c_str();
8077 : }
8078 :
8079 : /************************************************************************/
8080 : /* GDALAlgorithmArgGetAliases() */
8081 : /************************************************************************/
8082 :
8083 : /** Return the aliases (potentially none)
8084 : *
8085 : * @param hArg Handle to an argument. Must NOT be null.
8086 : * @return a NULL terminated list of names, which must be destroyed with
8087 : * CSLDestroy()
8088 :
8089 : * @since 3.11
8090 : */
8091 163509 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
8092 : {
8093 163509 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8094 163509 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
8095 : }
8096 :
8097 : /************************************************************************/
8098 : /* GDALAlgorithmArgGetMetaVar() */
8099 : /************************************************************************/
8100 :
8101 : /** Return the "meta-var" hint.
8102 : *
8103 : * By default, the meta-var value is the long name of the argument in
8104 : * upper case.
8105 : *
8106 : * @param hArg Handle to an argument. Must NOT be null.
8107 : * @return meta-var hint whose lifetime is bound to hArg and which must not
8108 : * be freed.
8109 : * @since 3.11
8110 : */
8111 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
8112 : {
8113 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8114 1 : return hArg->ptr->GetMetaVar().c_str();
8115 : }
8116 :
8117 : /************************************************************************/
8118 : /* GDALAlgorithmArgGetCategory() */
8119 : /************************************************************************/
8120 :
8121 : /** Return the argument category
8122 : *
8123 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
8124 : *
8125 : * @param hArg Handle to an argument. Must NOT be null.
8126 : * @return category whose lifetime is bound to hArg and which must not
8127 : * be freed.
8128 : * @since 3.11
8129 : */
8130 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
8131 : {
8132 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8133 1 : return hArg->ptr->GetCategory().c_str();
8134 : }
8135 :
8136 : /************************************************************************/
8137 : /* GDALAlgorithmArgIsPositional() */
8138 : /************************************************************************/
8139 :
8140 : /** Return if the argument is a positional one.
8141 : *
8142 : * @param hArg Handle to an argument. Must NOT be null.
8143 : * @since 3.11
8144 : */
8145 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
8146 : {
8147 1 : VALIDATE_POINTER1(hArg, __func__, false);
8148 1 : return hArg->ptr->IsPositional();
8149 : }
8150 :
8151 : /************************************************************************/
8152 : /* GDALAlgorithmArgIsRequired() */
8153 : /************************************************************************/
8154 :
8155 : /** Return whether the argument is required. Defaults to false.
8156 : *
8157 : * @param hArg Handle to an argument. Must NOT be null.
8158 : * @since 3.11
8159 : */
8160 163509 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
8161 : {
8162 163509 : VALIDATE_POINTER1(hArg, __func__, false);
8163 163509 : return hArg->ptr->IsRequired();
8164 : }
8165 :
8166 : /************************************************************************/
8167 : /* GDALAlgorithmArgGetMinCount() */
8168 : /************************************************************************/
8169 :
8170 : /** Return the minimum number of values for the argument.
8171 : *
8172 : * Defaults to 0.
8173 : * Only applies to list type of arguments.
8174 : *
8175 : * @param hArg Handle to an argument. Must NOT be null.
8176 : * @since 3.11
8177 : */
8178 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
8179 : {
8180 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8181 1 : return hArg->ptr->GetMinCount();
8182 : }
8183 :
8184 : /************************************************************************/
8185 : /* GDALAlgorithmArgGetMaxCount() */
8186 : /************************************************************************/
8187 :
8188 : /** Return the maximum number of values for the argument.
8189 : *
8190 : * Defaults to 1 for scalar types, and INT_MAX for list types.
8191 : * Only applies to list type of arguments.
8192 : *
8193 : * @param hArg Handle to an argument. Must NOT be null.
8194 : * @since 3.11
8195 : */
8196 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
8197 : {
8198 1 : VALIDATE_POINTER1(hArg, __func__, 0);
8199 1 : return hArg->ptr->GetMaxCount();
8200 : }
8201 :
8202 : /************************************************************************/
8203 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
8204 : /************************************************************************/
8205 :
8206 : /** Return whether, for list type of arguments, several values, space
8207 : * separated, may be specified. That is "--foo=bar,baz".
8208 : * The default is true.
8209 : *
8210 : * @param hArg Handle to an argument. Must NOT be null.
8211 : * @since 3.11
8212 : */
8213 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
8214 : {
8215 1 : VALIDATE_POINTER1(hArg, __func__, false);
8216 1 : return hArg->ptr->GetPackedValuesAllowed();
8217 : }
8218 :
8219 : /************************************************************************/
8220 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
8221 : /************************************************************************/
8222 :
8223 : /** Return whether, for list type of arguments, the argument may be
8224 : * repeated. That is "--foo=bar --foo=baz".
8225 : * The default is true.
8226 : *
8227 : * @param hArg Handle to an argument. Must NOT be null.
8228 : * @since 3.11
8229 : */
8230 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
8231 : {
8232 1 : VALIDATE_POINTER1(hArg, __func__, false);
8233 1 : return hArg->ptr->GetRepeatedArgAllowed();
8234 : }
8235 :
8236 : /************************************************************************/
8237 : /* GDALAlgorithmArgGetChoices() */
8238 : /************************************************************************/
8239 :
8240 : /** Return the allowed values (as strings) for the argument.
8241 : *
8242 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
8243 : *
8244 : * @param hArg Handle to an argument. Must NOT be null.
8245 : * @return a NULL terminated list of names, which must be destroyed with
8246 : * CSLDestroy()
8247 :
8248 : * @since 3.11
8249 : */
8250 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
8251 : {
8252 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8253 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
8254 : }
8255 :
8256 : /************************************************************************/
8257 : /* GDALAlgorithmArgGetMetadataItem() */
8258 : /************************************************************************/
8259 :
8260 : /** Return the values of the metadata item of an argument.
8261 : *
8262 : * @param hArg Handle to an argument. Must NOT be null.
8263 : * @param pszItem Name of the item. Must NOT be null.
8264 : * @return a NULL terminated list of values, which must be destroyed with
8265 : * CSLDestroy()
8266 :
8267 : * @since 3.11
8268 : */
8269 79 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
8270 : const char *pszItem)
8271 : {
8272 79 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8273 79 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
8274 79 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
8275 79 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
8276 : }
8277 :
8278 : /************************************************************************/
8279 : /* GDALAlgorithmArgIsExplicitlySet() */
8280 : /************************************************************************/
8281 :
8282 : /** Return whether the argument value has been explicitly set with Set()
8283 : *
8284 : * @param hArg Handle to an argument. Must NOT be null.
8285 : * @since 3.11
8286 : */
8287 726 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
8288 : {
8289 726 : VALIDATE_POINTER1(hArg, __func__, false);
8290 726 : return hArg->ptr->IsExplicitlySet();
8291 : }
8292 :
8293 : /************************************************************************/
8294 : /* GDALAlgorithmArgHasDefaultValue() */
8295 : /************************************************************************/
8296 :
8297 : /** Return if the argument has a declared default value.
8298 : *
8299 : * @param hArg Handle to an argument. Must NOT be null.
8300 : * @since 3.11
8301 : */
8302 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
8303 : {
8304 2 : VALIDATE_POINTER1(hArg, __func__, false);
8305 2 : return hArg->ptr->HasDefaultValue();
8306 : }
8307 :
8308 : /************************************************************************/
8309 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
8310 : /************************************************************************/
8311 :
8312 : /** Return the argument default value as a integer.
8313 : *
8314 : * Must only be called on arguments whose type is GAAT_BOOLEAN
8315 : *
8316 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8317 : * argument has a default value.
8318 : *
8319 : * @param hArg Handle to an argument. Must NOT be null.
8320 : * @since 3.12
8321 : */
8322 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
8323 : {
8324 3 : VALIDATE_POINTER1(hArg, __func__, false);
8325 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8326 : {
8327 1 : CPLError(CE_Failure, CPLE_AppDefined,
8328 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8329 : __func__);
8330 1 : return false;
8331 : }
8332 2 : return hArg->ptr->GetDefault<bool>();
8333 : }
8334 :
8335 : /************************************************************************/
8336 : /* GDALAlgorithmArgGetDefaultAsString() */
8337 : /************************************************************************/
8338 :
8339 : /** Return the argument default value as a string.
8340 : *
8341 : * Must only be called on arguments whose type is GAAT_STRING.
8342 : *
8343 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8344 : * argument has a default value.
8345 : *
8346 : * @param hArg Handle to an argument. Must NOT be null.
8347 : * @return string whose lifetime is bound to hArg and which must not
8348 : * be freed.
8349 : * @since 3.11
8350 : */
8351 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
8352 : {
8353 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8354 3 : if (hArg->ptr->GetType() != GAAT_STRING)
8355 : {
8356 2 : CPLError(CE_Failure, CPLE_AppDefined,
8357 : "%s must only be called on arguments of type GAAT_STRING",
8358 : __func__);
8359 2 : return nullptr;
8360 : }
8361 1 : return hArg->ptr->GetDefault<std::string>().c_str();
8362 : }
8363 :
8364 : /************************************************************************/
8365 : /* GDALAlgorithmArgGetDefaultAsInteger() */
8366 : /************************************************************************/
8367 :
8368 : /** Return the argument default value as a integer.
8369 : *
8370 : * Must only be called on arguments whose type is GAAT_INTEGER
8371 : *
8372 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8373 : * argument has a default value.
8374 : *
8375 : * @param hArg Handle to an argument. Must NOT be null.
8376 : * @since 3.12
8377 : */
8378 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
8379 : {
8380 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8381 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8382 : {
8383 2 : CPLError(CE_Failure, CPLE_AppDefined,
8384 : "%s must only be called on arguments of type GAAT_INTEGER",
8385 : __func__);
8386 2 : return 0;
8387 : }
8388 1 : return hArg->ptr->GetDefault<int>();
8389 : }
8390 :
8391 : /************************************************************************/
8392 : /* GDALAlgorithmArgGetDefaultAsDouble() */
8393 : /************************************************************************/
8394 :
8395 : /** Return the argument default value as a double.
8396 : *
8397 : * Must only be called on arguments whose type is GAAT_REAL
8398 : *
8399 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8400 : * argument has a default value.
8401 : *
8402 : * @param hArg Handle to an argument. Must NOT be null.
8403 : * @since 3.12
8404 : */
8405 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
8406 : {
8407 3 : VALIDATE_POINTER1(hArg, __func__, 0);
8408 3 : if (hArg->ptr->GetType() != GAAT_REAL)
8409 : {
8410 2 : CPLError(CE_Failure, CPLE_AppDefined,
8411 : "%s must only be called on arguments of type GAAT_REAL",
8412 : __func__);
8413 2 : return 0;
8414 : }
8415 1 : return hArg->ptr->GetDefault<double>();
8416 : }
8417 :
8418 : /************************************************************************/
8419 : /* GDALAlgorithmArgGetDefaultAsStringList() */
8420 : /************************************************************************/
8421 :
8422 : /** Return the argument default value as a string list.
8423 : *
8424 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8425 : *
8426 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8427 : * argument has a default value.
8428 : *
8429 : * @param hArg Handle to an argument. Must NOT be null.
8430 : * @return a NULL terminated list of names, which must be destroyed with
8431 : * CSLDestroy()
8432 :
8433 : * @since 3.12
8434 : */
8435 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8436 : {
8437 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8438 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8439 : {
8440 2 : CPLError(CE_Failure, CPLE_AppDefined,
8441 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8442 : __func__);
8443 2 : return nullptr;
8444 : }
8445 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8446 1 : .StealList();
8447 : }
8448 :
8449 : /************************************************************************/
8450 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8451 : /************************************************************************/
8452 :
8453 : /** Return the argument default value as a integer list.
8454 : *
8455 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8456 : *
8457 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8458 : * argument has a default value.
8459 : *
8460 : * @param hArg Handle to an argument. Must NOT be null.
8461 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8462 : * @since 3.12
8463 : */
8464 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8465 : size_t *pnCount)
8466 : {
8467 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8468 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8469 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8470 : {
8471 2 : CPLError(
8472 : CE_Failure, CPLE_AppDefined,
8473 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8474 : __func__);
8475 2 : *pnCount = 0;
8476 2 : return nullptr;
8477 : }
8478 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8479 1 : *pnCount = val.size();
8480 1 : return val.data();
8481 : }
8482 :
8483 : /************************************************************************/
8484 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8485 : /************************************************************************/
8486 :
8487 : /** Return the argument default value as a real list.
8488 : *
8489 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8490 : *
8491 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8492 : * argument has a default value.
8493 : *
8494 : * @param hArg Handle to an argument. Must NOT be null.
8495 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8496 : * @since 3.12
8497 : */
8498 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8499 : size_t *pnCount)
8500 : {
8501 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8502 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8503 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8504 : {
8505 2 : CPLError(CE_Failure, CPLE_AppDefined,
8506 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8507 : __func__);
8508 2 : *pnCount = 0;
8509 2 : return nullptr;
8510 : }
8511 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8512 1 : *pnCount = val.size();
8513 1 : return val.data();
8514 : }
8515 :
8516 : /************************************************************************/
8517 : /* GDALAlgorithmArgIsHidden() */
8518 : /************************************************************************/
8519 :
8520 : /** Return whether the argument is hidden (for GDAL internal use)
8521 : *
8522 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8523 : * GDALAlgorithmArgIsHiddenForAPI().
8524 : *
8525 : * @param hArg Handle to an argument. Must NOT be null.
8526 : * @since 3.12
8527 : */
8528 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8529 : {
8530 1 : VALIDATE_POINTER1(hArg, __func__, false);
8531 1 : return hArg->ptr->IsHidden();
8532 : }
8533 :
8534 : /************************************************************************/
8535 : /* GDALAlgorithmArgIsHiddenForCLI() */
8536 : /************************************************************************/
8537 :
8538 : /** Return whether the argument must not be mentioned in CLI usage.
8539 : *
8540 : * For example, "output-value" for "gdal raster info", which is only
8541 : * meant when the algorithm is used from a non-CLI context.
8542 : *
8543 : * @param hArg Handle to an argument. Must NOT be null.
8544 : * @since 3.11
8545 : */
8546 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8547 : {
8548 1 : VALIDATE_POINTER1(hArg, __func__, false);
8549 1 : return hArg->ptr->IsHiddenForCLI();
8550 : }
8551 :
8552 : /************************************************************************/
8553 : /* GDALAlgorithmArgIsHiddenForAPI() */
8554 : /************************************************************************/
8555 :
8556 : /** Return whether the argument must not be mentioned in the context of an
8557 : * API use.
8558 : * Said otherwise, if it is only for CLI usage.
8559 : *
8560 : * For example "--help"
8561 : *
8562 : * @param hArg Handle to an argument. Must NOT be null.
8563 : * @since 3.12
8564 : */
8565 225747 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8566 : {
8567 225747 : VALIDATE_POINTER1(hArg, __func__, false);
8568 225747 : return hArg->ptr->IsHiddenForAPI();
8569 : }
8570 :
8571 : /************************************************************************/
8572 : /* GDALAlgorithmArgIsOnlyForCLI() */
8573 : /************************************************************************/
8574 :
8575 : /** Return whether the argument must not be mentioned in the context of an
8576 : * API use.
8577 : * Said otherwise, if it is only for CLI usage.
8578 : *
8579 : * For example "--help"
8580 : *
8581 : * @param hArg Handle to an argument. Must NOT be null.
8582 : * @since 3.11
8583 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8584 : */
8585 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8586 : {
8587 0 : VALIDATE_POINTER1(hArg, __func__, false);
8588 0 : return hArg->ptr->IsHiddenForAPI();
8589 : }
8590 :
8591 : /************************************************************************/
8592 : /* GDALAlgorithmArgIsAvailableInPipelineStep() */
8593 : /************************************************************************/
8594 :
8595 : /** Return whether the argument is available in a pipeline step.
8596 : *
8597 : * If false, it is only available in standalone mode.
8598 : *
8599 : * @param hArg Handle to an argument. Must NOT be null.
8600 : * @since 3.13
8601 : */
8602 2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
8603 : {
8604 2 : VALIDATE_POINTER1(hArg, __func__, false);
8605 2 : return hArg->ptr->IsAvailableInPipelineStep();
8606 : }
8607 :
8608 : /************************************************************************/
8609 : /* GDALAlgorithmArgIsInput() */
8610 : /************************************************************************/
8611 :
8612 : /** Indicate whether the value of the argument is read-only during the
8613 : * execution of the algorithm.
8614 : *
8615 : * Default is true.
8616 : *
8617 : * @param hArg Handle to an argument. Must NOT be null.
8618 : * @since 3.11
8619 : */
8620 222959 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8621 : {
8622 222959 : VALIDATE_POINTER1(hArg, __func__, false);
8623 222959 : return hArg->ptr->IsInput();
8624 : }
8625 :
8626 : /************************************************************************/
8627 : /* GDALAlgorithmArgIsOutput() */
8628 : /************************************************************************/
8629 :
8630 : /** Return whether (at least part of) the value of the argument is set
8631 : * during the execution of the algorithm.
8632 : *
8633 : * For example, "output-value" for "gdal raster info"
8634 : * Default is false.
8635 : * An argument may return both IsInput() and IsOutput() as true.
8636 : * For example the "gdal raster convert" algorithm consumes the dataset
8637 : * name of its "output" argument, and sets the dataset object during its
8638 : * execution.
8639 : *
8640 : * @param hArg Handle to an argument. Must NOT be null.
8641 : * @since 3.11
8642 : */
8643 124827 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8644 : {
8645 124827 : VALIDATE_POINTER1(hArg, __func__, false);
8646 124827 : return hArg->ptr->IsOutput();
8647 : }
8648 :
8649 : /************************************************************************/
8650 : /* GDALAlgorithmArgGetDatasetType() */
8651 : /************************************************************************/
8652 :
8653 : /** Get which type of dataset is allowed / generated.
8654 : *
8655 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8656 : * GDAL_OF_MULTIDIM_RASTER.
8657 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8658 : *
8659 : * @param hArg Handle to an argument. Must NOT be null.
8660 : * @since 3.11
8661 : */
8662 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8663 : {
8664 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8665 2 : return hArg->ptr->GetDatasetType();
8666 : }
8667 :
8668 : /************************************************************************/
8669 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8670 : /************************************************************************/
8671 :
8672 : /** Indicates which components among name and dataset are accepted as
8673 : * input, when this argument serves as an input.
8674 : *
8675 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8676 : * input.
8677 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8678 : * accepted as input.
8679 : * If both bits are set, the algorithm can accept either a name or a dataset
8680 : * object.
8681 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8682 : *
8683 : * @param hArg Handle to an argument. Must NOT be null.
8684 : * @return string whose lifetime is bound to hAlg and which must not
8685 : * be freed.
8686 : * @since 3.11
8687 : */
8688 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8689 : {
8690 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8691 2 : return hArg->ptr->GetDatasetInputFlags();
8692 : }
8693 :
8694 : /************************************************************************/
8695 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8696 : /************************************************************************/
8697 :
8698 : /** Indicates which components among name and dataset are modified,
8699 : * when this argument serves as an output.
8700 : *
8701 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8702 : * output (that is the algorithm will generate the name. Rarely used).
8703 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8704 : * generated as output, and available for use after the algorithm has
8705 : * completed.
8706 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8707 : *
8708 : * @param hArg Handle to an argument. Must NOT be null.
8709 : * @return string whose lifetime is bound to hAlg and which must not
8710 : * be freed.
8711 : * @since 3.11
8712 : */
8713 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8714 : {
8715 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8716 2 : return hArg->ptr->GetDatasetOutputFlags();
8717 : }
8718 :
8719 : /************************************************************************/
8720 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8721 : /************************************************************************/
8722 :
8723 : /** Return the name of the mutual exclusion group to which this argument
8724 : * belongs to.
8725 : *
8726 : * Or empty string if it does not belong to any exclusion group.
8727 : *
8728 : * @param hArg Handle to an argument. Must NOT be null.
8729 : * @return string whose lifetime is bound to hArg and which must not
8730 : * be freed.
8731 : * @since 3.11
8732 : */
8733 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8734 : {
8735 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8736 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8737 : }
8738 :
8739 : /************************************************************************/
8740 : /* GDALAlgorithmArgGetMutualDependencyGroup() */
8741 : /************************************************************************/
8742 :
8743 : /** Return the name of the mutual dependency group to which this argument
8744 : * belongs to.
8745 : *
8746 : * Or empty string if it does not belong to any dependency group.
8747 : *
8748 : * @param hArg Handle to an argument. Must NOT be null.
8749 : * @return string whose lifetime is bound to hArg and which must not
8750 : * be freed.
8751 : * @since 3.13
8752 : */
8753 5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
8754 : {
8755 5 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8756 5 : return hArg->ptr->GetMutualDependencyGroup().c_str();
8757 : }
8758 :
8759 : /************************************************************************/
8760 : /* GDALAlgorithmArgGetDirectDependencies() */
8761 : /************************************************************************/
8762 :
8763 : /** Return the list of names of arguments that this argument depends on.
8764 : *
8765 : * This is not necessarily a symmetric relationship.
8766 : * If argument A depends on argument B, it doesn't mean that B depends on A.
8767 : * Mutual dependency groups are a special case of dependencies,
8768 : * where all arguments of the group depend on each other and are not
8769 : * returned by this method.
8770 : *
8771 : * @param hArg Handle to an argument. Must NOT be null.
8772 : * @return a NULL terminated list of names, which must be destroyed with
8773 : * CSLDestroy()
8774 : * @since 3.13
8775 : */
8776 7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
8777 : {
8778 7 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8779 7 : return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
8780 : }
8781 :
8782 : /************************************************************************/
8783 : /* GDALAlgorithmArgGetAsBoolean() */
8784 : /************************************************************************/
8785 :
8786 : /** Return the argument value as a boolean.
8787 : *
8788 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8789 : *
8790 : * @param hArg Handle to an argument. Must NOT be null.
8791 : * @since 3.11
8792 : */
8793 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8794 : {
8795 8 : VALIDATE_POINTER1(hArg, __func__, false);
8796 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8797 : {
8798 1 : CPLError(CE_Failure, CPLE_AppDefined,
8799 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8800 : __func__);
8801 1 : return false;
8802 : }
8803 7 : return hArg->ptr->Get<bool>();
8804 : }
8805 :
8806 : /************************************************************************/
8807 : /* GDALAlgorithmArgGetAsString() */
8808 : /************************************************************************/
8809 :
8810 : /** Return the argument value as a string.
8811 : *
8812 : * Must only be called on arguments whose type is GAAT_STRING.
8813 : *
8814 : * @param hArg Handle to an argument. Must NOT be null.
8815 : * @return string whose lifetime is bound to hArg and which must not
8816 : * be freed.
8817 : * @since 3.11
8818 : */
8819 377 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8820 : {
8821 377 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8822 377 : if (hArg->ptr->GetType() != GAAT_STRING)
8823 : {
8824 1 : CPLError(CE_Failure, CPLE_AppDefined,
8825 : "%s must only be called on arguments of type GAAT_STRING",
8826 : __func__);
8827 1 : return nullptr;
8828 : }
8829 376 : return hArg->ptr->Get<std::string>().c_str();
8830 : }
8831 :
8832 : /************************************************************************/
8833 : /* GDALAlgorithmArgGetAsDatasetValue() */
8834 : /************************************************************************/
8835 :
8836 : /** Return the argument value as a GDALArgDatasetValueH.
8837 : *
8838 : * Must only be called on arguments whose type is GAAT_DATASET
8839 : *
8840 : * @param hArg Handle to an argument. Must NOT be null.
8841 : * @return handle to a GDALArgDatasetValue that must be released with
8842 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8843 : * the one of hArg.
8844 : * @since 3.11
8845 : */
8846 3323 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8847 : {
8848 3323 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8849 3323 : if (hArg->ptr->GetType() != GAAT_DATASET)
8850 : {
8851 1 : CPLError(CE_Failure, CPLE_AppDefined,
8852 : "%s must only be called on arguments of type GAAT_DATASET",
8853 : __func__);
8854 1 : return nullptr;
8855 : }
8856 3322 : return std::make_unique<GDALArgDatasetValueHS>(
8857 6644 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8858 3322 : .release();
8859 : }
8860 :
8861 : /************************************************************************/
8862 : /* GDALAlgorithmArgGetAsInteger() */
8863 : /************************************************************************/
8864 :
8865 : /** Return the argument value as a integer.
8866 : *
8867 : * Must only be called on arguments whose type is GAAT_INTEGER
8868 : *
8869 : * @param hArg Handle to an argument. Must NOT be null.
8870 : * @since 3.11
8871 : */
8872 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8873 : {
8874 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8875 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8876 : {
8877 1 : CPLError(CE_Failure, CPLE_AppDefined,
8878 : "%s must only be called on arguments of type GAAT_INTEGER",
8879 : __func__);
8880 1 : return 0;
8881 : }
8882 25 : return hArg->ptr->Get<int>();
8883 : }
8884 :
8885 : /************************************************************************/
8886 : /* GDALAlgorithmArgGetAsDouble() */
8887 : /************************************************************************/
8888 :
8889 : /** Return the argument value as a double.
8890 : *
8891 : * Must only be called on arguments whose type is GAAT_REAL
8892 : *
8893 : * @param hArg Handle to an argument. Must NOT be null.
8894 : * @since 3.11
8895 : */
8896 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8897 : {
8898 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8899 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8900 : {
8901 1 : CPLError(CE_Failure, CPLE_AppDefined,
8902 : "%s must only be called on arguments of type GAAT_REAL",
8903 : __func__);
8904 1 : return 0;
8905 : }
8906 7 : return hArg->ptr->Get<double>();
8907 : }
8908 :
8909 : /************************************************************************/
8910 : /* GDALAlgorithmArgGetAsStringList() */
8911 : /************************************************************************/
8912 :
8913 : /** Return the argument value as a string list.
8914 : *
8915 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8916 : *
8917 : * @param hArg Handle to an argument. Must NOT be null.
8918 : * @return a NULL terminated list of names, which must be destroyed with
8919 : * CSLDestroy()
8920 :
8921 : * @since 3.11
8922 : */
8923 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8924 : {
8925 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8926 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8927 : {
8928 1 : CPLError(CE_Failure, CPLE_AppDefined,
8929 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8930 : __func__);
8931 1 : return nullptr;
8932 : }
8933 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8934 3 : .StealList();
8935 : }
8936 :
8937 : /************************************************************************/
8938 : /* GDALAlgorithmArgGetAsIntegerList() */
8939 : /************************************************************************/
8940 :
8941 : /** Return the argument value as a integer list.
8942 : *
8943 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8944 : *
8945 : * @param hArg Handle to an argument. Must NOT be null.
8946 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8947 : * @since 3.11
8948 : */
8949 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8950 : size_t *pnCount)
8951 : {
8952 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8953 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8954 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8955 : {
8956 1 : CPLError(
8957 : CE_Failure, CPLE_AppDefined,
8958 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8959 : __func__);
8960 1 : *pnCount = 0;
8961 1 : return nullptr;
8962 : }
8963 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8964 7 : *pnCount = val.size();
8965 7 : return val.data();
8966 : }
8967 :
8968 : /************************************************************************/
8969 : /* GDALAlgorithmArgGetAsDoubleList() */
8970 : /************************************************************************/
8971 :
8972 : /** Return the argument value as a real list.
8973 : *
8974 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8975 : *
8976 : * @param hArg Handle to an argument. Must NOT be null.
8977 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8978 : * @since 3.11
8979 : */
8980 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8981 : size_t *pnCount)
8982 : {
8983 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8984 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8985 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8986 : {
8987 1 : CPLError(CE_Failure, CPLE_AppDefined,
8988 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8989 : __func__);
8990 1 : *pnCount = 0;
8991 1 : return nullptr;
8992 : }
8993 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8994 7 : *pnCount = val.size();
8995 7 : return val.data();
8996 : }
8997 :
8998 : /************************************************************************/
8999 : /* GDALAlgorithmArgSetAsBoolean() */
9000 : /************************************************************************/
9001 :
9002 : /** Set the value for a GAAT_BOOLEAN argument.
9003 : *
9004 : * It cannot be called several times for a given argument.
9005 : * Validation checks and other actions are run.
9006 : *
9007 : * @param hArg Handle to an argument. Must NOT be null.
9008 : * @param value value.
9009 : * @return true if success.
9010 : * @since 3.11
9011 : */
9012 :
9013 756 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
9014 : {
9015 756 : VALIDATE_POINTER1(hArg, __func__, false);
9016 756 : return hArg->ptr->Set(value);
9017 : }
9018 :
9019 : /************************************************************************/
9020 : /* GDALAlgorithmArgSetAsString() */
9021 : /************************************************************************/
9022 :
9023 : /** Set the value for a GAAT_STRING argument.
9024 : *
9025 : * It cannot be called several times for a given argument.
9026 : * Validation checks and other actions are run.
9027 : *
9028 : * @param hArg Handle to an argument. Must NOT be null.
9029 : * @param value value (may be null)
9030 : * @return true if success.
9031 : * @since 3.11
9032 : */
9033 :
9034 3350 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
9035 : {
9036 3350 : VALIDATE_POINTER1(hArg, __func__, false);
9037 3350 : return hArg->ptr->Set(value ? value : "");
9038 : }
9039 :
9040 : /************************************************************************/
9041 : /* GDALAlgorithmArgSetAsInteger() */
9042 : /************************************************************************/
9043 :
9044 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
9045 : *
9046 : * It cannot be called several times for a given argument.
9047 : * Validation checks and other actions are run.
9048 : *
9049 : * @param hArg Handle to an argument. Must NOT be null.
9050 : * @param value value.
9051 : * @return true if success.
9052 : * @since 3.11
9053 : */
9054 :
9055 489 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
9056 : {
9057 489 : VALIDATE_POINTER1(hArg, __func__, false);
9058 489 : return hArg->ptr->Set(value);
9059 : }
9060 :
9061 : /************************************************************************/
9062 : /* GDALAlgorithmArgSetAsDouble() */
9063 : /************************************************************************/
9064 :
9065 : /** Set the value for a GAAT_REAL argument.
9066 : *
9067 : * It cannot be called several times for a given argument.
9068 : * Validation checks and other actions are run.
9069 : *
9070 : * @param hArg Handle to an argument. Must NOT be null.
9071 : * @param value value.
9072 : * @return true if success.
9073 : * @since 3.11
9074 : */
9075 :
9076 263 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
9077 : {
9078 263 : VALIDATE_POINTER1(hArg, __func__, false);
9079 263 : return hArg->ptr->Set(value);
9080 : }
9081 :
9082 : /************************************************************************/
9083 : /* GDALAlgorithmArgSetAsDatasetValue() */
9084 : /************************************************************************/
9085 :
9086 : /** Set the value for a GAAT_DATASET argument.
9087 : *
9088 : * It cannot be called several times for a given argument.
9089 : * Validation checks and other actions are run.
9090 : *
9091 : * @param hArg Handle to an argument. Must NOT be null.
9092 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
9093 : * @return true if success.
9094 : * @since 3.11
9095 : */
9096 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
9097 : GDALArgDatasetValueH value)
9098 : {
9099 2 : VALIDATE_POINTER1(hArg, __func__, false);
9100 2 : VALIDATE_POINTER1(value, __func__, false);
9101 2 : return hArg->ptr->SetFrom(*(value->ptr));
9102 : }
9103 :
9104 : /************************************************************************/
9105 : /* GDALAlgorithmArgSetDataset() */
9106 : /************************************************************************/
9107 :
9108 : /** Set dataset object, increasing its reference counter.
9109 : *
9110 : * @param hArg Handle to an argument. Must NOT be null.
9111 : * @param hDS Dataset object. May be null.
9112 : * @return true if success.
9113 : * @since 3.11
9114 : */
9115 :
9116 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
9117 : {
9118 2 : VALIDATE_POINTER1(hArg, __func__, false);
9119 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
9120 : }
9121 :
9122 : /************************************************************************/
9123 : /* GDALAlgorithmArgSetAsStringList() */
9124 : /************************************************************************/
9125 :
9126 : /** Set the value for a GAAT_STRING_LIST argument.
9127 : *
9128 : * It cannot be called several times for a given argument.
9129 : * Validation checks and other actions are run.
9130 : *
9131 : * @param hArg Handle to an argument. Must NOT be null.
9132 : * @param value value as a NULL terminated list (may be null)
9133 : * @return true if success.
9134 : * @since 3.11
9135 : */
9136 :
9137 920 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
9138 : {
9139 920 : VALIDATE_POINTER1(hArg, __func__, false);
9140 920 : return hArg->ptr->Set(
9141 1840 : static_cast<std::vector<std::string>>(CPLStringList(value)));
9142 : }
9143 :
9144 : /************************************************************************/
9145 : /* GDALAlgorithmArgSetAsIntegerList() */
9146 : /************************************************************************/
9147 :
9148 : /** Set the value for a GAAT_INTEGER_LIST argument.
9149 : *
9150 : * It cannot be called several times for a given argument.
9151 : * Validation checks and other actions are run.
9152 : *
9153 : * @param hArg Handle to an argument. Must NOT be null.
9154 : * @param nCount Number of values in pnValues.
9155 : * @param pnValues Pointer to an array of integer values of size nCount.
9156 : * @return true if success.
9157 : * @since 3.11
9158 : */
9159 65 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
9160 : const int *pnValues)
9161 : {
9162 65 : VALIDATE_POINTER1(hArg, __func__, false);
9163 65 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
9164 : }
9165 :
9166 : /************************************************************************/
9167 : /* GDALAlgorithmArgSetAsDoubleList() */
9168 : /************************************************************************/
9169 :
9170 : /** Set the value for a GAAT_REAL_LIST argument.
9171 : *
9172 : * It cannot be called several times for a given argument.
9173 : * Validation checks and other actions are run.
9174 : *
9175 : * @param hArg Handle to an argument. Must NOT be null.
9176 : * @param nCount Number of values in pnValues.
9177 : * @param pnValues Pointer to an array of double values of size nCount.
9178 : * @return true if success.
9179 : * @since 3.11
9180 : */
9181 238 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
9182 : const double *pnValues)
9183 : {
9184 238 : VALIDATE_POINTER1(hArg, __func__, false);
9185 238 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
9186 : }
9187 :
9188 : /************************************************************************/
9189 : /* GDALAlgorithmArgSetDatasets() */
9190 : /************************************************************************/
9191 :
9192 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
9193 : *
9194 : * @param hArg Handle to an argument. Must NOT be null.
9195 : * @param nCount Number of values in pnValues.
9196 : * @param pahDS Pointer to an array of dataset of size nCount.
9197 : * @return true if success.
9198 : * @since 3.11
9199 : */
9200 :
9201 1364 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
9202 : GDALDatasetH *pahDS)
9203 : {
9204 1364 : VALIDATE_POINTER1(hArg, __func__, false);
9205 2728 : std::vector<GDALArgDatasetValue> values;
9206 2754 : for (size_t i = 0; i < nCount; ++i)
9207 : {
9208 1390 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
9209 : }
9210 1364 : return hArg->ptr->Set(std::move(values));
9211 : }
9212 :
9213 : /************************************************************************/
9214 : /* GDALAlgorithmArgSetDatasetNames() */
9215 : /************************************************************************/
9216 :
9217 : /** Set dataset names to a GAAT_DATASET_LIST argument.
9218 : *
9219 : * @param hArg Handle to an argument. Must NOT be null.
9220 : * @param names Dataset names as a NULL terminated list (may be null)
9221 : * @return true if success.
9222 : * @since 3.11
9223 : */
9224 :
9225 817 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
9226 : {
9227 817 : VALIDATE_POINTER1(hArg, __func__, false);
9228 1634 : std::vector<GDALArgDatasetValue> values;
9229 1705 : for (size_t i = 0; names[i]; ++i)
9230 : {
9231 888 : values.emplace_back(names[i]);
9232 : }
9233 817 : return hArg->ptr->Set(std::move(values));
9234 : }
9235 :
9236 : /************************************************************************/
9237 : /* GDALArgDatasetValueCreate() */
9238 : /************************************************************************/
9239 :
9240 : /** Instantiate an empty GDALArgDatasetValue
9241 : *
9242 : * @return new handle to free with GDALArgDatasetValueRelease()
9243 : * @since 3.11
9244 : */
9245 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
9246 : {
9247 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
9248 : }
9249 :
9250 : /************************************************************************/
9251 : /* GDALArgDatasetValueRelease() */
9252 : /************************************************************************/
9253 :
9254 : /** Release a handle to a GDALArgDatasetValue
9255 : *
9256 : * @since 3.11
9257 : */
9258 3323 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
9259 : {
9260 3323 : delete hValue;
9261 3323 : }
9262 :
9263 : /************************************************************************/
9264 : /* GDALArgDatasetValueGetName() */
9265 : /************************************************************************/
9266 :
9267 : /** Return the name component of the GDALArgDatasetValue
9268 : *
9269 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9270 : * @return string whose lifetime is bound to hAlg and which must not
9271 : * be freed.
9272 : * @since 3.11
9273 : */
9274 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
9275 : {
9276 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9277 1 : return hValue->ptr->GetName().c_str();
9278 : }
9279 :
9280 : /************************************************************************/
9281 : /* GDALArgDatasetValueGetDatasetRef() */
9282 : /************************************************************************/
9283 :
9284 : /** Return the dataset component of the GDALArgDatasetValue.
9285 : *
9286 : * This does not modify the reference counter, hence the lifetime of the
9287 : * returned object is not guaranteed to exceed the one of hValue.
9288 : *
9289 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9290 : * @since 3.11
9291 : */
9292 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
9293 : {
9294 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9295 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
9296 : }
9297 :
9298 : /************************************************************************/
9299 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
9300 : /************************************************************************/
9301 :
9302 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
9303 : * reference count if not null. Once done with the dataset, the caller should
9304 : * call GDALReleaseDataset().
9305 : *
9306 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9307 : * @since 3.11
9308 : */
9309 : GDALDatasetH
9310 1127 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
9311 : {
9312 1127 : VALIDATE_POINTER1(hValue, __func__, nullptr);
9313 1127 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
9314 : }
9315 :
9316 : /************************************************************************/
9317 : /* GDALArgDatasetValueSetName() */
9318 : /************************************************************************/
9319 :
9320 : /** Set dataset name
9321 : *
9322 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9323 : * @param pszName Dataset name. May be null.
9324 : * @since 3.11
9325 : */
9326 :
9327 1435 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
9328 : const char *pszName)
9329 : {
9330 1435 : VALIDATE_POINTER0(hValue, __func__);
9331 1435 : hValue->ptr->Set(pszName ? pszName : "");
9332 : }
9333 :
9334 : /************************************************************************/
9335 : /* GDALArgDatasetValueSetDataset() */
9336 : /************************************************************************/
9337 :
9338 : /** Set dataset object, increasing its reference counter.
9339 : *
9340 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
9341 : * @param hDS Dataset object. May be null.
9342 : * @since 3.11
9343 : */
9344 :
9345 746 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
9346 : GDALDatasetH hDS)
9347 : {
9348 746 : VALIDATE_POINTER0(hValue, __func__);
9349 746 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
9350 : }
|