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 <type_traits>
37 : #include <string_view>
38 : #include <regex>
39 :
40 : #ifndef _
41 : #define _(x) (x)
42 : #endif
43 :
44 : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
45 :
46 : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
47 :
48 : constexpr const char *GDAL_ARG_NAME_BAND = "band";
49 :
50 : //! @cond Doxygen_Suppress
51 : struct GDALAlgorithmArgHS
52 : {
53 : GDALAlgorithmArg *ptr = nullptr;
54 :
55 337167 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
56 : {
57 337167 : }
58 : };
59 :
60 : //! @endcond
61 :
62 : //! @cond Doxygen_Suppress
63 : struct GDALArgDatasetValueHS
64 : {
65 : GDALArgDatasetValue val{};
66 : GDALArgDatasetValue *ptr = nullptr;
67 :
68 1 : GDALArgDatasetValueHS() : ptr(&val)
69 : {
70 1 : }
71 :
72 3061 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
73 : {
74 3061 : }
75 :
76 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
77 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
78 : };
79 :
80 : //! @endcond
81 :
82 : /************************************************************************/
83 : /* GDALAlgorithmArgTypeIsList() */
84 : /************************************************************************/
85 :
86 399510 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
87 : {
88 399510 : switch (type)
89 : {
90 263183 : case GAAT_BOOLEAN:
91 : case GAAT_STRING:
92 : case GAAT_INTEGER:
93 : case GAAT_REAL:
94 : case GAAT_DATASET:
95 263183 : break;
96 :
97 136327 : case GAAT_STRING_LIST:
98 : case GAAT_INTEGER_LIST:
99 : case GAAT_REAL_LIST:
100 : case GAAT_DATASET_LIST:
101 136327 : return true;
102 : }
103 :
104 263183 : return false;
105 : }
106 :
107 : /************************************************************************/
108 : /* GDALAlgorithmArgTypeName() */
109 : /************************************************************************/
110 :
111 5378 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
112 : {
113 5378 : switch (type)
114 : {
115 1336 : case GAAT_BOOLEAN:
116 1336 : break;
117 1442 : case GAAT_STRING:
118 1442 : return "string";
119 370 : case GAAT_INTEGER:
120 370 : return "integer";
121 472 : case GAAT_REAL:
122 472 : return "real";
123 249 : case GAAT_DATASET:
124 249 : return "dataset";
125 995 : case GAAT_STRING_LIST:
126 995 : return "string_list";
127 92 : case GAAT_INTEGER_LIST:
128 92 : return "integer_list";
129 219 : case GAAT_REAL_LIST:
130 219 : return "real_list";
131 203 : case GAAT_DATASET_LIST:
132 203 : return "dataset_list";
133 : }
134 :
135 1336 : return "boolean";
136 : }
137 :
138 : /************************************************************************/
139 : /* GDALAlgorithmArgDatasetTypeName() */
140 : /************************************************************************/
141 :
142 21728 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
143 : {
144 21728 : std::string ret;
145 21728 : if ((type & GDAL_OF_RASTER) != 0)
146 11934 : ret = "raster";
147 21728 : if ((type & GDAL_OF_VECTOR) != 0)
148 : {
149 10588 : if (!ret.empty())
150 : {
151 1187 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
152 257 : ret += ", ";
153 : else
154 930 : ret += " or ";
155 : }
156 10588 : ret += "vector";
157 : }
158 21728 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
159 : {
160 576 : if (!ret.empty())
161 : {
162 316 : ret += " or ";
163 : }
164 576 : ret += "multidimensional raster";
165 : }
166 21728 : return ret;
167 : }
168 :
169 : /************************************************************************/
170 : /* GDALAlgorithmArgDecl() */
171 : /************************************************************************/
172 :
173 : // cppcheck-suppress uninitMemberVar
174 321458 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
175 : char chShortName,
176 : const std::string &description,
177 321458 : GDALAlgorithmArgType type)
178 : : m_longName(longName),
179 321458 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
180 : m_description(description), m_type(type),
181 642916 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
182 321458 : .toupper()),
183 964374 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
184 : {
185 321458 : if (m_type == GAAT_BOOLEAN)
186 : {
187 134118 : m_defaultValue = false;
188 : }
189 321458 : }
190 :
191 : /************************************************************************/
192 : /* GDALAlgorithmArgDecl::SetMinCount() */
193 : /************************************************************************/
194 :
195 17832 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
196 : {
197 17832 : if (!GDALAlgorithmArgTypeIsList(m_type))
198 : {
199 1 : CPLError(CE_Failure, CPLE_NotSupported,
200 : "SetMinCount() illegal on scalar argument '%s'",
201 1 : GetName().c_str());
202 : }
203 : else
204 : {
205 17831 : m_minCount = count;
206 : }
207 17832 : return *this;
208 : }
209 :
210 : /************************************************************************/
211 : /* GDALAlgorithmArgDecl::SetMaxCount() */
212 : /************************************************************************/
213 :
214 16871 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
215 : {
216 16871 : if (!GDALAlgorithmArgTypeIsList(m_type))
217 : {
218 1 : CPLError(CE_Failure, CPLE_NotSupported,
219 : "SetMaxCount() illegal on scalar argument '%s'",
220 1 : GetName().c_str());
221 : }
222 : else
223 : {
224 16870 : m_maxCount = count;
225 : }
226 16871 : return *this;
227 : }
228 :
229 : /************************************************************************/
230 : /* GDALAlgorithmArg::~GDALAlgorithmArg() */
231 : /************************************************************************/
232 :
233 : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
234 :
235 : /************************************************************************/
236 : /* GDALAlgorithmArg::Set() */
237 : /************************************************************************/
238 :
239 1168 : bool GDALAlgorithmArg::Set(bool value)
240 : {
241 1168 : if (m_decl.GetType() != GAAT_BOOLEAN)
242 : {
243 14 : CPLError(
244 : CE_Failure, CPLE_AppDefined,
245 : "Calling Set(bool) on argument '%s' of type %s is not supported",
246 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
247 7 : return false;
248 : }
249 1161 : return SetInternal(value);
250 : }
251 :
252 3849 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
253 : {
254 3885 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
255 36 : value.front() == '@')
256 : {
257 2 : GByte *pabyData = nullptr;
258 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
259 2 : 10 * 1024 * 1024))
260 : {
261 : // Remove UTF-8 BOM
262 1 : size_t offset = 0;
263 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
264 1 : pabyData[2] == 0xBF)
265 : {
266 1 : offset = 3;
267 : }
268 1 : value = reinterpret_cast<const char *>(pabyData + offset);
269 1 : VSIFree(pabyData);
270 : }
271 : else
272 : {
273 1 : return false;
274 : }
275 : }
276 :
277 3848 : if (m_decl.IsRemoveSQLCommentsEnabled())
278 35 : value = CPLRemoveSQLComments(value);
279 :
280 3848 : return true;
281 : }
282 :
283 3870 : bool GDALAlgorithmArg::Set(const std::string &value)
284 : {
285 3870 : switch (m_decl.GetType())
286 : {
287 9 : case GAAT_BOOLEAN:
288 17 : if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
289 17 : EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
290 : {
291 4 : return Set(true);
292 : }
293 5 : else if (EQUAL(value.c_str(), "0") ||
294 4 : EQUAL(value.c_str(), "FALSE") ||
295 9 : EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
296 : {
297 4 : return Set(false);
298 : }
299 1 : break;
300 :
301 8 : case GAAT_INTEGER:
302 : case GAAT_INTEGER_LIST:
303 : {
304 8 : errno = 0;
305 8 : char *endptr = nullptr;
306 8 : const auto v = std::strtoll(value.c_str(), &endptr, 10);
307 13 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
308 5 : endptr == value.c_str() + value.size())
309 : {
310 3 : if (m_decl.GetType() == GAAT_INTEGER)
311 3 : return Set(static_cast<int>(v));
312 : else
313 1 : return Set(std::vector<int>{static_cast<int>(v)});
314 : }
315 5 : break;
316 : }
317 :
318 5 : case GAAT_REAL:
319 : case GAAT_REAL_LIST:
320 : {
321 5 : char *endptr = nullptr;
322 5 : const double v = CPLStrtod(value.c_str(), &endptr);
323 5 : if (endptr == value.c_str() + value.size())
324 : {
325 3 : if (m_decl.GetType() == GAAT_REAL)
326 3 : return Set(v);
327 : else
328 1 : return Set(std::vector<double>{v});
329 : }
330 2 : break;
331 : }
332 :
333 3832 : case GAAT_STRING:
334 3832 : break;
335 :
336 1 : case GAAT_STRING_LIST:
337 2 : return Set(std::vector<std::string>{value});
338 :
339 15 : case GAAT_DATASET:
340 15 : return SetDatasetName(value);
341 :
342 0 : case GAAT_DATASET_LIST:
343 : {
344 0 : std::vector<GDALArgDatasetValue> v;
345 0 : v.resize(1);
346 0 : v[0].Set(value);
347 0 : return Set(std::move(v));
348 : }
349 : }
350 :
351 3840 : if (m_decl.GetType() != GAAT_STRING)
352 : {
353 16 : CPLError(CE_Failure, CPLE_AppDefined,
354 : "Calling Set(std::string) on argument '%s' of type %s is not "
355 : "supported",
356 8 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
357 8 : return false;
358 : }
359 :
360 3832 : std::string newValue(value);
361 3832 : return ProcessString(newValue) && SetInternal(newValue);
362 : }
363 :
364 841 : bool GDALAlgorithmArg::Set(int value)
365 : {
366 841 : if (m_decl.GetType() == GAAT_BOOLEAN)
367 : {
368 3 : if (value == 1)
369 1 : return Set(true);
370 2 : else if (value == 0)
371 1 : return Set(false);
372 : }
373 838 : else if (m_decl.GetType() == GAAT_REAL)
374 : {
375 3 : return Set(static_cast<double>(value));
376 : }
377 835 : else if (m_decl.GetType() == GAAT_STRING)
378 : {
379 2 : return Set(std::to_string(value));
380 : }
381 833 : else if (m_decl.GetType() == GAAT_INTEGER_LIST)
382 : {
383 1 : return Set(std::vector<int>{value});
384 : }
385 832 : else if (m_decl.GetType() == GAAT_REAL_LIST)
386 : {
387 1 : return Set(std::vector<double>{static_cast<double>(value)});
388 : }
389 831 : else if (m_decl.GetType() == GAAT_STRING_LIST)
390 : {
391 2 : return Set(std::vector<std::string>{std::to_string(value)});
392 : }
393 :
394 831 : if (m_decl.GetType() != GAAT_INTEGER)
395 : {
396 2 : CPLError(
397 : CE_Failure, CPLE_AppDefined,
398 : "Calling Set(int) on argument '%s' of type %s is not supported",
399 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
400 1 : return false;
401 : }
402 830 : return SetInternal(value);
403 : }
404 :
405 287 : bool GDALAlgorithmArg::Set(double value)
406 : {
407 290 : if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
408 290 : value <= INT_MAX && static_cast<int>(value) == value)
409 : {
410 2 : return Set(static_cast<int>(value));
411 : }
412 285 : else if (m_decl.GetType() == GAAT_STRING)
413 : {
414 2 : return Set(std::to_string(value));
415 : }
416 285 : else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
417 285 : value <= INT_MAX && static_cast<int>(value) == value)
418 : {
419 1 : return Set(std::vector<int>{static_cast<int>(value)});
420 : }
421 282 : else if (m_decl.GetType() == GAAT_REAL_LIST)
422 : {
423 0 : return Set(std::vector<double>{value});
424 : }
425 282 : else if (m_decl.GetType() == GAAT_STRING_LIST)
426 : {
427 2 : return Set(std::vector<std::string>{std::to_string(value)});
428 : }
429 281 : else if (m_decl.GetType() != GAAT_REAL)
430 : {
431 6 : CPLError(
432 : CE_Failure, CPLE_AppDefined,
433 : "Calling Set(double) on argument '%s' of type %s is not supported",
434 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
435 3 : return false;
436 : }
437 278 : return SetInternal(value);
438 : }
439 :
440 5813 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
441 : {
442 5816 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
443 3 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
444 : {
445 3 : CPLError(
446 : CE_Failure, CPLE_AppDefined,
447 : "Dataset object '%s' is created by algorithm and cannot be set "
448 : "as an input.",
449 3 : arg->GetName().c_str());
450 3 : return false;
451 : }
452 5810 : else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
453 : {
454 2 : CPLError(CE_Failure, CPLE_AppDefined,
455 : "A dataset cannot be set as an input argument of '%s'.",
456 2 : arg->GetName().c_str());
457 2 : return false;
458 : }
459 :
460 5808 : return true;
461 : }
462 :
463 9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
464 : {
465 9 : if (m_decl.GetType() != GAAT_DATASET)
466 : {
467 2 : CPLError(CE_Failure, CPLE_AppDefined,
468 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
469 : "is not supported",
470 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
471 1 : return false;
472 : }
473 8 : if (!CheckCanSetDatasetObject(this))
474 2 : return false;
475 6 : m_explicitlySet = true;
476 6 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
477 6 : val.Set(ds);
478 6 : return RunAllActions();
479 : }
480 :
481 3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
482 : {
483 3 : if (m_decl.GetType() != GAAT_DATASET)
484 : {
485 2 : CPLError(CE_Failure, CPLE_AppDefined,
486 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
487 : "is not supported",
488 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
489 1 : return false;
490 : }
491 2 : if (!CheckCanSetDatasetObject(this))
492 1 : return false;
493 1 : m_explicitlySet = true;
494 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
495 1 : val.Set(std::move(ds));
496 1 : return RunAllActions();
497 : }
498 :
499 539 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
500 : {
501 539 : if (m_decl.GetType() != GAAT_DATASET)
502 : {
503 2 : CPLError(CE_Failure, CPLE_AppDefined,
504 : "Calling SetDatasetName() on argument '%s' of type %s is "
505 : "not supported",
506 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
507 1 : return false;
508 : }
509 538 : m_explicitlySet = true;
510 538 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
511 538 : return RunAllActions();
512 : }
513 :
514 940 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
515 : {
516 940 : if (m_decl.GetType() != GAAT_DATASET)
517 : {
518 2 : CPLError(CE_Failure, CPLE_AppDefined,
519 : "Calling SetFrom() on argument '%s' of type %s is "
520 : "not supported",
521 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
522 1 : return false;
523 : }
524 939 : if (!CheckCanSetDatasetObject(this))
525 1 : return false;
526 938 : m_explicitlySet = true;
527 938 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
528 938 : return RunAllActions();
529 : }
530 :
531 970 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
532 : {
533 970 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
534 : {
535 3 : std::vector<int> v_i;
536 4 : for (const std::string &s : value)
537 : {
538 3 : errno = 0;
539 3 : char *endptr = nullptr;
540 3 : const auto v = std::strtoll(s.c_str(), &endptr, 10);
541 5 : if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
542 2 : endptr == s.c_str() + s.size())
543 : {
544 1 : v_i.push_back(static_cast<int>(v));
545 : }
546 : else
547 : {
548 2 : break;
549 : }
550 : }
551 3 : if (v_i.size() == value.size())
552 1 : return Set(v_i);
553 : }
554 967 : else if (m_decl.GetType() == GAAT_REAL_LIST)
555 : {
556 2 : std::vector<double> v_d;
557 3 : for (const std::string &s : value)
558 : {
559 2 : char *endptr = nullptr;
560 2 : const double v = CPLStrtod(s.c_str(), &endptr);
561 2 : if (endptr == s.c_str() + s.size())
562 : {
563 1 : v_d.push_back(v);
564 : }
565 : else
566 : {
567 1 : break;
568 : }
569 : }
570 2 : if (v_d.size() == value.size())
571 1 : return Set(v_d);
572 : }
573 1928 : else if ((m_decl.GetType() == GAAT_INTEGER ||
574 1925 : m_decl.GetType() == GAAT_REAL ||
575 2892 : m_decl.GetType() == GAAT_STRING) &&
576 5 : value.size() == 1)
577 : {
578 4 : return Set(value[0]);
579 : }
580 961 : else if (m_decl.GetType() == GAAT_DATASET_LIST)
581 : {
582 30 : std::vector<GDALArgDatasetValue> dsVector;
583 46 : for (const std::string &s : value)
584 31 : dsVector.emplace_back(s);
585 15 : return Set(std::move(dsVector));
586 : }
587 :
588 949 : if (m_decl.GetType() != GAAT_STRING_LIST)
589 : {
590 10 : CPLError(CE_Failure, CPLE_AppDefined,
591 : "Calling Set(const std::vector<std::string> &) on argument "
592 : "'%s' of type %s is not supported",
593 5 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
594 5 : return false;
595 : }
596 :
597 1874 : if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
598 930 : m_decl.IsRemoveSQLCommentsEnabled())
599 : {
600 28 : std::vector<std::string> newValue(value);
601 31 : for (auto &s : newValue)
602 : {
603 17 : if (!ProcessString(s))
604 0 : return false;
605 : }
606 14 : return SetInternal(newValue);
607 : }
608 : else
609 : {
610 930 : return SetInternal(value);
611 : }
612 : }
613 :
614 169 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
615 : {
616 169 : if (m_decl.GetType() == GAAT_REAL_LIST)
617 : {
618 2 : std::vector<double> v_d;
619 2 : for (int i : value)
620 1 : v_d.push_back(i);
621 1 : return Set(v_d);
622 : }
623 168 : else if (m_decl.GetType() == GAAT_STRING_LIST)
624 : {
625 2 : std::vector<std::string> v_s;
626 3 : for (int i : value)
627 2 : v_s.push_back(std::to_string(i));
628 1 : return Set(v_s);
629 : }
630 332 : else if ((m_decl.GetType() == GAAT_INTEGER ||
631 328 : m_decl.GetType() == GAAT_REAL ||
632 497 : m_decl.GetType() == GAAT_STRING) &&
633 5 : value.size() == 1)
634 : {
635 3 : return Set(value[0]);
636 : }
637 :
638 164 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
639 : {
640 6 : CPLError(CE_Failure, CPLE_AppDefined,
641 : "Calling Set(const std::vector<int> &) on argument '%s' of "
642 : "type %s is not supported",
643 3 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
644 3 : return false;
645 : }
646 161 : return SetInternal(value);
647 : }
648 :
649 263 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
650 : {
651 263 : if (m_decl.GetType() == GAAT_INTEGER_LIST)
652 : {
653 2 : std::vector<int> v_i;
654 3 : for (double d : value)
655 : {
656 2 : if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
657 : {
658 1 : v_i.push_back(static_cast<int>(d));
659 : }
660 : else
661 : {
662 : break;
663 : }
664 : }
665 2 : if (v_i.size() == value.size())
666 1 : return Set(v_i);
667 : }
668 261 : else if (m_decl.GetType() == GAAT_STRING_LIST)
669 : {
670 2 : std::vector<std::string> v_s;
671 3 : for (double d : value)
672 2 : v_s.push_back(std::to_string(d));
673 1 : return Set(v_s);
674 : }
675 519 : else if ((m_decl.GetType() == GAAT_INTEGER ||
676 517 : m_decl.GetType() == GAAT_REAL ||
677 778 : m_decl.GetType() == GAAT_STRING) &&
678 3 : value.size() == 1)
679 : {
680 3 : return Set(value[0]);
681 : }
682 :
683 258 : if (m_decl.GetType() != GAAT_REAL_LIST)
684 : {
685 4 : CPLError(CE_Failure, CPLE_AppDefined,
686 : "Calling Set(const std::vector<double> &) on argument '%s' of "
687 : "type %s is not supported",
688 2 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
689 2 : return false;
690 : }
691 256 : return SetInternal(value);
692 : }
693 :
694 3277 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
695 : {
696 3277 : if (m_decl.GetType() != GAAT_DATASET_LIST)
697 : {
698 2 : CPLError(CE_Failure, CPLE_AppDefined,
699 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
700 : "argument '%s' of type %s is not supported",
701 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
702 1 : return false;
703 : }
704 3276 : m_explicitlySet = true;
705 3276 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
706 3276 : return RunAllActions();
707 : }
708 :
709 : GDALAlgorithmArg &
710 0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
711 : {
712 0 : Set(std::move(value));
713 0 : return *this;
714 : }
715 :
716 1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
717 : {
718 1 : const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
719 1 : return Set(value.exportToWkt(apszOptions));
720 : }
721 :
722 4067 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
723 : {
724 4067 : if (m_decl.GetType() != other.GetType())
725 : {
726 2 : CPLError(CE_Failure, CPLE_AppDefined,
727 : "Calling SetFrom() on argument '%s' of type %s whereas "
728 : "other argument type is %s is not supported",
729 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
730 : GDALAlgorithmArgTypeName(other.GetType()));
731 1 : return false;
732 : }
733 :
734 4066 : switch (m_decl.GetType())
735 : {
736 90 : case GAAT_BOOLEAN:
737 90 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
738 90 : break;
739 783 : case GAAT_STRING:
740 1566 : *std::get<std::string *>(m_value) =
741 783 : *std::get<std::string *>(other.m_value);
742 783 : break;
743 6 : case GAAT_INTEGER:
744 6 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
745 6 : break;
746 1 : case GAAT_REAL:
747 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
748 1 : break;
749 934 : case GAAT_DATASET:
750 934 : return SetFrom(other.Get<GDALArgDatasetValue>());
751 39 : case GAAT_STRING_LIST:
752 78 : *std::get<std::vector<std::string> *>(m_value) =
753 39 : *std::get<std::vector<std::string> *>(other.m_value);
754 39 : break;
755 1 : case GAAT_INTEGER_LIST:
756 2 : *std::get<std::vector<int> *>(m_value) =
757 1 : *std::get<std::vector<int> *>(other.m_value);
758 1 : break;
759 1 : case GAAT_REAL_LIST:
760 2 : *std::get<std::vector<double> *>(m_value) =
761 1 : *std::get<std::vector<double> *>(other.m_value);
762 1 : break;
763 2211 : case GAAT_DATASET_LIST:
764 : {
765 2211 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
766 2216 : for (const auto &val :
767 6643 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
768 : {
769 4432 : GDALArgDatasetValue v;
770 2216 : v.SetFrom(val);
771 2216 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
772 2216 : ->push_back(std::move(v));
773 : }
774 2211 : break;
775 : }
776 : }
777 3132 : m_explicitlySet = true;
778 3132 : return RunAllActions();
779 : }
780 :
781 : /************************************************************************/
782 : /* GDALAlgorithmArg::RunAllActions() */
783 : /************************************************************************/
784 :
785 15352 : bool GDALAlgorithmArg::RunAllActions()
786 : {
787 15352 : if (!RunValidationActions())
788 139 : return false;
789 15213 : RunActions();
790 15213 : return true;
791 : }
792 :
793 : /************************************************************************/
794 : /* GDALAlgorithmArg::RunActions() */
795 : /************************************************************************/
796 :
797 15214 : void GDALAlgorithmArg::RunActions()
798 : {
799 15509 : for (const auto &f : m_actions)
800 295 : f();
801 15214 : }
802 :
803 : /************************************************************************/
804 : /* GDALAlgorithmArg::ValidateChoice() */
805 : /************************************************************************/
806 :
807 : // Returns the canonical value if matching a valid choice, or empty string
808 : // otherwise.
809 2498 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
810 : {
811 14887 : for (const std::string &choice : GetChoices())
812 : {
813 14769 : if (EQUAL(value.c_str(), choice.c_str()))
814 : {
815 2380 : return choice;
816 : }
817 : }
818 :
819 190 : for (const std::string &choice : GetHiddenChoices())
820 : {
821 172 : if (EQUAL(value.c_str(), choice.c_str()))
822 : {
823 100 : return choice;
824 : }
825 : }
826 :
827 36 : std::string expected;
828 220 : for (const auto &choice : GetChoices())
829 : {
830 202 : if (!expected.empty())
831 184 : expected += ", ";
832 202 : expected += '\'';
833 202 : expected += choice;
834 202 : expected += '\'';
835 : }
836 18 : if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
837 : {
838 6 : return "?";
839 : }
840 24 : CPLError(CE_Failure, CPLE_IllegalArg,
841 : "Invalid value '%s' for string argument '%s'. Should be "
842 : "one among %s.",
843 12 : value.c_str(), GetName().c_str(), expected.c_str());
844 12 : return std::string();
845 : }
846 :
847 : /************************************************************************/
848 : /* GDALAlgorithmArg::ValidateIntRange() */
849 : /************************************************************************/
850 :
851 2657 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
852 : {
853 2657 : bool ret = true;
854 :
855 2657 : const auto [minVal, minValIsIncluded] = GetMinValue();
856 2657 : if (!std::isnan(minVal))
857 : {
858 2024 : if (minValIsIncluded && val < minVal)
859 : {
860 3 : CPLError(CE_Failure, CPLE_IllegalArg,
861 : "Value of argument '%s' is %d, but should be >= %d",
862 3 : GetName().c_str(), val, static_cast<int>(minVal));
863 3 : ret = false;
864 : }
865 2021 : else if (!minValIsIncluded && val <= minVal)
866 : {
867 1 : CPLError(CE_Failure, CPLE_IllegalArg,
868 : "Value of argument '%s' is %d, but should be > %d",
869 1 : GetName().c_str(), val, static_cast<int>(minVal));
870 1 : ret = false;
871 : }
872 : }
873 :
874 2657 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
875 2657 : if (!std::isnan(maxVal))
876 : {
877 :
878 382 : if (maxValIsIncluded && val > maxVal)
879 : {
880 1 : CPLError(CE_Failure, CPLE_IllegalArg,
881 : "Value of argument '%s' is %d, but should be <= %d",
882 1 : GetName().c_str(), val, static_cast<int>(maxVal));
883 1 : ret = false;
884 : }
885 381 : else if (!maxValIsIncluded && val >= maxVal)
886 : {
887 1 : CPLError(CE_Failure, CPLE_IllegalArg,
888 : "Value of argument '%s' is %d, but should be < %d",
889 1 : GetName().c_str(), val, static_cast<int>(maxVal));
890 1 : ret = false;
891 : }
892 : }
893 :
894 2657 : return ret;
895 : }
896 :
897 : /************************************************************************/
898 : /* GDALAlgorithmArg::ValidateRealRange() */
899 : /************************************************************************/
900 :
901 2105 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
902 : {
903 2105 : bool ret = true;
904 :
905 2105 : const auto [minVal, minValIsIncluded] = GetMinValue();
906 2105 : if (!std::isnan(minVal))
907 : {
908 213 : if (minValIsIncluded && !(val >= minVal))
909 : {
910 11 : CPLError(CE_Failure, CPLE_IllegalArg,
911 : "Value of argument '%s' is %g, but should be >= %g",
912 11 : GetName().c_str(), val, minVal);
913 11 : ret = false;
914 : }
915 202 : else if (!minValIsIncluded && !(val > minVal))
916 : {
917 4 : CPLError(CE_Failure, CPLE_IllegalArg,
918 : "Value of argument '%s' is %g, but should be > %g",
919 4 : GetName().c_str(), val, minVal);
920 4 : ret = false;
921 : }
922 : }
923 :
924 2105 : const auto [maxVal, maxValIsIncluded] = GetMaxValue();
925 2105 : if (!std::isnan(maxVal))
926 : {
927 :
928 58 : if (maxValIsIncluded && !(val <= maxVal))
929 : {
930 2 : CPLError(CE_Failure, CPLE_IllegalArg,
931 : "Value of argument '%s' is %g, but should be <= %g",
932 2 : GetName().c_str(), val, maxVal);
933 2 : ret = false;
934 : }
935 56 : else if (!maxValIsIncluded && !(val < maxVal))
936 : {
937 1 : CPLError(CE_Failure, CPLE_IllegalArg,
938 : "Value of argument '%s' is %g, but should be < %g",
939 1 : GetName().c_str(), val, maxVal);
940 1 : ret = false;
941 : }
942 : }
943 :
944 2105 : return ret;
945 : }
946 :
947 : /************************************************************************/
948 : /* CheckDuplicateValues() */
949 : /************************************************************************/
950 :
951 : template <class T>
952 82 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
953 : const std::vector<T> &values)
954 : {
955 164 : auto tmpValues = values;
956 82 : bool bHasDupValues = false;
957 : if constexpr (std::is_floating_point_v<T>)
958 : {
959 : // Avoid undefined behavior with NaN values
960 4 : std::sort(tmpValues.begin(), tmpValues.end(),
961 21 : [](T a, T b)
962 : {
963 21 : if (std::isnan(a) && !std::isnan(b))
964 3 : return true;
965 18 : if (std::isnan(b))
966 10 : return false;
967 8 : return a < b;
968 : });
969 :
970 : bHasDupValues =
971 4 : std::adjacent_find(tmpValues.begin(), tmpValues.end(),
972 6 : [](T a, T b)
973 : {
974 6 : if (std::isnan(a) && std::isnan(b))
975 1 : return true;
976 5 : return a == b;
977 8 : }) != tmpValues.end();
978 : }
979 : else
980 : {
981 78 : std::sort(tmpValues.begin(), tmpValues.end());
982 78 : bHasDupValues = std::adjacent_find(tmpValues.begin(),
983 156 : tmpValues.end()) != tmpValues.end();
984 : }
985 82 : if (bHasDupValues)
986 : {
987 10 : CPLError(CE_Failure, CPLE_AppDefined,
988 : "'%s' must be a list of unique values.",
989 10 : arg->GetName().c_str());
990 10 : return false;
991 : }
992 72 : return true;
993 : }
994 :
995 : /************************************************************************/
996 : /* GDALAlgorithmArg::RunValidationActions() */
997 : /************************************************************************/
998 :
999 33972 : bool GDALAlgorithmArg::RunValidationActions()
1000 : {
1001 33972 : bool ret = true;
1002 :
1003 33972 : if (GetType() == GAAT_STRING && !GetChoices().empty())
1004 : {
1005 1621 : auto &val = Get<std::string>();
1006 3242 : std::string validVal = ValidateChoice(val);
1007 1621 : if (validVal.empty())
1008 7 : ret = false;
1009 : else
1010 1614 : val = std::move(validVal);
1011 : }
1012 32351 : else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
1013 : {
1014 633 : auto &values = Get<std::vector<std::string>>();
1015 1510 : for (std::string &val : values)
1016 : {
1017 1754 : std::string validVal = ValidateChoice(val);
1018 877 : if (validVal.empty())
1019 5 : ret = false;
1020 : else
1021 872 : val = std::move(validVal);
1022 : }
1023 : }
1024 :
1025 : const auto CheckMinCharCount =
1026 942 : [this, &ret](const std::string &val, int nMinCharCount)
1027 : {
1028 930 : if (val.size() < static_cast<size_t>(nMinCharCount))
1029 : {
1030 12 : CPLError(CE_Failure, CPLE_IllegalArg,
1031 : "Value of argument '%s' is '%s', but should have at least "
1032 : "%d character%s",
1033 6 : GetName().c_str(), val.c_str(), nMinCharCount,
1034 : nMinCharCount > 1 ? "s" : "");
1035 6 : ret = false;
1036 : }
1037 34902 : };
1038 :
1039 : const auto CheckMaxCharCount =
1040 12065 : [this, &ret](const std::string &val, int nMaxCharCount)
1041 : {
1042 12063 : if (val.size() > static_cast<size_t>(nMaxCharCount))
1043 : {
1044 2 : CPLError(
1045 : CE_Failure, CPLE_IllegalArg,
1046 : "Value of argument '%s' is '%s', but should have no more than "
1047 : "%d character%s",
1048 1 : GetName().c_str(), val.c_str(), nMaxCharCount,
1049 : nMaxCharCount > 1 ? "s" : "");
1050 1 : ret = false;
1051 : }
1052 46035 : };
1053 :
1054 33972 : switch (GetType())
1055 : {
1056 2644 : case GAAT_BOOLEAN:
1057 2644 : break;
1058 :
1059 9547 : case GAAT_STRING:
1060 : {
1061 9547 : const auto &val = Get<std::string>();
1062 9547 : const int nMinCharCount = GetMinCharCount();
1063 9547 : if (nMinCharCount > 0)
1064 : {
1065 848 : CheckMinCharCount(val, nMinCharCount);
1066 : }
1067 :
1068 9547 : const int nMaxCharCount = GetMaxCharCount();
1069 9547 : CheckMaxCharCount(val, nMaxCharCount);
1070 9547 : break;
1071 : }
1072 :
1073 2061 : case GAAT_STRING_LIST:
1074 : {
1075 2061 : const int nMinCharCount = GetMinCharCount();
1076 2061 : const int nMaxCharCount = GetMaxCharCount();
1077 2061 : const auto &values = Get<std::vector<std::string>>();
1078 4577 : for (const auto &val : values)
1079 : {
1080 2516 : if (nMinCharCount > 0)
1081 82 : CheckMinCharCount(val, nMinCharCount);
1082 2516 : CheckMaxCharCount(val, nMaxCharCount);
1083 : }
1084 :
1085 2125 : if (!GetDuplicateValuesAllowed() &&
1086 64 : !CheckDuplicateValues(this, values))
1087 2 : ret = false;
1088 2061 : break;
1089 : }
1090 :
1091 1969 : case GAAT_INTEGER:
1092 : {
1093 1969 : ret = ValidateIntRange(Get<int>()) && ret;
1094 1969 : break;
1095 : }
1096 :
1097 337 : case GAAT_INTEGER_LIST:
1098 : {
1099 337 : const auto &values = Get<std::vector<int>>();
1100 1025 : for (int v : values)
1101 688 : ret = ValidateIntRange(v) && ret;
1102 :
1103 340 : if (!GetDuplicateValuesAllowed() &&
1104 3 : !CheckDuplicateValues(this, values))
1105 1 : ret = false;
1106 337 : break;
1107 : }
1108 :
1109 542 : case GAAT_REAL:
1110 : {
1111 542 : ret = ValidateRealRange(Get<double>()) && ret;
1112 542 : break;
1113 : }
1114 :
1115 565 : case GAAT_REAL_LIST:
1116 : {
1117 565 : const auto &values = Get<std::vector<double>>();
1118 2128 : for (double v : values)
1119 1563 : ret = ValidateRealRange(v) && ret;
1120 :
1121 569 : if (!GetDuplicateValuesAllowed() &&
1122 4 : !CheckDuplicateValues(this, values))
1123 2 : ret = false;
1124 565 : break;
1125 : }
1126 :
1127 5553 : case GAAT_DATASET:
1128 5553 : break;
1129 :
1130 10754 : case GAAT_DATASET_LIST:
1131 : {
1132 10754 : if (!GetDuplicateValuesAllowed())
1133 : {
1134 11 : const auto &values = Get<std::vector<GDALArgDatasetValue>>();
1135 22 : std::vector<std::string> aosValues;
1136 34 : for (const auto &v : values)
1137 : {
1138 23 : const GDALDataset *poDS = v.GetDatasetRef();
1139 23 : if (poDS)
1140 : {
1141 16 : auto poDriver = poDS->GetDriver();
1142 : // The dataset name for a MEM driver is not relevant,
1143 : // so use the pointer address
1144 32 : if ((poDriver &&
1145 24 : EQUAL(poDriver->GetDescription(), "MEM")) ||
1146 8 : poDS->GetDescription()[0] == 0)
1147 : {
1148 8 : aosValues.push_back(CPLSPrintf("%p", poDS));
1149 : }
1150 : else
1151 : {
1152 8 : aosValues.push_back(poDS->GetDescription());
1153 : }
1154 : }
1155 : else
1156 : {
1157 7 : aosValues.push_back(v.GetName());
1158 : }
1159 : }
1160 11 : if (!CheckDuplicateValues(this, aosValues))
1161 5 : ret = false;
1162 : }
1163 10754 : break;
1164 : }
1165 : }
1166 :
1167 33972 : if (GDALAlgorithmArgTypeIsList(GetType()))
1168 : {
1169 13717 : int valueCount = 0;
1170 13717 : if (GetType() == GAAT_STRING_LIST)
1171 : {
1172 2061 : valueCount =
1173 2061 : static_cast<int>(Get<std::vector<std::string>>().size());
1174 : }
1175 11656 : else if (GetType() == GAAT_INTEGER_LIST)
1176 : {
1177 337 : valueCount = static_cast<int>(Get<std::vector<int>>().size());
1178 : }
1179 11319 : else if (GetType() == GAAT_REAL_LIST)
1180 : {
1181 565 : valueCount = static_cast<int>(Get<std::vector<double>>().size());
1182 : }
1183 10754 : else if (GetType() == GAAT_DATASET_LIST)
1184 : {
1185 10754 : valueCount = static_cast<int>(
1186 10754 : Get<std::vector<GDALArgDatasetValue>>().size());
1187 : }
1188 :
1189 13717 : if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
1190 : {
1191 14 : ReportError(CE_Failure, CPLE_AppDefined,
1192 : "%d value%s been specified for argument '%s', "
1193 : "whereas exactly %d %s expected.",
1194 : valueCount, valueCount > 1 ? "s have" : " has",
1195 7 : GetName().c_str(), GetMinCount(),
1196 7 : GetMinCount() > 1 ? "were" : "was");
1197 7 : ret = false;
1198 : }
1199 13710 : else if (valueCount < GetMinCount())
1200 : {
1201 6 : ReportError(CE_Failure, CPLE_AppDefined,
1202 : "Only %d value%s been specified for argument '%s', "
1203 : "whereas at least %d %s expected.",
1204 : valueCount, valueCount > 1 ? "s have" : " has",
1205 3 : GetName().c_str(), GetMinCount(),
1206 3 : GetMinCount() > 1 ? "were" : "was");
1207 3 : ret = false;
1208 : }
1209 13707 : else if (valueCount > GetMaxCount())
1210 : {
1211 2 : ReportError(CE_Failure, CPLE_AppDefined,
1212 : "%d value%s been specified for argument '%s', "
1213 : "whereas at most %d %s expected.",
1214 : valueCount, valueCount > 1 ? "s have" : " has",
1215 1 : GetName().c_str(), GetMaxCount(),
1216 1 : GetMaxCount() > 1 ? "were" : "was");
1217 1 : ret = false;
1218 : }
1219 : }
1220 :
1221 33972 : if (ret)
1222 : {
1223 40400 : for (const auto &f : m_validationActions)
1224 : {
1225 6492 : if (!f())
1226 84 : ret = false;
1227 : }
1228 : }
1229 :
1230 33972 : return ret;
1231 : }
1232 :
1233 : /************************************************************************/
1234 : /* GDALAlgorithmArg::ReportError() */
1235 : /************************************************************************/
1236 :
1237 11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1238 : const char *fmt, ...) const
1239 : {
1240 : va_list args;
1241 11 : va_start(args, fmt);
1242 11 : if (m_owner)
1243 : {
1244 11 : m_owner->ReportError(eErrClass, err_no, "%s",
1245 22 : CPLString().vPrintf(fmt, args).c_str());
1246 : }
1247 : else
1248 : {
1249 0 : CPLError(eErrClass, err_no, "%s",
1250 0 : CPLString().vPrintf(fmt, args).c_str());
1251 : }
1252 11 : va_end(args);
1253 11 : }
1254 :
1255 : /************************************************************************/
1256 : /* GDALAlgorithmArg::GetEscapedString() */
1257 : /************************************************************************/
1258 :
1259 : /* static */
1260 130 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
1261 : {
1262 142 : if (s.find_first_of("\" \\,") != std::string::npos &&
1263 6 : !(s.size() > 4 &&
1264 6 : s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
1265 2 : s[1] == ' ' && s[s.size() - 2] == ' ' &&
1266 2 : s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
1267 : {
1268 8 : return std::string("\"")
1269 : .append(
1270 8 : CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
1271 4 : .append("\"");
1272 : }
1273 : else
1274 : {
1275 126 : return s;
1276 : }
1277 : }
1278 :
1279 : /************************************************************************/
1280 : /* GDALAlgorithmArg::Serialize() */
1281 : /************************************************************************/
1282 :
1283 39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
1284 : bool absolutePath) const
1285 : {
1286 39 : serializedArg.clear();
1287 :
1288 39 : if (!IsExplicitlySet())
1289 : {
1290 0 : return false;
1291 : }
1292 :
1293 78 : std::string ret = "--";
1294 39 : ret += GetName();
1295 39 : if (GetType() == GAAT_BOOLEAN)
1296 : {
1297 0 : serializedArg = std::move(ret);
1298 0 : return true;
1299 : }
1300 :
1301 5 : const auto AddListValueSeparator = [this, &ret]()
1302 : {
1303 1 : if (GetPackedValuesAllowed())
1304 : {
1305 0 : ret += ',';
1306 : }
1307 : else
1308 : {
1309 1 : ret += " --";
1310 1 : ret += GetName();
1311 1 : ret += ' ';
1312 : }
1313 40 : };
1314 :
1315 0 : const auto MakeAbsolutePath = [](const std::string &filename)
1316 : {
1317 : VSIStatBufL sStat;
1318 0 : if (VSIStatL(filename.c_str(), &sStat) != 0 ||
1319 0 : !CPLIsFilenameRelative(filename.c_str()))
1320 0 : return filename;
1321 0 : char *pszCWD = CPLGetCurrentDir();
1322 0 : if (!pszCWD)
1323 0 : return filename;
1324 : const auto absPath =
1325 0 : CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
1326 0 : CPLFree(pszCWD);
1327 0 : return absPath;
1328 : };
1329 :
1330 39 : ret += ' ';
1331 39 : switch (GetType())
1332 : {
1333 0 : case GAAT_BOOLEAN:
1334 0 : break;
1335 8 : case GAAT_STRING:
1336 : {
1337 8 : const auto &val = Get<std::string>();
1338 8 : ret += GetEscapedString(val);
1339 8 : break;
1340 : }
1341 0 : case GAAT_INTEGER:
1342 : {
1343 0 : ret += CPLSPrintf("%d", Get<int>());
1344 0 : break;
1345 : }
1346 0 : case GAAT_REAL:
1347 : {
1348 0 : ret += CPLSPrintf("%.17g", Get<double>());
1349 0 : break;
1350 : }
1351 2 : case GAAT_DATASET:
1352 : {
1353 2 : const auto &val = Get<GDALArgDatasetValue>();
1354 2 : const auto &str = val.GetName();
1355 2 : if (str.empty())
1356 : {
1357 0 : return false;
1358 : }
1359 2 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
1360 2 : break;
1361 : }
1362 4 : case GAAT_STRING_LIST:
1363 : {
1364 4 : const auto &vals = Get<std::vector<std::string>>();
1365 8 : for (size_t i = 0; i < vals.size(); ++i)
1366 : {
1367 4 : if (i > 0)
1368 1 : AddListValueSeparator();
1369 4 : ret += GetEscapedString(vals[i]);
1370 : }
1371 4 : break;
1372 : }
1373 0 : case GAAT_INTEGER_LIST:
1374 : {
1375 0 : const auto &vals = Get<std::vector<int>>();
1376 0 : for (size_t i = 0; i < vals.size(); ++i)
1377 : {
1378 0 : if (i > 0)
1379 0 : AddListValueSeparator();
1380 0 : ret += CPLSPrintf("%d", vals[i]);
1381 : }
1382 0 : break;
1383 : }
1384 0 : case GAAT_REAL_LIST:
1385 : {
1386 0 : const auto &vals = Get<std::vector<double>>();
1387 0 : for (size_t i = 0; i < vals.size(); ++i)
1388 : {
1389 0 : if (i > 0)
1390 0 : AddListValueSeparator();
1391 0 : ret += CPLSPrintf("%.17g", vals[i]);
1392 : }
1393 0 : break;
1394 : }
1395 25 : case GAAT_DATASET_LIST:
1396 : {
1397 25 : const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
1398 49 : for (size_t i = 0; i < vals.size(); ++i)
1399 : {
1400 25 : if (i > 0)
1401 0 : AddListValueSeparator();
1402 25 : const auto &val = vals[i];
1403 25 : const auto &str = val.GetName();
1404 25 : if (str.empty())
1405 : {
1406 1 : return false;
1407 : }
1408 48 : ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
1409 24 : : str);
1410 : }
1411 24 : break;
1412 : }
1413 : }
1414 :
1415 38 : serializedArg = std::move(ret);
1416 38 : return true;
1417 : }
1418 :
1419 : /************************************************************************/
1420 : /* ~GDALInConstructionAlgorithmArg() */
1421 : /************************************************************************/
1422 :
1423 : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
1424 :
1425 : /************************************************************************/
1426 : /* GDALInConstructionAlgorithmArg::AddAlias() */
1427 : /************************************************************************/
1428 :
1429 : GDALInConstructionAlgorithmArg &
1430 61571 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
1431 : {
1432 61571 : m_decl.AddAlias(alias);
1433 61571 : if (m_owner)
1434 61571 : m_owner->AddAliasFor(this, alias);
1435 61571 : return *this;
1436 : }
1437 :
1438 : /************************************************************************/
1439 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
1440 : /************************************************************************/
1441 :
1442 : GDALInConstructionAlgorithmArg &
1443 13007 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
1444 : {
1445 13007 : m_decl.AddHiddenAlias(alias);
1446 13007 : if (m_owner)
1447 13007 : m_owner->AddAliasFor(this, alias);
1448 13007 : return *this;
1449 : }
1450 :
1451 : /************************************************************************/
1452 : /* GDALInConstructionAlgorithmArg::AddShortNameAlias() */
1453 : /************************************************************************/
1454 :
1455 : GDALInConstructionAlgorithmArg &
1456 48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
1457 : {
1458 48 : m_decl.AddShortNameAlias(shortNameAlias);
1459 48 : if (m_owner)
1460 48 : m_owner->AddShortNameAliasFor(this, shortNameAlias);
1461 48 : return *this;
1462 : }
1463 :
1464 : /************************************************************************/
1465 : /* GDALInConstructionAlgorithmArg::SetPositional() */
1466 : /************************************************************************/
1467 :
1468 21123 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
1469 : {
1470 21123 : m_decl.SetPositional();
1471 21123 : if (m_owner)
1472 21123 : m_owner->SetPositional(this);
1473 21123 : return *this;
1474 : }
1475 :
1476 : /************************************************************************/
1477 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
1478 : /************************************************************************/
1479 :
1480 1274 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
1481 2548 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
1482 1274 : m_nameSet(true)
1483 : {
1484 1274 : if (m_poDS)
1485 1274 : m_poDS->Reference();
1486 1274 : }
1487 :
1488 : /************************************************************************/
1489 : /* GDALArgDatasetValue::Set() */
1490 : /************************************************************************/
1491 :
1492 2170 : void GDALArgDatasetValue::Set(const std::string &name)
1493 : {
1494 2170 : Close();
1495 2170 : m_name = name;
1496 2170 : m_nameSet = true;
1497 2170 : if (m_ownerArg)
1498 2165 : m_ownerArg->NotifyValueSet();
1499 2170 : }
1500 :
1501 : /************************************************************************/
1502 : /* GDALArgDatasetValue::Set() */
1503 : /************************************************************************/
1504 :
1505 1880 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
1506 : {
1507 1880 : Close();
1508 1880 : m_poDS = poDS.release();
1509 1880 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1510 1880 : m_nameSet = true;
1511 1880 : if (m_ownerArg)
1512 1758 : m_ownerArg->NotifyValueSet();
1513 1880 : }
1514 :
1515 : /************************************************************************/
1516 : /* GDALArgDatasetValue::Set() */
1517 : /************************************************************************/
1518 :
1519 7529 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
1520 : {
1521 7529 : Close();
1522 7529 : m_poDS = poDS;
1523 7529 : if (m_poDS)
1524 6706 : m_poDS->Reference();
1525 7529 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
1526 7529 : m_nameSet = true;
1527 7529 : if (m_ownerArg)
1528 3160 : m_ownerArg->NotifyValueSet();
1529 7529 : }
1530 :
1531 : /************************************************************************/
1532 : /* GDALArgDatasetValue::SetFrom() */
1533 : /************************************************************************/
1534 :
1535 3154 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
1536 : {
1537 3154 : Close();
1538 3154 : m_name = other.m_name;
1539 3154 : m_nameSet = other.m_nameSet;
1540 3154 : m_poDS = other.m_poDS;
1541 3154 : if (m_poDS)
1542 2214 : m_poDS->Reference();
1543 3154 : }
1544 :
1545 : /************************************************************************/
1546 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
1547 : /************************************************************************/
1548 :
1549 30321 : GDALArgDatasetValue::~GDALArgDatasetValue()
1550 : {
1551 30321 : Close();
1552 30321 : }
1553 :
1554 : /************************************************************************/
1555 : /* GDALArgDatasetValue::Close() */
1556 : /************************************************************************/
1557 :
1558 50034 : bool GDALArgDatasetValue::Close()
1559 : {
1560 50034 : bool ret = true;
1561 50034 : if (m_poDS && m_poDS->Dereference() == 0)
1562 : {
1563 3192 : ret = m_poDS->Close() == CE_None;
1564 3192 : delete m_poDS;
1565 : }
1566 50034 : m_poDS = nullptr;
1567 50034 : return ret;
1568 : }
1569 :
1570 : /************************************************************************/
1571 : /* GDALArgDatasetValue::operator=() */
1572 : /************************************************************************/
1573 :
1574 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
1575 : {
1576 2 : Close();
1577 2 : m_poDS = other.m_poDS;
1578 2 : m_name = other.m_name;
1579 2 : m_nameSet = other.m_nameSet;
1580 2 : other.m_poDS = nullptr;
1581 2 : other.m_name.clear();
1582 2 : other.m_nameSet = false;
1583 2 : return *this;
1584 : }
1585 :
1586 : /************************************************************************/
1587 : /* GDALArgDatasetValue::GetDataset() */
1588 : /************************************************************************/
1589 :
1590 1025 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
1591 : {
1592 1025 : if (m_poDS)
1593 1025 : m_poDS->Reference();
1594 1025 : return m_poDS;
1595 : }
1596 :
1597 : /************************************************************************/
1598 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
1599 : /************************************************************************/
1600 :
1601 2978 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
1602 2978 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
1603 : {
1604 2978 : other.m_poDS = nullptr;
1605 2978 : other.m_name.clear();
1606 2978 : }
1607 :
1608 : /************************************************************************/
1609 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
1610 : /************************************************************************/
1611 :
1612 3212 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
1613 : bool noneAllowed, const std::vector<std::string> &specialValues)
1614 : {
1615 3212 : if (GetType() != GAAT_STRING)
1616 : {
1617 1 : CPLError(CE_Failure, CPLE_AppDefined,
1618 : "SetIsCRSArg() can only be called on a String argument");
1619 1 : return *this;
1620 : }
1621 : AddValidationAction(
1622 665 : [this, noneAllowed, specialValues]()
1623 : {
1624 : const std::string &osVal =
1625 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
1626 329 : ->Get<std::string>();
1627 329 : if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
1628 0 : return true;
1629 :
1630 645 : if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
1631 316 : std::find(specialValues.begin(), specialValues.end(), osVal) ==
1632 645 : specialValues.end())
1633 : {
1634 308 : OGRSpatialReference oSRS;
1635 308 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
1636 : {
1637 7 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
1638 : "Invalid value for '%s' argument",
1639 7 : GetName().c_str());
1640 7 : return false;
1641 : }
1642 : }
1643 322 : return true;
1644 3211 : });
1645 :
1646 : SetAutoCompleteFunction(
1647 40 : [this, noneAllowed, specialValues](const std::string ¤tValue)
1648 : {
1649 10 : bool bIsRaster = false;
1650 10 : OGREnvelope sDatasetLongLatEnv;
1651 20 : std::string osCelestialBodyName;
1652 10 : if (GetName() == "dst-crs")
1653 : {
1654 10 : auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
1655 10 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
1656 : {
1657 : auto &val =
1658 10 : inputArg->Get<std::vector<GDALArgDatasetValue>>();
1659 10 : if (val.size() == 1)
1660 : {
1661 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1662 : auto poDS = std::unique_ptr<GDALDataset>(
1663 4 : GDALDataset::Open(val[0].GetName().c_str()));
1664 2 : if (poDS)
1665 : {
1666 2 : bIsRaster = poDS->GetRasterCount() != 0;
1667 2 : if (auto poCRS = poDS->GetSpatialRef())
1668 : {
1669 : const char *pszCelestialBodyName =
1670 2 : poCRS->GetCelestialBodyName();
1671 2 : if (pszCelestialBodyName)
1672 2 : osCelestialBodyName = pszCelestialBodyName;
1673 :
1674 2 : if (!pszCelestialBodyName ||
1675 2 : !EQUAL(pszCelestialBodyName, "Earth"))
1676 : {
1677 0 : OGRSpatialReference oLongLat;
1678 0 : oLongLat.CopyGeogCSFrom(poCRS);
1679 0 : oLongLat.SetAxisMappingStrategy(
1680 : OAMS_TRADITIONAL_GIS_ORDER);
1681 0 : poDS->GetExtent(&sDatasetLongLatEnv,
1682 0 : &oLongLat);
1683 : }
1684 : else
1685 : {
1686 2 : poDS->GetExtentWGS84LongLat(
1687 2 : &sDatasetLongLatEnv);
1688 : }
1689 : }
1690 : }
1691 : }
1692 : }
1693 : }
1694 :
1695 : const auto IsCRSCompatible =
1696 42959 : [bIsRaster, &sDatasetLongLatEnv,
1697 73695 : &osCelestialBodyName](const OSRCRSInfo *crsInfo)
1698 : {
1699 42959 : if (!sDatasetLongLatEnv.IsInit())
1700 30685 : return true;
1701 24108 : return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
1702 11834 : !(bIsRaster &&
1703 5917 : crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
1704 11652 : crsInfo->dfWestLongitudeDeg <
1705 11652 : crsInfo->dfEastLongitudeDeg &&
1706 11517 : sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
1707 5618 : sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
1708 615 : sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
1709 24437 : sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
1710 329 : ((!osCelestialBodyName.empty() &&
1711 658 : crsInfo->pszCelestialBodyName &&
1712 329 : osCelestialBodyName ==
1713 329 : crsInfo->pszCelestialBodyName) ||
1714 0 : (osCelestialBodyName.empty() &&
1715 12274 : !crsInfo->pszCelestialBodyName));
1716 10 : };
1717 :
1718 10 : std::vector<std::string> oRet;
1719 10 : if (noneAllowed)
1720 0 : oRet.push_back("none");
1721 10 : oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
1722 10 : if (!currentValue.empty())
1723 : {
1724 : const CPLStringList aosTokens(
1725 14 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
1726 7 : int nCount = 0;
1727 : std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
1728 : pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
1729 : nullptr, &nCount),
1730 14 : OSRDestroyCRSInfoList);
1731 14 : std::string osCode;
1732 :
1733 14 : std::vector<const OSRCRSInfo *> candidates;
1734 46270 : for (int i = 0; i < nCount; ++i)
1735 : {
1736 46263 : const auto *entry = (pCRSList.get())[i];
1737 46263 : if (!entry->bDeprecated && IsCRSCompatible(entry))
1738 : {
1739 49425 : if (aosTokens.size() == 1 ||
1740 18411 : STARTS_WITH(entry->pszCode, aosTokens[1]))
1741 : {
1742 12666 : if (candidates.empty())
1743 7 : osCode = entry->pszCode;
1744 12666 : candidates.push_back(entry);
1745 : }
1746 : }
1747 : }
1748 7 : if (candidates.size() == 1)
1749 : {
1750 1 : oRet.push_back(std::move(osCode));
1751 : }
1752 : else
1753 : {
1754 6 : if (sDatasetLongLatEnv.IsInit())
1755 : {
1756 2 : std::sort(
1757 : candidates.begin(), candidates.end(),
1758 2999 : [](const OSRCRSInfo *a, const OSRCRSInfo *b)
1759 : {
1760 2999 : const double dfXa =
1761 2999 : a->dfWestLongitudeDeg >
1762 2999 : a->dfEastLongitudeDeg
1763 2999 : ? a->dfWestLongitudeDeg -
1764 0 : a->dfEastLongitudeDeg
1765 2999 : : (180 - a->dfWestLongitudeDeg) +
1766 2999 : (a->dfEastLongitudeDeg - -180);
1767 2999 : const double dfYa = a->dfNorthLatitudeDeg -
1768 2999 : a->dfSouthLatitudeDeg;
1769 2999 : const double dfXb =
1770 2999 : b->dfWestLongitudeDeg >
1771 2999 : b->dfEastLongitudeDeg
1772 2999 : ? b->dfWestLongitudeDeg -
1773 0 : b->dfEastLongitudeDeg
1774 2999 : : (180 - b->dfWestLongitudeDeg) +
1775 2999 : (b->dfEastLongitudeDeg - -180);
1776 2999 : const double dfYb = b->dfNorthLatitudeDeg -
1777 2999 : b->dfSouthLatitudeDeg;
1778 2999 : const double diffArea =
1779 2999 : dfXa * dfYa - dfXb * dfYb;
1780 2999 : if (diffArea < 0)
1781 279 : return true;
1782 2720 : if (diffArea == 0)
1783 : {
1784 2506 : if (std::string_view(a->pszName) ==
1785 2506 : b->pszName)
1786 : {
1787 57 : if (a->eType ==
1788 13 : OSR_CRS_TYPE_GEOGRAPHIC_2D &&
1789 13 : b->eType !=
1790 : OSR_CRS_TYPE_GEOGRAPHIC_2D)
1791 13 : return true;
1792 44 : if (a->eType ==
1793 32 : OSR_CRS_TYPE_GEOGRAPHIC_3D &&
1794 32 : b->eType == OSR_CRS_TYPE_GEOCENTRIC)
1795 9 : return true;
1796 35 : return false;
1797 : }
1798 4898 : return std::string_view(a->pszCode) <
1799 4898 : b->pszCode;
1800 : }
1801 214 : return false;
1802 : });
1803 : }
1804 :
1805 12671 : for (const auto *entry : candidates)
1806 : {
1807 25330 : std::string val = std::string(entry->pszCode)
1808 12665 : .append(" -- ")
1809 25330 : .append(entry->pszName);
1810 12665 : if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
1811 1294 : val.append(" (geographic 2D)");
1812 11371 : else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
1813 446 : val.append(" (geographic 3D)");
1814 10925 : else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
1815 397 : val.append(" (geocentric)");
1816 12665 : oRet.push_back(std::move(val));
1817 : }
1818 : }
1819 : }
1820 10 : if (currentValue.empty() || oRet.empty())
1821 : {
1822 : const CPLStringList aosAuthorities(
1823 6 : OSRGetAuthorityListFromDatabase());
1824 18 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
1825 : {
1826 15 : int nCount = 0;
1827 15 : OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
1828 : pszAuth, nullptr, &nCount));
1829 15 : if (nCount)
1830 12 : oRet.push_back(std::string(pszAuth).append(":"));
1831 : }
1832 : }
1833 20 : return oRet;
1834 3211 : });
1835 :
1836 3211 : return *this;
1837 : }
1838 :
1839 : /************************************************************************/
1840 : /* GDALAlgorithm::GDALAlgorithm() */
1841 : /************************************************************************/
1842 :
1843 21101 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
1844 : const std::string &description,
1845 21101 : const std::string &helpURL)
1846 : : m_name(name), m_description(description), m_helpURL(helpURL),
1847 42042 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
1848 21101 : ? "https://gdal.org" + m_helpURL
1849 62913 : : m_helpURL)
1850 : {
1851 : auto &helpArg =
1852 : AddArg("help", 'h', _("Display help message and exit"),
1853 42202 : &m_helpRequested)
1854 21101 : .SetHiddenForAPI()
1855 42202 : .SetCategory(GAAC_COMMON)
1856 14 : .AddAction([this]()
1857 21101 : { m_specialActionRequested = m_calledFromCommandLine; });
1858 : auto &helpDocArg =
1859 : AddArg("help-doc", 0,
1860 : _("Display help message for use by documentation"),
1861 42202 : &m_helpDocRequested)
1862 21101 : .SetHidden()
1863 12 : .AddAction([this]()
1864 21101 : { m_specialActionRequested = m_calledFromCommandLine; });
1865 : auto &jsonUsageArg =
1866 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
1867 42202 : &m_JSONUsageRequested)
1868 21101 : .SetHiddenForAPI()
1869 42202 : .SetCategory(GAAC_COMMON)
1870 4 : .AddAction([this]()
1871 21101 : { m_specialActionRequested = m_calledFromCommandLine; });
1872 42202 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
1873 42202 : .SetMetaVar("<KEY>=<VALUE>")
1874 21101 : .SetHiddenForAPI()
1875 42202 : .SetCategory(GAAC_COMMON)
1876 : .AddAction(
1877 2 : [this]()
1878 : {
1879 2 : ReportError(
1880 : CE_Warning, CPLE_AppDefined,
1881 : "Configuration options passed with the 'config' argument "
1882 : "are ignored");
1883 21101 : });
1884 :
1885 21101 : AddValidationAction(
1886 12477 : [this, &helpArg, &helpDocArg, &jsonUsageArg]()
1887 : {
1888 6435 : if (!m_calledFromCommandLine && m_specialActionRequested)
1889 : {
1890 0 : for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
1891 : {
1892 0 : if (arg->IsExplicitlySet())
1893 : {
1894 0 : ReportError(CE_Failure, CPLE_AppDefined,
1895 : "'%s' argument only available when called "
1896 : "from command line",
1897 0 : arg->GetName().c_str());
1898 0 : return false;
1899 : }
1900 : }
1901 : }
1902 6435 : return true;
1903 : });
1904 21101 : }
1905 :
1906 : /************************************************************************/
1907 : /* GDALAlgorithm::~GDALAlgorithm() */
1908 : /************************************************************************/
1909 :
1910 : GDALAlgorithm::~GDALAlgorithm() = default;
1911 :
1912 : /************************************************************************/
1913 : /* GDALAlgorithm::ParseArgument() */
1914 : /************************************************************************/
1915 :
1916 2981 : bool GDALAlgorithm::ParseArgument(
1917 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
1918 : std::map<
1919 : GDALAlgorithmArg *,
1920 : std::variant<std::vector<std::string>, std::vector<int>,
1921 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1922 : &inConstructionValues)
1923 : {
1924 2981 : const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
1925 2981 : if (arg->IsExplicitlySet() && !isListArg)
1926 : {
1927 : // Hack for "gdal info" to be able to pass an opened raster dataset
1928 : // by "gdal raster info" to the "gdal vector info" algorithm.
1929 3 : if (arg->SkipIfAlreadySet())
1930 : {
1931 1 : arg->SetSkipIfAlreadySet(false);
1932 1 : return true;
1933 : }
1934 :
1935 2 : ReportError(CE_Failure, CPLE_IllegalArg,
1936 : "Argument '%s' has already been specified.", name.c_str());
1937 2 : return false;
1938 : }
1939 :
1940 3044 : if (!arg->GetRepeatedArgAllowed() &&
1941 66 : cpl::contains(inConstructionValues, arg))
1942 : {
1943 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1944 : "Argument '%s' has already been specified.", name.c_str());
1945 1 : return false;
1946 : }
1947 :
1948 2977 : switch (arg->GetType())
1949 : {
1950 304 : case GAAT_BOOLEAN:
1951 : {
1952 304 : if (value.empty() || value == "true")
1953 302 : return arg->Set(true);
1954 2 : else if (value == "false")
1955 1 : return arg->Set(false);
1956 : else
1957 : {
1958 1 : ReportError(
1959 : CE_Failure, CPLE_IllegalArg,
1960 : "Invalid value '%s' for boolean argument '%s'. Should be "
1961 : "'true' or 'false'.",
1962 : value.c_str(), name.c_str());
1963 1 : return false;
1964 : }
1965 : }
1966 :
1967 736 : case GAAT_STRING:
1968 : {
1969 736 : return arg->Set(value);
1970 : }
1971 :
1972 350 : case GAAT_INTEGER:
1973 : {
1974 350 : errno = 0;
1975 350 : char *endptr = nullptr;
1976 350 : const auto val = std::strtol(value.c_str(), &endptr, 10);
1977 349 : if (errno == 0 && endptr &&
1978 699 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
1979 : val <= INT_MAX)
1980 : {
1981 347 : return arg->Set(static_cast<int>(val));
1982 : }
1983 : else
1984 : {
1985 3 : ReportError(CE_Failure, CPLE_IllegalArg,
1986 : "Expected integer value for argument '%s', "
1987 : "but got '%s'.",
1988 : name.c_str(), value.c_str());
1989 3 : return false;
1990 : }
1991 : }
1992 :
1993 32 : case GAAT_REAL:
1994 : {
1995 32 : char *endptr = nullptr;
1996 32 : double dfValue = CPLStrtod(value.c_str(), &endptr);
1997 32 : if (endptr != value.c_str() + value.size())
1998 : {
1999 1 : ReportError(
2000 : CE_Failure, CPLE_IllegalArg,
2001 : "Expected real value for argument '%s', but got '%s'.",
2002 : name.c_str(), value.c_str());
2003 1 : return false;
2004 : }
2005 31 : return arg->Set(dfValue);
2006 : }
2007 :
2008 522 : case GAAT_DATASET:
2009 : {
2010 522 : return arg->SetDatasetName(value);
2011 : }
2012 :
2013 255 : case GAAT_STRING_LIST:
2014 : {
2015 : const CPLStringList aosTokens(
2016 255 : arg->GetPackedValuesAllowed()
2017 164 : ? CSLTokenizeString2(value.c_str(), ",",
2018 : CSLT_HONOURSTRINGS |
2019 : CSLT_PRESERVEQUOTES)
2020 674 : : CSLAddString(nullptr, value.c_str()));
2021 255 : if (!cpl::contains(inConstructionValues, arg))
2022 : {
2023 231 : inConstructionValues[arg] = std::vector<std::string>();
2024 : }
2025 : auto &valueVector =
2026 255 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
2027 542 : for (const char *v : aosTokens)
2028 : {
2029 287 : valueVector.push_back(v);
2030 : }
2031 255 : break;
2032 : }
2033 :
2034 62 : case GAAT_INTEGER_LIST:
2035 : {
2036 : const CPLStringList aosTokens(
2037 62 : arg->GetPackedValuesAllowed()
2038 62 : ? CSLTokenizeString2(
2039 : value.c_str(), ",",
2040 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2041 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2042 124 : : CSLAddString(nullptr, value.c_str()));
2043 62 : if (!cpl::contains(inConstructionValues, arg))
2044 : {
2045 58 : inConstructionValues[arg] = std::vector<int>();
2046 : }
2047 : auto &valueVector =
2048 62 : std::get<std::vector<int>>(inConstructionValues[arg]);
2049 192 : for (const char *v : aosTokens)
2050 : {
2051 136 : errno = 0;
2052 136 : char *endptr = nullptr;
2053 136 : const auto val = std::strtol(v, &endptr, 10);
2054 136 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
2055 132 : val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
2056 : {
2057 130 : valueVector.push_back(static_cast<int>(val));
2058 : }
2059 : else
2060 : {
2061 6 : ReportError(
2062 : CE_Failure, CPLE_IllegalArg,
2063 : "Expected list of integer value for argument '%s', "
2064 : "but got '%s'.",
2065 : name.c_str(), value.c_str());
2066 6 : return false;
2067 : }
2068 : }
2069 56 : break;
2070 : }
2071 :
2072 99 : case GAAT_REAL_LIST:
2073 : {
2074 : const CPLStringList aosTokens(
2075 99 : arg->GetPackedValuesAllowed()
2076 99 : ? CSLTokenizeString2(
2077 : value.c_str(), ",",
2078 : CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
2079 : CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
2080 198 : : CSLAddString(nullptr, value.c_str()));
2081 99 : if (!cpl::contains(inConstructionValues, arg))
2082 : {
2083 97 : inConstructionValues[arg] = std::vector<double>();
2084 : }
2085 : auto &valueVector =
2086 99 : std::get<std::vector<double>>(inConstructionValues[arg]);
2087 401 : for (const char *v : aosTokens)
2088 : {
2089 306 : char *endptr = nullptr;
2090 306 : double dfValue = CPLStrtod(v, &endptr);
2091 306 : if (strlen(v) == 0 || endptr != v + strlen(v))
2092 : {
2093 4 : ReportError(
2094 : CE_Failure, CPLE_IllegalArg,
2095 : "Expected list of real value for argument '%s', "
2096 : "but got '%s'.",
2097 : name.c_str(), value.c_str());
2098 4 : return false;
2099 : }
2100 302 : valueVector.push_back(dfValue);
2101 : }
2102 95 : break;
2103 : }
2104 :
2105 617 : case GAAT_DATASET_LIST:
2106 : {
2107 617 : if (!cpl::contains(inConstructionValues, arg))
2108 : {
2109 605 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
2110 : }
2111 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
2112 617 : inConstructionValues[arg]);
2113 617 : if (!value.empty() && value[0] == '{' && value.back() == '}')
2114 : {
2115 12 : valueVector.push_back(GDALArgDatasetValue(value));
2116 : }
2117 : else
2118 : {
2119 : const CPLStringList aosTokens(
2120 605 : arg->GetPackedValuesAllowed()
2121 6 : ? CSLTokenizeString2(value.c_str(), ",",
2122 : CSLT_HONOURSTRINGS |
2123 : CSLT_STRIPLEADSPACES)
2124 1216 : : CSLAddString(nullptr, value.c_str()));
2125 1213 : for (const char *v : aosTokens)
2126 : {
2127 608 : valueVector.push_back(GDALArgDatasetValue(v));
2128 : }
2129 : }
2130 617 : break;
2131 : }
2132 : }
2133 :
2134 1023 : return true;
2135 : }
2136 :
2137 : /************************************************************************/
2138 : /* GDALAlgorithm::ParseCommandLineArguments() */
2139 : /************************************************************************/
2140 :
2141 1970 : bool GDALAlgorithm::ParseCommandLineArguments(
2142 : const std::vector<std::string> &args)
2143 : {
2144 1970 : if (m_parsedSubStringAlreadyCalled)
2145 : {
2146 6 : ReportError(CE_Failure, CPLE_AppDefined,
2147 : "ParseCommandLineArguments() can only be called once per "
2148 : "instance.");
2149 6 : return false;
2150 : }
2151 1964 : m_parsedSubStringAlreadyCalled = true;
2152 :
2153 : // AWS like syntax supported too (not advertized)
2154 1964 : if (args.size() == 1 && args[0] == "help")
2155 : {
2156 1 : auto arg = GetArg("help");
2157 1 : assert(arg);
2158 1 : arg->Set(true);
2159 1 : arg->RunActions();
2160 1 : return true;
2161 : }
2162 :
2163 1963 : if (HasSubAlgorithms())
2164 : {
2165 423 : if (args.empty())
2166 : {
2167 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
2168 2 : m_callPath.size() == 1 ? "command" : "subcommand");
2169 2 : return false;
2170 : }
2171 421 : if (!args[0].empty() && args[0][0] == '-')
2172 : {
2173 : // go on argument parsing
2174 : }
2175 : else
2176 : {
2177 418 : const auto nCounter = CPLGetErrorCounter();
2178 418 : m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
2179 418 : if (m_selectedSubAlgHolder)
2180 : {
2181 415 : m_selectedSubAlg = m_selectedSubAlgHolder.get();
2182 415 : m_selectedSubAlg->SetReferencePathForRelativePaths(
2183 415 : m_referencePath);
2184 415 : m_selectedSubAlg->m_executionForStreamOutput =
2185 415 : m_executionForStreamOutput;
2186 415 : m_selectedSubAlg->m_calledFromCommandLine =
2187 415 : m_calledFromCommandLine;
2188 415 : m_selectedSubAlg->m_skipValidationInParseCommandLine =
2189 415 : m_skipValidationInParseCommandLine;
2190 415 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
2191 830 : std::vector<std::string>(args.begin() + 1, args.end()));
2192 415 : m_selectedSubAlg->PropagateSpecialActionTo(this);
2193 415 : return bRet;
2194 : }
2195 : else
2196 : {
2197 4 : if (!(CPLGetErrorCounter() == nCounter + 1 &&
2198 1 : strstr(CPLGetLastErrorMsg(), "Do you mean")))
2199 : {
2200 2 : ReportError(CE_Failure, CPLE_AppDefined,
2201 2 : "Unknown command: '%s'", args[0].c_str());
2202 : }
2203 3 : return false;
2204 : }
2205 : }
2206 : }
2207 :
2208 : std::map<
2209 : GDALAlgorithmArg *,
2210 : std::variant<std::vector<std::string>, std::vector<int>,
2211 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
2212 3086 : inConstructionValues;
2213 :
2214 3086 : std::vector<std::string> lArgs(args);
2215 1543 : bool helpValueRequested = false;
2216 4529 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
2217 : {
2218 3085 : const auto &strArg = lArgs[i];
2219 3085 : GDALAlgorithmArg *arg = nullptr;
2220 3085 : std::string name;
2221 3085 : std::string value;
2222 3085 : bool hasValue = false;
2223 3085 : if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
2224 5 : helpValueRequested = true;
2225 3085 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
2226 : {
2227 1974 : const auto equalPos = strArg.find('=');
2228 3948 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
2229 1974 : : strArg;
2230 1974 : const std::string nameWithoutDash = name.substr(2);
2231 1974 : auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2232 2027 : if (m_arbitraryLongNameArgsAllowed &&
2233 2027 : iterArg == m_mapLongNameToArg.end())
2234 : {
2235 16 : GetArg(nameWithoutDash);
2236 16 : iterArg = m_mapLongNameToArg.find(nameWithoutDash);
2237 : }
2238 1974 : if (iterArg == m_mapLongNameToArg.end())
2239 : {
2240 : const std::string bestCandidate =
2241 26 : GetSuggestionForArgumentName(nameWithoutDash);
2242 26 : if (!bestCandidate.empty())
2243 : {
2244 2 : ReportError(CE_Failure, CPLE_IllegalArg,
2245 : "Option '%s' is unknown. Do you mean '--%s'?",
2246 : name.c_str(), bestCandidate.c_str());
2247 : }
2248 : else
2249 : {
2250 24 : ReportError(CE_Failure, CPLE_IllegalArg,
2251 : "Option '%s' is unknown.", name.c_str());
2252 : }
2253 26 : return false;
2254 : }
2255 1948 : arg = iterArg->second;
2256 1948 : if (equalPos != std::string::npos)
2257 : {
2258 427 : hasValue = true;
2259 427 : value = strArg.substr(equalPos + 1);
2260 : }
2261 : }
2262 1185 : else if (strArg.size() >= 2 && strArg[0] == '-' &&
2263 74 : CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
2264 : {
2265 143 : for (size_t j = 1; j < strArg.size(); ++j)
2266 : {
2267 74 : name.clear();
2268 74 : name += strArg[j];
2269 74 : const auto iterArg = m_mapShortNameToArg.find(name);
2270 74 : if (iterArg == m_mapShortNameToArg.end())
2271 : {
2272 5 : const std::string nameWithoutDash = strArg.substr(1);
2273 5 : if (m_mapLongNameToArg.find(nameWithoutDash) !=
2274 10 : m_mapLongNameToArg.end())
2275 : {
2276 1 : ReportError(CE_Failure, CPLE_IllegalArg,
2277 : "Short name option '%s' is unknown. Do you "
2278 : "mean '--%s' (with leading double dash) ?",
2279 : name.c_str(), nameWithoutDash.c_str());
2280 : }
2281 : else
2282 : {
2283 : const std::string bestCandidate =
2284 8 : GetSuggestionForArgumentName(nameWithoutDash);
2285 4 : if (!bestCandidate.empty())
2286 : {
2287 1 : ReportError(
2288 : CE_Failure, CPLE_IllegalArg,
2289 : "Short name option '%s' is unknown. Do you "
2290 : "mean '--%s' (with leading double dash) ?",
2291 : name.c_str(), bestCandidate.c_str());
2292 : }
2293 : else
2294 : {
2295 3 : ReportError(CE_Failure, CPLE_IllegalArg,
2296 : "Short name option '%s' is unknown.",
2297 : name.c_str());
2298 : }
2299 : }
2300 5 : return false;
2301 : }
2302 69 : arg = iterArg->second;
2303 69 : if (strArg.size() > 2)
2304 : {
2305 0 : if (arg->GetType() != GAAT_BOOLEAN)
2306 : {
2307 0 : ReportError(CE_Failure, CPLE_IllegalArg,
2308 : "Invalid argument '%s'. Option '%s' is not "
2309 : "a boolean option.",
2310 : strArg.c_str(), name.c_str());
2311 0 : return false;
2312 : }
2313 :
2314 0 : if (!ParseArgument(arg, name, "true", inConstructionValues))
2315 0 : return false;
2316 : }
2317 : }
2318 69 : if (strArg.size() > 2)
2319 : {
2320 0 : lArgs.erase(lArgs.begin() + i);
2321 0 : continue;
2322 : }
2323 : }
2324 : else
2325 : {
2326 1037 : ++i;
2327 1037 : continue;
2328 : }
2329 2017 : CPLAssert(arg);
2330 :
2331 2017 : if (arg && arg->GetType() == GAAT_BOOLEAN)
2332 : {
2333 305 : if (!hasValue)
2334 : {
2335 302 : hasValue = true;
2336 302 : value = "true";
2337 : }
2338 : }
2339 :
2340 2017 : if (!hasValue)
2341 : {
2342 1288 : if (i + 1 == lArgs.size())
2343 : {
2344 30 : if (m_parseForAutoCompletion)
2345 : {
2346 24 : lArgs.erase(lArgs.begin() + i);
2347 24 : break;
2348 : }
2349 6 : ReportError(
2350 : CE_Failure, CPLE_IllegalArg,
2351 : "Expected value for argument '%s', but ran short of tokens",
2352 : name.c_str());
2353 6 : return false;
2354 : }
2355 1258 : value = lArgs[i + 1];
2356 1258 : lArgs.erase(lArgs.begin() + i + 1);
2357 : }
2358 :
2359 1987 : if (arg && !ParseArgument(arg, name, value, inConstructionValues))
2360 38 : return false;
2361 :
2362 1949 : lArgs.erase(lArgs.begin() + i);
2363 : }
2364 :
2365 1468 : if (m_specialActionRequested)
2366 : {
2367 23 : return true;
2368 : }
2369 :
2370 2396 : const auto ProcessInConstructionValues = [&inConstructionValues]()
2371 : {
2372 2362 : for (auto &[arg, value] : inConstructionValues)
2373 : {
2374 975 : if (arg->GetType() == GAAT_STRING_LIST)
2375 : {
2376 228 : if (!arg->Set(std::get<std::vector<std::string>>(
2377 228 : inConstructionValues[arg])))
2378 : {
2379 34 : return false;
2380 : }
2381 : }
2382 747 : else if (arg->GetType() == GAAT_INTEGER_LIST)
2383 : {
2384 53 : if (!arg->Set(
2385 53 : std::get<std::vector<int>>(inConstructionValues[arg])))
2386 : {
2387 4 : return false;
2388 : }
2389 : }
2390 694 : else if (arg->GetType() == GAAT_REAL_LIST)
2391 : {
2392 93 : if (!arg->Set(std::get<std::vector<double>>(
2393 93 : inConstructionValues[arg])))
2394 : {
2395 10 : return false;
2396 : }
2397 : }
2398 601 : else if (arg->GetType() == GAAT_DATASET_LIST)
2399 : {
2400 601 : if (!arg->Set(
2401 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
2402 601 : inConstructionValues[arg]))))
2403 : {
2404 2 : return false;
2405 : }
2406 : }
2407 : }
2408 1387 : return true;
2409 1445 : };
2410 :
2411 : // Process positional arguments that have not been set through their
2412 : // option name.
2413 1445 : size_t i = 0;
2414 1445 : size_t iCurPosArg = 0;
2415 :
2416 : // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
2417 1466 : if (m_positionalArgs.size() == 3 &&
2418 22 : (m_positionalArgs[0]->IsRequired() ||
2419 21 : m_positionalArgs[0]->GetMinCount() == 1) &&
2420 40 : m_positionalArgs[0]->GetMaxCount() == 1 &&
2421 27 : (m_positionalArgs[1]->IsRequired() ||
2422 27 : m_positionalArgs[1]->GetMinCount() == 1) &&
2423 : /* Second argument may have several occurrences */
2424 40 : m_positionalArgs[1]->GetMaxCount() >= 1 &&
2425 20 : (m_positionalArgs[2]->IsRequired() ||
2426 20 : m_positionalArgs[2]->GetMinCount() == 1) &&
2427 20 : m_positionalArgs[2]->GetMaxCount() == 1 &&
2428 9 : !m_positionalArgs[0]->IsExplicitlySet() &&
2429 1475 : !m_positionalArgs[1]->IsExplicitlySet() &&
2430 9 : !m_positionalArgs[2]->IsExplicitlySet())
2431 : {
2432 7 : if (lArgs.size() - i < 3)
2433 : {
2434 1 : ReportError(CE_Failure, CPLE_AppDefined,
2435 : "Not enough positional values.");
2436 1 : return false;
2437 : }
2438 12 : bool ok = ParseArgument(m_positionalArgs[0],
2439 6 : m_positionalArgs[0]->GetName().c_str(),
2440 6 : lArgs[i], inConstructionValues);
2441 6 : if (ok)
2442 : {
2443 5 : ++i;
2444 11 : for (; i + 1 < lArgs.size() && ok; ++i)
2445 : {
2446 12 : ok = ParseArgument(m_positionalArgs[1],
2447 6 : m_positionalArgs[1]->GetName().c_str(),
2448 6 : lArgs[i], inConstructionValues);
2449 : }
2450 : }
2451 6 : if (ok)
2452 : {
2453 10 : ok = ParseArgument(m_positionalArgs[2],
2454 10 : m_positionalArgs[2]->GetName().c_str(), lArgs[i],
2455 : inConstructionValues);
2456 5 : ++i;
2457 : }
2458 6 : if (!ok)
2459 : {
2460 3 : ProcessInConstructionValues();
2461 3 : return false;
2462 : }
2463 : }
2464 :
2465 493 : if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
2466 575 : !m_positionalArgs[0]->IsExplicitlySet() &&
2467 2247 : m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
2468 114 : (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
2469 57 : m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
2470 : {
2471 57 : ++iCurPosArg;
2472 : }
2473 :
2474 2394 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
2475 : {
2476 960 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
2477 963 : while (arg->IsExplicitlySet())
2478 : {
2479 4 : ++iCurPosArg;
2480 4 : if (iCurPosArg == m_positionalArgs.size())
2481 1 : break;
2482 3 : arg = m_positionalArgs[iCurPosArg];
2483 : }
2484 960 : if (iCurPosArg == m_positionalArgs.size())
2485 : {
2486 1 : break;
2487 : }
2488 1479 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
2489 520 : arg->GetMinCount() != arg->GetMaxCount())
2490 : {
2491 104 : if (iCurPosArg == 0)
2492 : {
2493 68 : size_t nCountAtEnd = 0;
2494 93 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
2495 : {
2496 27 : const auto *otherArg = m_positionalArgs[j];
2497 27 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
2498 : {
2499 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
2500 : {
2501 2 : ReportError(
2502 : CE_Failure, CPLE_AppDefined,
2503 : "Ambiguity in definition of positional "
2504 : "argument "
2505 : "'%s' given it has a varying number of values, "
2506 : "but follows argument '%s' which also has a "
2507 : "varying number of values",
2508 1 : otherArg->GetName().c_str(),
2509 1 : arg->GetName().c_str());
2510 1 : ProcessInConstructionValues();
2511 1 : return false;
2512 : }
2513 3 : nCountAtEnd += otherArg->GetMinCount();
2514 : }
2515 : else
2516 : {
2517 23 : if (!otherArg->IsRequired())
2518 : {
2519 2 : ReportError(
2520 : CE_Failure, CPLE_AppDefined,
2521 : "Ambiguity in definition of positional "
2522 : "argument "
2523 : "'%s', given it is not required but follows "
2524 : "argument '%s' which has a varying number of "
2525 : "values",
2526 1 : otherArg->GetName().c_str(),
2527 1 : arg->GetName().c_str());
2528 1 : ProcessInConstructionValues();
2529 1 : return false;
2530 : }
2531 22 : nCountAtEnd++;
2532 : }
2533 : }
2534 66 : if (lArgs.size() < nCountAtEnd)
2535 : {
2536 1 : ReportError(CE_Failure, CPLE_AppDefined,
2537 : "Not enough positional values.");
2538 1 : ProcessInConstructionValues();
2539 1 : return false;
2540 : }
2541 137 : for (; i < lArgs.size() - nCountAtEnd; ++i)
2542 : {
2543 72 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2544 : inConstructionValues))
2545 : {
2546 0 : ProcessInConstructionValues();
2547 0 : return false;
2548 : }
2549 : }
2550 : }
2551 36 : else if (iCurPosArg == m_positionalArgs.size() - 1)
2552 : {
2553 82 : for (; i < lArgs.size(); ++i)
2554 : {
2555 47 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2556 : inConstructionValues))
2557 : {
2558 0 : ProcessInConstructionValues();
2559 0 : return false;
2560 : }
2561 : }
2562 : }
2563 : else
2564 : {
2565 1 : ReportError(CE_Failure, CPLE_AppDefined,
2566 : "Ambiguity in definition of positional arguments: "
2567 : "arguments with varying number of values must be "
2568 : "first or last one.");
2569 1 : return false;
2570 : }
2571 : }
2572 : else
2573 : {
2574 855 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
2575 : {
2576 1 : ReportError(CE_Failure, CPLE_AppDefined,
2577 : "Not enough positional values.");
2578 1 : return false;
2579 : }
2580 854 : const size_t iMax = i + arg->GetMaxCount();
2581 1711 : for (; i < iMax; ++i)
2582 : {
2583 858 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
2584 : inConstructionValues))
2585 : {
2586 1 : ProcessInConstructionValues();
2587 1 : return false;
2588 : }
2589 : }
2590 : }
2591 953 : ++iCurPosArg;
2592 : }
2593 :
2594 1435 : if (i < lArgs.size())
2595 : {
2596 21 : ReportError(CE_Failure, CPLE_AppDefined,
2597 : "Positional values starting at '%s' are not expected.",
2598 21 : lArgs[i].c_str());
2599 21 : return false;
2600 : }
2601 :
2602 1414 : if (!ProcessInConstructionValues())
2603 : {
2604 33 : return false;
2605 : }
2606 :
2607 : // Skip to first unset positional argument.
2608 2375 : while (iCurPosArg < m_positionalArgs.size() &&
2609 536 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
2610 : {
2611 458 : ++iCurPosArg;
2612 : }
2613 : // Check if this positional argument is required.
2614 1458 : if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
2615 77 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
2616 47 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
2617 30 : : m_positionalArgs[iCurPosArg]->IsRequired()))
2618 : {
2619 66 : ReportError(CE_Failure, CPLE_AppDefined,
2620 : "Positional arguments starting at '%s' have not been "
2621 : "specified.",
2622 66 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
2623 66 : return false;
2624 : }
2625 :
2626 1315 : if (m_calledFromCommandLine)
2627 : {
2628 4793 : for (auto &arg : m_args)
2629 : {
2630 6312 : if (arg->IsExplicitlySet() &&
2631 1021 : ((arg->GetType() == GAAT_STRING &&
2632 1018 : arg->Get<std::string>() == "?") ||
2633 936 : (arg->GetType() == GAAT_STRING_LIST &&
2634 157 : arg->Get<std::vector<std::string>>().size() == 1 &&
2635 78 : arg->Get<std::vector<std::string>>()[0] == "?")))
2636 : {
2637 : {
2638 10 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2639 5 : ValidateArguments();
2640 : }
2641 :
2642 5 : auto choices = arg->GetChoices();
2643 5 : if (choices.empty())
2644 2 : choices = arg->GetAutoCompleteChoices(std::string());
2645 5 : if (!choices.empty())
2646 : {
2647 5 : if (choices.size() == 1)
2648 : {
2649 4 : ReportError(
2650 : CE_Failure, CPLE_AppDefined,
2651 : "Single potential value for argument '%s' is '%s'",
2652 4 : arg->GetName().c_str(), choices.front().c_str());
2653 : }
2654 : else
2655 : {
2656 6 : std::string msg("Potential values for argument '");
2657 3 : msg += arg->GetName();
2658 3 : msg += "' are:";
2659 45 : for (const auto &v : choices)
2660 : {
2661 42 : msg += "\n- ";
2662 42 : msg += v;
2663 : }
2664 3 : ReportError(CE_Failure, CPLE_AppDefined, "%s",
2665 : msg.c_str());
2666 : }
2667 5 : return false;
2668 : }
2669 : }
2670 : }
2671 : }
2672 :
2673 1310 : return m_skipValidationInParseCommandLine || ValidateArguments();
2674 : }
2675 :
2676 : /************************************************************************/
2677 : /* GDALAlgorithm::ReportError() */
2678 : /************************************************************************/
2679 :
2680 : //! @cond Doxygen_Suppress
2681 846 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
2682 : const char *fmt, ...) const
2683 : {
2684 : va_list args;
2685 846 : va_start(args, fmt);
2686 846 : CPLError(eErrClass, err_no, "%s",
2687 846 : std::string(m_name)
2688 846 : .append(": ")
2689 1692 : .append(CPLString().vPrintf(fmt, args))
2690 : .c_str());
2691 846 : va_end(args);
2692 846 : }
2693 :
2694 : //! @endcond
2695 :
2696 : /************************************************************************/
2697 : /* GDALAlgorithm::ProcessDatasetArg() */
2698 : /************************************************************************/
2699 :
2700 9127 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
2701 : GDALAlgorithm *algForOutput)
2702 : {
2703 9127 : bool ret = true;
2704 :
2705 9127 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
2706 9127 : const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
2707 9127 : const bool update = hasUpdateArg && updateArg->Get<bool>();
2708 :
2709 9127 : const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
2710 9127 : const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
2711 9127 : const bool append = hasAppendArg && appendArg->Get<bool>();
2712 :
2713 9127 : const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
2714 : const bool overwrite =
2715 15286 : (arg->IsOutput() && overwriteArg &&
2716 15286 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
2717 :
2718 9127 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
2719 18254 : auto &val = [arg]() -> GDALArgDatasetValue &
2720 : {
2721 9127 : if (arg->GetType() == GAAT_DATASET_LIST)
2722 5057 : return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
2723 : else
2724 4070 : return arg->Get<GDALArgDatasetValue>();
2725 9127 : }();
2726 : const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
2727 14296 : arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
2728 14304 : !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
2729 8 : !overwrite;
2730 9127 : if (!val.GetDatasetRef() && !val.IsNameSet())
2731 : {
2732 3 : ReportError(CE_Failure, CPLE_AppDefined,
2733 : "Argument '%s' has no dataset object or dataset name.",
2734 3 : arg->GetName().c_str());
2735 3 : ret = false;
2736 : }
2737 9124 : else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
2738 : {
2739 1 : return false;
2740 : }
2741 199 : else if (m_inputDatasetCanBeOmitted &&
2742 9322 : val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
2743 8 : !arg->IsOutput())
2744 : {
2745 8 : return true;
2746 : }
2747 13350 : else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
2748 4235 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
2749 : onlyInputSpecifiedInUpdateAndOutputNotRequired))
2750 : {
2751 1284 : int flags = arg->GetDatasetType();
2752 1284 : bool assignToOutputArg = false;
2753 :
2754 : // Check if input and output parameters point to the same
2755 : // filename (for vector datasets)
2756 2368 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
2757 2368 : outputArg && outputArg->GetType() == GAAT_DATASET)
2758 : {
2759 62 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
2760 121 : if (!outputVal.GetDatasetRef() &&
2761 121 : outputVal.GetName() == val.GetName() &&
2762 2 : (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
2763 : {
2764 2 : assignToOutputArg = true;
2765 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2766 : }
2767 60 : else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
2768 : {
2769 2 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
2770 : }
2771 : }
2772 :
2773 1284 : if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
2774 1201 : flags |= GDAL_OF_VERBOSE_ERROR;
2775 1284 : if ((arg == outputArg || !outputArg) && update)
2776 : {
2777 85 : flags |= GDAL_OF_UPDATE;
2778 85 : if (!append)
2779 64 : flags |= GDAL_OF_VERBOSE_ERROR;
2780 : }
2781 :
2782 1284 : const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
2783 : const bool readOnly =
2784 1327 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
2785 43 : readOnlyArg->Get<bool>());
2786 1284 : if (readOnly)
2787 12 : flags &= ~GDAL_OF_UPDATE;
2788 :
2789 2568 : CPLStringList aosOpenOptions;
2790 2568 : CPLStringList aosAllowedDrivers;
2791 1284 : if (arg->IsInput())
2792 : {
2793 1284 : if (arg == outputArg)
2794 : {
2795 83 : if (update && !overwrite)
2796 : {
2797 83 : const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
2798 83 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2799 31 : aosOpenOptions = CPLStringList(
2800 31 : ooArg->Get<std::vector<std::string>>());
2801 : }
2802 : }
2803 : else
2804 : {
2805 1201 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
2806 1201 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
2807 : aosOpenOptions =
2808 1150 : CPLStringList(ooArg->Get<std::vector<std::string>>());
2809 :
2810 1201 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2811 1201 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
2812 : aosAllowedDrivers =
2813 1107 : CPLStringList(ifArg->Get<std::vector<std::string>>());
2814 : }
2815 : }
2816 :
2817 2568 : std::string osDatasetName = val.GetName();
2818 1284 : if (!m_referencePath.empty())
2819 : {
2820 42 : osDatasetName = GDALDataset::BuildFilename(
2821 21 : osDatasetName.c_str(), m_referencePath.c_str(), true);
2822 : }
2823 1284 : if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
2824 0 : osDatasetName = "/vsistdin/";
2825 :
2826 : // Handle special case of overview delete in GTiff which would fail
2827 : // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
2828 142 : if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
2829 1428 : m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
2830 2 : aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
2831 : {
2832 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2833 : GDALDriverH hDrv =
2834 2 : GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
2835 2 : if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
2836 : {
2837 : // Cleaning does not break COG layout
2838 2 : aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
2839 : }
2840 : }
2841 :
2842 1284 : auto oIter = m_oMapDatasetNameToDataset.find(osDatasetName.c_str());
2843 : GDALDataset *poDS;
2844 : {
2845 : // The PostGISRaster may emit an error message, that is not
2846 : // relevant, if it is the vector driver that was intended
2847 1284 : std::unique_ptr<CPLErrorStateBackuper> poBackuper;
2848 1284 : if (cpl::starts_with(osDatasetName, "PG:") &&
2849 0 : (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
2850 : {
2851 0 : poBackuper = std::make_unique<CPLErrorStateBackuper>(
2852 0 : CPLQuietErrorHandler);
2853 : }
2854 :
2855 1284 : CPL_IGNORE_RET_VAL(poBackuper);
2856 1284 : poDS = oIter != m_oMapDatasetNameToDataset.end()
2857 1284 : ? oIter->second
2858 1281 : : GDALDataset::Open(osDatasetName.c_str(), flags,
2859 1281 : aosAllowedDrivers.List(),
2860 1281 : aosOpenOptions.List());
2861 :
2862 : // Retry with PostGIS vector driver
2863 59 : if (!poDS && poBackuper &&
2864 0 : GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
2865 1343 : aosAllowedDrivers.empty() && aosOpenOptions.empty())
2866 : {
2867 0 : poBackuper.reset();
2868 0 : poDS = GDALDataset::Open(
2869 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
2870 0 : aosAllowedDrivers.List(), aosOpenOptions.List());
2871 : }
2872 : }
2873 :
2874 1284 : if (poDS)
2875 : {
2876 1225 : if (oIter != m_oMapDatasetNameToDataset.end())
2877 : {
2878 3 : if (arg->GetType() == GAAT_DATASET)
2879 3 : arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
2880 3 : poDS->Reference();
2881 3 : m_oMapDatasetNameToDataset.erase(oIter);
2882 : }
2883 :
2884 : // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
2885 : // where the PG: dataset will be first opened with the PostGISRaster
2886 : // driver whereas the PostgreSQL (vector) one is actually wanted.
2887 1660 : if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
2888 1742 : (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
2889 82 : aosOpenOptions.empty())
2890 : {
2891 78 : auto poDrv = poDS->GetDriver();
2892 78 : if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
2893 : {
2894 : // Retry with PostgreSQL (vector) driver
2895 : std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
2896 0 : osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
2897 0 : if (poTmpDS)
2898 : {
2899 0 : poDS->ReleaseRef();
2900 0 : poDS = poTmpDS.release();
2901 : }
2902 : }
2903 : }
2904 :
2905 1225 : if (assignToOutputArg)
2906 : {
2907 : // Avoid opening twice the same datasource if it is both
2908 : // the input and output.
2909 : // Known to cause problems with at least FGdb, SQLite
2910 : // and GPKG drivers. See #4270
2911 : // Restrict to those 3 drivers. For example it is known
2912 : // to break with the PG driver due to the way it
2913 : // manages transactions.
2914 2 : auto poDriver = poDS->GetDriver();
2915 4 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
2916 2 : EQUAL(poDriver->GetDescription(), "SQLite") ||
2917 2 : EQUAL(poDriver->GetDescription(), "GPKG")))
2918 : {
2919 2 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
2920 : }
2921 : }
2922 1225 : val.SetDatasetOpenedByAlgorithm();
2923 1225 : val.Set(poDS);
2924 1225 : poDS->ReleaseRef();
2925 : }
2926 59 : else if (!append)
2927 : {
2928 57 : ret = false;
2929 : }
2930 : }
2931 :
2932 : // Deal with overwriting the output dataset
2933 9118 : if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
2934 : {
2935 2953 : if (!append)
2936 : {
2937 : // If outputting to MEM, do not try to erase a real file of the same name!
2938 : const auto outputFormatArg =
2939 2941 : algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2940 8793 : if (!(outputFormatArg &&
2941 2926 : outputFormatArg->GetType() == GAAT_STRING &&
2942 2926 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
2943 1898 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2944 1004 : "stream") ||
2945 1004 : EQUAL(outputFormatArg->Get<std::string>().c_str(),
2946 : "Memory"))))
2947 : {
2948 1019 : const char *pszType = "";
2949 1019 : GDALDriver *poDriver = nullptr;
2950 1992 : if (!val.GetName().empty() &&
2951 973 : GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
2952 : &poDriver))
2953 : {
2954 78 : if (!overwrite)
2955 : {
2956 68 : std::string options;
2957 34 : if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
2958 : {
2959 11 : options += "--";
2960 11 : options += GDAL_ARG_NAME_OVERWRITE_LAYER;
2961 : }
2962 34 : if (hasAppendArg)
2963 : {
2964 22 : if (!options.empty())
2965 8 : options += '/';
2966 22 : options += "--";
2967 22 : options += GDAL_ARG_NAME_APPEND;
2968 : }
2969 34 : if (hasUpdateArg)
2970 : {
2971 15 : if (!options.empty())
2972 12 : options += '/';
2973 15 : options += "--";
2974 15 : options += GDAL_ARG_NAME_UPDATE;
2975 : }
2976 :
2977 34 : if (poDriver)
2978 : {
2979 68 : const char *pszPrefix = poDriver->GetMetadataItem(
2980 34 : GDAL_DMD_CONNECTION_PREFIX);
2981 34 : if (pszPrefix &&
2982 0 : STARTS_WITH_CI(val.GetName().c_str(),
2983 : pszPrefix))
2984 : {
2985 0 : bool bExists = false;
2986 : {
2987 : CPLErrorStateBackuper oBackuper(
2988 0 : CPLQuietErrorHandler);
2989 0 : bExists = std::unique_ptr<GDALDataset>(
2990 : GDALDataset::Open(
2991 0 : val.GetName().c_str())) !=
2992 : nullptr;
2993 : }
2994 0 : if (bExists)
2995 : {
2996 0 : if (!options.empty())
2997 0 : options = " You may specify the " +
2998 0 : options + " option.";
2999 0 : ReportError(CE_Failure, CPLE_AppDefined,
3000 : "%s '%s' already exists.%s",
3001 0 : pszType, val.GetName().c_str(),
3002 : options.c_str());
3003 0 : return false;
3004 : }
3005 :
3006 0 : return true;
3007 : }
3008 : }
3009 :
3010 34 : if (!options.empty())
3011 28 : options = '/' + options;
3012 68 : ReportError(
3013 : CE_Failure, CPLE_AppDefined,
3014 : "%s '%s' already exists. You may specify the "
3015 : "--overwrite%s option.",
3016 34 : pszType, val.GetName().c_str(), options.c_str());
3017 34 : return false;
3018 : }
3019 44 : else if (EQUAL(pszType, "File"))
3020 : {
3021 1 : if (VSIUnlink(val.GetName().c_str()) != 0)
3022 : {
3023 0 : ReportError(CE_Failure, CPLE_AppDefined,
3024 : "Deleting %s failed: %s",
3025 0 : val.GetName().c_str(),
3026 0 : VSIStrerror(errno));
3027 0 : return false;
3028 : }
3029 : }
3030 43 : else if (EQUAL(pszType, "Directory"))
3031 : {
3032 : // We don't want the user to accidentally erase a non-GDAL dataset
3033 1 : ReportError(CE_Failure, CPLE_AppDefined,
3034 : "Directory '%s' already exists, but is not "
3035 : "recognized as a valid GDAL dataset. "
3036 : "Please manually delete it before retrying",
3037 1 : val.GetName().c_str());
3038 1 : return false;
3039 : }
3040 42 : else if (poDriver)
3041 : {
3042 : bool bDeleteOK;
3043 : {
3044 : CPLErrorStateBackuper oBackuper(
3045 42 : CPLQuietErrorHandler);
3046 42 : bDeleteOK = (poDriver->Delete(
3047 42 : val.GetName().c_str()) == CE_None);
3048 : }
3049 : VSIStatBufL sStat;
3050 45 : if (!bDeleteOK &&
3051 3 : VSIStatL(val.GetName().c_str(), &sStat) == 0)
3052 : {
3053 3 : if (VSI_ISDIR(sStat.st_mode))
3054 : {
3055 : // We don't want the user to accidentally erase a non-GDAL dataset
3056 0 : ReportError(
3057 : CE_Failure, CPLE_AppDefined,
3058 : "Directory '%s' already exists, but is not "
3059 : "recognized as a valid GDAL dataset. "
3060 : "Please manually delete it before retrying",
3061 0 : val.GetName().c_str());
3062 2 : return false;
3063 : }
3064 3 : else if (VSIUnlink(val.GetName().c_str()) != 0)
3065 : {
3066 2 : ReportError(CE_Failure, CPLE_AppDefined,
3067 : "Deleting %s failed: %s",
3068 2 : val.GetName().c_str(),
3069 2 : VSIStrerror(errno));
3070 2 : return false;
3071 : }
3072 : }
3073 : }
3074 : }
3075 : }
3076 : }
3077 : }
3078 :
3079 : // If outputting to stdout, automatically turn off progress bar
3080 9081 : if (arg == outputArg && val.GetName() == "/vsistdout/")
3081 : {
3082 8 : auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
3083 8 : if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
3084 5 : quietArg->Set(true);
3085 : }
3086 :
3087 9081 : return ret;
3088 : }
3089 :
3090 : /************************************************************************/
3091 : /* GDALAlgorithm::ValidateArguments() */
3092 : /************************************************************************/
3093 :
3094 6439 : bool GDALAlgorithm::ValidateArguments()
3095 : {
3096 6439 : if (m_selectedSubAlg)
3097 3 : return m_selectedSubAlg->ValidateArguments();
3098 :
3099 6436 : if (m_specialActionRequested)
3100 1 : return true;
3101 :
3102 6435 : m_arbitraryLongNameArgsAllowed = false;
3103 :
3104 : // If only --output=format=MEM/stream is specified and not --output,
3105 : // then set empty name for --output.
3106 6435 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
3107 6435 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3108 3838 : if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
3109 2563 : !outputArg->IsExplicitlySet() &&
3110 353 : outputFormatArg->GetType() == GAAT_STRING &&
3111 353 : (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
3112 583 : EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
3113 10601 : outputArg->GetType() == GAAT_DATASET &&
3114 328 : (outputArg->GetDatasetInputFlags() & GADV_NAME))
3115 : {
3116 328 : outputArg->Get<GDALArgDatasetValue>().Set("");
3117 : }
3118 :
3119 : // The method may emit several errors if several constraints are not met.
3120 6435 : bool ret = true;
3121 6435 : std::map<std::string, std::string> mutualExclusionGroupUsed;
3122 121170 : for (auto &arg : m_args)
3123 : {
3124 : // Check mutually exclusive arguments
3125 114735 : if (arg->IsExplicitlySet())
3126 : {
3127 18620 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3128 18620 : if (!mutualExclusionGroup.empty())
3129 : {
3130 : auto oIter =
3131 755 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
3132 755 : if (oIter != mutualExclusionGroupUsed.end())
3133 : {
3134 13 : ret = false;
3135 26 : ReportError(
3136 : CE_Failure, CPLE_AppDefined,
3137 : "Argument '%s' is mutually exclusive with '%s'.",
3138 26 : arg->GetName().c_str(), oIter->second.c_str());
3139 : }
3140 : else
3141 : {
3142 742 : mutualExclusionGroupUsed[mutualExclusionGroup] =
3143 1484 : arg->GetName();
3144 : }
3145 : }
3146 : }
3147 :
3148 114889 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
3149 154 : !arg->HasDefaultValue())
3150 : {
3151 154 : bool emitError = true;
3152 154 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3153 154 : if (!mutualExclusionGroup.empty())
3154 : {
3155 1765 : for (const auto &otherArg : m_args)
3156 : {
3157 1751 : if (otherArg->GetMutualExclusionGroup() ==
3158 1856 : mutualExclusionGroup &&
3159 105 : otherArg->IsExplicitlySet())
3160 : {
3161 74 : emitError = false;
3162 74 : break;
3163 : }
3164 : }
3165 : }
3166 232 : if (emitError && !(m_inputDatasetCanBeOmitted &&
3167 48 : arg->GetName() == GDAL_ARG_NAME_INPUT &&
3168 60 : (arg->GetType() == GAAT_DATASET ||
3169 30 : arg->GetType() == GAAT_DATASET_LIST)))
3170 : {
3171 50 : ReportError(CE_Failure, CPLE_AppDefined,
3172 : "Required argument '%s' has not been specified.",
3173 50 : arg->GetName().c_str());
3174 50 : ret = false;
3175 : }
3176 : }
3177 114581 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
3178 : {
3179 4070 : if (!ProcessDatasetArg(arg.get(), this))
3180 51 : ret = false;
3181 : }
3182 :
3183 120002 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
3184 5267 : arg->AutoOpenDataset())
3185 : {
3186 4794 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
3187 4794 : if (listVal.size() == 1)
3188 : {
3189 4788 : if (!ProcessDatasetArg(arg.get(), this))
3190 37 : ret = false;
3191 : }
3192 : else
3193 : {
3194 18 : for (auto &val : listVal)
3195 : {
3196 12 : if (!val.GetDatasetRef() && val.GetName().empty())
3197 : {
3198 0 : ReportError(CE_Failure, CPLE_AppDefined,
3199 : "Argument '%s' has no dataset object or "
3200 : "dataset name.",
3201 0 : arg->GetName().c_str());
3202 0 : ret = false;
3203 : }
3204 12 : else if (!val.GetDatasetRef())
3205 : {
3206 : int flags =
3207 4 : arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
3208 :
3209 8 : CPLStringList aosOpenOptions;
3210 8 : CPLStringList aosAllowedDrivers;
3211 4 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
3212 : {
3213 : const auto ooArg =
3214 4 : GetArg(GDAL_ARG_NAME_OPEN_OPTION);
3215 4 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
3216 : {
3217 4 : aosOpenOptions = CPLStringList(
3218 4 : ooArg->Get<std::vector<std::string>>());
3219 : }
3220 :
3221 : const auto ifArg =
3222 4 : GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
3223 4 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
3224 : {
3225 4 : aosAllowedDrivers = CPLStringList(
3226 4 : ifArg->Get<std::vector<std::string>>());
3227 : }
3228 :
3229 4 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3230 0 : if (updateArg &&
3231 4 : updateArg->GetType() == GAAT_BOOLEAN &&
3232 0 : updateArg->Get<bool>())
3233 : {
3234 0 : flags |= GDAL_OF_UPDATE;
3235 : }
3236 : }
3237 :
3238 : auto poDS = std::unique_ptr<GDALDataset>(
3239 4 : GDALDataset::Open(val.GetName().c_str(), flags,
3240 4 : aosAllowedDrivers.List(),
3241 12 : aosOpenOptions.List()));
3242 4 : if (poDS)
3243 : {
3244 3 : val.Set(std::move(poDS));
3245 : }
3246 : else
3247 : {
3248 1 : ret = false;
3249 : }
3250 : }
3251 : }
3252 : }
3253 : }
3254 :
3255 114735 : if (arg->IsExplicitlySet() && !arg->RunValidationActions())
3256 : {
3257 8 : ret = false;
3258 : }
3259 : }
3260 :
3261 27402 : for (const auto &f : m_validationActions)
3262 : {
3263 20967 : if (!f())
3264 78 : ret = false;
3265 : }
3266 :
3267 6435 : return ret;
3268 : }
3269 :
3270 : /************************************************************************/
3271 : /* GDALAlgorithm::InstantiateSubAlgorithm */
3272 : /************************************************************************/
3273 :
3274 : std::unique_ptr<GDALAlgorithm>
3275 9794 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
3276 : bool suggestionAllowed) const
3277 : {
3278 9794 : auto ret = m_subAlgRegistry.Instantiate(name);
3279 19588 : auto childCallPath = m_callPath;
3280 9794 : childCallPath.push_back(name);
3281 9794 : if (!ret)
3282 : {
3283 914 : ret = GDALGlobalAlgorithmRegistry::GetSingleton()
3284 914 : .InstantiateDeclaredSubAlgorithm(childCallPath);
3285 : }
3286 9794 : if (ret)
3287 : {
3288 9659 : ret->SetCallPath(childCallPath);
3289 : }
3290 135 : else if (suggestionAllowed)
3291 : {
3292 58 : std::string bestCandidate;
3293 29 : size_t bestDistance = std::numeric_limits<size_t>::max();
3294 470 : for (const std::string &candidate : GetSubAlgorithmNames())
3295 : {
3296 : const size_t distance =
3297 441 : CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
3298 : /* transpositionAllowed = */ true);
3299 441 : if (distance < bestDistance)
3300 : {
3301 71 : bestCandidate = candidate;
3302 71 : bestDistance = distance;
3303 : }
3304 370 : else if (distance == bestDistance)
3305 : {
3306 45 : bestCandidate.clear();
3307 : }
3308 : }
3309 29 : if (!bestCandidate.empty() && bestDistance <= 2)
3310 : {
3311 4 : CPLError(CE_Failure, CPLE_AppDefined,
3312 : "Algorithm '%s' is unknown. Do you mean '%s'?",
3313 : name.c_str(), bestCandidate.c_str());
3314 : }
3315 : }
3316 19588 : return ret;
3317 : }
3318 :
3319 : /************************************************************************/
3320 : /* GDALAlgorithm::GetSuggestionForArgumentName() */
3321 : /************************************************************************/
3322 :
3323 : std::string
3324 36 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
3325 : {
3326 36 : if (osName.size() >= 3)
3327 : {
3328 33 : std::string bestCandidate;
3329 33 : size_t bestDistance = std::numeric_limits<size_t>::max();
3330 677 : for (const auto &[key, value] : m_mapLongNameToArg)
3331 : {
3332 644 : CPL_IGNORE_RET_VAL(value);
3333 644 : const size_t distance = CPLLevenshteinDistance(
3334 : osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
3335 644 : if (distance < bestDistance)
3336 : {
3337 85 : bestCandidate = key;
3338 85 : bestDistance = distance;
3339 : }
3340 559 : else if (distance == bestDistance)
3341 : {
3342 77 : bestCandidate.clear();
3343 : }
3344 : }
3345 47 : if (!bestCandidate.empty() &&
3346 14 : bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
3347 : {
3348 5 : return bestCandidate;
3349 : }
3350 : }
3351 31 : return std::string();
3352 : }
3353 :
3354 : /************************************************************************/
3355 : /* GDALAlgorithm::IsKnownOutputRelatedBooleanArgName() */
3356 : /************************************************************************/
3357 :
3358 : /* static */
3359 22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
3360 : {
3361 66 : return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
3362 66 : osName == GDAL_ARG_NAME_OVERWRITE ||
3363 44 : osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
3364 : }
3365 :
3366 : /************************************************************************/
3367 : /* GDALAlgorithm::HasOutputString() */
3368 : /************************************************************************/
3369 :
3370 67 : bool GDALAlgorithm::HasOutputString() const
3371 : {
3372 67 : auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
3373 67 : return outputStringArg && outputStringArg->IsOutput();
3374 : }
3375 :
3376 : /************************************************************************/
3377 : /* GDALAlgorithm::GetArg() */
3378 : /************************************************************************/
3379 :
3380 446768 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
3381 : bool suggestionAllowed, bool isConst)
3382 : {
3383 446768 : const auto nPos = osName.find_first_not_of('-');
3384 446768 : if (nPos == std::string::npos)
3385 23 : return nullptr;
3386 893490 : std::string osKey = osName.substr(nPos);
3387 : {
3388 446745 : const auto oIter = m_mapLongNameToArg.find(osKey);
3389 446745 : if (oIter != m_mapLongNameToArg.end())
3390 416605 : return oIter->second;
3391 : }
3392 : {
3393 30140 : const auto oIter = m_mapShortNameToArg.find(osKey);
3394 30140 : if (oIter != m_mapShortNameToArg.end())
3395 6 : return oIter->second;
3396 : }
3397 :
3398 30134 : if (!isConst && m_arbitraryLongNameArgsAllowed)
3399 : {
3400 22 : const auto nDotPos = osKey.find('.');
3401 : const std::string osKeyEnd =
3402 22 : nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
3403 22 : if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
3404 : {
3405 : m_arbitraryLongNameArgsValuesBool.emplace_back(
3406 0 : std::make_unique<bool>());
3407 0 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3408 0 : m_arbitraryLongNameArgsValuesBool.back().get())
3409 0 : .SetUserProvided();
3410 : }
3411 : else
3412 : {
3413 44 : const std::string osKeyInit = osKey;
3414 22 : if (osKey == "oo")
3415 0 : osKey = GDAL_ARG_NAME_OPEN_OPTION;
3416 22 : else if (osKey == "co")
3417 0 : osKey = GDAL_ARG_NAME_CREATION_OPTION;
3418 22 : else if (osKey == "of")
3419 0 : osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
3420 22 : else if (osKey == "if")
3421 0 : osKey = GDAL_ARG_NAME_INPUT_FORMAT;
3422 : m_arbitraryLongNameArgsValuesStr.emplace_back(
3423 22 : std::make_unique<std::string>());
3424 : auto &arg =
3425 44 : AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
3426 44 : m_arbitraryLongNameArgsValuesStr.back().get())
3427 22 : .SetUserProvided();
3428 22 : if (osKey != osKeyInit)
3429 0 : arg.AddAlias(osKeyInit);
3430 : }
3431 22 : const auto oIter = m_mapLongNameToArg.find(osKey);
3432 22 : CPLAssert(oIter != m_mapLongNameToArg.end());
3433 22 : return oIter->second;
3434 : }
3435 :
3436 30112 : if (suggestionAllowed)
3437 : {
3438 12 : const std::string bestCandidate = GetSuggestionForArgumentName(osName);
3439 6 : if (!bestCandidate.empty())
3440 : {
3441 2 : CPLError(CE_Failure, CPLE_AppDefined,
3442 : "Argument '%s' is unknown. Do you mean '%s'?",
3443 : osName.c_str(), bestCandidate.c_str());
3444 : }
3445 : }
3446 :
3447 30112 : return nullptr;
3448 : }
3449 :
3450 : /************************************************************************/
3451 : /* GDALAlgorithm::AddAliasFor() */
3452 : /************************************************************************/
3453 :
3454 : //! @cond Doxygen_Suppress
3455 74578 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
3456 : const std::string &alias)
3457 : {
3458 74578 : if (cpl::contains(m_mapLongNameToArg, alias))
3459 : {
3460 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
3461 : alias.c_str());
3462 : }
3463 : else
3464 : {
3465 74577 : m_mapLongNameToArg[alias] = arg;
3466 : }
3467 74578 : }
3468 :
3469 : //! @endcond
3470 :
3471 : /************************************************************************/
3472 : /* GDALAlgorithm::AddShortNameAliasFor() */
3473 : /************************************************************************/
3474 :
3475 : //! @cond Doxygen_Suppress
3476 48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
3477 : char shortNameAlias)
3478 : {
3479 96 : std::string alias;
3480 48 : alias += shortNameAlias;
3481 48 : if (cpl::contains(m_mapShortNameToArg, alias))
3482 : {
3483 0 : ReportError(CE_Failure, CPLE_AppDefined,
3484 : "Short name '%s' already declared.", alias.c_str());
3485 : }
3486 : else
3487 : {
3488 48 : m_mapShortNameToArg[alias] = arg;
3489 : }
3490 48 : }
3491 :
3492 : //! @endcond
3493 :
3494 : /************************************************************************/
3495 : /* GDALAlgorithm::SetPositional() */
3496 : /************************************************************************/
3497 :
3498 : //! @cond Doxygen_Suppress
3499 21123 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
3500 : {
3501 21123 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
3502 : arg) == m_positionalArgs.end());
3503 21123 : m_positionalArgs.push_back(arg);
3504 21123 : }
3505 :
3506 : //! @endcond
3507 :
3508 : /************************************************************************/
3509 : /* GDALAlgorithm::HasSubAlgorithms() */
3510 : /************************************************************************/
3511 :
3512 12344 : bool GDALAlgorithm::HasSubAlgorithms() const
3513 : {
3514 12344 : if (!m_subAlgRegistry.empty())
3515 3151 : return true;
3516 9193 : return !GDALGlobalAlgorithmRegistry::GetSingleton()
3517 18386 : .GetDeclaredSubAlgorithmNames(m_callPath)
3518 9193 : .empty();
3519 : }
3520 :
3521 : /************************************************************************/
3522 : /* GDALAlgorithm::GetSubAlgorithmNames() */
3523 : /************************************************************************/
3524 :
3525 1328 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
3526 : {
3527 1328 : std::vector<std::string> ret = m_subAlgRegistry.GetNames();
3528 1328 : const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
3529 2656 : .GetDeclaredSubAlgorithmNames(m_callPath);
3530 1328 : ret.insert(ret.end(), other.begin(), other.end());
3531 1328 : if (!other.empty())
3532 415 : std::sort(ret.begin(), ret.end());
3533 2656 : return ret;
3534 : }
3535 :
3536 : /************************************************************************/
3537 : /* GDALAlgorithm::AddArg() */
3538 : /************************************************************************/
3539 :
3540 : GDALInConstructionAlgorithmArg &
3541 300327 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
3542 : {
3543 300327 : auto argRaw = arg.get();
3544 300327 : const auto &longName = argRaw->GetName();
3545 300327 : if (!longName.empty())
3546 : {
3547 300314 : if (longName[0] == '-')
3548 : {
3549 1 : ReportError(CE_Failure, CPLE_AppDefined,
3550 : "Long name '%s' should not start with '-'",
3551 : longName.c_str());
3552 : }
3553 300314 : if (longName.find('=') != std::string::npos)
3554 : {
3555 1 : ReportError(CE_Failure, CPLE_AppDefined,
3556 : "Long name '%s' should not contain a '=' character",
3557 : longName.c_str());
3558 : }
3559 300314 : if (cpl::contains(m_mapLongNameToArg, longName))
3560 : {
3561 1 : ReportError(CE_Failure, CPLE_AppDefined,
3562 : "Long name '%s' already declared", longName.c_str());
3563 : }
3564 300314 : m_mapLongNameToArg[longName] = argRaw;
3565 : }
3566 300327 : const auto &shortName = argRaw->GetShortName();
3567 300327 : if (!shortName.empty())
3568 : {
3569 145844 : if (shortName.size() != 1 ||
3570 72922 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
3571 64 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
3572 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
3573 : {
3574 1 : ReportError(CE_Failure, CPLE_AppDefined,
3575 : "Short name '%s' should be a single letter or digit",
3576 : shortName.c_str());
3577 : }
3578 72922 : if (cpl::contains(m_mapShortNameToArg, shortName))
3579 : {
3580 1 : ReportError(CE_Failure, CPLE_AppDefined,
3581 : "Short name '%s' already declared", shortName.c_str());
3582 : }
3583 72922 : m_mapShortNameToArg[shortName] = argRaw;
3584 : }
3585 300327 : m_args.emplace_back(std::move(arg));
3586 : return *(
3587 300327 : cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
3588 : }
3589 :
3590 : GDALInConstructionAlgorithmArg &
3591 134114 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3592 : const std::string &helpMessage, bool *pValue)
3593 : {
3594 134114 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3595 : this,
3596 268228 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
3597 268228 : pValue));
3598 : }
3599 :
3600 : GDALInConstructionAlgorithmArg &
3601 48212 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3602 : const std::string &helpMessage, std::string *pValue)
3603 : {
3604 48212 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3605 : this,
3606 96424 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
3607 96424 : pValue));
3608 : }
3609 :
3610 : GDALInConstructionAlgorithmArg &
3611 11725 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3612 : const std::string &helpMessage, int *pValue)
3613 : {
3614 11725 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3615 : this,
3616 23450 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
3617 23450 : pValue));
3618 : }
3619 :
3620 : GDALInConstructionAlgorithmArg &
3621 10048 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3622 : const std::string &helpMessage, double *pValue)
3623 : {
3624 10048 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3625 : this,
3626 20096 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
3627 20096 : pValue));
3628 : }
3629 :
3630 : GDALInConstructionAlgorithmArg &
3631 11427 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3632 : const std::string &helpMessage,
3633 : GDALArgDatasetValue *pValue, GDALArgDatasetType type)
3634 : {
3635 22854 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3636 : this,
3637 22854 : GDALAlgorithmArgDecl(longName, chShortName,
3638 : helpMessage, GAAT_DATASET),
3639 11427 : pValue))
3640 11427 : .SetDatasetType(type);
3641 11427 : pValue->SetOwnerArgument(&arg);
3642 11427 : return arg;
3643 : }
3644 :
3645 : GDALInConstructionAlgorithmArg &
3646 64012 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3647 : const std::string &helpMessage,
3648 : std::vector<std::string> *pValue)
3649 : {
3650 64012 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3651 : this,
3652 128024 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3653 : GAAT_STRING_LIST),
3654 128024 : pValue));
3655 : }
3656 :
3657 : GDALInConstructionAlgorithmArg &
3658 2177 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3659 : const std::string &helpMessage, std::vector<int> *pValue)
3660 : {
3661 2177 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3662 : this,
3663 4354 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3664 : GAAT_INTEGER_LIST),
3665 4354 : pValue));
3666 : }
3667 :
3668 : GDALInConstructionAlgorithmArg &
3669 5111 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3670 : const std::string &helpMessage,
3671 : std::vector<double> *pValue)
3672 : {
3673 5111 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3674 : this,
3675 10222 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3676 : GAAT_REAL_LIST),
3677 10222 : pValue));
3678 : }
3679 :
3680 : GDALInConstructionAlgorithmArg &
3681 13501 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
3682 : const std::string &helpMessage,
3683 : std::vector<GDALArgDatasetValue> *pValue,
3684 : GDALArgDatasetType type)
3685 : {
3686 27002 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
3687 : this,
3688 27002 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
3689 : GAAT_DATASET_LIST),
3690 13501 : pValue))
3691 27002 : .SetDatasetType(type);
3692 : }
3693 :
3694 : /************************************************************************/
3695 : /* MsgOrDefault() */
3696 : /************************************************************************/
3697 :
3698 100987 : inline const char *MsgOrDefault(const char *helpMessage,
3699 : const char *defaultMessage)
3700 : {
3701 100987 : return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
3702 : }
3703 :
3704 : /************************************************************************/
3705 : /* GDALAlgorithm::SetAutoCompleteFunctionForFilename() */
3706 : /************************************************************************/
3707 :
3708 : /* static */
3709 16422 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
3710 : GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
3711 : {
3712 : arg.SetAutoCompleteFunction(
3713 7 : [&arg,
3714 2442 : type](const std::string ¤tValue) -> std::vector<std::string>
3715 : {
3716 14 : std::vector<std::string> oRet;
3717 :
3718 7 : if (arg.IsHidden())
3719 0 : return oRet;
3720 :
3721 : {
3722 7 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3723 : VSIStatBufL sStat;
3724 10 : if (!currentValue.empty() && currentValue.back() != '/' &&
3725 3 : VSIStatL(currentValue.c_str(), &sStat) == 0)
3726 : {
3727 0 : return oRet;
3728 : }
3729 : }
3730 :
3731 7 : auto poDM = GetGDALDriverManager();
3732 14 : std::set<std::string> oExtensions;
3733 7 : if (type)
3734 : {
3735 1362 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
3736 : {
3737 1356 : auto poDriver = poDM->GetDriver(i);
3738 3842 : if (((type & GDAL_OF_RASTER) != 0 &&
3739 1130 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
3740 581 : ((type & GDAL_OF_VECTOR) != 0 &&
3741 2848 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
3742 491 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
3743 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
3744 : {
3745 : const char *pszExtensions =
3746 865 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
3747 865 : if (pszExtensions)
3748 : {
3749 : const CPLStringList aosExts(
3750 1142 : CSLTokenizeString2(pszExtensions, " ", 0));
3751 1291 : for (const char *pszExt : cpl::Iterate(aosExts))
3752 720 : oExtensions.insert(CPLString(pszExt).tolower());
3753 : }
3754 : }
3755 : }
3756 : }
3757 :
3758 14 : std::string osDir;
3759 14 : const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
3760 14 : std::string osPrefix;
3761 7 : if (STARTS_WITH(currentValue.c_str(), "/vsi"))
3762 : {
3763 79 : for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
3764 : {
3765 78 : if (STARTS_WITH(currentValue.c_str(), pszPrefix))
3766 : {
3767 2 : osPrefix = pszPrefix;
3768 2 : break;
3769 : }
3770 : }
3771 3 : if (osPrefix.empty())
3772 1 : return aosVSIPrefixes;
3773 2 : if (currentValue == osPrefix)
3774 1 : osDir = osPrefix;
3775 : }
3776 6 : if (osDir.empty())
3777 : {
3778 5 : osDir = CPLGetDirnameSafe(currentValue.c_str());
3779 5 : if (!osPrefix.empty() && osDir.size() < osPrefix.size())
3780 0 : osDir = std::move(osPrefix);
3781 : }
3782 :
3783 6 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
3784 12 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
3785 6 : if (currentValue.empty())
3786 1 : osDir.clear();
3787 : const std::string currentFilename =
3788 12 : CPLGetFilename(currentValue.c_str());
3789 6 : if (psDir)
3790 : {
3791 442 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
3792 : {
3793 437 : if ((currentFilename.empty() ||
3794 218 : STARTS_WITH(psEntry->pszName,
3795 220 : currentFilename.c_str())) &&
3796 220 : strcmp(psEntry->pszName, ".") != 0 &&
3797 1313 : strcmp(psEntry->pszName, "..") != 0 &&
3798 220 : (oExtensions.empty() ||
3799 219 : !strstr(psEntry->pszName, ".aux.xml")))
3800 : {
3801 870 : if (oExtensions.empty() ||
3802 217 : cpl::contains(
3803 : oExtensions,
3804 435 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
3805 652 : .tolower()) ||
3806 186 : VSI_ISDIR(psEntry->nMode))
3807 : {
3808 72 : std::string osVal;
3809 36 : if (osDir.empty() || osDir == ".")
3810 4 : osVal = psEntry->pszName;
3811 : else
3812 64 : osVal = CPLFormFilenameSafe(
3813 64 : osDir.c_str(), psEntry->pszName, nullptr);
3814 36 : if (VSI_ISDIR(psEntry->nMode))
3815 4 : osVal += osSep;
3816 36 : oRet.push_back(std::move(osVal));
3817 : }
3818 : }
3819 437 : }
3820 5 : VSICloseDir(psDir);
3821 : }
3822 6 : return oRet;
3823 16422 : });
3824 16422 : }
3825 :
3826 : /************************************************************************/
3827 : /* GDALAlgorithm::AddInputDatasetArg() */
3828 : /************************************************************************/
3829 :
3830 594 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3831 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3832 : bool positionalAndRequired, const char *helpMessage)
3833 : {
3834 : auto &arg = AddArg(
3835 : GDAL_ARG_NAME_INPUT, 'i',
3836 : MsgOrDefault(helpMessage,
3837 : CPLSPrintf("Input %s dataset",
3838 594 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3839 1188 : pValue, type);
3840 594 : if (positionalAndRequired)
3841 587 : arg.SetPositional().SetRequired();
3842 :
3843 594 : SetAutoCompleteFunctionForFilename(arg, type);
3844 :
3845 594 : AddValidationAction(
3846 130 : [pValue]()
3847 : {
3848 129 : if (pValue->GetName() == "-")
3849 1 : pValue->Set("/vsistdin/");
3850 129 : return true;
3851 : });
3852 :
3853 594 : return arg;
3854 : }
3855 :
3856 : /************************************************************************/
3857 : /* GDALAlgorithm::AddInputDatasetArg() */
3858 : /************************************************************************/
3859 :
3860 13032 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
3861 : std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
3862 : bool positionalAndRequired, const char *helpMessage)
3863 : {
3864 : auto &arg =
3865 : AddArg(GDAL_ARG_NAME_INPUT, 'i',
3866 : MsgOrDefault(
3867 : helpMessage,
3868 : CPLSPrintf("Input %s datasets",
3869 13032 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3870 39096 : pValue, type)
3871 13032 : .SetPackedValuesAllowed(false);
3872 13032 : if (positionalAndRequired)
3873 1715 : arg.SetPositional().SetRequired();
3874 :
3875 13032 : SetAutoCompleteFunctionForFilename(arg, type);
3876 :
3877 13032 : AddValidationAction(
3878 5933 : [pValue]()
3879 : {
3880 11289 : for (auto &val : *pValue)
3881 : {
3882 5356 : if (val.GetName() == "-")
3883 1 : val.Set("/vsistdin/");
3884 : }
3885 5933 : return true;
3886 : });
3887 13032 : return arg;
3888 : }
3889 :
3890 : /************************************************************************/
3891 : /* GDALAlgorithm::AddOutputDatasetArg() */
3892 : /************************************************************************/
3893 :
3894 8095 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
3895 : GDALArgDatasetValue *pValue, GDALArgDatasetType type,
3896 : bool positionalAndRequired, const char *helpMessage)
3897 : {
3898 : auto &arg =
3899 : AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
3900 : MsgOrDefault(
3901 : helpMessage,
3902 : CPLSPrintf("Output %s dataset",
3903 8095 : GDALAlgorithmArgDatasetTypeName(type).c_str())),
3904 24285 : pValue, type)
3905 8095 : .SetIsInput(true)
3906 8095 : .SetIsOutput(true)
3907 8095 : .SetDatasetInputFlags(GADV_NAME)
3908 8095 : .SetDatasetOutputFlags(GADV_OBJECT);
3909 8095 : if (positionalAndRequired)
3910 4251 : arg.SetPositional().SetRequired();
3911 :
3912 8095 : AddValidationAction(
3913 11243 : [this, &arg, pValue]()
3914 : {
3915 3465 : if (pValue->GetName() == "-")
3916 4 : pValue->Set("/vsistdout/");
3917 :
3918 3465 : auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
3919 3417 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
3920 5519 : (!outputFormatArg->IsExplicitlySet() ||
3921 8984 : outputFormatArg->Get<std::string>().empty()) &&
3922 1315 : arg.IsExplicitlySet())
3923 : {
3924 : const auto vrtCompatible =
3925 987 : outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
3926 182 : if (vrtCompatible && !vrtCompatible->empty() &&
3927 1169 : vrtCompatible->front() == "false" &&
3928 1078 : EQUAL(
3929 : CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
3930 : "VRT"))
3931 : {
3932 6 : ReportError(
3933 : CE_Failure, CPLE_NotSupported,
3934 : "VRT output is not supported.%s",
3935 6 : outputFormatArg->GetDescription().find("GDALG") !=
3936 : std::string::npos
3937 : ? " Consider using the GDALG driver instead (files "
3938 : "with .gdalg.json extension)"
3939 : : "");
3940 6 : return false;
3941 : }
3942 981 : else if (pValue->GetName().size() > strlen(".gdalg.json") &&
3943 1939 : EQUAL(pValue->GetName()
3944 : .substr(pValue->GetName().size() -
3945 : strlen(".gdalg.json"))
3946 : .c_str(),
3947 2920 : ".gdalg.json") &&
3948 27 : outputFormatArg->GetDescription().find("GDALG") ==
3949 : std::string::npos)
3950 : {
3951 0 : ReportError(CE_Failure, CPLE_NotSupported,
3952 : "GDALG output is not supported");
3953 0 : return false;
3954 : }
3955 : }
3956 3459 : return true;
3957 : });
3958 :
3959 8095 : return arg;
3960 : }
3961 :
3962 : /************************************************************************/
3963 : /* GDALAlgorithm::AddOverwriteArg() */
3964 : /************************************************************************/
3965 :
3966 : GDALInConstructionAlgorithmArg &
3967 8011 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
3968 : {
3969 : return AddArg(
3970 : GDAL_ARG_NAME_OVERWRITE, 0,
3971 : MsgOrDefault(
3972 : helpMessage,
3973 : _("Whether overwriting existing output dataset is allowed")),
3974 16022 : pValue)
3975 16022 : .SetDefault(false);
3976 : }
3977 :
3978 : /************************************************************************/
3979 : /* GDALAlgorithm::AddOverwriteLayerArg() */
3980 : /************************************************************************/
3981 :
3982 : GDALInConstructionAlgorithmArg &
3983 3322 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
3984 : {
3985 3322 : AddValidationAction(
3986 1456 : [this]
3987 : {
3988 1455 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
3989 1455 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
3990 : {
3991 1 : ReportError(CE_Failure, CPLE_AppDefined,
3992 : "--update argument must exist for "
3993 : "--overwrite-layer, even if hidden");
3994 1 : return false;
3995 : }
3996 1454 : return true;
3997 : });
3998 : return AddArg(
3999 : GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
4000 : MsgOrDefault(
4001 : helpMessage,
4002 : _("Whether overwriting existing output layer is allowed")),
4003 6644 : pValue)
4004 3322 : .SetDefault(false)
4005 : .AddAction(
4006 19 : [this]
4007 : {
4008 19 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4009 19 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4010 : {
4011 19 : updateArg->Set(true);
4012 : }
4013 6663 : });
4014 : }
4015 :
4016 : /************************************************************************/
4017 : /* GDALAlgorithm::AddUpdateArg() */
4018 : /************************************************************************/
4019 :
4020 : GDALInConstructionAlgorithmArg &
4021 3873 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
4022 : {
4023 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
4024 : MsgOrDefault(
4025 : helpMessage,
4026 : _("Whether to open existing dataset in update mode")),
4027 7746 : pValue)
4028 7746 : .SetDefault(false);
4029 : }
4030 :
4031 : /************************************************************************/
4032 : /* GDALAlgorithm::AddAppendLayerArg() */
4033 : /************************************************************************/
4034 :
4035 : GDALInConstructionAlgorithmArg &
4036 3105 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
4037 : {
4038 3105 : AddValidationAction(
4039 1412 : [this]
4040 : {
4041 1411 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4042 1411 : if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
4043 : {
4044 1 : ReportError(CE_Failure, CPLE_AppDefined,
4045 : "--update argument must exist for --append, even "
4046 : "if hidden");
4047 1 : return false;
4048 : }
4049 1410 : return true;
4050 : });
4051 : return AddArg(GDAL_ARG_NAME_APPEND, 0,
4052 : MsgOrDefault(
4053 : helpMessage,
4054 : _("Whether appending to existing layer is allowed")),
4055 6210 : pValue)
4056 3105 : .SetDefault(false)
4057 : .AddAction(
4058 25 : [this]
4059 : {
4060 25 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4061 25 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
4062 : {
4063 25 : updateArg->Set(true);
4064 : }
4065 6235 : });
4066 : }
4067 :
4068 : /************************************************************************/
4069 : /* GDALAlgorithm::AddOptionsSuggestions() */
4070 : /************************************************************************/
4071 :
4072 : /* static */
4073 29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
4074 : const std::string ¤tValue,
4075 : std::vector<std::string> &oRet)
4076 : {
4077 29 : if (!pszXML)
4078 0 : return false;
4079 58 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
4080 29 : if (!poTree)
4081 0 : return false;
4082 :
4083 58 : std::string typedOptionName = currentValue;
4084 29 : const auto posEqual = typedOptionName.find('=');
4085 58 : std::string typedValue;
4086 29 : if (posEqual != 0 && posEqual != std::string::npos)
4087 : {
4088 2 : typedValue = currentValue.substr(posEqual + 1);
4089 2 : typedOptionName.resize(posEqual);
4090 : }
4091 :
4092 405 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4093 376 : psChild = psChild->psNext)
4094 : {
4095 389 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4096 402 : if (pszName && typedOptionName == pszName &&
4097 13 : (strcmp(psChild->pszValue, "Option") == 0 ||
4098 2 : strcmp(psChild->pszValue, "Argument") == 0))
4099 : {
4100 13 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
4101 13 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
4102 13 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
4103 13 : if (EQUAL(pszType, "string-select"))
4104 : {
4105 90 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
4106 85 : psChild2 = psChild2->psNext)
4107 : {
4108 85 : if (EQUAL(psChild2->pszValue, "Value"))
4109 : {
4110 75 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
4111 : }
4112 : }
4113 : }
4114 8 : else if (EQUAL(pszType, "boolean"))
4115 : {
4116 3 : if (typedValue == "YES" || typedValue == "NO")
4117 : {
4118 1 : oRet.push_back(currentValue);
4119 1 : return true;
4120 : }
4121 2 : oRet.push_back("NO");
4122 2 : oRet.push_back("YES");
4123 : }
4124 5 : else if (EQUAL(pszType, "int"))
4125 : {
4126 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
4127 2 : atoi(pszMax) - atoi(pszMin) < 25)
4128 : {
4129 1 : const int nMax = atoi(pszMax);
4130 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
4131 12 : oRet.push_back(std::to_string(i));
4132 : }
4133 : }
4134 :
4135 12 : if (oRet.empty())
4136 : {
4137 4 : if (pszMin && pszMax)
4138 : {
4139 1 : oRet.push_back(std::string("##"));
4140 2 : oRet.push_back(std::string("validity range: [")
4141 1 : .append(pszMin)
4142 1 : .append(",")
4143 1 : .append(pszMax)
4144 1 : .append("]"));
4145 : }
4146 3 : else if (pszMin)
4147 : {
4148 1 : oRet.push_back(std::string("##"));
4149 1 : oRet.push_back(
4150 1 : std::string("validity range: >= ").append(pszMin));
4151 : }
4152 2 : else if (pszMax)
4153 : {
4154 1 : oRet.push_back(std::string("##"));
4155 1 : oRet.push_back(
4156 1 : std::string("validity range: <= ").append(pszMax));
4157 : }
4158 1 : else if (const char *pszDescription =
4159 1 : CPLGetXMLValue(psChild, "description", nullptr))
4160 : {
4161 1 : oRet.push_back(std::string("##"));
4162 2 : oRet.push_back(std::string("type: ")
4163 1 : .append(pszType)
4164 1 : .append(", description: ")
4165 1 : .append(pszDescription));
4166 : }
4167 : }
4168 :
4169 12 : return true;
4170 : }
4171 : }
4172 :
4173 319 : for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
4174 303 : psChild = psChild->psNext)
4175 : {
4176 303 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
4177 303 : if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
4178 5 : strcmp(psChild->pszValue, "Argument") == 0))
4179 : {
4180 300 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
4181 300 : if (!pszScope ||
4182 40 : (EQUAL(pszScope, "raster") &&
4183 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
4184 20 : (EQUAL(pszScope, "vector") &&
4185 0 : (datasetType & GDAL_OF_VECTOR) != 0))
4186 : {
4187 280 : oRet.push_back(std::string(pszName).append("="));
4188 : }
4189 : }
4190 : }
4191 :
4192 16 : return false;
4193 : }
4194 :
4195 : /************************************************************************/
4196 : /* GDALAlgorithm::OpenOptionCompleteFunction() */
4197 : /************************************************************************/
4198 :
4199 : //! @cond Doxygen_Suppress
4200 : std::vector<std::string>
4201 2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string ¤tValue) const
4202 : {
4203 2 : std::vector<std::string> oRet;
4204 :
4205 2 : int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
4206 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4207 4 : if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
4208 2 : inputArg->GetType() == GAAT_DATASET_LIST))
4209 : {
4210 2 : datasetType = inputArg->GetDatasetType();
4211 : }
4212 :
4213 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4214 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
4215 2 : inputFormat->IsExplicitlySet())
4216 : {
4217 : const auto &aosAllowedDrivers =
4218 1 : inputFormat->Get<std::vector<std::string>>();
4219 1 : if (aosAllowedDrivers.size() == 1)
4220 : {
4221 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
4222 1 : aosAllowedDrivers[0].c_str());
4223 1 : if (poDriver)
4224 : {
4225 1 : AddOptionsSuggestions(
4226 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
4227 : datasetType, currentValue, oRet);
4228 : }
4229 1 : return oRet;
4230 : }
4231 : }
4232 :
4233 1 : const auto AddSuggestions = [datasetType, ¤tValue,
4234 369 : &oRet](const GDALArgDatasetValue &datasetValue)
4235 : {
4236 1 : auto poDM = GetGDALDriverManager();
4237 :
4238 1 : const auto &osDSName = datasetValue.GetName();
4239 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
4240 1 : if (!osExt.empty())
4241 : {
4242 1 : std::set<std::string> oVisitedExtensions;
4243 227 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4244 : {
4245 226 : auto poDriver = poDM->GetDriver(i);
4246 678 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
4247 226 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
4248 71 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
4249 452 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
4250 71 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
4251 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
4252 : {
4253 : const char *pszExtensions =
4254 155 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
4255 155 : if (pszExtensions)
4256 : {
4257 : const CPLStringList aosExts(
4258 102 : CSLTokenizeString2(pszExtensions, " ", 0));
4259 225 : for (const char *pszExt : cpl::Iterate(aosExts))
4260 : {
4261 127 : if (EQUAL(pszExt, osExt.c_str()) &&
4262 3 : !cpl::contains(oVisitedExtensions, pszExt))
4263 : {
4264 1 : oVisitedExtensions.insert(pszExt);
4265 1 : if (AddOptionsSuggestions(
4266 : poDriver->GetMetadataItem(
4267 1 : GDAL_DMD_OPENOPTIONLIST),
4268 : datasetType, currentValue, oRet))
4269 : {
4270 0 : return;
4271 : }
4272 1 : break;
4273 : }
4274 : }
4275 : }
4276 : }
4277 : }
4278 : }
4279 1 : };
4280 :
4281 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
4282 : {
4283 0 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
4284 0 : AddSuggestions(datasetValue);
4285 : }
4286 1 : else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4287 : {
4288 1 : auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4289 1 : if (datasetValues.size() == 1)
4290 1 : AddSuggestions(datasetValues[0]);
4291 : }
4292 :
4293 1 : return oRet;
4294 : }
4295 :
4296 : //! @endcond
4297 :
4298 : /************************************************************************/
4299 : /* GDALAlgorithm::AddOpenOptionsArg() */
4300 : /************************************************************************/
4301 :
4302 : GDALInConstructionAlgorithmArg &
4303 8905 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
4304 : const char *helpMessage)
4305 : {
4306 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
4307 17810 : MsgOrDefault(helpMessage, _("Open options")), pValue)
4308 17810 : .AddAlias("oo")
4309 17810 : .SetMetaVar("<KEY>=<VALUE>")
4310 8905 : .SetPackedValuesAllowed(false)
4311 8905 : .SetCategory(GAAC_ADVANCED);
4312 :
4313 21 : arg.AddValidationAction([this, &arg]()
4314 8926 : { return ParseAndValidateKeyValue(arg); });
4315 :
4316 : arg.SetAutoCompleteFunction(
4317 2 : [this](const std::string ¤tValue)
4318 8907 : { return OpenOptionCompleteFunction(currentValue); });
4319 :
4320 8905 : return arg;
4321 : }
4322 :
4323 : /************************************************************************/
4324 : /* GDALAlgorithm::AddOutputOpenOptionsArg() */
4325 : /************************************************************************/
4326 :
4327 : GDALInConstructionAlgorithmArg &
4328 3093 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
4329 : const char *helpMessage)
4330 : {
4331 : auto &arg =
4332 : AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
4333 6186 : MsgOrDefault(helpMessage, _("Output open options")), pValue)
4334 6186 : .AddAlias("output-oo")
4335 6186 : .SetMetaVar("<KEY>=<VALUE>")
4336 3093 : .SetPackedValuesAllowed(false)
4337 3093 : .SetCategory(GAAC_ADVANCED);
4338 :
4339 0 : arg.AddValidationAction([this, &arg]()
4340 3093 : { return ParseAndValidateKeyValue(arg); });
4341 :
4342 : arg.SetAutoCompleteFunction(
4343 0 : [this](const std::string ¤tValue)
4344 3093 : { return OpenOptionCompleteFunction(currentValue); });
4345 :
4346 3093 : return arg;
4347 : }
4348 :
4349 : /************************************************************************/
4350 : /* ValidateFormat() */
4351 : /************************************************************************/
4352 :
4353 4480 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
4354 : bool bStreamAllowed,
4355 : bool bGDALGAllowed) const
4356 : {
4357 4480 : if (arg.GetChoices().empty())
4358 : {
4359 : const auto Validate =
4360 19163 : [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
4361 : {
4362 4375 : if (const auto extraFormats =
4363 4375 : arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4364 : {
4365 60 : for (const auto &extraFormat : *extraFormats)
4366 : {
4367 48 : if (EQUAL(val.c_str(), extraFormat.c_str()))
4368 14 : return true;
4369 : }
4370 : }
4371 :
4372 4361 : if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
4373 1749 : return true;
4374 :
4375 2618 : if (EQUAL(val.c_str(), "GDALG") &&
4376 6 : arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
4377 : {
4378 2 : if (bGDALGAllowed)
4379 : {
4380 2 : return true;
4381 : }
4382 : else
4383 : {
4384 0 : ReportError(CE_Failure, CPLE_NotSupported,
4385 : "GDALG output is not supported.");
4386 0 : return false;
4387 : }
4388 : }
4389 :
4390 : const auto vrtCompatible =
4391 2610 : arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4392 440 : if (vrtCompatible && !vrtCompatible->empty() &&
4393 3050 : vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
4394 : {
4395 7 : ReportError(CE_Failure, CPLE_NotSupported,
4396 : "VRT output is not supported.%s",
4397 : bGDALGAllowed
4398 : ? " Consider using the GDALG driver instead "
4399 : "(files with .gdalg.json extension)."
4400 : : "");
4401 7 : return false;
4402 : }
4403 :
4404 : const auto allowedFormats =
4405 2603 : arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4406 2624 : if (allowedFormats && !allowedFormats->empty() &&
4407 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4408 2624 : val) != allowedFormats->end())
4409 : {
4410 9 : return true;
4411 : }
4412 :
4413 : const auto excludedFormats =
4414 2594 : arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4415 2606 : if (excludedFormats && !excludedFormats->empty() &&
4416 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4417 2606 : val) != excludedFormats->end())
4418 : {
4419 0 : ReportError(CE_Failure, CPLE_NotSupported,
4420 : "%s output is not supported.", val.c_str());
4421 0 : return false;
4422 : }
4423 :
4424 2594 : auto hDriver = GDALGetDriverByName(val.c_str());
4425 2594 : if (!hDriver)
4426 : {
4427 : auto poMissingDriver =
4428 4 : GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
4429 4 : if (poMissingDriver)
4430 : {
4431 : const std::string msg =
4432 0 : GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
4433 0 : ReportError(CE_Failure, CPLE_AppDefined,
4434 : "Invalid value for argument '%s'. Driver '%s' "
4435 : "not found but is known. However plugin %s",
4436 0 : arg.GetName().c_str(), val.c_str(),
4437 : msg.c_str());
4438 : }
4439 : else
4440 : {
4441 8 : ReportError(CE_Failure, CPLE_AppDefined,
4442 : "Invalid value for argument '%s'. Driver '%s' "
4443 : "does not exist.",
4444 4 : arg.GetName().c_str(), val.c_str());
4445 : }
4446 4 : return false;
4447 : }
4448 :
4449 2590 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4450 2590 : if (caps)
4451 : {
4452 7776 : for (const std::string &cap : *caps)
4453 : {
4454 : const char *pszVal =
4455 5211 : GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
4456 5211 : if (!(pszVal && pszVal[0]))
4457 : {
4458 1529 : if (cap == GDAL_DCAP_CREATECOPY &&
4459 0 : std::find(caps->begin(), caps->end(),
4460 763 : GDAL_DCAP_RASTER) != caps->end() &&
4461 763 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
4462 1529 : nullptr) &&
4463 763 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
4464 : nullptr))
4465 : {
4466 : // if it supports Create, it supports CreateCopy
4467 : }
4468 3 : else if (cap == GDAL_DMD_EXTENSIONS)
4469 : {
4470 2 : ReportError(
4471 : CE_Failure, CPLE_AppDefined,
4472 : "Invalid value for argument '%s'. Driver '%s' "
4473 : "does "
4474 : "not advertise any file format extension.",
4475 1 : arg.GetName().c_str(), val.c_str());
4476 3 : return false;
4477 : }
4478 : else
4479 : {
4480 2 : if (cap == GDAL_DCAP_CREATE)
4481 : {
4482 1 : auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
4483 1 : if (updateArg &&
4484 2 : updateArg->GetType() == GAAT_BOOLEAN &&
4485 1 : updateArg->IsExplicitlySet())
4486 : {
4487 0 : continue;
4488 : }
4489 :
4490 2 : ReportError(
4491 : CE_Failure, CPLE_AppDefined,
4492 : "Invalid value for argument '%s'. "
4493 : "Driver '%s' does not have write support.",
4494 1 : arg.GetName().c_str(), val.c_str());
4495 1 : return false;
4496 : }
4497 : else
4498 : {
4499 2 : ReportError(
4500 : CE_Failure, CPLE_AppDefined,
4501 : "Invalid value for argument '%s'. Driver "
4502 : "'%s' "
4503 : "does "
4504 : "not expose the required '%s' capability.",
4505 1 : arg.GetName().c_str(), val.c_str(),
4506 : cap.c_str());
4507 1 : return false;
4508 : }
4509 : }
4510 : }
4511 : }
4512 : }
4513 2587 : return true;
4514 4378 : };
4515 :
4516 4378 : if (arg.GetType() == GAAT_STRING)
4517 : {
4518 4368 : return Validate(arg.Get<std::string>());
4519 : }
4520 12 : else if (arg.GetType() == GAAT_STRING_LIST)
4521 : {
4522 19 : for (const auto &val : arg.Get<std::vector<std::string>>())
4523 : {
4524 9 : if (!Validate(val))
4525 2 : return false;
4526 : }
4527 : }
4528 : }
4529 :
4530 112 : return true;
4531 : }
4532 :
4533 : /************************************************************************/
4534 : /* FormatAutoCompleteFunction() */
4535 : /************************************************************************/
4536 :
4537 : /* static */
4538 7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
4539 : const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
4540 : {
4541 7 : std::vector<std::string> res;
4542 7 : auto poDM = GetGDALDriverManager();
4543 7 : const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
4544 7 : const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
4545 7 : const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
4546 7 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
4547 7 : if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
4548 0 : res = std::move(*extraFormats);
4549 1588 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
4550 : {
4551 1581 : auto poDriver = poDM->GetDriver(i);
4552 :
4553 0 : if (vrtCompatible && !vrtCompatible->empty() &&
4554 1581 : vrtCompatible->front() == "false" &&
4555 0 : EQUAL(poDriver->GetDescription(), "VRT"))
4556 : {
4557 : // do nothing
4558 : }
4559 1581 : else if (allowedFormats && !allowedFormats->empty() &&
4560 0 : std::find(allowedFormats->begin(), allowedFormats->end(),
4561 1581 : poDriver->GetDescription()) != allowedFormats->end())
4562 : {
4563 0 : res.push_back(poDriver->GetDescription());
4564 : }
4565 1581 : else if (excludedFormats && !excludedFormats->empty() &&
4566 0 : std::find(excludedFormats->begin(), excludedFormats->end(),
4567 0 : poDriver->GetDescription()) !=
4568 1581 : excludedFormats->end())
4569 : {
4570 0 : continue;
4571 : }
4572 1581 : else if (caps)
4573 : {
4574 1581 : bool ok = true;
4575 3127 : for (const std::string &cap : *caps)
4576 : {
4577 2355 : if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
4578 : {
4579 0 : if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
4580 0 : !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
4581 : {
4582 0 : ok = false;
4583 0 : break;
4584 : }
4585 : }
4586 2355 : else if (const char *pszVal =
4587 2355 : poDriver->GetMetadataItem(cap.c_str());
4588 1474 : pszVal && pszVal[0])
4589 : {
4590 : }
4591 1269 : else if (cap == GDAL_DCAP_CREATECOPY &&
4592 0 : (std::find(caps->begin(), caps->end(),
4593 388 : GDAL_DCAP_RASTER) != caps->end() &&
4594 1657 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
4595 388 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
4596 : {
4597 : // if it supports Create, it supports CreateCopy
4598 : }
4599 : else
4600 : {
4601 809 : ok = false;
4602 809 : break;
4603 : }
4604 : }
4605 1581 : if (ok)
4606 : {
4607 772 : res.push_back(poDriver->GetDescription());
4608 : }
4609 : }
4610 : }
4611 7 : if (bGDALGAllowed)
4612 4 : res.push_back("GDALG");
4613 7 : return res;
4614 : }
4615 :
4616 : /************************************************************************/
4617 : /* GDALAlgorithm::AddInputFormatsArg() */
4618 : /************************************************************************/
4619 :
4620 : GDALInConstructionAlgorithmArg &
4621 8674 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
4622 : const char *helpMessage)
4623 : {
4624 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
4625 17348 : MsgOrDefault(helpMessage, _("Input formats")), pValue)
4626 17348 : .AddAlias("if")
4627 8674 : .SetCategory(GAAC_ADVANCED);
4628 12 : arg.AddValidationAction([this, &arg]()
4629 8686 : { return ValidateFormat(arg, false, false); });
4630 : arg.SetAutoCompleteFunction(
4631 1 : [&arg](const std::string &)
4632 8675 : { return FormatAutoCompleteFunction(arg, false, false); });
4633 8674 : return arg;
4634 : }
4635 :
4636 : /************************************************************************/
4637 : /* GDALAlgorithm::AddOutputFormatArg() */
4638 : /************************************************************************/
4639 :
4640 : GDALInConstructionAlgorithmArg &
4641 9078 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
4642 : bool bGDALGAllowed, const char *helpMessage)
4643 : {
4644 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
4645 : MsgOrDefault(helpMessage,
4646 : bGDALGAllowed
4647 : ? _("Output format (\"GDALG\" allowed)")
4648 : : _("Output format")),
4649 18156 : pValue)
4650 18156 : .AddAlias("of")
4651 9078 : .AddAlias("format");
4652 : arg.AddValidationAction(
4653 4464 : [this, &arg, bStreamAllowed, bGDALGAllowed]()
4654 13542 : { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
4655 : arg.SetAutoCompleteFunction(
4656 4 : [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
4657 : {
4658 : return FormatAutoCompleteFunction(arg, bStreamAllowed,
4659 4 : bGDALGAllowed);
4660 9078 : });
4661 9078 : return arg;
4662 : }
4663 :
4664 : /************************************************************************/
4665 : /* GDALAlgorithm::AddOutputDataTypeArg() */
4666 : /************************************************************************/
4667 : GDALInConstructionAlgorithmArg &
4668 1721 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
4669 : const char *helpMessage)
4670 : {
4671 : auto &arg =
4672 : AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
4673 3442 : MsgOrDefault(helpMessage, _("Output data type")), pValue)
4674 3442 : .AddAlias("ot")
4675 3442 : .AddAlias("datatype")
4676 5163 : .AddMetadataItem("type", {"GDALDataType"})
4677 : .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
4678 : "UInt64", "Int64", "CInt16", "CInt32", "Float16",
4679 1721 : "Float32", "Float64", "CFloat32", "CFloat64")
4680 1721 : .SetHiddenChoices("Byte");
4681 1721 : return arg;
4682 : }
4683 :
4684 : /************************************************************************/
4685 : /* GDALAlgorithm::AddNodataArg() */
4686 : /************************************************************************/
4687 :
4688 : GDALInConstructionAlgorithmArg &
4689 608 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
4690 : const std::string &optionName,
4691 : const char *helpMessage)
4692 : {
4693 : auto &arg = AddArg(
4694 : optionName, 0,
4695 : MsgOrDefault(helpMessage,
4696 : noneAllowed
4697 : ? _("Assign a specified nodata value to output bands "
4698 : "('none', numeric value, 'nan', 'inf', '-inf')")
4699 : : _("Assign a specified nodata value to output bands "
4700 : "(numeric value, 'nan', 'inf', '-inf')")),
4701 608 : pValue);
4702 : arg.AddValidationAction(
4703 356 : [this, pValue, noneAllowed, optionName]()
4704 : {
4705 77 : if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
4706 : {
4707 67 : char *endptr = nullptr;
4708 67 : CPLStrtod(pValue->c_str(), &endptr);
4709 67 : if (endptr != pValue->c_str() + pValue->size())
4710 : {
4711 1 : ReportError(CE_Failure, CPLE_IllegalArg,
4712 : "Value of '%s' should be %sa "
4713 : "numeric value, 'nan', 'inf' or '-inf'",
4714 : optionName.c_str(),
4715 : noneAllowed ? "'none', " : "");
4716 1 : return false;
4717 : }
4718 : }
4719 76 : return true;
4720 608 : });
4721 608 : return arg;
4722 : }
4723 :
4724 : /************************************************************************/
4725 : /* GDALAlgorithm::AddOutputStringArg() */
4726 : /************************************************************************/
4727 :
4728 : GDALInConstructionAlgorithmArg &
4729 5738 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
4730 : {
4731 : return AddArg(
4732 : GDAL_ARG_NAME_OUTPUT_STRING, 0,
4733 : MsgOrDefault(helpMessage,
4734 : _("Output string, in which the result is placed")),
4735 11476 : pValue)
4736 5738 : .SetHiddenForCLI()
4737 5738 : .SetIsInput(false)
4738 11476 : .SetIsOutput(true);
4739 : }
4740 :
4741 : /************************************************************************/
4742 : /* GDALAlgorithm::AddStdoutArg() */
4743 : /************************************************************************/
4744 :
4745 : GDALInConstructionAlgorithmArg &
4746 1455 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
4747 : {
4748 : return AddArg(GDAL_ARG_NAME_STDOUT, 0,
4749 : MsgOrDefault(helpMessage,
4750 : _("Directly output on stdout. If enabled, "
4751 : "output-string will be empty")),
4752 2910 : pValue)
4753 2910 : .SetHidden();
4754 : }
4755 :
4756 : /************************************************************************/
4757 : /* GDALAlgorithm::AddLayerNameArg() */
4758 : /************************************************************************/
4759 :
4760 : GDALInConstructionAlgorithmArg &
4761 206 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
4762 : {
4763 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4764 206 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4765 : }
4766 :
4767 : /************************************************************************/
4768 : /* GDALAlgorithm::AddArrayNameArg() */
4769 : /************************************************************************/
4770 :
4771 : GDALInConstructionAlgorithmArg &
4772 50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
4773 : {
4774 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
4775 100 : pValue)
4776 2 : .SetAutoCompleteFunction([this](const std::string &)
4777 102 : { return AutoCompleteArrayName(); });
4778 : }
4779 :
4780 : /************************************************************************/
4781 : /* GDALAlgorithm::AddArrayNameArg() */
4782 : /************************************************************************/
4783 :
4784 : GDALInConstructionAlgorithmArg &
4785 76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
4786 : const char *helpMessage)
4787 : {
4788 : return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
4789 152 : pValue)
4790 0 : .SetAutoCompleteFunction([this](const std::string &)
4791 152 : { return AutoCompleteArrayName(); });
4792 : }
4793 :
4794 : /************************************************************************/
4795 : /* GDALAlgorithm::AutoCompleteArrayName() */
4796 : /************************************************************************/
4797 :
4798 2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
4799 : {
4800 2 : std::vector<std::string> ret;
4801 4 : std::string osDSName;
4802 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
4803 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
4804 : {
4805 0 : auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
4806 0 : if (!inputDatasets.empty())
4807 : {
4808 0 : osDSName = inputDatasets[0].GetName();
4809 : }
4810 : }
4811 2 : else if (inputArg && inputArg->GetType() == GAAT_DATASET)
4812 : {
4813 2 : auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
4814 2 : osDSName = inputDataset.GetName();
4815 : }
4816 :
4817 2 : if (!osDSName.empty())
4818 : {
4819 4 : CPLStringList aosAllowedDrivers;
4820 2 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
4821 2 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
4822 : aosAllowedDrivers =
4823 2 : CPLStringList(ifArg->Get<std::vector<std::string>>());
4824 :
4825 4 : CPLStringList aosOpenOptions;
4826 2 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
4827 2 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
4828 : aosOpenOptions =
4829 2 : CPLStringList(ooArg->Get<std::vector<std::string>>());
4830 :
4831 2 : if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4832 : osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
4833 4 : aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
4834 : {
4835 2 : if (auto poRG = poDS->GetRootGroup())
4836 : {
4837 1 : ret = poRG->GetMDArrayFullNamesRecursive();
4838 : }
4839 : }
4840 : }
4841 :
4842 4 : return ret;
4843 : }
4844 :
4845 : /************************************************************************/
4846 : /* GDALAlgorithm::AddMemorySizeArg() */
4847 : /************************************************************************/
4848 :
4849 : GDALInConstructionAlgorithmArg &
4850 210 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
4851 : const std::string &optionName,
4852 : const char *helpMessage)
4853 : {
4854 420 : return AddArg(optionName, 0, helpMessage, pStrValue)
4855 210 : .SetDefault(*pStrValue)
4856 : .AddValidationAction(
4857 139 : [this, pValue, pStrValue]()
4858 : {
4859 47 : CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
4860 : GIntBig nBytes;
4861 : bool bUnitSpecified;
4862 47 : if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
4863 47 : &bUnitSpecified) != CE_None)
4864 : {
4865 2 : return false;
4866 : }
4867 45 : if (!bUnitSpecified)
4868 : {
4869 1 : ReportError(CE_Failure, CPLE_AppDefined,
4870 : "Memory size must have a unit or be a "
4871 : "percentage of usable RAM (2GB, 5%%, etc.)");
4872 1 : return false;
4873 : }
4874 : if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
4875 : {
4876 : // -1 to please CoverityScan
4877 : if (static_cast<std::uint64_t>(nBytes) >
4878 : std::numeric_limits<size_t>::max() - 1U)
4879 : {
4880 : ReportError(CE_Failure, CPLE_AppDefined,
4881 : "Memory size %s is too large.",
4882 : pStrValue->c_str());
4883 : return false;
4884 : }
4885 : }
4886 :
4887 44 : *pValue = static_cast<size_t>(nBytes);
4888 44 : return true;
4889 420 : });
4890 : }
4891 :
4892 : /************************************************************************/
4893 : /* GDALAlgorithm::AddOutputLayerNameArg() */
4894 : /************************************************************************/
4895 :
4896 : GDALInConstructionAlgorithmArg &
4897 474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
4898 : const char *helpMessage)
4899 : {
4900 : return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
4901 474 : MsgOrDefault(helpMessage, _("Output layer name")), pValue);
4902 : }
4903 :
4904 : /************************************************************************/
4905 : /* GDALAlgorithm::AddLayerNameArg() */
4906 : /************************************************************************/
4907 :
4908 : GDALInConstructionAlgorithmArg &
4909 882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
4910 : const char *helpMessage)
4911 : {
4912 : return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
4913 882 : MsgOrDefault(helpMessage, _("Input layer name")), pValue);
4914 : }
4915 :
4916 : /************************************************************************/
4917 : /* GDALAlgorithm::AddGeometryTypeArg() */
4918 : /************************************************************************/
4919 :
4920 : GDALInConstructionAlgorithmArg &
4921 409 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
4922 : {
4923 : return AddArg("geometry-type", 0,
4924 818 : MsgOrDefault(helpMessage, _("Geometry type")), pValue)
4925 : .SetAutoCompleteFunction(
4926 3 : [](const std::string ¤tValue)
4927 : {
4928 3 : std::vector<std::string> oRet;
4929 51 : for (const char *type :
4930 : {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
4931 : "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
4932 : "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
4933 : "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
4934 54 : "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
4935 : {
4936 68 : if (currentValue.empty() ||
4937 17 : STARTS_WITH(type, currentValue.c_str()))
4938 : {
4939 35 : oRet.push_back(type);
4940 35 : oRet.push_back(std::string(type).append("Z"));
4941 35 : oRet.push_back(std::string(type).append("M"));
4942 35 : oRet.push_back(std::string(type).append("ZM"));
4943 : }
4944 : }
4945 3 : return oRet;
4946 818 : })
4947 : .AddValidationAction(
4948 118 : [this, pValue]()
4949 : {
4950 107 : if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
4951 115 : wkbUnknown &&
4952 8 : !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
4953 : {
4954 3 : ReportError(CE_Failure, CPLE_AppDefined,
4955 : "Invalid geometry type '%s'", pValue->c_str());
4956 3 : return false;
4957 : }
4958 104 : return true;
4959 818 : });
4960 : }
4961 :
4962 : /************************************************************************/
4963 : /* GDALAlgorithm::SetAutoCompleteFunctionForLayerName() */
4964 : /************************************************************************/
4965 :
4966 : /* static */
4967 2824 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
4968 : GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
4969 : {
4970 2824 : CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
4971 : datasetArg.GetType() == GAAT_DATASET_LIST);
4972 :
4973 : layerArg.SetAutoCompleteFunction(
4974 18 : [&datasetArg](const std::string ¤tValue)
4975 : {
4976 6 : std::vector<std::string> ret;
4977 12 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4978 6 : GDALArgDatasetValue *dsVal = nullptr;
4979 6 : if (datasetArg.GetType() == GAAT_DATASET)
4980 : {
4981 0 : dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
4982 : }
4983 : else
4984 : {
4985 6 : auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
4986 6 : if (val.size() == 1)
4987 : {
4988 6 : dsVal = &val[0];
4989 : }
4990 : }
4991 6 : if (dsVal && !dsVal->GetName().empty())
4992 : {
4993 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
4994 12 : dsVal->GetName().c_str(), GDAL_OF_VECTOR));
4995 6 : if (poDS)
4996 : {
4997 12 : for (auto &&poLayer : poDS->GetLayers())
4998 : {
4999 6 : if (currentValue == poLayer->GetDescription())
5000 : {
5001 1 : ret.clear();
5002 1 : ret.push_back(poLayer->GetDescription());
5003 1 : break;
5004 : }
5005 5 : ret.push_back(poLayer->GetDescription());
5006 : }
5007 : }
5008 : }
5009 12 : return ret;
5010 2824 : });
5011 2824 : }
5012 :
5013 : /************************************************************************/
5014 : /* GDALAlgorithm::SetAutoCompleteFunctionForFieldName() */
5015 : /************************************************************************/
5016 :
5017 131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
5018 : GDALInConstructionAlgorithmArg &fieldArg,
5019 : GDALInConstructionAlgorithmArg &layerNameArg,
5020 : std::vector<GDALArgDatasetValue> &datasetArg)
5021 : {
5022 :
5023 : fieldArg.SetAutoCompleteFunction(
5024 12 : [&datasetArg, &layerNameArg](const std::string ¤tValue)
5025 : {
5026 8 : std::set<std::string> ret;
5027 4 : if (!datasetArg.empty())
5028 : {
5029 4 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5030 :
5031 14 : auto getLayerFields = [&ret, ¤tValue](OGRLayer *poLayer)
5032 : {
5033 2 : auto poDefn = poLayer->GetLayerDefn();
5034 2 : const int nFieldCount = poDefn->GetFieldCount();
5035 8 : for (int iField = 0; iField < nFieldCount; iField++)
5036 : {
5037 : const char *fieldName =
5038 6 : poDefn->GetFieldDefn(iField)->GetNameRef();
5039 6 : if (currentValue == fieldName)
5040 : {
5041 0 : ret.clear();
5042 0 : ret.insert(fieldName);
5043 0 : break;
5044 : }
5045 6 : ret.insert(fieldName);
5046 : }
5047 2 : };
5048 :
5049 2 : GDALArgDatasetValue &dsVal = datasetArg[0];
5050 :
5051 2 : if (!dsVal.GetName().empty())
5052 : {
5053 : auto poDS = std::unique_ptr<GDALDataset>(
5054 2 : GDALDataset::Open(dsVal.GetName().c_str(),
5055 4 : GDAL_OF_VECTOR | GDAL_OF_READONLY));
5056 2 : if (poDS)
5057 : {
5058 2 : const auto &layerName = layerNameArg.Get<std::string>();
5059 2 : if (layerName.empty())
5060 : {
5061 : // Loop through all layers
5062 4 : for (auto &&poLayer : poDS->GetLayers())
5063 : {
5064 2 : getLayerFields(poLayer);
5065 : }
5066 : }
5067 : else
5068 : {
5069 0 : const auto poLayer = poDS->GetLayerByName(
5070 0 : layerNameArg.Get<std::string>().c_str());
5071 0 : if (poLayer)
5072 : {
5073 0 : getLayerFields(poLayer);
5074 : }
5075 : }
5076 : }
5077 : }
5078 : }
5079 4 : std::vector<std::string> retVector(ret.begin(), ret.end());
5080 8 : return retVector;
5081 131 : });
5082 131 : }
5083 :
5084 : /************************************************************************/
5085 : /* GDALAlgorithm::AddFieldNameArg() */
5086 : /************************************************************************/
5087 :
5088 : GDALInConstructionAlgorithmArg &
5089 131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
5090 : {
5091 : return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
5092 131 : pValue);
5093 : }
5094 :
5095 : /************************************************************************/
5096 : /* GDALAlgorithm::ParseFieldDefinition() */
5097 : /************************************************************************/
5098 67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
5099 : OGRFieldDefn *poFieldDefn,
5100 : std::string *posError)
5101 : {
5102 : static const std::regex re(
5103 67 : R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
5104 134 : std::smatch match;
5105 67 : if (std::regex_match(posStrDef, match, re))
5106 : {
5107 132 : const std::string name = match[1];
5108 132 : const std::string type = match[2];
5109 66 : const int width = match[3].matched ? std::stoi(match[3]) : 0;
5110 66 : const int precision = match[4].matched ? std::stoi(match[4]) : 0;
5111 66 : poFieldDefn->SetName(name.c_str());
5112 :
5113 66 : const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
5114 66 : if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
5115 : {
5116 1 : if (posError)
5117 1 : *posError = "Unsupported field type: " + type;
5118 :
5119 1 : return false;
5120 : }
5121 65 : poFieldDefn->SetType(typeEnum);
5122 65 : poFieldDefn->SetWidth(width);
5123 65 : poFieldDefn->SetPrecision(precision);
5124 65 : return true;
5125 : }
5126 :
5127 1 : if (posError)
5128 : *posError = "Invalid field definition format. Expected "
5129 1 : "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
5130 :
5131 1 : return false;
5132 : }
5133 :
5134 : /************************************************************************/
5135 : /* GDALAlgorithm::AddFieldDefinitionArg() */
5136 : /************************************************************************/
5137 :
5138 : GDALInConstructionAlgorithmArg &
5139 120 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
5140 : std::vector<OGRFieldDefn> *pFieldDefns,
5141 : const char *helpMessage)
5142 : {
5143 : auto &arg =
5144 : AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
5145 240 : pValues)
5146 240 : .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
5147 120 : .SetPackedValuesAllowed(true)
5148 120 : .SetRepeatedArgAllowed(true);
5149 :
5150 132 : auto validationFunction = [this, pFieldDefns, pValues]()
5151 : {
5152 65 : pFieldDefns->clear();
5153 130 : for (const auto &strValue : *pValues)
5154 : {
5155 67 : OGRFieldDefn fieldDefn("", OFTString);
5156 67 : std::string error;
5157 67 : if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
5158 : &error))
5159 : {
5160 2 : ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
5161 2 : return false;
5162 : }
5163 : // Check uniqueness of field names
5164 67 : for (const auto &existingFieldDefn : *pFieldDefns)
5165 : {
5166 2 : if (EQUAL(existingFieldDefn.GetNameRef(),
5167 : fieldDefn.GetNameRef()))
5168 : {
5169 0 : ReportError(CE_Failure, CPLE_AppDefined,
5170 : "Duplicate field name: '%s'",
5171 : fieldDefn.GetNameRef());
5172 0 : return false;
5173 : }
5174 : }
5175 65 : pFieldDefns->push_back(fieldDefn);
5176 : }
5177 63 : return true;
5178 120 : };
5179 :
5180 120 : arg.AddValidationAction(std::move(validationFunction));
5181 :
5182 120 : return arg;
5183 : }
5184 :
5185 : /************************************************************************/
5186 : /* GDALAlgorithm::AddFieldTypeSubtypeArg() */
5187 : /************************************************************************/
5188 :
5189 262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
5190 : OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
5191 : std::string *pStrValue, const std::string &argName, const char *helpMessage)
5192 : {
5193 : auto &arg =
5194 524 : AddArg(argName.empty() ? std::string("field-type") : argName, 0,
5195 786 : MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
5196 : .SetAutoCompleteFunction(
5197 1 : [](const std::string ¤tValue)
5198 : {
5199 1 : std::vector<std::string> oRet;
5200 6 : for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
5201 : {
5202 : const char *pszSubType =
5203 5 : OGRFieldDefn::GetFieldSubTypeName(
5204 : static_cast<OGRFieldSubType>(i));
5205 5 : if (pszSubType != nullptr)
5206 : {
5207 5 : if (currentValue.empty() ||
5208 0 : STARTS_WITH(pszSubType, currentValue.c_str()))
5209 : {
5210 5 : oRet.push_back(pszSubType);
5211 : }
5212 : }
5213 : }
5214 :
5215 15 : for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
5216 : {
5217 : // Skip deprecated
5218 14 : if (static_cast<OGRFieldType>(i) ==
5219 13 : OGRFieldType::OFTWideString ||
5220 : static_cast<OGRFieldType>(i) ==
5221 : OGRFieldType::OFTWideStringList)
5222 2 : continue;
5223 12 : const char *pszType = OGRFieldDefn::GetFieldTypeName(
5224 : static_cast<OGRFieldType>(i));
5225 12 : if (pszType != nullptr)
5226 : {
5227 12 : if (currentValue.empty() ||
5228 0 : STARTS_WITH(pszType, currentValue.c_str()))
5229 : {
5230 12 : oRet.push_back(pszType);
5231 : }
5232 : }
5233 : }
5234 1 : return oRet;
5235 262 : });
5236 :
5237 : auto validationFunction =
5238 845 : [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
5239 : {
5240 120 : bool isValid{true};
5241 120 : *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
5242 :
5243 : // String is returned for unknown types
5244 120 : if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
5245 : {
5246 16 : isValid = false;
5247 : }
5248 :
5249 120 : *pSubtypeValue =
5250 120 : OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
5251 :
5252 120 : if (*pSubtypeValue != OFSTNone)
5253 : {
5254 15 : isValid = true;
5255 15 : switch (*pSubtypeValue)
5256 : {
5257 6 : case OFSTBoolean:
5258 : case OFSTInt16:
5259 : {
5260 6 : *pTypeValue = OFTInteger;
5261 6 : break;
5262 : }
5263 3 : case OFSTFloat32:
5264 : {
5265 3 : *pTypeValue = OFTReal;
5266 3 : break;
5267 : }
5268 6 : default:
5269 : {
5270 6 : *pTypeValue = OFTString;
5271 6 : break;
5272 : }
5273 : }
5274 : }
5275 :
5276 120 : if (!isValid)
5277 : {
5278 2 : ReportError(CE_Failure, CPLE_AppDefined,
5279 : "Invalid value for argument '%s': '%s'",
5280 1 : arg.GetName().c_str(), pStrValue->c_str());
5281 : }
5282 :
5283 120 : return isValid;
5284 262 : };
5285 :
5286 262 : if (!pStrValue->empty())
5287 : {
5288 0 : arg.SetDefault(*pStrValue);
5289 0 : validationFunction();
5290 : }
5291 :
5292 262 : arg.AddValidationAction(std::move(validationFunction));
5293 :
5294 262 : return arg;
5295 : }
5296 :
5297 : /************************************************************************/
5298 : /* GDALAlgorithm::ValidateBandArg() */
5299 : /************************************************************************/
5300 :
5301 3851 : bool GDALAlgorithm::ValidateBandArg() const
5302 : {
5303 3851 : bool ret = true;
5304 3851 : const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
5305 3851 : const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
5306 1414 : if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
5307 280 : (inputDatasetArg->GetType() == GAAT_DATASET ||
5308 5259 : inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
5309 143 : (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
5310 : {
5311 104 : const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
5312 : {
5313 99 : if (nBand > poDS->GetRasterCount())
5314 : {
5315 5 : ReportError(CE_Failure, CPLE_AppDefined,
5316 : "Value of 'band' should be greater or equal than "
5317 : "1 and less or equal than %d.",
5318 : poDS->GetRasterCount());
5319 5 : return false;
5320 : }
5321 94 : return true;
5322 86 : };
5323 :
5324 : const auto ValidateForOneDataset =
5325 292 : [&bandArg, &CheckBand](const GDALDataset *poDS)
5326 : {
5327 81 : bool l_ret = true;
5328 81 : if (bandArg->GetType() == GAAT_INTEGER)
5329 : {
5330 24 : l_ret = CheckBand(poDS, bandArg->Get<int>());
5331 : }
5332 57 : else if (bandArg->GetType() == GAAT_INTEGER_LIST)
5333 : {
5334 130 : for (int nBand : bandArg->Get<std::vector<int>>())
5335 : {
5336 75 : l_ret = l_ret && CheckBand(poDS, nBand);
5337 : }
5338 : }
5339 81 : return l_ret;
5340 86 : };
5341 :
5342 86 : if (inputDatasetArg->GetType() == GAAT_DATASET)
5343 : {
5344 : auto poDS =
5345 6 : inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
5346 6 : if (poDS && !ValidateForOneDataset(poDS))
5347 2 : ret = false;
5348 : }
5349 : else
5350 : {
5351 80 : CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
5352 79 : for (auto &datasetValue :
5353 238 : inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
5354 : {
5355 79 : auto poDS = datasetValue.GetDatasetRef();
5356 79 : if (poDS && !ValidateForOneDataset(poDS))
5357 3 : ret = false;
5358 : }
5359 : }
5360 : }
5361 3851 : return ret;
5362 : }
5363 :
5364 : /************************************************************************/
5365 : /* GDALAlgorithm::RunPreStepPipelineValidations() */
5366 : /************************************************************************/
5367 :
5368 3070 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
5369 : {
5370 3070 : return ValidateBandArg();
5371 : }
5372 :
5373 : /************************************************************************/
5374 : /* GDALAlgorithm::AddBandArg() */
5375 : /************************************************************************/
5376 :
5377 : GDALInConstructionAlgorithmArg &
5378 1445 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
5379 : {
5380 1762 : AddValidationAction([this]() { return ValidateBandArg(); });
5381 :
5382 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5383 : MsgOrDefault(helpMessage, _("Input band (1-based index)")),
5384 2890 : pValue)
5385 : .AddValidationAction(
5386 34 : [pValue]()
5387 : {
5388 34 : if (*pValue <= 0)
5389 : {
5390 1 : CPLError(CE_Failure, CPLE_AppDefined,
5391 : "Value of 'band' should greater or equal to 1.");
5392 1 : return false;
5393 : }
5394 33 : return true;
5395 2890 : });
5396 : }
5397 :
5398 : /************************************************************************/
5399 : /* GDALAlgorithm::AddBandArg() */
5400 : /************************************************************************/
5401 :
5402 : GDALInConstructionAlgorithmArg &
5403 837 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
5404 : {
5405 1301 : AddValidationAction([this]() { return ValidateBandArg(); });
5406 :
5407 : return AddArg(GDAL_ARG_NAME_BAND, 'b',
5408 : MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
5409 1674 : pValue)
5410 : .AddValidationAction(
5411 126 : [pValue]()
5412 : {
5413 397 : for (int val : *pValue)
5414 : {
5415 272 : if (val <= 0)
5416 : {
5417 1 : CPLError(CE_Failure, CPLE_AppDefined,
5418 : "Value of 'band' should greater or equal "
5419 : "to 1.");
5420 1 : return false;
5421 : }
5422 : }
5423 125 : return true;
5424 1674 : });
5425 : }
5426 :
5427 : /************************************************************************/
5428 : /* ParseAndValidateKeyValue() */
5429 : /************************************************************************/
5430 :
5431 402 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
5432 : {
5433 406 : const auto Validate = [this, &arg](const std::string &val)
5434 : {
5435 401 : if (val.find('=') == std::string::npos)
5436 : {
5437 5 : ReportError(
5438 : CE_Failure, CPLE_AppDefined,
5439 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
5440 5 : arg.GetName().c_str());
5441 5 : return false;
5442 : }
5443 :
5444 396 : return true;
5445 402 : };
5446 :
5447 402 : if (arg.GetType() == GAAT_STRING)
5448 : {
5449 0 : return Validate(arg.Get<std::string>());
5450 : }
5451 402 : else if (arg.GetType() == GAAT_STRING_LIST)
5452 : {
5453 402 : std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
5454 402 : if (vals.size() == 1)
5455 : {
5456 : // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
5457 734 : std::vector<std::string> newVals;
5458 734 : std::string curToken;
5459 367 : bool canSplitOnComma = true;
5460 367 : char lastSep = 0;
5461 367 : bool inString = false;
5462 367 : bool equalFoundInLastToken = false;
5463 5318 : for (char c : vals[0])
5464 : {
5465 4955 : if (!inString && c == ',')
5466 : {
5467 10 : if (lastSep != '=' || !equalFoundInLastToken)
5468 : {
5469 2 : canSplitOnComma = false;
5470 2 : break;
5471 : }
5472 8 : lastSep = c;
5473 8 : newVals.push_back(curToken);
5474 8 : curToken.clear();
5475 8 : equalFoundInLastToken = false;
5476 : }
5477 4945 : else if (!inString && c == '=')
5478 : {
5479 366 : if (lastSep == '=')
5480 : {
5481 2 : canSplitOnComma = false;
5482 2 : break;
5483 : }
5484 364 : equalFoundInLastToken = true;
5485 364 : lastSep = c;
5486 364 : curToken += c;
5487 : }
5488 4579 : else if (c == '"')
5489 : {
5490 4 : inString = !inString;
5491 4 : curToken += c;
5492 : }
5493 : else
5494 : {
5495 4575 : curToken += c;
5496 : }
5497 : }
5498 367 : if (canSplitOnComma && !inString && equalFoundInLastToken)
5499 : {
5500 354 : if (!curToken.empty())
5501 354 : newVals.emplace_back(std::move(curToken));
5502 354 : vals = std::move(newVals);
5503 : }
5504 : }
5505 :
5506 798 : for (const auto &val : vals)
5507 : {
5508 401 : if (!Validate(val))
5509 5 : return false;
5510 : }
5511 : }
5512 :
5513 397 : return true;
5514 : }
5515 :
5516 : /************************************************************************/
5517 : /* IsGDALGOutput() */
5518 : /************************************************************************/
5519 :
5520 2060 : bool GDALAlgorithm::IsGDALGOutput() const
5521 : {
5522 2060 : bool isGDALGOutput = false;
5523 2060 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5524 2060 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5525 3525 : if (outputArg && outputArg->GetType() == GAAT_DATASET &&
5526 1465 : outputArg->IsExplicitlySet())
5527 : {
5528 2873 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
5529 1424 : outputFormatArg->IsExplicitlySet())
5530 : {
5531 : const auto &val =
5532 926 : outputFormatArg->GDALAlgorithmArg::Get<std::string>();
5533 926 : isGDALGOutput = EQUAL(val.c_str(), "GDALG");
5534 : }
5535 : else
5536 : {
5537 : const auto &filename =
5538 523 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
5539 523 : isGDALGOutput =
5540 1019 : filename.GetName().size() > strlen(".gdalg.json") &&
5541 496 : EQUAL(filename.GetName().c_str() + filename.GetName().size() -
5542 : strlen(".gdalg.json"),
5543 : ".gdalg.json");
5544 : }
5545 : }
5546 2060 : return isGDALGOutput;
5547 : }
5548 :
5549 : /************************************************************************/
5550 : /* ProcessGDALGOutput() */
5551 : /************************************************************************/
5552 :
5553 2399 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
5554 : {
5555 2399 : if (!SupportsStreamedOutput())
5556 786 : return ProcessGDALGOutputRet::NOT_GDALG;
5557 :
5558 1613 : if (IsGDALGOutput())
5559 : {
5560 11 : const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5561 : const auto &filename =
5562 11 : outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
5563 : VSIStatBufL sStat;
5564 11 : if (VSIStatL(filename.c_str(), &sStat) == 0)
5565 : {
5566 0 : const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
5567 0 : if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
5568 : {
5569 0 : if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
5570 : {
5571 0 : CPLError(CE_Failure, CPLE_AppDefined,
5572 : "File '%s' already exists. Specify the "
5573 : "--overwrite option to overwrite it.",
5574 : filename.c_str());
5575 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5576 : }
5577 : }
5578 : }
5579 :
5580 22 : std::string osCommandLine;
5581 :
5582 44 : for (const auto &path : GDALAlgorithm::m_callPath)
5583 : {
5584 33 : if (!osCommandLine.empty())
5585 22 : osCommandLine += ' ';
5586 33 : osCommandLine += path;
5587 : }
5588 :
5589 250 : for (const auto &arg : GetArgs())
5590 : {
5591 265 : if (arg->IsExplicitlySet() &&
5592 41 : arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
5593 30 : arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
5594 280 : arg->GetName() != GDAL_ARG_NAME_UPDATE &&
5595 15 : arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
5596 : {
5597 14 : osCommandLine += ' ';
5598 14 : std::string strArg;
5599 14 : if (!arg->Serialize(strArg))
5600 : {
5601 0 : CPLError(CE_Failure, CPLE_AppDefined,
5602 : "Cannot serialize argument %s",
5603 0 : arg->GetName().c_str());
5604 0 : return ProcessGDALGOutputRet::GDALG_ERROR;
5605 : }
5606 14 : osCommandLine += strArg;
5607 : }
5608 : }
5609 :
5610 11 : osCommandLine += " --output-format stream --output streamed_dataset";
5611 :
5612 11 : std::string outStringUnused;
5613 11 : return SaveGDALG(filename, outStringUnused, osCommandLine)
5614 11 : ? ProcessGDALGOutputRet::GDALG_OK
5615 11 : : ProcessGDALGOutputRet::GDALG_ERROR;
5616 : }
5617 :
5618 1602 : return ProcessGDALGOutputRet::NOT_GDALG;
5619 : }
5620 :
5621 : /************************************************************************/
5622 : /* GDALAlgorithm::SaveGDALG() */
5623 : /************************************************************************/
5624 :
5625 22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
5626 : std::string &outString,
5627 : const std::string &commandLine)
5628 : {
5629 44 : CPLJSONDocument oDoc;
5630 22 : oDoc.GetRoot().Add("type", "gdal_streamed_alg");
5631 22 : oDoc.GetRoot().Add("command_line", commandLine);
5632 22 : oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
5633 :
5634 22 : if (!filename.empty())
5635 21 : return oDoc.Save(filename);
5636 :
5637 1 : outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
5638 1 : return true;
5639 : }
5640 :
5641 : /************************************************************************/
5642 : /* GDALAlgorithm::AddCreationOptionsArg() */
5643 : /************************************************************************/
5644 :
5645 : GDALInConstructionAlgorithmArg &
5646 7934 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
5647 : const char *helpMessage)
5648 : {
5649 : auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
5650 15868 : MsgOrDefault(helpMessage, _("Creation option")), pValue)
5651 15868 : .AddAlias("co")
5652 15868 : .SetMetaVar("<KEY>=<VALUE>")
5653 7934 : .SetPackedValuesAllowed(false);
5654 147 : arg.AddValidationAction([this, &arg]()
5655 8081 : { return ParseAndValidateKeyValue(arg); });
5656 :
5657 : arg.SetAutoCompleteFunction(
5658 48 : [this](const std::string ¤tValue)
5659 : {
5660 16 : std::vector<std::string> oRet;
5661 :
5662 16 : int datasetType =
5663 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
5664 16 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5665 16 : if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
5666 0 : outputArg->GetType() == GAAT_DATASET_LIST))
5667 : {
5668 16 : datasetType = outputArg->GetDatasetType();
5669 : }
5670 :
5671 16 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5672 32 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5673 16 : outputFormat->IsExplicitlySet())
5674 : {
5675 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5676 6 : outputFormat->Get<std::string>().c_str());
5677 6 : if (poDriver)
5678 : {
5679 6 : AddOptionsSuggestions(
5680 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
5681 : datasetType, currentValue, oRet);
5682 : }
5683 6 : return oRet;
5684 : }
5685 :
5686 10 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5687 : {
5688 10 : auto poDM = GetGDALDriverManager();
5689 10 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5690 10 : const auto &osDSName = datasetValue.GetName();
5691 10 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5692 10 : if (!osExt.empty())
5693 : {
5694 10 : std::set<std::string> oVisitedExtensions;
5695 709 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5696 : {
5697 706 : auto poDriver = poDM->GetDriver(i);
5698 2118 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
5699 706 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
5700 213 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
5701 1412 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
5702 213 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
5703 0 : poDriver->GetMetadataItem(
5704 0 : GDAL_DCAP_MULTIDIM_RASTER)))
5705 : {
5706 : const char *pszExtensions =
5707 493 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5708 493 : if (pszExtensions)
5709 : {
5710 : const CPLStringList aosExts(
5711 320 : CSLTokenizeString2(pszExtensions, " ", 0));
5712 710 : for (const char *pszExt : cpl::Iterate(aosExts))
5713 : {
5714 416 : if (EQUAL(pszExt, osExt.c_str()) &&
5715 16 : !cpl::contains(oVisitedExtensions,
5716 : pszExt))
5717 : {
5718 10 : oVisitedExtensions.insert(pszExt);
5719 10 : if (AddOptionsSuggestions(
5720 : poDriver->GetMetadataItem(
5721 10 : GDAL_DMD_CREATIONOPTIONLIST),
5722 : datasetType, currentValue,
5723 : oRet))
5724 : {
5725 7 : return oRet;
5726 : }
5727 3 : break;
5728 : }
5729 : }
5730 : }
5731 : }
5732 : }
5733 : }
5734 : }
5735 :
5736 3 : return oRet;
5737 7934 : });
5738 :
5739 7934 : return arg;
5740 : }
5741 :
5742 : /************************************************************************/
5743 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
5744 : /************************************************************************/
5745 :
5746 : GDALInConstructionAlgorithmArg &
5747 3791 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
5748 : const char *helpMessage)
5749 : {
5750 : auto &arg =
5751 : AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
5752 7582 : MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
5753 7582 : .AddAlias("lco")
5754 7582 : .SetMetaVar("<KEY>=<VALUE>")
5755 3791 : .SetPackedValuesAllowed(false);
5756 73 : arg.AddValidationAction([this, &arg]()
5757 3864 : { return ParseAndValidateKeyValue(arg); });
5758 :
5759 : arg.SetAutoCompleteFunction(
5760 5 : [this](const std::string ¤tValue)
5761 : {
5762 2 : std::vector<std::string> oRet;
5763 :
5764 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
5765 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
5766 2 : outputFormat->IsExplicitlySet())
5767 : {
5768 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
5769 1 : outputFormat->Get<std::string>().c_str());
5770 1 : if (poDriver)
5771 : {
5772 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
5773 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5774 : GDAL_OF_VECTOR, currentValue, oRet);
5775 : }
5776 1 : return oRet;
5777 : }
5778 :
5779 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
5780 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
5781 : {
5782 1 : auto poDM = GetGDALDriverManager();
5783 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
5784 1 : const auto &osDSName = datasetValue.GetName();
5785 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
5786 1 : if (!osExt.empty())
5787 : {
5788 1 : std::set<std::string> oVisitedExtensions;
5789 227 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
5790 : {
5791 226 : auto poDriver = poDM->GetDriver(i);
5792 226 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
5793 : {
5794 : const char *pszExtensions =
5795 90 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
5796 90 : if (pszExtensions)
5797 : {
5798 : const CPLStringList aosExts(
5799 61 : CSLTokenizeString2(pszExtensions, " ", 0));
5800 154 : for (const char *pszExt : cpl::Iterate(aosExts))
5801 : {
5802 95 : if (EQUAL(pszExt, osExt.c_str()) &&
5803 1 : !cpl::contains(oVisitedExtensions,
5804 : pszExt))
5805 : {
5806 1 : oVisitedExtensions.insert(pszExt);
5807 1 : if (AddOptionsSuggestions(
5808 : poDriver->GetMetadataItem(
5809 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
5810 : GDAL_OF_VECTOR, currentValue,
5811 : oRet))
5812 : {
5813 0 : return oRet;
5814 : }
5815 1 : break;
5816 : }
5817 : }
5818 : }
5819 : }
5820 : }
5821 : }
5822 : }
5823 :
5824 1 : return oRet;
5825 3791 : });
5826 :
5827 3791 : return arg;
5828 : }
5829 :
5830 : /************************************************************************/
5831 : /* GDALAlgorithm::AddBBOXArg() */
5832 : /************************************************************************/
5833 :
5834 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
5835 : GDALInConstructionAlgorithmArg &
5836 1796 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
5837 : {
5838 : auto &arg = AddArg("bbox", 0,
5839 : MsgOrDefault(helpMessage,
5840 : _("Bounding box as xmin,ymin,xmax,ymax")),
5841 3592 : pValue)
5842 1796 : .SetRepeatedArgAllowed(false)
5843 1796 : .SetMinCount(4)
5844 1796 : .SetMaxCount(4)
5845 1796 : .SetDisplayHintAboutRepetition(false);
5846 : arg.AddValidationAction(
5847 162 : [&arg]()
5848 : {
5849 162 : const auto &val = arg.Get<std::vector<double>>();
5850 162 : CPLAssert(val.size() == 4);
5851 162 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
5852 : {
5853 5 : CPLError(CE_Failure, CPLE_AppDefined,
5854 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
5855 : "xmin <= xmax and ymin <= ymax");
5856 5 : return false;
5857 : }
5858 157 : return true;
5859 1796 : });
5860 1796 : return arg;
5861 : }
5862 :
5863 : /************************************************************************/
5864 : /* GDALAlgorithm::AddActiveLayerArg() */
5865 : /************************************************************************/
5866 :
5867 : GDALInConstructionAlgorithmArg &
5868 1701 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
5869 : {
5870 : return AddArg("active-layer", 0,
5871 : MsgOrDefault(helpMessage,
5872 : _("Set active layer (if not specified, all)")),
5873 1701 : pValue);
5874 : }
5875 :
5876 : /************************************************************************/
5877 : /* GDALAlgorithm::AddNumThreadsArg() */
5878 : /************************************************************************/
5879 :
5880 : GDALInConstructionAlgorithmArg &
5881 676 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
5882 : const char *helpMessage)
5883 : {
5884 : auto &arg =
5885 : AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
5886 : MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
5887 676 : pStrValue);
5888 :
5889 : AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
5890 1352 : _("Number of jobs (read-only, hidden argument)"), pValue)
5891 676 : .SetHidden();
5892 :
5893 2583 : auto lambda = [this, &arg, pValue, pStrValue]
5894 : {
5895 861 : bool bOK = false;
5896 861 : const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
5897 : const int nLimit = std::clamp(
5898 861 : pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
5899 1722 : CPLGetNumCPUs());
5900 : const int nNumThreads =
5901 861 : GDALGetNumThreads(pStrValue->c_str(), nLimit,
5902 : /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
5903 861 : if (bOK)
5904 : {
5905 861 : *pValue = nNumThreads;
5906 : }
5907 : else
5908 : {
5909 0 : ReportError(CE_Failure, CPLE_IllegalArg,
5910 : "Invalid value for '%s' argument",
5911 0 : arg.GetName().c_str());
5912 : }
5913 861 : return bOK;
5914 676 : };
5915 676 : if (!pStrValue->empty())
5916 : {
5917 630 : arg.SetDefault(*pStrValue);
5918 630 : lambda();
5919 : }
5920 676 : arg.AddValidationAction(std::move(lambda));
5921 676 : return arg;
5922 : }
5923 :
5924 : /************************************************************************/
5925 : /* GDALAlgorithm::AddAbsolutePathArg() */
5926 : /************************************************************************/
5927 :
5928 : GDALInConstructionAlgorithmArg &
5929 619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
5930 : {
5931 : return AddArg(
5932 : "absolute-path", 0,
5933 : MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
5934 : "should be stored as an absolute path")),
5935 619 : pValue);
5936 : }
5937 :
5938 : /************************************************************************/
5939 : /* GDALAlgorithm::AddPixelFunctionNameArg() */
5940 : /************************************************************************/
5941 :
5942 : GDALInConstructionAlgorithmArg &
5943 137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
5944 : const char *helpMessage)
5945 : {
5946 :
5947 : const auto pixelFunctionNames =
5948 137 : VRTDerivedRasterBand::GetPixelFunctionNames();
5949 : return AddArg(
5950 : "pixel-function", 0,
5951 : MsgOrDefault(
5952 : helpMessage,
5953 : _("Specify a pixel function to calculate output value from "
5954 : "overlapping inputs")),
5955 274 : pValue)
5956 274 : .SetChoices(pixelFunctionNames);
5957 : }
5958 :
5959 : /************************************************************************/
5960 : /* GDALAlgorithm::AddPixelFunctionArgsArg() */
5961 : /************************************************************************/
5962 :
5963 : GDALInConstructionAlgorithmArg &
5964 137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
5965 : const char *helpMessage)
5966 : {
5967 : auto &pixelFunctionArgArg =
5968 : AddArg("pixel-function-arg", 0,
5969 : MsgOrDefault(
5970 : helpMessage,
5971 : _("Specify argument(s) to pass to the pixel function")),
5972 274 : pValue)
5973 274 : .SetMetaVar("<NAME>=<VALUE>")
5974 137 : .SetRepeatedArgAllowed(true);
5975 : pixelFunctionArgArg.AddValidationAction(
5976 7 : [this, &pixelFunctionArgArg]()
5977 144 : { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
5978 :
5979 : pixelFunctionArgArg.SetAutoCompleteFunction(
5980 12 : [this](const std::string ¤tValue)
5981 : {
5982 12 : std::string pixelFunction;
5983 6 : const auto pixelFunctionArg = GetArg("pixel-function");
5984 6 : if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
5985 : {
5986 6 : pixelFunction = pixelFunctionArg->Get<std::string>();
5987 : }
5988 :
5989 6 : std::vector<std::string> ret;
5990 :
5991 6 : if (!pixelFunction.empty())
5992 : {
5993 5 : const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
5994 : pixelFunction.c_str());
5995 5 : if (!pair)
5996 : {
5997 1 : ret.push_back("**");
5998 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
5999 1 : ret.push_back(std::string("\xC2\xA0"
6000 : "Invalid pixel function name"));
6001 : }
6002 4 : else if (pair->second.find("Argument name=") ==
6003 : std::string::npos)
6004 : {
6005 1 : ret.push_back("**");
6006 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
6007 1 : ret.push_back(
6008 2 : std::string(
6009 : "\xC2\xA0"
6010 : "No pixel function arguments for pixel function '")
6011 1 : .append(pixelFunction)
6012 1 : .append("'"));
6013 : }
6014 : else
6015 : {
6016 3 : AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
6017 : ret);
6018 : }
6019 : }
6020 :
6021 12 : return ret;
6022 137 : });
6023 :
6024 137 : return pixelFunctionArgArg;
6025 : }
6026 :
6027 : /************************************************************************/
6028 : /* GDALAlgorithm::AddProgressArg() */
6029 : /************************************************************************/
6030 :
6031 8062 : void GDALAlgorithm::AddProgressArg()
6032 : {
6033 : AddArg(GDAL_ARG_NAME_QUIET, 'q',
6034 16124 : _("Quiet mode (no progress bar or warning message)"), &m_quiet)
6035 16124 : .SetCategory(GAAC_COMMON)
6036 8062 : .AddAction([this]() { m_progressBarRequested = false; });
6037 :
6038 16124 : AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
6039 8062 : .SetHidden();
6040 8062 : }
6041 :
6042 : /************************************************************************/
6043 : /* GDALAlgorithm::Run() */
6044 : /************************************************************************/
6045 :
6046 4421 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
6047 : {
6048 4421 : WarnIfDeprecated();
6049 :
6050 4421 : if (m_selectedSubAlg)
6051 : {
6052 388 : if (m_calledFromCommandLine)
6053 233 : m_selectedSubAlg->m_calledFromCommandLine = true;
6054 388 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
6055 : }
6056 :
6057 4033 : if (m_helpRequested || m_helpDocRequested)
6058 : {
6059 16 : if (m_calledFromCommandLine)
6060 16 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
6061 16 : return true;
6062 : }
6063 :
6064 4017 : if (m_JSONUsageRequested)
6065 : {
6066 3 : if (m_calledFromCommandLine)
6067 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
6068 3 : return true;
6069 : }
6070 :
6071 4014 : if (!ValidateArguments())
6072 112 : return false;
6073 :
6074 3902 : switch (ProcessGDALGOutput())
6075 : {
6076 0 : case ProcessGDALGOutputRet::GDALG_ERROR:
6077 0 : return false;
6078 :
6079 11 : case ProcessGDALGOutputRet::GDALG_OK:
6080 11 : return true;
6081 :
6082 3891 : case ProcessGDALGOutputRet::NOT_GDALG:
6083 3891 : break;
6084 : }
6085 :
6086 3891 : if (m_executionForStreamOutput)
6087 : {
6088 82 : if (!CheckSafeForStreamOutput())
6089 : {
6090 4 : return false;
6091 : }
6092 : }
6093 :
6094 3887 : return RunImpl(pfnProgress, pProgressData);
6095 : }
6096 :
6097 : /************************************************************************/
6098 : /* GDALAlgorithm::CheckSafeForStreamOutput() */
6099 : /************************************************************************/
6100 :
6101 37 : bool GDALAlgorithm::CheckSafeForStreamOutput()
6102 : {
6103 37 : const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
6104 37 : if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
6105 : {
6106 37 : const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
6107 37 : if (!EQUAL(val.c_str(), "stream"))
6108 : {
6109 : // For security reasons, to avoid that reading a .gdalg.json file
6110 : // writes a file on the file system.
6111 4 : ReportError(
6112 : CE_Failure, CPLE_NotSupported,
6113 : "in streamed execution, --format stream should be used");
6114 4 : return false;
6115 : }
6116 : }
6117 33 : return true;
6118 : }
6119 :
6120 : /************************************************************************/
6121 : /* GDALAlgorithm::Finalize() */
6122 : /************************************************************************/
6123 :
6124 1631 : bool GDALAlgorithm::Finalize()
6125 : {
6126 1631 : bool ret = true;
6127 1631 : if (m_selectedSubAlg)
6128 239 : ret = m_selectedSubAlg->Finalize();
6129 :
6130 29706 : for (auto &arg : m_args)
6131 : {
6132 28075 : if (arg->GetType() == GAAT_DATASET)
6133 : {
6134 1312 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
6135 : }
6136 26763 : else if (arg->GetType() == GAAT_DATASET_LIST)
6137 : {
6138 2496 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
6139 : {
6140 1180 : ret = ds.Close() && ret;
6141 : }
6142 : }
6143 : }
6144 1631 : return ret;
6145 : }
6146 :
6147 : /************************************************************************/
6148 : /* GDALAlgorithm::GetArgNamesForCLI() */
6149 : /************************************************************************/
6150 :
6151 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
6152 691 : GDALAlgorithm::GetArgNamesForCLI() const
6153 : {
6154 1382 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6155 :
6156 691 : size_t maxOptLen = 0;
6157 8614 : for (const auto &arg : m_args)
6158 : {
6159 7923 : if (arg->IsHidden() || arg->IsHiddenForCLI())
6160 1703 : continue;
6161 6220 : std::string opt;
6162 6220 : bool addComma = false;
6163 6220 : if (!arg->GetShortName().empty())
6164 : {
6165 1429 : opt += '-';
6166 1429 : opt += arg->GetShortName();
6167 1429 : addComma = true;
6168 : }
6169 6220 : for (char alias : arg->GetShortNameAliases())
6170 : {
6171 0 : if (addComma)
6172 0 : opt += ", ";
6173 0 : opt += "-";
6174 0 : opt += alias;
6175 0 : addComma = true;
6176 : }
6177 6970 : for (const std::string &alias : arg->GetAliases())
6178 : {
6179 750 : if (addComma)
6180 322 : opt += ", ";
6181 750 : opt += "--";
6182 750 : opt += alias;
6183 750 : addComma = true;
6184 : }
6185 6220 : if (!arg->GetName().empty())
6186 : {
6187 6220 : if (addComma)
6188 1857 : opt += ", ";
6189 6220 : opt += "--";
6190 6220 : opt += arg->GetName();
6191 : }
6192 6220 : const auto &metaVar = arg->GetMetaVar();
6193 6220 : if (!metaVar.empty())
6194 : {
6195 3878 : opt += ' ';
6196 3878 : if (metaVar.front() != '<')
6197 2761 : opt += '<';
6198 3878 : opt += metaVar;
6199 3878 : if (metaVar.back() != '>')
6200 2785 : opt += '>';
6201 : }
6202 6220 : maxOptLen = std::max(maxOptLen, opt.size());
6203 6220 : options.emplace_back(arg.get(), opt);
6204 : }
6205 :
6206 1382 : return std::make_pair(std::move(options), maxOptLen);
6207 : }
6208 :
6209 : /************************************************************************/
6210 : /* GDALAlgorithm::GetUsageForCLI() */
6211 : /************************************************************************/
6212 :
6213 : std::string
6214 410 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
6215 : const UsageOptions &usageOptions) const
6216 : {
6217 410 : if (m_selectedSubAlg)
6218 7 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
6219 :
6220 806 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
6221 806 : std::string osPath;
6222 811 : for (const std::string &s : m_callPath)
6223 : {
6224 408 : if (!osPath.empty())
6225 49 : osPath += ' ';
6226 408 : osPath += s;
6227 : }
6228 403 : osRet += ' ';
6229 403 : osRet += osPath;
6230 :
6231 403 : bool hasNonPositionals = false;
6232 4988 : for (const auto &arg : m_args)
6233 : {
6234 4585 : if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
6235 3288 : hasNonPositionals = true;
6236 : }
6237 :
6238 403 : if (HasSubAlgorithms())
6239 : {
6240 9 : if (m_callPath.size() == 1)
6241 : {
6242 8 : osRet += " <COMMAND>";
6243 8 : if (hasNonPositionals)
6244 8 : osRet += " [OPTIONS]";
6245 8 : if (usageOptions.isPipelineStep)
6246 : {
6247 5 : const size_t nLenFirstLine = osRet.size();
6248 5 : osRet += '\n';
6249 5 : osRet.append(nLenFirstLine, '-');
6250 5 : osRet += '\n';
6251 : }
6252 8 : osRet += "\nwhere <COMMAND> is one of:\n";
6253 : }
6254 : else
6255 : {
6256 1 : osRet += " <SUBCOMMAND>";
6257 1 : if (hasNonPositionals)
6258 1 : osRet += " [OPTIONS]";
6259 1 : if (usageOptions.isPipelineStep)
6260 : {
6261 0 : const size_t nLenFirstLine = osRet.size();
6262 0 : osRet += '\n';
6263 0 : osRet.append(nLenFirstLine, '-');
6264 0 : osRet += '\n';
6265 : }
6266 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
6267 : }
6268 9 : size_t maxNameLen = 0;
6269 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6270 : {
6271 43 : maxNameLen = std::max(maxNameLen, subAlgName.size());
6272 : }
6273 52 : for (const auto &subAlgName : GetSubAlgorithmNames())
6274 : {
6275 86 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6276 43 : if (subAlg && !subAlg->IsHidden())
6277 : {
6278 43 : const std::string &name(subAlg->GetName());
6279 43 : osRet += " - ";
6280 43 : osRet += name;
6281 43 : osRet += ": ";
6282 43 : osRet.append(maxNameLen - name.size(), ' ');
6283 43 : osRet += subAlg->GetDescription();
6284 43 : if (!subAlg->m_aliases.empty())
6285 : {
6286 6 : bool first = true;
6287 6 : for (const auto &alias : subAlg->GetAliases())
6288 : {
6289 6 : if (alias ==
6290 : GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
6291 6 : break;
6292 0 : if (first)
6293 0 : osRet += " (alias: ";
6294 : else
6295 0 : osRet += ", ";
6296 0 : osRet += alias;
6297 0 : first = false;
6298 : }
6299 6 : if (!first)
6300 : {
6301 0 : osRet += ')';
6302 : }
6303 : }
6304 43 : osRet += '\n';
6305 : }
6306 : }
6307 :
6308 9 : if (shortUsage && hasNonPositionals)
6309 : {
6310 2 : osRet += "\nTry '";
6311 2 : osRet += osPath;
6312 2 : osRet += " --help' for help.\n";
6313 : }
6314 : }
6315 : else
6316 : {
6317 394 : if (!m_args.empty())
6318 : {
6319 394 : if (hasNonPositionals)
6320 394 : osRet += " [OPTIONS]";
6321 582 : for (const auto *arg : m_positionalArgs)
6322 : {
6323 261 : if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
6324 73 : (GetName() == "pipeline" && arg->GetName() == "pipeline"))
6325 : {
6326 : const bool optional =
6327 199 : (!arg->IsRequired() && !(GetName() == "pipeline" &&
6328 28 : arg->GetName() == "pipeline"));
6329 171 : osRet += ' ';
6330 171 : if (optional)
6331 25 : osRet += '[';
6332 171 : const std::string &metavar = arg->GetMetaVar();
6333 171 : if (!metavar.empty() && metavar[0] == '<')
6334 : {
6335 4 : osRet += metavar;
6336 : }
6337 : else
6338 : {
6339 167 : osRet += '<';
6340 167 : osRet += metavar;
6341 167 : osRet += '>';
6342 : }
6343 213 : if (arg->GetType() == GAAT_DATASET_LIST &&
6344 42 : arg->GetMaxCount() > 1)
6345 : {
6346 28 : osRet += "...";
6347 : }
6348 171 : if (optional)
6349 25 : osRet += ']';
6350 : }
6351 : }
6352 : }
6353 :
6354 394 : const size_t nLenFirstLine = osRet.size();
6355 394 : osRet += '\n';
6356 394 : if (usageOptions.isPipelineStep)
6357 : {
6358 309 : osRet.append(nLenFirstLine, '-');
6359 309 : osRet += '\n';
6360 : }
6361 :
6362 394 : if (shortUsage)
6363 : {
6364 21 : osRet += "Try '";
6365 21 : osRet += osPath;
6366 21 : osRet += " --help' for help.\n";
6367 21 : return osRet;
6368 : }
6369 :
6370 373 : osRet += '\n';
6371 373 : osRet += m_description;
6372 373 : osRet += '\n';
6373 : }
6374 :
6375 382 : if (!m_args.empty() && !shortUsage)
6376 : {
6377 760 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
6378 : size_t maxOptLen;
6379 380 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
6380 380 : if (usageOptions.maxOptLen)
6381 311 : maxOptLen = usageOptions.maxOptLen;
6382 :
6383 760 : const std::string userProvidedOpt = "--<user-provided-option>=<value>";
6384 380 : if (m_arbitraryLongNameArgsAllowed)
6385 2 : maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
6386 :
6387 : const auto OutputArg =
6388 2319 : [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
6389 20226 : const std::string &opt)
6390 : {
6391 2319 : osRet += " ";
6392 2319 : osRet += opt;
6393 2319 : osRet += " ";
6394 2319 : osRet.append(maxOptLen - opt.size(), ' ');
6395 2319 : osRet += arg->GetDescription();
6396 :
6397 2319 : const auto &choices = arg->GetChoices();
6398 2319 : if (!choices.empty())
6399 : {
6400 209 : osRet += ". ";
6401 209 : osRet += arg->GetMetaVar();
6402 209 : osRet += '=';
6403 209 : bool firstChoice = true;
6404 1665 : for (const auto &choice : choices)
6405 : {
6406 1456 : if (!firstChoice)
6407 1247 : osRet += '|';
6408 1456 : osRet += choice;
6409 1456 : firstChoice = false;
6410 : }
6411 : }
6412 :
6413 4572 : if (arg->GetType() == GAAT_DATASET ||
6414 2253 : arg->GetType() == GAAT_DATASET_LIST)
6415 : {
6416 132 : if (arg->GetDatasetInputFlags() == GADV_NAME &&
6417 17 : arg->GetDatasetOutputFlags() == GADV_OBJECT)
6418 : {
6419 9 : osRet += " (created by algorithm)";
6420 : }
6421 : }
6422 :
6423 2319 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
6424 : {
6425 175 : osRet += " (default: ";
6426 175 : osRet += arg->GetDefault<std::string>();
6427 175 : osRet += ')';
6428 : }
6429 2144 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
6430 : {
6431 66 : if (arg->GetDefault<bool>())
6432 0 : osRet += " (default: true)";
6433 : }
6434 2078 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
6435 : {
6436 76 : osRet += " (default: ";
6437 76 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
6438 76 : osRet += ')';
6439 : }
6440 2002 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
6441 : {
6442 49 : osRet += " (default: ";
6443 49 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
6444 49 : osRet += ')';
6445 : }
6446 2351 : else if (arg->GetType() == GAAT_STRING_LIST &&
6447 398 : arg->HasDefaultValue())
6448 : {
6449 : const auto &defaultVal =
6450 9 : arg->GetDefault<std::vector<std::string>>();
6451 9 : if (defaultVal.size() == 1)
6452 : {
6453 9 : osRet += " (default: ";
6454 9 : osRet += defaultVal[0];
6455 9 : osRet += ')';
6456 : }
6457 : }
6458 1971 : else if (arg->GetType() == GAAT_INTEGER_LIST &&
6459 27 : arg->HasDefaultValue())
6460 : {
6461 0 : const auto &defaultVal = arg->GetDefault<std::vector<int>>();
6462 0 : if (defaultVal.size() == 1)
6463 : {
6464 0 : osRet += " (default: ";
6465 0 : osRet += CPLSPrintf("%d", defaultVal[0]);
6466 0 : osRet += ')';
6467 : }
6468 : }
6469 1944 : else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
6470 : {
6471 0 : const auto &defaultVal = arg->GetDefault<std::vector<double>>();
6472 0 : if (defaultVal.size() == 1)
6473 : {
6474 0 : osRet += " (default: ";
6475 0 : osRet += CPLSPrintf("%g", defaultVal[0]);
6476 0 : osRet += ')';
6477 : }
6478 : }
6479 :
6480 2319 : if (arg->GetDisplayHintAboutRepetition())
6481 : {
6482 2352 : if (arg->GetMinCount() > 0 &&
6483 92 : arg->GetMinCount() == arg->GetMaxCount())
6484 : {
6485 18 : if (arg->GetMinCount() != 1)
6486 5 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
6487 : }
6488 2316 : else if (arg->GetMinCount() > 0 &&
6489 74 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
6490 : {
6491 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
6492 8 : arg->GetMaxCount());
6493 : }
6494 2234 : else if (arg->GetMinCount() > 0)
6495 : {
6496 66 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
6497 : }
6498 2168 : else if (arg->GetMaxCount() > 1)
6499 : {
6500 387 : osRet += " [may be repeated]";
6501 : }
6502 : }
6503 :
6504 2319 : if (arg->IsRequired())
6505 : {
6506 169 : osRet += " [required]";
6507 : }
6508 :
6509 2319 : osRet += '\n';
6510 :
6511 2319 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6512 2319 : if (!mutualExclusionGroup.empty())
6513 : {
6514 410 : std::string otherArgs;
6515 3914 : for (const auto &otherArg : m_args)
6516 : {
6517 6762 : if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
6518 3053 : otherArg.get() == arg)
6519 861 : continue;
6520 2848 : if (otherArg->GetMutualExclusionGroup() ==
6521 : mutualExclusionGroup)
6522 : {
6523 278 : if (!otherArgs.empty())
6524 77 : otherArgs += ", ";
6525 278 : otherArgs += "--";
6526 278 : otherArgs += otherArg->GetName();
6527 : }
6528 : }
6529 205 : if (!otherArgs.empty())
6530 : {
6531 201 : osRet += " ";
6532 201 : osRet += " ";
6533 201 : osRet.append(maxOptLen, ' ');
6534 201 : osRet += "Mutually exclusive with ";
6535 201 : osRet += otherArgs;
6536 201 : osRet += '\n';
6537 : }
6538 : }
6539 2319 : };
6540 :
6541 380 : if (!m_positionalArgs.empty())
6542 : {
6543 150 : osRet += "\nPositional arguments:\n";
6544 1601 : for (const auto &[arg, opt] : options)
6545 : {
6546 1451 : if (arg->IsPositional())
6547 141 : OutputArg(arg, opt);
6548 : }
6549 : }
6550 :
6551 380 : if (hasNonPositionals)
6552 : {
6553 380 : bool hasCommon = false;
6554 380 : bool hasBase = false;
6555 380 : bool hasAdvanced = false;
6556 380 : bool hasEsoteric = false;
6557 760 : std::vector<std::string> categories;
6558 3670 : for (const auto &iter : options)
6559 : {
6560 3290 : const auto &arg = iter.first;
6561 3290 : if (!arg->IsPositional())
6562 : {
6563 3149 : const auto &category = arg->GetCategory();
6564 3149 : if (category == GAAC_COMMON)
6565 : {
6566 1183 : hasCommon = true;
6567 : }
6568 1966 : else if (category == GAAC_BASE)
6569 : {
6570 1737 : hasBase = true;
6571 : }
6572 229 : else if (category == GAAC_ADVANCED)
6573 : {
6574 178 : hasAdvanced = true;
6575 : }
6576 51 : else if (category == GAAC_ESOTERIC)
6577 : {
6578 18 : hasEsoteric = true;
6579 : }
6580 33 : else if (std::find(categories.begin(), categories.end(),
6581 33 : category) == categories.end())
6582 : {
6583 9 : categories.push_back(category);
6584 : }
6585 : }
6586 : }
6587 380 : if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
6588 67 : categories.insert(categories.begin(), GAAC_ADVANCED);
6589 380 : if (hasBase)
6590 334 : categories.insert(categories.begin(), GAAC_BASE);
6591 380 : if (hasCommon && !usageOptions.isPipelineStep)
6592 66 : categories.insert(categories.begin(), GAAC_COMMON);
6593 380 : if (hasEsoteric)
6594 6 : categories.push_back(GAAC_ESOTERIC);
6595 :
6596 862 : for (const auto &category : categories)
6597 : {
6598 482 : osRet += "\n";
6599 482 : if (category != GAAC_BASE)
6600 : {
6601 148 : osRet += category;
6602 148 : osRet += ' ';
6603 : }
6604 482 : osRet += "Options:\n";
6605 5193 : for (const auto &[arg, opt] : options)
6606 : {
6607 4711 : if (!arg->IsPositional() && arg->GetCategory() == category)
6608 2178 : OutputArg(arg, opt);
6609 : }
6610 482 : if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
6611 : {
6612 2 : osRet += " ";
6613 2 : osRet += userProvidedOpt;
6614 2 : osRet += " ";
6615 2 : if (userProvidedOpt.size() < maxOptLen)
6616 0 : osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
6617 2 : osRet += "Argument provided by user";
6618 2 : osRet += '\n';
6619 : }
6620 : }
6621 : }
6622 : }
6623 :
6624 382 : if (!m_longDescription.empty())
6625 : {
6626 6 : osRet += '\n';
6627 6 : osRet += m_longDescription;
6628 6 : osRet += '\n';
6629 : }
6630 :
6631 382 : if (!m_helpDocRequested && !usageOptions.isPipelineMain)
6632 : {
6633 369 : if (!m_helpURL.empty())
6634 : {
6635 369 : osRet += "\nFor more details, consult ";
6636 369 : osRet += GetHelpFullURL();
6637 369 : osRet += '\n';
6638 : }
6639 369 : osRet += GetUsageForCLIEnd();
6640 : }
6641 :
6642 382 : return osRet;
6643 : }
6644 :
6645 : /************************************************************************/
6646 : /* GDALAlgorithm::GetUsageForCLIEnd() */
6647 : /************************************************************************/
6648 :
6649 : //! @cond Doxygen_Suppress
6650 376 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
6651 : {
6652 376 : std::string osRet;
6653 :
6654 376 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
6655 : {
6656 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
6657 : "alternative interface to GDAL and OGR command line "
6658 : "utilities.\nThe project reserves the right to modify, "
6659 : "rename, reorganize, and change the behavior of the utility\n"
6660 : "until it is officially frozen in a future feature release of "
6661 13 : "GDAL.\n";
6662 : }
6663 376 : return osRet;
6664 : }
6665 :
6666 : //! @endcond
6667 :
6668 : /************************************************************************/
6669 : /* GDALAlgorithm::GetUsageAsJSON() */
6670 : /************************************************************************/
6671 :
6672 561 : std::string GDALAlgorithm::GetUsageAsJSON() const
6673 : {
6674 1122 : CPLJSONDocument oDoc;
6675 1122 : auto oRoot = oDoc.GetRoot();
6676 :
6677 561 : if (m_displayInJSONUsage)
6678 : {
6679 559 : oRoot.Add("name", m_name);
6680 559 : CPLJSONArray jFullPath;
6681 1160 : for (const std::string &s : m_callPath)
6682 : {
6683 601 : jFullPath.Add(s);
6684 : }
6685 559 : oRoot.Add("full_path", jFullPath);
6686 : }
6687 :
6688 561 : oRoot.Add("description", m_description);
6689 561 : if (!m_helpURL.empty())
6690 : {
6691 560 : oRoot.Add("short_url", m_helpURL);
6692 560 : oRoot.Add("url", GetHelpFullURL());
6693 : }
6694 :
6695 1122 : CPLJSONArray jSubAlgorithms;
6696 761 : for (const auto &subAlgName : GetSubAlgorithmNames())
6697 : {
6698 400 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
6699 200 : if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
6700 : {
6701 198 : CPLJSONDocument oSubDoc;
6702 198 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
6703 198 : jSubAlgorithms.Add(oSubDoc.GetRoot());
6704 : }
6705 : }
6706 561 : oRoot.Add("sub_algorithms", jSubAlgorithms);
6707 :
6708 561 : if (m_arbitraryLongNameArgsAllowed)
6709 : {
6710 1 : oRoot.Add("user_provided_arguments_allowed", true);
6711 : }
6712 :
6713 5333 : const auto ProcessArg = [](const GDALAlgorithmArg *arg)
6714 : {
6715 5333 : CPLJSONObject jArg;
6716 5333 : jArg.Add("name", arg->GetName());
6717 5333 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
6718 5333 : jArg.Add("description", arg->GetDescription());
6719 :
6720 5333 : const auto &metaVar = arg->GetMetaVar();
6721 5333 : if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
6722 : {
6723 1611 : if (metaVar.front() == '<' && metaVar.back() == '>' &&
6724 1611 : metaVar.substr(1, metaVar.size() - 2).find('>') ==
6725 : std::string::npos)
6726 32 : jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
6727 : else
6728 834 : jArg.Add("metavar", metaVar);
6729 : }
6730 :
6731 5333 : const auto &choices = arg->GetChoices();
6732 5333 : if (!choices.empty())
6733 : {
6734 389 : CPLJSONArray jChoices;
6735 3400 : for (const auto &choice : choices)
6736 3011 : jChoices.Add(choice);
6737 389 : jArg.Add("choices", jChoices);
6738 : }
6739 5333 : if (arg->HasDefaultValue())
6740 : {
6741 1165 : switch (arg->GetType())
6742 : {
6743 420 : case GAAT_BOOLEAN:
6744 420 : jArg.Add("default", arg->GetDefault<bool>());
6745 420 : break;
6746 348 : case GAAT_STRING:
6747 348 : jArg.Add("default", arg->GetDefault<std::string>());
6748 348 : break;
6749 199 : case GAAT_INTEGER:
6750 199 : jArg.Add("default", arg->GetDefault<int>());
6751 199 : break;
6752 178 : case GAAT_REAL:
6753 178 : jArg.Add("default", arg->GetDefault<double>());
6754 178 : break;
6755 18 : case GAAT_STRING_LIST:
6756 : {
6757 : const auto &val =
6758 18 : arg->GetDefault<std::vector<std::string>>();
6759 18 : if (val.size() == 1)
6760 : {
6761 17 : jArg.Add("default", val[0]);
6762 : }
6763 : else
6764 : {
6765 1 : CPLJSONArray jArr;
6766 3 : for (const auto &s : val)
6767 : {
6768 2 : jArr.Add(s);
6769 : }
6770 1 : jArg.Add("default", jArr);
6771 : }
6772 18 : break;
6773 : }
6774 1 : case GAAT_INTEGER_LIST:
6775 : {
6776 1 : const auto &val = arg->GetDefault<std::vector<int>>();
6777 1 : if (val.size() == 1)
6778 : {
6779 0 : jArg.Add("default", val[0]);
6780 : }
6781 : else
6782 : {
6783 1 : CPLJSONArray jArr;
6784 3 : for (int i : val)
6785 : {
6786 2 : jArr.Add(i);
6787 : }
6788 1 : jArg.Add("default", jArr);
6789 : }
6790 1 : break;
6791 : }
6792 1 : case GAAT_REAL_LIST:
6793 : {
6794 1 : const auto &val = arg->GetDefault<std::vector<double>>();
6795 1 : if (val.size() == 1)
6796 : {
6797 0 : jArg.Add("default", val[0]);
6798 : }
6799 : else
6800 : {
6801 1 : CPLJSONArray jArr;
6802 3 : for (double d : val)
6803 : {
6804 2 : jArr.Add(d);
6805 : }
6806 1 : jArg.Add("default", jArr);
6807 : }
6808 1 : break;
6809 : }
6810 0 : case GAAT_DATASET:
6811 : case GAAT_DATASET_LIST:
6812 0 : CPLError(CE_Warning, CPLE_AppDefined,
6813 : "Unhandled default value for arg %s",
6814 0 : arg->GetName().c_str());
6815 0 : break;
6816 : }
6817 : }
6818 :
6819 5333 : const auto [minVal, minValIsIncluded] = arg->GetMinValue();
6820 5333 : if (!std::isnan(minVal))
6821 : {
6822 668 : if (arg->GetType() == GAAT_INTEGER ||
6823 260 : arg->GetType() == GAAT_INTEGER_LIST)
6824 171 : jArg.Add("min_value", static_cast<int>(minVal));
6825 : else
6826 237 : jArg.Add("min_value", minVal);
6827 408 : jArg.Add("min_value_is_included", minValIsIncluded);
6828 : }
6829 :
6830 5333 : const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
6831 5333 : if (!std::isnan(maxVal))
6832 : {
6833 199 : if (arg->GetType() == GAAT_INTEGER ||
6834 82 : arg->GetType() == GAAT_INTEGER_LIST)
6835 35 : jArg.Add("max_value", static_cast<int>(maxVal));
6836 : else
6837 82 : jArg.Add("max_value", maxVal);
6838 117 : jArg.Add("max_value_is_included", maxValIsIncluded);
6839 : }
6840 :
6841 5333 : jArg.Add("required", arg->IsRequired());
6842 5333 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
6843 : {
6844 1493 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
6845 1493 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
6846 1493 : jArg.Add("min_count", arg->GetMinCount());
6847 1493 : jArg.Add("max_count", arg->GetMaxCount());
6848 : }
6849 5333 : jArg.Add("category", arg->GetCategory());
6850 :
6851 10418 : if (arg->GetType() == GAAT_DATASET ||
6852 5085 : arg->GetType() == GAAT_DATASET_LIST)
6853 : {
6854 : {
6855 449 : CPLJSONArray jAr;
6856 449 : if (arg->GetDatasetType() & GDAL_OF_RASTER)
6857 307 : jAr.Add("raster");
6858 449 : if (arg->GetDatasetType() & GDAL_OF_VECTOR)
6859 179 : jAr.Add("vector");
6860 449 : if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
6861 28 : jAr.Add("multidim_raster");
6862 449 : jArg.Add("dataset_type", jAr);
6863 : }
6864 :
6865 614 : const auto GetFlags = [](int flags)
6866 : {
6867 614 : CPLJSONArray jAr;
6868 614 : if (flags & GADV_NAME)
6869 449 : jAr.Add("name");
6870 614 : if (flags & GADV_OBJECT)
6871 572 : jAr.Add("dataset");
6872 614 : return jAr;
6873 : };
6874 :
6875 449 : if (arg->IsInput())
6876 : {
6877 449 : jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
6878 : }
6879 449 : if (arg->IsOutput())
6880 : {
6881 165 : jArg.Add("output_flags",
6882 330 : GetFlags(arg->GetDatasetOutputFlags()));
6883 : }
6884 : }
6885 :
6886 5333 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
6887 5333 : if (!mutualExclusionGroup.empty())
6888 : {
6889 655 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
6890 : }
6891 :
6892 10666 : const auto &metadata = arg->GetMetadata();
6893 5333 : if (!metadata.empty())
6894 : {
6895 444 : CPLJSONObject jMetadata;
6896 927 : for (const auto &[key, values] : metadata)
6897 : {
6898 966 : CPLJSONArray jValue;
6899 1169 : for (const auto &value : values)
6900 686 : jValue.Add(value);
6901 483 : jMetadata.Add(key, jValue);
6902 : }
6903 444 : jArg.Add("metadata", jMetadata);
6904 : }
6905 :
6906 10666 : return jArg;
6907 : };
6908 :
6909 : {
6910 561 : CPLJSONArray jArgs;
6911 8820 : for (const auto &arg : m_args)
6912 : {
6913 8259 : if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
6914 5111 : jArgs.Add(ProcessArg(arg.get()));
6915 : }
6916 561 : oRoot.Add("input_arguments", jArgs);
6917 : }
6918 :
6919 : {
6920 561 : CPLJSONArray jArgs;
6921 8820 : for (const auto &arg : m_args)
6922 : {
6923 8259 : if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
6924 57 : jArgs.Add(ProcessArg(arg.get()));
6925 : }
6926 561 : oRoot.Add("output_arguments", jArgs);
6927 : }
6928 :
6929 : {
6930 561 : CPLJSONArray jArgs;
6931 8820 : for (const auto &arg : m_args)
6932 : {
6933 8259 : if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
6934 165 : jArgs.Add(ProcessArg(arg.get()));
6935 : }
6936 561 : oRoot.Add("input_output_arguments", jArgs);
6937 : }
6938 :
6939 561 : if (m_supportsStreamedOutput)
6940 : {
6941 115 : oRoot.Add("supports_streamed_output", true);
6942 : }
6943 :
6944 1122 : return oDoc.SaveAsString();
6945 : }
6946 :
6947 : /************************************************************************/
6948 : /* GDALAlgorithm::GetAutoComplete() */
6949 : /************************************************************************/
6950 :
6951 : std::vector<std::string>
6952 242 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
6953 : bool lastWordIsComplete, bool showAllOptions)
6954 : {
6955 484 : std::vector<std::string> ret;
6956 :
6957 : // Get inner-most algorithm
6958 242 : std::unique_ptr<GDALAlgorithm> curAlgHolder;
6959 242 : GDALAlgorithm *curAlg = this;
6960 479 : while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
6961 : {
6962 : auto subAlg = curAlg->InstantiateSubAlgorithm(
6963 344 : args.front(), /* suggestionAllowed = */ false);
6964 344 : if (!subAlg)
6965 106 : break;
6966 238 : if (args.size() == 1 && !lastWordIsComplete)
6967 : {
6968 5 : int nCount = 0;
6969 115 : for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
6970 : {
6971 110 : if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
6972 6 : nCount++;
6973 : }
6974 5 : if (nCount >= 2)
6975 : {
6976 11 : for (const std::string &subAlgName :
6977 23 : curAlg->GetSubAlgorithmNames())
6978 : {
6979 11 : subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
6980 11 : if (subAlg && !subAlg->IsHidden())
6981 11 : ret.push_back(subAlg->GetName());
6982 : }
6983 1 : return ret;
6984 : }
6985 : }
6986 237 : showAllOptions = false;
6987 237 : args.erase(args.begin());
6988 237 : curAlgHolder = std::move(subAlg);
6989 237 : curAlg = curAlgHolder.get();
6990 : }
6991 241 : if (curAlg != this)
6992 : {
6993 129 : curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
6994 : return curAlg->GetAutoComplete(args, lastWordIsComplete,
6995 129 : /* showAllOptions = */ false);
6996 : }
6997 :
6998 224 : std::string option;
6999 224 : std::string value;
7000 112 : ExtractLastOptionAndValue(args, option, value);
7001 :
7002 139 : if (option.empty() && !args.empty() && !args.back().empty() &&
7003 27 : args.back()[0] == '-')
7004 : {
7005 24 : const auto &lastArg = args.back();
7006 : // List available options
7007 339 : for (const auto &arg : GetArgs())
7008 : {
7009 583 : if (arg->IsHidden() || arg->IsHiddenForCLI() ||
7010 531 : (!showAllOptions &&
7011 720 : (arg->GetName() == "help" || arg->GetName() == "config" ||
7012 434 : arg->GetName() == "version" ||
7013 217 : arg->GetName() == "json-usage")))
7014 : {
7015 116 : continue;
7016 : }
7017 199 : if (!arg->GetShortName().empty())
7018 : {
7019 126 : std::string str = std::string("-").append(arg->GetShortName());
7020 42 : if (lastArg == str)
7021 0 : ret.push_back(std::move(str));
7022 : }
7023 199 : if (lastArg != "-" && lastArg != "--")
7024 : {
7025 52 : for (const std::string &alias : arg->GetAliases())
7026 : {
7027 48 : std::string str = std::string("--").append(alias);
7028 16 : if (cpl::starts_with(str, lastArg))
7029 3 : ret.push_back(std::move(str));
7030 : }
7031 : }
7032 199 : if (!arg->GetName().empty())
7033 : {
7034 597 : std::string str = std::string("--").append(arg->GetName());
7035 199 : if (cpl::starts_with(str, lastArg))
7036 165 : ret.push_back(std::move(str));
7037 : }
7038 : }
7039 24 : std::sort(ret.begin(), ret.end());
7040 : }
7041 88 : else if (!option.empty())
7042 : {
7043 : // List possible choices for current option
7044 82 : auto arg = GetArg(option);
7045 82 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7046 : {
7047 82 : ret = arg->GetChoices();
7048 82 : if (ret.empty())
7049 : {
7050 : {
7051 77 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7052 77 : SetParseForAutoCompletion();
7053 77 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7054 : }
7055 77 : ret = arg->GetAutoCompleteChoices(value);
7056 : }
7057 : else
7058 : {
7059 5 : std::sort(ret.begin(), ret.end());
7060 : }
7061 82 : if (!ret.empty() && ret.back() == value)
7062 : {
7063 2 : ret.clear();
7064 : }
7065 80 : else if (ret.empty())
7066 : {
7067 9 : ret.push_back("**");
7068 : // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
7069 18 : ret.push_back(std::string("\xC2\xA0"
7070 : "description: ")
7071 9 : .append(arg->GetDescription()));
7072 : }
7073 : }
7074 : }
7075 : else
7076 : {
7077 : // List possible sub-algorithms
7078 59 : for (const std::string &subAlgName : GetSubAlgorithmNames())
7079 : {
7080 106 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
7081 53 : if (subAlg && !subAlg->IsHidden())
7082 53 : ret.push_back(subAlg->GetName());
7083 : }
7084 6 : if (!ret.empty())
7085 : {
7086 2 : std::sort(ret.begin(), ret.end());
7087 : }
7088 :
7089 : // Try filenames
7090 6 : if (ret.empty() && !args.empty())
7091 : {
7092 : {
7093 3 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
7094 3 : SetParseForAutoCompletion();
7095 3 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
7096 : }
7097 :
7098 3 : const std::string &lastArg = args.back();
7099 3 : GDALAlgorithmArg *arg = nullptr;
7100 18 : for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
7101 21 : "like", "source", "destination"})
7102 : {
7103 18 : if (!arg)
7104 : {
7105 3 : auto newArg = GetArg(name);
7106 3 : if (newArg)
7107 : {
7108 3 : if (!newArg->IsExplicitlySet())
7109 : {
7110 0 : arg = newArg;
7111 : }
7112 6 : else if (newArg->GetType() == GAAT_STRING ||
7113 5 : newArg->GetType() == GAAT_STRING_LIST ||
7114 8 : newArg->GetType() == GAAT_DATASET ||
7115 2 : newArg->GetType() == GAAT_DATASET_LIST)
7116 : {
7117 : VSIStatBufL sStat;
7118 5 : if ((!lastArg.empty() && lastArg.back() == '/') ||
7119 2 : VSIStatL(lastArg.c_str(), &sStat) != 0)
7120 : {
7121 3 : arg = newArg;
7122 : }
7123 : }
7124 : }
7125 : }
7126 : }
7127 3 : if (arg)
7128 : {
7129 3 : ret = arg->GetAutoCompleteChoices(lastArg);
7130 : }
7131 : }
7132 : }
7133 :
7134 112 : return ret;
7135 : }
7136 :
7137 : /************************************************************************/
7138 : /* GDALAlgorithm::GetFieldIndices() */
7139 : /************************************************************************/
7140 :
7141 44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
7142 : OGRLayerH hLayer, std::vector<int> &indices)
7143 : {
7144 44 : VALIDATE_POINTER1(hLayer, __func__, false);
7145 :
7146 44 : const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
7147 :
7148 44 : if (names.size() == 1 && names[0] == "ALL")
7149 : {
7150 12 : const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
7151 28 : for (int i = 0; i < nSrcFieldCount; ++i)
7152 : {
7153 16 : indices.push_back(i);
7154 : }
7155 : }
7156 32 : else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
7157 : {
7158 6 : std::set<int> fieldsAdded;
7159 14 : for (const std::string &osFieldName : names)
7160 : {
7161 :
7162 : const int nIdx =
7163 10 : layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
7164 :
7165 10 : if (nIdx < 0)
7166 : {
7167 2 : CPLError(CE_Failure, CPLE_AppDefined,
7168 : "Field '%s' does not exist in layer '%s'",
7169 2 : osFieldName.c_str(), layer.GetName());
7170 2 : return false;
7171 : }
7172 :
7173 8 : if (fieldsAdded.insert(nIdx).second)
7174 : {
7175 7 : indices.push_back(nIdx);
7176 : }
7177 : }
7178 : }
7179 :
7180 42 : return true;
7181 : }
7182 :
7183 : /************************************************************************/
7184 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
7185 : /************************************************************************/
7186 :
7187 112 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
7188 : std::string &option,
7189 : std::string &value) const
7190 : {
7191 112 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
7192 : {
7193 82 : const auto nPosEqual = args.back().find('=');
7194 82 : if (nPosEqual == std::string::npos)
7195 : {
7196 : // Deal with "gdal ... --option"
7197 63 : if (GetArg(args.back()))
7198 : {
7199 39 : option = args.back();
7200 39 : args.pop_back();
7201 : }
7202 : }
7203 : else
7204 : {
7205 : // Deal with "gdal ... --option=<value>"
7206 19 : if (GetArg(args.back().substr(0, nPosEqual)))
7207 : {
7208 19 : option = args.back().substr(0, nPosEqual);
7209 19 : value = args.back().substr(nPosEqual + 1);
7210 19 : args.pop_back();
7211 : }
7212 : }
7213 : }
7214 55 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
7215 25 : args[args.size() - 2][0] == '-')
7216 : {
7217 : // Deal with "gdal ... --option <value>"
7218 24 : auto arg = GetArg(args[args.size() - 2]);
7219 24 : if (arg && arg->GetType() != GAAT_BOOLEAN)
7220 : {
7221 24 : option = args[args.size() - 2];
7222 24 : value = args.back();
7223 24 : args.pop_back();
7224 : }
7225 : }
7226 :
7227 112 : const auto IsKeyValueOption = [](const std::string &osStr)
7228 : {
7229 302 : return osStr == "--co" || osStr == "--creation-option" ||
7230 279 : osStr == "--lco" || osStr == "--layer-creation-option" ||
7231 300 : osStr == "--oo" || osStr == "--open-option";
7232 : };
7233 :
7234 112 : if (IsKeyValueOption(option))
7235 : {
7236 22 : const auto nPosEqual = value.find('=');
7237 22 : if (nPosEqual != std::string::npos)
7238 : {
7239 11 : value.resize(nPosEqual);
7240 : }
7241 : }
7242 112 : }
7243 :
7244 : //! @cond Doxygen_Suppress
7245 :
7246 : /************************************************************************/
7247 : /* GDALContainerAlgorithm::RunImpl() */
7248 : /************************************************************************/
7249 :
7250 0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
7251 : {
7252 0 : return false;
7253 : }
7254 :
7255 : //! @endcond
7256 :
7257 : /************************************************************************/
7258 : /* GDALAlgorithmRelease() */
7259 : /************************************************************************/
7260 :
7261 : /** Release a handle to an algorithm.
7262 : *
7263 : * @since 3.11
7264 : */
7265 12136 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
7266 : {
7267 12136 : delete hAlg;
7268 12136 : }
7269 :
7270 : /************************************************************************/
7271 : /* GDALAlgorithmGetName() */
7272 : /************************************************************************/
7273 :
7274 : /** Return the algorithm name.
7275 : *
7276 : * @param hAlg Handle to an algorithm. Must NOT be null.
7277 : * @return algorithm name whose lifetime is bound to hAlg and which must not
7278 : * be freed.
7279 : * @since 3.11
7280 : */
7281 5698 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
7282 : {
7283 5698 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7284 5698 : return hAlg->ptr->GetName().c_str();
7285 : }
7286 :
7287 : /************************************************************************/
7288 : /* GDALAlgorithmGetDescription() */
7289 : /************************************************************************/
7290 :
7291 : /** Return the algorithm (short) description.
7292 : *
7293 : * @param hAlg Handle to an algorithm. Must NOT be null.
7294 : * @return algorithm description whose lifetime is bound to hAlg and which must
7295 : * not be freed.
7296 : * @since 3.11
7297 : */
7298 5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
7299 : {
7300 5619 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7301 5619 : return hAlg->ptr->GetDescription().c_str();
7302 : }
7303 :
7304 : /************************************************************************/
7305 : /* GDALAlgorithmGetLongDescription() */
7306 : /************************************************************************/
7307 :
7308 : /** Return the algorithm (longer) description.
7309 : *
7310 : * @param hAlg Handle to an algorithm. Must NOT be null.
7311 : * @return algorithm description whose lifetime is bound to hAlg and which must
7312 : * not be freed.
7313 : * @since 3.11
7314 : */
7315 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
7316 : {
7317 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7318 2 : return hAlg->ptr->GetLongDescription().c_str();
7319 : }
7320 :
7321 : /************************************************************************/
7322 : /* GDALAlgorithmGetHelpFullURL() */
7323 : /************************************************************************/
7324 :
7325 : /** Return the algorithm full URL.
7326 : *
7327 : * @param hAlg Handle to an algorithm. Must NOT be null.
7328 : * @return algorithm URL whose lifetime is bound to hAlg and which must
7329 : * not be freed.
7330 : * @since 3.11
7331 : */
7332 4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
7333 : {
7334 4963 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7335 4963 : return hAlg->ptr->GetHelpFullURL().c_str();
7336 : }
7337 :
7338 : /************************************************************************/
7339 : /* GDALAlgorithmHasSubAlgorithms() */
7340 : /************************************************************************/
7341 :
7342 : /** Return whether the algorithm has sub-algorithms.
7343 : *
7344 : * @param hAlg Handle to an algorithm. Must NOT be null.
7345 : * @since 3.11
7346 : */
7347 9024 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
7348 : {
7349 9024 : VALIDATE_POINTER1(hAlg, __func__, false);
7350 9024 : return hAlg->ptr->HasSubAlgorithms();
7351 : }
7352 :
7353 : /************************************************************************/
7354 : /* GDALAlgorithmGetSubAlgorithmNames() */
7355 : /************************************************************************/
7356 :
7357 : /** Get the names of registered algorithms.
7358 : *
7359 : * @param hAlg Handle to an algorithm. Must NOT be null.
7360 : * @return a NULL terminated list of names, which must be destroyed with
7361 : * CSLDestroy()
7362 : * @since 3.11
7363 : */
7364 707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
7365 : {
7366 707 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7367 707 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
7368 : }
7369 :
7370 : /************************************************************************/
7371 : /* GDALAlgorithmInstantiateSubAlgorithm() */
7372 : /************************************************************************/
7373 :
7374 : /** Instantiate an algorithm by its name (or its alias).
7375 : *
7376 : * @param hAlg Handle to an algorithm. Must NOT be null.
7377 : * @param pszSubAlgName Algorithm name. Must NOT be null.
7378 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
7379 : * or NULL if the algorithm does not exist or another error occurred.
7380 : * @since 3.11
7381 : */
7382 8468 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
7383 : const char *pszSubAlgName)
7384 : {
7385 8468 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7386 8468 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
7387 16936 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
7388 : return subAlg
7389 16936 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
7390 16936 : : nullptr;
7391 : }
7392 :
7393 : /************************************************************************/
7394 : /* GDALAlgorithmParseCommandLineArguments() */
7395 : /************************************************************************/
7396 :
7397 : /** Parse a command line argument, which does not include the algorithm
7398 : * name, to set the value of corresponding arguments.
7399 : *
7400 : * @param hAlg Handle to an algorithm. Must NOT be null.
7401 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
7402 : * @return true if successful, false otherwise
7403 : * @since 3.11
7404 : */
7405 :
7406 352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
7407 : CSLConstList papszArgs)
7408 : {
7409 352 : VALIDATE_POINTER1(hAlg, __func__, false);
7410 352 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
7411 : }
7412 :
7413 : /************************************************************************/
7414 : /* GDALAlgorithmGetActualAlgorithm() */
7415 : /************************************************************************/
7416 :
7417 : /** Return the actual algorithm that is going to be invoked, when the
7418 : * current algorithm has sub-algorithms.
7419 : *
7420 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
7421 : *
7422 : * Note that the lifetime of the returned algorithm does not exceed the one of
7423 : * the hAlg instance that owns it.
7424 : *
7425 : * @param hAlg Handle to an algorithm. Must NOT be null.
7426 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
7427 : * @since 3.11
7428 : */
7429 913 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
7430 : {
7431 913 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7432 913 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
7433 : }
7434 :
7435 : /************************************************************************/
7436 : /* GDALAlgorithmRun() */
7437 : /************************************************************************/
7438 :
7439 : /** Execute the algorithm, starting with ValidateArguments() and then
7440 : * calling RunImpl().
7441 : *
7442 : * @param hAlg Handle to an algorithm. Must NOT be null.
7443 : * @param pfnProgress Progress callback. May be null.
7444 : * @param pProgressData Progress callback user data. May be null.
7445 : * @return true if successful, false otherwise
7446 : * @since 3.11
7447 : */
7448 :
7449 2612 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
7450 : void *pProgressData)
7451 : {
7452 2612 : VALIDATE_POINTER1(hAlg, __func__, false);
7453 2612 : return hAlg->ptr->Run(pfnProgress, pProgressData);
7454 : }
7455 :
7456 : /************************************************************************/
7457 : /* GDALAlgorithmFinalize() */
7458 : /************************************************************************/
7459 :
7460 : /** Complete any pending actions, and return the final status.
7461 : * This is typically useful for algorithm that generate an output dataset.
7462 : *
7463 : * Note that this function does *NOT* release memory associated with the
7464 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
7465 : *
7466 : * @param hAlg Handle to an algorithm. Must NOT be null.
7467 : * @return true if successful, false otherwise
7468 : * @since 3.11
7469 : */
7470 :
7471 879 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
7472 : {
7473 879 : VALIDATE_POINTER1(hAlg, __func__, false);
7474 879 : return hAlg->ptr->Finalize();
7475 : }
7476 :
7477 : /************************************************************************/
7478 : /* GDALAlgorithmGetUsageAsJSON() */
7479 : /************************************************************************/
7480 :
7481 : /** Return the usage of the algorithm as a JSON-serialized string.
7482 : *
7483 : * This can be used to dynamically generate interfaces to algorithms.
7484 : *
7485 : * @param hAlg Handle to an algorithm. Must NOT be null.
7486 : * @return a string that must be freed with CPLFree()
7487 : * @since 3.11
7488 : */
7489 4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
7490 : {
7491 4 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7492 4 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
7493 : }
7494 :
7495 : /************************************************************************/
7496 : /* GDALAlgorithmGetArgNames() */
7497 : /************************************************************************/
7498 :
7499 : /** Return the list of available argument names.
7500 : *
7501 : * @param hAlg Handle to an algorithm. Must NOT be null.
7502 : * @return a NULL terminated list of names, which must be destroyed with
7503 : * CSLDestroy()
7504 : * @since 3.11
7505 : */
7506 15408 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
7507 : {
7508 15408 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7509 30816 : CPLStringList list;
7510 342329 : for (const auto &arg : hAlg->ptr->GetArgs())
7511 326921 : list.AddString(arg->GetName().c_str());
7512 15408 : return list.StealList();
7513 : }
7514 :
7515 : /************************************************************************/
7516 : /* GDALAlgorithmGetArg() */
7517 : /************************************************************************/
7518 :
7519 : /** Return an argument from its name.
7520 : *
7521 : * The lifetime of the returned object does not exceed the one of hAlg.
7522 : *
7523 : * @param hAlg Handle to an algorithm. Must NOT be null.
7524 : * @param pszArgName Argument name. Must NOT be null.
7525 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7526 : * or nullptr in case of error
7527 : * @since 3.11
7528 : */
7529 327822 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
7530 : const char *pszArgName)
7531 : {
7532 327822 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7533 327822 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7534 655644 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7535 327822 : /* isConst = */ true);
7536 327822 : if (!arg)
7537 3 : return nullptr;
7538 327819 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7539 : }
7540 :
7541 : /************************************************************************/
7542 : /* GDALAlgorithmGetArgNonConst() */
7543 : /************************************************************************/
7544 :
7545 : /** Return an argument from its name, possibly allowing creation of user-provided
7546 : * argument if the algorithm allow it.
7547 : *
7548 : * The lifetime of the returned object does not exceed the one of hAlg.
7549 : *
7550 : * @param hAlg Handle to an algorithm. Must NOT be null.
7551 : * @param pszArgName Argument name. Must NOT be null.
7552 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
7553 : * or nullptr in case of error
7554 : * @since 3.12
7555 : */
7556 9349 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
7557 : const char *pszArgName)
7558 : {
7559 9349 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
7560 9349 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
7561 18698 : auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
7562 9349 : /* isConst = */ false);
7563 9349 : if (!arg)
7564 1 : return nullptr;
7565 9348 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
7566 : }
7567 :
7568 : /************************************************************************/
7569 : /* GDALAlgorithmArgRelease() */
7570 : /************************************************************************/
7571 :
7572 : /** Release a handle to an argument.
7573 : *
7574 : * @since 3.11
7575 : */
7576 337167 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
7577 : {
7578 337167 : delete hArg;
7579 337167 : }
7580 :
7581 : /************************************************************************/
7582 : /* GDALAlgorithmArgGetName() */
7583 : /************************************************************************/
7584 :
7585 : /** Return the name of an argument.
7586 : *
7587 : * @param hArg Handle to an argument. Must NOT be null.
7588 : * @return argument name whose lifetime is bound to hArg and which must not
7589 : * be freed.
7590 : * @since 3.11
7591 : */
7592 18601 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
7593 : {
7594 18601 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7595 18601 : return hArg->ptr->GetName().c_str();
7596 : }
7597 :
7598 : /************************************************************************/
7599 : /* GDALAlgorithmArgGetType() */
7600 : /************************************************************************/
7601 :
7602 : /** Get the type of an argument
7603 : *
7604 : * @param hArg Handle to an argument. Must NOT be null.
7605 : * @since 3.11
7606 : */
7607 417396 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
7608 : {
7609 417396 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
7610 417396 : return hArg->ptr->GetType();
7611 : }
7612 :
7613 : /************************************************************************/
7614 : /* GDALAlgorithmArgGetDescription() */
7615 : /************************************************************************/
7616 :
7617 : /** Return the description of an argument.
7618 : *
7619 : * @param hArg Handle to an argument. Must NOT be null.
7620 : * @return argument description whose lifetime is bound to hArg and which must not
7621 : * be freed.
7622 : * @since 3.11
7623 : */
7624 83313 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
7625 : {
7626 83313 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7627 83313 : return hArg->ptr->GetDescription().c_str();
7628 : }
7629 :
7630 : /************************************************************************/
7631 : /* GDALAlgorithmArgGetShortName() */
7632 : /************************************************************************/
7633 :
7634 : /** Return the short name, or empty string if there is none
7635 : *
7636 : * @param hArg Handle to an argument. Must NOT be null.
7637 : * @return short name whose lifetime is bound to hArg and which must not
7638 : * be freed.
7639 : * @since 3.11
7640 : */
7641 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
7642 : {
7643 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7644 1 : return hArg->ptr->GetShortName().c_str();
7645 : }
7646 :
7647 : /************************************************************************/
7648 : /* GDALAlgorithmArgGetAliases() */
7649 : /************************************************************************/
7650 :
7651 : /** Return the aliases (potentially none)
7652 : *
7653 : * @param hArg Handle to an argument. Must NOT be null.
7654 : * @return a NULL terminated list of names, which must be destroyed with
7655 : * CSLDestroy()
7656 :
7657 : * @since 3.11
7658 : */
7659 1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
7660 : {
7661 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7662 1 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
7663 : }
7664 :
7665 : /************************************************************************/
7666 : /* GDALAlgorithmArgGetMetaVar() */
7667 : /************************************************************************/
7668 :
7669 : /** Return the "meta-var" hint.
7670 : *
7671 : * By default, the meta-var value is the long name of the argument in
7672 : * upper case.
7673 : *
7674 : * @param hArg Handle to an argument. Must NOT be null.
7675 : * @return meta-var hint whose lifetime is bound to hArg and which must not
7676 : * be freed.
7677 : * @since 3.11
7678 : */
7679 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
7680 : {
7681 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7682 1 : return hArg->ptr->GetMetaVar().c_str();
7683 : }
7684 :
7685 : /************************************************************************/
7686 : /* GDALAlgorithmArgGetCategory() */
7687 : /************************************************************************/
7688 :
7689 : /** Return the argument category
7690 : *
7691 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
7692 : *
7693 : * @param hArg Handle to an argument. Must NOT be null.
7694 : * @return category whose lifetime is bound to hArg and which must not
7695 : * be freed.
7696 : * @since 3.11
7697 : */
7698 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
7699 : {
7700 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7701 1 : return hArg->ptr->GetCategory().c_str();
7702 : }
7703 :
7704 : /************************************************************************/
7705 : /* GDALAlgorithmArgIsPositional() */
7706 : /************************************************************************/
7707 :
7708 : /** Return if the argument is a positional one.
7709 : *
7710 : * @param hArg Handle to an argument. Must NOT be null.
7711 : * @since 3.11
7712 : */
7713 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
7714 : {
7715 1 : VALIDATE_POINTER1(hArg, __func__, false);
7716 1 : return hArg->ptr->IsPositional();
7717 : }
7718 :
7719 : /************************************************************************/
7720 : /* GDALAlgorithmArgIsRequired() */
7721 : /************************************************************************/
7722 :
7723 : /** Return whether the argument is required. Defaults to false.
7724 : *
7725 : * @param hArg Handle to an argument. Must NOT be null.
7726 : * @since 3.11
7727 : */
7728 157605 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
7729 : {
7730 157605 : VALIDATE_POINTER1(hArg, __func__, false);
7731 157605 : return hArg->ptr->IsRequired();
7732 : }
7733 :
7734 : /************************************************************************/
7735 : /* GDALAlgorithmArgGetMinCount() */
7736 : /************************************************************************/
7737 :
7738 : /** Return the minimum number of values for the argument.
7739 : *
7740 : * Defaults to 0.
7741 : * Only applies to list type of arguments.
7742 : *
7743 : * @param hArg Handle to an argument. Must NOT be null.
7744 : * @since 3.11
7745 : */
7746 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
7747 : {
7748 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7749 1 : return hArg->ptr->GetMinCount();
7750 : }
7751 :
7752 : /************************************************************************/
7753 : /* GDALAlgorithmArgGetMaxCount() */
7754 : /************************************************************************/
7755 :
7756 : /** Return the maximum number of values for the argument.
7757 : *
7758 : * Defaults to 1 for scalar types, and INT_MAX for list types.
7759 : * Only applies to list type of arguments.
7760 : *
7761 : * @param hArg Handle to an argument. Must NOT be null.
7762 : * @since 3.11
7763 : */
7764 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
7765 : {
7766 1 : VALIDATE_POINTER1(hArg, __func__, 0);
7767 1 : return hArg->ptr->GetMaxCount();
7768 : }
7769 :
7770 : /************************************************************************/
7771 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
7772 : /************************************************************************/
7773 :
7774 : /** Return whether, for list type of arguments, several values, space
7775 : * separated, may be specified. That is "--foo=bar,baz".
7776 : * The default is true.
7777 : *
7778 : * @param hArg Handle to an argument. Must NOT be null.
7779 : * @since 3.11
7780 : */
7781 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
7782 : {
7783 1 : VALIDATE_POINTER1(hArg, __func__, false);
7784 1 : return hArg->ptr->GetPackedValuesAllowed();
7785 : }
7786 :
7787 : /************************************************************************/
7788 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
7789 : /************************************************************************/
7790 :
7791 : /** Return whether, for list type of arguments, the argument may be
7792 : * repeated. That is "--foo=bar --foo=baz".
7793 : * The default is true.
7794 : *
7795 : * @param hArg Handle to an argument. Must NOT be null.
7796 : * @since 3.11
7797 : */
7798 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
7799 : {
7800 1 : VALIDATE_POINTER1(hArg, __func__, false);
7801 1 : return hArg->ptr->GetRepeatedArgAllowed();
7802 : }
7803 :
7804 : /************************************************************************/
7805 : /* GDALAlgorithmArgGetChoices() */
7806 : /************************************************************************/
7807 :
7808 : /** Return the allowed values (as strings) for the argument.
7809 : *
7810 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
7811 : *
7812 : * @param hArg Handle to an argument. Must NOT be null.
7813 : * @return a NULL terminated list of names, which must be destroyed with
7814 : * CSLDestroy()
7815 :
7816 : * @since 3.11
7817 : */
7818 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
7819 : {
7820 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7821 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
7822 : }
7823 :
7824 : /************************************************************************/
7825 : /* GDALAlgorithmArgGetMetadataItem() */
7826 : /************************************************************************/
7827 :
7828 : /** Return the values of the metadata item of an argument.
7829 : *
7830 : * @param hArg Handle to an argument. Must NOT be null.
7831 : * @param pszItem Name of the item. Must NOT be null.
7832 : * @return a NULL terminated list of values, which must be destroyed with
7833 : * CSLDestroy()
7834 :
7835 : * @since 3.11
7836 : */
7837 63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
7838 : const char *pszItem)
7839 : {
7840 63 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7841 63 : VALIDATE_POINTER1(pszItem, __func__, nullptr);
7842 63 : const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
7843 63 : return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
7844 : }
7845 :
7846 : /************************************************************************/
7847 : /* GDALAlgorithmArgIsExplicitlySet() */
7848 : /************************************************************************/
7849 :
7850 : /** Return whether the argument value has been explicitly set with Set()
7851 : *
7852 : * @param hArg Handle to an argument. Must NOT be null.
7853 : * @since 3.11
7854 : */
7855 600 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
7856 : {
7857 600 : VALIDATE_POINTER1(hArg, __func__, false);
7858 600 : return hArg->ptr->IsExplicitlySet();
7859 : }
7860 :
7861 : /************************************************************************/
7862 : /* GDALAlgorithmArgHasDefaultValue() */
7863 : /************************************************************************/
7864 :
7865 : /** Return if the argument has a declared default value.
7866 : *
7867 : * @param hArg Handle to an argument. Must NOT be null.
7868 : * @since 3.11
7869 : */
7870 2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
7871 : {
7872 2 : VALIDATE_POINTER1(hArg, __func__, false);
7873 2 : return hArg->ptr->HasDefaultValue();
7874 : }
7875 :
7876 : /************************************************************************/
7877 : /* GDALAlgorithmArgGetDefaultAsBoolean() */
7878 : /************************************************************************/
7879 :
7880 : /** Return the argument default value as a integer.
7881 : *
7882 : * Must only be called on arguments whose type is GAAT_BOOLEAN
7883 : *
7884 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7885 : * argument has a default value.
7886 : *
7887 : * @param hArg Handle to an argument. Must NOT be null.
7888 : * @since 3.12
7889 : */
7890 3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
7891 : {
7892 3 : VALIDATE_POINTER1(hArg, __func__, false);
7893 3 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
7894 : {
7895 1 : CPLError(CE_Failure, CPLE_AppDefined,
7896 : "%s must only be called on arguments of type GAAT_BOOLEAN",
7897 : __func__);
7898 1 : return false;
7899 : }
7900 2 : return hArg->ptr->GetDefault<bool>();
7901 : }
7902 :
7903 : /************************************************************************/
7904 : /* GDALAlgorithmArgGetDefaultAsString() */
7905 : /************************************************************************/
7906 :
7907 : /** Return the argument default value as a string.
7908 : *
7909 : * Must only be called on arguments whose type is GAAT_STRING.
7910 : *
7911 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7912 : * argument has a default value.
7913 : *
7914 : * @param hArg Handle to an argument. Must NOT be null.
7915 : * @return string whose lifetime is bound to hArg and which must not
7916 : * be freed.
7917 : * @since 3.11
7918 : */
7919 3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
7920 : {
7921 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
7922 3 : if (hArg->ptr->GetType() != GAAT_STRING)
7923 : {
7924 2 : CPLError(CE_Failure, CPLE_AppDefined,
7925 : "%s must only be called on arguments of type GAAT_STRING",
7926 : __func__);
7927 2 : return nullptr;
7928 : }
7929 1 : return hArg->ptr->GetDefault<std::string>().c_str();
7930 : }
7931 :
7932 : /************************************************************************/
7933 : /* GDALAlgorithmArgGetDefaultAsInteger() */
7934 : /************************************************************************/
7935 :
7936 : /** Return the argument default value as a integer.
7937 : *
7938 : * Must only be called on arguments whose type is GAAT_INTEGER
7939 : *
7940 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7941 : * argument has a default value.
7942 : *
7943 : * @param hArg Handle to an argument. Must NOT be null.
7944 : * @since 3.12
7945 : */
7946 3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
7947 : {
7948 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7949 3 : if (hArg->ptr->GetType() != GAAT_INTEGER)
7950 : {
7951 2 : CPLError(CE_Failure, CPLE_AppDefined,
7952 : "%s must only be called on arguments of type GAAT_INTEGER",
7953 : __func__);
7954 2 : return 0;
7955 : }
7956 1 : return hArg->ptr->GetDefault<int>();
7957 : }
7958 :
7959 : /************************************************************************/
7960 : /* GDALAlgorithmArgGetDefaultAsDouble() */
7961 : /************************************************************************/
7962 :
7963 : /** Return the argument default value as a double.
7964 : *
7965 : * Must only be called on arguments whose type is GAAT_REAL
7966 : *
7967 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7968 : * argument has a default value.
7969 : *
7970 : * @param hArg Handle to an argument. Must NOT be null.
7971 : * @since 3.12
7972 : */
7973 3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
7974 : {
7975 3 : VALIDATE_POINTER1(hArg, __func__, 0);
7976 3 : if (hArg->ptr->GetType() != GAAT_REAL)
7977 : {
7978 2 : CPLError(CE_Failure, CPLE_AppDefined,
7979 : "%s must only be called on arguments of type GAAT_REAL",
7980 : __func__);
7981 2 : return 0;
7982 : }
7983 1 : return hArg->ptr->GetDefault<double>();
7984 : }
7985 :
7986 : /************************************************************************/
7987 : /* GDALAlgorithmArgGetDefaultAsStringList() */
7988 : /************************************************************************/
7989 :
7990 : /** Return the argument default value as a string list.
7991 : *
7992 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
7993 : *
7994 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
7995 : * argument has a default value.
7996 : *
7997 : * @param hArg Handle to an argument. Must NOT be null.
7998 : * @return a NULL terminated list of names, which must be destroyed with
7999 : * CSLDestroy()
8000 :
8001 : * @since 3.12
8002 : */
8003 3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
8004 : {
8005 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8006 3 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8007 : {
8008 2 : CPLError(CE_Failure, CPLE_AppDefined,
8009 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8010 : __func__);
8011 2 : return nullptr;
8012 : }
8013 2 : return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
8014 1 : .StealList();
8015 : }
8016 :
8017 : /************************************************************************/
8018 : /* GDALAlgorithmArgGetDefaultAsIntegerList() */
8019 : /************************************************************************/
8020 :
8021 : /** Return the argument default value as a integer list.
8022 : *
8023 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8024 : *
8025 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8026 : * argument has a default value.
8027 : *
8028 : * @param hArg Handle to an argument. Must NOT be null.
8029 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8030 : * @since 3.12
8031 : */
8032 3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
8033 : size_t *pnCount)
8034 : {
8035 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8036 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8037 3 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8038 : {
8039 2 : CPLError(
8040 : CE_Failure, CPLE_AppDefined,
8041 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8042 : __func__);
8043 2 : *pnCount = 0;
8044 2 : return nullptr;
8045 : }
8046 1 : const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
8047 1 : *pnCount = val.size();
8048 1 : return val.data();
8049 : }
8050 :
8051 : /************************************************************************/
8052 : /* GDALAlgorithmArgGetDefaultAsDoubleList() */
8053 : /************************************************************************/
8054 :
8055 : /** Return the argument default value as a real list.
8056 : *
8057 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8058 : *
8059 : * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
8060 : * argument has a default value.
8061 : *
8062 : * @param hArg Handle to an argument. Must NOT be null.
8063 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8064 : * @since 3.12
8065 : */
8066 3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
8067 : size_t *pnCount)
8068 : {
8069 3 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8070 3 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8071 3 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8072 : {
8073 2 : CPLError(CE_Failure, CPLE_AppDefined,
8074 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8075 : __func__);
8076 2 : *pnCount = 0;
8077 2 : return nullptr;
8078 : }
8079 1 : const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
8080 1 : *pnCount = val.size();
8081 1 : return val.data();
8082 : }
8083 :
8084 : /************************************************************************/
8085 : /* GDALAlgorithmArgIsHidden() */
8086 : /************************************************************************/
8087 :
8088 : /** Return whether the argument is hidden (for GDAL internal use)
8089 : *
8090 : * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
8091 : * GDALAlgorithmArgIsHiddenForAPI().
8092 : *
8093 : * @param hArg Handle to an argument. Must NOT be null.
8094 : * @since 3.12
8095 : */
8096 1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
8097 : {
8098 1 : VALIDATE_POINTER1(hArg, __func__, false);
8099 1 : return hArg->ptr->IsHidden();
8100 : }
8101 :
8102 : /************************************************************************/
8103 : /* GDALAlgorithmArgIsHiddenForCLI() */
8104 : /************************************************************************/
8105 :
8106 : /** Return whether the argument must not be mentioned in CLI usage.
8107 : *
8108 : * For example, "output-value" for "gdal raster info", which is only
8109 : * meant when the algorithm is used from a non-CLI context.
8110 : *
8111 : * @param hArg Handle to an argument. Must NOT be null.
8112 : * @since 3.11
8113 : */
8114 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
8115 : {
8116 1 : VALIDATE_POINTER1(hArg, __func__, false);
8117 1 : return hArg->ptr->IsHiddenForCLI();
8118 : }
8119 :
8120 : /************************************************************************/
8121 : /* GDALAlgorithmArgIsHiddenForAPI() */
8122 : /************************************************************************/
8123 :
8124 : /** Return whether the argument must not be mentioned in the context of an
8125 : * API use.
8126 : * Said otherwise, if it is only for CLI usage.
8127 : *
8128 : * For example "--help"
8129 : *
8130 : * @param hArg Handle to an argument. Must NOT be null.
8131 : * @since 3.12
8132 : */
8133 213201 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
8134 : {
8135 213201 : VALIDATE_POINTER1(hArg, __func__, false);
8136 213201 : return hArg->ptr->IsHiddenForAPI();
8137 : }
8138 :
8139 : /************************************************************************/
8140 : /* GDALAlgorithmArgIsOnlyForCLI() */
8141 : /************************************************************************/
8142 :
8143 : /** Return whether the argument must not be mentioned in the context of an
8144 : * API use.
8145 : * Said otherwise, if it is only for CLI usage.
8146 : *
8147 : * For example "--help"
8148 : *
8149 : * @param hArg Handle to an argument. Must NOT be null.
8150 : * @since 3.11
8151 : * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
8152 : */
8153 0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
8154 : {
8155 0 : VALIDATE_POINTER1(hArg, __func__, false);
8156 0 : return hArg->ptr->IsHiddenForAPI();
8157 : }
8158 :
8159 : /************************************************************************/
8160 : /* GDALAlgorithmArgIsInput() */
8161 : /************************************************************************/
8162 :
8163 : /** Indicate whether the value of the argument is read-only during the
8164 : * execution of the algorithm.
8165 : *
8166 : * Default is true.
8167 : *
8168 : * @param hArg Handle to an argument. Must NOT be null.
8169 : * @since 3.11
8170 : */
8171 210331 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
8172 : {
8173 210331 : VALIDATE_POINTER1(hArg, __func__, false);
8174 210331 : return hArg->ptr->IsInput();
8175 : }
8176 :
8177 : /************************************************************************/
8178 : /* GDALAlgorithmArgIsOutput() */
8179 : /************************************************************************/
8180 :
8181 : /** Return whether (at least part of) the value of the argument is set
8182 : * during the execution of the algorithm.
8183 : *
8184 : * For example, "output-value" for "gdal raster info"
8185 : * Default is false.
8186 : * An argument may return both IsInput() and IsOutput() as true.
8187 : * For example the "gdal raster convert" algorithm consumes the dataset
8188 : * name of its "output" argument, and sets the dataset object during its
8189 : * execution.
8190 : *
8191 : * @param hArg Handle to an argument. Must NOT be null.
8192 : * @since 3.11
8193 : */
8194 116566 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
8195 : {
8196 116566 : VALIDATE_POINTER1(hArg, __func__, false);
8197 116566 : return hArg->ptr->IsOutput();
8198 : }
8199 :
8200 : /************************************************************************/
8201 : /* GDALAlgorithmArgGetDatasetType() */
8202 : /************************************************************************/
8203 :
8204 : /** Get which type of dataset is allowed / generated.
8205 : *
8206 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
8207 : * GDAL_OF_MULTIDIM_RASTER.
8208 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8209 : *
8210 : * @param hArg Handle to an argument. Must NOT be null.
8211 : * @since 3.11
8212 : */
8213 2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
8214 : {
8215 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8216 2 : return hArg->ptr->GetDatasetType();
8217 : }
8218 :
8219 : /************************************************************************/
8220 : /* GDALAlgorithmArgGetDatasetInputFlags() */
8221 : /************************************************************************/
8222 :
8223 : /** Indicates which components among name and dataset are accepted as
8224 : * input, when this argument serves as an input.
8225 : *
8226 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
8227 : * input.
8228 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8229 : * accepted as input.
8230 : * If both bits are set, the algorithm can accept either a name or a dataset
8231 : * object.
8232 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8233 : *
8234 : * @param hArg Handle to an argument. Must NOT be null.
8235 : * @return string whose lifetime is bound to hAlg and which must not
8236 : * be freed.
8237 : * @since 3.11
8238 : */
8239 2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
8240 : {
8241 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8242 2 : return hArg->ptr->GetDatasetInputFlags();
8243 : }
8244 :
8245 : /************************************************************************/
8246 : /* GDALAlgorithmArgGetDatasetOutputFlags() */
8247 : /************************************************************************/
8248 :
8249 : /** Indicates which components among name and dataset are modified,
8250 : * when this argument serves as an output.
8251 : *
8252 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
8253 : * output (that is the algorithm will generate the name. Rarely used).
8254 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
8255 : * generated as output, and available for use after the algorithm has
8256 : * completed.
8257 : * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
8258 : *
8259 : * @param hArg Handle to an argument. Must NOT be null.
8260 : * @return string whose lifetime is bound to hAlg and which must not
8261 : * be freed.
8262 : * @since 3.11
8263 : */
8264 2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
8265 : {
8266 2 : VALIDATE_POINTER1(hArg, __func__, 0);
8267 2 : return hArg->ptr->GetDatasetOutputFlags();
8268 : }
8269 :
8270 : /************************************************************************/
8271 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
8272 : /************************************************************************/
8273 :
8274 : /** Return the name of the mutual exclusion group to which this argument
8275 : * belongs to.
8276 : *
8277 : * Or empty string if it does not belong to any exclusion group.
8278 : *
8279 : * @param hArg Handle to an argument. Must NOT be null.
8280 : * @return string whose lifetime is bound to hArg and which must not
8281 : * be freed.
8282 : * @since 3.11
8283 : */
8284 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
8285 : {
8286 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8287 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
8288 : }
8289 :
8290 : /************************************************************************/
8291 : /* GDALAlgorithmArgGetAsBoolean() */
8292 : /************************************************************************/
8293 :
8294 : /** Return the argument value as a boolean.
8295 : *
8296 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
8297 : *
8298 : * @param hArg Handle to an argument. Must NOT be null.
8299 : * @since 3.11
8300 : */
8301 8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
8302 : {
8303 8 : VALIDATE_POINTER1(hArg, __func__, false);
8304 8 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
8305 : {
8306 1 : CPLError(CE_Failure, CPLE_AppDefined,
8307 : "%s must only be called on arguments of type GAAT_BOOLEAN",
8308 : __func__);
8309 1 : return false;
8310 : }
8311 7 : return hArg->ptr->Get<bool>();
8312 : }
8313 :
8314 : /************************************************************************/
8315 : /* GDALAlgorithmArgGetAsString() */
8316 : /************************************************************************/
8317 :
8318 : /** Return the argument value as a string.
8319 : *
8320 : * Must only be called on arguments whose type is GAAT_STRING.
8321 : *
8322 : * @param hArg Handle to an argument. Must NOT be null.
8323 : * @return string whose lifetime is bound to hArg and which must not
8324 : * be freed.
8325 : * @since 3.11
8326 : */
8327 354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
8328 : {
8329 354 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8330 354 : if (hArg->ptr->GetType() != GAAT_STRING)
8331 : {
8332 1 : CPLError(CE_Failure, CPLE_AppDefined,
8333 : "%s must only be called on arguments of type GAAT_STRING",
8334 : __func__);
8335 1 : return nullptr;
8336 : }
8337 353 : return hArg->ptr->Get<std::string>().c_str();
8338 : }
8339 :
8340 : /************************************************************************/
8341 : /* GDALAlgorithmArgGetAsDatasetValue() */
8342 : /************************************************************************/
8343 :
8344 : /** Return the argument value as a GDALArgDatasetValueH.
8345 : *
8346 : * Must only be called on arguments whose type is GAAT_DATASET
8347 : *
8348 : * @param hArg Handle to an argument. Must NOT be null.
8349 : * @return handle to a GDALArgDatasetValue that must be released with
8350 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
8351 : * the one of hArg.
8352 : * @since 3.11
8353 : */
8354 3062 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
8355 : {
8356 3062 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8357 3062 : if (hArg->ptr->GetType() != GAAT_DATASET)
8358 : {
8359 1 : CPLError(CE_Failure, CPLE_AppDefined,
8360 : "%s must only be called on arguments of type GAAT_DATASET",
8361 : __func__);
8362 1 : return nullptr;
8363 : }
8364 3061 : return std::make_unique<GDALArgDatasetValueHS>(
8365 6122 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
8366 3061 : .release();
8367 : }
8368 :
8369 : /************************************************************************/
8370 : /* GDALAlgorithmArgGetAsInteger() */
8371 : /************************************************************************/
8372 :
8373 : /** Return the argument value as a integer.
8374 : *
8375 : * Must only be called on arguments whose type is GAAT_INTEGER
8376 : *
8377 : * @param hArg Handle to an argument. Must NOT be null.
8378 : * @since 3.11
8379 : */
8380 26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
8381 : {
8382 26 : VALIDATE_POINTER1(hArg, __func__, 0);
8383 26 : if (hArg->ptr->GetType() != GAAT_INTEGER)
8384 : {
8385 1 : CPLError(CE_Failure, CPLE_AppDefined,
8386 : "%s must only be called on arguments of type GAAT_INTEGER",
8387 : __func__);
8388 1 : return 0;
8389 : }
8390 25 : return hArg->ptr->Get<int>();
8391 : }
8392 :
8393 : /************************************************************************/
8394 : /* GDALAlgorithmArgGetAsDouble() */
8395 : /************************************************************************/
8396 :
8397 : /** Return the argument value as a double.
8398 : *
8399 : * Must only be called on arguments whose type is GAAT_REAL
8400 : *
8401 : * @param hArg Handle to an argument. Must NOT be null.
8402 : * @since 3.11
8403 : */
8404 8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
8405 : {
8406 8 : VALIDATE_POINTER1(hArg, __func__, 0);
8407 8 : if (hArg->ptr->GetType() != GAAT_REAL)
8408 : {
8409 1 : CPLError(CE_Failure, CPLE_AppDefined,
8410 : "%s must only be called on arguments of type GAAT_REAL",
8411 : __func__);
8412 1 : return 0;
8413 : }
8414 7 : return hArg->ptr->Get<double>();
8415 : }
8416 :
8417 : /************************************************************************/
8418 : /* GDALAlgorithmArgGetAsStringList() */
8419 : /************************************************************************/
8420 :
8421 : /** Return the argument value as a string list.
8422 : *
8423 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
8424 : *
8425 : * @param hArg Handle to an argument. Must NOT be null.
8426 : * @return a NULL terminated list of names, which must be destroyed with
8427 : * CSLDestroy()
8428 :
8429 : * @since 3.11
8430 : */
8431 4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
8432 : {
8433 4 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8434 4 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
8435 : {
8436 1 : CPLError(CE_Failure, CPLE_AppDefined,
8437 : "%s must only be called on arguments of type GAAT_STRING_LIST",
8438 : __func__);
8439 1 : return nullptr;
8440 : }
8441 6 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
8442 3 : .StealList();
8443 : }
8444 :
8445 : /************************************************************************/
8446 : /* GDALAlgorithmArgGetAsIntegerList() */
8447 : /************************************************************************/
8448 :
8449 : /** Return the argument value as a integer list.
8450 : *
8451 : * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
8452 : *
8453 : * @param hArg Handle to an argument. Must NOT be null.
8454 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8455 : * @since 3.11
8456 : */
8457 8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
8458 : size_t *pnCount)
8459 : {
8460 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8461 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8462 8 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
8463 : {
8464 1 : CPLError(
8465 : CE_Failure, CPLE_AppDefined,
8466 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
8467 : __func__);
8468 1 : *pnCount = 0;
8469 1 : return nullptr;
8470 : }
8471 7 : const auto &val = hArg->ptr->Get<std::vector<int>>();
8472 7 : *pnCount = val.size();
8473 7 : return val.data();
8474 : }
8475 :
8476 : /************************************************************************/
8477 : /* GDALAlgorithmArgGetAsDoubleList() */
8478 : /************************************************************************/
8479 :
8480 : /** Return the argument value as a real list.
8481 : *
8482 : * Must only be called on arguments whose type is GAAT_REAL_LIST.
8483 : *
8484 : * @param hArg Handle to an argument. Must NOT be null.
8485 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
8486 : * @since 3.11
8487 : */
8488 8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
8489 : size_t *pnCount)
8490 : {
8491 8 : VALIDATE_POINTER1(hArg, __func__, nullptr);
8492 8 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
8493 8 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
8494 : {
8495 1 : CPLError(CE_Failure, CPLE_AppDefined,
8496 : "%s must only be called on arguments of type GAAT_REAL_LIST",
8497 : __func__);
8498 1 : *pnCount = 0;
8499 1 : return nullptr;
8500 : }
8501 7 : const auto &val = hArg->ptr->Get<std::vector<double>>();
8502 7 : *pnCount = val.size();
8503 7 : return val.data();
8504 : }
8505 :
8506 : /************************************************************************/
8507 : /* GDALAlgorithmArgSetAsBoolean() */
8508 : /************************************************************************/
8509 :
8510 : /** Set the value for a GAAT_BOOLEAN argument.
8511 : *
8512 : * It cannot be called several times for a given argument.
8513 : * Validation checks and other actions are run.
8514 : *
8515 : * @param hArg Handle to an argument. Must NOT be null.
8516 : * @param value value.
8517 : * @return true if success.
8518 : * @since 3.11
8519 : */
8520 :
8521 682 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
8522 : {
8523 682 : VALIDATE_POINTER1(hArg, __func__, false);
8524 682 : return hArg->ptr->Set(value);
8525 : }
8526 :
8527 : /************************************************************************/
8528 : /* GDALAlgorithmArgSetAsString() */
8529 : /************************************************************************/
8530 :
8531 : /** Set the value for a GAAT_STRING argument.
8532 : *
8533 : * It cannot be called several times for a given argument.
8534 : * Validation checks and other actions are run.
8535 : *
8536 : * @param hArg Handle to an argument. Must NOT be null.
8537 : * @param value value (may be null)
8538 : * @return true if success.
8539 : * @since 3.11
8540 : */
8541 :
8542 3022 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
8543 : {
8544 3022 : VALIDATE_POINTER1(hArg, __func__, false);
8545 3022 : return hArg->ptr->Set(value ? value : "");
8546 : }
8547 :
8548 : /************************************************************************/
8549 : /* GDALAlgorithmArgSetAsInteger() */
8550 : /************************************************************************/
8551 :
8552 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
8553 : *
8554 : * It cannot be called several times for a given argument.
8555 : * Validation checks and other actions are run.
8556 : *
8557 : * @param hArg Handle to an argument. Must NOT be null.
8558 : * @param value value.
8559 : * @return true if success.
8560 : * @since 3.11
8561 : */
8562 :
8563 471 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
8564 : {
8565 471 : VALIDATE_POINTER1(hArg, __func__, false);
8566 471 : return hArg->ptr->Set(value);
8567 : }
8568 :
8569 : /************************************************************************/
8570 : /* GDALAlgorithmArgSetAsDouble() */
8571 : /************************************************************************/
8572 :
8573 : /** Set the value for a GAAT_REAL argument.
8574 : *
8575 : * It cannot be called several times for a given argument.
8576 : * Validation checks and other actions are run.
8577 : *
8578 : * @param hArg Handle to an argument. Must NOT be null.
8579 : * @param value value.
8580 : * @return true if success.
8581 : * @since 3.11
8582 : */
8583 :
8584 239 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
8585 : {
8586 239 : VALIDATE_POINTER1(hArg, __func__, false);
8587 239 : return hArg->ptr->Set(value);
8588 : }
8589 :
8590 : /************************************************************************/
8591 : /* GDALAlgorithmArgSetAsDatasetValue() */
8592 : /************************************************************************/
8593 :
8594 : /** Set the value for a GAAT_DATASET argument.
8595 : *
8596 : * It cannot be called several times for a given argument.
8597 : * Validation checks and other actions are run.
8598 : *
8599 : * @param hArg Handle to an argument. Must NOT be null.
8600 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
8601 : * @return true if success.
8602 : * @since 3.11
8603 : */
8604 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
8605 : GDALArgDatasetValueH value)
8606 : {
8607 2 : VALIDATE_POINTER1(hArg, __func__, false);
8608 2 : VALIDATE_POINTER1(value, __func__, false);
8609 2 : return hArg->ptr->SetFrom(*(value->ptr));
8610 : }
8611 :
8612 : /************************************************************************/
8613 : /* GDALAlgorithmArgSetDataset() */
8614 : /************************************************************************/
8615 :
8616 : /** Set dataset object, increasing its reference counter.
8617 : *
8618 : * @param hArg Handle to an argument. Must NOT be null.
8619 : * @param hDS Dataset object. May be null.
8620 : * @return true if success.
8621 : * @since 3.11
8622 : */
8623 :
8624 2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
8625 : {
8626 2 : VALIDATE_POINTER1(hArg, __func__, false);
8627 2 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
8628 : }
8629 :
8630 : /************************************************************************/
8631 : /* GDALAlgorithmArgSetAsStringList() */
8632 : /************************************************************************/
8633 :
8634 : /** Set the value for a GAAT_STRING_LIST argument.
8635 : *
8636 : * It cannot be called several times for a given argument.
8637 : * Validation checks and other actions are run.
8638 : *
8639 : * @param hArg Handle to an argument. Must NOT be null.
8640 : * @param value value as a NULL terminated list (may be null)
8641 : * @return true if success.
8642 : * @since 3.11
8643 : */
8644 :
8645 700 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
8646 : {
8647 700 : VALIDATE_POINTER1(hArg, __func__, false);
8648 700 : return hArg->ptr->Set(
8649 1400 : static_cast<std::vector<std::string>>(CPLStringList(value)));
8650 : }
8651 :
8652 : /************************************************************************/
8653 : /* GDALAlgorithmArgSetAsIntegerList() */
8654 : /************************************************************************/
8655 :
8656 : /** Set the value for a GAAT_INTEGER_LIST argument.
8657 : *
8658 : * It cannot be called several times for a given argument.
8659 : * Validation checks and other actions are run.
8660 : *
8661 : * @param hArg Handle to an argument. Must NOT be null.
8662 : * @param nCount Number of values in pnValues.
8663 : * @param pnValues Pointer to an array of integer values of size nCount.
8664 : * @return true if success.
8665 : * @since 3.11
8666 : */
8667 100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
8668 : const int *pnValues)
8669 : {
8670 100 : VALIDATE_POINTER1(hArg, __func__, false);
8671 100 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
8672 : }
8673 :
8674 : /************************************************************************/
8675 : /* GDALAlgorithmArgSetAsDoubleList() */
8676 : /************************************************************************/
8677 :
8678 : /** Set the value for a GAAT_REAL_LIST argument.
8679 : *
8680 : * It cannot be called several times for a given argument.
8681 : * Validation checks and other actions are run.
8682 : *
8683 : * @param hArg Handle to an argument. Must NOT be null.
8684 : * @param nCount Number of values in pnValues.
8685 : * @param pnValues Pointer to an array of double values of size nCount.
8686 : * @return true if success.
8687 : * @since 3.11
8688 : */
8689 152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
8690 : const double *pnValues)
8691 : {
8692 152 : VALIDATE_POINTER1(hArg, __func__, false);
8693 152 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
8694 : }
8695 :
8696 : /************************************************************************/
8697 : /* GDALAlgorithmArgSetDatasets() */
8698 : /************************************************************************/
8699 :
8700 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
8701 : *
8702 : * @param hArg Handle to an argument. Must NOT be null.
8703 : * @param nCount Number of values in pnValues.
8704 : * @param pahDS Pointer to an array of dataset of size nCount.
8705 : * @return true if success.
8706 : * @since 3.11
8707 : */
8708 :
8709 1239 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
8710 : GDALDatasetH *pahDS)
8711 : {
8712 1239 : VALIDATE_POINTER1(hArg, __func__, false);
8713 2478 : std::vector<GDALArgDatasetValue> values;
8714 2504 : for (size_t i = 0; i < nCount; ++i)
8715 : {
8716 1265 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
8717 : }
8718 1239 : return hArg->ptr->Set(std::move(values));
8719 : }
8720 :
8721 : /************************************************************************/
8722 : /* GDALAlgorithmArgSetDatasetNames() */
8723 : /************************************************************************/
8724 :
8725 : /** Set dataset names to a GAAT_DATASET_LIST argument.
8726 : *
8727 : * @param hArg Handle to an argument. Must NOT be null.
8728 : * @param names Dataset names as a NULL terminated list (may be null)
8729 : * @return true if success.
8730 : * @since 3.11
8731 : */
8732 :
8733 704 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
8734 : {
8735 704 : VALIDATE_POINTER1(hArg, __func__, false);
8736 1408 : std::vector<GDALArgDatasetValue> values;
8737 1473 : for (size_t i = 0; names[i]; ++i)
8738 : {
8739 769 : values.emplace_back(names[i]);
8740 : }
8741 704 : return hArg->ptr->Set(std::move(values));
8742 : }
8743 :
8744 : /************************************************************************/
8745 : /* GDALArgDatasetValueCreate() */
8746 : /************************************************************************/
8747 :
8748 : /** Instantiate an empty GDALArgDatasetValue
8749 : *
8750 : * @return new handle to free with GDALArgDatasetValueRelease()
8751 : * @since 3.11
8752 : */
8753 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
8754 : {
8755 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
8756 : }
8757 :
8758 : /************************************************************************/
8759 : /* GDALArgDatasetValueRelease() */
8760 : /************************************************************************/
8761 :
8762 : /** Release a handle to a GDALArgDatasetValue
8763 : *
8764 : * @since 3.11
8765 : */
8766 3062 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
8767 : {
8768 3062 : delete hValue;
8769 3062 : }
8770 :
8771 : /************************************************************************/
8772 : /* GDALArgDatasetValueGetName() */
8773 : /************************************************************************/
8774 :
8775 : /** Return the name component of the GDALArgDatasetValue
8776 : *
8777 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8778 : * @return string whose lifetime is bound to hAlg and which must not
8779 : * be freed.
8780 : * @since 3.11
8781 : */
8782 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
8783 : {
8784 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8785 1 : return hValue->ptr->GetName().c_str();
8786 : }
8787 :
8788 : /************************************************************************/
8789 : /* GDALArgDatasetValueGetDatasetRef() */
8790 : /************************************************************************/
8791 :
8792 : /** Return the dataset component of the GDALArgDatasetValue.
8793 : *
8794 : * This does not modify the reference counter, hence the lifetime of the
8795 : * returned object is not guaranteed to exceed the one of hValue.
8796 : *
8797 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8798 : * @since 3.11
8799 : */
8800 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
8801 : {
8802 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8803 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
8804 : }
8805 :
8806 : /************************************************************************/
8807 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
8808 : /************************************************************************/
8809 :
8810 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
8811 : * reference count if not null. Once done with the dataset, the caller should
8812 : * call GDALReleaseDataset().
8813 : *
8814 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8815 : * @since 3.11
8816 : */
8817 : GDALDatasetH
8818 1025 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
8819 : {
8820 1025 : VALIDATE_POINTER1(hValue, __func__, nullptr);
8821 1025 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
8822 : }
8823 :
8824 : /************************************************************************/
8825 : /* GDALArgDatasetValueSetName() */
8826 : /************************************************************************/
8827 :
8828 : /** Set dataset name
8829 : *
8830 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8831 : * @param pszName Dataset name. May be null.
8832 : * @since 3.11
8833 : */
8834 :
8835 1293 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
8836 : const char *pszName)
8837 : {
8838 1293 : VALIDATE_POINTER0(hValue, __func__);
8839 1293 : hValue->ptr->Set(pszName ? pszName : "");
8840 : }
8841 :
8842 : /************************************************************************/
8843 : /* GDALArgDatasetValueSetDataset() */
8844 : /************************************************************************/
8845 :
8846 : /** Set dataset object, increasing its reference counter.
8847 : *
8848 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
8849 : * @param hDS Dataset object. May be null.
8850 : * @since 3.11
8851 : */
8852 :
8853 729 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
8854 : GDALDatasetH hDS)
8855 : {
8856 729 : VALIDATE_POINTER0(hValue, __func__);
8857 729 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
8858 : }
|