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