Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: GDALAlgorithm class
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "cpl_port.h"
14 : #include "cpl_conv.h"
15 : #include "cpl_error.h"
16 : #include "cpl_json.h"
17 : #include "cpl_levenshtein.h"
18 : #include "cpl_minixml.h"
19 : #include "cpl_multiproc.h"
20 :
21 : #include "gdalalgorithm.h"
22 : #include "gdalalg_abstract_pipeline.h"
23 : #include "gdal_priv.h"
24 : #include "gdal_thread_pool.h"
25 : #include "ogrsf_frmts.h"
26 : #include "ogr_spatialref.h"
27 : #include "vrtdataset.h"
28 :
29 : #include <algorithm>
30 : #include <cassert>
31 : #include <cerrno>
32 : #include <cmath>
33 : #include <cstdlib>
34 : #include <limits>
35 : #include <map>
36 : #include <string_view>
37 :
38 : #ifndef _
39 : #define _(x) (x)
40 : #endif
41 :
42 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
43 :
44 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
45 :
46 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
47 :
48 : //! @cond Doxygen_Suppress
49 : struct GDALAlgorithmArgHS
50 : {
51 : GDALAlgorithmArg *ptr = nullptr;
52 :
53 331375 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
54 : {
55 331375 : }
56 : };
57 :
58 : //! @endcond
59 :
60 : //! @cond Doxygen_Suppress
61 : struct GDALArgDatasetValueHS
62 : {
63 : GDALArgDatasetValue val{};
64 : GDALArgDatasetValue *ptr = nullptr;
65 :
66 1 : GDALArgDatasetValueHS() : ptr(&val)
67 : {
68 1 : }
69 :
70 2957 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
71 : {
72 2957 : }
73 :
74 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
75 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
76 : };
77 :
78 : //! @endcond
79 :
80 : /************************************************************************/
81 : /* GDALAlgorithmArgTypeIsList() */
82 : /************************************************************************/
83 :
84 390322 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
85 : {
86 390322 : switch (type)
87 : {
88 256417 : case GAAT_BOOLEAN:
89 : case GAAT_STRING:
90 : case GAAT_INTEGER:
91 : case GAAT_REAL:
92 : case GAAT_DATASET:
93 256417 : break;
94 :
95 133905 : case GAAT_STRING_LIST:
96 : case GAAT_INTEGER_LIST:
97 : case GAAT_REAL_LIST:
98 : case GAAT_DATASET_LIST:
99 133905 : return true;
100 : }
101 :
102 256417 : return false;
103 : }
104 :
105 : /************************************************************************/
106 : /* GDALAlgorithmArgTypeName() */
107 : /************************************************************************/
108 :
109 5269 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
110 : {
111 5269 : switch (type)
112 : {
113 1312 : case GAAT_BOOLEAN:
114 1312 : break;
115 1404 : case GAAT_STRING:
116 1404 : return "string";
117 370 : case GAAT_INTEGER:
118 370 : return "integer";
119 472 : case GAAT_REAL:
120 472 : return "real";
121 242 : case GAAT_DATASET:
122 242 : return "dataset";
123 960 : case GAAT_STRING_LIST:
124 960 : return "string_list";
125 92 : case GAAT_INTEGER_LIST:
126 92 : return "integer_list";
127 219 : case GAAT_REAL_LIST:
128 219 : return "real_list";
129 198 : case GAAT_DATASET_LIST:
130 198 : return "dataset_list";
131 : }
132 :
133 1312 : return "boolean";
134 : }
135 :
136 : /************************************************************************/
137 : /* GDALAlgorithmArgDatasetTypeName() */
138 : /************************************************************************/
139 :
140 21448 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
141 : {
142 21448 : std::string ret;
143 21448 : if ((type & GDAL_OF_RASTER) != 0)
144 11931 : ret = "raster";
145 21448 : if ((type & GDAL_OF_VECTOR) != 0)
146 : {
147 10311 : if (!ret.empty())
148 : {
149 1187 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
150 257 : ret += ", ";
151 : else
152 930 : ret += " or ";
153 : }
154 10311 : ret += "vector";
155 : }
156 21448 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
157 : {
158 576 : if (!ret.empty())
159 : {
160 316 : ret += " or ";
161 : }
162 576 : ret += "multidimensional raster";
163 : }
164 21448 : return ret;
165 : }
166 :
167 : /************************************************************************/
168 : /* GDALAlgorithmArgDecl() */
169 : /************************************************************************/
170 :
171 : // cppcheck-suppress uninitMemberVar
172 313319 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
173 : char chShortName,
174 : const std::string &description,
175 313319 : GDALAlgorithmArgType type)
176 : : m_longName(longName),
177 313319 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
178 : m_description(description), m_type(type),
179 626638 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
180 313319 : .toupper()),
181 939957 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
182 : {
183 313319 : if (m_type == GAAT_BOOLEAN)
184 : {
185 130428 : m_defaultValue = false;
186 : }
187 313319 : }
188 :
189 : /************************************************************************/
190 : /* GDALAlgorithmArgDecl::SetMinCount() */
191 : /************************************************************************/
192 :
193 17689 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
194 : {
195 17689 : if (!GDALAlgorithmArgTypeIsList(m_type))
196 : {
197 1 : CPLError(CE_Failure, CPLE_NotSupported,
198 : "SetMinCount() illegal on scalar argument '%s'",
199 1 : GetName().c_str());
200 : }
201 : else
202 : {
203 17688 : m_minCount = count;
204 : }
205 17689 : return *this;
206 : }
207 :
208 : /************************************************************************/
209 : /* GDALAlgorithmArgDecl::SetMaxCount() */
210 : /************************************************************************/
211 :
212 16725 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
213 : {
214 16725 : if (!GDALAlgorithmArgTypeIsList(m_type))
215 : {
216 1 : CPLError(CE_Failure, CPLE_NotSupported,
217 : "SetMaxCount() illegal on scalar argument '%s'",
218 1 : GetName().c_str());
219 : }
220 : else
221 : {
222 16724 : m_maxCount = count;
223 : }
224 16725 : return *this;
225 : }
226 :
227 : /************************************************************************/
228 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
229 : /************************************************************************/
230 :
231 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
232 :
233 : /************************************************************************/
234 : /* GDALAlgorithmArg::Set() */
235 : /************************************************************************/
236 :
237 1134 : bool GDALAlgorithmArg::Set(bool value)
238 : {
239 1134 : if (m_decl.GetType() != GAAT_BOOLEAN)
240 : {
241 14 : CPLError(
242 : CE_Failure, CPLE_AppDefined,
243 : "Calling Set(bool) on argument '%s' of type %s is not supported",
244 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
245 7 : return false;
246 : }
247 1127 : return SetInternal(value);
248 : }
249 :
250 3691 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
251 : {
252 3727 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
253 36 : value.front() == '@')
254 : {
255 2 : GByte *pabyData = nullptr;
256 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
257 2 : 10 * 1024 * 1024))
258 : {
259 : // Remove UTF-8 BOM
260 1 : size_t offset = 0;
261 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
262 1 : pabyData[2] == 0xBF)
263 : {
264 1 : offset = 3;
265 : }
266 1 : value = reinterpret_cast<const char *>(pabyData + offset);
267 1 : VSIFree(pabyData);
268 : }
269 : else
270 : {
271 1 : return false;
272 : }
273 : }
274 :
275 3690 : if (m_decl.IsRemoveSQLCommentsEnabled())
276 35 : value = CPLRemoveSQLComments(value);
277 :
278 3690 : return true;
279 : }
280 :
281 3712 : bool GDALAlgorithmArg::Set(const std::string &value)
282 : {
283 3712 : switch (m_decl.GetType())
284 : {
285 9 : case GAAT_BOOLEAN:
286 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
287 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
288 : {
289 4 : return Set(true);
290 : }
291 5 : else if (EQUAL(value.c_str(), "0") ||
292 4 : EQUAL(value.c_str(), "FALSE") ||
293 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
294 : {
295 4 : return Set(false);
296 : }
297 1 : break;
298 :
299 8 : case GAAT_INTEGER:
300 : case GAAT_INTEGER_LIST:
301 : {
302 8 : errno = 0;
303 8 : char *endptr = nullptr;
304 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
305 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
306 5 : endptr == value.c_str() + value.size())
307 : {
308 3 : if (m_decl.GetType() == GAAT_INTEGER)
309 3 : return Set(static_cast<int>(v));
310 : else
311 1 : return Set(std::vector<int>{static_cast<int>(v)});
312 : }
313 5 : break;
314 : }
315 :
316 5 : case GAAT_REAL:
317 : case GAAT_REAL_LIST:
318 : {
319 5 : char *endptr = nullptr;
320 5 : const double v = CPLStrtod(value.c_str(), &endptr);
321 5 : if (endptr == value.c_str() + value.size())
322 : {
323 3 : if (m_decl.GetType() == GAAT_REAL)
324 3 : return Set(v);
325 : else
326 1 : return Set(std::vector<double>{v});
327 : }
328 2 : break;
329 : }
330 :
331 3674 : case GAAT_STRING:
332 3674 : break;
333 :
334 1 : case GAAT_STRING_LIST:
335 2 : return Set(std::vector<std::string>{value});
336 :
337 15 : case GAAT_DATASET:
338 15 : return SetDatasetName(value);
339 :
340 0 : case GAAT_DATASET_LIST:
341 : {
342 0 : std::vector<GDALArgDatasetValue> v;
343 0 : v.resize(1);
344 0 : v[0].Set(value);
345 0 : return Set(std::move(v));
346 : }
347 : }
348 :
349 3682 : if (m_decl.GetType() != GAAT_STRING)
350 : {
351 16 : CPLError(CE_Failure, CPLE_AppDefined,
352 : "Calling Set(std::string) on argument '%s' of type %s is not "
353 : "supported",
354 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
355 8 : return false;
356 : }
357 :
358 3674 : std::string newValue(value);
359 3674 : return ProcessString(newValue) && SetInternal(newValue);
360 : }
361 :
362 841 : bool GDALAlgorithmArg::Set(int value)
363 : {
364 841 : if (m_decl.GetType() == GAAT_BOOLEAN)
365 : {
366 3 : if (value == 1)
367 1 : return Set(true);
368 2 : else if (value == 0)
369 1 : return Set(false);
370 : }
371 838 : else if (m_decl.GetType() == GAAT_REAL)
372 : {
373 3 : return Set(static_cast<double>(value));
374 : }
375 835 : else if (m_decl.GetType() == GAAT_STRING)
376 : {
377 2 : return Set(std::to_string(value));
378 : }
379 833 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
380 : {
381 1 : return Set(std::vector<int>{value});
382 : }
383 832 : else if (m_decl.GetType() == GAAT_REAL_LIST)
384 : {
385 1 : return Set(std::vector<double>{static_cast<double>(value)});
386 : }
387 831 : else if (m_decl.GetType() == GAAT_STRING_LIST)
388 : {
389 2 : return Set(std::vector<std::string>{std::to_string(value)});
390 : }
391 :
392 831 : if (m_decl.GetType() != GAAT_INTEGER)
393 : {
394 2 : CPLError(
395 : CE_Failure, CPLE_AppDefined,
396 : "Calling Set(int) on argument '%s' of type %s is not supported",
397 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
398 1 : return false;
399 : }
400 830 : return SetInternal(value);
401 : }
402 :
403 281 : bool GDALAlgorithmArg::Set(double value)
404 : {
405 284 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
406 284 : value <= INT_MAX && static_cast<int>(value) == value)
407 : {
408 2 : return Set(static_cast<int>(value));
409 : }
410 279 : else if (m_decl.GetType() == GAAT_STRING)
411 : {
412 2 : return Set(std::to_string(value));
413 : }
414 279 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
415 279 : value <= INT_MAX && static_cast<int>(value) == value)
416 : {
417 1 : return Set(std::vector<int>{static_cast<int>(value)});
418 : }
419 276 : else if (m_decl.GetType() == GAAT_REAL_LIST)
420 : {
421 0 : return Set(std::vector<double>{value});
422 : }
423 276 : else if (m_decl.GetType() == GAAT_STRING_LIST)
424 : {
425 2 : return Set(std::vector<std::string>{std::to_string(value)});
426 : }
427 275 : else if (m_decl.GetType() != GAAT_REAL)
428 : {
429 6 : CPLError(
430 : CE_Failure, CPLE_AppDefined,
431 : "Calling Set(double) on argument '%s' of type %s is not supported",
432 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
433 3 : return false;
434 : }
435 272 : return SetInternal(value);
436 : }
437 :
438 5783 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
439 : {
440 5786 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
441 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
442 : {
443 3 : CPLError(
444 : CE_Failure, CPLE_AppDefined,
445 : "Dataset object '%s' is created by algorithm and cannot be set "
446 : "as an input.",
447 3 : arg->GetName().c_str());
448 3 : return false;
449 : }
450 5780 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
451 : {
452 2 : CPLError(CE_Failure, CPLE_AppDefined,
453 : "A dataset cannot be set as an input argument of '%s'.",
454 2 : arg->GetName().c_str());
455 2 : return false;
456 : }
457 :
458 5778 : return true;
459 : }
460 :
461 9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
462 : {
463 9 : if (m_decl.GetType() != GAAT_DATASET)
464 : {
465 2 : CPLError(CE_Failure, CPLE_AppDefined,
466 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
467 : "is not supported",
468 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
469 1 : return false;
470 : }
471 8 : if (!CheckCanSetDatasetObject(this))
472 2 : return false;
473 6 : m_explicitlySet = true;
474 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
475 6 : val.Set(ds);
476 6 : return RunAllActions();
477 : }
478 :
479 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
480 : {
481 3 : if (m_decl.GetType() != GAAT_DATASET)
482 : {
483 2 : CPLError(CE_Failure, CPLE_AppDefined,
484 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
485 : "is not supported",
486 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
487 1 : return false;
488 : }
489 2 : if (!CheckCanSetDatasetObject(this))
490 1 : return false;
491 1 : m_explicitlySet = true;
492 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
493 1 : val.Set(std::move(ds));
494 1 : return RunAllActions();
495 : }
496 :
497 539 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
498 : {
499 539 : if (m_decl.GetType() != GAAT_DATASET)
500 : {
501 2 : CPLError(CE_Failure, CPLE_AppDefined,
502 : "Calling SetDatasetName() on argument '%s' of type %s is "
503 : "not supported",
504 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
505 1 : return false;
506 : }
507 538 : m_explicitlySet = true;
508 538 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
509 538 : return RunAllActions();
510 : }
511 :
512 934 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
513 : {
514 934 : if (m_decl.GetType() != GAAT_DATASET)
515 : {
516 2 : CPLError(CE_Failure, CPLE_AppDefined,
517 : "Calling SetFrom() on argument '%s' of type %s is "
518 : "not supported",
519 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
520 1 : return false;
521 : }
522 933 : if (!CheckCanSetDatasetObject(this))
523 1 : return false;
524 932 : m_explicitlySet = true;
525 932 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
526 932 : return RunAllActions();
527 : }
528 :
529 926 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
530 : {
531 926 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
532 : {
533 3 : std::vector<int> v_i;
534 4 : for (const std::string &s : value)
535 : {
536 3 : errno = 0;
537 3 : char *endptr = nullptr;
538 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
539 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
540 2 : endptr == s.c_str() + s.size())
541 : {
542 1 : v_i.push_back(static_cast<int>(v));
543 : }
544 : else
545 : {
546 2 : break;
547 : }
548 : }
549 3 : if (v_i.size() == value.size())
550 1 : return Set(v_i);
551 : }
552 923 : else if (m_decl.GetType() == GAAT_REAL_LIST)
553 : {
554 2 : std::vector<double> v_d;
555 3 : for (const std::string &s : value)
556 : {
557 2 : char *endptr = nullptr;
558 2 : const double v = CPLStrtod(s.c_str(), &endptr);
559 2 : if (endptr == s.c_str() + s.size())
560 : {
561 1 : v_d.push_back(v);
562 : }
563 : else
564 : {
565 1 : break;
566 : }
567 : }
568 2 : if (v_d.size() == value.size())
569 1 : return Set(v_d);
570 : }
571 1840 : else if ((m_decl.GetType() == GAAT_INTEGER ||
572 1837 : m_decl.GetType() == GAAT_REAL ||
573 2760 : m_decl.GetType() == GAAT_STRING) &&
574 5 : value.size() == 1)
575 : {
576 4 : return Set(value[0]);
577 : }
578 917 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
579 : {
580 30 : std::vector<GDALArgDatasetValue> dsVector;
581 46 : for (const std::string &s : value)
582 31 : dsVector.emplace_back(s);
583 15 : return Set(std::move(dsVector));
584 : }
585 :
586 905 : if (m_decl.GetType() != GAAT_STRING_LIST)
587 : {
588 10 : CPLError(CE_Failure, CPLE_AppDefined,
589 : "Calling Set(const std::vector<std::string> &) on argument "
590 : "'%s' of type %s is not supported",
591 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
592 5 : return false;
593 : }
594 :
595 1786 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
596 886 : m_decl.IsRemoveSQLCommentsEnabled())
597 : {
598 28 : std::vector<std::string> newValue(value);
599 31 : for (auto &s : newValue)
600 : {
601 17 : if (!ProcessString(s))
602 0 : return false;
603 : }
604 14 : return SetInternal(newValue);
605 : }
606 : else
607 : {
608 886 : return SetInternal(value);
609 : }
610 : }
611 :
612 167 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
613 : {
614 167 : if (m_decl.GetType() == GAAT_REAL_LIST)
615 : {
616 2 : std::vector<double> v_d;
617 2 : for (int i : value)
618 1 : v_d.push_back(i);
619 1 : return Set(v_d);
620 : }
621 166 : else if (m_decl.GetType() == GAAT_STRING_LIST)
622 : {
623 2 : std::vector<std::string> v_s;
624 3 : for (int i : value)
625 2 : v_s.push_back(std::to_string(i));
626 1 : return Set(v_s);
627 : }
628 328 : else if ((m_decl.GetType() == GAAT_INTEGER ||
629 324 : m_decl.GetType() == GAAT_REAL ||
630 491 : m_decl.GetType() == GAAT_STRING) &&
631 5 : value.size() == 1)
632 : {
633 3 : return Set(value[0]);
634 : }
635 :
636 162 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
637 : {
638 6 : CPLError(CE_Failure, CPLE_AppDefined,
639 : "Calling Set(const std::vector<int> &) on argument '%s' of "
640 : "type %s is not supported",
641 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
642 3 : return false;
643 : }
644 159 : return SetInternal(value);
645 : }
646 :
647 260 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
648 : {
649 260 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
650 : {
651 2 : std::vector<int> v_i;
652 3 : for (double d : value)
653 : {
654 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
655 : {
656 1 : v_i.push_back(static_cast<int>(d));
657 : }
658 : else
659 : {
660 : break;
661 : }
662 : }
663 2 : if (v_i.size() == value.size())
664 1 : return Set(v_i);
665 : }
666 258 : else if (m_decl.GetType() == GAAT_STRING_LIST)
667 : {
668 2 : std::vector<std::string> v_s;
669 3 : for (double d : value)
670 2 : v_s.push_back(std::to_string(d));
671 1 : return Set(v_s);
672 : }
673 513 : else if ((m_decl.GetType() == GAAT_INTEGER ||
674 511 : m_decl.GetType() == GAAT_REAL ||
675 769 : m_decl.GetType() == GAAT_STRING) &&
676 3 : value.size() == 1)
677 : {
678 3 : return Set(value[0]);
679 : }
680 :
681 255 : if (m_decl.GetType() != GAAT_REAL_LIST)
682 : {
683 4 : CPLError(CE_Failure, CPLE_AppDefined,
684 : "Calling Set(const std::vector<double> &) on argument '%s' of "
685 : "type %s is not supported",
686 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
687 2 : return false;
688 : }
689 253 : return SetInternal(value);
690 : }
691 :
692 3247 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
693 : {
694 3247 : if (m_decl.GetType() != GAAT_DATASET_LIST)
695 : {
696 2 : CPLError(CE_Failure, CPLE_AppDefined,
697 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
698 : "argument '%s' of type %s is not supported",
699 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
700 1 : return false;
701 : }
702 3246 : m_explicitlySet = true;
703 3246 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
704 3246 : return RunAllActions();
705 : }
706 :
707 : GDALAlgorithmArg &
708 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
709 : {
710 0 : Set(std::move(value));
711 0 : return *this;
712 : }
713 :
714 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
715 : {
716 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
717 1 : return Set(value.exportToWkt(apszOptions));
718 : }
719 :
720 4041 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
721 : {
722 4041 : if (m_decl.GetType() != other.GetType())
723 : {
724 2 : CPLError(CE_Failure, CPLE_AppDefined,
725 : "Calling SetFrom() on argument '%s' of type %s whereas "
726 : "other argument type is %s is not supported",
727 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
728 : GDALAlgorithmArgTypeName(other.GetType()));
729 1 : return false;
730 : }
731 :
732 4040 : switch (m_decl.GetType())
733 : {
734 90 : case GAAT_BOOLEAN:
735 90 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
736 90 : break;
737 777 : case GAAT_STRING:
738 1554 : *std::get<std::string *>(m_value) =
739 777 : *std::get<std::string *>(other.m_value);
740 777 : break;
741 6 : case GAAT_INTEGER:
742 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
743 6 : break;
744 1 : case GAAT_REAL:
745 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
746 1 : break;
747 928 : case GAAT_DATASET:
748 928 : return SetFrom(other.Get<GDALArgDatasetValue>());
749 39 : case GAAT_STRING_LIST:
750 78 : *std::get<std::vector<std::string> *>(m_value) =
751 39 : *std::get<std::vector<std::string> *>(other.m_value);
752 39 : break;
753 1 : case GAAT_INTEGER_LIST:
754 2 : *std::get<std::vector<int> *>(m_value) =
755 1 : *std::get<std::vector<int> *>(other.m_value);
756 1 : break;
757 1 : case GAAT_REAL_LIST:
758 2 : *std::get<std::vector<double> *>(m_value) =
759 1 : *std::get<std::vector<double> *>(other.m_value);
760 1 : break;
761 2197 : case GAAT_DATASET_LIST:
762 : {
763 2197 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
764 2202 : for (const auto &val :
765 6601 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
766 : {
767 4404 : GDALArgDatasetValue v;
768 2202 : v.SetFrom(val);
769 2202 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
770 2202 : ->push_back(std::move(v));
771 : }
772 2197 : break;
773 : }
774 : }
775 3112 : m_explicitlySet = true;
776 3112 : return RunAllActions();
777 : }
778 :
779 : /************************************************************************/
780 : /* GDALAlgorithmArg::RunAllActions() */
781 : /************************************************************************/
782 :
783 15049 : bool GDALAlgorithmArg::RunAllActions()
784 : {
785 15049 : if (!RunValidationActions())
786 128 : return false;
787 14921 : RunActions();
788 14921 : return true;
789 : }
790 :
791 : /************************************************************************/
792 : /* GDALAlgorithmArg::RunActions() */
793 : /************************************************************************/
794 :
795 14922 : void GDALAlgorithmArg::RunActions()
796 : {
797 15214 : for (const auto &f : m_actions)
798 292 : f();
799 14922 : }
800 :
801 : /************************************************************************/
802 : /* GDALAlgorithmArg::ValidateChoice() */
803 : /************************************************************************/
804 :
805 : // Returns the canonical value if matching a valid choice, or empty string
806 : // otherwise.
807 2498 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
808 : {
809 14887 : for (const std::string &choice : GetChoices())
810 : {
811 14769 : if (EQUAL(value.c_str(), choice.c_str()))
812 : {
813 2380 : return choice;
814 : }
815 : }
816 :
817 190 : for (const std::string &choice : GetHiddenChoices())
818 : {
819 172 : if (EQUAL(value.c_str(), choice.c_str()))
820 : {
821 100 : return choice;
822 : }
823 : }
824 :
825 36 : std::string expected;
826 220 : for (const auto &choice : GetChoices())
827 : {
828 202 : if (!expected.empty())
829 184 : expected += ", ";
830 202 : expected += '\'';
831 202 : expected += choice;
832 202 : expected += '\'';
833 : }
834 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
835 : {
836 6 : return "?";
837 : }
838 24 : CPLError(CE_Failure, CPLE_IllegalArg,
839 : "Invalid value '%s' for string argument '%s'. Should be "
840 : "one among %s.",
841 12 : value.c_str(), GetName().c_str(), expected.c_str());
842 12 : return std::string();
843 : }
844 :
845 : /************************************************************************/
846 : /* GDALAlgorithmArg::ValidateIntRange() */
847 : /************************************************************************/
848 :
849 2650 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
850 : {
851 2650 : bool ret = true;
852 :
853 2650 : const auto [minVal, minValIsIncluded] = GetMinValue();
854 2650 : if (!std::isnan(minVal))
855 : {
856 2024 : if (minValIsIncluded && val < minVal)
857 : {
858 3 : CPLError(CE_Failure, CPLE_IllegalArg,
859 : "Value of argument '%s' is %d, but should be >= %d",
860 3 : GetName().c_str(), val, static_cast<int>(minVal));
861 3 : ret = false;
862 : }
863 2021 : else if (!minValIsIncluded && val <= minVal)
864 : {
865 1 : CPLError(CE_Failure, CPLE_IllegalArg,
866 : "Value of argument '%s' is %d, but should be > %d",
867 1 : GetName().c_str(), val, static_cast<int>(minVal));
868 1 : ret = false;
869 : }
870 : }
871 :
872 2650 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
873 2650 : if (!std::isnan(maxVal))
874 : {
875 :
876 382 : if (maxValIsIncluded && val > maxVal)
877 : {
878 1 : CPLError(CE_Failure, CPLE_IllegalArg,
879 : "Value of argument '%s' is %d, but should be <= %d",
880 1 : GetName().c_str(), val, static_cast<int>(maxVal));
881 1 : ret = false;
882 : }
883 381 : else if (!maxValIsIncluded && val >= maxVal)
884 : {
885 1 : CPLError(CE_Failure, CPLE_IllegalArg,
886 : "Value of argument '%s' is %d, but should be < %d",
887 1 : GetName().c_str(), val, static_cast<int>(maxVal));
888 1 : ret = false;
889 : }
890 : }
891 :
892 2650 : return ret;
893 : }
894 :
895 : /************************************************************************/
896 : /* GDALAlgorithmArg::ValidateRealRange() */
897 : /************************************************************************/
898 :
899 2080 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
900 : {
901 2080 : bool ret = true;
902 :
903 2080 : const auto [minVal, minValIsIncluded] = GetMinValue();
904 2080 : if (!std::isnan(minVal))
905 : {
906 201 : if (minValIsIncluded && !(val >= minVal))
907 : {
908 11 : CPLError(CE_Failure, CPLE_IllegalArg,
909 : "Value of argument '%s' is %g, but should be >= %g",
910 11 : GetName().c_str(), val, minVal);
911 11 : ret = false;
912 : }
913 190 : else if (!minValIsIncluded && !(val > minVal))
914 : {
915 4 : CPLError(CE_Failure, CPLE_IllegalArg,
916 : "Value of argument '%s' is %g, but should be > %g",
917 4 : GetName().c_str(), val, minVal);
918 4 : ret = false;
919 : }
920 : }
921 :
922 2080 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
923 2080 : if (!std::isnan(maxVal))
924 : {
925 :
926 46 : if (maxValIsIncluded && !(val <= maxVal))
927 : {
928 2 : CPLError(CE_Failure, CPLE_IllegalArg,
929 : "Value of argument '%s' is %g, but should be <= %g",
930 2 : GetName().c_str(), val, maxVal);
931 2 : ret = false;
932 : }
933 44 : else if (!maxValIsIncluded && !(val < maxVal))
934 : {
935 1 : CPLError(CE_Failure, CPLE_IllegalArg,
936 : "Value of argument '%s' is %g, but should be < %g",
937 1 : GetName().c_str(), val, maxVal);
938 1 : ret = false;
939 : }
940 : }
941 :
942 2080 : return ret;
943 : }
944 :
945 : /************************************************************************/
946 : /* GDALAlgorithmArg::RunValidationActions() */
947 : /************************************************************************/
948 :
949 33331 : bool GDALAlgorithmArg::RunValidationActions()
950 : {
951 33331 : bool ret = true;
952 :
953 33331 : if (GetType() == GAAT_STRING && !GetChoices().empty())
954 : {
955 1621 : auto &val = Get<std::string>();
956 3242 : std::string validVal = ValidateChoice(val);
957 1621 : if (validVal.empty())
958 7 : ret = false;
959 : else
960 1614 : val = std::move(validVal);
961 : }
962 31710 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
963 : {
964 633 : auto &values = Get<std::vector<std::string>>();
965 1510 : for (std::string &val : values)
966 : {
967 1754 : std::string validVal = ValidateChoice(val);
968 877 : if (validVal.empty())
969 5 : ret = false;
970 : else
971 872 : val = std::move(validVal);
972 : }
973 : }
974 :
975 : const auto CheckMinCharCount =
976 942 : [this, &ret](const std::string &val, int nMinCharCount)
977 : {
978 930 : if (val.size() < static_cast<size_t>(nMinCharCount))
979 : {
980 12 : CPLError(CE_Failure, CPLE_IllegalArg,
981 : "Value of argument '%s' is '%s', but should have at least "
982 : "%d character%s",
983 6 : GetName().c_str(), val.c_str(), nMinCharCount,
984 : nMinCharCount > 1 ? "s" : "");
985 6 : ret = false;
986 : }
987 34261 : };
988 :
989 : const auto CheckMaxCharCount =
990 11653 : [this, &ret](const std::string &val, int nMaxCharCount)
991 : {
992 11651 : if (val.size() > static_cast<size_t>(nMaxCharCount))
993 : {
994 2 : CPLError(
995 : CE_Failure, CPLE_IllegalArg,
996 : "Value of argument '%s' is '%s', but should have no more than "
997 : "%d character%s",
998 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
999 : nMaxCharCount > 1 ? "s" : "");
1000 1 : ret = false;
1001 : }
1002 44982 : };
1003 :
1004 33331 : if (GetType() == GAAT_STRING)
1005 : {
1006 9231 : const auto &val = Get<std::string>();
1007 9231 : const int nMinCharCount = GetMinCharCount();
1008 9231 : if (nMinCharCount > 0)
1009 : {
1010 848 : CheckMinCharCount(val, nMinCharCount);
1011 : }
1012 :
1013 9231 : const int nMaxCharCount = GetMaxCharCount();
1014 9231 : CheckMaxCharCount(val, nMaxCharCount);
1015 : }
1016 24100 : else if (GetType() == GAAT_STRING_LIST)
1017 : {
1018 1980 : const int nMinCharCount = GetMinCharCount();
1019 1980 : const int nMaxCharCount = GetMaxCharCount();
1020 4400 : for (const auto &val : Get<std::vector<std::string>>())
1021 : {
1022 2420 : if (nMinCharCount > 0)
1023 82 : CheckMinCharCount(val, nMinCharCount);
1024 2420 : CheckMaxCharCount(val, nMaxCharCount);
1025 : }
1026 : }
1027 22120 : else if (GetType() == GAAT_INTEGER)
1028 : {
1029 1969 : ret = ValidateIntRange(Get<int>()) && ret;
1030 : }
1031 20151 : else if (GetType() == GAAT_INTEGER_LIST)
1032 : {
1033 1015 : for (int v : Get<std::vector<int>>())
1034 681 : ret = ValidateIntRange(v) && ret;
1035 : }
1036 19817 : else if (GetType() == GAAT_REAL)
1037 : {
1038 530 : ret = ValidateRealRange(Get<double>()) && ret;
1039 : }
1040 19287 : else if (GetType() == GAAT_REAL_LIST)
1041 : {
1042 2111 : for (double v : Get<std::vector<double>>())
1043 1550 : ret = ValidateRealRange(v) && ret;
1044 : }
1045 :
1046 33331 : if (GDALAlgorithmArgTypeIsList(GetType()))
1047 : {
1048 13550 : int valueCount = 0;
1049 13550 : if (GetType() == GAAT_STRING_LIST)
1050 : {
1051 1980 : valueCount =
1052 1980 : static_cast<int>(Get<std::vector<std::string>>().size());
1053 : }
1054 11570 : else if (GetType() == GAAT_INTEGER_LIST)
1055 : {
1056 334 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1057 : }
1058 11236 : else if (GetType() == GAAT_REAL_LIST)
1059 : {
1060 561 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1061 : }
1062 10675 : else if (GetType() == GAAT_DATASET_LIST)
1063 : {
1064 10675 : valueCount = static_cast<int>(
1065 10675 : Get<std::vector<GDALArgDatasetValue>>().size());
1066 : }
1067 :
1068 13550 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1069 : {
1070 14 : ReportError(CE_Failure, CPLE_AppDefined,
1071 : "%d value%s been specified for argument '%s', "
1072 : "whereas exactly %d %s expected.",
1073 : valueCount, valueCount > 1 ? "s have" : " has",
1074 7 : GetName().c_str(), GetMinCount(),
1075 7 : GetMinCount() > 1 ? "were" : "was");
1076 7 : ret = false;
1077 : }
1078 13543 : else if (valueCount < GetMinCount())
1079 : {
1080 6 : ReportError(CE_Failure, CPLE_AppDefined,
1081 : "Only %d value%s been specified for argument '%s', "
1082 : "whereas at least %d %s expected.",
1083 : valueCount, valueCount > 1 ? "s have" : " has",
1084 3 : GetName().c_str(), GetMinCount(),
1085 3 : GetMinCount() > 1 ? "were" : "was");
1086 3 : ret = false;
1087 : }
1088 13540 : else if (valueCount > GetMaxCount())
1089 : {
1090 2 : ReportError(CE_Failure, CPLE_AppDefined,
1091 : "%d value%s been specified for argument '%s', "
1092 : "whereas at most %d %s expected.",
1093 : valueCount, valueCount > 1 ? "s have" : " has",
1094 1 : GetName().c_str(), GetMaxCount(),
1095 1 : GetMaxCount() > 1 ? "were" : "was");
1096 1 : ret = false;
1097 : }
1098 : }
1099 :
1100 33331 : if (ret)
1101 : {
1102 39569 : for (const auto &f : m_validationActions)
1103 : {
1104 6292 : if (!f())
1105 81 : ret = false;
1106 : }
1107 : }
1108 :
1109 33331 : return ret;
1110 : }
1111 :
1112 : /************************************************************************/
1113 : /* GDALAlgorithmArg::ReportError() */
1114 : /************************************************************************/
1115 :
1116 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1117 : const char *fmt, ...) const
1118 : {
1119 : va_list args;
1120 11 : va_start(args, fmt);
1121 11 : if (m_owner)
1122 : {
1123 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1124 22 : CPLString().vPrintf(fmt, args).c_str());
1125 : }
1126 : else
1127 : {
1128 0 : CPLError(eErrClass, err_no, "%s",
1129 0 : CPLString().vPrintf(fmt, args).c_str());
1130 : }
1131 11 : va_end(args);
1132 11 : }
1133 :
1134 : /************************************************************************/
1135 : /* GDALAlgorithmArg::GetEscapedString() */
1136 : /************************************************************************/
1137 :
1138 : /* static */
1139 130 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1140 : {
1141 142 : if (s.find_first_of("\" \\,") != std::string::npos &&
1142 6 : !(s.size() > 4 &&
1143 6 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1144 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1145 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1146 : {
1147 8 : return std::string("\"")
1148 : .append(
1149 8 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1150 4 : .append("\"");
1151 : }
1152 : else
1153 : {
1154 126 : return s;
1155 : }
1156 : }
1157 :
1158 : /************************************************************************/
1159 : /* GDALAlgorithmArg::Serialize() */
1160 : /************************************************************************/
1161 :
1162 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1163 : bool absolutePath) const
1164 : {
1165 39 : serializedArg.clear();
1166 :
1167 39 : if (!IsExplicitlySet())
1168 : {
1169 0 : return false;
1170 : }
1171 :
1172 78 : std::string ret = "--";
1173 39 : ret += GetName();
1174 39 : if (GetType() == GAAT_BOOLEAN)
1175 : {
1176 0 : serializedArg = std::move(ret);
1177 0 : return true;
1178 : }
1179 :
1180 5 : const auto AddListValueSeparator = [this, &ret]()
1181 : {
1182 1 : if (GetPackedValuesAllowed())
1183 : {
1184 0 : ret += ',';
1185 : }
1186 : else
1187 : {
1188 1 : ret += " --";
1189 1 : ret += GetName();
1190 1 : ret += ' ';
1191 : }
1192 40 : };
1193 :
1194 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1195 : {
1196 : VSIStatBufL sStat;
1197 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1198 0 : !CPLIsFilenameRelative(filename.c_str()))
1199 0 : return filename;
1200 0 : char *pszCWD = CPLGetCurrentDir();
1201 0 : if (!pszCWD)
1202 0 : return filename;
1203 : const auto absPath =
1204 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1205 0 : CPLFree(pszCWD);
1206 0 : return absPath;
1207 : };
1208 :
1209 39 : ret += ' ';
1210 39 : switch (GetType())
1211 : {
1212 0 : case GAAT_BOOLEAN:
1213 0 : break;
1214 8 : case GAAT_STRING:
1215 : {
1216 8 : const auto &val = Get<std::string>();
1217 8 : ret += GetEscapedString(val);
1218 8 : break;
1219 : }
1220 0 : case GAAT_INTEGER:
1221 : {
1222 0 : ret += CPLSPrintf("%d", Get<int>());
1223 0 : break;
1224 : }
1225 0 : case GAAT_REAL:
1226 : {
1227 0 : ret += CPLSPrintf("%.17g", Get<double>());
1228 0 : break;
1229 : }
1230 2 : case GAAT_DATASET:
1231 : {
1232 2 : const auto &val = Get<GDALArgDatasetValue>();
1233 2 : const auto &str = val.GetName();
1234 2 : if (str.empty())
1235 : {
1236 0 : return false;
1237 : }
1238 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1239 2 : break;
1240 : }
1241 4 : case GAAT_STRING_LIST:
1242 : {
1243 4 : const auto &vals = Get<std::vector<std::string>>();
1244 8 : for (size_t i = 0; i < vals.size(); ++i)
1245 : {
1246 4 : if (i > 0)
1247 1 : AddListValueSeparator();
1248 4 : ret += GetEscapedString(vals[i]);
1249 : }
1250 4 : break;
1251 : }
1252 0 : case GAAT_INTEGER_LIST:
1253 : {
1254 0 : const auto &vals = Get<std::vector<int>>();
1255 0 : for (size_t i = 0; i < vals.size(); ++i)
1256 : {
1257 0 : if (i > 0)
1258 0 : AddListValueSeparator();
1259 0 : ret += CPLSPrintf("%d", vals[i]);
1260 : }
1261 0 : break;
1262 : }
1263 0 : case GAAT_REAL_LIST:
1264 : {
1265 0 : const auto &vals = Get<std::vector<double>>();
1266 0 : for (size_t i = 0; i < vals.size(); ++i)
1267 : {
1268 0 : if (i > 0)
1269 0 : AddListValueSeparator();
1270 0 : ret += CPLSPrintf("%.17g", vals[i]);
1271 : }
1272 0 : break;
1273 : }
1274 25 : case GAAT_DATASET_LIST:
1275 : {
1276 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1277 49 : for (size_t i = 0; i < vals.size(); ++i)
1278 : {
1279 25 : if (i > 0)
1280 0 : AddListValueSeparator();
1281 25 : const auto &val = vals[i];
1282 25 : const auto &str = val.GetName();
1283 25 : if (str.empty())
1284 : {
1285 1 : return false;
1286 : }
1287 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1288 24 : : str);
1289 : }
1290 24 : break;
1291 : }
1292 : }
1293 :
1294 38 : serializedArg = std::move(ret);
1295 38 : return true;
1296 : }
1297 :
1298 : /************************************************************************/
1299 : /* ~GDALInConstructionAlgorithmArg() */
1300 : /************************************************************************/
1301 :
1302 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1303 :
1304 : /************************************************************************/
1305 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1306 : /************************************************************************/
1307 :
1308 : GDALInConstructionAlgorithmArg &
1309 60220 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1310 : {
1311 60220 : m_decl.AddAlias(alias);
1312 60220 : if (m_owner)
1313 60220 : m_owner->AddAliasFor(this, alias);
1314 60220 : return *this;
1315 : }
1316 :
1317 : /************************************************************************/
1318 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1319 : /************************************************************************/
1320 :
1321 : GDALInConstructionAlgorithmArg &
1322 12738 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1323 : {
1324 12738 : m_decl.AddHiddenAlias(alias);
1325 12738 : if (m_owner)
1326 12738 : m_owner->AddAliasFor(this, alias);
1327 12738 : return *this;
1328 : }
1329 :
1330 : /************************************************************************/
1331 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1332 : /************************************************************************/
1333 :
1334 : GDALInConstructionAlgorithmArg &
1335 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1336 : {
1337 48 : m_decl.AddShortNameAlias(shortNameAlias);
1338 48 : if (m_owner)
1339 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1340 48 : return *this;
1341 : }
1342 :
1343 : /************************************************************************/
1344 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1345 : /************************************************************************/
1346 :
1347 20686 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1348 : {
1349 20686 : m_decl.SetPositional();
1350 20686 : if (m_owner)
1351 20686 : m_owner->SetPositional(this);
1352 20686 : return *this;
1353 : }
1354 :
1355 : /************************************************************************/
1356 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1357 : /************************************************************************/
1358 :
1359 1258 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1360 2516 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1361 1258 : m_nameSet(true)
1362 : {
1363 1258 : if (m_poDS)
1364 1258 : m_poDS->Reference();
1365 1258 : }
1366 :
1367 : /************************************************************************/
1368 : /* GDALArgDatasetValue::Set() */
1369 : /************************************************************************/
1370 :
1371 2102 : void GDALArgDatasetValue::Set(const std::string &name)
1372 : {
1373 2102 : Close();
1374 2102 : m_name = name;
1375 2102 : m_nameSet = true;
1376 2102 : if (m_ownerArg)
1377 2097 : m_ownerArg->NotifyValueSet();
1378 2102 : }
1379 :
1380 : /************************************************************************/
1381 : /* GDALArgDatasetValue::Set() */
1382 : /************************************************************************/
1383 :
1384 1838 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1385 : {
1386 1838 : Close();
1387 1838 : m_poDS = poDS.release();
1388 1838 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1389 1838 : m_nameSet = true;
1390 1838 : if (m_ownerArg)
1391 1716 : m_ownerArg->NotifyValueSet();
1392 1838 : }
1393 :
1394 : /************************************************************************/
1395 : /* GDALArgDatasetValue::Set() */
1396 : /************************************************************************/
1397 :
1398 7461 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1399 : {
1400 7461 : Close();
1401 7461 : m_poDS = poDS;
1402 7461 : if (m_poDS)
1403 6644 : m_poDS->Reference();
1404 7461 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1405 7461 : m_nameSet = true;
1406 7461 : if (m_ownerArg)
1407 3120 : m_ownerArg->NotifyValueSet();
1408 7461 : }
1409 :
1410 : /************************************************************************/
1411 : /* GDALArgDatasetValue::SetFrom() */
1412 : /************************************************************************/
1413 :
1414 3134 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1415 : {
1416 3134 : Close();
1417 3134 : m_name = other.m_name;
1418 3134 : m_nameSet = other.m_nameSet;
1419 3134 : m_poDS = other.m_poDS;
1420 3134 : if (m_poDS)
1421 2200 : m_poDS->Reference();
1422 3134 : }
1423 :
1424 : /************************************************************************/
1425 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1426 : /************************************************************************/
1427 :
1428 29702 : GDALArgDatasetValue::~GDALArgDatasetValue()
1429 : {
1430 29702 : Close();
1431 29702 : }
1432 :
1433 : /************************************************************************/
1434 : /* GDALArgDatasetValue::Close() */
1435 : /************************************************************************/
1436 :
1437 49199 : bool GDALArgDatasetValue::Close()
1438 : {
1439 49199 : bool ret = true;
1440 49199 : if (m_poDS && m_poDS->Dereference() == 0)
1441 : {
1442 3128 : ret = m_poDS->Close() == CE_None;
1443 3128 : delete m_poDS;
1444 : }
1445 49199 : m_poDS = nullptr;
1446 49199 : return ret;
1447 : }
1448 :
1449 : /************************************************************************/
1450 : /* GDALArgDatasetValue::operator=() */
1451 : /************************************************************************/
1452 :
1453 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1454 : {
1455 2 : Close();
1456 2 : m_poDS = other.m_poDS;
1457 2 : m_name = other.m_name;
1458 2 : m_nameSet = other.m_nameSet;
1459 2 : other.m_poDS = nullptr;
1460 2 : other.m_name.clear();
1461 2 : other.m_nameSet = false;
1462 2 : return *this;
1463 : }
1464 :
1465 : /************************************************************************/
1466 : /* GDALArgDatasetValue::GetDataset() */
1467 : /************************************************************************/
1468 :
1469 997 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1470 : {
1471 997 : if (m_poDS)
1472 997 : m_poDS->Reference();
1473 997 : return m_poDS;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1478 : /************************************************************************/
1479 :
1480 2951 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1481 2951 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1482 : {
1483 2951 : other.m_poDS = nullptr;
1484 2951 : other.m_name.clear();
1485 2951 : }
1486 :
1487 : /************************************************************************/
1488 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1489 : /************************************************************************/
1490 :
1491 3089 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1492 : bool noneAllowed, const std::vector<std::string> &specialValues)
1493 : {
1494 3089 : if (GetType() != GAAT_STRING)
1495 : {
1496 1 : CPLError(CE_Failure, CPLE_AppDefined,
1497 : "SetIsCRSArg() can only be called on a String argument");
1498 1 : return *this;
1499 : }
1500 : AddValidationAction(
1501 546 : [this, noneAllowed, specialValues]()
1502 : {
1503 : const std::string &osVal =
1504 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1505 270 : ->Get<std::string>();
1506 270 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1507 0 : return true;
1508 :
1509 527 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1510 257 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1511 527 : specialValues.end())
1512 : {
1513 249 : OGRSpatialReference oSRS;
1514 249 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1515 : {
1516 6 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1517 : "Invalid value for '%s' argument",
1518 6 : GetName().c_str());
1519 6 : return false;
1520 : }
1521 : }
1522 264 : return true;
1523 3088 : });
1524 :
1525 : SetAutoCompleteFunction(
1526 40 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1527 : {
1528 10 : bool bIsRaster = false;
1529 10 : OGREnvelope sDatasetLongLatEnv;
1530 20 : std::string osCelestialBodyName;
1531 10 : if (GetName() == "dst-crs")
1532 : {
1533 10 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1534 10 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1535 : {
1536 : auto &val =
1537 10 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1538 10 : if (val.size() == 1)
1539 : {
1540 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1541 : auto poDS = std::unique_ptr<GDALDataset>(
1542 4 : GDALDataset::Open(val[0].GetName().c_str()));
1543 2 : if (poDS)
1544 : {
1545 2 : bIsRaster = poDS->GetRasterCount() != 0;
1546 2 : if (auto poCRS = poDS->GetSpatialRef())
1547 : {
1548 : const char *pszCelestialBodyName =
1549 2 : poCRS->GetCelestialBodyName();
1550 2 : if (pszCelestialBodyName)
1551 2 : osCelestialBodyName = pszCelestialBodyName;
1552 :
1553 2 : if (!pszCelestialBodyName ||
1554 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1555 : {
1556 0 : OGRSpatialReference oLongLat;
1557 0 : oLongLat.CopyGeogCSFrom(poCRS);
1558 0 : oLongLat.SetAxisMappingStrategy(
1559 : OAMS_TRADITIONAL_GIS_ORDER);
1560 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1561 0 : &oLongLat);
1562 : }
1563 : else
1564 : {
1565 2 : poDS->GetExtentWGS84LongLat(
1566 2 : &sDatasetLongLatEnv);
1567 : }
1568 : }
1569 : }
1570 : }
1571 : }
1572 : }
1573 :
1574 : const auto IsCRSCompatible =
1575 42959 : [bIsRaster, &sDatasetLongLatEnv,
1576 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1577 : {
1578 42959 : if (!sDatasetLongLatEnv.IsInit())
1579 30685 : return true;
1580 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1581 11834 : !(bIsRaster &&
1582 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1583 11652 : crsInfo->dfWestLongitudeDeg <
1584 11652 : crsInfo->dfEastLongitudeDeg &&
1585 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1586 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1587 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1588 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1589 329 : ((!osCelestialBodyName.empty() &&
1590 658 : crsInfo->pszCelestialBodyName &&
1591 329 : osCelestialBodyName ==
1592 329 : crsInfo->pszCelestialBodyName) ||
1593 0 : (osCelestialBodyName.empty() &&
1594 12274 : !crsInfo->pszCelestialBodyName));
1595 10 : };
1596 :
1597 10 : std::vector<std::string> oRet;
1598 10 : if (noneAllowed)
1599 0 : oRet.push_back("none");
1600 10 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1601 10 : if (!currentValue.empty())
1602 : {
1603 : const CPLStringList aosTokens(
1604 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1605 7 : int nCount = 0;
1606 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1607 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1608 : nullptr, &nCount),
1609 14 : OSRDestroyCRSInfoList);
1610 14 : std::string osCode;
1611 :
1612 14 : std::vector<const OSRCRSInfo *> candidates;
1613 46270 : for (int i = 0; i < nCount; ++i)
1614 : {
1615 46263 : const auto *entry = (pCRSList.get())[i];
1616 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1617 : {
1618 49425 : if (aosTokens.size() == 1 ||
1619 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1620 : {
1621 12666 : if (candidates.empty())
1622 7 : osCode = entry->pszCode;
1623 12666 : candidates.push_back(entry);
1624 : }
1625 : }
1626 : }
1627 7 : if (candidates.size() == 1)
1628 : {
1629 1 : oRet.push_back(std::move(osCode));
1630 : }
1631 : else
1632 : {
1633 6 : if (sDatasetLongLatEnv.IsInit())
1634 : {
1635 2 : std::sort(
1636 : candidates.begin(), candidates.end(),
1637 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1638 : {
1639 2999 : const double dfXa =
1640 2999 : a->dfWestLongitudeDeg >
1641 2999 : a->dfEastLongitudeDeg
1642 2999 : ? a->dfWestLongitudeDeg -
1643 0 : a->dfEastLongitudeDeg
1644 2999 : : (180 - a->dfWestLongitudeDeg) +
1645 2999 : (a->dfEastLongitudeDeg - -180);
1646 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1647 2999 : a->dfSouthLatitudeDeg;
1648 2999 : const double dfXb =
1649 2999 : b->dfWestLongitudeDeg >
1650 2999 : b->dfEastLongitudeDeg
1651 2999 : ? b->dfWestLongitudeDeg -
1652 0 : b->dfEastLongitudeDeg
1653 2999 : : (180 - b->dfWestLongitudeDeg) +
1654 2999 : (b->dfEastLongitudeDeg - -180);
1655 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1656 2999 : b->dfSouthLatitudeDeg;
1657 2999 : const double diffArea =
1658 2999 : dfXa * dfYa - dfXb * dfYb;
1659 2999 : if (diffArea < 0)
1660 279 : return true;
1661 2720 : if (diffArea == 0)
1662 : {
1663 2506 : if (std::string_view(a->pszName) ==
1664 2506 : b->pszName)
1665 : {
1666 57 : if (a->eType ==
1667 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1668 13 : b->eType !=
1669 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1670 13 : return true;
1671 44 : if (a->eType ==
1672 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1673 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1674 9 : return true;
1675 35 : return false;
1676 : }
1677 4898 : return std::string_view(a->pszCode) <
1678 4898 : b->pszCode;
1679 : }
1680 214 : return false;
1681 : });
1682 : }
1683 :
1684 12671 : for (const auto *entry : candidates)
1685 : {
1686 25330 : std::string val = std::string(entry->pszCode)
1687 12665 : .append(" -- ")
1688 25330 : .append(entry->pszName);
1689 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1690 1294 : val.append(" (geographic 2D)");
1691 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1692 446 : val.append(" (geographic 3D)");
1693 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1694 397 : val.append(" (geocentric)");
1695 12665 : oRet.push_back(std::move(val));
1696 : }
1697 : }
1698 : }
1699 10 : if (currentValue.empty() || oRet.empty())
1700 : {
1701 : const CPLStringList aosAuthorities(
1702 6 : OSRGetAuthorityListFromDatabase());
1703 18 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1704 : {
1705 15 : int nCount = 0;
1706 15 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1707 : pszAuth, nullptr, &nCount));
1708 15 : if (nCount)
1709 12 : oRet.push_back(std::string(pszAuth).append(":"));
1710 : }
1711 : }
1712 20 : return oRet;
1713 3088 : });
1714 :
1715 3088 : return *this;
1716 : }
1717 :
1718 : /************************************************************************/
1719 : /* GDALAlgorithm::GDALAlgorithm() */
1720 : /************************************************************************/
1721 :
1722 20358 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1723 : const std::string &description,
1724 20358 : const std::string &helpURL)
1725 : : m_name(name), m_description(description), m_helpURL(helpURL),
1726 40563 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1727 20358 : ? "https://gdal.org" + m_helpURL
1728 60705 : : m_helpURL)
1729 : {
1730 : auto &helpArg =
1731 : AddArg("help", 'h', _("Display help message and exit"),
1732 40716 : &m_helpRequested)
1733 20358 : .SetHiddenForAPI()
1734 40716 : .SetCategory(GAAC_COMMON)
1735 14 : .AddAction([this]()
1736 20358 : { m_specialActionRequested = m_calledFromCommandLine; });
1737 : auto &helpDocArg =
1738 : AddArg("help-doc", 0,
1739 : _("Display help message for use by documentation"),
1740 40716 : &m_helpDocRequested)
1741 20358 : .SetHidden()
1742 12 : .AddAction([this]()
1743 20358 : { m_specialActionRequested = m_calledFromCommandLine; });
1744 : auto &jsonUsageArg =
1745 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1746 40716 : &m_JSONUsageRequested)
1747 20358 : .SetHiddenForAPI()
1748 40716 : .SetCategory(GAAC_COMMON)
1749 4 : .AddAction([this]()
1750 20358 : { m_specialActionRequested = m_calledFromCommandLine; });
1751 40716 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1752 40716 : .SetMetaVar("<KEY>=<VALUE>")
1753 20358 : .SetHiddenForAPI()
1754 40716 : .SetCategory(GAAC_COMMON)
1755 : .AddAction(
1756 2 : [this]()
1757 : {
1758 2 : ReportError(
1759 : CE_Warning, CPLE_AppDefined,
1760 : "Configuration options passed with the 'config' argument "
1761 : "are ignored");
1762 20358 : });
1763 :
1764 20358 : AddValidationAction(
1765 12305 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1766 : {
1767 6349 : if (!m_calledFromCommandLine && m_specialActionRequested)
1768 : {
1769 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1770 : {
1771 0 : if (arg->IsExplicitlySet())
1772 : {
1773 0 : ReportError(CE_Failure, CPLE_AppDefined,
1774 : "'%s' argument only available when called "
1775 : "from command line",
1776 0 : arg->GetName().c_str());
1777 0 : return false;
1778 : }
1779 : }
1780 : }
1781 6349 : return true;
1782 : });
1783 20358 : }
1784 :
1785 : /************************************************************************/
1786 : /* GDALAlgorithm::~GDALAlgorithm() */
1787 : /************************************************************************/
1788 :
1789 : GDALAlgorithm::~GDALAlgorithm() = default;
1790 :
1791 : /************************************************************************/
1792 : /* GDALAlgorithm::ParseArgument() */
1793 : /************************************************************************/
1794 :
1795 2971 : bool GDALAlgorithm::ParseArgument(
1796 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1797 : std::map<
1798 : GDALAlgorithmArg *,
1799 : std::variant<std::vector<std::string>, std::vector<int>,
1800 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1801 : &inConstructionValues)
1802 : {
1803 2971 : const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1804 2971 : if (arg->IsExplicitlySet() && !isListArg)
1805 : {
1806 : // Hack for "gdal info" to be able to pass an opened raster dataset
1807 : // by "gdal raster info" to the "gdal vector info" algorithm.
1808 3 : if (arg->SkipIfAlreadySet())
1809 : {
1810 1 : arg->SetSkipIfAlreadySet(false);
1811 1 : return true;
1812 : }
1813 :
1814 2 : ReportError(CE_Failure, CPLE_IllegalArg,
1815 : "Argument '%s' has already been specified.", name.c_str());
1816 2 : return false;
1817 : }
1818 :
1819 3034 : if (!arg->GetRepeatedArgAllowed() &&
1820 66 : cpl::contains(inConstructionValues, arg))
1821 : {
1822 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1823 : "Argument '%s' has already been specified.", name.c_str());
1824 1 : return false;
1825 : }
1826 :
1827 2967 : switch (arg->GetType())
1828 : {
1829 304 : case GAAT_BOOLEAN:
1830 : {
1831 304 : if (value.empty() || value == "true")
1832 302 : return arg->Set(true);
1833 2 : else if (value == "false")
1834 1 : return arg->Set(false);
1835 : else
1836 : {
1837 1 : ReportError(
1838 : CE_Failure, CPLE_IllegalArg,
1839 : "Invalid value '%s' for boolean argument '%s'. Should be "
1840 : "'true' or 'false'.",
1841 : value.c_str(), name.c_str());
1842 1 : return false;
1843 : }
1844 : }
1845 :
1846 736 : case GAAT_STRING:
1847 : {
1848 736 : return arg->Set(value);
1849 : }
1850 :
1851 350 : case GAAT_INTEGER:
1852 : {
1853 350 : errno = 0;
1854 350 : char *endptr = nullptr;
1855 350 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1856 349 : if (errno == 0 && endptr &&
1857 699 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1858 : val <= INT_MAX)
1859 : {
1860 347 : return arg->Set(static_cast<int>(val));
1861 : }
1862 : else
1863 : {
1864 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1865 : "Expected integer value for argument '%s', "
1866 : "but got '%s'.",
1867 : name.c_str(), value.c_str());
1868 3 : return false;
1869 : }
1870 : }
1871 :
1872 32 : case GAAT_REAL:
1873 : {
1874 32 : char *endptr = nullptr;
1875 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
1876 32 : if (endptr != value.c_str() + value.size())
1877 : {
1878 1 : ReportError(
1879 : CE_Failure, CPLE_IllegalArg,
1880 : "Expected real value for argument '%s', but got '%s'.",
1881 : name.c_str(), value.c_str());
1882 1 : return false;
1883 : }
1884 31 : return arg->Set(dfValue);
1885 : }
1886 :
1887 522 : case GAAT_DATASET:
1888 : {
1889 522 : return arg->SetDatasetName(value);
1890 : }
1891 :
1892 252 : case GAAT_STRING_LIST:
1893 : {
1894 : const CPLStringList aosTokens(
1895 252 : arg->GetPackedValuesAllowed()
1896 161 : ? CSLTokenizeString2(value.c_str(), ",",
1897 : CSLT_HONOURSTRINGS |
1898 : CSLT_PRESERVEQUOTES)
1899 665 : : CSLAddString(nullptr, value.c_str()));
1900 252 : if (!cpl::contains(inConstructionValues, arg))
1901 : {
1902 228 : inConstructionValues[arg] = std::vector<std::string>();
1903 : }
1904 : auto &valueVector =
1905 252 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
1906 531 : for (const char *v : aosTokens)
1907 : {
1908 279 : valueVector.push_back(v);
1909 : }
1910 252 : break;
1911 : }
1912 :
1913 60 : case GAAT_INTEGER_LIST:
1914 : {
1915 : const CPLStringList aosTokens(
1916 60 : arg->GetPackedValuesAllowed()
1917 60 : ? CSLTokenizeString2(
1918 : value.c_str(), ",",
1919 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1920 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1921 120 : : CSLAddString(nullptr, value.c_str()));
1922 60 : if (!cpl::contains(inConstructionValues, arg))
1923 : {
1924 56 : inConstructionValues[arg] = std::vector<int>();
1925 : }
1926 : auto &valueVector =
1927 60 : std::get<std::vector<int>>(inConstructionValues[arg]);
1928 185 : for (const char *v : aosTokens)
1929 : {
1930 131 : errno = 0;
1931 131 : char *endptr = nullptr;
1932 131 : const auto val = std::strtol(v, &endptr, 10);
1933 131 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
1934 127 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
1935 : {
1936 125 : valueVector.push_back(static_cast<int>(val));
1937 : }
1938 : else
1939 : {
1940 6 : ReportError(
1941 : CE_Failure, CPLE_IllegalArg,
1942 : "Expected list of integer value for argument '%s', "
1943 : "but got '%s'.",
1944 : name.c_str(), value.c_str());
1945 6 : return false;
1946 : }
1947 : }
1948 54 : break;
1949 : }
1950 :
1951 96 : case GAAT_REAL_LIST:
1952 : {
1953 : const CPLStringList aosTokens(
1954 96 : arg->GetPackedValuesAllowed()
1955 96 : ? CSLTokenizeString2(
1956 : value.c_str(), ",",
1957 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
1958 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
1959 192 : : CSLAddString(nullptr, value.c_str()));
1960 96 : if (!cpl::contains(inConstructionValues, arg))
1961 : {
1962 94 : inConstructionValues[arg] = std::vector<double>();
1963 : }
1964 : auto &valueVector =
1965 96 : std::get<std::vector<double>>(inConstructionValues[arg]);
1966 388 : for (const char *v : aosTokens)
1967 : {
1968 296 : char *endptr = nullptr;
1969 296 : double dfValue = CPLStrtod(v, &endptr);
1970 296 : if (strlen(v) == 0 || endptr != v + strlen(v))
1971 : {
1972 4 : ReportError(
1973 : CE_Failure, CPLE_IllegalArg,
1974 : "Expected list of real value for argument '%s', "
1975 : "but got '%s'.",
1976 : name.c_str(), value.c_str());
1977 4 : return false;
1978 : }
1979 292 : valueVector.push_back(dfValue);
1980 : }
1981 92 : break;
1982 : }
1983 :
1984 615 : case GAAT_DATASET_LIST:
1985 : {
1986 615 : if (!cpl::contains(inConstructionValues, arg))
1987 : {
1988 603 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
1989 : }
1990 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1991 615 : inConstructionValues[arg]);
1992 615 : if (!value.empty() && value[0] == '{' && value.back() == '}')
1993 : {
1994 12 : valueVector.push_back(GDALArgDatasetValue(value));
1995 : }
1996 : else
1997 : {
1998 : const CPLStringList aosTokens(
1999 603 : arg->GetPackedValuesAllowed()
2000 4 : ? CSLTokenizeString2(value.c_str(), ",",
2001 : CSLT_HONOURSTRINGS |
2002 : CSLT_STRIPLEADSPACES)
2003 1210 : : CSLAddString(nullptr, value.c_str()));
2004 1206 : for (const char *v : aosTokens)
2005 : {
2006 603 : valueVector.push_back(GDALArgDatasetValue(v));
2007 : }
2008 : }
2009 615 : break;
2010 : }
2011 : }
2012 :
2013 1013 : return true;
2014 : }
2015 :
2016 : /************************************************************************/
2017 : /* GDALAlgorithm::ParseCommandLineArguments() */
2018 : /************************************************************************/
2019 :
2020 1960 : bool GDALAlgorithm::ParseCommandLineArguments(
2021 : const std::vector<std::string> &args)
2022 : {
2023 1960 : if (m_parsedSubStringAlreadyCalled)
2024 : {
2025 6 : ReportError(CE_Failure, CPLE_AppDefined,
2026 : "ParseCommandLineArguments() can only be called once per "
2027 : "instance.");
2028 6 : return false;
2029 : }
2030 1954 : m_parsedSubStringAlreadyCalled = true;
2031 :
2032 : // AWS like syntax supported too (not advertized)
2033 1954 : if (args.size() == 1 && args[0] == "help")
2034 : {
2035 1 : auto arg = GetArg("help");
2036 1 : assert(arg);
2037 1 : arg->Set(true);
2038 1 : arg->RunActions();
2039 1 : return true;
2040 : }
2041 :
2042 1953 : if (HasSubAlgorithms())
2043 : {
2044 423 : if (args.empty())
2045 : {
2046 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2047 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2048 2 : return false;
2049 : }
2050 421 : if (!args[0].empty() && args[0][0] == '-')
2051 : {
2052 : // go on argument parsing
2053 : }
2054 : else
2055 : {
2056 418 : const auto nCounter = CPLGetErrorCounter();
2057 418 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2058 418 : if (m_selectedSubAlgHolder)
2059 : {
2060 415 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2061 415 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2062 415 : m_referencePath);
2063 415 : m_selectedSubAlg->m_executionForStreamOutput =
2064 415 : m_executionForStreamOutput;
2065 415 : m_selectedSubAlg->m_calledFromCommandLine =
2066 415 : m_calledFromCommandLine;
2067 415 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2068 415 : m_skipValidationInParseCommandLine;
2069 415 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2070 830 : std::vector<std::string>(args.begin() + 1, args.end()));
2071 415 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2072 415 : return bRet;
2073 : }
2074 : else
2075 : {
2076 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2077 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2078 : {
2079 2 : ReportError(CE_Failure, CPLE_AppDefined,
2080 2 : "Unknown command: '%s'", args[0].c_str());
2081 : }
2082 3 : return false;
2083 : }
2084 : }
2085 : }
2086 :
2087 : std::map<
2088 : GDALAlgorithmArg *,
2089 : std::variant<std::vector<std::string>, std::vector<int>,
2090 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2091 3066 : inConstructionValues;
2092 :
2093 3066 : std::vector<std::string> lArgs(args);
2094 1533 : bool helpValueRequested = false;
2095 4509 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2096 : {
2097 3075 : const auto &strArg = lArgs[i];
2098 3075 : GDALAlgorithmArg *arg = nullptr;
2099 3075 : std::string name;
2100 3075 : std::string value;
2101 3075 : bool hasValue = false;
2102 3075 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2103 5 : helpValueRequested = true;
2104 3075 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2105 : {
2106 1964 : const auto equalPos = strArg.find('=');
2107 3928 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2108 1964 : : strArg;
2109 1964 : const std::string nameWithoutDash = name.substr(2);
2110 1964 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2111 2017 : if (m_arbitraryLongNameArgsAllowed &&
2112 2017 : iterArg == m_mapLongNameToArg.end())
2113 : {
2114 16 : GetArg(nameWithoutDash);
2115 16 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2116 : }
2117 1964 : if (iterArg == m_mapLongNameToArg.end())
2118 : {
2119 : const std::string bestCandidate =
2120 26 : GetSuggestionForArgumentName(nameWithoutDash);
2121 26 : if (!bestCandidate.empty())
2122 : {
2123 2 : ReportError(CE_Failure, CPLE_IllegalArg,
2124 : "Option '%s' is unknown. Do you mean '--%s'?",
2125 : name.c_str(), bestCandidate.c_str());
2126 : }
2127 : else
2128 : {
2129 24 : ReportError(CE_Failure, CPLE_IllegalArg,
2130 : "Option '%s' is unknown.", name.c_str());
2131 : }
2132 26 : return false;
2133 : }
2134 1938 : arg = iterArg->second;
2135 1938 : if (equalPos != std::string::npos)
2136 : {
2137 417 : hasValue = true;
2138 417 : value = strArg.substr(equalPos + 1);
2139 : }
2140 : }
2141 1185 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2142 74 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2143 : {
2144 143 : for (size_t j = 1; j < strArg.size(); ++j)
2145 : {
2146 74 : name.clear();
2147 74 : name += strArg[j];
2148 74 : const auto iterArg = m_mapShortNameToArg.find(name);
2149 74 : if (iterArg == m_mapShortNameToArg.end())
2150 : {
2151 5 : const std::string nameWithoutDash = strArg.substr(1);
2152 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2153 10 : m_mapLongNameToArg.end())
2154 : {
2155 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2156 : "Short name option '%s' is unknown. Do you "
2157 : "mean '--%s' (with leading double dash) ?",
2158 : name.c_str(), nameWithoutDash.c_str());
2159 : }
2160 : else
2161 : {
2162 : const std::string bestCandidate =
2163 8 : GetSuggestionForArgumentName(nameWithoutDash);
2164 4 : if (!bestCandidate.empty())
2165 : {
2166 1 : ReportError(
2167 : CE_Failure, CPLE_IllegalArg,
2168 : "Short name option '%s' is unknown. Do you "
2169 : "mean '--%s' (with leading double dash) ?",
2170 : name.c_str(), bestCandidate.c_str());
2171 : }
2172 : else
2173 : {
2174 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2175 : "Short name option '%s' is unknown.",
2176 : name.c_str());
2177 : }
2178 : }
2179 5 : return false;
2180 : }
2181 69 : arg = iterArg->second;
2182 69 : if (strArg.size() > 2)
2183 : {
2184 0 : if (arg->GetType() != GAAT_BOOLEAN)
2185 : {
2186 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2187 : "Invalid argument '%s'. Option '%s' is not "
2188 : "a boolean option.",
2189 : strArg.c_str(), name.c_str());
2190 0 : return false;
2191 : }
2192 :
2193 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2194 0 : return false;
2195 : }
2196 : }
2197 69 : if (strArg.size() > 2)
2198 : {
2199 0 : lArgs.erase(lArgs.begin() + i);
2200 0 : continue;
2201 : }
2202 : }
2203 : else
2204 : {
2205 1037 : ++i;
2206 1037 : continue;
2207 : }
2208 2007 : CPLAssert(arg);
2209 :
2210 2007 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2211 : {
2212 305 : if (!hasValue)
2213 : {
2214 302 : hasValue = true;
2215 302 : value = "true";
2216 : }
2217 : }
2218 :
2219 2007 : if (!hasValue)
2220 : {
2221 1288 : if (i + 1 == lArgs.size())
2222 : {
2223 30 : if (m_parseForAutoCompletion)
2224 : {
2225 24 : lArgs.erase(lArgs.begin() + i);
2226 24 : break;
2227 : }
2228 6 : ReportError(
2229 : CE_Failure, CPLE_IllegalArg,
2230 : "Expected value for argument '%s', but ran short of tokens",
2231 : name.c_str());
2232 6 : return false;
2233 : }
2234 1258 : value = lArgs[i + 1];
2235 1258 : lArgs.erase(lArgs.begin() + i + 1);
2236 : }
2237 :
2238 1977 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2239 38 : return false;
2240 :
2241 1939 : lArgs.erase(lArgs.begin() + i);
2242 : }
2243 :
2244 1458 : if (m_specialActionRequested)
2245 : {
2246 23 : return true;
2247 : }
2248 :
2249 2374 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2250 : {
2251 2345 : for (auto &[arg, value] : inConstructionValues)
2252 : {
2253 963 : if (arg->GetType() == GAAT_STRING_LIST)
2254 : {
2255 224 : if (!arg->Set(std::get<std::vector<std::string>>(
2256 224 : inConstructionValues[arg])))
2257 : {
2258 29 : return false;
2259 : }
2260 : }
2261 739 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2262 : {
2263 51 : if (!arg->Set(
2264 51 : std::get<std::vector<int>>(inConstructionValues[arg])))
2265 : {
2266 3 : return false;
2267 : }
2268 : }
2269 688 : else if (arg->GetType() == GAAT_REAL_LIST)
2270 : {
2271 90 : if (!arg->Set(std::get<std::vector<double>>(
2272 90 : inConstructionValues[arg])))
2273 : {
2274 8 : return false;
2275 : }
2276 : }
2277 598 : else if (arg->GetType() == GAAT_DATASET_LIST)
2278 : {
2279 598 : if (!arg->Set(
2280 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2281 598 : inConstructionValues[arg]))))
2282 : {
2283 1 : return false;
2284 : }
2285 : }
2286 : }
2287 1382 : return true;
2288 1435 : };
2289 :
2290 : // Process positional arguments that have not been set through their
2291 : // option name.
2292 1435 : size_t i = 0;
2293 1435 : size_t iCurPosArg = 0;
2294 :
2295 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2296 1456 : if (m_positionalArgs.size() == 3 &&
2297 22 : (m_positionalArgs[0]->IsRequired() ||
2298 21 : m_positionalArgs[0]->GetMinCount() == 1) &&
2299 40 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2300 27 : (m_positionalArgs[1]->IsRequired() ||
2301 27 : m_positionalArgs[1]->GetMinCount() == 1) &&
2302 : /* Second argument may have several occurrences */
2303 40 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2304 20 : (m_positionalArgs[2]->IsRequired() ||
2305 20 : m_positionalArgs[2]->GetMinCount() == 1) &&
2306 20 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2307 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2308 1465 : !m_positionalArgs[1]->IsExplicitlySet() &&
2309 9 : !m_positionalArgs[2]->IsExplicitlySet())
2310 : {
2311 7 : if (lArgs.size() - i < 3)
2312 : {
2313 1 : ReportError(CE_Failure, CPLE_AppDefined,
2314 : "Not enough positional values.");
2315 1 : return false;
2316 : }
2317 12 : bool ok = ParseArgument(m_positionalArgs[0],
2318 6 : m_positionalArgs[0]->GetName().c_str(),
2319 6 : lArgs[i], inConstructionValues);
2320 6 : if (ok)
2321 : {
2322 5 : ++i;
2323 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2324 : {
2325 12 : ok = ParseArgument(m_positionalArgs[1],
2326 6 : m_positionalArgs[1]->GetName().c_str(),
2327 6 : lArgs[i], inConstructionValues);
2328 : }
2329 : }
2330 6 : if (ok)
2331 : {
2332 10 : ok = ParseArgument(m_positionalArgs[2],
2333 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2334 : inConstructionValues);
2335 5 : ++i;
2336 : }
2337 6 : if (!ok)
2338 : {
2339 3 : ProcessInConstructionValues();
2340 3 : return false;
2341 : }
2342 : }
2343 :
2344 493 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2345 575 : !m_positionalArgs[0]->IsExplicitlySet() &&
2346 2237 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2347 114 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2348 57 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2349 : {
2350 57 : ++iCurPosArg;
2351 : }
2352 :
2353 2384 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2354 : {
2355 960 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2356 963 : while (arg->IsExplicitlySet())
2357 : {
2358 4 : ++iCurPosArg;
2359 4 : if (iCurPosArg == m_positionalArgs.size())
2360 1 : break;
2361 3 : arg = m_positionalArgs[iCurPosArg];
2362 : }
2363 960 : if (iCurPosArg == m_positionalArgs.size())
2364 : {
2365 1 : break;
2366 : }
2367 1479 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2368 520 : arg->GetMinCount() != arg->GetMaxCount())
2369 : {
2370 104 : if (iCurPosArg == 0)
2371 : {
2372 68 : size_t nCountAtEnd = 0;
2373 93 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2374 : {
2375 27 : const auto *otherArg = m_positionalArgs[j];
2376 27 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2377 : {
2378 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2379 : {
2380 2 : ReportError(
2381 : CE_Failure, CPLE_AppDefined,
2382 : "Ambiguity in definition of positional "
2383 : "argument "
2384 : "'%s' given it has a varying number of values, "
2385 : "but follows argument '%s' which also has a "
2386 : "varying number of values",
2387 1 : otherArg->GetName().c_str(),
2388 1 : arg->GetName().c_str());
2389 1 : ProcessInConstructionValues();
2390 1 : return false;
2391 : }
2392 3 : nCountAtEnd += otherArg->GetMinCount();
2393 : }
2394 : else
2395 : {
2396 23 : if (!otherArg->IsRequired())
2397 : {
2398 2 : ReportError(
2399 : CE_Failure, CPLE_AppDefined,
2400 : "Ambiguity in definition of positional "
2401 : "argument "
2402 : "'%s', given it is not required but follows "
2403 : "argument '%s' which has a varying number of "
2404 : "values",
2405 1 : otherArg->GetName().c_str(),
2406 1 : arg->GetName().c_str());
2407 1 : ProcessInConstructionValues();
2408 1 : return false;
2409 : }
2410 22 : nCountAtEnd++;
2411 : }
2412 : }
2413 66 : if (lArgs.size() < nCountAtEnd)
2414 : {
2415 1 : ReportError(CE_Failure, CPLE_AppDefined,
2416 : "Not enough positional values.");
2417 1 : ProcessInConstructionValues();
2418 1 : return false;
2419 : }
2420 137 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2421 : {
2422 72 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2423 : inConstructionValues))
2424 : {
2425 0 : ProcessInConstructionValues();
2426 0 : return false;
2427 : }
2428 : }
2429 : }
2430 36 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2431 : {
2432 82 : for (; i < lArgs.size(); ++i)
2433 : {
2434 47 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2435 : inConstructionValues))
2436 : {
2437 0 : ProcessInConstructionValues();
2438 0 : return false;
2439 : }
2440 : }
2441 : }
2442 : else
2443 : {
2444 1 : ReportError(CE_Failure, CPLE_AppDefined,
2445 : "Ambiguity in definition of positional arguments: "
2446 : "arguments with varying number of values must be "
2447 : "first or last one.");
2448 1 : return false;
2449 : }
2450 : }
2451 : else
2452 : {
2453 855 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2454 : {
2455 1 : ReportError(CE_Failure, CPLE_AppDefined,
2456 : "Not enough positional values.");
2457 1 : return false;
2458 : }
2459 854 : const size_t iMax = i + arg->GetMaxCount();
2460 1711 : for (; i < iMax; ++i)
2461 : {
2462 858 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2463 : inConstructionValues))
2464 : {
2465 1 : ProcessInConstructionValues();
2466 1 : return false;
2467 : }
2468 : }
2469 : }
2470 953 : ++iCurPosArg;
2471 : }
2472 :
2473 1425 : if (i < lArgs.size())
2474 : {
2475 21 : ReportError(CE_Failure, CPLE_AppDefined,
2476 : "Positional values starting at '%s' are not expected.",
2477 21 : lArgs[i].c_str());
2478 21 : return false;
2479 : }
2480 :
2481 1404 : if (!ProcessInConstructionValues())
2482 : {
2483 28 : return false;
2484 : }
2485 :
2486 : // Skip to first unset positional argument.
2487 2370 : while (iCurPosArg < m_positionalArgs.size() &&
2488 536 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2489 : {
2490 458 : ++iCurPosArg;
2491 : }
2492 : // Check if this positional argument is required.
2493 1453 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2494 77 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2495 47 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2496 30 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2497 : {
2498 66 : ReportError(CE_Failure, CPLE_AppDefined,
2499 : "Positional arguments starting at '%s' have not been "
2500 : "specified.",
2501 66 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2502 66 : return false;
2503 : }
2504 :
2505 1310 : if (m_calledFromCommandLine)
2506 : {
2507 4793 : for (auto &arg : m_args)
2508 : {
2509 6312 : if (arg->IsExplicitlySet() &&
2510 1021 : ((arg->GetType() == GAAT_STRING &&
2511 1018 : arg->Get<std::string>() == "?") ||
2512 936 : (arg->GetType() == GAAT_STRING_LIST &&
2513 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2514 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2515 : {
2516 : {
2517 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2518 5 : ValidateArguments();
2519 : }
2520 :
2521 5 : auto choices = arg->GetChoices();
2522 5 : if (choices.empty())
2523 2 : choices = arg->GetAutoCompleteChoices(std::string());
2524 5 : if (!choices.empty())
2525 : {
2526 5 : if (choices.size() == 1)
2527 : {
2528 4 : ReportError(
2529 : CE_Failure, CPLE_AppDefined,
2530 : "Single potential value for argument '%s' is '%s'",
2531 4 : arg->GetName().c_str(), choices.front().c_str());
2532 : }
2533 : else
2534 : {
2535 6 : std::string msg("Potential values for argument '");
2536 3 : msg += arg->GetName();
2537 3 : msg += "' are:";
2538 45 : for (const auto &v : choices)
2539 : {
2540 42 : msg += "\n- ";
2541 42 : msg += v;
2542 : }
2543 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2544 : msg.c_str());
2545 : }
2546 5 : return false;
2547 : }
2548 : }
2549 : }
2550 : }
2551 :
2552 1305 : return m_skipValidationInParseCommandLine || ValidateArguments();
2553 : }
2554 :
2555 : /************************************************************************/
2556 : /* GDALAlgorithm::ReportError() */
2557 : /************************************************************************/
2558 :
2559 : //! @cond Doxygen_Suppress
2560 824 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2561 : const char *fmt, ...) const
2562 : {
2563 : va_list args;
2564 824 : va_start(args, fmt);
2565 824 : CPLError(eErrClass, err_no, "%s",
2566 824 : std::string(m_name)
2567 824 : .append(": ")
2568 1648 : .append(CPLString().vPrintf(fmt, args))
2569 : .c_str());
2570 824 : va_end(args);
2571 824 : }
2572 :
2573 : //! @endcond
2574 :
2575 : /************************************************************************/
2576 : /* GDALAlgorithm::ProcessDatasetArg() */
2577 : /************************************************************************/
2578 :
2579 9025 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2580 : GDALAlgorithm *algForOutput)
2581 : {
2582 9025 : bool ret = true;
2583 :
2584 9025 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2585 9025 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2586 9025 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2587 :
2588 9025 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2589 9025 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2590 9025 : const bool append = hasAppendArg && appendArg->Get<bool>();
2591 :
2592 9025 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2593 : const bool overwrite =
2594 15060 : (arg->IsOutput() && overwriteArg &&
2595 15060 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2596 :
2597 9025 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2598 18050 : auto &val = [arg]() -> GDALArgDatasetValue &
2599 : {
2600 9025 : if (arg->GetType() == GAAT_DATASET_LIST)
2601 5027 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2602 : else
2603 3998 : return arg->Get<GDALArgDatasetValue>();
2604 9025 : }();
2605 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2606 14164 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2607 14172 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2608 8 : !overwrite;
2609 9025 : if (!val.GetDatasetRef() && !val.IsNameSet())
2610 : {
2611 3 : ReportError(CE_Failure, CPLE_AppDefined,
2612 : "Argument '%s' has no dataset object or dataset name.",
2613 3 : arg->GetName().c_str());
2614 3 : ret = false;
2615 : }
2616 9022 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2617 : {
2618 1 : return false;
2619 : }
2620 199 : else if (m_inputDatasetCanBeOmitted &&
2621 9220 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2622 8 : !arg->IsOutput())
2623 : {
2624 8 : return true;
2625 : }
2626 13170 : else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2627 4157 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2628 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2629 : {
2630 1256 : int flags = arg->GetDatasetType();
2631 1256 : bool assignToOutputArg = false;
2632 :
2633 : // Check if input and output parameters point to the same
2634 : // filename (for vector datasets)
2635 2331 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2636 2331 : outputArg && outputArg->GetType() == GAAT_DATASET)
2637 : {
2638 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2639 121 : if (!outputVal.GetDatasetRef() &&
2640 121 : outputVal.GetName() == val.GetName() &&
2641 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2642 : {
2643 2 : assignToOutputArg = true;
2644 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2645 : }
2646 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2647 : {
2648 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2649 : }
2650 : }
2651 :
2652 1256 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2653 1185 : flags |= GDAL_OF_VERBOSE_ERROR;
2654 1256 : if ((arg == outputArg || !outputArg) && update)
2655 : {
2656 73 : flags |= GDAL_OF_UPDATE;
2657 73 : if (!append)
2658 52 : flags |= GDAL_OF_VERBOSE_ERROR;
2659 : }
2660 :
2661 1256 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2662 : const bool readOnly =
2663 1299 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2664 43 : readOnlyArg->Get<bool>());
2665 1256 : if (readOnly)
2666 12 : flags &= ~GDAL_OF_UPDATE;
2667 :
2668 2512 : CPLStringList aosOpenOptions;
2669 2512 : CPLStringList aosAllowedDrivers;
2670 1256 : if (arg->IsInput())
2671 : {
2672 1256 : if (arg == outputArg)
2673 : {
2674 71 : if (update && !overwrite)
2675 : {
2676 71 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2677 71 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2678 19 : aosOpenOptions = CPLStringList(
2679 19 : ooArg->Get<std::vector<std::string>>());
2680 : }
2681 : }
2682 : else
2683 : {
2684 1185 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2685 1185 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2686 : aosOpenOptions =
2687 1141 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2688 :
2689 1185 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2690 1185 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2691 : aosAllowedDrivers =
2692 1098 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2693 : }
2694 : }
2695 :
2696 2512 : std::string osDatasetName = val.GetName();
2697 1256 : if (!m_referencePath.empty())
2698 : {
2699 42 : osDatasetName = GDALDataset::BuildFilename(
2700 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2701 : }
2702 1256 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2703 0 : osDatasetName = "/vsistdin/";
2704 :
2705 : // Handle special case of overview delete in GTiff which would fail
2706 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2707 130 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2708 1388 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2709 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2710 : {
2711 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2712 : GDALDriverH hDrv =
2713 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2714 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2715 : {
2716 : // Cleaning does not break COG layout
2717 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2718 : }
2719 : }
2720 :
2721 1256 : auto oIter = m_oMapDatasetNameToDataset.find(osDatasetName.c_str());
2722 : GDALDataset *poDS;
2723 : {
2724 : // The PostGISRaster may emit an error message, that is not
2725 : // relevant, if it is the vector driver that was intended
2726 1256 : std::unique_ptr<CPLErrorStateBackuper> poBackuper;
2727 1256 : if (cpl::starts_with(osDatasetName, "PG:") &&
2728 0 : (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
2729 : {
2730 0 : poBackuper = std::make_unique<CPLErrorStateBackuper>(
2731 0 : CPLQuietErrorHandler);
2732 : }
2733 :
2734 1256 : CPL_IGNORE_RET_VAL(poBackuper);
2735 1256 : poDS = oIter != m_oMapDatasetNameToDataset.end()
2736 1256 : ? oIter->second
2737 1253 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2738 1253 : aosAllowedDrivers.List(),
2739 1253 : aosOpenOptions.List());
2740 :
2741 : // Retry with PostGIS vector driver
2742 58 : if (!poDS && poBackuper &&
2743 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2744 1314 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2745 : {
2746 0 : poBackuper.reset();
2747 0 : poDS = GDALDataset::Open(
2748 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2749 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2750 : }
2751 : }
2752 :
2753 1256 : if (poDS)
2754 : {
2755 1198 : if (oIter != m_oMapDatasetNameToDataset.end())
2756 : {
2757 3 : if (arg->GetType() == GAAT_DATASET)
2758 3 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2759 3 : poDS->Reference();
2760 3 : m_oMapDatasetNameToDataset.erase(oIter);
2761 : }
2762 :
2763 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2764 : // where the PG: dataset will be first opened with the PostGISRaster
2765 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2766 1612 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2767 1690 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2768 78 : aosOpenOptions.empty())
2769 : {
2770 74 : auto poDrv = poDS->GetDriver();
2771 74 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2772 : {
2773 : // Retry with PostgreSQL (vector) driver
2774 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2775 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2776 0 : if (poTmpDS)
2777 : {
2778 0 : poDS->ReleaseRef();
2779 0 : poDS = poTmpDS.release();
2780 : }
2781 : }
2782 : }
2783 :
2784 1198 : if (assignToOutputArg)
2785 : {
2786 : // Avoid opening twice the same datasource if it is both
2787 : // the input and output.
2788 : // Known to cause problems with at least FGdb, SQLite
2789 : // and GPKG drivers. See #4270
2790 : // Restrict to those 3 drivers. For example it is known
2791 : // to break with the PG driver due to the way it
2792 : // manages transactions.
2793 2 : auto poDriver = poDS->GetDriver();
2794 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2795 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
2796 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
2797 : {
2798 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2799 : }
2800 : }
2801 1198 : val.SetDatasetOpenedByAlgorithm();
2802 1198 : val.Set(poDS);
2803 1198 : poDS->ReleaseRef();
2804 : }
2805 58 : else if (!append)
2806 : {
2807 56 : ret = false;
2808 : }
2809 : }
2810 :
2811 : // Deal with overwriting the output dataset
2812 9016 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2813 : {
2814 2903 : if (!append)
2815 : {
2816 : // If outputting to MEM, do not try to erase a real file of the same name!
2817 : const auto outputFormatArg =
2818 2891 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2819 8643 : if (!(outputFormatArg &&
2820 2876 : outputFormatArg->GetType() == GAAT_STRING &&
2821 2876 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2822 1851 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2823 969 : "stream") ||
2824 969 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2825 : "Memory"))))
2826 : {
2827 984 : const char *pszType = "";
2828 984 : GDALDriver *poDriver = nullptr;
2829 1922 : if (!val.GetName().empty() &&
2830 938 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
2831 : &poDriver))
2832 : {
2833 72 : if (!overwrite)
2834 : {
2835 62 : std::string options;
2836 31 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
2837 : {
2838 8 : options += "--";
2839 8 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
2840 : }
2841 31 : if (hasAppendArg)
2842 : {
2843 22 : if (!options.empty())
2844 8 : options += '/';
2845 22 : options += "--";
2846 22 : options += GDAL_ARG_NAME_APPEND;
2847 : }
2848 31 : if (hasUpdateArg)
2849 : {
2850 12 : if (!options.empty())
2851 9 : options += '/';
2852 12 : options += "--";
2853 12 : options += GDAL_ARG_NAME_UPDATE;
2854 : }
2855 :
2856 31 : if (poDriver)
2857 : {
2858 62 : const char *pszPrefix = poDriver->GetMetadataItem(
2859 31 : GDAL_DMD_CONNECTION_PREFIX);
2860 31 : if (pszPrefix &&
2861 0 : STARTS_WITH_CI(val.GetName().c_str(),
2862 : pszPrefix))
2863 : {
2864 0 : bool bExists = false;
2865 : {
2866 : CPLErrorStateBackuper oBackuper(
2867 0 : CPLQuietErrorHandler);
2868 0 : bExists = std::unique_ptr<GDALDataset>(
2869 : GDALDataset::Open(
2870 0 : val.GetName().c_str())) !=
2871 : nullptr;
2872 : }
2873 0 : if (bExists)
2874 : {
2875 0 : if (!options.empty())
2876 0 : options = " You may specify the " +
2877 0 : options + " option.";
2878 0 : ReportError(CE_Failure, CPLE_AppDefined,
2879 : "%s '%s' already exists.%s",
2880 0 : pszType, val.GetName().c_str(),
2881 : options.c_str());
2882 0 : return false;
2883 : }
2884 :
2885 0 : return true;
2886 : }
2887 : }
2888 :
2889 31 : if (!options.empty())
2890 25 : options = '/' + options;
2891 62 : ReportError(
2892 : CE_Failure, CPLE_AppDefined,
2893 : "%s '%s' already exists. You may specify the "
2894 : "--overwrite%s option.",
2895 31 : pszType, val.GetName().c_str(), options.c_str());
2896 31 : return false;
2897 : }
2898 41 : else if (EQUAL(pszType, "File"))
2899 : {
2900 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
2901 : {
2902 0 : ReportError(CE_Failure, CPLE_AppDefined,
2903 : "Deleting %s failed: %s",
2904 0 : val.GetName().c_str(),
2905 0 : VSIStrerror(errno));
2906 0 : return false;
2907 : }
2908 : }
2909 40 : else if (EQUAL(pszType, "Directory"))
2910 : {
2911 : // We don't want the user to accidentally erase a non-GDAL dataset
2912 1 : ReportError(CE_Failure, CPLE_AppDefined,
2913 : "Directory '%s' already exists, but is not "
2914 : "recognized as a valid GDAL dataset. "
2915 : "Please manually delete it before retrying",
2916 1 : val.GetName().c_str());
2917 1 : return false;
2918 : }
2919 39 : else if (poDriver)
2920 : {
2921 : bool bDeleteOK;
2922 : {
2923 : CPLErrorStateBackuper oBackuper(
2924 39 : CPLQuietErrorHandler);
2925 39 : bDeleteOK = (poDriver->Delete(
2926 39 : val.GetName().c_str()) == CE_None);
2927 : }
2928 : VSIStatBufL sStat;
2929 42 : if (!bDeleteOK &&
2930 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
2931 : {
2932 3 : if (VSI_ISDIR(sStat.st_mode))
2933 : {
2934 : // We don't want the user to accidentally erase a non-GDAL dataset
2935 0 : ReportError(
2936 : CE_Failure, CPLE_AppDefined,
2937 : "Directory '%s' already exists, but is not "
2938 : "recognized as a valid GDAL dataset. "
2939 : "Please manually delete it before retrying",
2940 0 : val.GetName().c_str());
2941 2 : return false;
2942 : }
2943 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
2944 : {
2945 2 : ReportError(CE_Failure, CPLE_AppDefined,
2946 : "Deleting %s failed: %s",
2947 2 : val.GetName().c_str(),
2948 2 : VSIStrerror(errno));
2949 2 : return false;
2950 : }
2951 : }
2952 : }
2953 : }
2954 : }
2955 : }
2956 : }
2957 :
2958 : // If outputting to stdout, automatically turn off progress bar
2959 8982 : if (arg == outputArg && val.GetName() == "/vsistdout/")
2960 : {
2961 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
2962 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
2963 5 : quietArg->Set(true);
2964 : }
2965 :
2966 8982 : return ret;
2967 : }
2968 :
2969 : /************************************************************************/
2970 : /* GDALAlgorithm::ValidateArguments() */
2971 : /************************************************************************/
2972 :
2973 6353 : bool GDALAlgorithm::ValidateArguments()
2974 : {
2975 6353 : if (m_selectedSubAlg)
2976 3 : return m_selectedSubAlg->ValidateArguments();
2977 :
2978 6350 : if (m_specialActionRequested)
2979 1 : return true;
2980 :
2981 6349 : m_arbitraryLongNameArgsAllowed = false;
2982 :
2983 : // If only --output=format=MEM/stream is specified and not --output,
2984 : // then set empty name for --output.
2985 6349 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2986 6349 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2987 3776 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
2988 2548 : !outputArg->IsExplicitlySet() &&
2989 353 : outputFormatArg->GetType() == GAAT_STRING &&
2990 353 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2991 583 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
2992 10453 : outputArg->GetType() == GAAT_DATASET &&
2993 328 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
2994 : {
2995 328 : outputArg->Get<GDALArgDatasetValue>().Set("");
2996 : }
2997 :
2998 : // The method may emit several errors if several constraints are not met.
2999 6349 : bool ret = true;
3000 6349 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3001 119432 : for (auto &arg : m_args)
3002 : {
3003 : // Check mutually exclusive arguments
3004 113083 : if (arg->IsExplicitlySet())
3005 : {
3006 18282 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3007 18282 : if (!mutualExclusionGroup.empty())
3008 : {
3009 : auto oIter =
3010 705 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3011 705 : if (oIter != mutualExclusionGroupUsed.end())
3012 : {
3013 11 : ret = false;
3014 22 : ReportError(
3015 : CE_Failure, CPLE_AppDefined,
3016 : "Argument '%s' is mutually exclusive with '%s'.",
3017 22 : arg->GetName().c_str(), oIter->second.c_str());
3018 : }
3019 : else
3020 : {
3021 694 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3022 1388 : arg->GetName();
3023 : }
3024 : }
3025 : }
3026 :
3027 113237 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3028 154 : !arg->HasDefaultValue())
3029 : {
3030 154 : bool emitError = true;
3031 154 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3032 154 : if (!mutualExclusionGroup.empty())
3033 : {
3034 1765 : for (const auto &otherArg : m_args)
3035 : {
3036 1751 : if (otherArg->GetMutualExclusionGroup() ==
3037 1856 : mutualExclusionGroup &&
3038 105 : otherArg->IsExplicitlySet())
3039 : {
3040 74 : emitError = false;
3041 74 : break;
3042 : }
3043 : }
3044 : }
3045 232 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3046 48 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3047 60 : (arg->GetType() == GAAT_DATASET ||
3048 30 : arg->GetType() == GAAT_DATASET_LIST)))
3049 : {
3050 50 : ReportError(CE_Failure, CPLE_AppDefined,
3051 : "Required argument '%s' has not been specified.",
3052 50 : arg->GetName().c_str());
3053 50 : ret = false;
3054 : }
3055 : }
3056 112929 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3057 : {
3058 3998 : if (!ProcessDatasetArg(arg.get(), this))
3059 48 : ret = false;
3060 : }
3061 :
3062 118315 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
3063 5232 : arg->AutoOpenDataset())
3064 : {
3065 4760 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3066 4760 : if (listVal.size() == 1)
3067 : {
3068 4758 : if (!ProcessDatasetArg(arg.get(), this))
3069 36 : ret = false;
3070 : }
3071 : else
3072 : {
3073 6 : for (auto &val : listVal)
3074 : {
3075 4 : if (!val.GetDatasetRef() && val.GetName().empty())
3076 : {
3077 0 : ReportError(CE_Failure, CPLE_AppDefined,
3078 : "Argument '%s' has no dataset object or "
3079 : "dataset name.",
3080 0 : arg->GetName().c_str());
3081 0 : ret = false;
3082 : }
3083 4 : else if (!val.GetDatasetRef())
3084 : {
3085 : int flags =
3086 4 : arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3087 :
3088 8 : CPLStringList aosOpenOptions;
3089 8 : CPLStringList aosAllowedDrivers;
3090 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3091 : {
3092 : const auto ooArg =
3093 4 : GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3094 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3095 : {
3096 4 : aosOpenOptions = CPLStringList(
3097 4 : ooArg->Get<std::vector<std::string>>());
3098 : }
3099 :
3100 : const auto ifArg =
3101 4 : GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3102 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3103 : {
3104 4 : aosAllowedDrivers = CPLStringList(
3105 4 : ifArg->Get<std::vector<std::string>>());
3106 : }
3107 :
3108 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3109 0 : if (updateArg &&
3110 4 : updateArg->GetType() == GAAT_BOOLEAN &&
3111 0 : updateArg->Get<bool>())
3112 : {
3113 0 : flags |= GDAL_OF_UPDATE;
3114 : }
3115 : }
3116 :
3117 : auto poDS = std::unique_ptr<GDALDataset>(
3118 4 : GDALDataset::Open(val.GetName().c_str(), flags,
3119 4 : aosAllowedDrivers.List(),
3120 12 : aosOpenOptions.List()));
3121 4 : if (poDS)
3122 : {
3123 3 : val.Set(std::move(poDS));
3124 : }
3125 : else
3126 : {
3127 1 : ret = false;
3128 : }
3129 : }
3130 : }
3131 : }
3132 : }
3133 :
3134 113083 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3135 : {
3136 6 : ret = false;
3137 : }
3138 : }
3139 :
3140 26974 : for (const auto &f : m_validationActions)
3141 : {
3142 20625 : if (!f())
3143 73 : ret = false;
3144 : }
3145 :
3146 6349 : return ret;
3147 : }
3148 :
3149 : /************************************************************************/
3150 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3151 : /************************************************************************/
3152 :
3153 : std::unique_ptr<GDALAlgorithm>
3154 9553 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3155 : bool suggestionAllowed) const
3156 : {
3157 9553 : auto ret = m_subAlgRegistry.Instantiate(name);
3158 19106 : auto childCallPath = m_callPath;
3159 9553 : childCallPath.push_back(name);
3160 9553 : if (!ret)
3161 : {
3162 768 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3163 768 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3164 : }
3165 9553 : if (ret)
3166 : {
3167 9421 : ret->SetCallPath(childCallPath);
3168 : }
3169 132 : else if (suggestionAllowed)
3170 : {
3171 52 : std::string bestCandidate;
3172 26 : size_t bestDistance = std::numeric_limits<size_t>::max();
3173 449 : for (const std::string &candidate : GetSubAlgorithmNames())
3174 : {
3175 : const size_t distance =
3176 423 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3177 : /* transpositionAllowed = */ true);
3178 423 : if (distance < bestDistance)
3179 : {
3180 65 : bestCandidate = candidate;
3181 65 : bestDistance = distance;
3182 : }
3183 358 : else if (distance == bestDistance)
3184 : {
3185 43 : bestCandidate.clear();
3186 : }
3187 : }
3188 26 : if (!bestCandidate.empty() && bestDistance <= 2)
3189 : {
3190 4 : CPLError(CE_Failure, CPLE_AppDefined,
3191 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3192 : name.c_str(), bestCandidate.c_str());
3193 : }
3194 : }
3195 19106 : return ret;
3196 : }
3197 :
3198 : /************************************************************************/
3199 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3200 : /************************************************************************/
3201 :
3202 : std::string
3203 36 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3204 : {
3205 36 : if (osName.size() >= 3)
3206 : {
3207 33 : std::string bestCandidate;
3208 33 : size_t bestDistance = std::numeric_limits<size_t>::max();
3209 677 : for (const auto &[key, value] : m_mapLongNameToArg)
3210 : {
3211 644 : CPL_IGNORE_RET_VAL(value);
3212 644 : const size_t distance = CPLLevenshteinDistance(
3213 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3214 644 : if (distance < bestDistance)
3215 : {
3216 85 : bestCandidate = key;
3217 85 : bestDistance = distance;
3218 : }
3219 559 : else if (distance == bestDistance)
3220 : {
3221 77 : bestCandidate.clear();
3222 : }
3223 : }
3224 47 : if (!bestCandidate.empty() &&
3225 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3226 : {
3227 5 : return bestCandidate;
3228 : }
3229 : }
3230 31 : return std::string();
3231 : }
3232 :
3233 : /************************************************************************/
3234 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3235 : /************************************************************************/
3236 :
3237 : /* static */
3238 22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3239 : {
3240 66 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3241 66 : osName == GDAL_ARG_NAME_OVERWRITE ||
3242 44 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3243 : }
3244 :
3245 : /************************************************************************/
3246 : /* GDALAlgorithm::HasOutputString() */
3247 : /************************************************************************/
3248 :
3249 67 : bool GDALAlgorithm::HasOutputString() const
3250 : {
3251 67 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3252 67 : return outputStringArg && outputStringArg->IsOutput();
3253 : }
3254 :
3255 : /************************************************************************/
3256 : /* GDALAlgorithm::GetArg() */
3257 : /************************************************************************/
3258 :
3259 439604 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3260 : bool suggestionAllowed, bool isConst)
3261 : {
3262 439604 : const auto nPos = osName.find_first_not_of('-');
3263 439604 : if (nPos == std::string::npos)
3264 23 : return nullptr;
3265 879162 : std::string osKey = osName.substr(nPos);
3266 : {
3267 439581 : const auto oIter = m_mapLongNameToArg.find(osKey);
3268 439581 : if (oIter != m_mapLongNameToArg.end())
3269 409738 : return oIter->second;
3270 : }
3271 : {
3272 29843 : const auto oIter = m_mapShortNameToArg.find(osKey);
3273 29843 : if (oIter != m_mapShortNameToArg.end())
3274 6 : return oIter->second;
3275 : }
3276 :
3277 29837 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3278 : {
3279 22 : const auto nDotPos = osKey.find('.');
3280 : const std::string osKeyEnd =
3281 22 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3282 22 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3283 : {
3284 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3285 0 : std::make_unique<bool>());
3286 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3287 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3288 0 : .SetUserProvided();
3289 : }
3290 : else
3291 : {
3292 44 : const std::string osKeyInit = osKey;
3293 22 : if (osKey == "oo")
3294 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3295 22 : else if (osKey == "co")
3296 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3297 22 : else if (osKey == "of")
3298 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3299 22 : else if (osKey == "if")
3300 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3301 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3302 22 : std::make_unique<std::string>());
3303 : auto &arg =
3304 44 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3305 44 : m_arbitraryLongNameArgsValuesStr.back().get())
3306 22 : .SetUserProvided();
3307 22 : if (osKey != osKeyInit)
3308 0 : arg.AddAlias(osKeyInit);
3309 : }
3310 22 : const auto oIter = m_mapLongNameToArg.find(osKey);
3311 22 : CPLAssert(oIter != m_mapLongNameToArg.end());
3312 22 : return oIter->second;
3313 : }
3314 :
3315 29815 : if (suggestionAllowed)
3316 : {
3317 12 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3318 6 : if (!bestCandidate.empty())
3319 : {
3320 2 : CPLError(CE_Failure, CPLE_AppDefined,
3321 : "Argument '%s' is unknown. Do you mean '%s'?",
3322 : osName.c_str(), bestCandidate.c_str());
3323 : }
3324 : }
3325 :
3326 29815 : return nullptr;
3327 : }
3328 :
3329 : /************************************************************************/
3330 : /* GDALAlgorithm::AddAliasFor() */
3331 : /************************************************************************/
3332 :
3333 : //! @cond Doxygen_Suppress
3334 72958 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3335 : const std::string &alias)
3336 : {
3337 72958 : if (cpl::contains(m_mapLongNameToArg, alias))
3338 : {
3339 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3340 : alias.c_str());
3341 : }
3342 : else
3343 : {
3344 72957 : m_mapLongNameToArg[alias] = arg;
3345 : }
3346 72958 : }
3347 :
3348 : //! @endcond
3349 :
3350 : /************************************************************************/
3351 : /* GDALAlgorithm::AddShortNameAliasFor() */
3352 : /************************************************************************/
3353 :
3354 : //! @cond Doxygen_Suppress
3355 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3356 : char shortNameAlias)
3357 : {
3358 96 : std::string alias;
3359 48 : alias += shortNameAlias;
3360 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3361 : {
3362 0 : ReportError(CE_Failure, CPLE_AppDefined,
3363 : "Short name '%s' already declared.", alias.c_str());
3364 : }
3365 : else
3366 : {
3367 48 : m_mapShortNameToArg[alias] = arg;
3368 : }
3369 48 : }
3370 :
3371 : //! @endcond
3372 :
3373 : /************************************************************************/
3374 : /* GDALAlgorithm::SetPositional() */
3375 : /************************************************************************/
3376 :
3377 : //! @cond Doxygen_Suppress
3378 20686 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3379 : {
3380 20686 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3381 : arg) == m_positionalArgs.end());
3382 20686 : m_positionalArgs.push_back(arg);
3383 20686 : }
3384 :
3385 : //! @endcond
3386 :
3387 : /************************************************************************/
3388 : /* GDALAlgorithm::HasSubAlgorithms() */
3389 : /************************************************************************/
3390 :
3391 12063 : bool GDALAlgorithm::HasSubAlgorithms() const
3392 : {
3393 12063 : if (!m_subAlgRegistry.empty())
3394 3098 : return true;
3395 8965 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3396 17930 : .GetDeclaredSubAlgorithmNames(m_callPath)
3397 8965 : .empty();
3398 : }
3399 :
3400 : /************************************************************************/
3401 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3402 : /************************************************************************/
3403 :
3404 1275 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3405 : {
3406 1275 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3407 1275 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3408 2550 : .GetDeclaredSubAlgorithmNames(m_callPath);
3409 1275 : ret.insert(ret.end(), other.begin(), other.end());
3410 1275 : if (!other.empty())
3411 369 : std::sort(ret.begin(), ret.end());
3412 2550 : return ret;
3413 : }
3414 :
3415 : /************************************************************************/
3416 : /* GDALAlgorithm::AddArg() */
3417 : /************************************************************************/
3418 :
3419 : GDALInConstructionAlgorithmArg &
3420 292931 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3421 : {
3422 292931 : auto argRaw = arg.get();
3423 292931 : const auto &longName = argRaw->GetName();
3424 292931 : if (!longName.empty())
3425 : {
3426 292918 : if (longName[0] == '-')
3427 : {
3428 1 : ReportError(CE_Failure, CPLE_AppDefined,
3429 : "Long name '%s' should not start with '-'",
3430 : longName.c_str());
3431 : }
3432 292918 : if (longName.find('=') != std::string::npos)
3433 : {
3434 1 : ReportError(CE_Failure, CPLE_AppDefined,
3435 : "Long name '%s' should not contain a '=' character",
3436 : longName.c_str());
3437 : }
3438 292918 : if (cpl::contains(m_mapLongNameToArg, longName))
3439 : {
3440 1 : ReportError(CE_Failure, CPLE_AppDefined,
3441 : "Long name '%s' already declared", longName.c_str());
3442 : }
3443 292918 : m_mapLongNameToArg[longName] = argRaw;
3444 : }
3445 292931 : const auto &shortName = argRaw->GetShortName();
3446 292931 : if (!shortName.empty())
3447 : {
3448 142354 : if (shortName.size() != 1 ||
3449 71177 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3450 64 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3451 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3452 : {
3453 1 : ReportError(CE_Failure, CPLE_AppDefined,
3454 : "Short name '%s' should be a single letter or digit",
3455 : shortName.c_str());
3456 : }
3457 71177 : if (cpl::contains(m_mapShortNameToArg, shortName))
3458 : {
3459 1 : ReportError(CE_Failure, CPLE_AppDefined,
3460 : "Short name '%s' already declared", shortName.c_str());
3461 : }
3462 71177 : m_mapShortNameToArg[shortName] = argRaw;
3463 : }
3464 292931 : m_args.emplace_back(std::move(arg));
3465 : return *(
3466 292931 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3467 : }
3468 :
3469 : GDALInConstructionAlgorithmArg &
3470 130424 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3471 : const std::string &helpMessage, bool *pValue)
3472 : {
3473 130424 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3474 : this,
3475 260848 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3476 260848 : pValue));
3477 : }
3478 :
3479 : GDALInConstructionAlgorithmArg &
3480 46854 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3481 : const std::string &helpMessage, std::string *pValue)
3482 : {
3483 46854 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3484 : this,
3485 93708 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3486 93708 : pValue));
3487 : }
3488 :
3489 : GDALInConstructionAlgorithmArg &
3490 11722 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3491 : const std::string &helpMessage, int *pValue)
3492 : {
3493 11722 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3494 : this,
3495 23444 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3496 23444 : pValue));
3497 : }
3498 :
3499 : GDALInConstructionAlgorithmArg &
3500 10042 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3501 : const std::string &helpMessage, double *pValue)
3502 : {
3503 10042 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3504 : this,
3505 20084 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3506 20084 : pValue));
3507 : }
3508 :
3509 : GDALInConstructionAlgorithmArg &
3510 11004 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3511 : const std::string &helpMessage,
3512 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3513 : {
3514 22008 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3515 : this,
3516 22008 : GDALAlgorithmArgDecl(longName, chShortName,
3517 : helpMessage, GAAT_DATASET),
3518 11004 : pValue))
3519 11004 : .SetDatasetType(type);
3520 11004 : pValue->SetOwnerArgument(&arg);
3521 11004 : return arg;
3522 : }
3523 :
3524 : GDALInConstructionAlgorithmArg &
3525 62302 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3526 : const std::string &helpMessage,
3527 : std::vector<std::string> *pValue)
3528 : {
3529 62302 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3530 : this,
3531 124604 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3532 : GAAT_STRING_LIST),
3533 124604 : pValue));
3534 : }
3535 :
3536 : GDALInConstructionAlgorithmArg &
3537 2160 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3538 : const std::string &helpMessage, std::vector<int> *pValue)
3539 : {
3540 2160 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3541 : this,
3542 4320 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3543 : GAAT_INTEGER_LIST),
3544 4320 : pValue));
3545 : }
3546 :
3547 : GDALInConstructionAlgorithmArg &
3548 5094 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3549 : const std::string &helpMessage,
3550 : std::vector<double> *pValue)
3551 : {
3552 5094 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3553 : this,
3554 10188 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3555 : GAAT_REAL_LIST),
3556 10188 : pValue));
3557 : }
3558 :
3559 : GDALInConstructionAlgorithmArg &
3560 13329 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3561 : const std::string &helpMessage,
3562 : std::vector<GDALArgDatasetValue> *pValue,
3563 : GDALArgDatasetType type)
3564 : {
3565 26658 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3566 : this,
3567 26658 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3568 : GAAT_DATASET_LIST),
3569 13329 : pValue))
3570 26658 : .SetDatasetType(type);
3571 : }
3572 :
3573 : /************************************************************************/
3574 : /* MsgOrDefault() */
3575 : /************************************************************************/
3576 :
3577 98891 : inline const char *MsgOrDefault(const char *helpMessage,
3578 : const char *defaultMessage)
3579 : {
3580 98891 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3581 : }
3582 :
3583 : /************************************************************************/
3584 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3585 : /************************************************************************/
3586 :
3587 : /* static */
3588 15991 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3589 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3590 : {
3591 : arg.SetAutoCompleteFunction(
3592 7 : [&arg,
3593 2442 : type](const std::string ¤tValue) -> std::vector<std::string>
3594 : {
3595 14 : std::vector<std::string> oRet;
3596 :
3597 7 : if (arg.IsHidden())
3598 0 : return oRet;
3599 :
3600 : {
3601 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3602 : VSIStatBufL sStat;
3603 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3604 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3605 : {
3606 0 : return oRet;
3607 : }
3608 : }
3609 :
3610 7 : auto poDM = GetGDALDriverManager();
3611 14 : std::set<std::string> oExtensions;
3612 7 : if (type)
3613 : {
3614 1362 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3615 : {
3616 1356 : auto poDriver = poDM->GetDriver(i);
3617 3842 : if (((type & GDAL_OF_RASTER) != 0 &&
3618 1130 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3619 581 : ((type & GDAL_OF_VECTOR) != 0 &&
3620 2848 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3621 491 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3622 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3623 : {
3624 : const char *pszExtensions =
3625 865 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3626 865 : if (pszExtensions)
3627 : {
3628 : const CPLStringList aosExts(
3629 1142 : CSLTokenizeString2(pszExtensions, " ", 0));
3630 1291 : for (const char *pszExt : cpl::Iterate(aosExts))
3631 720 : oExtensions.insert(CPLString(pszExt).tolower());
3632 : }
3633 : }
3634 : }
3635 : }
3636 :
3637 14 : std::string osDir;
3638 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3639 14 : std::string osPrefix;
3640 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3641 : {
3642 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3643 : {
3644 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3645 : {
3646 2 : osPrefix = pszPrefix;
3647 2 : break;
3648 : }
3649 : }
3650 3 : if (osPrefix.empty())
3651 1 : return aosVSIPrefixes;
3652 2 : if (currentValue == osPrefix)
3653 1 : osDir = osPrefix;
3654 : }
3655 6 : if (osDir.empty())
3656 : {
3657 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3658 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3659 0 : osDir = std::move(osPrefix);
3660 : }
3661 :
3662 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3663 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3664 6 : if (currentValue.empty())
3665 1 : osDir.clear();
3666 : const std::string currentFilename =
3667 12 : CPLGetFilename(currentValue.c_str());
3668 6 : if (psDir)
3669 : {
3670 440 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3671 : {
3672 435 : if ((currentFilename.empty() ||
3673 217 : STARTS_WITH(psEntry->pszName,
3674 219 : currentFilename.c_str())) &&
3675 219 : strcmp(psEntry->pszName, ".") != 0 &&
3676 1307 : strcmp(psEntry->pszName, "..") != 0 &&
3677 219 : (oExtensions.empty() ||
3678 218 : !strstr(psEntry->pszName, ".aux.xml")))
3679 : {
3680 866 : if (oExtensions.empty() ||
3681 216 : cpl::contains(
3682 : oExtensions,
3683 433 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3684 649 : .tolower()) ||
3685 185 : VSI_ISDIR(psEntry->nMode))
3686 : {
3687 72 : std::string osVal;
3688 36 : if (osDir.empty() || osDir == ".")
3689 4 : osVal = psEntry->pszName;
3690 : else
3691 64 : osVal = CPLFormFilenameSafe(
3692 64 : osDir.c_str(), psEntry->pszName, nullptr);
3693 36 : if (VSI_ISDIR(psEntry->nMode))
3694 4 : osVal += osSep;
3695 36 : oRet.push_back(std::move(osVal));
3696 : }
3697 : }
3698 435 : }
3699 5 : VSICloseDir(psDir);
3700 : }
3701 6 : return oRet;
3702 15991 : });
3703 15991 : }
3704 :
3705 : /************************************************************************/
3706 : /* GDALAlgorithm::AddInputDatasetArg() */
3707 : /************************************************************************/
3708 :
3709 594 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3710 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3711 : bool positionalAndRequired, const char *helpMessage)
3712 : {
3713 : auto &arg = AddArg(
3714 : GDAL_ARG_NAME_INPUT, 'i',
3715 : MsgOrDefault(helpMessage,
3716 : CPLSPrintf("Input %s dataset",
3717 594 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3718 1188 : pValue, type);
3719 594 : if (positionalAndRequired)
3720 587 : arg.SetPositional().SetRequired();
3721 :
3722 594 : SetAutoCompleteFunctionForFilename(arg, type);
3723 :
3724 594 : AddValidationAction(
3725 130 : [pValue]()
3726 : {
3727 129 : if (pValue->GetName() == "-")
3728 1 : pValue->Set("/vsistdin/");
3729 129 : return true;
3730 : });
3731 :
3732 594 : return arg;
3733 : }
3734 :
3735 : /************************************************************************/
3736 : /* GDALAlgorithm::AddInputDatasetArg() */
3737 : /************************************************************************/
3738 :
3739 12888 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3740 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
3741 : bool positionalAndRequired, const char *helpMessage)
3742 : {
3743 : auto &arg =
3744 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
3745 : MsgOrDefault(
3746 : helpMessage,
3747 : CPLSPrintf("Input %s datasets",
3748 12888 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3749 38664 : pValue, type)
3750 12888 : .SetPackedValuesAllowed(false);
3751 12888 : if (positionalAndRequired)
3752 1711 : arg.SetPositional().SetRequired();
3753 :
3754 12888 : SetAutoCompleteFunctionForFilename(arg, type);
3755 :
3756 12888 : AddValidationAction(
3757 5863 : [pValue]()
3758 : {
3759 11189 : for (auto &val : *pValue)
3760 : {
3761 5326 : if (val.GetName() == "-")
3762 1 : val.Set("/vsistdin/");
3763 : }
3764 5863 : return true;
3765 : });
3766 12888 : return arg;
3767 : }
3768 :
3769 : /************************************************************************/
3770 : /* GDALAlgorithm::AddOutputDatasetArg() */
3771 : /************************************************************************/
3772 :
3773 7959 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
3774 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3775 : bool positionalAndRequired, const char *helpMessage)
3776 : {
3777 : auto &arg =
3778 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
3779 : MsgOrDefault(
3780 : helpMessage,
3781 : CPLSPrintf("Output %s dataset",
3782 7959 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3783 23877 : pValue, type)
3784 7959 : .SetIsInput(true)
3785 7959 : .SetIsOutput(true)
3786 7959 : .SetDatasetInputFlags(GADV_NAME)
3787 7959 : .SetDatasetOutputFlags(GADV_OBJECT);
3788 7959 : if (positionalAndRequired)
3789 4251 : arg.SetPositional().SetRequired();
3790 :
3791 7959 : AddValidationAction(
3792 10931 : [this, &arg, pValue]()
3793 : {
3794 3403 : if (pValue->GetName() == "-")
3795 4 : pValue->Set("/vsistdout/");
3796 :
3797 3403 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3798 3355 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
3799 5442 : (!outputFormatArg->IsExplicitlySet() ||
3800 8845 : outputFormatArg->Get<std::string>().empty()) &&
3801 1268 : arg.IsExplicitlySet())
3802 : {
3803 : const auto vrtCompatible =
3804 940 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3805 182 : if (vrtCompatible && !vrtCompatible->empty() &&
3806 1122 : vrtCompatible->front() == "false" &&
3807 1031 : EQUAL(
3808 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
3809 : "VRT"))
3810 : {
3811 6 : ReportError(
3812 : CE_Failure, CPLE_NotSupported,
3813 : "VRT output is not supported.%s",
3814 6 : outputFormatArg->GetDescription().find("GDALG") !=
3815 : std::string::npos
3816 : ? " Consider using the GDALG driver instead (files "
3817 : "with .gdalg.json extension)"
3818 : : "");
3819 6 : return false;
3820 : }
3821 934 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
3822 1845 : EQUAL(pValue->GetName()
3823 : .substr(pValue->GetName().size() -
3824 : strlen(".gdalg.json"))
3825 : .c_str(),
3826 2779 : ".gdalg.json") &&
3827 27 : outputFormatArg->GetDescription().find("GDALG") ==
3828 : std::string::npos)
3829 : {
3830 0 : ReportError(CE_Failure, CPLE_NotSupported,
3831 : "GDALG output is not supported");
3832 0 : return false;
3833 : }
3834 : }
3835 3397 : return true;
3836 : });
3837 :
3838 7959 : return arg;
3839 : }
3840 :
3841 : /************************************************************************/
3842 : /* GDALAlgorithm::AddOverwriteArg() */
3843 : /************************************************************************/
3844 :
3845 : GDALInConstructionAlgorithmArg &
3846 7875 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
3847 : {
3848 : return AddArg(
3849 : GDAL_ARG_NAME_OVERWRITE, 0,
3850 : MsgOrDefault(
3851 : helpMessage,
3852 : _("Whether overwriting existing output dataset is allowed")),
3853 15750 : pValue)
3854 15750 : .SetDefault(false);
3855 : }
3856 :
3857 : /************************************************************************/
3858 : /* GDALAlgorithm::AddOverwriteLayerArg() */
3859 : /************************************************************************/
3860 :
3861 : GDALInConstructionAlgorithmArg &
3862 3189 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
3863 : {
3864 3189 : AddValidationAction(
3865 1397 : [this]
3866 : {
3867 1396 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3868 1396 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3869 : {
3870 1 : ReportError(CE_Failure, CPLE_AppDefined,
3871 : "--update argument must exist for "
3872 : "--overwrite-layer, even if hidden");
3873 1 : return false;
3874 : }
3875 1395 : return true;
3876 : });
3877 : return AddArg(
3878 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
3879 : MsgOrDefault(
3880 : helpMessage,
3881 : _("Whether overwriting existing output layer is allowed")),
3882 6378 : pValue)
3883 3189 : .SetDefault(false)
3884 : .AddAction(
3885 17 : [this]
3886 : {
3887 17 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3888 17 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3889 : {
3890 17 : updateArg->Set(true);
3891 : }
3892 6395 : });
3893 : }
3894 :
3895 : /************************************************************************/
3896 : /* GDALAlgorithm::AddUpdateArg() */
3897 : /************************************************************************/
3898 :
3899 : GDALInConstructionAlgorithmArg &
3900 3740 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
3901 : {
3902 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
3903 : MsgOrDefault(
3904 : helpMessage,
3905 : _("Whether to open existing dataset in update mode")),
3906 7480 : pValue)
3907 7480 : .SetDefault(false);
3908 : }
3909 :
3910 : /************************************************************************/
3911 : /* GDALAlgorithm::AddAppendLayerArg() */
3912 : /************************************************************************/
3913 :
3914 : GDALInConstructionAlgorithmArg &
3915 3092 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
3916 : {
3917 3092 : AddValidationAction(
3918 1400 : [this]
3919 : {
3920 1399 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3921 1399 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3922 : {
3923 1 : ReportError(CE_Failure, CPLE_AppDefined,
3924 : "--update argument must exist for --append, even "
3925 : "if hidden");
3926 1 : return false;
3927 : }
3928 1398 : return true;
3929 : });
3930 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
3931 : MsgOrDefault(
3932 : helpMessage,
3933 : _("Whether appending to existing layer is allowed")),
3934 6184 : pValue)
3935 3092 : .SetDefault(false)
3936 : .AddAction(
3937 25 : [this]
3938 : {
3939 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3940 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
3941 : {
3942 25 : updateArg->Set(true);
3943 : }
3944 6209 : });
3945 : }
3946 :
3947 : /************************************************************************/
3948 : /* GDALAlgorithm::AddOptionsSuggestions() */
3949 : /************************************************************************/
3950 :
3951 : /* static */
3952 29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
3953 : const std::string ¤tValue,
3954 : std::vector<std::string> &oRet)
3955 : {
3956 29 : if (!pszXML)
3957 0 : return false;
3958 58 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
3959 29 : if (!poTree)
3960 0 : return false;
3961 :
3962 58 : std::string typedOptionName = currentValue;
3963 29 : const auto posEqual = typedOptionName.find('=');
3964 58 : std::string typedValue;
3965 29 : if (posEqual != 0 && posEqual != std::string::npos)
3966 : {
3967 2 : typedValue = currentValue.substr(posEqual + 1);
3968 2 : typedOptionName.resize(posEqual);
3969 : }
3970 :
3971 405 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
3972 376 : psChild = psChild->psNext)
3973 : {
3974 389 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
3975 402 : if (pszName && typedOptionName == pszName &&
3976 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
3977 2 : strcmp(psChild->pszValue, "Argument") == 0))
3978 : {
3979 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
3980 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
3981 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
3982 13 : if (EQUAL(pszType, "string-select"))
3983 : {
3984 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
3985 85 : psChild2 = psChild2->psNext)
3986 : {
3987 85 : if (EQUAL(psChild2->pszValue, "Value"))
3988 : {
3989 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
3990 : }
3991 : }
3992 : }
3993 8 : else if (EQUAL(pszType, "boolean"))
3994 : {
3995 3 : if (typedValue == "YES" || typedValue == "NO")
3996 : {
3997 1 : oRet.push_back(currentValue);
3998 1 : return true;
3999 : }
4000 2 : oRet.push_back("NO");
4001 2 : oRet.push_back("YES");
4002 : }
4003 5 : else if (EQUAL(pszType, "int"))
4004 : {
4005 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4006 2 : atoi(pszMax) - atoi(pszMin) < 25)
4007 : {
4008 1 : const int nMax = atoi(pszMax);
4009 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4010 12 : oRet.push_back(std::to_string(i));
4011 : }
4012 : }
4013 :
4014 12 : if (oRet.empty())
4015 : {
4016 4 : if (pszMin && pszMax)
4017 : {
4018 1 : oRet.push_back(std::string("##"));
4019 2 : oRet.push_back(std::string("validity range: [")
4020 1 : .append(pszMin)
4021 1 : .append(",")
4022 1 : .append(pszMax)
4023 1 : .append("]"));
4024 : }
4025 3 : else if (pszMin)
4026 : {
4027 1 : oRet.push_back(std::string("##"));
4028 1 : oRet.push_back(
4029 1 : std::string("validity range: >= ").append(pszMin));
4030 : }
4031 2 : else if (pszMax)
4032 : {
4033 1 : oRet.push_back(std::string("##"));
4034 1 : oRet.push_back(
4035 1 : std::string("validity range: <= ").append(pszMax));
4036 : }
4037 1 : else if (const char *pszDescription =
4038 1 : CPLGetXMLValue(psChild, "description", nullptr))
4039 : {
4040 1 : oRet.push_back(std::string("##"));
4041 2 : oRet.push_back(std::string("type: ")
4042 1 : .append(pszType)
4043 1 : .append(", description: ")
4044 1 : .append(pszDescription));
4045 : }
4046 : }
4047 :
4048 12 : return true;
4049 : }
4050 : }
4051 :
4052 319 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4053 303 : psChild = psChild->psNext)
4054 : {
4055 303 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4056 303 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4057 5 : strcmp(psChild->pszValue, "Argument") == 0))
4058 : {
4059 300 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4060 300 : if (!pszScope ||
4061 40 : (EQUAL(pszScope, "raster") &&
4062 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4063 20 : (EQUAL(pszScope, "vector") &&
4064 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4065 : {
4066 280 : oRet.push_back(std::string(pszName).append("="));
4067 : }
4068 : }
4069 : }
4070 :
4071 16 : return false;
4072 : }
4073 :
4074 : /************************************************************************/
4075 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4076 : /************************************************************************/
4077 :
4078 : //! @cond Doxygen_Suppress
4079 : std::vector<std::string>
4080 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4081 : {
4082 2 : std::vector<std::string> oRet;
4083 :
4084 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4085 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4086 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4087 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4088 : {
4089 2 : datasetType = inputArg->GetDatasetType();
4090 : }
4091 :
4092 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4093 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4094 2 : inputFormat->IsExplicitlySet())
4095 : {
4096 : const auto &aosAllowedDrivers =
4097 1 : inputFormat->Get<std::vector<std::string>>();
4098 1 : if (aosAllowedDrivers.size() == 1)
4099 : {
4100 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4101 1 : aosAllowedDrivers[0].c_str());
4102 1 : if (poDriver)
4103 : {
4104 1 : AddOptionsSuggestions(
4105 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4106 : datasetType, currentValue, oRet);
4107 : }
4108 1 : return oRet;
4109 : }
4110 : }
4111 :
4112 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4113 369 : &oRet](const GDALArgDatasetValue &datasetValue)
4114 : {
4115 1 : auto poDM = GetGDALDriverManager();
4116 :
4117 1 : const auto &osDSName = datasetValue.GetName();
4118 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4119 1 : if (!osExt.empty())
4120 : {
4121 1 : std::set<std::string> oVisitedExtensions;
4122 227 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4123 : {
4124 226 : auto poDriver = poDM->GetDriver(i);
4125 678 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4126 226 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4127 71 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4128 452 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4129 71 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4130 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4131 : {
4132 : const char *pszExtensions =
4133 155 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4134 155 : if (pszExtensions)
4135 : {
4136 : const CPLStringList aosExts(
4137 102 : CSLTokenizeString2(pszExtensions, " ", 0));
4138 225 : for (const char *pszExt : cpl::Iterate(aosExts))
4139 : {
4140 127 : if (EQUAL(pszExt, osExt.c_str()) &&
4141 3 : !cpl::contains(oVisitedExtensions, pszExt))
4142 : {
4143 1 : oVisitedExtensions.insert(pszExt);
4144 1 : if (AddOptionsSuggestions(
4145 : poDriver->GetMetadataItem(
4146 1 : GDAL_DMD_OPENOPTIONLIST),
4147 : datasetType, currentValue, oRet))
4148 : {
4149 0 : return;
4150 : }
4151 1 : break;
4152 : }
4153 : }
4154 : }
4155 : }
4156 : }
4157 : }
4158 1 : };
4159 :
4160 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4161 : {
4162 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4163 0 : AddSuggestions(datasetValue);
4164 : }
4165 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4166 : {
4167 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4168 1 : if (datasetValues.size() == 1)
4169 1 : AddSuggestions(datasetValues[0]);
4170 : }
4171 :
4172 1 : return oRet;
4173 : }
4174 :
4175 : //! @endcond
4176 :
4177 : /************************************************************************/
4178 : /* GDALAlgorithm::AddOpenOptionsArg() */
4179 : /************************************************************************/
4180 :
4181 : GDALInConstructionAlgorithmArg &
4182 8768 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4183 : const char *helpMessage)
4184 : {
4185 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4186 17536 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4187 17536 : .AddAlias("oo")
4188 17536 : .SetMetaVar("<KEY>=<VALUE>")
4189 8768 : .SetPackedValuesAllowed(false)
4190 8768 : .SetCategory(GAAC_ADVANCED);
4191 :
4192 21 : arg.AddValidationAction([this, &arg]()
4193 8789 : { return ParseAndValidateKeyValue(arg); });
4194 :
4195 : arg.SetAutoCompleteFunction(
4196 2 : [this](const std::string ¤tValue)
4197 8770 : { return OpenOptionCompleteFunction(currentValue); });
4198 :
4199 8768 : return arg;
4200 : }
4201 :
4202 : /************************************************************************/
4203 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4204 : /************************************************************************/
4205 :
4206 : GDALInConstructionAlgorithmArg &
4207 2960 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4208 : const char *helpMessage)
4209 : {
4210 : auto &arg =
4211 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4212 5920 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4213 5920 : .AddAlias("output-oo")
4214 5920 : .SetMetaVar("<KEY>=<VALUE>")
4215 2960 : .SetPackedValuesAllowed(false)
4216 2960 : .SetCategory(GAAC_ADVANCED);
4217 :
4218 0 : arg.AddValidationAction([this, &arg]()
4219 2960 : { return ParseAndValidateKeyValue(arg); });
4220 :
4221 : arg.SetAutoCompleteFunction(
4222 0 : [this](const std::string ¤tValue)
4223 2960 : { return OpenOptionCompleteFunction(currentValue); });
4224 :
4225 2960 : return arg;
4226 : }
4227 :
4228 : /************************************************************************/
4229 : /* ValidateFormat() */
4230 : /************************************************************************/
4231 :
4232 4450 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4233 : bool bStreamAllowed,
4234 : bool bGDALGAllowed) const
4235 : {
4236 4450 : if (arg.GetChoices().empty())
4237 : {
4238 : const auto Validate =
4239 19079 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4240 : {
4241 4345 : if (const auto extraFormats =
4242 4345 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4243 : {
4244 60 : for (const auto &extraFormat : *extraFormats)
4245 : {
4246 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4247 14 : return true;
4248 : }
4249 : }
4250 :
4251 4331 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4252 1725 : return true;
4253 :
4254 2612 : if (EQUAL(val.c_str(), "GDALG") &&
4255 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4256 : {
4257 2 : if (bGDALGAllowed)
4258 : {
4259 2 : return true;
4260 : }
4261 : else
4262 : {
4263 0 : ReportError(CE_Failure, CPLE_NotSupported,
4264 : "GDALG output is not supported.");
4265 0 : return false;
4266 : }
4267 : }
4268 :
4269 : const auto vrtCompatible =
4270 2604 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4271 440 : if (vrtCompatible && !vrtCompatible->empty() &&
4272 3044 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4273 : {
4274 7 : ReportError(CE_Failure, CPLE_NotSupported,
4275 : "VRT output is not supported.%s",
4276 : bGDALGAllowed
4277 : ? " Consider using the GDALG driver instead "
4278 : "(files with .gdalg.json extension)."
4279 : : "");
4280 7 : return false;
4281 : }
4282 :
4283 : const auto allowedFormats =
4284 2597 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4285 2618 : if (allowedFormats && !allowedFormats->empty() &&
4286 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4287 2618 : val) != allowedFormats->end())
4288 : {
4289 9 : return true;
4290 : }
4291 :
4292 : const auto excludedFormats =
4293 2588 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4294 2600 : if (excludedFormats && !excludedFormats->empty() &&
4295 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4296 2600 : val) != excludedFormats->end())
4297 : {
4298 0 : ReportError(CE_Failure, CPLE_NotSupported,
4299 : "%s output is not supported.", val.c_str());
4300 0 : return false;
4301 : }
4302 :
4303 2588 : auto hDriver = GDALGetDriverByName(val.c_str());
4304 2588 : if (!hDriver)
4305 : {
4306 : auto poMissingDriver =
4307 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4308 4 : if (poMissingDriver)
4309 : {
4310 : const std::string msg =
4311 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4312 0 : ReportError(CE_Failure, CPLE_AppDefined,
4313 : "Invalid value for argument '%s'. Driver '%s' "
4314 : "not found but is known. However plugin %s",
4315 0 : arg.GetName().c_str(), val.c_str(),
4316 : msg.c_str());
4317 : }
4318 : else
4319 : {
4320 8 : ReportError(CE_Failure, CPLE_AppDefined,
4321 : "Invalid value for argument '%s'. Driver '%s' "
4322 : "does not exist.",
4323 4 : arg.GetName().c_str(), val.c_str());
4324 : }
4325 4 : return false;
4326 : }
4327 :
4328 2584 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4329 2584 : if (caps)
4330 : {
4331 7758 : for (const std::string &cap : *caps)
4332 : {
4333 : const char *pszVal =
4334 5199 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4335 5199 : if (!(pszVal && pszVal[0]))
4336 : {
4337 1529 : if (cap == GDAL_DCAP_CREATECOPY &&
4338 0 : std::find(caps->begin(), caps->end(),
4339 763 : GDAL_DCAP_RASTER) != caps->end() &&
4340 763 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4341 1529 : nullptr) &&
4342 763 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4343 : nullptr))
4344 : {
4345 : // if it supports Create, it supports CreateCopy
4346 : }
4347 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4348 : {
4349 2 : ReportError(
4350 : CE_Failure, CPLE_AppDefined,
4351 : "Invalid value for argument '%s'. Driver '%s' "
4352 : "does "
4353 : "not advertise any file format extension.",
4354 1 : arg.GetName().c_str(), val.c_str());
4355 3 : return false;
4356 : }
4357 : else
4358 : {
4359 2 : if (cap == GDAL_DCAP_CREATE)
4360 : {
4361 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4362 1 : if (updateArg &&
4363 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4364 1 : updateArg->IsExplicitlySet())
4365 : {
4366 0 : continue;
4367 : }
4368 :
4369 2 : ReportError(
4370 : CE_Failure, CPLE_AppDefined,
4371 : "Invalid value for argument '%s'. "
4372 : "Driver '%s' does not have write support.",
4373 1 : arg.GetName().c_str(), val.c_str());
4374 1 : return false;
4375 : }
4376 : else
4377 : {
4378 2 : ReportError(
4379 : CE_Failure, CPLE_AppDefined,
4380 : "Invalid value for argument '%s'. Driver "
4381 : "'%s' "
4382 : "does "
4383 : "not expose the required '%s' capability.",
4384 1 : arg.GetName().c_str(), val.c_str(),
4385 : cap.c_str());
4386 1 : return false;
4387 : }
4388 : }
4389 : }
4390 : }
4391 : }
4392 2581 : return true;
4393 4348 : };
4394 :
4395 4348 : if (arg.GetType() == GAAT_STRING)
4396 : {
4397 4338 : return Validate(arg.Get<std::string>());
4398 : }
4399 12 : else if (arg.GetType() == GAAT_STRING_LIST)
4400 : {
4401 19 : for (const auto &val : arg.Get<std::vector<std::string>>())
4402 : {
4403 9 : if (!Validate(val))
4404 2 : return false;
4405 : }
4406 : }
4407 : }
4408 :
4409 112 : return true;
4410 : }
4411 :
4412 : /************************************************************************/
4413 : /* FormatAutoCompleteFunction() */
4414 : /************************************************************************/
4415 :
4416 : /* static */
4417 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4418 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4419 : {
4420 7 : std::vector<std::string> res;
4421 7 : auto poDM = GetGDALDriverManager();
4422 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4423 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4424 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4425 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4426 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4427 0 : res = std::move(*extraFormats);
4428 1588 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4429 : {
4430 1581 : auto poDriver = poDM->GetDriver(i);
4431 :
4432 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4433 1581 : vrtCompatible->front() == "false" &&
4434 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4435 : {
4436 : // do nothing
4437 : }
4438 1581 : else if (allowedFormats && !allowedFormats->empty() &&
4439 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4440 1581 : poDriver->GetDescription()) != allowedFormats->end())
4441 : {
4442 0 : res.push_back(poDriver->GetDescription());
4443 : }
4444 1581 : else if (excludedFormats && !excludedFormats->empty() &&
4445 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4446 0 : poDriver->GetDescription()) !=
4447 1581 : excludedFormats->end())
4448 : {
4449 0 : continue;
4450 : }
4451 1581 : else if (caps)
4452 : {
4453 1581 : bool ok = true;
4454 3127 : for (const std::string &cap : *caps)
4455 : {
4456 2355 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4457 : {
4458 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4459 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4460 : {
4461 0 : ok = false;
4462 0 : break;
4463 : }
4464 : }
4465 2355 : else if (const char *pszVal =
4466 2355 : poDriver->GetMetadataItem(cap.c_str());
4467 1474 : pszVal && pszVal[0])
4468 : {
4469 : }
4470 1269 : else if (cap == GDAL_DCAP_CREATECOPY &&
4471 0 : (std::find(caps->begin(), caps->end(),
4472 388 : GDAL_DCAP_RASTER) != caps->end() &&
4473 1657 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4474 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4475 : {
4476 : // if it supports Create, it supports CreateCopy
4477 : }
4478 : else
4479 : {
4480 809 : ok = false;
4481 809 : break;
4482 : }
4483 : }
4484 1581 : if (ok)
4485 : {
4486 772 : res.push_back(poDriver->GetDescription());
4487 : }
4488 : }
4489 : }
4490 7 : if (bGDALGAllowed)
4491 4 : res.push_back("GDALG");
4492 7 : return res;
4493 : }
4494 :
4495 : /************************************************************************/
4496 : /* GDALAlgorithm::AddInputFormatsArg() */
4497 : /************************************************************************/
4498 :
4499 : GDALInConstructionAlgorithmArg &
4500 8537 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4501 : const char *helpMessage)
4502 : {
4503 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4504 17074 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4505 17074 : .AddAlias("if")
4506 8537 : .SetCategory(GAAC_ADVANCED);
4507 12 : arg.AddValidationAction([this, &arg]()
4508 8549 : { return ValidateFormat(arg, false, false); });
4509 : arg.SetAutoCompleteFunction(
4510 1 : [&arg](const std::string &)
4511 8538 : { return FormatAutoCompleteFunction(arg, false, false); });
4512 8537 : return arg;
4513 : }
4514 :
4515 : /************************************************************************/
4516 : /* GDALAlgorithm::AddOutputFormatArg() */
4517 : /************************************************************************/
4518 :
4519 : GDALInConstructionAlgorithmArg &
4520 8942 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4521 : bool bGDALGAllowed, const char *helpMessage)
4522 : {
4523 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4524 : MsgOrDefault(helpMessage,
4525 : bGDALGAllowed
4526 : ? _("Output format (\"GDALG\" allowed)")
4527 : : _("Output format")),
4528 17884 : pValue)
4529 17884 : .AddAlias("of")
4530 8942 : .AddAlias("format");
4531 : arg.AddValidationAction(
4532 4434 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4533 13376 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4534 : arg.SetAutoCompleteFunction(
4535 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4536 : {
4537 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4538 4 : bGDALGAllowed);
4539 8942 : });
4540 8942 : return arg;
4541 : }
4542 :
4543 : /************************************************************************/
4544 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4545 : /************************************************************************/
4546 : GDALInConstructionAlgorithmArg &
4547 1721 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4548 : const char *helpMessage)
4549 : {
4550 : auto &arg =
4551 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4552 3442 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4553 3442 : .AddAlias("ot")
4554 3442 : .AddAlias("datatype")
4555 5163 : .AddMetadataItem("type", {"GDALDataType"})
4556 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4557 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4558 1721 : "Float32", "Float64", "CFloat32", "CFloat64")
4559 1721 : .SetHiddenChoices("Byte");
4560 1721 : return arg;
4561 : }
4562 :
4563 : /************************************************************************/
4564 : /* GDALAlgorithm::AddNodataArg() */
4565 : /************************************************************************/
4566 :
4567 : GDALInConstructionAlgorithmArg &
4568 608 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4569 : const std::string &optionName,
4570 : const char *helpMessage)
4571 : {
4572 : auto &arg = AddArg(
4573 : optionName, 0,
4574 : MsgOrDefault(helpMessage,
4575 : noneAllowed
4576 : ? _("Assign a specified nodata value to output bands "
4577 : "('none', numeric value, 'nan', 'inf', '-inf')")
4578 : : _("Assign a specified nodata value to output bands "
4579 : "(numeric value, 'nan', 'inf', '-inf')")),
4580 608 : pValue);
4581 : arg.AddValidationAction(
4582 356 : [this, pValue, noneAllowed, optionName]()
4583 : {
4584 77 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4585 : {
4586 67 : char *endptr = nullptr;
4587 67 : CPLStrtod(pValue->c_str(), &endptr);
4588 67 : if (endptr != pValue->c_str() + pValue->size())
4589 : {
4590 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4591 : "Value of '%s' should be %sa "
4592 : "numeric value, 'nan', 'inf' or '-inf'",
4593 : optionName.c_str(),
4594 : noneAllowed ? "'none', " : "");
4595 1 : return false;
4596 : }
4597 : }
4598 76 : return true;
4599 608 : });
4600 608 : return arg;
4601 : }
4602 :
4603 : /************************************************************************/
4604 : /* GDALAlgorithm::AddOutputStringArg() */
4605 : /************************************************************************/
4606 :
4607 : GDALInConstructionAlgorithmArg &
4608 5403 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4609 : {
4610 : return AddArg(
4611 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4612 : MsgOrDefault(helpMessage,
4613 : _("Output string, in which the result is placed")),
4614 10806 : pValue)
4615 5403 : .SetHiddenForCLI()
4616 5403 : .SetIsInput(false)
4617 10806 : .SetIsOutput(true);
4618 : }
4619 :
4620 : /************************************************************************/
4621 : /* GDALAlgorithm::AddStdoutArg() */
4622 : /************************************************************************/
4623 :
4624 : GDALInConstructionAlgorithmArg &
4625 1454 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4626 : {
4627 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4628 : MsgOrDefault(helpMessage,
4629 : _("Directly output on stdout. If enabled, "
4630 : "output-string will be empty")),
4631 2908 : pValue)
4632 2908 : .SetHidden();
4633 : }
4634 :
4635 : /************************************************************************/
4636 : /* GDALAlgorithm::AddLayerNameArg() */
4637 : /************************************************************************/
4638 :
4639 : GDALInConstructionAlgorithmArg &
4640 206 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4641 : {
4642 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4643 206 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4644 : }
4645 :
4646 : /************************************************************************/
4647 : /* GDALAlgorithm::AddArrayNameArg() */
4648 : /************************************************************************/
4649 :
4650 : GDALInConstructionAlgorithmArg &
4651 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
4652 : {
4653 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
4654 100 : pValue)
4655 2 : .SetAutoCompleteFunction([this](const std::string &)
4656 102 : { return AutoCompleteArrayName(); });
4657 : }
4658 :
4659 : /************************************************************************/
4660 : /* GDALAlgorithm::AddArrayNameArg() */
4661 : /************************************************************************/
4662 :
4663 : GDALInConstructionAlgorithmArg &
4664 76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
4665 : const char *helpMessage)
4666 : {
4667 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
4668 152 : pValue)
4669 0 : .SetAutoCompleteFunction([this](const std::string &)
4670 152 : { return AutoCompleteArrayName(); });
4671 : }
4672 :
4673 : /************************************************************************/
4674 : /* GDALAlgorithm::AutoCompleteArrayName() */
4675 : /************************************************************************/
4676 :
4677 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
4678 : {
4679 2 : std::vector<std::string> ret;
4680 4 : std::string osDSName;
4681 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4682 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4683 : {
4684 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4685 0 : if (!inputDatasets.empty())
4686 : {
4687 0 : osDSName = inputDatasets[0].GetName();
4688 : }
4689 : }
4690 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
4691 : {
4692 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
4693 2 : osDSName = inputDataset.GetName();
4694 : }
4695 :
4696 2 : if (!osDSName.empty())
4697 : {
4698 4 : CPLStringList aosAllowedDrivers;
4699 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4700 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
4701 : aosAllowedDrivers =
4702 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
4703 :
4704 4 : CPLStringList aosOpenOptions;
4705 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
4706 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
4707 : aosOpenOptions =
4708 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
4709 :
4710 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4711 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
4712 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
4713 : {
4714 2 : if (auto poRG = poDS->GetRootGroup())
4715 : {
4716 1 : ret = poRG->GetMDArrayFullNamesRecursive();
4717 : }
4718 : }
4719 : }
4720 :
4721 4 : return ret;
4722 : }
4723 :
4724 : /************************************************************************/
4725 : /* GDALAlgorithm::AddMemorySizeArg() */
4726 : /************************************************************************/
4727 :
4728 : GDALInConstructionAlgorithmArg &
4729 210 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
4730 : const std::string &optionName,
4731 : const char *helpMessage)
4732 : {
4733 420 : return AddArg(optionName, 0, helpMessage, pStrValue)
4734 210 : .SetDefault(*pStrValue)
4735 : .AddValidationAction(
4736 139 : [this, pValue, pStrValue]()
4737 : {
4738 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
4739 : GIntBig nBytes;
4740 : bool bUnitSpecified;
4741 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
4742 47 : &bUnitSpecified) != CE_None)
4743 : {
4744 2 : return false;
4745 : }
4746 45 : if (!bUnitSpecified)
4747 : {
4748 1 : ReportError(CE_Failure, CPLE_AppDefined,
4749 : "Memory size must have a unit or be a "
4750 : "percentage of usable RAM (2GB, 5%%, etc.)");
4751 1 : return false;
4752 : }
4753 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
4754 : {
4755 : // -1 to please CoverityScan
4756 : if (static_cast<std::uint64_t>(nBytes) >
4757 : std::numeric_limits<size_t>::max() - 1U)
4758 : {
4759 : ReportError(CE_Failure, CPLE_AppDefined,
4760 : "Memory size %s is too large.",
4761 : pStrValue->c_str());
4762 : return false;
4763 : }
4764 : }
4765 :
4766 44 : *pValue = static_cast<size_t>(nBytes);
4767 44 : return true;
4768 420 : });
4769 : }
4770 :
4771 : /************************************************************************/
4772 : /* GDALAlgorithm::AddOutputLayerNameArg() */
4773 : /************************************************************************/
4774 :
4775 : GDALInConstructionAlgorithmArg &
4776 474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
4777 : const char *helpMessage)
4778 : {
4779 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
4780 474 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
4781 : }
4782 :
4783 : /************************************************************************/
4784 : /* GDALAlgorithm::AddLayerNameArg() */
4785 : /************************************************************************/
4786 :
4787 : GDALInConstructionAlgorithmArg &
4788 881 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
4789 : const char *helpMessage)
4790 : {
4791 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4792 881 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4793 : }
4794 :
4795 : /************************************************************************/
4796 : /* GDALAlgorithm::AddGeometryTypeArg() */
4797 : /************************************************************************/
4798 :
4799 : GDALInConstructionAlgorithmArg &
4800 289 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
4801 : {
4802 : return AddArg("geometry-type", 0,
4803 578 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
4804 : .SetAutoCompleteFunction(
4805 3 : [](const std::string ¤tValue)
4806 : {
4807 3 : std::vector<std::string> oRet;
4808 51 : for (const char *type :
4809 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
4810 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
4811 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
4812 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
4813 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
4814 : {
4815 68 : if (currentValue.empty() ||
4816 17 : STARTS_WITH(type, currentValue.c_str()))
4817 : {
4818 35 : oRet.push_back(type);
4819 35 : oRet.push_back(std::string(type).append("Z"));
4820 35 : oRet.push_back(std::string(type).append("M"));
4821 35 : oRet.push_back(std::string(type).append("ZM"));
4822 : }
4823 : }
4824 3 : return oRet;
4825 578 : })
4826 : .AddValidationAction(
4827 53 : [this, pValue]()
4828 : {
4829 46 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
4830 51 : wkbUnknown &&
4831 5 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
4832 : {
4833 2 : ReportError(CE_Failure, CPLE_AppDefined,
4834 : "Invalid geometry type '%s'", pValue->c_str());
4835 2 : return false;
4836 : }
4837 44 : return true;
4838 578 : });
4839 : }
4840 :
4841 : /************************************************************************/
4842 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
4843 : /************************************************************************/
4844 :
4845 : /* static */
4846 2687 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
4847 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
4848 : {
4849 2687 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
4850 : datasetArg.GetType() == GAAT_DATASET_LIST);
4851 :
4852 : layerArg.SetAutoCompleteFunction(
4853 18 : [&datasetArg](const std::string ¤tValue)
4854 : {
4855 6 : std::vector<std::string> ret;
4856 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4857 6 : GDALArgDatasetValue *dsVal = nullptr;
4858 6 : if (datasetArg.GetType() == GAAT_DATASET)
4859 : {
4860 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
4861 : }
4862 : else
4863 : {
4864 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
4865 6 : if (val.size() == 1)
4866 : {
4867 6 : dsVal = &val[0];
4868 : }
4869 : }
4870 6 : if (dsVal && !dsVal->GetName().empty())
4871 : {
4872 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4873 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
4874 6 : if (poDS)
4875 : {
4876 12 : for (auto &&poLayer : poDS->GetLayers())
4877 : {
4878 6 : if (currentValue == poLayer->GetDescription())
4879 : {
4880 1 : ret.clear();
4881 1 : ret.push_back(poLayer->GetDescription());
4882 1 : break;
4883 : }
4884 5 : ret.push_back(poLayer->GetDescription());
4885 : }
4886 : }
4887 : }
4888 12 : return ret;
4889 2687 : });
4890 2687 : }
4891 :
4892 : /************************************************************************/
4893 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
4894 : /************************************************************************/
4895 :
4896 131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
4897 : GDALInConstructionAlgorithmArg &fieldArg,
4898 : GDALInConstructionAlgorithmArg &layerNameArg,
4899 : std::vector<GDALArgDatasetValue> &datasetArg)
4900 : {
4901 :
4902 : fieldArg.SetAutoCompleteFunction(
4903 12 : [&datasetArg, &layerNameArg](const std::string ¤tValue)
4904 : {
4905 8 : std::set<std::string> ret;
4906 4 : if (!datasetArg.empty())
4907 : {
4908 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4909 :
4910 14 : auto getLayerFields = [&ret, ¤tValue](OGRLayer *poLayer)
4911 : {
4912 2 : auto poDefn = poLayer->GetLayerDefn();
4913 2 : const int nFieldCount = poDefn->GetFieldCount();
4914 8 : for (int iField = 0; iField < nFieldCount; iField++)
4915 : {
4916 : const char *fieldName =
4917 6 : poDefn->GetFieldDefn(iField)->GetNameRef();
4918 6 : if (currentValue == fieldName)
4919 : {
4920 0 : ret.clear();
4921 0 : ret.insert(fieldName);
4922 0 : break;
4923 : }
4924 6 : ret.insert(fieldName);
4925 : }
4926 2 : };
4927 :
4928 2 : GDALArgDatasetValue &dsVal = datasetArg[0];
4929 :
4930 2 : if (!dsVal.GetName().empty())
4931 : {
4932 : auto poDS = std::unique_ptr<GDALDataset>(
4933 2 : GDALDataset::Open(dsVal.GetName().c_str(),
4934 4 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
4935 2 : if (poDS)
4936 : {
4937 2 : const auto &layerName = layerNameArg.Get<std::string>();
4938 2 : if (layerName.empty())
4939 : {
4940 : // Loop through all layers
4941 4 : for (auto &&poLayer : poDS->GetLayers())
4942 : {
4943 2 : getLayerFields(poLayer);
4944 : }
4945 : }
4946 : else
4947 : {
4948 0 : const auto poLayer = poDS->GetLayerByName(
4949 0 : layerNameArg.Get<std::string>().c_str());
4950 0 : if (poLayer)
4951 : {
4952 0 : getLayerFields(poLayer);
4953 : }
4954 : }
4955 : }
4956 : }
4957 : }
4958 4 : std::vector<std::string> retVector(ret.begin(), ret.end());
4959 8 : return retVector;
4960 131 : });
4961 131 : }
4962 :
4963 : /************************************************************************/
4964 : /* GDALAlgorithm::AddFieldNameArg() */
4965 : /************************************************************************/
4966 :
4967 : GDALInConstructionAlgorithmArg &
4968 131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
4969 : {
4970 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
4971 131 : pValue);
4972 : }
4973 :
4974 : /************************************************************************/
4975 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
4976 : /************************************************************************/
4977 :
4978 262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
4979 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
4980 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
4981 : {
4982 : auto &arg =
4983 524 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
4984 786 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
4985 : .SetAutoCompleteFunction(
4986 1 : [](const std::string ¤tValue)
4987 : {
4988 1 : std::vector<std::string> oRet;
4989 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
4990 : {
4991 : const char *pszSubType =
4992 5 : OGRFieldDefn::GetFieldSubTypeName(
4993 : static_cast<OGRFieldSubType>(i));
4994 5 : if (pszSubType != nullptr)
4995 : {
4996 5 : if (currentValue.empty() ||
4997 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
4998 : {
4999 5 : oRet.push_back(pszSubType);
5000 : }
5001 : }
5002 : }
5003 :
5004 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5005 : {
5006 : // Skip deprecated
5007 14 : if (static_cast<OGRFieldType>(i) ==
5008 13 : OGRFieldType::OFTWideString ||
5009 : static_cast<OGRFieldType>(i) ==
5010 : OGRFieldType::OFTWideStringList)
5011 2 : continue;
5012 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5013 : static_cast<OGRFieldType>(i));
5014 12 : if (pszType != nullptr)
5015 : {
5016 12 : if (currentValue.empty() ||
5017 0 : STARTS_WITH(pszType, currentValue.c_str()))
5018 : {
5019 12 : oRet.push_back(pszType);
5020 : }
5021 : }
5022 : }
5023 1 : return oRet;
5024 262 : });
5025 :
5026 : auto validationFunction =
5027 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5028 : {
5029 120 : bool isValid{true};
5030 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5031 :
5032 : // String is returned for unknown types
5033 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5034 : {
5035 16 : isValid = false;
5036 : }
5037 :
5038 120 : *pSubtypeValue =
5039 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5040 :
5041 120 : if (*pSubtypeValue != OFSTNone)
5042 : {
5043 15 : isValid = true;
5044 15 : switch (*pSubtypeValue)
5045 : {
5046 6 : case OFSTBoolean:
5047 : case OFSTInt16:
5048 : {
5049 6 : *pTypeValue = OFTInteger;
5050 6 : break;
5051 : }
5052 3 : case OFSTFloat32:
5053 : {
5054 3 : *pTypeValue = OFTReal;
5055 3 : break;
5056 : }
5057 6 : default:
5058 : {
5059 6 : *pTypeValue = OFTString;
5060 6 : break;
5061 : }
5062 : }
5063 : }
5064 :
5065 120 : if (!isValid)
5066 : {
5067 2 : ReportError(CE_Failure, CPLE_AppDefined,
5068 : "Invalid value for argument '%s': '%s'",
5069 1 : arg.GetName().c_str(), pStrValue->c_str());
5070 : }
5071 :
5072 120 : return isValid;
5073 262 : };
5074 :
5075 262 : if (!pStrValue->empty())
5076 : {
5077 0 : arg.SetDefault(*pStrValue);
5078 0 : validationFunction();
5079 : }
5080 :
5081 262 : arg.AddValidationAction(std::move(validationFunction));
5082 :
5083 262 : return arg;
5084 : }
5085 :
5086 : /************************************************************************/
5087 : /* GDALAlgorithm::ValidateBandArg() */
5088 : /************************************************************************/
5089 :
5090 3793 : bool GDALAlgorithm::ValidateBandArg() const
5091 : {
5092 3793 : bool ret = true;
5093 3793 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5094 3793 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5095 1408 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5096 280 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5097 5195 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5098 143 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5099 : {
5100 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5101 : {
5102 99 : if (nBand > poDS->GetRasterCount())
5103 : {
5104 5 : ReportError(CE_Failure, CPLE_AppDefined,
5105 : "Value of 'band' should be greater or equal than "
5106 : "1 and less or equal than %d.",
5107 : poDS->GetRasterCount());
5108 5 : return false;
5109 : }
5110 94 : return true;
5111 86 : };
5112 :
5113 : const auto ValidateForOneDataset =
5114 292 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5115 : {
5116 81 : bool l_ret = true;
5117 81 : if (bandArg->GetType() == GAAT_INTEGER)
5118 : {
5119 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5120 : }
5121 57 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5122 : {
5123 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5124 : {
5125 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5126 : }
5127 : }
5128 81 : return l_ret;
5129 86 : };
5130 :
5131 86 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5132 : {
5133 : auto poDS =
5134 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5135 6 : if (poDS && !ValidateForOneDataset(poDS))
5136 2 : ret = false;
5137 : }
5138 : else
5139 : {
5140 80 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5141 79 : for (auto &datasetValue :
5142 238 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5143 : {
5144 79 : auto poDS = datasetValue.GetDatasetRef();
5145 79 : if (poDS && !ValidateForOneDataset(poDS))
5146 3 : ret = false;
5147 : }
5148 : }
5149 : }
5150 3793 : return ret;
5151 : }
5152 :
5153 : /************************************************************************/
5154 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5155 : /************************************************************************/
5156 :
5157 3015 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5158 : {
5159 3015 : return ValidateBandArg();
5160 : }
5161 :
5162 : /************************************************************************/
5163 : /* GDALAlgorithm::AddBandArg() */
5164 : /************************************************************************/
5165 :
5166 : GDALInConstructionAlgorithmArg &
5167 1445 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5168 : {
5169 1762 : AddValidationAction([this]() { return ValidateBandArg(); });
5170 :
5171 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5172 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5173 2890 : pValue)
5174 : .AddValidationAction(
5175 34 : [pValue]()
5176 : {
5177 34 : if (*pValue <= 0)
5178 : {
5179 1 : CPLError(CE_Failure, CPLE_AppDefined,
5180 : "Value of 'band' should greater or equal to 1.");
5181 1 : return false;
5182 : }
5183 33 : return true;
5184 2890 : });
5185 : }
5186 :
5187 : /************************************************************************/
5188 : /* GDALAlgorithm::AddBandArg() */
5189 : /************************************************************************/
5190 :
5191 : GDALInConstructionAlgorithmArg &
5192 834 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5193 : {
5194 1295 : AddValidationAction([this]() { return ValidateBandArg(); });
5195 :
5196 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5197 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5198 1668 : pValue)
5199 : .AddValidationAction(
5200 126 : [pValue]()
5201 : {
5202 397 : for (int val : *pValue)
5203 : {
5204 272 : if (val <= 0)
5205 : {
5206 1 : CPLError(CE_Failure, CPLE_AppDefined,
5207 : "Value of 'band' should greater or equal "
5208 : "to 1.");
5209 1 : return false;
5210 : }
5211 : }
5212 125 : return true;
5213 1668 : });
5214 : }
5215 :
5216 : /************************************************************************/
5217 : /* ParseAndValidateKeyValue() */
5218 : /************************************************************************/
5219 :
5220 402 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5221 : {
5222 406 : const auto Validate = [this, &arg](const std::string &val)
5223 : {
5224 401 : if (val.find('=') == std::string::npos)
5225 : {
5226 5 : ReportError(
5227 : CE_Failure, CPLE_AppDefined,
5228 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5229 5 : arg.GetName().c_str());
5230 5 : return false;
5231 : }
5232 :
5233 396 : return true;
5234 402 : };
5235 :
5236 402 : if (arg.GetType() == GAAT_STRING)
5237 : {
5238 0 : return Validate(arg.Get<std::string>());
5239 : }
5240 402 : else if (arg.GetType() == GAAT_STRING_LIST)
5241 : {
5242 402 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5243 402 : if (vals.size() == 1)
5244 : {
5245 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5246 734 : std::vector<std::string> newVals;
5247 734 : std::string curToken;
5248 367 : bool canSplitOnComma = true;
5249 367 : char lastSep = 0;
5250 367 : bool inString = false;
5251 367 : bool equalFoundInLastToken = false;
5252 5318 : for (char c : vals[0])
5253 : {
5254 4955 : if (!inString && c == ',')
5255 : {
5256 10 : if (lastSep != '=' || !equalFoundInLastToken)
5257 : {
5258 2 : canSplitOnComma = false;
5259 2 : break;
5260 : }
5261 8 : lastSep = c;
5262 8 : newVals.push_back(curToken);
5263 8 : curToken.clear();
5264 8 : equalFoundInLastToken = false;
5265 : }
5266 4945 : else if (!inString && c == '=')
5267 : {
5268 366 : if (lastSep == '=')
5269 : {
5270 2 : canSplitOnComma = false;
5271 2 : break;
5272 : }
5273 364 : equalFoundInLastToken = true;
5274 364 : lastSep = c;
5275 364 : curToken += c;
5276 : }
5277 4579 : else if (c == '"')
5278 : {
5279 4 : inString = !inString;
5280 4 : curToken += c;
5281 : }
5282 : else
5283 : {
5284 4575 : curToken += c;
5285 : }
5286 : }
5287 367 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5288 : {
5289 354 : if (!curToken.empty())
5290 354 : newVals.emplace_back(std::move(curToken));
5291 354 : vals = std::move(newVals);
5292 : }
5293 : }
5294 :
5295 798 : for (const auto &val : vals)
5296 : {
5297 401 : if (!Validate(val))
5298 5 : return false;
5299 : }
5300 : }
5301 :
5302 397 : return true;
5303 : }
5304 :
5305 : /************************************************************************/
5306 : /* IsGDALGOutput() */
5307 : /************************************************************************/
5308 :
5309 2015 : bool GDALAlgorithm::IsGDALGOutput() const
5310 : {
5311 2015 : bool isGDALGOutput = false;
5312 2015 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5313 2015 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5314 3436 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5315 1421 : outputArg->IsExplicitlySet())
5316 : {
5317 2785 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5318 1380 : outputFormatArg->IsExplicitlySet())
5319 : {
5320 : const auto &val =
5321 920 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5322 920 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5323 : }
5324 : else
5325 : {
5326 : const auto &filename =
5327 485 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5328 485 : isGDALGOutput =
5329 943 : filename.GetName().size() > strlen(".gdalg.json") &&
5330 458 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5331 : strlen(".gdalg.json"),
5332 : ".gdalg.json");
5333 : }
5334 : }
5335 2015 : return isGDALGOutput;
5336 : }
5337 :
5338 : /************************************************************************/
5339 : /* ProcessGDALGOutput() */
5340 : /************************************************************************/
5341 :
5342 2344 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5343 : {
5344 2344 : if (!SupportsStreamedOutput())
5345 776 : return ProcessGDALGOutputRet::NOT_GDALG;
5346 :
5347 1568 : if (IsGDALGOutput())
5348 : {
5349 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5350 : const auto &filename =
5351 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5352 : VSIStatBufL sStat;
5353 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5354 : {
5355 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5356 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5357 : {
5358 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5359 : {
5360 0 : CPLError(CE_Failure, CPLE_AppDefined,
5361 : "File '%s' already exists. Specify the "
5362 : "--overwrite option to overwrite it.",
5363 : filename.c_str());
5364 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5365 : }
5366 : }
5367 : }
5368 :
5369 22 : std::string osCommandLine;
5370 :
5371 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5372 : {
5373 33 : if (!osCommandLine.empty())
5374 22 : osCommandLine += ' ';
5375 33 : osCommandLine += path;
5376 : }
5377 :
5378 250 : for (const auto &arg : GetArgs())
5379 : {
5380 265 : if (arg->IsExplicitlySet() &&
5381 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5382 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5383 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5384 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5385 : {
5386 14 : osCommandLine += ' ';
5387 14 : std::string strArg;
5388 14 : if (!arg->Serialize(strArg))
5389 : {
5390 0 : CPLError(CE_Failure, CPLE_AppDefined,
5391 : "Cannot serialize argument %s",
5392 0 : arg->GetName().c_str());
5393 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5394 : }
5395 14 : osCommandLine += strArg;
5396 : }
5397 : }
5398 :
5399 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5400 :
5401 11 : std::string outStringUnused;
5402 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5403 11 : ? ProcessGDALGOutputRet::GDALG_OK
5404 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5405 : }
5406 :
5407 1557 : return ProcessGDALGOutputRet::NOT_GDALG;
5408 : }
5409 :
5410 : /************************************************************************/
5411 : /* GDALAlgorithm::SaveGDALG() */
5412 : /************************************************************************/
5413 :
5414 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5415 : std::string &outString,
5416 : const std::string &commandLine)
5417 : {
5418 44 : CPLJSONDocument oDoc;
5419 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5420 22 : oDoc.GetRoot().Add("command_line", commandLine);
5421 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5422 :
5423 22 : if (!filename.empty())
5424 21 : return oDoc.Save(filename);
5425 :
5426 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5427 1 : return true;
5428 : }
5429 :
5430 : /************************************************************************/
5431 : /* GDALAlgorithm::AddCreationOptionsArg() */
5432 : /************************************************************************/
5433 :
5434 : GDALInConstructionAlgorithmArg &
5435 7798 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5436 : const char *helpMessage)
5437 : {
5438 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5439 15596 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5440 15596 : .AddAlias("co")
5441 15596 : .SetMetaVar("<KEY>=<VALUE>")
5442 7798 : .SetPackedValuesAllowed(false);
5443 147 : arg.AddValidationAction([this, &arg]()
5444 7945 : { return ParseAndValidateKeyValue(arg); });
5445 :
5446 : arg.SetAutoCompleteFunction(
5447 48 : [this](const std::string ¤tValue)
5448 : {
5449 16 : std::vector<std::string> oRet;
5450 :
5451 16 : int datasetType =
5452 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5453 16 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5454 16 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5455 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5456 : {
5457 16 : datasetType = outputArg->GetDatasetType();
5458 : }
5459 :
5460 16 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5461 32 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5462 16 : outputFormat->IsExplicitlySet())
5463 : {
5464 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5465 6 : outputFormat->Get<std::string>().c_str());
5466 6 : if (poDriver)
5467 : {
5468 6 : AddOptionsSuggestions(
5469 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5470 : datasetType, currentValue, oRet);
5471 : }
5472 6 : return oRet;
5473 : }
5474 :
5475 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5476 : {
5477 10 : auto poDM = GetGDALDriverManager();
5478 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5479 10 : const auto &osDSName = datasetValue.GetName();
5480 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5481 10 : if (!osExt.empty())
5482 : {
5483 10 : std::set<std::string> oVisitedExtensions;
5484 709 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5485 : {
5486 706 : auto poDriver = poDM->GetDriver(i);
5487 2118 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5488 706 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5489 213 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5490 1412 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5491 213 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5492 0 : poDriver->GetMetadataItem(
5493 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5494 : {
5495 : const char *pszExtensions =
5496 493 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5497 493 : if (pszExtensions)
5498 : {
5499 : const CPLStringList aosExts(
5500 320 : CSLTokenizeString2(pszExtensions, " ", 0));
5501 710 : for (const char *pszExt : cpl::Iterate(aosExts))
5502 : {
5503 416 : if (EQUAL(pszExt, osExt.c_str()) &&
5504 16 : !cpl::contains(oVisitedExtensions,
5505 : pszExt))
5506 : {
5507 10 : oVisitedExtensions.insert(pszExt);
5508 10 : if (AddOptionsSuggestions(
5509 : poDriver->GetMetadataItem(
5510 10 : GDAL_DMD_CREATIONOPTIONLIST),
5511 : datasetType, currentValue,
5512 : oRet))
5513 : {
5514 7 : return oRet;
5515 : }
5516 3 : break;
5517 : }
5518 : }
5519 : }
5520 : }
5521 : }
5522 : }
5523 : }
5524 :
5525 3 : return oRet;
5526 7798 : });
5527 :
5528 7798 : return arg;
5529 : }
5530 :
5531 : /************************************************************************/
5532 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
5533 : /************************************************************************/
5534 :
5535 : GDALInConstructionAlgorithmArg &
5536 3655 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
5537 : const char *helpMessage)
5538 : {
5539 : auto &arg =
5540 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
5541 7310 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
5542 7310 : .AddAlias("lco")
5543 7310 : .SetMetaVar("<KEY>=<VALUE>")
5544 3655 : .SetPackedValuesAllowed(false);
5545 73 : arg.AddValidationAction([this, &arg]()
5546 3728 : { return ParseAndValidateKeyValue(arg); });
5547 :
5548 : arg.SetAutoCompleteFunction(
5549 5 : [this](const std::string ¤tValue)
5550 : {
5551 2 : std::vector<std::string> oRet;
5552 :
5553 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5554 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5555 2 : outputFormat->IsExplicitlySet())
5556 : {
5557 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5558 1 : outputFormat->Get<std::string>().c_str());
5559 1 : if (poDriver)
5560 : {
5561 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
5562 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5563 : GDAL_OF_VECTOR, currentValue, oRet);
5564 : }
5565 1 : return oRet;
5566 : }
5567 :
5568 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5569 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5570 : {
5571 1 : auto poDM = GetGDALDriverManager();
5572 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5573 1 : const auto &osDSName = datasetValue.GetName();
5574 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5575 1 : if (!osExt.empty())
5576 : {
5577 1 : std::set<std::string> oVisitedExtensions;
5578 227 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5579 : {
5580 226 : auto poDriver = poDM->GetDriver(i);
5581 226 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
5582 : {
5583 : const char *pszExtensions =
5584 90 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5585 90 : if (pszExtensions)
5586 : {
5587 : const CPLStringList aosExts(
5588 61 : CSLTokenizeString2(pszExtensions, " ", 0));
5589 154 : for (const char *pszExt : cpl::Iterate(aosExts))
5590 : {
5591 95 : if (EQUAL(pszExt, osExt.c_str()) &&
5592 1 : !cpl::contains(oVisitedExtensions,
5593 : pszExt))
5594 : {
5595 1 : oVisitedExtensions.insert(pszExt);
5596 1 : if (AddOptionsSuggestions(
5597 : poDriver->GetMetadataItem(
5598 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5599 : GDAL_OF_VECTOR, currentValue,
5600 : oRet))
5601 : {
5602 0 : return oRet;
5603 : }
5604 1 : break;
5605 : }
5606 : }
5607 : }
5608 : }
5609 : }
5610 : }
5611 : }
5612 :
5613 1 : return oRet;
5614 3655 : });
5615 :
5616 3655 : return arg;
5617 : }
5618 :
5619 : /************************************************************************/
5620 : /* GDALAlgorithm::AddBBOXArg() */
5621 : /************************************************************************/
5622 :
5623 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
5624 : GDALInConstructionAlgorithmArg &
5625 1796 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
5626 : {
5627 : auto &arg = AddArg("bbox", 0,
5628 : MsgOrDefault(helpMessage,
5629 : _("Bounding box as xmin,ymin,xmax,ymax")),
5630 3592 : pValue)
5631 1796 : .SetRepeatedArgAllowed(false)
5632 1796 : .SetMinCount(4)
5633 1796 : .SetMaxCount(4)
5634 1796 : .SetDisplayHintAboutRepetition(false);
5635 : arg.AddValidationAction(
5636 162 : [&arg]()
5637 : {
5638 162 : const auto &val = arg.Get<std::vector<double>>();
5639 162 : CPLAssert(val.size() == 4);
5640 162 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
5641 : {
5642 5 : CPLError(CE_Failure, CPLE_AppDefined,
5643 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
5644 : "xmin <= xmax and ymin <= ymax");
5645 5 : return false;
5646 : }
5647 157 : return true;
5648 1796 : });
5649 1796 : return arg;
5650 : }
5651 :
5652 : /************************************************************************/
5653 : /* GDALAlgorithm::AddActiveLayerArg() */
5654 : /************************************************************************/
5655 :
5656 : GDALInConstructionAlgorithmArg &
5657 1695 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
5658 : {
5659 : return AddArg("active-layer", 0,
5660 : MsgOrDefault(helpMessage,
5661 : _("Set active layer (if not specified, all)")),
5662 1695 : pValue);
5663 : }
5664 :
5665 : /************************************************************************/
5666 : /* GDALAlgorithm::AddNumThreadsArg() */
5667 : /************************************************************************/
5668 :
5669 : GDALInConstructionAlgorithmArg &
5670 676 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
5671 : const char *helpMessage)
5672 : {
5673 : auto &arg =
5674 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
5675 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
5676 676 : pStrValue);
5677 :
5678 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
5679 1352 : _("Number of jobs (read-only, hidden argument)"), pValue)
5680 676 : .SetHidden();
5681 :
5682 2583 : auto lambda = [this, &arg, pValue, pStrValue]
5683 : {
5684 861 : bool bOK = false;
5685 861 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
5686 : const int nLimit = std::clamp(
5687 861 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
5688 1722 : CPLGetNumCPUs());
5689 : const int nNumThreads =
5690 861 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
5691 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
5692 861 : if (bOK)
5693 : {
5694 861 : *pValue = nNumThreads;
5695 : }
5696 : else
5697 : {
5698 0 : ReportError(CE_Failure, CPLE_IllegalArg,
5699 : "Invalid value for '%s' argument",
5700 0 : arg.GetName().c_str());
5701 : }
5702 861 : return bOK;
5703 676 : };
5704 676 : if (!pStrValue->empty())
5705 : {
5706 630 : arg.SetDefault(*pStrValue);
5707 630 : lambda();
5708 : }
5709 676 : arg.AddValidationAction(std::move(lambda));
5710 676 : return arg;
5711 : }
5712 :
5713 : /************************************************************************/
5714 : /* GDALAlgorithm::AddAbsolutePathArg() */
5715 : /************************************************************************/
5716 :
5717 : GDALInConstructionAlgorithmArg &
5718 619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
5719 : {
5720 : return AddArg(
5721 : "absolute-path", 0,
5722 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
5723 : "should be stored as an absolute path")),
5724 619 : pValue);
5725 : }
5726 :
5727 : /************************************************************************/
5728 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
5729 : /************************************************************************/
5730 :
5731 : GDALInConstructionAlgorithmArg &
5732 137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
5733 : const char *helpMessage)
5734 : {
5735 :
5736 : const auto pixelFunctionNames =
5737 137 : VRTDerivedRasterBand::GetPixelFunctionNames();
5738 : return AddArg(
5739 : "pixel-function", 0,
5740 : MsgOrDefault(
5741 : helpMessage,
5742 : _("Specify a pixel function to calculate output value from "
5743 : "overlapping inputs")),
5744 274 : pValue)
5745 274 : .SetChoices(pixelFunctionNames);
5746 : }
5747 :
5748 : /************************************************************************/
5749 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
5750 : /************************************************************************/
5751 :
5752 : GDALInConstructionAlgorithmArg &
5753 137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
5754 : const char *helpMessage)
5755 : {
5756 : auto &pixelFunctionArgArg =
5757 : AddArg("pixel-function-arg", 0,
5758 : MsgOrDefault(
5759 : helpMessage,
5760 : _("Specify argument(s) to pass to the pixel function")),
5761 274 : pValue)
5762 274 : .SetMetaVar("<NAME>=<VALUE>")
5763 137 : .SetRepeatedArgAllowed(true);
5764 : pixelFunctionArgArg.AddValidationAction(
5765 7 : [this, &pixelFunctionArgArg]()
5766 144 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
5767 :
5768 : pixelFunctionArgArg.SetAutoCompleteFunction(
5769 12 : [this](const std::string ¤tValue)
5770 : {
5771 12 : std::string pixelFunction;
5772 6 : const auto pixelFunctionArg = GetArg("pixel-function");
5773 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
5774 : {
5775 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
5776 : }
5777 :
5778 6 : std::vector<std::string> ret;
5779 :
5780 6 : if (!pixelFunction.empty())
5781 : {
5782 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
5783 : pixelFunction.c_str());
5784 5 : if (!pair)
5785 : {
5786 1 : ret.push_back("**");
5787 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5788 1 : ret.push_back(std::string("\xC2\xA0"
5789 : "Invalid pixel function name"));
5790 : }
5791 4 : else if (pair->second.find("Argument name=") ==
5792 : std::string::npos)
5793 : {
5794 1 : ret.push_back("**");
5795 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5796 1 : ret.push_back(
5797 2 : std::string(
5798 : "\xC2\xA0"
5799 : "No pixel function arguments for pixel function '")
5800 1 : .append(pixelFunction)
5801 1 : .append("'"));
5802 : }
5803 : else
5804 : {
5805 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
5806 : ret);
5807 : }
5808 : }
5809 :
5810 12 : return ret;
5811 137 : });
5812 :
5813 137 : return pixelFunctionArgArg;
5814 : }
5815 :
5816 : /************************************************************************/
5817 : /* GDALAlgorithm::AddProgressArg() */
5818 : /************************************************************************/
5819 :
5820 7778 : void GDALAlgorithm::AddProgressArg()
5821 : {
5822 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
5823 15556 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
5824 15556 : .SetCategory(GAAC_COMMON)
5825 7778 : .AddAction([this]() { m_progressBarRequested = false; });
5826 :
5827 15556 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
5828 7778 : .SetHidden();
5829 7778 : }
5830 :
5831 : /************************************************************************/
5832 : /* GDALAlgorithm::Run() */
5833 : /************************************************************************/
5834 :
5835 4350 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
5836 : {
5837 4350 : WarnIfDeprecated();
5838 :
5839 4350 : if (m_selectedSubAlg)
5840 : {
5841 388 : if (m_calledFromCommandLine)
5842 233 : m_selectedSubAlg->m_calledFromCommandLine = true;
5843 388 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
5844 : }
5845 :
5846 3962 : if (m_helpRequested || m_helpDocRequested)
5847 : {
5848 16 : if (m_calledFromCommandLine)
5849 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
5850 16 : return true;
5851 : }
5852 :
5853 3946 : if (m_JSONUsageRequested)
5854 : {
5855 3 : if (m_calledFromCommandLine)
5856 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
5857 3 : return true;
5858 : }
5859 :
5860 3943 : if (!ValidateArguments())
5861 103 : return false;
5862 :
5863 3840 : switch (ProcessGDALGOutput())
5864 : {
5865 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
5866 0 : return false;
5867 :
5868 11 : case ProcessGDALGOutputRet::GDALG_OK:
5869 11 : return true;
5870 :
5871 3829 : case ProcessGDALGOutputRet::NOT_GDALG:
5872 3829 : break;
5873 : }
5874 :
5875 3829 : if (m_executionForStreamOutput)
5876 : {
5877 82 : if (!CheckSafeForStreamOutput())
5878 : {
5879 4 : return false;
5880 : }
5881 : }
5882 :
5883 3825 : return RunImpl(pfnProgress, pProgressData);
5884 : }
5885 :
5886 : /************************************************************************/
5887 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
5888 : /************************************************************************/
5889 :
5890 37 : bool GDALAlgorithm::CheckSafeForStreamOutput()
5891 : {
5892 37 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5893 37 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
5894 : {
5895 37 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5896 37 : if (!EQUAL(val.c_str(), "stream"))
5897 : {
5898 : // For security reasons, to avoid that reading a .gdalg.json file
5899 : // writes a file on the file system.
5900 4 : ReportError(
5901 : CE_Failure, CPLE_NotSupported,
5902 : "in streamed execution, --format stream should be used");
5903 4 : return false;
5904 : }
5905 : }
5906 33 : return true;
5907 : }
5908 :
5909 : /************************************************************************/
5910 : /* GDALAlgorithm::Finalize() */
5911 : /************************************************************************/
5912 :
5913 1622 : bool GDALAlgorithm::Finalize()
5914 : {
5915 1622 : bool ret = true;
5916 1622 : if (m_selectedSubAlg)
5917 239 : ret = m_selectedSubAlg->Finalize();
5918 :
5919 29558 : for (auto &arg : m_args)
5920 : {
5921 27936 : if (arg->GetType() == GAAT_DATASET)
5922 : {
5923 1302 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
5924 : }
5925 26634 : else if (arg->GetType() == GAAT_DATASET_LIST)
5926 : {
5927 2490 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
5928 : {
5929 1178 : ret = ds.Close() && ret;
5930 : }
5931 : }
5932 : }
5933 1622 : return ret;
5934 : }
5935 :
5936 : /************************************************************************/
5937 : /* GDALAlgorithm::GetArgNamesForCLI() */
5938 : /************************************************************************/
5939 :
5940 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
5941 683 : GDALAlgorithm::GetArgNamesForCLI() const
5942 : {
5943 1366 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
5944 :
5945 683 : size_t maxOptLen = 0;
5946 8414 : for (const auto &arg : m_args)
5947 : {
5948 7731 : if (arg->IsHidden() || arg->IsHiddenForCLI())
5949 1695 : continue;
5950 6036 : std::string opt;
5951 6036 : bool addComma = false;
5952 6036 : if (!arg->GetShortName().empty())
5953 : {
5954 1389 : opt += '-';
5955 1389 : opt += arg->GetShortName();
5956 1389 : addComma = true;
5957 : }
5958 6036 : for (char alias : arg->GetShortNameAliases())
5959 : {
5960 0 : if (addComma)
5961 0 : opt += ", ";
5962 0 : opt += "-";
5963 0 : opt += alias;
5964 0 : addComma = true;
5965 : }
5966 6714 : for (const std::string &alias : arg->GetAliases())
5967 : {
5968 678 : if (addComma)
5969 290 : opt += ", ";
5970 678 : opt += "--";
5971 678 : opt += alias;
5972 678 : addComma = true;
5973 : }
5974 6036 : if (!arg->GetName().empty())
5975 : {
5976 6036 : if (addComma)
5977 1777 : opt += ", ";
5978 6036 : opt += "--";
5979 6036 : opt += arg->GetName();
5980 : }
5981 6036 : const auto &metaVar = arg->GetMetaVar();
5982 6036 : if (!metaVar.empty())
5983 : {
5984 3742 : opt += ' ';
5985 3742 : if (metaVar.front() != '<')
5986 2673 : opt += '<';
5987 3742 : opt += metaVar;
5988 3742 : if (metaVar.back() != '>')
5989 2689 : opt += '>';
5990 : }
5991 6036 : maxOptLen = std::max(maxOptLen, opt.size());
5992 6036 : options.emplace_back(arg.get(), opt);
5993 : }
5994 :
5995 1366 : return std::make_pair(std::move(options), maxOptLen);
5996 : }
5997 :
5998 : /************************************************************************/
5999 : /* GDALAlgorithm::GetUsageForCLI() */
6000 : /************************************************************************/
6001 :
6002 : std::string
6003 406 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6004 : const UsageOptions &usageOptions) const
6005 : {
6006 406 : if (m_selectedSubAlg)
6007 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6008 :
6009 798 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6010 798 : std::string osPath;
6011 803 : for (const std::string &s : m_callPath)
6012 : {
6013 404 : if (!osPath.empty())
6014 49 : osPath += ' ';
6015 404 : osPath += s;
6016 : }
6017 399 : osRet += ' ';
6018 399 : osRet += osPath;
6019 :
6020 399 : bool hasNonPositionals = false;
6021 4888 : for (const auto &arg : m_args)
6022 : {
6023 4489 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6024 3200 : hasNonPositionals = true;
6025 : }
6026 :
6027 399 : if (HasSubAlgorithms())
6028 : {
6029 9 : if (m_callPath.size() == 1)
6030 : {
6031 8 : osRet += " <COMMAND>";
6032 8 : if (hasNonPositionals)
6033 8 : osRet += " [OPTIONS]";
6034 8 : if (usageOptions.isPipelineStep)
6035 : {
6036 5 : const size_t nLenFirstLine = osRet.size();
6037 5 : osRet += '\n';
6038 5 : osRet.append(nLenFirstLine, '-');
6039 5 : osRet += '\n';
6040 : }
6041 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6042 : }
6043 : else
6044 : {
6045 1 : osRet += " <SUBCOMMAND>";
6046 1 : if (hasNonPositionals)
6047 1 : osRet += " [OPTIONS]";
6048 1 : if (usageOptions.isPipelineStep)
6049 : {
6050 0 : const size_t nLenFirstLine = osRet.size();
6051 0 : osRet += '\n';
6052 0 : osRet.append(nLenFirstLine, '-');
6053 0 : osRet += '\n';
6054 : }
6055 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6056 : }
6057 9 : size_t maxNameLen = 0;
6058 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6059 : {
6060 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6061 : }
6062 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6063 : {
6064 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6065 43 : if (subAlg && !subAlg->IsHidden())
6066 : {
6067 43 : const std::string &name(subAlg->GetName());
6068 43 : osRet += " - ";
6069 43 : osRet += name;
6070 43 : osRet += ": ";
6071 43 : osRet.append(maxNameLen - name.size(), ' ');
6072 43 : osRet += subAlg->GetDescription();
6073 43 : if (!subAlg->m_aliases.empty())
6074 : {
6075 6 : bool first = true;
6076 6 : for (const auto &alias : subAlg->GetAliases())
6077 : {
6078 6 : if (alias ==
6079 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6080 6 : break;
6081 0 : if (first)
6082 0 : osRet += " (alias: ";
6083 : else
6084 0 : osRet += ", ";
6085 0 : osRet += alias;
6086 0 : first = false;
6087 : }
6088 6 : if (!first)
6089 : {
6090 0 : osRet += ')';
6091 : }
6092 : }
6093 43 : osRet += '\n';
6094 : }
6095 : }
6096 :
6097 9 : if (shortUsage && hasNonPositionals)
6098 : {
6099 2 : osRet += "\nTry '";
6100 2 : osRet += osPath;
6101 2 : osRet += " --help' for help.\n";
6102 : }
6103 : }
6104 : else
6105 : {
6106 390 : if (!m_args.empty())
6107 : {
6108 390 : if (hasNonPositionals)
6109 390 : osRet += " [OPTIONS]";
6110 574 : for (const auto *arg : m_positionalArgs)
6111 : {
6112 257 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6113 73 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6114 : {
6115 : const bool optional =
6116 195 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6117 28 : arg->GetName() == "pipeline"));
6118 167 : osRet += ' ';
6119 167 : if (optional)
6120 25 : osRet += '[';
6121 167 : const std::string &metavar = arg->GetMetaVar();
6122 167 : if (!metavar.empty() && metavar[0] == '<')
6123 : {
6124 4 : osRet += metavar;
6125 : }
6126 : else
6127 : {
6128 163 : osRet += '<';
6129 163 : osRet += metavar;
6130 163 : osRet += '>';
6131 : }
6132 209 : if (arg->GetType() == GAAT_DATASET_LIST &&
6133 42 : arg->GetMaxCount() > 1)
6134 : {
6135 28 : osRet += "...";
6136 : }
6137 167 : if (optional)
6138 25 : osRet += ']';
6139 : }
6140 : }
6141 : }
6142 :
6143 390 : const size_t nLenFirstLine = osRet.size();
6144 390 : osRet += '\n';
6145 390 : if (usageOptions.isPipelineStep)
6146 : {
6147 305 : osRet.append(nLenFirstLine, '-');
6148 305 : osRet += '\n';
6149 : }
6150 :
6151 390 : if (shortUsage)
6152 : {
6153 21 : osRet += "Try '";
6154 21 : osRet += osPath;
6155 21 : osRet += " --help' for help.\n";
6156 21 : return osRet;
6157 : }
6158 :
6159 369 : osRet += '\n';
6160 369 : osRet += m_description;
6161 369 : osRet += '\n';
6162 : }
6163 :
6164 378 : if (!m_args.empty() && !shortUsage)
6165 : {
6166 752 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6167 : size_t maxOptLen;
6168 376 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6169 376 : if (usageOptions.maxOptLen)
6170 307 : maxOptLen = usageOptions.maxOptLen;
6171 :
6172 752 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6173 376 : if (m_arbitraryLongNameArgsAllowed)
6174 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6175 :
6176 : const auto OutputArg =
6177 2239 : [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
6178 19618 : const std::string &opt)
6179 : {
6180 2239 : osRet += " ";
6181 2239 : osRet += opt;
6182 2239 : osRet += " ";
6183 2239 : osRet.append(maxOptLen - opt.size(), ' ');
6184 2239 : osRet += arg->GetDescription();
6185 :
6186 2239 : const auto &choices = arg->GetChoices();
6187 2239 : if (!choices.empty())
6188 : {
6189 209 : osRet += ". ";
6190 209 : osRet += arg->GetMetaVar();
6191 209 : osRet += '=';
6192 209 : bool firstChoice = true;
6193 1665 : for (const auto &choice : choices)
6194 : {
6195 1456 : if (!firstChoice)
6196 1247 : osRet += '|';
6197 1456 : osRet += choice;
6198 1456 : firstChoice = false;
6199 : }
6200 : }
6201 :
6202 4416 : if (arg->GetType() == GAAT_DATASET ||
6203 2177 : arg->GetType() == GAAT_DATASET_LIST)
6204 : {
6205 124 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
6206 17 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6207 : {
6208 9 : osRet += " (created by algorithm)";
6209 : }
6210 : }
6211 :
6212 2239 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6213 : {
6214 171 : osRet += " (default: ";
6215 171 : osRet += arg->GetDefault<std::string>();
6216 171 : osRet += ')';
6217 : }
6218 2068 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6219 : {
6220 50 : if (arg->GetDefault<bool>())
6221 0 : osRet += " (default: true)";
6222 : }
6223 2018 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6224 : {
6225 76 : osRet += " (default: ";
6226 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6227 76 : osRet += ')';
6228 : }
6229 1942 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6230 : {
6231 49 : osRet += " (default: ";
6232 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6233 49 : osRet += ')';
6234 : }
6235 2263 : else if (arg->GetType() == GAAT_STRING_LIST &&
6236 370 : arg->HasDefaultValue())
6237 : {
6238 : const auto &defaultVal =
6239 9 : arg->GetDefault<std::vector<std::string>>();
6240 9 : if (defaultVal.size() == 1)
6241 : {
6242 9 : osRet += " (default: ";
6243 9 : osRet += defaultVal[0];
6244 9 : osRet += ')';
6245 : }
6246 : }
6247 1911 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6248 27 : arg->HasDefaultValue())
6249 : {
6250 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6251 0 : if (defaultVal.size() == 1)
6252 : {
6253 0 : osRet += " (default: ";
6254 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6255 0 : osRet += ')';
6256 : }
6257 : }
6258 1884 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6259 : {
6260 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6261 0 : if (defaultVal.size() == 1)
6262 : {
6263 0 : osRet += " (default: ";
6264 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6265 0 : osRet += ')';
6266 : }
6267 : }
6268 :
6269 2239 : if (arg->GetDisplayHintAboutRepetition())
6270 : {
6271 2272 : if (arg->GetMinCount() > 0 &&
6272 92 : arg->GetMinCount() == arg->GetMaxCount())
6273 : {
6274 18 : if (arg->GetMinCount() != 1)
6275 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6276 : }
6277 2236 : else if (arg->GetMinCount() > 0 &&
6278 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6279 : {
6280 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6281 8 : arg->GetMaxCount());
6282 : }
6283 2154 : else if (arg->GetMinCount() > 0)
6284 : {
6285 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6286 : }
6287 2088 : else if (arg->GetMaxCount() > 1)
6288 : {
6289 359 : osRet += " [may be repeated]";
6290 : }
6291 : }
6292 :
6293 2239 : if (arg->IsRequired())
6294 : {
6295 165 : osRet += " [required]";
6296 : }
6297 :
6298 2239 : osRet += '\n';
6299 :
6300 2239 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6301 2239 : if (!mutualExclusionGroup.empty())
6302 : {
6303 386 : std::string otherArgs;
6304 3626 : for (const auto &otherArg : m_args)
6305 : {
6306 6222 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6307 2789 : otherArg.get() == arg)
6308 837 : continue;
6309 2596 : if (otherArg->GetMutualExclusionGroup() ==
6310 : mutualExclusionGroup)
6311 : {
6312 254 : if (!otherArgs.empty())
6313 65 : otherArgs += ", ";
6314 254 : otherArgs += "--";
6315 254 : otherArgs += otherArg->GetName();
6316 : }
6317 : }
6318 193 : if (!otherArgs.empty())
6319 : {
6320 189 : osRet += " ";
6321 189 : osRet += " ";
6322 189 : osRet.append(maxOptLen, ' ');
6323 189 : osRet += "Mutually exclusive with ";
6324 189 : osRet += otherArgs;
6325 189 : osRet += '\n';
6326 : }
6327 : }
6328 2239 : };
6329 :
6330 376 : if (!m_positionalArgs.empty())
6331 : {
6332 146 : osRet += "\nPositional arguments:\n";
6333 1509 : for (const auto &[arg, opt] : options)
6334 : {
6335 1363 : if (arg->IsPositional())
6336 137 : OutputArg(arg, opt);
6337 : }
6338 : }
6339 :
6340 376 : if (hasNonPositionals)
6341 : {
6342 376 : bool hasCommon = false;
6343 376 : bool hasBase = false;
6344 376 : bool hasAdvanced = false;
6345 376 : bool hasEsoteric = false;
6346 752 : std::vector<std::string> categories;
6347 3574 : for (const auto &iter : options)
6348 : {
6349 3198 : const auto &arg = iter.first;
6350 3198 : if (!arg->IsPositional())
6351 : {
6352 3061 : const auto &category = arg->GetCategory();
6353 3061 : if (category == GAAC_COMMON)
6354 : {
6355 1171 : hasCommon = true;
6356 : }
6357 1890 : else if (category == GAAC_BASE)
6358 : {
6359 1673 : hasBase = true;
6360 : }
6361 217 : else if (category == GAAC_ADVANCED)
6362 : {
6363 166 : hasAdvanced = true;
6364 : }
6365 51 : else if (category == GAAC_ESOTERIC)
6366 : {
6367 18 : hasEsoteric = true;
6368 : }
6369 33 : else if (std::find(categories.begin(), categories.end(),
6370 33 : category) == categories.end())
6371 : {
6372 9 : categories.push_back(category);
6373 : }
6374 : }
6375 : }
6376 376 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6377 63 : categories.insert(categories.begin(), GAAC_ADVANCED);
6378 376 : if (hasBase)
6379 330 : categories.insert(categories.begin(), GAAC_BASE);
6380 376 : if (hasCommon && !usageOptions.isPipelineStep)
6381 66 : categories.insert(categories.begin(), GAAC_COMMON);
6382 376 : if (hasEsoteric)
6383 6 : categories.push_back(GAAC_ESOTERIC);
6384 :
6385 850 : for (const auto &category : categories)
6386 : {
6387 474 : osRet += "\n";
6388 474 : if (category != GAAC_BASE)
6389 : {
6390 144 : osRet += category;
6391 144 : osRet += ' ';
6392 : }
6393 474 : osRet += "Options:\n";
6394 5005 : for (const auto &[arg, opt] : options)
6395 : {
6396 4531 : if (!arg->IsPositional() && arg->GetCategory() == category)
6397 2102 : OutputArg(arg, opt);
6398 : }
6399 474 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6400 : {
6401 2 : osRet += " ";
6402 2 : osRet += userProvidedOpt;
6403 2 : osRet += " ";
6404 2 : if (userProvidedOpt.size() < maxOptLen)
6405 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6406 2 : osRet += "Argument provided by user";
6407 2 : osRet += '\n';
6408 : }
6409 : }
6410 : }
6411 : }
6412 :
6413 378 : if (!m_longDescription.empty())
6414 : {
6415 6 : osRet += '\n';
6416 6 : osRet += m_longDescription;
6417 6 : osRet += '\n';
6418 : }
6419 :
6420 378 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6421 : {
6422 365 : if (!m_helpURL.empty())
6423 : {
6424 365 : osRet += "\nFor more details, consult ";
6425 365 : osRet += GetHelpFullURL();
6426 365 : osRet += '\n';
6427 : }
6428 365 : osRet += GetUsageForCLIEnd();
6429 : }
6430 :
6431 378 : return osRet;
6432 : }
6433 :
6434 : /************************************************************************/
6435 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6436 : /************************************************************************/
6437 :
6438 : //! @cond Doxygen_Suppress
6439 372 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6440 : {
6441 372 : std::string osRet;
6442 :
6443 372 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6444 : {
6445 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6446 : "alternative interface to GDAL and OGR command line "
6447 : "utilities.\nThe project reserves the right to modify, "
6448 : "rename, reorganize, and change the behavior of the utility\n"
6449 : "until it is officially frozen in a future feature release of "
6450 13 : "GDAL.\n";
6451 : }
6452 372 : return osRet;
6453 : }
6454 :
6455 : //! @endcond
6456 :
6457 : /************************************************************************/
6458 : /* GDALAlgorithm::GetUsageAsJSON() */
6459 : /************************************************************************/
6460 :
6461 553 : std::string GDALAlgorithm::GetUsageAsJSON() const
6462 : {
6463 1106 : CPLJSONDocument oDoc;
6464 1106 : auto oRoot = oDoc.GetRoot();
6465 :
6466 553 : if (m_displayInJSONUsage)
6467 : {
6468 551 : oRoot.Add("name", m_name);
6469 551 : CPLJSONArray jFullPath;
6470 1138 : for (const std::string &s : m_callPath)
6471 : {
6472 587 : jFullPath.Add(s);
6473 : }
6474 551 : oRoot.Add("full_path", jFullPath);
6475 : }
6476 :
6477 553 : oRoot.Add("description", m_description);
6478 553 : if (!m_helpURL.empty())
6479 : {
6480 552 : oRoot.Add("short_url", m_helpURL);
6481 552 : oRoot.Add("url", GetHelpFullURL());
6482 : }
6483 :
6484 1106 : CPLJSONArray jSubAlgorithms;
6485 749 : for (const auto &subAlgName : GetSubAlgorithmNames())
6486 : {
6487 392 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6488 196 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
6489 : {
6490 194 : CPLJSONDocument oSubDoc;
6491 194 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
6492 194 : jSubAlgorithms.Add(oSubDoc.GetRoot());
6493 : }
6494 : }
6495 553 : oRoot.Add("sub_algorithms", jSubAlgorithms);
6496 :
6497 553 : if (m_arbitraryLongNameArgsAllowed)
6498 : {
6499 1 : oRoot.Add("user_provided_arguments_allowed", true);
6500 : }
6501 :
6502 5224 : const auto ProcessArg = [](const GDALAlgorithmArg *arg)
6503 : {
6504 5224 : CPLJSONObject jArg;
6505 5224 : jArg.Add("name", arg->GetName());
6506 5224 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
6507 5224 : jArg.Add("description", arg->GetDescription());
6508 :
6509 5224 : const auto &metaVar = arg->GetMetaVar();
6510 5224 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
6511 : {
6512 1556 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
6513 1556 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
6514 : std::string::npos)
6515 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
6516 : else
6517 799 : jArg.Add("metavar", metaVar);
6518 : }
6519 :
6520 5224 : const auto &choices = arg->GetChoices();
6521 5224 : if (!choices.empty())
6522 : {
6523 388 : CPLJSONArray jChoices;
6524 3396 : for (const auto &choice : choices)
6525 3008 : jChoices.Add(choice);
6526 388 : jArg.Add("choices", jChoices);
6527 : }
6528 5224 : if (arg->HasDefaultValue())
6529 : {
6530 1138 : switch (arg->GetType())
6531 : {
6532 399 : case GAAT_BOOLEAN:
6533 399 : jArg.Add("default", arg->GetDefault<bool>());
6534 399 : break;
6535 342 : case GAAT_STRING:
6536 342 : jArg.Add("default", arg->GetDefault<std::string>());
6537 342 : break;
6538 199 : case GAAT_INTEGER:
6539 199 : jArg.Add("default", arg->GetDefault<int>());
6540 199 : break;
6541 178 : case GAAT_REAL:
6542 178 : jArg.Add("default", arg->GetDefault<double>());
6543 178 : break;
6544 18 : case GAAT_STRING_LIST:
6545 : {
6546 : const auto &val =
6547 18 : arg->GetDefault<std::vector<std::string>>();
6548 18 : if (val.size() == 1)
6549 : {
6550 17 : jArg.Add("default", val[0]);
6551 : }
6552 : else
6553 : {
6554 1 : CPLJSONArray jArr;
6555 3 : for (const auto &s : val)
6556 : {
6557 2 : jArr.Add(s);
6558 : }
6559 1 : jArg.Add("default", jArr);
6560 : }
6561 18 : break;
6562 : }
6563 1 : case GAAT_INTEGER_LIST:
6564 : {
6565 1 : const auto &val = arg->GetDefault<std::vector<int>>();
6566 1 : if (val.size() == 1)
6567 : {
6568 0 : jArg.Add("default", val[0]);
6569 : }
6570 : else
6571 : {
6572 1 : CPLJSONArray jArr;
6573 3 : for (int i : val)
6574 : {
6575 2 : jArr.Add(i);
6576 : }
6577 1 : jArg.Add("default", jArr);
6578 : }
6579 1 : break;
6580 : }
6581 1 : case GAAT_REAL_LIST:
6582 : {
6583 1 : const auto &val = arg->GetDefault<std::vector<double>>();
6584 1 : if (val.size() == 1)
6585 : {
6586 0 : jArg.Add("default", val[0]);
6587 : }
6588 : else
6589 : {
6590 1 : CPLJSONArray jArr;
6591 3 : for (double d : val)
6592 : {
6593 2 : jArr.Add(d);
6594 : }
6595 1 : jArg.Add("default", jArr);
6596 : }
6597 1 : break;
6598 : }
6599 0 : case GAAT_DATASET:
6600 : case GAAT_DATASET_LIST:
6601 0 : CPLError(CE_Warning, CPLE_AppDefined,
6602 : "Unhandled default value for arg %s",
6603 0 : arg->GetName().c_str());
6604 0 : break;
6605 : }
6606 : }
6607 :
6608 5224 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
6609 5224 : if (!std::isnan(minVal))
6610 : {
6611 668 : if (arg->GetType() == GAAT_INTEGER ||
6612 260 : arg->GetType() == GAAT_INTEGER_LIST)
6613 171 : jArg.Add("min_value", static_cast<int>(minVal));
6614 : else
6615 237 : jArg.Add("min_value", minVal);
6616 408 : jArg.Add("min_value_is_included", minValIsIncluded);
6617 : }
6618 :
6619 5224 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
6620 5224 : if (!std::isnan(maxVal))
6621 : {
6622 199 : if (arg->GetType() == GAAT_INTEGER ||
6623 82 : arg->GetType() == GAAT_INTEGER_LIST)
6624 35 : jArg.Add("max_value", static_cast<int>(maxVal));
6625 : else
6626 82 : jArg.Add("max_value", maxVal);
6627 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
6628 : }
6629 :
6630 5224 : jArg.Add("required", arg->IsRequired());
6631 5224 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
6632 : {
6633 1453 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
6634 1453 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
6635 1453 : jArg.Add("min_count", arg->GetMinCount());
6636 1453 : jArg.Add("max_count", arg->GetMaxCount());
6637 : }
6638 5224 : jArg.Add("category", arg->GetCategory());
6639 :
6640 10207 : if (arg->GetType() == GAAT_DATASET ||
6641 4983 : arg->GetType() == GAAT_DATASET_LIST)
6642 : {
6643 : {
6644 437 : CPLJSONArray jAr;
6645 437 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
6646 305 : jAr.Add("raster");
6647 437 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
6648 168 : jAr.Add("vector");
6649 437 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
6650 28 : jAr.Add("multidim_raster");
6651 437 : jArg.Add("dataset_type", jAr);
6652 : }
6653 :
6654 597 : const auto GetFlags = [](int flags)
6655 : {
6656 597 : CPLJSONArray jAr;
6657 597 : if (flags & GADV_NAME)
6658 437 : jAr.Add("name");
6659 597 : if (flags & GADV_OBJECT)
6660 555 : jAr.Add("dataset");
6661 597 : return jAr;
6662 : };
6663 :
6664 437 : if (arg->IsInput())
6665 : {
6666 437 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
6667 : }
6668 437 : if (arg->IsOutput())
6669 : {
6670 160 : jArg.Add("output_flags",
6671 320 : GetFlags(arg->GetDatasetOutputFlags()));
6672 : }
6673 : }
6674 :
6675 5224 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6676 5224 : if (!mutualExclusionGroup.empty())
6677 : {
6678 640 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
6679 : }
6680 :
6681 10448 : const auto &metadata = arg->GetMetadata();
6682 5224 : if (!metadata.empty())
6683 : {
6684 434 : CPLJSONObject jMetadata;
6685 907 : for (const auto &[key, values] : metadata)
6686 : {
6687 946 : CPLJSONArray jValue;
6688 1144 : for (const auto &value : values)
6689 671 : jValue.Add(value);
6690 473 : jMetadata.Add(key, jValue);
6691 : }
6692 434 : jArg.Add("metadata", jMetadata);
6693 : }
6694 :
6695 10448 : return jArg;
6696 : };
6697 :
6698 : {
6699 553 : CPLJSONArray jArgs;
6700 8669 : for (const auto &arg : m_args)
6701 : {
6702 8116 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
6703 5009 : jArgs.Add(ProcessArg(arg.get()));
6704 : }
6705 553 : oRoot.Add("input_arguments", jArgs);
6706 : }
6707 :
6708 : {
6709 553 : CPLJSONArray jArgs;
6710 8669 : for (const auto &arg : m_args)
6711 : {
6712 8116 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
6713 55 : jArgs.Add(ProcessArg(arg.get()));
6714 : }
6715 553 : oRoot.Add("output_arguments", jArgs);
6716 : }
6717 :
6718 : {
6719 553 : CPLJSONArray jArgs;
6720 8669 : for (const auto &arg : m_args)
6721 : {
6722 8116 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
6723 160 : jArgs.Add(ProcessArg(arg.get()));
6724 : }
6725 553 : oRoot.Add("input_output_arguments", jArgs);
6726 : }
6727 :
6728 553 : if (m_supportsStreamedOutput)
6729 : {
6730 110 : oRoot.Add("supports_streamed_output", true);
6731 : }
6732 :
6733 1106 : return oDoc.SaveAsString();
6734 : }
6735 :
6736 : /************************************************************************/
6737 : /* GDALAlgorithm::GetAutoComplete() */
6738 : /************************************************************************/
6739 :
6740 : std::vector<std::string>
6741 242 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
6742 : bool lastWordIsComplete, bool showAllOptions)
6743 : {
6744 484 : std::vector<std::string> ret;
6745 :
6746 : // Get inner-most algorithm
6747 242 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
6748 242 : GDALAlgorithm *curAlg = this;
6749 479 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
6750 : {
6751 : auto subAlg = curAlg->InstantiateSubAlgorithm(
6752 344 : args.front(), /* suggestionAllowed = */ false);
6753 344 : if (!subAlg)
6754 106 : break;
6755 238 : if (args.size() == 1 && !lastWordIsComplete)
6756 : {
6757 5 : int nCount = 0;
6758 114 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
6759 : {
6760 109 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
6761 6 : nCount++;
6762 : }
6763 5 : if (nCount >= 2)
6764 : {
6765 11 : for (const std::string &subAlgName :
6766 23 : curAlg->GetSubAlgorithmNames())
6767 : {
6768 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
6769 11 : if (subAlg && !subAlg->IsHidden())
6770 11 : ret.push_back(subAlg->GetName());
6771 : }
6772 1 : return ret;
6773 : }
6774 : }
6775 237 : showAllOptions = false;
6776 237 : args.erase(args.begin());
6777 237 : curAlgHolder = std::move(subAlg);
6778 237 : curAlg = curAlgHolder.get();
6779 : }
6780 241 : if (curAlg != this)
6781 : {
6782 129 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
6783 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
6784 129 : /* showAllOptions = */ false);
6785 : }
6786 :
6787 224 : std::string option;
6788 224 : std::string value;
6789 112 : ExtractLastOptionAndValue(args, option, value);
6790 :
6791 139 : if (option.empty() && !args.empty() && !args.back().empty() &&
6792 27 : args.back()[0] == '-')
6793 : {
6794 24 : const auto &lastArg = args.back();
6795 : // List available options
6796 339 : for (const auto &arg : GetArgs())
6797 : {
6798 583 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
6799 531 : (!showAllOptions &&
6800 720 : (arg->GetName() == "help" || arg->GetName() == "config" ||
6801 434 : arg->GetName() == "version" ||
6802 217 : arg->GetName() == "json-usage")))
6803 : {
6804 116 : continue;
6805 : }
6806 199 : if (!arg->GetShortName().empty())
6807 : {
6808 126 : std::string str = std::string("-").append(arg->GetShortName());
6809 42 : if (lastArg == str)
6810 0 : ret.push_back(std::move(str));
6811 : }
6812 199 : if (lastArg != "-" && lastArg != "--")
6813 : {
6814 52 : for (const std::string &alias : arg->GetAliases())
6815 : {
6816 48 : std::string str = std::string("--").append(alias);
6817 16 : if (cpl::starts_with(str, lastArg))
6818 3 : ret.push_back(std::move(str));
6819 : }
6820 : }
6821 199 : if (!arg->GetName().empty())
6822 : {
6823 597 : std::string str = std::string("--").append(arg->GetName());
6824 199 : if (cpl::starts_with(str, lastArg))
6825 165 : ret.push_back(std::move(str));
6826 : }
6827 : }
6828 24 : std::sort(ret.begin(), ret.end());
6829 : }
6830 88 : else if (!option.empty())
6831 : {
6832 : // List possible choices for current option
6833 82 : auto arg = GetArg(option);
6834 82 : if (arg && arg->GetType() != GAAT_BOOLEAN)
6835 : {
6836 82 : ret = arg->GetChoices();
6837 82 : if (ret.empty())
6838 : {
6839 : {
6840 77 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6841 77 : SetParseForAutoCompletion();
6842 77 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6843 : }
6844 77 : ret = arg->GetAutoCompleteChoices(value);
6845 : }
6846 : else
6847 : {
6848 5 : std::sort(ret.begin(), ret.end());
6849 : }
6850 82 : if (!ret.empty() && ret.back() == value)
6851 : {
6852 2 : ret.clear();
6853 : }
6854 80 : else if (ret.empty())
6855 : {
6856 9 : ret.push_back("**");
6857 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6858 18 : ret.push_back(std::string("\xC2\xA0"
6859 : "description: ")
6860 9 : .append(arg->GetDescription()));
6861 : }
6862 : }
6863 : }
6864 : else
6865 : {
6866 : // List possible sub-algorithms
6867 59 : for (const std::string &subAlgName : GetSubAlgorithmNames())
6868 : {
6869 106 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6870 53 : if (subAlg && !subAlg->IsHidden())
6871 53 : ret.push_back(subAlg->GetName());
6872 : }
6873 6 : if (!ret.empty())
6874 : {
6875 2 : std::sort(ret.begin(), ret.end());
6876 : }
6877 :
6878 : // Try filenames
6879 6 : if (ret.empty() && !args.empty())
6880 : {
6881 : {
6882 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
6883 3 : SetParseForAutoCompletion();
6884 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
6885 : }
6886 :
6887 3 : const std::string &lastArg = args.back();
6888 3 : GDALAlgorithmArg *arg = nullptr;
6889 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
6890 21 : "like", "source", "destination"})
6891 : {
6892 18 : if (!arg)
6893 : {
6894 3 : auto newArg = GetArg(name);
6895 3 : if (newArg)
6896 : {
6897 3 : if (!newArg->IsExplicitlySet())
6898 : {
6899 0 : arg = newArg;
6900 : }
6901 6 : else if (newArg->GetType() == GAAT_STRING ||
6902 5 : newArg->GetType() == GAAT_STRING_LIST ||
6903 8 : newArg->GetType() == GAAT_DATASET ||
6904 2 : newArg->GetType() == GAAT_DATASET_LIST)
6905 : {
6906 : VSIStatBufL sStat;
6907 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
6908 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
6909 : {
6910 3 : arg = newArg;
6911 : }
6912 : }
6913 : }
6914 : }
6915 : }
6916 3 : if (arg)
6917 : {
6918 3 : ret = arg->GetAutoCompleteChoices(lastArg);
6919 : }
6920 : }
6921 : }
6922 :
6923 112 : return ret;
6924 : }
6925 :
6926 : /************************************************************************/
6927 : /* GDALAlgorithm::GetFieldIndices() */
6928 : /************************************************************************/
6929 :
6930 43 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
6931 : OGRLayerH hLayer, std::vector<int> &indices)
6932 : {
6933 43 : VALIDATE_POINTER1(hLayer, __func__, false);
6934 :
6935 43 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
6936 :
6937 43 : if (names.size() == 1 && names[0] == "ALL")
6938 : {
6939 11 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
6940 27 : for (int i = 0; i < nSrcFieldCount; ++i)
6941 : {
6942 16 : indices.push_back(i);
6943 : }
6944 : }
6945 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
6946 : {
6947 6 : std::set<int> fieldsAdded;
6948 14 : for (const std::string &osFieldName : names)
6949 : {
6950 :
6951 : const int nIdx =
6952 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
6953 :
6954 10 : if (nIdx < 0)
6955 : {
6956 2 : CPLError(CE_Failure, CPLE_AppDefined,
6957 : "Field '%s' does not exist in layer '%s'",
6958 2 : osFieldName.c_str(), layer.GetName());
6959 2 : return false;
6960 : }
6961 :
6962 8 : if (fieldsAdded.insert(nIdx).second)
6963 : {
6964 7 : indices.push_back(nIdx);
6965 : }
6966 : }
6967 : }
6968 :
6969 41 : return true;
6970 : }
6971 :
6972 : /************************************************************************/
6973 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
6974 : /************************************************************************/
6975 :
6976 112 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
6977 : std::string &option,
6978 : std::string &value) const
6979 : {
6980 112 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
6981 : {
6982 82 : const auto nPosEqual = args.back().find('=');
6983 82 : if (nPosEqual == std::string::npos)
6984 : {
6985 : // Deal with "gdal ... --option"
6986 63 : if (GetArg(args.back()))
6987 : {
6988 39 : option = args.back();
6989 39 : args.pop_back();
6990 : }
6991 : }
6992 : else
6993 : {
6994 : // Deal with "gdal ... --option=<value>"
6995 19 : if (GetArg(args.back().substr(0, nPosEqual)))
6996 : {
6997 19 : option = args.back().substr(0, nPosEqual);
6998 19 : value = args.back().substr(nPosEqual + 1);
6999 19 : args.pop_back();
7000 : }
7001 : }
7002 : }
7003 55 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7004 25 : args[args.size() - 2][0] == '-')
7005 : {
7006 : // Deal with "gdal ... --option <value>"
7007 24 : auto arg = GetArg(args[args.size() - 2]);
7008 24 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7009 : {
7010 24 : option = args[args.size() - 2];
7011 24 : value = args.back();
7012 24 : args.pop_back();
7013 : }
7014 : }
7015 :
7016 112 : const auto IsKeyValueOption = [](const std::string &osStr)
7017 : {
7018 302 : return osStr == "--co" || osStr == "--creation-option" ||
7019 279 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7020 300 : osStr == "--oo" || osStr == "--open-option";
7021 : };
7022 :
7023 112 : if (IsKeyValueOption(option))
7024 : {
7025 22 : const auto nPosEqual = value.find('=');
7026 22 : if (nPosEqual != std::string::npos)
7027 : {
7028 11 : value.resize(nPosEqual);
7029 : }
7030 : }
7031 112 : }
7032 :
7033 : //! @cond Doxygen_Suppress
7034 :
7035 : /************************************************************************/
7036 : /* GDALContainerAlgorithm::RunImpl() */
7037 : /************************************************************************/
7038 :
7039 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7040 : {
7041 0 : return false;
7042 : }
7043 :
7044 : //! @endcond
7045 :
7046 : /************************************************************************/
7047 : /* GDALAlgorithmRelease() */
7048 : /************************************************************************/
7049 :
7050 : /** Release a handle to an algorithm.
7051 : *
7052 : * @since 3.11
7053 : */
7054 11812 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7055 : {
7056 11812 : delete hAlg;
7057 11812 : }
7058 :
7059 : /************************************************************************/
7060 : /* GDALAlgorithmGetName() */
7061 : /************************************************************************/
7062 :
7063 : /** Return the algorithm name.
7064 : *
7065 : * @param hAlg Handle to an algorithm. Must NOT be null.
7066 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7067 : * be freed.
7068 : * @since 3.11
7069 : */
7070 5531 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7071 : {
7072 5531 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7073 5531 : return hAlg->ptr->GetName().c_str();
7074 : }
7075 :
7076 : /************************************************************************/
7077 : /* GDALAlgorithmGetDescription() */
7078 : /************************************************************************/
7079 :
7080 : /** Return the algorithm (short) description.
7081 : *
7082 : * @param hAlg Handle to an algorithm. Must NOT be null.
7083 : * @return algorithm description whose lifetime is bound to hAlg and which must
7084 : * not be freed.
7085 : * @since 3.11
7086 : */
7087 5455 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7088 : {
7089 5455 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7090 5455 : return hAlg->ptr->GetDescription().c_str();
7091 : }
7092 :
7093 : /************************************************************************/
7094 : /* GDALAlgorithmGetLongDescription() */
7095 : /************************************************************************/
7096 :
7097 : /** Return the algorithm (longer) description.
7098 : *
7099 : * @param hAlg Handle to an algorithm. Must NOT be null.
7100 : * @return algorithm description whose lifetime is bound to hAlg and which must
7101 : * not be freed.
7102 : * @since 3.11
7103 : */
7104 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7105 : {
7106 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7107 2 : return hAlg->ptr->GetLongDescription().c_str();
7108 : }
7109 :
7110 : /************************************************************************/
7111 : /* GDALAlgorithmGetHelpFullURL() */
7112 : /************************************************************************/
7113 :
7114 : /** Return the algorithm full URL.
7115 : *
7116 : * @param hAlg Handle to an algorithm. Must NOT be null.
7117 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7118 : * not be freed.
7119 : * @since 3.11
7120 : */
7121 4840 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7122 : {
7123 4840 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7124 4840 : return hAlg->ptr->GetHelpFullURL().c_str();
7125 : }
7126 :
7127 : /************************************************************************/
7128 : /* GDALAlgorithmHasSubAlgorithms() */
7129 : /************************************************************************/
7130 :
7131 : /** Return whether the algorithm has sub-algorithms.
7132 : *
7133 : * @param hAlg Handle to an algorithm. Must NOT be null.
7134 : * @since 3.11
7135 : */
7136 8757 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7137 : {
7138 8757 : VALIDATE_POINTER1(hAlg, __func__, false);
7139 8757 : return hAlg->ptr->HasSubAlgorithms();
7140 : }
7141 :
7142 : /************************************************************************/
7143 : /* GDALAlgorithmGetSubAlgorithmNames() */
7144 : /************************************************************************/
7145 :
7146 : /** Get the names of registered algorithms.
7147 : *
7148 : * @param hAlg Handle to an algorithm. Must NOT be null.
7149 : * @return a NULL terminated list of names, which must be destroyed with
7150 : * CSLDestroy()
7151 : * @since 3.11
7152 : */
7153 665 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7154 : {
7155 665 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7156 665 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7157 : }
7158 :
7159 : /************************************************************************/
7160 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7161 : /************************************************************************/
7162 :
7163 : /** Instantiate an algorithm by its name (or its alias).
7164 : *
7165 : * @param hAlg Handle to an algorithm. Must NOT be null.
7166 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7167 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7168 : * or NULL if the algorithm does not exist or another error occurred.
7169 : * @since 3.11
7170 : */
7171 8231 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7172 : const char *pszSubAlgName)
7173 : {
7174 8231 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7175 8231 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7176 16462 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7177 : return subAlg
7178 16462 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7179 16462 : : nullptr;
7180 : }
7181 :
7182 : /************************************************************************/
7183 : /* GDALAlgorithmParseCommandLineArguments() */
7184 : /************************************************************************/
7185 :
7186 : /** Parse a command line argument, which does not include the algorithm
7187 : * name, to set the value of corresponding arguments.
7188 : *
7189 : * @param hAlg Handle to an algorithm. Must NOT be null.
7190 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7191 : * @return true if successful, false otherwise
7192 : * @since 3.11
7193 : */
7194 :
7195 352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7196 : CSLConstList papszArgs)
7197 : {
7198 352 : VALIDATE_POINTER1(hAlg, __func__, false);
7199 352 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7200 : }
7201 :
7202 : /************************************************************************/
7203 : /* GDALAlgorithmGetActualAlgorithm() */
7204 : /************************************************************************/
7205 :
7206 : /** Return the actual algorithm that is going to be invoked, when the
7207 : * current algorithm has sub-algorithms.
7208 : *
7209 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7210 : *
7211 : * Note that the lifetime of the returned algorithm does not exceed the one of
7212 : * the hAlg instance that owns it.
7213 : *
7214 : * @param hAlg Handle to an algorithm. Must NOT be null.
7215 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7216 : * @since 3.11
7217 : */
7218 883 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7219 : {
7220 883 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7221 883 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7222 : }
7223 :
7224 : /************************************************************************/
7225 : /* GDALAlgorithmRun() */
7226 : /************************************************************************/
7227 :
7228 : /** Execute the algorithm, starting with ValidateArguments() and then
7229 : * calling RunImpl().
7230 : *
7231 : * @param hAlg Handle to an algorithm. Must NOT be null.
7232 : * @param pfnProgress Progress callback. May be null.
7233 : * @param pProgressData Progress callback user data. May be null.
7234 : * @return true if successful, false otherwise
7235 : * @since 3.11
7236 : */
7237 :
7238 2548 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7239 : void *pProgressData)
7240 : {
7241 2548 : VALIDATE_POINTER1(hAlg, __func__, false);
7242 2548 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7243 : }
7244 :
7245 : /************************************************************************/
7246 : /* GDALAlgorithmFinalize() */
7247 : /************************************************************************/
7248 :
7249 : /** Complete any pending actions, and return the final status.
7250 : * This is typically useful for algorithm that generate an output dataset.
7251 : *
7252 : * Note that this function does *NOT* release memory associated with the
7253 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7254 : *
7255 : * @param hAlg Handle to an algorithm. Must NOT be null.
7256 : * @return true if successful, false otherwise
7257 : * @since 3.11
7258 : */
7259 :
7260 870 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7261 : {
7262 870 : VALIDATE_POINTER1(hAlg, __func__, false);
7263 870 : return hAlg->ptr->Finalize();
7264 : }
7265 :
7266 : /************************************************************************/
7267 : /* GDALAlgorithmGetUsageAsJSON() */
7268 : /************************************************************************/
7269 :
7270 : /** Return the usage of the algorithm as a JSON-serialized string.
7271 : *
7272 : * This can be used to dynamically generate interfaces to algorithms.
7273 : *
7274 : * @param hAlg Handle to an algorithm. Must NOT be null.
7275 : * @return a string that must be freed with CPLFree()
7276 : * @since 3.11
7277 : */
7278 4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7279 : {
7280 4 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7281 4 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7282 : }
7283 :
7284 : /************************************************************************/
7285 : /* GDALAlgorithmGetArgNames() */
7286 : /************************************************************************/
7287 :
7288 : /** Return the list of available argument names.
7289 : *
7290 : * @param hAlg Handle to an algorithm. Must NOT be null.
7291 : * @return a NULL terminated list of names, which must be destroyed with
7292 : * CSLDestroy()
7293 : * @since 3.11
7294 : */
7295 15029 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7296 : {
7297 15029 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7298 30058 : CPLStringList list;
7299 336512 : for (const auto &arg : hAlg->ptr->GetArgs())
7300 321483 : list.AddString(arg->GetName().c_str());
7301 15029 : return list.StealList();
7302 : }
7303 :
7304 : /************************************************************************/
7305 : /* GDALAlgorithmGetArg() */
7306 : /************************************************************************/
7307 :
7308 : /** Return an argument from its name.
7309 : *
7310 : * The lifetime of the returned object does not exceed the one of hAlg.
7311 : *
7312 : * @param hAlg Handle to an algorithm. Must NOT be null.
7313 : * @param pszArgName Argument name. Must NOT be null.
7314 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7315 : * or nullptr in case of error
7316 : * @since 3.11
7317 : */
7318 322354 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7319 : const char *pszArgName)
7320 : {
7321 322354 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7322 322354 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7323 644708 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7324 322354 : /* isConst = */ true);
7325 322354 : if (!arg)
7326 3 : return nullptr;
7327 322351 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7328 : }
7329 :
7330 : /************************************************************************/
7331 : /* GDALAlgorithmGetArgNonConst() */
7332 : /************************************************************************/
7333 :
7334 : /** Return an argument from its name, possibly allowing creation of user-provided
7335 : * argument if the algorithm allow it.
7336 : *
7337 : * The lifetime of the returned object does not exceed the one of hAlg.
7338 : *
7339 : * @param hAlg Handle to an algorithm. Must NOT be null.
7340 : * @param pszArgName Argument name. Must NOT be null.
7341 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7342 : * or nullptr in case of error
7343 : * @since 3.12
7344 : */
7345 9025 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7346 : const char *pszArgName)
7347 : {
7348 9025 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7349 9025 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7350 18050 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7351 9025 : /* isConst = */ false);
7352 9025 : if (!arg)
7353 1 : return nullptr;
7354 9024 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7355 : }
7356 :
7357 : /************************************************************************/
7358 : /* GDALAlgorithmArgRelease() */
7359 : /************************************************************************/
7360 :
7361 : /** Release a handle to an argument.
7362 : *
7363 : * @since 3.11
7364 : */
7365 331375 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7366 : {
7367 331375 : delete hArg;
7368 331375 : }
7369 :
7370 : /************************************************************************/
7371 : /* GDALAlgorithmArgGetName() */
7372 : /************************************************************************/
7373 :
7374 : /** Return the name of an argument.
7375 : *
7376 : * @param hArg Handle to an argument. Must NOT be null.
7377 : * @return argument name whose lifetime is bound to hArg and which must not
7378 : * be freed.
7379 : * @since 3.11
7380 : */
7381 18035 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7382 : {
7383 18035 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7384 18035 : return hArg->ptr->GetName().c_str();
7385 : }
7386 :
7387 : /************************************************************************/
7388 : /* GDALAlgorithmArgGetType() */
7389 : /************************************************************************/
7390 :
7391 : /** Get the type of an argument
7392 : *
7393 : * @param hArg Handle to an argument. Must NOT be null.
7394 : * @since 3.11
7395 : */
7396 410534 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
7397 : {
7398 410534 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
7399 410534 : return hArg->ptr->GetType();
7400 : }
7401 :
7402 : /************************************************************************/
7403 : /* GDALAlgorithmArgGetDescription() */
7404 : /************************************************************************/
7405 :
7406 : /** Return the description of an argument.
7407 : *
7408 : * @param hArg Handle to an argument. Must NOT be null.
7409 : * @return argument description whose lifetime is bound to hArg and which must not
7410 : * be freed.
7411 : * @since 3.11
7412 : */
7413 82083 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
7414 : {
7415 82083 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7416 82083 : return hArg->ptr->GetDescription().c_str();
7417 : }
7418 :
7419 : /************************************************************************/
7420 : /* GDALAlgorithmArgGetShortName() */
7421 : /************************************************************************/
7422 :
7423 : /** Return the short name, or empty string if there is none
7424 : *
7425 : * @param hArg Handle to an argument. Must NOT be null.
7426 : * @return short name whose lifetime is bound to hArg and which must not
7427 : * be freed.
7428 : * @since 3.11
7429 : */
7430 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
7431 : {
7432 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7433 1 : return hArg->ptr->GetShortName().c_str();
7434 : }
7435 :
7436 : /************************************************************************/
7437 : /* GDALAlgorithmArgGetAliases() */
7438 : /************************************************************************/
7439 :
7440 : /** Return the aliases (potentially none)
7441 : *
7442 : * @param hArg Handle to an argument. Must NOT be null.
7443 : * @return a NULL terminated list of names, which must be destroyed with
7444 : * CSLDestroy()
7445 :
7446 : * @since 3.11
7447 : */
7448 1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
7449 : {
7450 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7451 1 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
7452 : }
7453 :
7454 : /************************************************************************/
7455 : /* GDALAlgorithmArgGetMetaVar() */
7456 : /************************************************************************/
7457 :
7458 : /** Return the "meta-var" hint.
7459 : *
7460 : * By default, the meta-var value is the long name of the argument in
7461 : * upper case.
7462 : *
7463 : * @param hArg Handle to an argument. Must NOT be null.
7464 : * @return meta-var hint whose lifetime is bound to hArg and which must not
7465 : * be freed.
7466 : * @since 3.11
7467 : */
7468 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
7469 : {
7470 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7471 1 : return hArg->ptr->GetMetaVar().c_str();
7472 : }
7473 :
7474 : /************************************************************************/
7475 : /* GDALAlgorithmArgGetCategory() */
7476 : /************************************************************************/
7477 :
7478 : /** Return the argument category
7479 : *
7480 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
7481 : *
7482 : * @param hArg Handle to an argument. Must NOT be null.
7483 : * @return category whose lifetime is bound to hArg and which must not
7484 : * be freed.
7485 : * @since 3.11
7486 : */
7487 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
7488 : {
7489 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7490 1 : return hArg->ptr->GetCategory().c_str();
7491 : }
7492 :
7493 : /************************************************************************/
7494 : /* GDALAlgorithmArgIsPositional() */
7495 : /************************************************************************/
7496 :
7497 : /** Return if the argument is a positional one.
7498 : *
7499 : * @param hArg Handle to an argument. Must NOT be null.
7500 : * @since 3.11
7501 : */
7502 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
7503 : {
7504 1 : VALIDATE_POINTER1(hArg, __func__, false);
7505 1 : return hArg->ptr->IsPositional();
7506 : }
7507 :
7508 : /************************************************************************/
7509 : /* GDALAlgorithmArgIsRequired() */
7510 : /************************************************************************/
7511 :
7512 : /** Return whether the argument is required. Defaults to false.
7513 : *
7514 : * @param hArg Handle to an argument. Must NOT be null.
7515 : * @since 3.11
7516 : */
7517 155391 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
7518 : {
7519 155391 : VALIDATE_POINTER1(hArg, __func__, false);
7520 155391 : return hArg->ptr->IsRequired();
7521 : }
7522 :
7523 : /************************************************************************/
7524 : /* GDALAlgorithmArgGetMinCount() */
7525 : /************************************************************************/
7526 :
7527 : /** Return the minimum number of values for the argument.
7528 : *
7529 : * Defaults to 0.
7530 : * Only applies to list type of arguments.
7531 : *
7532 : * @param hArg Handle to an argument. Must NOT be null.
7533 : * @since 3.11
7534 : */
7535 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
7536 : {
7537 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7538 1 : return hArg->ptr->GetMinCount();
7539 : }
7540 :
7541 : /************************************************************************/
7542 : /* GDALAlgorithmArgGetMaxCount() */
7543 : /************************************************************************/
7544 :
7545 : /** Return the maximum number of values for the argument.
7546 : *
7547 : * Defaults to 1 for scalar types, and INT_MAX for list types.
7548 : * Only applies to list type of arguments.
7549 : *
7550 : * @param hArg Handle to an argument. Must NOT be null.
7551 : * @since 3.11
7552 : */
7553 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
7554 : {
7555 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7556 1 : return hArg->ptr->GetMaxCount();
7557 : }
7558 :
7559 : /************************************************************************/
7560 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
7561 : /************************************************************************/
7562 :
7563 : /** Return whether, for list type of arguments, several values, space
7564 : * separated, may be specified. That is "--foo=bar,baz".
7565 : * The default is true.
7566 : *
7567 : * @param hArg Handle to an argument. Must NOT be null.
7568 : * @since 3.11
7569 : */
7570 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
7571 : {
7572 1 : VALIDATE_POINTER1(hArg, __func__, false);
7573 1 : return hArg->ptr->GetPackedValuesAllowed();
7574 : }
7575 :
7576 : /************************************************************************/
7577 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
7578 : /************************************************************************/
7579 :
7580 : /** Return whether, for list type of arguments, the argument may be
7581 : * repeated. That is "--foo=bar --foo=baz".
7582 : * The default is true.
7583 : *
7584 : * @param hArg Handle to an argument. Must NOT be null.
7585 : * @since 3.11
7586 : */
7587 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
7588 : {
7589 1 : VALIDATE_POINTER1(hArg, __func__, false);
7590 1 : return hArg->ptr->GetRepeatedArgAllowed();
7591 : }
7592 :
7593 : /************************************************************************/
7594 : /* GDALAlgorithmArgGetChoices() */
7595 : /************************************************************************/
7596 :
7597 : /** Return the allowed values (as strings) for the argument.
7598 : *
7599 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
7600 : *
7601 : * @param hArg Handle to an argument. Must NOT be null.
7602 : * @return a NULL terminated list of names, which must be destroyed with
7603 : * CSLDestroy()
7604 :
7605 : * @since 3.11
7606 : */
7607 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
7608 : {
7609 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7610 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
7611 : }
7612 :
7613 : /************************************************************************/
7614 : /* GDALAlgorithmArgGetMetadataItem() */
7615 : /************************************************************************/
7616 :
7617 : /** Return the values of the metadata item of an argument.
7618 : *
7619 : * @param hArg Handle to an argument. Must NOT be null.
7620 : * @param pszItem Name of the item. Must NOT be null.
7621 : * @return a NULL terminated list of values, which must be destroyed with
7622 : * CSLDestroy()
7623 :
7624 : * @since 3.11
7625 : */
7626 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
7627 : const char *pszItem)
7628 : {
7629 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7630 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
7631 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
7632 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
7633 : }
7634 :
7635 : /************************************************************************/
7636 : /* GDALAlgorithmArgIsExplicitlySet() */
7637 : /************************************************************************/
7638 :
7639 : /** Return whether the argument value has been explicitly set with Set()
7640 : *
7641 : * @param hArg Handle to an argument. Must NOT be null.
7642 : * @since 3.11
7643 : */
7644 589 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
7645 : {
7646 589 : VALIDATE_POINTER1(hArg, __func__, false);
7647 589 : return hArg->ptr->IsExplicitlySet();
7648 : }
7649 :
7650 : /************************************************************************/
7651 : /* GDALAlgorithmArgHasDefaultValue() */
7652 : /************************************************************************/
7653 :
7654 : /** Return if the argument has a declared default value.
7655 : *
7656 : * @param hArg Handle to an argument. Must NOT be null.
7657 : * @since 3.11
7658 : */
7659 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
7660 : {
7661 2 : VALIDATE_POINTER1(hArg, __func__, false);
7662 2 : return hArg->ptr->HasDefaultValue();
7663 : }
7664 :
7665 : /************************************************************************/
7666 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
7667 : /************************************************************************/
7668 :
7669 : /** Return the argument default value as a integer.
7670 : *
7671 : * Must only be called on arguments whose type is GAAT_BOOLEAN
7672 : *
7673 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7674 : * argument has a default value.
7675 : *
7676 : * @param hArg Handle to an argument. Must NOT be null.
7677 : * @since 3.12
7678 : */
7679 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
7680 : {
7681 3 : VALIDATE_POINTER1(hArg, __func__, false);
7682 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
7683 : {
7684 1 : CPLError(CE_Failure, CPLE_AppDefined,
7685 : "%s must only be called on arguments of type GAAT_BOOLEAN",
7686 : __func__);
7687 1 : return false;
7688 : }
7689 2 : return hArg->ptr->GetDefault<bool>();
7690 : }
7691 :
7692 : /************************************************************************/
7693 : /* GDALAlgorithmArgGetDefaultAsString() */
7694 : /************************************************************************/
7695 :
7696 : /** Return the argument default value as a string.
7697 : *
7698 : * Must only be called on arguments whose type is GAAT_STRING.
7699 : *
7700 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7701 : * argument has a default value.
7702 : *
7703 : * @param hArg Handle to an argument. Must NOT be null.
7704 : * @return string whose lifetime is bound to hArg and which must not
7705 : * be freed.
7706 : * @since 3.11
7707 : */
7708 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
7709 : {
7710 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7711 3 : if (hArg->ptr->GetType() != GAAT_STRING)
7712 : {
7713 2 : CPLError(CE_Failure, CPLE_AppDefined,
7714 : "%s must only be called on arguments of type GAAT_STRING",
7715 : __func__);
7716 2 : return nullptr;
7717 : }
7718 1 : return hArg->ptr->GetDefault<std::string>().c_str();
7719 : }
7720 :
7721 : /************************************************************************/
7722 : /* GDALAlgorithmArgGetDefaultAsInteger() */
7723 : /************************************************************************/
7724 :
7725 : /** Return the argument default value as a integer.
7726 : *
7727 : * Must only be called on arguments whose type is GAAT_INTEGER
7728 : *
7729 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7730 : * argument has a default value.
7731 : *
7732 : * @param hArg Handle to an argument. Must NOT be null.
7733 : * @since 3.12
7734 : */
7735 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
7736 : {
7737 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7738 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
7739 : {
7740 2 : CPLError(CE_Failure, CPLE_AppDefined,
7741 : "%s must only be called on arguments of type GAAT_INTEGER",
7742 : __func__);
7743 2 : return 0;
7744 : }
7745 1 : return hArg->ptr->GetDefault<int>();
7746 : }
7747 :
7748 : /************************************************************************/
7749 : /* GDALAlgorithmArgGetDefaultAsDouble() */
7750 : /************************************************************************/
7751 :
7752 : /** Return the argument default value as a double.
7753 : *
7754 : * Must only be called on arguments whose type is GAAT_REAL
7755 : *
7756 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7757 : * argument has a default value.
7758 : *
7759 : * @param hArg Handle to an argument. Must NOT be null.
7760 : * @since 3.12
7761 : */
7762 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
7763 : {
7764 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7765 3 : if (hArg->ptr->GetType() != GAAT_REAL)
7766 : {
7767 2 : CPLError(CE_Failure, CPLE_AppDefined,
7768 : "%s must only be called on arguments of type GAAT_REAL",
7769 : __func__);
7770 2 : return 0;
7771 : }
7772 1 : return hArg->ptr->GetDefault<double>();
7773 : }
7774 :
7775 : /************************************************************************/
7776 : /* GDALAlgorithmArgGetDefaultAsStringList() */
7777 : /************************************************************************/
7778 :
7779 : /** Return the argument default value as a string list.
7780 : *
7781 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
7782 : *
7783 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7784 : * argument has a default value.
7785 : *
7786 : * @param hArg Handle to an argument. Must NOT be null.
7787 : * @return a NULL terminated list of names, which must be destroyed with
7788 : * CSLDestroy()
7789 :
7790 : * @since 3.12
7791 : */
7792 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
7793 : {
7794 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7795 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
7796 : {
7797 2 : CPLError(CE_Failure, CPLE_AppDefined,
7798 : "%s must only be called on arguments of type GAAT_STRING_LIST",
7799 : __func__);
7800 2 : return nullptr;
7801 : }
7802 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
7803 1 : .StealList();
7804 : }
7805 :
7806 : /************************************************************************/
7807 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
7808 : /************************************************************************/
7809 :
7810 : /** Return the argument default value as a integer list.
7811 : *
7812 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
7813 : *
7814 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7815 : * argument has a default value.
7816 : *
7817 : * @param hArg Handle to an argument. Must NOT be null.
7818 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7819 : * @since 3.12
7820 : */
7821 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
7822 : size_t *pnCount)
7823 : {
7824 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7825 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7826 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
7827 : {
7828 2 : CPLError(
7829 : CE_Failure, CPLE_AppDefined,
7830 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
7831 : __func__);
7832 2 : *pnCount = 0;
7833 2 : return nullptr;
7834 : }
7835 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
7836 1 : *pnCount = val.size();
7837 1 : return val.data();
7838 : }
7839 :
7840 : /************************************************************************/
7841 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
7842 : /************************************************************************/
7843 :
7844 : /** Return the argument default value as a real list.
7845 : *
7846 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
7847 : *
7848 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7849 : * argument has a default value.
7850 : *
7851 : * @param hArg Handle to an argument. Must NOT be null.
7852 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
7853 : * @since 3.12
7854 : */
7855 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
7856 : size_t *pnCount)
7857 : {
7858 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7859 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
7860 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
7861 : {
7862 2 : CPLError(CE_Failure, CPLE_AppDefined,
7863 : "%s must only be called on arguments of type GAAT_REAL_LIST",
7864 : __func__);
7865 2 : *pnCount = 0;
7866 2 : return nullptr;
7867 : }
7868 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
7869 1 : *pnCount = val.size();
7870 1 : return val.data();
7871 : }
7872 :
7873 : /************************************************************************/
7874 : /* GDALAlgorithmArgIsHidden() */
7875 : /************************************************************************/
7876 :
7877 : /** Return whether the argument is hidden (for GDAL internal use)
7878 : *
7879 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
7880 : * GDALAlgorithmArgIsHiddenForAPI().
7881 : *
7882 : * @param hArg Handle to an argument. Must NOT be null.
7883 : * @since 3.12
7884 : */
7885 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
7886 : {
7887 1 : VALIDATE_POINTER1(hArg, __func__, false);
7888 1 : return hArg->ptr->IsHidden();
7889 : }
7890 :
7891 : /************************************************************************/
7892 : /* GDALAlgorithmArgIsHiddenForCLI() */
7893 : /************************************************************************/
7894 :
7895 : /** Return whether the argument must not be mentioned in CLI usage.
7896 : *
7897 : * For example, "output-value" for "gdal raster info", which is only
7898 : * meant when the algorithm is used from a non-CLI context.
7899 : *
7900 : * @param hArg Handle to an argument. Must NOT be null.
7901 : * @since 3.11
7902 : */
7903 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
7904 : {
7905 1 : VALIDATE_POINTER1(hArg, __func__, false);
7906 1 : return hArg->ptr->IsHiddenForCLI();
7907 : }
7908 :
7909 : /************************************************************************/
7910 : /* GDALAlgorithmArgIsHiddenForAPI() */
7911 : /************************************************************************/
7912 :
7913 : /** Return whether the argument must not be mentioned in the context of an
7914 : * API use.
7915 : * Said otherwise, if it is only for CLI usage.
7916 : *
7917 : * For example "--help"
7918 : *
7919 : * @param hArg Handle to an argument. Must NOT be null.
7920 : * @since 3.12
7921 : */
7922 209716 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
7923 : {
7924 209716 : VALIDATE_POINTER1(hArg, __func__, false);
7925 209716 : return hArg->ptr->IsHiddenForAPI();
7926 : }
7927 :
7928 : /************************************************************************/
7929 : /* GDALAlgorithmArgIsOnlyForCLI() */
7930 : /************************************************************************/
7931 :
7932 : /** Return whether the argument must not be mentioned in the context of an
7933 : * API use.
7934 : * Said otherwise, if it is only for CLI usage.
7935 : *
7936 : * For example "--help"
7937 : *
7938 : * @param hArg Handle to an argument. Must NOT be null.
7939 : * @since 3.11
7940 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
7941 : */
7942 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
7943 : {
7944 0 : VALIDATE_POINTER1(hArg, __func__, false);
7945 0 : return hArg->ptr->IsHiddenForAPI();
7946 : }
7947 :
7948 : /************************************************************************/
7949 : /* GDALAlgorithmArgIsInput() */
7950 : /************************************************************************/
7951 :
7952 : /** Indicate whether the value of the argument is read-only during the
7953 : * execution of the algorithm.
7954 : *
7955 : * Default is true.
7956 : *
7957 : * @param hArg Handle to an argument. Must NOT be null.
7958 : * @since 3.11
7959 : */
7960 206805 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
7961 : {
7962 206805 : VALIDATE_POINTER1(hArg, __func__, false);
7963 206805 : return hArg->ptr->IsInput();
7964 : }
7965 :
7966 : /************************************************************************/
7967 : /* GDALAlgorithmArgIsOutput() */
7968 : /************************************************************************/
7969 :
7970 : /** Return whether (at least part of) the value of the argument is set
7971 : * during the execution of the algorithm.
7972 : *
7973 : * For example, "output-value" for "gdal raster info"
7974 : * Default is false.
7975 : * An argument may return both IsInput() and IsOutput() as true.
7976 : * For example the "gdal raster convert" algorithm consumes the dataset
7977 : * name of its "output" argument, and sets the dataset object during its
7978 : * execution.
7979 : *
7980 : * @param hArg Handle to an argument. Must NOT be null.
7981 : * @since 3.11
7982 : */
7983 114654 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
7984 : {
7985 114654 : VALIDATE_POINTER1(hArg, __func__, false);
7986 114654 : return hArg->ptr->IsOutput();
7987 : }
7988 :
7989 : /************************************************************************/
7990 : /* GDALAlgorithmArgGetDatasetType() */
7991 : /************************************************************************/
7992 :
7993 : /** Get which type of dataset is allowed / generated.
7994 : *
7995 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
7996 : * GDAL_OF_MULTIDIM_RASTER.
7997 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
7998 : *
7999 : * @param hArg Handle to an argument. Must NOT be null.
8000 : * @since 3.11
8001 : */
8002 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8003 : {
8004 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8005 2 : return hArg->ptr->GetDatasetType();
8006 : }
8007 :
8008 : /************************************************************************/
8009 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8010 : /************************************************************************/
8011 :
8012 : /** Indicates which components among name and dataset are accepted as
8013 : * input, when this argument serves as an input.
8014 : *
8015 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8016 : * input.
8017 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8018 : * accepted as input.
8019 : * If both bits are set, the algorithm can accept either a name or a dataset
8020 : * object.
8021 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8022 : *
8023 : * @param hArg Handle to an argument. Must NOT be null.
8024 : * @return string whose lifetime is bound to hAlg and which must not
8025 : * be freed.
8026 : * @since 3.11
8027 : */
8028 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8029 : {
8030 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8031 2 : return hArg->ptr->GetDatasetInputFlags();
8032 : }
8033 :
8034 : /************************************************************************/
8035 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8036 : /************************************************************************/
8037 :
8038 : /** Indicates which components among name and dataset are modified,
8039 : * when this argument serves as an output.
8040 : *
8041 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8042 : * output (that is the algorithm will generate the name. Rarely used).
8043 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8044 : * generated as output, and available for use after the algorithm has
8045 : * completed.
8046 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8047 : *
8048 : * @param hArg Handle to an argument. Must NOT be null.
8049 : * @return string whose lifetime is bound to hAlg and which must not
8050 : * be freed.
8051 : * @since 3.11
8052 : */
8053 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8054 : {
8055 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8056 2 : return hArg->ptr->GetDatasetOutputFlags();
8057 : }
8058 :
8059 : /************************************************************************/
8060 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8061 : /************************************************************************/
8062 :
8063 : /** Return the name of the mutual exclusion group to which this argument
8064 : * belongs to.
8065 : *
8066 : * Or empty string if it does not belong to any exclusion group.
8067 : *
8068 : * @param hArg Handle to an argument. Must NOT be null.
8069 : * @return string whose lifetime is bound to hArg and which must not
8070 : * be freed.
8071 : * @since 3.11
8072 : */
8073 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8074 : {
8075 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8076 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8077 : }
8078 :
8079 : /************************************************************************/
8080 : /* GDALAlgorithmArgGetAsBoolean() */
8081 : /************************************************************************/
8082 :
8083 : /** Return the argument value as a boolean.
8084 : *
8085 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8086 : *
8087 : * @param hArg Handle to an argument. Must NOT be null.
8088 : * @since 3.11
8089 : */
8090 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8091 : {
8092 8 : VALIDATE_POINTER1(hArg, __func__, false);
8093 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8094 : {
8095 1 : CPLError(CE_Failure, CPLE_AppDefined,
8096 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8097 : __func__);
8098 1 : return false;
8099 : }
8100 7 : return hArg->ptr->Get<bool>();
8101 : }
8102 :
8103 : /************************************************************************/
8104 : /* GDALAlgorithmArgGetAsString() */
8105 : /************************************************************************/
8106 :
8107 : /** Return the argument value as a string.
8108 : *
8109 : * Must only be called on arguments whose type is GAAT_STRING.
8110 : *
8111 : * @param hArg Handle to an argument. Must NOT be null.
8112 : * @return string whose lifetime is bound to hArg and which must not
8113 : * be freed.
8114 : * @since 3.11
8115 : */
8116 347 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8117 : {
8118 347 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8119 347 : if (hArg->ptr->GetType() != GAAT_STRING)
8120 : {
8121 1 : CPLError(CE_Failure, CPLE_AppDefined,
8122 : "%s must only be called on arguments of type GAAT_STRING",
8123 : __func__);
8124 1 : return nullptr;
8125 : }
8126 346 : return hArg->ptr->Get<std::string>().c_str();
8127 : }
8128 :
8129 : /************************************************************************/
8130 : /* GDALAlgorithmArgGetAsDatasetValue() */
8131 : /************************************************************************/
8132 :
8133 : /** Return the argument value as a GDALArgDatasetValueH.
8134 : *
8135 : * Must only be called on arguments whose type is GAAT_DATASET
8136 : *
8137 : * @param hArg Handle to an argument. Must NOT be null.
8138 : * @return handle to a GDALArgDatasetValue that must be released with
8139 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8140 : * the one of hArg.
8141 : * @since 3.11
8142 : */
8143 2958 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8144 : {
8145 2958 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8146 2958 : if (hArg->ptr->GetType() != GAAT_DATASET)
8147 : {
8148 1 : CPLError(CE_Failure, CPLE_AppDefined,
8149 : "%s must only be called on arguments of type GAAT_DATASET",
8150 : __func__);
8151 1 : return nullptr;
8152 : }
8153 2957 : return std::make_unique<GDALArgDatasetValueHS>(
8154 5914 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8155 2957 : .release();
8156 : }
8157 :
8158 : /************************************************************************/
8159 : /* GDALAlgorithmArgGetAsInteger() */
8160 : /************************************************************************/
8161 :
8162 : /** Return the argument value as a integer.
8163 : *
8164 : * Must only be called on arguments whose type is GAAT_INTEGER
8165 : *
8166 : * @param hArg Handle to an argument. Must NOT be null.
8167 : * @since 3.11
8168 : */
8169 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8170 : {
8171 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8172 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8173 : {
8174 1 : CPLError(CE_Failure, CPLE_AppDefined,
8175 : "%s must only be called on arguments of type GAAT_INTEGER",
8176 : __func__);
8177 1 : return 0;
8178 : }
8179 25 : return hArg->ptr->Get<int>();
8180 : }
8181 :
8182 : /************************************************************************/
8183 : /* GDALAlgorithmArgGetAsDouble() */
8184 : /************************************************************************/
8185 :
8186 : /** Return the argument value as a double.
8187 : *
8188 : * Must only be called on arguments whose type is GAAT_REAL
8189 : *
8190 : * @param hArg Handle to an argument. Must NOT be null.
8191 : * @since 3.11
8192 : */
8193 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8194 : {
8195 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8196 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8197 : {
8198 1 : CPLError(CE_Failure, CPLE_AppDefined,
8199 : "%s must only be called on arguments of type GAAT_REAL",
8200 : __func__);
8201 1 : return 0;
8202 : }
8203 7 : return hArg->ptr->Get<double>();
8204 : }
8205 :
8206 : /************************************************************************/
8207 : /* GDALAlgorithmArgGetAsStringList() */
8208 : /************************************************************************/
8209 :
8210 : /** Return the argument value as a string list.
8211 : *
8212 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8213 : *
8214 : * @param hArg Handle to an argument. Must NOT be null.
8215 : * @return a NULL terminated list of names, which must be destroyed with
8216 : * CSLDestroy()
8217 :
8218 : * @since 3.11
8219 : */
8220 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8221 : {
8222 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8223 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8224 : {
8225 1 : CPLError(CE_Failure, CPLE_AppDefined,
8226 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8227 : __func__);
8228 1 : return nullptr;
8229 : }
8230 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8231 3 : .StealList();
8232 : }
8233 :
8234 : /************************************************************************/
8235 : /* GDALAlgorithmArgGetAsIntegerList() */
8236 : /************************************************************************/
8237 :
8238 : /** Return the argument value as a integer list.
8239 : *
8240 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8241 : *
8242 : * @param hArg Handle to an argument. Must NOT be null.
8243 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8244 : * @since 3.11
8245 : */
8246 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8247 : size_t *pnCount)
8248 : {
8249 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8250 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8251 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8252 : {
8253 1 : CPLError(
8254 : CE_Failure, CPLE_AppDefined,
8255 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8256 : __func__);
8257 1 : *pnCount = 0;
8258 1 : return nullptr;
8259 : }
8260 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8261 7 : *pnCount = val.size();
8262 7 : return val.data();
8263 : }
8264 :
8265 : /************************************************************************/
8266 : /* GDALAlgorithmArgGetAsDoubleList() */
8267 : /************************************************************************/
8268 :
8269 : /** Return the argument value as a real list.
8270 : *
8271 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8272 : *
8273 : * @param hArg Handle to an argument. Must NOT be null.
8274 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8275 : * @since 3.11
8276 : */
8277 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8278 : size_t *pnCount)
8279 : {
8280 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8281 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8282 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8283 : {
8284 1 : CPLError(CE_Failure, CPLE_AppDefined,
8285 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8286 : __func__);
8287 1 : *pnCount = 0;
8288 1 : return nullptr;
8289 : }
8290 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8291 7 : *pnCount = val.size();
8292 7 : return val.data();
8293 : }
8294 :
8295 : /************************************************************************/
8296 : /* GDALAlgorithmArgSetAsBoolean() */
8297 : /************************************************************************/
8298 :
8299 : /** Set the value for a GAAT_BOOLEAN argument.
8300 : *
8301 : * It cannot be called several times for a given argument.
8302 : * Validation checks and other actions are run.
8303 : *
8304 : * @param hArg Handle to an argument. Must NOT be null.
8305 : * @param value value.
8306 : * @return true if success.
8307 : * @since 3.11
8308 : */
8309 :
8310 650 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8311 : {
8312 650 : VALIDATE_POINTER1(hArg, __func__, false);
8313 650 : return hArg->ptr->Set(value);
8314 : }
8315 :
8316 : /************************************************************************/
8317 : /* GDALAlgorithmArgSetAsString() */
8318 : /************************************************************************/
8319 :
8320 : /** Set the value for a GAAT_STRING argument.
8321 : *
8322 : * It cannot be called several times for a given argument.
8323 : * Validation checks and other actions are run.
8324 : *
8325 : * @param hArg Handle to an argument. Must NOT be null.
8326 : * @param value value (may be null)
8327 : * @return true if success.
8328 : * @since 3.11
8329 : */
8330 :
8331 2864 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
8332 : {
8333 2864 : VALIDATE_POINTER1(hArg, __func__, false);
8334 2864 : return hArg->ptr->Set(value ? value : "");
8335 : }
8336 :
8337 : /************************************************************************/
8338 : /* GDALAlgorithmArgSetAsInteger() */
8339 : /************************************************************************/
8340 :
8341 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
8342 : *
8343 : * It cannot be called several times for a given argument.
8344 : * Validation checks and other actions are run.
8345 : *
8346 : * @param hArg Handle to an argument. Must NOT be null.
8347 : * @param value value.
8348 : * @return true if success.
8349 : * @since 3.11
8350 : */
8351 :
8352 471 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8353 : {
8354 471 : VALIDATE_POINTER1(hArg, __func__, false);
8355 471 : return hArg->ptr->Set(value);
8356 : }
8357 :
8358 : /************************************************************************/
8359 : /* GDALAlgorithmArgSetAsDouble() */
8360 : /************************************************************************/
8361 :
8362 : /** Set the value for a GAAT_REAL argument.
8363 : *
8364 : * It cannot be called several times for a given argument.
8365 : * Validation checks and other actions are run.
8366 : *
8367 : * @param hArg Handle to an argument. Must NOT be null.
8368 : * @param value value.
8369 : * @return true if success.
8370 : * @since 3.11
8371 : */
8372 :
8373 233 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
8374 : {
8375 233 : VALIDATE_POINTER1(hArg, __func__, false);
8376 233 : return hArg->ptr->Set(value);
8377 : }
8378 :
8379 : /************************************************************************/
8380 : /* GDALAlgorithmArgSetAsDatasetValue() */
8381 : /************************************************************************/
8382 :
8383 : /** Set the value for a GAAT_DATASET argument.
8384 : *
8385 : * It cannot be called several times for a given argument.
8386 : * Validation checks and other actions are run.
8387 : *
8388 : * @param hArg Handle to an argument. Must NOT be null.
8389 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
8390 : * @return true if success.
8391 : * @since 3.11
8392 : */
8393 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
8394 : GDALArgDatasetValueH value)
8395 : {
8396 2 : VALIDATE_POINTER1(hArg, __func__, false);
8397 2 : VALIDATE_POINTER1(value, __func__, false);
8398 2 : return hArg->ptr->SetFrom(*(value->ptr));
8399 : }
8400 :
8401 : /************************************************************************/
8402 : /* GDALAlgorithmArgSetDataset() */
8403 : /************************************************************************/
8404 :
8405 : /** Set dataset object, increasing its reference counter.
8406 : *
8407 : * @param hArg Handle to an argument. Must NOT be null.
8408 : * @param hDS Dataset object. May be null.
8409 : * @return true if success.
8410 : * @since 3.11
8411 : */
8412 :
8413 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
8414 : {
8415 2 : VALIDATE_POINTER1(hArg, __func__, false);
8416 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
8417 : }
8418 :
8419 : /************************************************************************/
8420 : /* GDALAlgorithmArgSetAsStringList() */
8421 : /************************************************************************/
8422 :
8423 : /** Set the value for a GAAT_STRING_LIST argument.
8424 : *
8425 : * It cannot be called several times for a given argument.
8426 : * Validation checks and other actions are run.
8427 : *
8428 : * @param hArg Handle to an argument. Must NOT be null.
8429 : * @param value value as a NULL terminated list (may be null)
8430 : * @return true if success.
8431 : * @since 3.11
8432 : */
8433 :
8434 660 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
8435 : {
8436 660 : VALIDATE_POINTER1(hArg, __func__, false);
8437 660 : return hArg->ptr->Set(
8438 1320 : static_cast<std::vector<std::string>>(CPLStringList(value)));
8439 : }
8440 :
8441 : /************************************************************************/
8442 : /* GDALAlgorithmArgSetAsIntegerList() */
8443 : /************************************************************************/
8444 :
8445 : /** Set the value for a GAAT_INTEGER_LIST argument.
8446 : *
8447 : * It cannot be called several times for a given argument.
8448 : * Validation checks and other actions are run.
8449 : *
8450 : * @param hArg Handle to an argument. Must NOT be null.
8451 : * @param nCount Number of values in pnValues.
8452 : * @param pnValues Pointer to an array of integer values of size nCount.
8453 : * @return true if success.
8454 : * @since 3.11
8455 : */
8456 100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
8457 : const int *pnValues)
8458 : {
8459 100 : VALIDATE_POINTER1(hArg, __func__, false);
8460 100 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
8461 : }
8462 :
8463 : /************************************************************************/
8464 : /* GDALAlgorithmArgSetAsDoubleList() */
8465 : /************************************************************************/
8466 :
8467 : /** Set the value for a GAAT_REAL_LIST argument.
8468 : *
8469 : * It cannot be called several times for a given argument.
8470 : * Validation checks and other actions are run.
8471 : *
8472 : * @param hArg Handle to an argument. Must NOT be null.
8473 : * @param nCount Number of values in pnValues.
8474 : * @param pnValues Pointer to an array of double values of size nCount.
8475 : * @return true if success.
8476 : * @since 3.11
8477 : */
8478 152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
8479 : const double *pnValues)
8480 : {
8481 152 : VALIDATE_POINTER1(hArg, __func__, false);
8482 152 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
8483 : }
8484 :
8485 : /************************************************************************/
8486 : /* GDALAlgorithmArgSetDatasets() */
8487 : /************************************************************************/
8488 :
8489 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
8490 : *
8491 : * @param hArg Handle to an argument. Must NOT be null.
8492 : * @param nCount Number of values in pnValues.
8493 : * @param pahDS Pointer to an array of dataset of size nCount.
8494 : * @return true if success.
8495 : * @since 3.11
8496 : */
8497 :
8498 1231 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
8499 : GDALDatasetH *pahDS)
8500 : {
8501 1231 : VALIDATE_POINTER1(hArg, __func__, false);
8502 2462 : std::vector<GDALArgDatasetValue> values;
8503 2488 : for (size_t i = 0; i < nCount; ++i)
8504 : {
8505 1257 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
8506 : }
8507 1231 : return hArg->ptr->Set(std::move(values));
8508 : }
8509 :
8510 : /************************************************************************/
8511 : /* GDALAlgorithmArgSetDatasetNames() */
8512 : /************************************************************************/
8513 :
8514 : /** Set dataset names to a GAAT_DATASET_LIST argument.
8515 : *
8516 : * @param hArg Handle to an argument. Must NOT be null.
8517 : * @param names Dataset names as a NULL terminated list (may be null)
8518 : * @return true if success.
8519 : * @since 3.11
8520 : */
8521 :
8522 695 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
8523 : {
8524 695 : VALIDATE_POINTER1(hArg, __func__, false);
8525 1390 : std::vector<GDALArgDatasetValue> values;
8526 1455 : for (size_t i = 0; names[i]; ++i)
8527 : {
8528 760 : values.emplace_back(names[i]);
8529 : }
8530 695 : return hArg->ptr->Set(std::move(values));
8531 : }
8532 :
8533 : /************************************************************************/
8534 : /* GDALArgDatasetValueCreate() */
8535 : /************************************************************************/
8536 :
8537 : /** Instantiate an empty GDALArgDatasetValue
8538 : *
8539 : * @return new handle to free with GDALArgDatasetValueRelease()
8540 : * @since 3.11
8541 : */
8542 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
8543 : {
8544 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
8545 : }
8546 :
8547 : /************************************************************************/
8548 : /* GDALArgDatasetValueRelease() */
8549 : /************************************************************************/
8550 :
8551 : /** Release a handle to a GDALArgDatasetValue
8552 : *
8553 : * @since 3.11
8554 : */
8555 2958 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
8556 : {
8557 2958 : delete hValue;
8558 2958 : }
8559 :
8560 : /************************************************************************/
8561 : /* GDALArgDatasetValueGetName() */
8562 : /************************************************************************/
8563 :
8564 : /** Return the name component of the GDALArgDatasetValue
8565 : *
8566 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8567 : * @return string whose lifetime is bound to hAlg and which must not
8568 : * be freed.
8569 : * @since 3.11
8570 : */
8571 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
8572 : {
8573 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8574 1 : return hValue->ptr->GetName().c_str();
8575 : }
8576 :
8577 : /************************************************************************/
8578 : /* GDALArgDatasetValueGetDatasetRef() */
8579 : /************************************************************************/
8580 :
8581 : /** Return the dataset component of the GDALArgDatasetValue.
8582 : *
8583 : * This does not modify the reference counter, hence the lifetime of the
8584 : * returned object is not guaranteed to exceed the one of hValue.
8585 : *
8586 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8587 : * @since 3.11
8588 : */
8589 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
8590 : {
8591 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8592 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
8593 : }
8594 :
8595 : /************************************************************************/
8596 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
8597 : /************************************************************************/
8598 :
8599 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
8600 : * reference count if not null. Once done with the dataset, the caller should
8601 : * call GDALReleaseDataset().
8602 : *
8603 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8604 : * @since 3.11
8605 : */
8606 : GDALDatasetH
8607 997 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
8608 : {
8609 997 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8610 997 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
8611 : }
8612 :
8613 : /************************************************************************/
8614 : /* GDALArgDatasetValueSetName() */
8615 : /************************************************************************/
8616 :
8617 : /** Set dataset name
8618 : *
8619 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8620 : * @param pszName Dataset name. May be null.
8621 : * @since 3.11
8622 : */
8623 :
8624 1225 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
8625 : const char *pszName)
8626 : {
8627 1225 : VALIDATE_POINTER0(hValue, __func__);
8628 1225 : hValue->ptr->Set(pszName ? pszName : "");
8629 : }
8630 :
8631 : /************************************************************************/
8632 : /* GDALArgDatasetValueSetDataset() */
8633 : /************************************************************************/
8634 :
8635 : /** Set dataset object, increasing its reference counter.
8636 : *
8637 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8638 : * @param hDS Dataset object. May be null.
8639 : * @since 3.11
8640 : */
8641 :
8642 726 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
8643 : GDALDatasetH hDS)
8644 : {
8645 726 : VALIDATE_POINTER0(hValue, __func__);
8646 726 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
8647 : }
|