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