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_minixml.h"
18 :
19 : #include "gdalalgorithm.h"
20 : #include "gdal_priv.h"
21 :
22 : #include <algorithm>
23 : #include <cassert>
24 : #include <cerrno>
25 : #include <cstdlib>
26 : #include <map>
27 :
28 : #ifndef _
29 : #define _(x) (x)
30 : #endif
31 :
32 : constexpr const char *GDAL_ARG_NAME_INPUT_FORMAT = "input-format";
33 :
34 : constexpr const char *GDAL_ARG_NAME_OUTPUT_FORMAT = "output-format";
35 :
36 : constexpr const char *GDAL_ARG_NAME_OPEN_OPTION = "open-option";
37 :
38 : //! @cond Doxygen_Suppress
39 : struct GDALAlgorithmArgHS
40 : {
41 : GDALAlgorithmArg *ptr = nullptr;
42 :
43 152 : explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
44 : {
45 152 : }
46 : };
47 :
48 : //! @endcond
49 :
50 : //! @cond Doxygen_Suppress
51 : struct GDALArgDatasetValueHS
52 : {
53 : GDALArgDatasetValue val{};
54 : GDALArgDatasetValue *ptr = nullptr;
55 :
56 1 : GDALArgDatasetValueHS() : ptr(&val)
57 : {
58 1 : }
59 :
60 60 : explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
61 : {
62 60 : }
63 :
64 : GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
65 : GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
66 : };
67 :
68 : //! @endcond
69 :
70 : /************************************************************************/
71 : /* GDALAlgorithmArgTypeIsList() */
72 : /************************************************************************/
73 :
74 13142 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
75 : {
76 13142 : switch (type)
77 : {
78 9514 : case GAAT_BOOLEAN:
79 : case GAAT_STRING:
80 : case GAAT_INTEGER:
81 : case GAAT_REAL:
82 : case GAAT_DATASET:
83 9514 : break;
84 :
85 3628 : case GAAT_STRING_LIST:
86 : case GAAT_INTEGER_LIST:
87 : case GAAT_REAL_LIST:
88 : case GAAT_DATASET_LIST:
89 3628 : return true;
90 : }
91 :
92 9514 : return false;
93 : }
94 :
95 : /************************************************************************/
96 : /* GDALAlgorithmArgTypeName() */
97 : /************************************************************************/
98 :
99 462 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
100 : {
101 462 : switch (type)
102 : {
103 115 : case GAAT_BOOLEAN:
104 115 : break;
105 100 : case GAAT_STRING:
106 100 : return "string";
107 9 : case GAAT_INTEGER:
108 9 : return "integer";
109 4 : case GAAT_REAL:
110 4 : return "real";
111 68 : case GAAT_DATASET:
112 68 : return "dataset";
113 118 : case GAAT_STRING_LIST:
114 118 : return "string_list";
115 7 : case GAAT_INTEGER_LIST:
116 7 : return "integer_list";
117 37 : case GAAT_REAL_LIST:
118 37 : return "real_list";
119 4 : case GAAT_DATASET_LIST:
120 4 : return "dataset_list";
121 : }
122 :
123 115 : return "boolean";
124 : }
125 :
126 : /************************************************************************/
127 : /* GDALArgDatasetValueTypeName() */
128 : /************************************************************************/
129 :
130 739 : std::string GDALArgDatasetValueTypeName(GDALArgDatasetValueType type)
131 : {
132 739 : std::string ret;
133 739 : if ((type & GDAL_OF_RASTER) != 0)
134 441 : ret = "raster";
135 739 : if ((type & GDAL_OF_VECTOR) != 0)
136 : {
137 383 : if (!ret.empty())
138 : {
139 86 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
140 79 : ret += ", ";
141 : else
142 7 : ret += " or ";
143 : }
144 383 : ret += "vector";
145 : }
146 739 : if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
147 : {
148 82 : if (!ret.empty())
149 : {
150 81 : ret += " or ";
151 : }
152 82 : ret += "multidimensional raster";
153 : }
154 739 : return ret;
155 : }
156 :
157 : /************************************************************************/
158 : /* GDALAlgorithmArgDecl() */
159 : /************************************************************************/
160 :
161 : // cppcheck-suppress uninitMemberVar
162 11064 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
163 : char chShortName,
164 : const std::string &description,
165 11064 : GDALAlgorithmArgType type)
166 : : m_longName(longName),
167 11064 : m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
168 : m_description(description), m_type(type),
169 22128 : m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
170 11064 : .toupper()),
171 33192 : m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
172 : {
173 11064 : if (m_type == GAAT_BOOLEAN)
174 : {
175 6260 : m_defaultValue = false;
176 : }
177 11064 : }
178 :
179 : /************************************************************************/
180 : /* GDALAlgorithmArgDecl::SetMinCount() */
181 : /************************************************************************/
182 :
183 223 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
184 : {
185 223 : if (!GDALAlgorithmArgTypeIsList(m_type))
186 : {
187 1 : CPLError(CE_Failure, CPLE_NotSupported,
188 : "SetMinCount() illegal on scalar argument '%s'",
189 1 : GetName().c_str());
190 : }
191 : else
192 : {
193 222 : m_minCount = count;
194 : }
195 223 : return *this;
196 : }
197 :
198 : /************************************************************************/
199 : /* GDALAlgorithmArgDecl::SetMaxCount() */
200 : /************************************************************************/
201 :
202 151 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
203 : {
204 151 : if (!GDALAlgorithmArgTypeIsList(m_type))
205 : {
206 1 : CPLError(CE_Failure, CPLE_NotSupported,
207 : "SetMaxCount() illegal on scalar argument '%s'",
208 1 : GetName().c_str());
209 : }
210 : else
211 : {
212 150 : m_maxCount = count;
213 : }
214 151 : return *this;
215 : }
216 :
217 : /************************************************************************/
218 : /* GDALAlgorithmArg::Set() */
219 : /************************************************************************/
220 :
221 87 : bool GDALAlgorithmArg::Set(bool value)
222 : {
223 87 : if (m_decl.GetType() != GAAT_BOOLEAN)
224 : {
225 14 : CPLError(
226 : CE_Failure, CPLE_AppDefined,
227 : "Calling Set(bool) on argument '%s' of type %s is not supported",
228 7 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
229 7 : return false;
230 : }
231 80 : return SetInternal(value);
232 : }
233 :
234 136 : bool GDALAlgorithmArg::Set(const std::string &value)
235 : {
236 136 : if (m_decl.GetType() != GAAT_STRING)
237 : {
238 2 : CPLError(CE_Failure, CPLE_AppDefined,
239 : "Calling Set(std::string) on argument '%s' of type %s is not "
240 : "supported",
241 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
242 1 : return false;
243 : }
244 :
245 270 : std::string newValue(value);
246 142 : if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
247 7 : value.front() == '@')
248 : {
249 2 : GByte *pabyData = nullptr;
250 2 : if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
251 2 : 1024 * 1024))
252 : {
253 : // Remove UTF-8 BOM
254 1 : size_t offset = 0;
255 1 : if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
256 1 : pabyData[2] == 0xBF)
257 : {
258 1 : offset = 3;
259 : }
260 1 : newValue = reinterpret_cast<const char *>(pabyData + offset);
261 1 : VSIFree(pabyData);
262 : }
263 : else
264 : {
265 1 : return false;
266 : }
267 : }
268 :
269 134 : if (m_decl.IsRemoveSQLCommentsEnabled())
270 6 : newValue = CPLRemoveSQLComments(newValue);
271 :
272 134 : return SetInternal(newValue);
273 : }
274 :
275 18 : bool GDALAlgorithmArg::Set(int value)
276 : {
277 18 : if (m_decl.GetType() == GAAT_REAL)
278 : {
279 2 : return Set(static_cast<double>(value));
280 : }
281 16 : if (m_decl.GetType() != GAAT_INTEGER)
282 : {
283 2 : CPLError(
284 : CE_Failure, CPLE_AppDefined,
285 : "Calling Set(int) on argument '%s' of type %s is not supported",
286 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
287 1 : return false;
288 : }
289 15 : return SetInternal(value);
290 : }
291 :
292 6 : bool GDALAlgorithmArg::Set(double value)
293 : {
294 6 : if (m_decl.GetType() != GAAT_REAL)
295 : {
296 2 : CPLError(
297 : CE_Failure, CPLE_AppDefined,
298 : "Calling Set(double) on argument '%s' of type %s is not supported",
299 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
300 1 : return false;
301 : }
302 5 : return SetInternal(value);
303 : }
304 :
305 24 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
306 : {
307 24 : if (m_decl.GetType() != GAAT_DATASET)
308 : {
309 2 : CPLError(CE_Failure, CPLE_AppDefined,
310 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
311 : "is not supported",
312 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
313 1 : return false;
314 : }
315 23 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
316 23 : if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
317 : {
318 2 : CPLError(
319 : CE_Failure, CPLE_AppDefined,
320 : "Dataset object '%s' is created by algorithm and cannot be set "
321 : "as an input.",
322 2 : GetName().c_str());
323 2 : return false;
324 : }
325 21 : m_explicitlySet = true;
326 21 : val.Set(ds);
327 21 : return RunAllActions();
328 : }
329 :
330 2 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
331 : {
332 2 : if (m_decl.GetType() != GAAT_DATASET)
333 : {
334 2 : CPLError(CE_Failure, CPLE_AppDefined,
335 : "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
336 : "is not supported",
337 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
338 1 : return false;
339 : }
340 1 : auto &val = *std::get<GDALArgDatasetValue *>(m_value);
341 1 : if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
342 : {
343 0 : CPLError(
344 : CE_Failure, CPLE_AppDefined,
345 : "Dataset object '%s' is created by algorithm and cannot be set "
346 : "as an input.",
347 0 : GetName().c_str());
348 0 : return false;
349 : }
350 1 : m_explicitlySet = true;
351 1 : val.Set(std::move(ds));
352 1 : return RunAllActions();
353 : }
354 :
355 236 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
356 : {
357 236 : if (m_decl.GetType() != GAAT_DATASET)
358 : {
359 2 : CPLError(CE_Failure, CPLE_AppDefined,
360 : "Calling SetDatasetName() on argument '%s' of type %s is "
361 : "not supported",
362 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
363 1 : return false;
364 : }
365 235 : m_explicitlySet = true;
366 235 : std::get<GDALArgDatasetValue *>(m_value)->Set(name);
367 235 : return RunAllActions();
368 : }
369 :
370 21 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
371 : {
372 21 : if (m_decl.GetType() != GAAT_DATASET)
373 : {
374 2 : CPLError(CE_Failure, CPLE_AppDefined,
375 : "Calling SetFrom() on argument '%s' of type %s is "
376 : "not supported",
377 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
378 1 : return false;
379 : }
380 20 : m_explicitlySet = true;
381 20 : std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
382 20 : return RunAllActions();
383 : }
384 :
385 55 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
386 : {
387 55 : if (m_decl.GetType() != GAAT_STRING_LIST)
388 : {
389 2 : CPLError(CE_Failure, CPLE_AppDefined,
390 : "Calling Set(const std::vector<std::string> &) on argument "
391 : "'%s' of type %s is not supported",
392 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
393 1 : return false;
394 : }
395 54 : return SetInternal(value);
396 : }
397 :
398 12 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
399 : {
400 12 : if (m_decl.GetType() != GAAT_INTEGER_LIST)
401 : {
402 2 : CPLError(CE_Failure, CPLE_AppDefined,
403 : "Calling Set(const std::vector<int> &) on argument '%s' of "
404 : "type %s is not supported",
405 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
406 1 : return false;
407 : }
408 11 : return SetInternal(value);
409 : }
410 :
411 26 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
412 : {
413 26 : if (m_decl.GetType() != GAAT_REAL_LIST)
414 : {
415 2 : CPLError(CE_Failure, CPLE_AppDefined,
416 : "Calling Set(const std::vector<double> &) on argument '%s' of "
417 : "type %s is not supported",
418 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
419 1 : return false;
420 : }
421 25 : return SetInternal(value);
422 : }
423 :
424 28 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
425 : {
426 28 : if (m_decl.GetType() != GAAT_DATASET_LIST)
427 : {
428 2 : CPLError(CE_Failure, CPLE_AppDefined,
429 : "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
430 : "argument '%s' of type %s is not supported",
431 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
432 1 : return false;
433 : }
434 27 : m_explicitlySet = true;
435 27 : *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
436 27 : return RunAllActions();
437 : }
438 :
439 25 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
440 : {
441 25 : if (m_decl.GetType() != other.GetType())
442 : {
443 2 : CPLError(CE_Failure, CPLE_AppDefined,
444 : "Calling SetFrom() on argument '%s' of type %s whereas "
445 : "other argument type is %s is not supported",
446 1 : GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
447 : GDALAlgorithmArgTypeName(other.GetType()));
448 1 : return false;
449 : }
450 :
451 24 : switch (m_decl.GetType())
452 : {
453 1 : case GAAT_BOOLEAN:
454 1 : *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
455 1 : break;
456 1 : case GAAT_STRING:
457 2 : *std::get<std::string *>(m_value) =
458 1 : *std::get<std::string *>(other.m_value);
459 1 : break;
460 1 : case GAAT_INTEGER:
461 1 : *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
462 1 : break;
463 1 : case GAAT_REAL:
464 1 : *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
465 1 : break;
466 16 : case GAAT_DATASET:
467 16 : return SetFrom(other.Get<GDALArgDatasetValue>());
468 1 : case GAAT_STRING_LIST:
469 2 : *std::get<std::vector<std::string> *>(m_value) =
470 1 : *std::get<std::vector<std::string> *>(other.m_value);
471 1 : break;
472 1 : case GAAT_INTEGER_LIST:
473 2 : *std::get<std::vector<int> *>(m_value) =
474 1 : *std::get<std::vector<int> *>(other.m_value);
475 1 : break;
476 1 : case GAAT_REAL_LIST:
477 2 : *std::get<std::vector<double> *>(m_value) =
478 1 : *std::get<std::vector<double> *>(other.m_value);
479 1 : break;
480 1 : case GAAT_DATASET_LIST:
481 : {
482 1 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
483 2 : for (const auto &val :
484 5 : *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
485 : {
486 4 : GDALArgDatasetValue v;
487 2 : v.SetFrom(val);
488 2 : std::get<std::vector<GDALArgDatasetValue> *>(m_value)
489 2 : ->push_back(std::move(v));
490 : }
491 1 : break;
492 : }
493 : }
494 8 : m_explicitlySet = true;
495 8 : return RunAllActions();
496 : }
497 :
498 : /************************************************************************/
499 : /* GDALAlgorithmArg::RunAllActions() */
500 : /************************************************************************/
501 :
502 636 : bool GDALAlgorithmArg::RunAllActions()
503 : {
504 636 : if (!RunValidationActions())
505 25 : return false;
506 611 : RunActions();
507 611 : return true;
508 : }
509 :
510 : /************************************************************************/
511 : /* GDALAlgorithmArg::RunActions() */
512 : /************************************************************************/
513 :
514 612 : void GDALAlgorithmArg::RunActions()
515 : {
516 623 : for (const auto &f : m_actions)
517 11 : f();
518 612 : }
519 :
520 : /************************************************************************/
521 : /* GDALAlgorithmArg::RunValidationActions() */
522 : /************************************************************************/
523 :
524 636 : bool GDALAlgorithmArg::RunValidationActions()
525 : {
526 730 : for (const auto &f : m_validationActions)
527 : {
528 119 : if (!f())
529 25 : return false;
530 : }
531 611 : return true;
532 : }
533 :
534 : /************************************************************************/
535 : /* GDALInConstructionAlgorithmArg::AddAlias() */
536 : /************************************************************************/
537 :
538 : GDALInConstructionAlgorithmArg &
539 2195 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
540 : {
541 2195 : m_decl.AddAlias(alias);
542 2195 : if (m_owner)
543 2195 : m_owner->AddAliasFor(this, alias);
544 2195 : return *this;
545 : }
546 :
547 : /************************************************************************/
548 : /* GDALInConstructionAlgorithmArg::AddHiddenAlias() */
549 : /************************************************************************/
550 :
551 : GDALInConstructionAlgorithmArg &
552 352 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
553 : {
554 352 : m_decl.AddHiddenAlias(alias);
555 352 : if (m_owner)
556 352 : m_owner->AddAliasFor(this, alias);
557 352 : return *this;
558 : }
559 :
560 : /************************************************************************/
561 : /* GDALInConstructionAlgorithmArg::SetPositional() */
562 : /************************************************************************/
563 :
564 733 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
565 : {
566 733 : m_decl.SetPositional();
567 733 : if (m_owner)
568 733 : m_owner->SetPositional(this);
569 733 : return *this;
570 : }
571 :
572 : /************************************************************************/
573 : /* GDALArgDatasetValue::GDALArgDatasetValue() */
574 : /************************************************************************/
575 :
576 15 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
577 30 : : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
578 15 : m_nameSet(true)
579 : {
580 15 : if (m_poDS)
581 15 : m_poDS->Reference();
582 15 : }
583 :
584 : /************************************************************************/
585 : /* GDALArgDatasetValue::Set() */
586 : /************************************************************************/
587 :
588 259 : void GDALArgDatasetValue::Set(const std::string &name)
589 : {
590 259 : Close();
591 259 : m_name = name;
592 259 : m_nameSet = true;
593 259 : if (m_ownerArg)
594 255 : m_ownerArg->NotifyValueSet();
595 259 : }
596 :
597 : /************************************************************************/
598 : /* GDALArgDatasetValue::Set() */
599 : /************************************************************************/
600 :
601 106 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
602 : {
603 106 : Close();
604 106 : m_poDS = poDS.release();
605 106 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
606 106 : m_nameSet = true;
607 106 : if (m_ownerArg)
608 89 : m_ownerArg->NotifyValueSet();
609 106 : }
610 :
611 : /************************************************************************/
612 : /* GDALArgDatasetValue::Set() */
613 : /************************************************************************/
614 :
615 344 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
616 : {
617 344 : Close();
618 344 : m_poDS = poDS;
619 344 : if (m_poDS)
620 338 : m_poDS->Reference();
621 344 : m_name = m_poDS ? m_poDS->GetDescription() : std::string();
622 344 : m_nameSet = true;
623 344 : if (m_ownerArg)
624 215 : m_ownerArg->NotifyValueSet();
625 344 : }
626 :
627 : /************************************************************************/
628 : /* GDALArgDatasetValue::SetFrom() */
629 : /************************************************************************/
630 :
631 22 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
632 : {
633 22 : Close();
634 22 : m_name = other.m_name;
635 22 : m_nameSet = other.m_nameSet;
636 22 : m_poDS = other.m_poDS;
637 22 : if (m_poDS)
638 10 : m_poDS->Reference();
639 22 : }
640 :
641 : /************************************************************************/
642 : /* GDALArgDatasetValue::~GDALArgDatasetValue() */
643 : /************************************************************************/
644 :
645 1263 : GDALArgDatasetValue::~GDALArgDatasetValue()
646 : {
647 1263 : Close();
648 1263 : }
649 :
650 : /************************************************************************/
651 : /* GDALArgDatasetValue::Close() */
652 : /************************************************************************/
653 :
654 2273 : bool GDALArgDatasetValue::Close()
655 : {
656 2273 : bool ret = true;
657 2273 : if (m_poDS && m_poDS->Dereference() == 0)
658 : {
659 209 : ret = m_poDS->Close() == CE_None;
660 209 : delete m_poDS;
661 : }
662 2273 : m_poDS = nullptr;
663 2273 : return ret;
664 : }
665 :
666 : /************************************************************************/
667 : /* GDALArgDatasetValue::operator=() */
668 : /************************************************************************/
669 :
670 2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
671 : {
672 2 : Close();
673 2 : m_poDS = other.m_poDS;
674 2 : m_name = other.m_name;
675 2 : m_nameSet = other.m_nameSet;
676 2 : m_type = other.m_type;
677 2 : m_inputFlags = other.m_inputFlags;
678 2 : m_outputFlags = other.m_outputFlags;
679 2 : other.m_poDS = nullptr;
680 2 : other.m_name.clear();
681 2 : other.m_nameSet = false;
682 2 : return *this;
683 : }
684 :
685 : /************************************************************************/
686 : /* GDALArgDatasetValue::GetDataset() */
687 : /************************************************************************/
688 :
689 24 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
690 : {
691 24 : if (m_poDS)
692 23 : m_poDS->Reference();
693 24 : return m_poDS;
694 : }
695 :
696 : /************************************************************************/
697 : /* GDALArgDatasetValue(GDALArgDatasetValue &&other) */
698 : /************************************************************************/
699 :
700 25 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
701 50 : : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet),
702 25 : m_type(other.m_type), m_inputFlags(other.m_inputFlags),
703 25 : m_outputFlags(other.m_outputFlags)
704 : {
705 25 : other.m_poDS = nullptr;
706 25 : other.m_name.clear();
707 25 : }
708 :
709 : /************************************************************************/
710 : /* GDALInConstructionAlgorithmArg::SetIsCRSArg() */
711 : /************************************************************************/
712 :
713 : GDALInConstructionAlgorithmArg &
714 117 : GDALInConstructionAlgorithmArg::SetIsCRSArg(bool noneAllowed)
715 : {
716 117 : if (GetType() != GAAT_STRING)
717 : {
718 1 : CPLError(CE_Failure, CPLE_AppDefined,
719 : "SetIsCRSArg() can only be called on a String argument");
720 1 : return *this;
721 : }
722 : AddValidationAction(
723 50 : [this, noneAllowed]()
724 : {
725 : const std::string &osVal =
726 : static_cast<const GDALInConstructionAlgorithmArg *>(this)
727 23 : ->Get<std::string>();
728 23 : if (!noneAllowed || (osVal != "none" && osVal != "null"))
729 : {
730 20 : OGRSpatialReference oSRS;
731 20 : if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
732 : {
733 4 : m_owner->ReportError(CE_Failure, CPLE_AppDefined,
734 : "Invalid value for '%s' argument",
735 4 : GetName().c_str());
736 4 : return false;
737 : }
738 : }
739 19 : return true;
740 116 : });
741 :
742 : SetAutoCompleteFunction(
743 6 : [](const std::string ¤tValue)
744 : {
745 6 : std::vector<std::string> oRet;
746 6 : if (!currentValue.empty())
747 : {
748 : const CPLStringList aosTokens(
749 10 : CSLTokenizeString2(currentValue.c_str(), ":", 0));
750 5 : int nCount = 0;
751 5 : OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
752 : aosTokens[0], nullptr, &nCount);
753 10 : std::string osCode;
754 33050 : for (int i = 0; i < nCount; ++i)
755 : {
756 52872 : if (aosTokens.size() == 1 ||
757 19827 : STARTS_WITH(pCRSList[i]->pszCode, aosTokens[1]))
758 : {
759 13409 : if (oRet.empty())
760 5 : osCode = pCRSList[i]->pszCode;
761 26818 : oRet.push_back(std::string(pCRSList[i]->pszCode)
762 13409 : .append(" -- ")
763 13409 : .append(pCRSList[i]->pszName));
764 : }
765 : }
766 5 : if (oRet.size() == 1)
767 : {
768 : // If there is a single match, remove the name from the suggestion.
769 1 : oRet.clear();
770 1 : oRet.push_back(osCode);
771 : }
772 5 : OSRDestroyCRSInfoList(pCRSList);
773 : }
774 6 : if (oRet.empty())
775 : {
776 : const CPLStringList aosAuthorities(
777 2 : OSRGetAuthorityListFromDatabase());
778 6 : for (const char *pszAuth : cpl::Iterate(aosAuthorities))
779 : {
780 5 : int nCount = 0;
781 5 : OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
782 : pszAuth, nullptr, &nCount);
783 5 : OSRDestroyCRSInfoList(pCRSList);
784 5 : if (nCount)
785 4 : oRet.push_back(std::string(pszAuth).append(":"));
786 : }
787 : }
788 6 : return oRet;
789 116 : });
790 :
791 116 : return *this;
792 : }
793 :
794 : /************************************************************************/
795 : /* GDALAlgorithm::GDALAlgorithm() */
796 : /************************************************************************/
797 :
798 1100 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
799 : const std::string &description,
800 1100 : const std::string &helpURL)
801 : : m_name(name), m_description(description), m_helpURL(helpURL),
802 2200 : m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
803 1100 : ? "https://gdal.org" + m_helpURL
804 3153 : : m_helpURL)
805 : {
806 2200 : AddArg("help", 'h', _("Display help message and exit"), &m_helpRequested)
807 1100 : .SetOnlyForCLI()
808 2200 : .SetCategory(GAAC_COMMON)
809 1100 : .AddAction([this]() { m_specialActionRequested = true; });
810 2200 : AddArg("version", 0, _("Display GDAL version and exit"), &m_dummyBoolean)
811 1100 : .SetOnlyForCLI()
812 1100 : .SetCategory(GAAC_COMMON);
813 : AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
814 2200 : &m_JSONUsageRequested)
815 1100 : .SetOnlyForCLI()
816 2200 : .SetCategory(GAAC_COMMON)
817 1100 : .AddAction([this]() { m_specialActionRequested = true; });
818 : AddArg("drivers", 0, _("Display driver list as JSON document and exit"),
819 2200 : &m_dummyBoolean)
820 1100 : .SetOnlyForCLI()
821 1100 : .SetCategory(GAAC_COMMON);
822 2200 : AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
823 2200 : .SetMetaVar("<KEY>=<VALUE>")
824 1100 : .SetOnlyForCLI()
825 1100 : .SetCategory(GAAC_COMMON);
826 1100 : }
827 :
828 : /************************************************************************/
829 : /* GDALAlgorithm::~GDALAlgorithm() */
830 : /************************************************************************/
831 :
832 : GDALAlgorithm::~GDALAlgorithm() = default;
833 :
834 : /************************************************************************/
835 : /* GDALAlgorithm::ParseArgument() */
836 : /************************************************************************/
837 :
838 537 : bool GDALAlgorithm::ParseArgument(
839 : GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
840 : std::map<
841 : GDALAlgorithmArg *,
842 : std::variant<std::vector<std::string>, std::vector<int>,
843 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
844 : &inConstructionValues)
845 : {
846 537 : const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
847 537 : if (arg->IsExplicitlySet() && !isListArg)
848 : {
849 : // Hack for "gdal info" to be able to pass an opened raster dataset
850 : // by "gdal raster info" to the "gdal vector info" algorithm.
851 3 : if (arg->SkipIfAlreadySet())
852 : {
853 1 : arg->SetSkipIfAlreadySet(false);
854 1 : return true;
855 : }
856 :
857 2 : ReportError(CE_Failure, CPLE_IllegalArg,
858 : "Argument '%s' has already been specified.", name.c_str());
859 2 : return false;
860 : }
861 :
862 552 : if (!arg->GetRepeatedArgAllowed() &&
863 18 : cpl::contains(inConstructionValues, arg))
864 : {
865 1 : ReportError(CE_Failure, CPLE_IllegalArg,
866 : "Argument '%s' has already been specified.", name.c_str());
867 1 : return false;
868 : }
869 :
870 533 : switch (arg->GetType())
871 : {
872 71 : case GAAT_BOOLEAN:
873 : {
874 71 : if (value.empty() || value == "true")
875 69 : return arg->Set(true);
876 2 : else if (value == "false")
877 1 : return arg->Set(false);
878 : else
879 : {
880 1 : ReportError(
881 : CE_Failure, CPLE_IllegalArg,
882 : "Invalid value '%s' for boolean argument '%s'. Should be "
883 : "'true' or 'false'.",
884 : value.c_str(), name.c_str());
885 1 : return false;
886 : }
887 : }
888 :
889 103 : case GAAT_STRING:
890 : {
891 103 : const auto &choices = arg->GetChoices();
892 103 : const auto &hiddenChoices = arg->GetHiddenChoices();
893 130 : if (!choices.empty() &&
894 27 : std::find(choices.begin(), choices.end(), value) ==
895 130 : choices.end() &&
896 2 : std::find(hiddenChoices.begin(), hiddenChoices.end(), value) ==
897 105 : hiddenChoices.end())
898 : {
899 1 : std::string expected;
900 3 : for (const auto &choice : choices)
901 : {
902 2 : if (!expected.empty())
903 1 : expected += ", ";
904 2 : expected += '\'';
905 2 : expected += choice;
906 2 : expected += '\'';
907 : }
908 1 : ReportError(
909 : CE_Failure, CPLE_IllegalArg,
910 : "Invalid value '%s' for string argument '%s'. Should be "
911 : "one among %s.",
912 : value.c_str(), name.c_str(), expected.c_str());
913 1 : return false;
914 : }
915 :
916 102 : return arg->Set(value);
917 : }
918 :
919 7 : case GAAT_INTEGER:
920 : {
921 7 : errno = 0;
922 7 : char *endptr = nullptr;
923 7 : const auto val = std::strtol(value.c_str(), &endptr, 10);
924 6 : if (errno == 0 && endptr &&
925 13 : endptr == value.c_str() + value.size() && val >= INT_MIN &&
926 : val <= INT_MAX)
927 : {
928 4 : return arg->Set(static_cast<int>(val));
929 : }
930 : else
931 : {
932 3 : ReportError(CE_Failure, CPLE_IllegalArg,
933 : "Expected integer value for argument '%s', "
934 : "but got '%s'.",
935 : name.c_str(), value.c_str());
936 3 : return false;
937 : }
938 : }
939 :
940 2 : case GAAT_REAL:
941 : {
942 2 : char *endptr = nullptr;
943 2 : double dfValue = CPLStrtod(value.c_str(), &endptr);
944 2 : if (endptr != value.c_str() + value.size())
945 : {
946 1 : ReportError(
947 : CE_Failure, CPLE_IllegalArg,
948 : "Expected real value for argument '%s', but got '%s'.",
949 : name.c_str(), value.c_str());
950 1 : return false;
951 : }
952 1 : return arg->Set(dfValue);
953 : }
954 :
955 234 : case GAAT_DATASET:
956 : {
957 234 : return arg->SetDatasetName(value);
958 : }
959 :
960 71 : case GAAT_STRING_LIST:
961 : {
962 : const CPLStringList aosTokens(
963 71 : arg->GetPackedValuesAllowed()
964 68 : ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
965 139 : : CSLAddString(nullptr, value.c_str()));
966 71 : if (!cpl::contains(inConstructionValues, arg))
967 : {
968 51 : inConstructionValues[arg] = std::vector<std::string>();
969 : }
970 : auto &valueVector =
971 71 : std::get<std::vector<std::string>>(inConstructionValues[arg]);
972 71 : const auto &choices = arg->GetChoices();
973 71 : const auto &hiddenChoices = arg->GetHiddenChoices();
974 148 : for (const char *v : aosTokens)
975 : {
976 91 : if (!choices.empty() &&
977 10 : std::find(choices.begin(), choices.end(), v) ==
978 91 : choices.end() &&
979 0 : std::find(hiddenChoices.begin(), hiddenChoices.end(),
980 85 : value) == hiddenChoices.end())
981 : {
982 4 : std::string expected;
983 10 : for (const auto &choice : choices)
984 : {
985 6 : if (!expected.empty())
986 2 : expected += ", ";
987 6 : expected += '\'';
988 6 : expected += choice;
989 6 : expected += '\'';
990 : }
991 4 : ReportError(CE_Failure, CPLE_IllegalArg,
992 : "Invalid value '%s' for string argument '%s'. "
993 : "Should be "
994 : "one among %s.",
995 : v, name.c_str(), expected.c_str());
996 4 : return false;
997 : }
998 :
999 77 : valueVector.push_back(v);
1000 : }
1001 67 : break;
1002 : }
1003 :
1004 13 : case GAAT_INTEGER_LIST:
1005 : {
1006 : const CPLStringList aosTokens(
1007 13 : arg->GetPackedValuesAllowed()
1008 13 : ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
1009 26 : : CSLAddString(nullptr, value.c_str()));
1010 13 : if (!cpl::contains(inConstructionValues, arg))
1011 : {
1012 9 : inConstructionValues[arg] = std::vector<int>();
1013 : }
1014 : auto &valueVector =
1015 13 : std::get<std::vector<int>>(inConstructionValues[arg]);
1016 25 : for (const char *v : aosTokens)
1017 : {
1018 16 : errno = 0;
1019 16 : char *endptr = nullptr;
1020 16 : const auto val = std::strtol(v, &endptr, 10);
1021 16 : if (errno == 0 && endptr && endptr == v + strlen(v) &&
1022 12 : val >= INT_MIN && val <= INT_MAX)
1023 : {
1024 12 : valueVector.push_back(static_cast<int>(val));
1025 : }
1026 : else
1027 : {
1028 4 : ReportError(
1029 : CE_Failure, CPLE_IllegalArg,
1030 : "Expected list of integer value for argument '%s', "
1031 : "but got '%s'.",
1032 : name.c_str(), value.c_str());
1033 4 : return false;
1034 : }
1035 : }
1036 9 : break;
1037 : }
1038 :
1039 21 : case GAAT_REAL_LIST:
1040 : {
1041 : const CPLStringList aosTokens(
1042 21 : arg->GetPackedValuesAllowed()
1043 21 : ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
1044 42 : : CSLAddString(nullptr, value.c_str()));
1045 21 : if (!cpl::contains(inConstructionValues, arg))
1046 : {
1047 19 : inConstructionValues[arg] = std::vector<double>();
1048 : }
1049 : auto &valueVector =
1050 21 : std::get<std::vector<double>>(inConstructionValues[arg]);
1051 83 : for (const char *v : aosTokens)
1052 : {
1053 64 : char *endptr = nullptr;
1054 64 : double dfValue = CPLStrtod(v, &endptr);
1055 64 : if (endptr != v + strlen(v))
1056 : {
1057 2 : ReportError(
1058 : CE_Failure, CPLE_IllegalArg,
1059 : "Expected list of real value for argument '%s', "
1060 : "but got '%s'.",
1061 : name.c_str(), value.c_str());
1062 2 : return false;
1063 : }
1064 62 : valueVector.push_back(dfValue);
1065 : }
1066 19 : break;
1067 : }
1068 :
1069 11 : case GAAT_DATASET_LIST:
1070 : {
1071 : const CPLStringList aosTokens(
1072 22 : CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS));
1073 11 : if (!cpl::contains(inConstructionValues, arg))
1074 : {
1075 9 : inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
1076 : }
1077 : auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
1078 11 : inConstructionValues[arg]);
1079 22 : for (const char *v : aosTokens)
1080 : {
1081 11 : valueVector.push_back(GDALArgDatasetValue(v));
1082 : }
1083 11 : break;
1084 : }
1085 : }
1086 :
1087 106 : return true;
1088 : }
1089 :
1090 : /************************************************************************/
1091 : /* GDALAlgorithm::ParseCommandLineArguments() */
1092 : /************************************************************************/
1093 :
1094 429 : bool GDALAlgorithm::ParseCommandLineArguments(
1095 : const std::vector<std::string> &args)
1096 : {
1097 429 : if (m_parsedSubStringAlreadyCalled)
1098 : {
1099 1 : ReportError(CE_Failure, CPLE_AppDefined,
1100 : "ParseCommandLineArguments() can only be called once per "
1101 : "instance.");
1102 1 : return false;
1103 : }
1104 428 : m_parsedSubStringAlreadyCalled = true;
1105 :
1106 : // AWS like syntax supported too (not advertized)
1107 428 : if (args.size() == 1 && args[0] == "help")
1108 : {
1109 1 : auto arg = GetArg("help");
1110 1 : assert(arg);
1111 1 : arg->Set(true);
1112 1 : arg->RunActions();
1113 1 : return true;
1114 : }
1115 :
1116 427 : if (HasSubAlgorithms())
1117 : {
1118 29 : if (args.empty())
1119 : {
1120 2 : ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
1121 2 : m_callPath.size() == 1 ? "command" : "subcommand");
1122 2 : return false;
1123 : }
1124 27 : if (!args[0].empty() && args[0][0] == '-')
1125 : {
1126 : // go on argument parsing
1127 : }
1128 : else
1129 : {
1130 24 : m_shortCutAlg = InstantiateSubAlgorithm(args[0]);
1131 24 : if (m_shortCutAlg)
1132 : {
1133 23 : m_selectedSubAlg = m_shortCutAlg.get();
1134 23 : bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
1135 46 : std::vector<std::string>(args.begin() + 1, args.end()));
1136 23 : m_selectedSubAlg->PropagateSpecialActionTo(this);
1137 23 : return bRet;
1138 : }
1139 : else
1140 : {
1141 1 : ReportError(CE_Failure, CPLE_AppDefined,
1142 1 : "Unknown command: '%s'", args[0].c_str());
1143 1 : return false;
1144 : }
1145 : }
1146 : }
1147 :
1148 : std::map<
1149 : GDALAlgorithmArg *,
1150 : std::variant<std::vector<std::string>, std::vector<int>,
1151 : std::vector<double>, std::vector<GDALArgDatasetValue>>>
1152 802 : inConstructionValues;
1153 :
1154 802 : std::vector<std::string> lArgs(args);
1155 933 : for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
1156 : {
1157 584 : const auto &strArg = lArgs[i];
1158 584 : GDALAlgorithmArg *arg = nullptr;
1159 584 : std::string name;
1160 584 : std::string value;
1161 584 : bool hasValue = false;
1162 584 : if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
1163 : {
1164 288 : const auto equalPos = strArg.find('=');
1165 576 : name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
1166 288 : : strArg;
1167 288 : auto iterArg = m_mapLongNameToArg.find(name.substr(2));
1168 288 : if (iterArg == m_mapLongNameToArg.end())
1169 : {
1170 16 : ReportError(CE_Failure, CPLE_IllegalArg,
1171 : "Long name option '%s' is unknown.", name.c_str());
1172 16 : return false;
1173 : }
1174 272 : arg = iterArg->second;
1175 272 : if (equalPos != std::string::npos)
1176 : {
1177 145 : hasValue = true;
1178 145 : value = strArg.substr(equalPos + 1);
1179 : }
1180 : }
1181 296 : else if (strArg.size() >= 2 && strArg[0] == '-')
1182 : {
1183 16 : if (strArg.size() != 2)
1184 : {
1185 1 : ReportError(
1186 : CE_Failure, CPLE_IllegalArg,
1187 : "Option '%s' not recognized. Should be either a long "
1188 : "option or a one-letter short option.",
1189 : strArg.c_str());
1190 2 : return false;
1191 : }
1192 15 : name = strArg;
1193 15 : auto iterArg = m_mapShortNameToArg.find(name.substr(1));
1194 15 : if (iterArg == m_mapShortNameToArg.end())
1195 : {
1196 1 : ReportError(CE_Failure, CPLE_IllegalArg,
1197 : "Short name option '%s' is unknown.", name.c_str());
1198 1 : return false;
1199 : }
1200 14 : arg = iterArg->second;
1201 : }
1202 : else
1203 : {
1204 280 : ++i;
1205 280 : continue;
1206 : }
1207 286 : assert(arg);
1208 :
1209 286 : if (arg->GetType() == GAAT_BOOLEAN)
1210 : {
1211 72 : if (!hasValue)
1212 : {
1213 69 : hasValue = true;
1214 69 : value = "true";
1215 : }
1216 : }
1217 :
1218 286 : if (!hasValue)
1219 : {
1220 72 : if (i + 1 == lArgs.size())
1221 : {
1222 13 : if (m_parseForAutoCompletion)
1223 : {
1224 12 : lArgs.erase(lArgs.begin() + i);
1225 12 : break;
1226 : }
1227 1 : ReportError(
1228 : CE_Failure, CPLE_IllegalArg,
1229 : "Expected value for argument '%s', but ran short of tokens",
1230 : name.c_str());
1231 1 : return false;
1232 : }
1233 59 : value = lArgs[i + 1];
1234 59 : lArgs.erase(lArgs.begin() + i + 1);
1235 : }
1236 :
1237 273 : if (!ParseArgument(arg, name, value, inConstructionValues))
1238 21 : return false;
1239 :
1240 252 : lArgs.erase(lArgs.begin() + i);
1241 : }
1242 :
1243 361 : if (m_specialActionRequested)
1244 : {
1245 8 : return true;
1246 : }
1247 :
1248 431 : const auto ProcessInConstructionValues = [&inConstructionValues]()
1249 : {
1250 417 : for (auto &[arg, value] : inConstructionValues)
1251 : {
1252 80 : if (arg->GetType() == GAAT_STRING_LIST)
1253 : {
1254 48 : if (!arg->Set(std::get<std::vector<std::string>>(
1255 48 : inConstructionValues[arg])))
1256 : {
1257 14 : return false;
1258 : }
1259 : }
1260 32 : else if (arg->GetType() == GAAT_INTEGER_LIST)
1261 : {
1262 6 : if (!arg->Set(
1263 6 : std::get<std::vector<int>>(inConstructionValues[arg])))
1264 : {
1265 1 : return false;
1266 : }
1267 : }
1268 26 : else if (arg->GetType() == GAAT_REAL_LIST)
1269 : {
1270 17 : if (!arg->Set(std::get<std::vector<double>>(
1271 17 : inConstructionValues[arg])))
1272 : {
1273 6 : return false;
1274 : }
1275 : }
1276 9 : else if (arg->GetType() == GAAT_DATASET_LIST)
1277 : {
1278 9 : if (!arg->Set(
1279 : std::move(std::get<std::vector<GDALArgDatasetValue>>(
1280 9 : inConstructionValues[arg]))))
1281 : {
1282 1 : return false;
1283 : }
1284 : }
1285 : }
1286 337 : return true;
1287 353 : };
1288 :
1289 353 : size_t i = 0;
1290 353 : size_t iCurPosArg = 0;
1291 601 : while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
1292 : {
1293 257 : GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
1294 260 : while (arg->IsExplicitlySet())
1295 : {
1296 4 : ++iCurPosArg;
1297 4 : if (iCurPosArg == m_positionalArgs.size())
1298 1 : break;
1299 3 : arg = m_positionalArgs[iCurPosArg];
1300 : }
1301 257 : if (iCurPosArg == m_positionalArgs.size())
1302 : {
1303 1 : break;
1304 : }
1305 277 : if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
1306 21 : arg->GetMinCount() != arg->GetMaxCount())
1307 : {
1308 16 : if (iCurPosArg == 0)
1309 : {
1310 13 : size_t nCountAtEnd = 0;
1311 23 : for (size_t j = 1; j < m_positionalArgs.size(); j++)
1312 : {
1313 12 : const auto *otherArg = m_positionalArgs[j];
1314 12 : if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
1315 : {
1316 4 : if (otherArg->GetMinCount() != otherArg->GetMaxCount())
1317 : {
1318 2 : ReportError(
1319 : CE_Failure, CPLE_AppDefined,
1320 : "Ambiguity in definition of positional "
1321 : "argument "
1322 : "'%s' given it has a varying number of values, "
1323 : "but follows argument '%s' which also has a "
1324 : "varying number of values",
1325 1 : otherArg->GetName().c_str(),
1326 1 : arg->GetName().c_str());
1327 1 : ProcessInConstructionValues();
1328 1 : return false;
1329 : }
1330 3 : nCountAtEnd += otherArg->GetMinCount();
1331 : }
1332 : else
1333 : {
1334 8 : if (!otherArg->IsRequired())
1335 : {
1336 2 : ReportError(
1337 : CE_Failure, CPLE_AppDefined,
1338 : "Ambiguity in definition of positional "
1339 : "argument "
1340 : "'%s', given it is not required but follows "
1341 : "argument '%s' which has a varying number of "
1342 : "values",
1343 1 : otherArg->GetName().c_str(),
1344 1 : arg->GetName().c_str());
1345 1 : ProcessInConstructionValues();
1346 1 : return false;
1347 : }
1348 7 : nCountAtEnd++;
1349 : }
1350 : }
1351 11 : if (lArgs.size() < nCountAtEnd)
1352 : {
1353 1 : ReportError(CE_Failure, CPLE_AppDefined,
1354 : "Not enough positional values.");
1355 1 : ProcessInConstructionValues();
1356 1 : return false;
1357 : }
1358 26 : for (; i < lArgs.size() - nCountAtEnd; ++i)
1359 : {
1360 17 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
1361 : inConstructionValues))
1362 : {
1363 1 : ProcessInConstructionValues();
1364 1 : return false;
1365 : }
1366 : }
1367 : }
1368 3 : else if (iCurPosArg == m_positionalArgs.size() - 1)
1369 : {
1370 5 : for (; i < lArgs.size(); ++i)
1371 : {
1372 4 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
1373 : inConstructionValues))
1374 : {
1375 1 : ProcessInConstructionValues();
1376 1 : return false;
1377 : }
1378 : }
1379 : }
1380 : else
1381 : {
1382 1 : ReportError(CE_Failure, CPLE_AppDefined,
1383 : "Ambiguity in definition of positional arguments: "
1384 : "arguments with varying number of values must be "
1385 : "first or last one.");
1386 1 : return false;
1387 : }
1388 : }
1389 : else
1390 : {
1391 240 : if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
1392 : {
1393 1 : ReportError(CE_Failure, CPLE_AppDefined,
1394 : "Not enough positional values.");
1395 1 : return false;
1396 : }
1397 239 : const size_t iMax = i + arg->GetMaxCount();
1398 481 : for (; i < iMax; ++i)
1399 : {
1400 243 : if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
1401 : inConstructionValues))
1402 : {
1403 1 : ProcessInConstructionValues();
1404 1 : return false;
1405 : }
1406 : }
1407 : }
1408 248 : ++iCurPosArg;
1409 : }
1410 :
1411 345 : if (i < lArgs.size())
1412 : {
1413 3 : ReportError(CE_Failure, CPLE_AppDefined,
1414 : "Positional values starting at '%s' are not expected.",
1415 3 : lArgs[i].c_str());
1416 3 : ProcessInConstructionValues();
1417 3 : return false;
1418 : }
1419 410 : if (iCurPosArg < m_positionalArgs.size() &&
1420 68 : (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
1421 1 : ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
1422 67 : : m_positionalArgs[iCurPosArg]->IsRequired()))
1423 : {
1424 137 : while (iCurPosArg < m_positionalArgs.size() &&
1425 53 : m_positionalArgs[iCurPosArg]->IsExplicitlySet())
1426 : {
1427 31 : ++iCurPosArg;
1428 : }
1429 53 : if (iCurPosArg < m_positionalArgs.size())
1430 : {
1431 22 : ReportError(CE_Failure, CPLE_AppDefined,
1432 : "Positional arguments starting at '%s' have not been "
1433 : "specified.",
1434 22 : m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
1435 22 : ProcessInConstructionValues();
1436 22 : return false;
1437 : }
1438 : }
1439 :
1440 626 : return ProcessInConstructionValues() &&
1441 626 : (m_skipValidationInParseCommandLine || ValidateArguments());
1442 : }
1443 :
1444 : /************************************************************************/
1445 : /* GDALAlgorithm::ReportError() */
1446 : /************************************************************************/
1447 :
1448 : //! @cond Doxygen_Suppress
1449 141 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
1450 : const char *fmt, ...) const
1451 : {
1452 : va_list args;
1453 141 : va_start(args, fmt);
1454 141 : CPLError(eErrClass, err_no, "%s",
1455 141 : std::string(m_name)
1456 141 : .append(": ")
1457 282 : .append(CPLString().vPrintf(fmt, args))
1458 : .c_str());
1459 141 : va_end(args);
1460 141 : }
1461 :
1462 : //! @endcond
1463 :
1464 : /************************************************************************/
1465 : /* GDALAlgorithm::ProcessDatasetArg() */
1466 : /************************************************************************/
1467 :
1468 549 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
1469 : GDALAlgorithm *algForOutput)
1470 : {
1471 549 : bool ret = true;
1472 :
1473 549 : const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
1474 725 : const bool update = (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
1475 176 : updateArg->Get<bool>());
1476 549 : const auto overwriteArg = algForOutput->GetArg("overwrite");
1477 : const bool overwrite =
1478 944 : (arg->IsOutput() && overwriteArg &&
1479 944 : overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
1480 549 : auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
1481 549 : auto &val = arg->Get<GDALArgDatasetValue>();
1482 549 : if (!val.GetDatasetRef() && !val.IsNameSet())
1483 : {
1484 2 : ReportError(CE_Failure, CPLE_AppDefined,
1485 : "Argument '%s' has no dataset object or dataset name.",
1486 2 : arg->GetName().c_str());
1487 2 : ret = false;
1488 : }
1489 869 : else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
1490 322 : (!arg->IsOutput() || (arg == outputArg && update && !overwrite)))
1491 : {
1492 135 : int flags = val.GetType();
1493 135 : bool assignToOutputArg = false;
1494 :
1495 : // Check if input and output parameters point to the same
1496 : // filename (for vector datasets)
1497 255 : if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
1498 255 : outputArg && outputArg->GetType() == GAAT_DATASET)
1499 : {
1500 3 : auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
1501 6 : if (!outputVal.GetDatasetRef() &&
1502 6 : outputVal.GetName() == val.GetName() &&
1503 1 : (outputVal.GetInputFlags() & GADV_OBJECT) != 0)
1504 : {
1505 1 : assignToOutputArg = true;
1506 1 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
1507 : }
1508 : }
1509 :
1510 135 : if (!arg->IsOutput() || val.GetInputFlags() == GADV_NAME)
1511 133 : flags |= GDAL_OF_VERBOSE_ERROR;
1512 135 : if ((arg == outputArg || !outputArg) && update)
1513 3 : flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
1514 :
1515 135 : const auto readOnlyArg = algForOutput->GetArg(GDAL_ARG_NAME_READ_ONLY);
1516 : const bool readOnly =
1517 138 : (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
1518 3 : readOnlyArg->Get<bool>());
1519 135 : if (readOnly)
1520 2 : flags &= ~GDAL_OF_UPDATE;
1521 :
1522 270 : CPLStringList aosOpenOptions;
1523 270 : CPLStringList aosAllowedDrivers;
1524 135 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
1525 : {
1526 120 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
1527 120 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
1528 : aosOpenOptions =
1529 117 : CPLStringList(ooArg->Get<std::vector<std::string>>());
1530 :
1531 120 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
1532 120 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
1533 : aosAllowedDrivers =
1534 117 : CPLStringList(ifArg->Get<std::vector<std::string>>());
1535 : }
1536 :
1537 : auto poDS =
1538 135 : GDALDataset::Open(val.GetName().c_str(), flags,
1539 135 : aosAllowedDrivers.List(), aosOpenOptions.List());
1540 135 : if (poDS)
1541 : {
1542 120 : if (assignToOutputArg)
1543 : {
1544 : // Avoid opening twice the same datasource if it is both
1545 : // the input and output.
1546 : // Known to cause problems with at least FGdb, SQLite
1547 : // and GPKG drivers. See #4270
1548 : // Restrict to those 3 drivers. For example it is known
1549 : // to break with the PG driver due to the way it
1550 : // manages transactions.
1551 1 : auto poDriver = poDS->GetDriver();
1552 2 : if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
1553 1 : EQUAL(poDriver->GetDescription(), "SQLite") ||
1554 1 : EQUAL(poDriver->GetDescription(), "GPKG")))
1555 : {
1556 1 : outputArg->Get<GDALArgDatasetValue>().Set(poDS);
1557 : }
1558 : }
1559 120 : val.Set(poDS);
1560 120 : poDS->ReleaseRef();
1561 : }
1562 : else
1563 : {
1564 15 : ret = false;
1565 : }
1566 : }
1567 549 : return ret;
1568 : }
1569 :
1570 : /************************************************************************/
1571 : /* GDALAlgorithm::ValidateArguments() */
1572 : /************************************************************************/
1573 :
1574 600 : bool GDALAlgorithm::ValidateArguments()
1575 : {
1576 600 : if (m_selectedSubAlg)
1577 3 : return m_selectedSubAlg->ValidateArguments();
1578 :
1579 597 : if (m_specialActionRequested)
1580 1 : return true;
1581 :
1582 596 : bool ret = true;
1583 596 : std::map<std::string, std::string> mutualExclusionGroupUsed;
1584 8095 : for (auto &arg : m_args)
1585 : {
1586 : // Check mutually exclusive arguments
1587 7499 : if (arg->IsExplicitlySet())
1588 : {
1589 893 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
1590 893 : if (!mutualExclusionGroup.empty())
1591 : {
1592 : auto oIter =
1593 30 : mutualExclusionGroupUsed.find(mutualExclusionGroup);
1594 30 : if (oIter != mutualExclusionGroupUsed.end())
1595 : {
1596 1 : ret = false;
1597 2 : ReportError(
1598 : CE_Failure, CPLE_AppDefined,
1599 : "Argument '%s' is mutually exclusive with '%s'.",
1600 2 : arg->GetName().c_str(), oIter->second.c_str());
1601 : }
1602 : else
1603 : {
1604 29 : mutualExclusionGroupUsed[mutualExclusionGroup] =
1605 58 : arg->GetName();
1606 : }
1607 : }
1608 : }
1609 :
1610 7505 : if (arg->IsRequired() && !arg->IsExplicitlySet() &&
1611 6 : !arg->HasDefaultValue())
1612 : {
1613 6 : ReportError(CE_Failure, CPLE_AppDefined,
1614 : "Required argument '%s' has not been specified.",
1615 6 : arg->GetName().c_str());
1616 6 : ret = false;
1617 : }
1618 7493 : else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
1619 : {
1620 495 : if (!ProcessDatasetArg(arg.get(), this))
1621 16 : ret = false;
1622 : }
1623 7396 : else if (arg->IsExplicitlySet() &&
1624 398 : GDALAlgorithmArgTypeIsList(arg->GetType()))
1625 : {
1626 123 : int valueCount = 0;
1627 123 : if (arg->GetType() == GAAT_STRING_LIST)
1628 : {
1629 58 : valueCount = static_cast<int>(
1630 58 : arg->Get<std::vector<std::string>>().size());
1631 : }
1632 65 : else if (arg->GetType() == GAAT_INTEGER_LIST)
1633 : {
1634 8 : valueCount =
1635 8 : static_cast<int>(arg->Get<std::vector<int>>().size());
1636 : }
1637 57 : else if (arg->GetType() == GAAT_REAL_LIST)
1638 : {
1639 27 : valueCount =
1640 27 : static_cast<int>(arg->Get<std::vector<double>>().size());
1641 : }
1642 30 : else if (arg->GetType() == GAAT_DATASET_LIST)
1643 : {
1644 30 : valueCount = static_cast<int>(
1645 30 : arg->Get<std::vector<GDALArgDatasetValue>>().size());
1646 : }
1647 :
1648 197 : if (valueCount != arg->GetMinCount() &&
1649 74 : arg->GetMinCount() == arg->GetMaxCount())
1650 : {
1651 2 : ReportError(
1652 : CE_Failure, CPLE_AppDefined,
1653 : "%d value(s) have been specified for argument '%s', "
1654 : "whereas exactly %d were expected.",
1655 1 : valueCount, arg->GetName().c_str(), arg->GetMinCount());
1656 1 : ret = false;
1657 : }
1658 122 : else if (valueCount < arg->GetMinCount())
1659 : {
1660 4 : ReportError(
1661 : CE_Failure, CPLE_AppDefined,
1662 : "Only %d value(s) have been specified for argument '%s', "
1663 : "whereas at least %d were expected.",
1664 2 : valueCount, arg->GetName().c_str(), arg->GetMinCount());
1665 2 : ret = false;
1666 : }
1667 120 : else if (valueCount > arg->GetMaxCount())
1668 : {
1669 2 : ReportError(CE_Failure, CPLE_AppDefined,
1670 : "%d values have been specified for argument '%s', "
1671 : "whereas at most %d were expected.",
1672 1 : valueCount, arg->GetName().c_str(),
1673 : arg->GetMaxCount());
1674 1 : ret = false;
1675 : }
1676 : }
1677 :
1678 7529 : if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
1679 30 : arg->AutoOpenDataset())
1680 : {
1681 4 : auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
1682 8 : for (auto &val : listVal)
1683 : {
1684 4 : if (!val.GetDatasetRef() && val.GetName().empty())
1685 : {
1686 1 : ReportError(
1687 : CE_Failure, CPLE_AppDefined,
1688 : "Argument '%s' has no dataset object or dataset name.",
1689 1 : arg->GetName().c_str());
1690 1 : ret = false;
1691 : }
1692 3 : else if (!val.GetDatasetRef())
1693 : {
1694 3 : int flags = val.GetType() | GDAL_OF_VERBOSE_ERROR;
1695 :
1696 6 : CPLStringList aosOpenOptions;
1697 6 : CPLStringList aosAllowedDrivers;
1698 3 : if (arg->GetName() == GDAL_ARG_NAME_INPUT)
1699 : {
1700 1 : const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
1701 1 : if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
1702 : {
1703 1 : aosOpenOptions = CPLStringList(
1704 1 : ooArg->Get<std::vector<std::string>>());
1705 : }
1706 :
1707 1 : const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
1708 1 : if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
1709 : {
1710 1 : aosAllowedDrivers = CPLStringList(
1711 1 : ifArg->Get<std::vector<std::string>>());
1712 : }
1713 :
1714 1 : const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
1715 2 : if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
1716 1 : updateArg->Get<bool>())
1717 : {
1718 1 : flags |= GDAL_OF_UPDATE;
1719 : }
1720 : }
1721 :
1722 : auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1723 3 : val.GetName().c_str(), flags, aosAllowedDrivers.List(),
1724 9 : aosOpenOptions.List()));
1725 3 : if (poDS)
1726 : {
1727 2 : val.Set(std::move(poDS));
1728 : }
1729 : else
1730 : {
1731 1 : ret = false;
1732 : }
1733 : }
1734 : }
1735 : }
1736 : }
1737 596 : return ret;
1738 : }
1739 :
1740 : /************************************************************************/
1741 : /* GDALAlgorithm::GetArg() */
1742 : /************************************************************************/
1743 :
1744 2757 : const GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName) const
1745 : {
1746 2757 : const auto nPos = osName.find_first_not_of('-');
1747 2757 : if (nPos == std::string::npos)
1748 8 : return nullptr;
1749 5498 : const std::string osKey = osName.substr(nPos);
1750 : {
1751 2749 : const auto oIter = m_mapLongNameToArg.find(osKey);
1752 2749 : if (oIter != m_mapLongNameToArg.end())
1753 1765 : return oIter->second;
1754 : }
1755 : {
1756 984 : const auto oIter = m_mapShortNameToArg.find(osKey);
1757 984 : if (oIter != m_mapShortNameToArg.end())
1758 2 : return oIter->second;
1759 : }
1760 982 : return nullptr;
1761 : }
1762 :
1763 : /************************************************************************/
1764 : /* GDALAlgorithm::AddAliasFor() */
1765 : /************************************************************************/
1766 :
1767 : //! @cond Doxygen_Suppress
1768 2547 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
1769 : const std::string &alias)
1770 : {
1771 2547 : if (cpl::contains(m_mapLongNameToArg, alias))
1772 : {
1773 1 : ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
1774 : alias.c_str());
1775 : }
1776 : else
1777 : {
1778 2546 : m_mapLongNameToArg[alias] = arg;
1779 : }
1780 2547 : }
1781 :
1782 : //! @endcond
1783 :
1784 : /************************************************************************/
1785 : /* GDALAlgorithm::SetPositional() */
1786 : /************************************************************************/
1787 :
1788 : //! @cond Doxygen_Suppress
1789 733 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
1790 : {
1791 733 : CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
1792 : arg) == m_positionalArgs.end());
1793 733 : m_positionalArgs.push_back(arg);
1794 733 : }
1795 :
1796 : //! @endcond
1797 :
1798 : /************************************************************************/
1799 : /* GDALAlgorithm::AddArg() */
1800 : /************************************************************************/
1801 :
1802 : GDALInConstructionAlgorithmArg &
1803 11036 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
1804 : {
1805 11036 : auto argRaw = arg.get();
1806 11036 : const auto &longName = argRaw->GetName();
1807 11036 : if (!longName.empty())
1808 : {
1809 11033 : if (longName[0] == '-')
1810 : {
1811 1 : ReportError(CE_Failure, CPLE_AppDefined,
1812 : "Long name '%s' should not start with '-'",
1813 : longName.c_str());
1814 : }
1815 11033 : if (longName.find('=') != std::string::npos)
1816 : {
1817 1 : ReportError(CE_Failure, CPLE_AppDefined,
1818 : "Long name '%s' should not contain a '=' character",
1819 : longName.c_str());
1820 : }
1821 11033 : if (cpl::contains(m_mapLongNameToArg, longName))
1822 : {
1823 1 : ReportError(CE_Failure, CPLE_AppDefined,
1824 : "Long name '%s' already declared", longName.c_str());
1825 : }
1826 11033 : m_mapLongNameToArg[longName] = argRaw;
1827 : }
1828 11036 : const auto &shortName = argRaw->GetShortName();
1829 11036 : if (!shortName.empty())
1830 : {
1831 5368 : if (shortName.size() != 1 ||
1832 2684 : !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
1833 3 : (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
1834 2 : (shortName[0] >= '0' && shortName[0] <= '9')))
1835 : {
1836 1 : ReportError(CE_Failure, CPLE_AppDefined,
1837 : "Short name '%s' should be a single letter or digit",
1838 : shortName.c_str());
1839 : }
1840 2684 : if (cpl::contains(m_mapShortNameToArg, shortName))
1841 : {
1842 1 : ReportError(CE_Failure, CPLE_AppDefined,
1843 : "Short name '%s' already declared", shortName.c_str());
1844 : }
1845 2684 : m_mapShortNameToArg[shortName] = argRaw;
1846 : }
1847 11036 : m_args.emplace_back(std::move(arg));
1848 : return *(
1849 11036 : static_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
1850 : }
1851 :
1852 : GDALInConstructionAlgorithmArg &
1853 6256 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1854 : const std::string &helpMessage, bool *pValue)
1855 : {
1856 6256 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1857 : this,
1858 12512 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
1859 12512 : pValue));
1860 : }
1861 :
1862 : GDALInConstructionAlgorithmArg &
1863 1107 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1864 : const std::string &helpMessage, std::string *pValue)
1865 : {
1866 1107 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1867 : this,
1868 2214 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
1869 2214 : pValue));
1870 : }
1871 :
1872 : GDALInConstructionAlgorithmArg &
1873 66 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1874 : const std::string &helpMessage, int *pValue)
1875 : {
1876 66 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1877 : this,
1878 132 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
1879 132 : pValue));
1880 : }
1881 :
1882 : GDALInConstructionAlgorithmArg &
1883 4 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1884 : const std::string &helpMessage, double *pValue)
1885 : {
1886 4 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1887 : this,
1888 8 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
1889 8 : pValue));
1890 : }
1891 :
1892 : GDALInConstructionAlgorithmArg &
1893 781 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1894 : const std::string &helpMessage,
1895 : GDALArgDatasetValue *pValue, GDALArgDatasetValueType type)
1896 : {
1897 781 : pValue->SetType(type);
1898 781 : auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1899 : this,
1900 1562 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_DATASET),
1901 781 : pValue));
1902 781 : pValue->SetOwnerArgument(&arg);
1903 781 : return arg;
1904 : }
1905 :
1906 : GDALInConstructionAlgorithmArg &
1907 2567 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1908 : const std::string &helpMessage,
1909 : std::vector<std::string> *pValue)
1910 : {
1911 2567 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1912 : this,
1913 5134 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
1914 : GAAT_STRING_LIST),
1915 5134 : pValue));
1916 : }
1917 :
1918 : GDALInConstructionAlgorithmArg &
1919 44 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1920 : const std::string &helpMessage, std::vector<int> *pValue)
1921 : {
1922 44 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1923 : this,
1924 88 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
1925 : GAAT_INTEGER_LIST),
1926 88 : pValue));
1927 : }
1928 :
1929 : GDALInConstructionAlgorithmArg &
1930 182 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1931 : const std::string &helpMessage,
1932 : std::vector<double> *pValue)
1933 : {
1934 182 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1935 : this,
1936 364 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
1937 : GAAT_REAL_LIST),
1938 364 : pValue));
1939 : }
1940 :
1941 : GDALInConstructionAlgorithmArg &
1942 29 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
1943 : const std::string &helpMessage,
1944 : std::vector<GDALArgDatasetValue> *pValue,
1945 : GDALArgDatasetValueType)
1946 : {
1947 : // FIXME
1948 : // pValue->SetType(type);
1949 29 : return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
1950 : this,
1951 58 : GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
1952 : GAAT_DATASET_LIST),
1953 58 : pValue));
1954 : }
1955 :
1956 : /************************************************************************/
1957 : /* GDALAlgorithm::AddInputDatasetArg() */
1958 : /************************************************************************/
1959 :
1960 : GDALInConstructionAlgorithmArg &
1961 410 : GDALAlgorithm::AddInputDatasetArg(GDALArgDatasetValue *pValue,
1962 : GDALArgDatasetValueType type,
1963 : bool positionalAndRequired)
1964 : {
1965 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
1966 : CPLSPrintf("Input %s dataset",
1967 410 : GDALArgDatasetValueTypeName(type).c_str()),
1968 820 : pValue, type);
1969 410 : if (positionalAndRequired)
1970 293 : arg.SetPositional().SetRequired();
1971 :
1972 : arg.SetAutoCompleteFunction(
1973 1176 : [type](const std::string ¤tValue)
1974 : {
1975 3 : std::vector<std::string> oRet;
1976 :
1977 3 : auto poDM = GetGDALDriverManager();
1978 6 : std::set<std::string> oExtensions;
1979 720 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
1980 : {
1981 717 : auto poDriver = poDM->GetDriver(i);
1982 2151 : if (((type & GDAL_OF_RASTER) != 0 &&
1983 717 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
1984 228 : ((type & GDAL_OF_VECTOR) != 0 &&
1985 1434 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
1986 228 : ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
1987 0 : poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
1988 : {
1989 : const char *pszExtensions =
1990 489 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
1991 489 : if (pszExtensions)
1992 : {
1993 : const CPLStringList aosExts(
1994 630 : CSLTokenizeString2(pszExtensions, " ", 0));
1995 693 : for (const char *pszExt : cpl::Iterate(aosExts))
1996 378 : oExtensions.insert(CPLString(pszExt).tolower());
1997 : }
1998 : }
1999 : }
2000 :
2001 6 : std::string osDir = CPLGetDirnameSafe(currentValue.c_str());
2002 3 : auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
2003 6 : const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
2004 3 : if (currentValue.empty())
2005 1 : osDir.clear();
2006 : const std::string currentFilename =
2007 6 : CPLGetFilename(currentValue.c_str());
2008 3 : if (psDir)
2009 : {
2010 115 : while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
2011 : {
2012 112 : if ((currentFilename.empty() ||
2013 0 : STARTS_WITH(psEntry->pszName,
2014 112 : currentFilename.c_str())) &&
2015 112 : strcmp(psEntry->pszName, ".") != 0 &&
2016 336 : strcmp(psEntry->pszName, "..") != 0 &&
2017 112 : !strstr(psEntry->pszName, ".aux.xml"))
2018 : {
2019 333 : if (cpl::contains(
2020 : oExtensions,
2021 222 : CPLString(CPLGetExtensionSafe(psEntry->pszName))
2022 310 : .tolower()) ||
2023 88 : VSI_ISDIR(psEntry->nMode))
2024 : {
2025 54 : std::string osVal;
2026 27 : if (osDir.empty())
2027 4 : osVal = psEntry->pszName;
2028 : else
2029 46 : osVal = CPLFormFilenameSafe(
2030 46 : osDir.c_str(), psEntry->pszName, nullptr);
2031 27 : if (VSI_ISDIR(psEntry->nMode))
2032 4 : osVal += osSep;
2033 27 : oRet.push_back(osVal);
2034 : }
2035 : }
2036 112 : }
2037 3 : VSICloseDir(psDir);
2038 : }
2039 6 : return oRet;
2040 410 : });
2041 :
2042 410 : return arg;
2043 : }
2044 :
2045 : /************************************************************************/
2046 : /* GDALAlgorithm::AddInputDatasetArg() */
2047 : /************************************************************************/
2048 :
2049 : GDALInConstructionAlgorithmArg &
2050 1 : GDALAlgorithm::AddInputDatasetArg(std::vector<GDALArgDatasetValue> *pValue,
2051 : GDALArgDatasetValueType type,
2052 : bool positionalAndRequired)
2053 : {
2054 : auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
2055 : CPLSPrintf("Input %s datasets",
2056 1 : GDALArgDatasetValueTypeName(type).c_str()),
2057 2 : pValue, type);
2058 1 : if (positionalAndRequired)
2059 1 : arg.SetPositional().SetRequired();
2060 1 : return arg;
2061 : }
2062 :
2063 : /************************************************************************/
2064 : /* GDALAlgorithm::AddOutputDatasetArg() */
2065 : /************************************************************************/
2066 :
2067 : GDALInConstructionAlgorithmArg &
2068 321 : GDALAlgorithm::AddOutputDatasetArg(GDALArgDatasetValue *pValue,
2069 : GDALArgDatasetValueType type,
2070 : bool positionalAndRequired)
2071 : {
2072 321 : pValue->SetInputFlags(GADV_NAME);
2073 321 : pValue->SetOutputFlags(GADV_OBJECT);
2074 : auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
2075 : CPLSPrintf("Output %s dataset",
2076 321 : GDALArgDatasetValueTypeName(type).c_str()),
2077 963 : pValue, type)
2078 321 : .SetIsInput(true)
2079 321 : .SetIsOutput(true);
2080 321 : if (positionalAndRequired)
2081 204 : arg.SetPositional().SetRequired();
2082 321 : return arg;
2083 : }
2084 :
2085 : /************************************************************************/
2086 : /* GDALAlgorithm::AddOverwriteArg() */
2087 : /************************************************************************/
2088 :
2089 312 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOverwriteArg(bool *pValue)
2090 : {
2091 : return AddArg("overwrite", 0,
2092 624 : _("Whether overwriting existing output is allowed"), pValue)
2093 624 : .SetDefault(false);
2094 : }
2095 :
2096 : /************************************************************************/
2097 : /* GDALAlgorithm::AddUpdateArg() */
2098 : /************************************************************************/
2099 :
2100 132 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddUpdateArg(bool *pValue)
2101 : {
2102 : return AddArg(GDAL_ARG_NAME_UPDATE, 0,
2103 264 : _("Whether to open existing dataset in update mode"), pValue)
2104 264 : .SetDefault(false);
2105 : }
2106 :
2107 : /************************************************************************/
2108 : /* AddOptionsSuggestions() */
2109 : /************************************************************************/
2110 :
2111 19 : static bool AddOptionsSuggestions(const char *pszXML, int datasetType,
2112 : const std::string ¤tValue,
2113 : std::vector<std::string> &oRet)
2114 : {
2115 19 : if (!pszXML)
2116 0 : return false;
2117 38 : CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
2118 19 : if (!poTree)
2119 0 : return false;
2120 : const CPLXMLNode *psRoot =
2121 19 : CPLGetXMLNode(poTree.get(), "=CreationOptionList");
2122 19 : if (!psRoot)
2123 4 : psRoot = CPLGetXMLNode(poTree.get(), "=LayerCreationOptionList");
2124 19 : if (!psRoot)
2125 2 : psRoot = CPLGetXMLNode(poTree.get(), "=OpenOptionList");
2126 19 : if (!psRoot)
2127 0 : return false;
2128 :
2129 307 : for (const CPLXMLNode *psChild = psRoot->psChild; psChild;
2130 288 : psChild = psChild->psNext)
2131 : {
2132 298 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
2133 308 : if (pszName && currentValue == pszName &&
2134 10 : EQUAL(psChild->pszValue, "Option"))
2135 : {
2136 10 : const char *pszType = CPLGetXMLValue(psChild, "type", "");
2137 10 : const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
2138 10 : const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
2139 10 : if (EQUAL(pszType, "string-select"))
2140 : {
2141 72 : for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
2142 68 : psChild2 = psChild2->psNext)
2143 : {
2144 68 : if (EQUAL(psChild2->pszValue, "Value"))
2145 : {
2146 60 : oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
2147 : }
2148 : }
2149 : }
2150 6 : else if (EQUAL(pszType, "boolean"))
2151 : {
2152 1 : oRet.push_back("NO");
2153 1 : oRet.push_back("YES");
2154 : }
2155 5 : else if (EQUAL(pszType, "int"))
2156 : {
2157 5 : if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
2158 2 : atoi(pszMax) - atoi(pszMin) < 25)
2159 : {
2160 1 : const int nMax = atoi(pszMax);
2161 13 : for (int i = atoi(pszMin); i <= nMax; ++i)
2162 12 : oRet.push_back(std::to_string(i));
2163 : }
2164 : }
2165 :
2166 10 : if (oRet.empty())
2167 : {
2168 4 : if (pszMin && pszMax)
2169 : {
2170 1 : oRet.push_back(std::string("##"));
2171 2 : oRet.push_back(std::string("validity range: [")
2172 1 : .append(pszMin)
2173 1 : .append(",")
2174 1 : .append(pszMax)
2175 1 : .append("]"));
2176 : }
2177 3 : else if (pszMin)
2178 : {
2179 1 : oRet.push_back(std::string("##"));
2180 1 : oRet.push_back(
2181 1 : std::string("validity range: >= ").append(pszMin));
2182 : }
2183 2 : else if (pszMax)
2184 : {
2185 1 : oRet.push_back(std::string("##"));
2186 1 : oRet.push_back(
2187 1 : std::string("validity range: <= ").append(pszMax));
2188 : }
2189 1 : else if (const char *pszDescription =
2190 1 : CPLGetXMLValue(psChild, "description", nullptr))
2191 : {
2192 1 : oRet.push_back(std::string("##"));
2193 2 : oRet.push_back(std::string("type: ")
2194 1 : .append(pszType)
2195 1 : .append(", description: ")
2196 1 : .append(pszDescription));
2197 : }
2198 : }
2199 :
2200 10 : return true;
2201 : }
2202 : }
2203 :
2204 228 : for (const CPLXMLNode *psChild = psRoot->psChild; psChild;
2205 219 : psChild = psChild->psNext)
2206 : {
2207 219 : const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
2208 219 : if (pszName && EQUAL(psChild->pszValue, "Option"))
2209 : {
2210 219 : const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
2211 219 : if (!pszScope ||
2212 40 : (EQUAL(pszScope, "raster") &&
2213 40 : (datasetType & GDAL_OF_RASTER) != 0) ||
2214 20 : (EQUAL(pszScope, "vector") &&
2215 0 : (datasetType & GDAL_OF_VECTOR) != 0))
2216 : {
2217 199 : oRet.push_back(std::string(pszName).append("="));
2218 : }
2219 : }
2220 : }
2221 :
2222 9 : return false;
2223 : }
2224 :
2225 : /************************************************************************/
2226 : /* GDALAlgorithm::AddOpenOptionsArg() */
2227 : /************************************************************************/
2228 :
2229 : GDALInConstructionAlgorithmArg &
2230 388 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue)
2231 : {
2232 776 : auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0, _("Open options"), pValue)
2233 776 : .AddAlias("oo")
2234 776 : .SetMetaVar("KEY=VALUE")
2235 388 : .SetCategory(GAAC_ADVANCED);
2236 :
2237 : arg.SetAutoCompleteFunction(
2238 6 : [this](const std::string ¤tValue)
2239 : {
2240 2 : std::vector<std::string> oRet;
2241 :
2242 2 : int datasetType =
2243 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
2244 2 : auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
2245 2 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
2246 : {
2247 2 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
2248 2 : datasetType = datasetValue.GetType();
2249 : }
2250 :
2251 2 : auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
2252 4 : if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
2253 2 : inputFormat->IsExplicitlySet())
2254 : {
2255 : const auto &aosAllowedDrivers =
2256 1 : inputFormat->Get<std::vector<std::string>>();
2257 1 : if (aosAllowedDrivers.size() == 1)
2258 : {
2259 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
2260 1 : aosAllowedDrivers[0].c_str());
2261 1 : if (poDriver)
2262 : {
2263 1 : AddOptionsSuggestions(
2264 1 : poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
2265 : datasetType, currentValue, oRet);
2266 : }
2267 1 : return oRet;
2268 : }
2269 : }
2270 :
2271 1 : if (inputArg && inputArg->GetType() == GAAT_DATASET)
2272 : {
2273 1 : auto poDM = GetGDALDriverManager();
2274 1 : auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
2275 1 : const auto &osDSName = datasetValue.GetName();
2276 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
2277 1 : if (!osExt.empty())
2278 : {
2279 1 : std::set<std::string> oVisitedExtensions;
2280 240 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
2281 : {
2282 239 : auto poDriver = poDM->GetDriver(i);
2283 717 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
2284 239 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
2285 76 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
2286 478 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
2287 76 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
2288 0 : poDriver->GetMetadataItem(
2289 0 : GDAL_DCAP_MULTIDIM_RASTER)))
2290 : {
2291 : const char *pszExtensions =
2292 163 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
2293 163 : if (pszExtensions)
2294 : {
2295 : const CPLStringList aosExts(
2296 105 : CSLTokenizeString2(pszExtensions, " ", 0));
2297 229 : for (const char *pszExt : cpl::Iterate(aosExts))
2298 : {
2299 128 : if (EQUAL(pszExt, osExt.c_str()) &&
2300 3 : !cpl::contains(oVisitedExtensions,
2301 : pszExt))
2302 : {
2303 1 : oVisitedExtensions.insert(pszExt);
2304 1 : if (AddOptionsSuggestions(
2305 : poDriver->GetMetadataItem(
2306 1 : GDAL_DMD_OPENOPTIONLIST),
2307 : datasetType, currentValue,
2308 : oRet))
2309 : {
2310 0 : return oRet;
2311 : }
2312 1 : break;
2313 : }
2314 : }
2315 : }
2316 : }
2317 : }
2318 : }
2319 : }
2320 :
2321 1 : return oRet;
2322 388 : });
2323 :
2324 388 : return arg;
2325 : }
2326 :
2327 : /************************************************************************/
2328 : /* ValidateFormat() */
2329 : /************************************************************************/
2330 :
2331 42 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg) const
2332 : {
2333 42 : if (arg.GetChoices().empty())
2334 : {
2335 43 : const auto Validate = [this, &arg](const std::string &val)
2336 : {
2337 19 : auto hDriver = GDALGetDriverByName(val.c_str());
2338 19 : if (!hDriver)
2339 : {
2340 2 : ReportError(CE_Failure, CPLE_AppDefined,
2341 : "Invalid value for argument '%s'. Driver '%s' does "
2342 : "not exist",
2343 1 : arg.GetName().c_str(), val.c_str());
2344 1 : return false;
2345 : }
2346 :
2347 18 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
2348 18 : if (caps)
2349 : {
2350 46 : for (const std::string &cap : *caps)
2351 : {
2352 31 : if (!GDALGetMetadataItem(hDriver, cap.c_str(), nullptr))
2353 : {
2354 8 : if (cap == GDAL_DCAP_CREATECOPY &&
2355 0 : std::find(caps->begin(), caps->end(),
2356 3 : GDAL_DCAP_RASTER) != caps->end() &&
2357 3 : GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
2358 8 : nullptr) &&
2359 3 : GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
2360 : nullptr))
2361 : {
2362 : // if it supports Create, it supports CreateCopy
2363 : }
2364 : else
2365 : {
2366 4 : ReportError(
2367 : CE_Failure, CPLE_AppDefined,
2368 : "Invalid value for argument '%s'. Driver '%s' "
2369 : "does "
2370 : "not expose the required '%s' capability.",
2371 2 : arg.GetName().c_str(), val.c_str(),
2372 : cap.c_str());
2373 2 : return false;
2374 : }
2375 : }
2376 : }
2377 : }
2378 16 : return true;
2379 19 : };
2380 :
2381 19 : if (arg.GetType() == GAAT_STRING)
2382 : {
2383 17 : return Validate(arg.Get<std::string>());
2384 : }
2385 4 : else if (arg.GetType() == GAAT_STRING_LIST)
2386 : {
2387 6 : for (const auto &val : arg.Get<std::vector<std::string>>())
2388 : {
2389 4 : if (!Validate(val))
2390 2 : return false;
2391 : }
2392 : }
2393 : }
2394 :
2395 25 : return true;
2396 : }
2397 :
2398 : /************************************************************************/
2399 : /* FormatAutoCompleteFunction() */
2400 : /************************************************************************/
2401 :
2402 : static std::vector<std::string>
2403 1 : FormatAutoCompleteFunction(const GDALAlgorithmArg &arg)
2404 : {
2405 1 : std::vector<std::string> res;
2406 1 : auto poDM = GetGDALDriverManager();
2407 240 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
2408 : {
2409 239 : auto poDriver = poDM->GetDriver(i);
2410 :
2411 239 : const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
2412 239 : if (caps)
2413 : {
2414 239 : bool ok = true;
2415 491 : for (const std::string &cap : *caps)
2416 : {
2417 402 : if (poDriver->GetMetadataItem(cap.c_str()))
2418 : {
2419 : }
2420 280 : else if (cap == GDAL_DCAP_CREATECOPY &&
2421 0 : std::find(caps->begin(), caps->end(),
2422 102 : GDAL_DCAP_RASTER) != caps->end() &&
2423 382 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
2424 102 : poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
2425 : {
2426 : // if it supports Create, it supports CreateCopy
2427 : }
2428 : else
2429 : {
2430 150 : ok = false;
2431 150 : break;
2432 : }
2433 : }
2434 239 : if (ok)
2435 : {
2436 89 : res.push_back(poDriver->GetDescription());
2437 : }
2438 : }
2439 : }
2440 1 : return res;
2441 : }
2442 :
2443 : /************************************************************************/
2444 : /* GDALAlgorithm::AddInputFormatsArg() */
2445 : /************************************************************************/
2446 :
2447 : GDALInConstructionAlgorithmArg &
2448 375 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue)
2449 : {
2450 : auto &arg =
2451 750 : AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0, _("Input formats"), pValue)
2452 750 : .AddAlias("if")
2453 375 : .SetCategory(GAAC_ADVANCED);
2454 379 : arg.AddValidationAction([this, &arg]() { return ValidateFormat(arg); });
2455 0 : arg.SetAutoCompleteFunction([&arg](const std::string &)
2456 375 : { return FormatAutoCompleteFunction(arg); });
2457 375 : return arg;
2458 : }
2459 :
2460 : /************************************************************************/
2461 : /* GDALAlgorithm::AddOutputFormatArg() */
2462 : /************************************************************************/
2463 :
2464 : GDALInConstructionAlgorithmArg &
2465 404 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue)
2466 : {
2467 : auto &arg =
2468 808 : AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f', _("Output format"), pValue)
2469 808 : .AddAlias("of")
2470 404 : .AddAlias("format");
2471 442 : arg.AddValidationAction([this, &arg]() { return ValidateFormat(arg); });
2472 1 : arg.SetAutoCompleteFunction([&arg](const std::string &)
2473 405 : { return FormatAutoCompleteFunction(arg); });
2474 404 : return arg;
2475 : }
2476 :
2477 : /************************************************************************/
2478 : /* GDALAlgorithm::AddOutputStringArg() */
2479 : /************************************************************************/
2480 :
2481 : GDALInConstructionAlgorithmArg &
2482 80 : GDALAlgorithm::AddOutputStringArg(std::string *pValue)
2483 : {
2484 : return AddArg("output-string", 0,
2485 160 : _("Output string, in which the result is placed"), pValue)
2486 80 : .SetHiddenForCLI()
2487 80 : .SetIsInput(false)
2488 160 : .SetIsOutput(true);
2489 : }
2490 :
2491 : /************************************************************************/
2492 : /* GDALAlgorithm::AddLayerNameArg() */
2493 : /************************************************************************/
2494 :
2495 : GDALInConstructionAlgorithmArg &
2496 1 : GDALAlgorithm::AddLayerNameArg(std::string *pValue)
2497 : {
2498 1 : return AddArg("layer", 'l', _("Layer name"), pValue);
2499 : }
2500 :
2501 : /************************************************************************/
2502 : /* GDALAlgorithm::AddLayerNameArg() */
2503 : /************************************************************************/
2504 :
2505 : GDALInConstructionAlgorithmArg &
2506 36 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue)
2507 : {
2508 36 : return AddArg("layer", 'l', _("Layer name"), pValue);
2509 : }
2510 :
2511 : /************************************************************************/
2512 : /* ValidateKeyValue() */
2513 : /************************************************************************/
2514 :
2515 15 : bool GDALAlgorithm::ValidateKeyValue(const GDALAlgorithmArg &arg) const
2516 : {
2517 22 : const auto Validate = [this, &arg](const std::string &val)
2518 : {
2519 20 : if (val.find('=') == std::string::npos)
2520 : {
2521 2 : ReportError(
2522 : CE_Failure, CPLE_AppDefined,
2523 : "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
2524 2 : arg.GetName().c_str());
2525 2 : return false;
2526 : }
2527 :
2528 18 : return true;
2529 15 : };
2530 :
2531 15 : if (arg.GetType() == GAAT_STRING)
2532 : {
2533 0 : return Validate(arg.Get<std::string>());
2534 : }
2535 15 : else if (arg.GetType() == GAAT_STRING_LIST)
2536 : {
2537 33 : for (const auto &val : arg.Get<std::vector<std::string>>())
2538 : {
2539 20 : if (!Validate(val))
2540 2 : return false;
2541 : }
2542 : }
2543 :
2544 13 : return true;
2545 : }
2546 :
2547 : /************************************************************************/
2548 : /* GDALAlgorithm::AddCreationOptionsArg() */
2549 : /************************************************************************/
2550 :
2551 : GDALInConstructionAlgorithmArg &
2552 314 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue)
2553 : {
2554 628 : auto &arg = AddArg("creation-option", 0, _("Creation option"), pValue)
2555 628 : .AddAlias("co")
2556 314 : .SetMetaVar("<KEY>=<VALUE>");
2557 322 : arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
2558 :
2559 : arg.SetAutoCompleteFunction(
2560 45 : [this](const std::string ¤tValue)
2561 : {
2562 15 : std::vector<std::string> oRet;
2563 :
2564 15 : int datasetType =
2565 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
2566 15 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2567 15 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
2568 : {
2569 15 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
2570 15 : datasetType = datasetValue.GetType();
2571 : }
2572 :
2573 15 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2574 30 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
2575 15 : outputFormat->IsExplicitlySet())
2576 : {
2577 12 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
2578 6 : outputFormat->Get<std::string>().c_str());
2579 6 : if (poDriver)
2580 : {
2581 6 : AddOptionsSuggestions(
2582 6 : poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
2583 : datasetType, currentValue, oRet);
2584 : }
2585 6 : return oRet;
2586 : }
2587 :
2588 9 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
2589 : {
2590 9 : auto poDM = GetGDALDriverManager();
2591 9 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
2592 9 : const auto &osDSName = datasetValue.GetName();
2593 9 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
2594 9 : if (!osExt.empty())
2595 : {
2596 9 : std::set<std::string> oVisitedExtensions;
2597 515 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
2598 : {
2599 513 : auto poDriver = poDM->GetDriver(i);
2600 1539 : if (((datasetType & GDAL_OF_RASTER) != 0 &&
2601 513 : poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
2602 152 : ((datasetType & GDAL_OF_VECTOR) != 0 &&
2603 1026 : poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
2604 152 : ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
2605 0 : poDriver->GetMetadataItem(
2606 0 : GDAL_DCAP_MULTIDIM_RASTER)))
2607 : {
2608 : const char *pszExtensions =
2609 361 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
2610 361 : if (pszExtensions)
2611 : {
2612 : const CPLStringList aosExts(
2613 231 : CSLTokenizeString2(pszExtensions, " ", 0));
2614 507 : for (const char *pszExt : cpl::Iterate(aosExts))
2615 : {
2616 298 : if (EQUAL(pszExt, osExt.c_str()) &&
2617 13 : !cpl::contains(oVisitedExtensions,
2618 : pszExt))
2619 : {
2620 9 : oVisitedExtensions.insert(pszExt);
2621 9 : if (AddOptionsSuggestions(
2622 : poDriver->GetMetadataItem(
2623 9 : GDAL_DMD_CREATIONOPTIONLIST),
2624 : datasetType, currentValue,
2625 : oRet))
2626 : {
2627 7 : return oRet;
2628 : }
2629 2 : break;
2630 : }
2631 : }
2632 : }
2633 : }
2634 : }
2635 : }
2636 : }
2637 :
2638 2 : return oRet;
2639 314 : });
2640 :
2641 314 : return arg;
2642 : }
2643 :
2644 : /************************************************************************/
2645 : /* GDALAlgorithm::AddLayerCreationOptionsArg() */
2646 : /************************************************************************/
2647 :
2648 : GDALInConstructionAlgorithmArg &
2649 131 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue)
2650 : {
2651 : auto &arg =
2652 262 : AddArg("layer-creation-option", 0, _("Layer creation option"), pValue)
2653 262 : .AddAlias("lco")
2654 131 : .SetMetaVar("<KEY>=<VALUE>");
2655 135 : arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
2656 :
2657 : arg.SetAutoCompleteFunction(
2658 5 : [this](const std::string ¤tValue)
2659 : {
2660 2 : std::vector<std::string> oRet;
2661 :
2662 2 : auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
2663 4 : if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
2664 2 : outputFormat->IsExplicitlySet())
2665 : {
2666 2 : auto poDriver = GetGDALDriverManager()->GetDriverByName(
2667 1 : outputFormat->Get<std::string>().c_str());
2668 1 : if (poDriver)
2669 : {
2670 1 : AddOptionsSuggestions(poDriver->GetMetadataItem(
2671 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
2672 : GDAL_OF_VECTOR, currentValue, oRet);
2673 : }
2674 1 : return oRet;
2675 : }
2676 :
2677 1 : auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
2678 1 : if (outputArg && outputArg->GetType() == GAAT_DATASET)
2679 : {
2680 1 : auto poDM = GetGDALDriverManager();
2681 1 : auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
2682 1 : const auto &osDSName = datasetValue.GetName();
2683 1 : const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
2684 1 : if (!osExt.empty())
2685 : {
2686 1 : std::set<std::string> oVisitedExtensions;
2687 240 : for (int i = 0; i < poDM->GetDriverCount(); ++i)
2688 : {
2689 239 : auto poDriver = poDM->GetDriver(i);
2690 239 : if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
2691 : {
2692 : const char *pszExtensions =
2693 93 : poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
2694 93 : if (pszExtensions)
2695 : {
2696 : const CPLStringList aosExts(
2697 62 : CSLTokenizeString2(pszExtensions, " ", 0));
2698 157 : for (const char *pszExt : cpl::Iterate(aosExts))
2699 : {
2700 97 : if (EQUAL(pszExt, osExt.c_str()) &&
2701 1 : !cpl::contains(oVisitedExtensions,
2702 : pszExt))
2703 : {
2704 1 : oVisitedExtensions.insert(pszExt);
2705 1 : if (AddOptionsSuggestions(
2706 : poDriver->GetMetadataItem(
2707 1 : GDAL_DS_LAYER_CREATIONOPTIONLIST),
2708 : GDAL_OF_VECTOR, currentValue,
2709 : oRet))
2710 : {
2711 0 : return oRet;
2712 : }
2713 1 : break;
2714 : }
2715 : }
2716 : }
2717 : }
2718 : }
2719 : }
2720 : }
2721 :
2722 1 : return oRet;
2723 131 : });
2724 :
2725 131 : return arg;
2726 : }
2727 :
2728 : /************************************************************************/
2729 : /* GDALAlgorithm::AddBBOXArg() */
2730 : /************************************************************************/
2731 :
2732 : /** Add bbox=xmin,ymin,xmax,ymax argument. */
2733 : GDALInConstructionAlgorithmArg &
2734 103 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
2735 : {
2736 : auto &arg = AddArg("bbox", 0,
2737 : helpMessage ? helpMessage
2738 : : _("Bounding box as xmin,ymin,xmax,ymax"),
2739 206 : pValue)
2740 103 : .SetRepeatedArgAllowed(false)
2741 103 : .SetMinCount(4)
2742 103 : .SetMaxCount(4)
2743 103 : .SetDisplayHintAboutRepetition(false);
2744 : arg.AddValidationAction(
2745 15 : [&arg]()
2746 : {
2747 15 : const auto &val = arg.Get<std::vector<double>>();
2748 15 : CPLAssert(val.size() == 4);
2749 15 : if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
2750 : {
2751 4 : CPLError(CE_Failure, CPLE_AppDefined,
2752 : "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
2753 : "xmin <= xmax and ymin <= ymax");
2754 4 : return false;
2755 : }
2756 11 : return true;
2757 103 : });
2758 103 : return arg;
2759 : }
2760 :
2761 : /************************************************************************/
2762 : /* GDALAlgorithm::AddProgressArg() */
2763 : /************************************************************************/
2764 :
2765 251 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddProgressArg()
2766 : {
2767 : return AddArg("progress", 0, _("Display progress bar"),
2768 502 : &m_progressBarRequested)
2769 251 : .SetOnlyForCLI()
2770 502 : .SetCategory(GAAC_COMMON);
2771 : }
2772 :
2773 : /************************************************************************/
2774 : /* GDALAlgorithm::Run() */
2775 : /************************************************************************/
2776 :
2777 331 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
2778 : {
2779 331 : if (m_selectedSubAlg)
2780 31 : return m_selectedSubAlg->Run(pfnProgress, pProgressData);
2781 :
2782 300 : if (m_helpRequested)
2783 : {
2784 1 : printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
2785 1 : return true;
2786 : }
2787 :
2788 299 : if (m_JSONUsageRequested)
2789 : {
2790 3 : printf("%s", GetUsageAsJSON().c_str()); /*ok*/
2791 3 : return true;
2792 : }
2793 :
2794 296 : return ValidateArguments() && RunImpl(pfnProgress, pProgressData);
2795 : }
2796 :
2797 : /************************************************************************/
2798 : /* GDALAlgorithm::Finalize() */
2799 : /************************************************************************/
2800 :
2801 241 : bool GDALAlgorithm::Finalize()
2802 : {
2803 241 : bool ret = true;
2804 241 : if (m_selectedSubAlg)
2805 29 : ret = m_selectedSubAlg->Finalize();
2806 :
2807 3273 : for (auto &arg : m_args)
2808 : {
2809 3032 : if (arg->GetType() == GAAT_DATASET)
2810 : {
2811 274 : ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
2812 : }
2813 2758 : else if (arg->GetType() == GAAT_DATASET_LIST)
2814 : {
2815 6 : for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
2816 : {
2817 3 : ret = ds.Close() && ret;
2818 : }
2819 : }
2820 : }
2821 241 : return ret;
2822 : }
2823 :
2824 : /************************************************************************/
2825 : /* GDALAlgorithm::GetArgNamesForCLI() */
2826 : /************************************************************************/
2827 :
2828 : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
2829 62 : GDALAlgorithm::GetArgNamesForCLI() const
2830 : {
2831 124 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
2832 :
2833 62 : size_t maxOptLen = 0;
2834 537 : for (const auto &arg : m_args)
2835 : {
2836 475 : if (arg->IsHiddenForCLI())
2837 25 : continue;
2838 450 : std::string opt;
2839 450 : bool addComma = false;
2840 450 : if (!arg->GetShortName().empty())
2841 : {
2842 113 : opt += '-';
2843 113 : opt += arg->GetShortName();
2844 113 : addComma = true;
2845 : }
2846 485 : for (const std::string &alias : arg->GetAliases())
2847 : {
2848 35 : if (addComma)
2849 15 : opt += ", ";
2850 35 : opt += "--";
2851 35 : opt += alias;
2852 35 : addComma = true;
2853 : }
2854 450 : if (!arg->GetName().empty())
2855 : {
2856 450 : if (addComma)
2857 133 : opt += ", ";
2858 450 : opt += "--";
2859 450 : opt += arg->GetName();
2860 : }
2861 450 : const auto &metaVar = arg->GetMetaVar();
2862 450 : if (!metaVar.empty())
2863 : {
2864 165 : opt += ' ';
2865 165 : if (metaVar.front() != '<')
2866 89 : opt += '<';
2867 165 : opt += metaVar;
2868 165 : if (metaVar.back() != '>')
2869 89 : opt += '>';
2870 : }
2871 450 : maxOptLen = std::max(maxOptLen, opt.size());
2872 450 : options.emplace_back(arg.get(), opt);
2873 : }
2874 :
2875 124 : return std::make_pair(std::move(options), maxOptLen);
2876 : }
2877 :
2878 : /************************************************************************/
2879 : /* GDALAlgorithm::GetUsageForCLI() */
2880 : /************************************************************************/
2881 :
2882 : std::string
2883 63 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
2884 : const UsageOptions &usageOptions) const
2885 : {
2886 63 : if (m_selectedSubAlg)
2887 4 : return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
2888 :
2889 118 : std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
2890 118 : std::string osPath;
2891 83 : for (const std::string &s : m_callPath)
2892 : {
2893 24 : if (!osPath.empty())
2894 3 : osPath += ' ';
2895 24 : osPath += s;
2896 : }
2897 59 : osRet += ' ';
2898 59 : osRet += osPath;
2899 :
2900 59 : bool hasNonPositionals = false;
2901 528 : for (const auto &arg : m_args)
2902 : {
2903 469 : if (!arg->IsHiddenForCLI() && !arg->IsPositional())
2904 393 : hasNonPositionals = true;
2905 : }
2906 :
2907 59 : if (HasSubAlgorithms())
2908 : {
2909 4 : if (m_callPath.size() == 1)
2910 : {
2911 3 : osRet += " <COMMAND>";
2912 3 : if (hasNonPositionals)
2913 3 : osRet += " [OPTIONS]";
2914 3 : osRet += "\nwhere <COMMAND> is one of:\n";
2915 : }
2916 : else
2917 : {
2918 1 : osRet += " <SUBCOMMAND>";
2919 1 : if (hasNonPositionals)
2920 1 : osRet += " [OPTIONS]";
2921 1 : osRet += "\nwhere <SUBCOMMAND> is one of:\n";
2922 : }
2923 4 : size_t maxNameLen = 0;
2924 20 : for (const auto &subAlgName : GetSubAlgorithmNames())
2925 : {
2926 16 : maxNameLen = std::max(maxNameLen, subAlgName.size());
2927 : }
2928 20 : for (const auto &subAlgName : GetSubAlgorithmNames())
2929 : {
2930 32 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
2931 16 : assert(subAlg);
2932 16 : const std::string &name(subAlg->GetName());
2933 16 : osRet += " - ";
2934 16 : osRet += name;
2935 16 : osRet += ": ";
2936 16 : osRet.append(maxNameLen - name.size(), ' ');
2937 16 : osRet += subAlg->GetDescription();
2938 16 : if (!subAlg->m_aliases.empty())
2939 : {
2940 0 : bool first = true;
2941 0 : for (const auto &alias : subAlg->GetAliases())
2942 : {
2943 0 : if (alias == GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
2944 0 : break;
2945 0 : if (first)
2946 0 : osRet += " (alias: ";
2947 : else
2948 0 : osRet += ", ";
2949 0 : osRet += alias;
2950 0 : first = false;
2951 : }
2952 0 : if (!first)
2953 : {
2954 0 : osRet += ')';
2955 : }
2956 : }
2957 16 : osRet += '\n';
2958 : }
2959 :
2960 4 : if (shortUsage && hasNonPositionals)
2961 : {
2962 2 : osRet += "\nTry '";
2963 2 : osRet += osPath;
2964 2 : osRet += " --help' for help.\n";
2965 : }
2966 : }
2967 : else
2968 : {
2969 55 : if (!m_args.empty())
2970 : {
2971 55 : if (hasNonPositionals)
2972 55 : osRet += " [OPTIONS]";
2973 86 : for (const auto *arg : m_positionalArgs)
2974 : {
2975 31 : osRet += " <";
2976 31 : osRet += arg->GetMetaVar();
2977 31 : osRet += '>';
2978 : }
2979 : }
2980 :
2981 55 : const size_t nLenFirstLine = osRet.size();
2982 55 : osRet += '\n';
2983 55 : if (usageOptions.isPipelineStep)
2984 : {
2985 9 : osRet.append(nLenFirstLine, '-');
2986 9 : osRet += '\n';
2987 : }
2988 :
2989 55 : if (shortUsage)
2990 : {
2991 4 : osRet += "Try '";
2992 4 : osRet += osPath;
2993 4 : osRet += " --help' for help.\n";
2994 4 : return osRet;
2995 : }
2996 :
2997 51 : osRet += '\n';
2998 51 : osRet += m_description;
2999 51 : osRet += '\n';
3000 : }
3001 :
3002 55 : if (!m_args.empty() && !shortUsage)
3003 : {
3004 106 : std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
3005 : size_t maxOptLen;
3006 53 : std::tie(options, maxOptLen) = GetArgNamesForCLI();
3007 53 : if (usageOptions.maxOptLen)
3008 9 : maxOptLen = usageOptions.maxOptLen;
3009 :
3010 : const auto OutputArg =
3011 324 : [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
3012 2149 : const std::string &opt)
3013 : {
3014 324 : osRet += " ";
3015 324 : osRet += opt;
3016 324 : osRet += " ";
3017 324 : osRet.append(maxOptLen - opt.size(), ' ');
3018 324 : osRet += arg->GetDescription();
3019 :
3020 324 : const auto &choices = arg->GetChoices();
3021 324 : if (!choices.empty())
3022 : {
3023 5 : osRet += ". ";
3024 5 : osRet += arg->GetMetaVar();
3025 5 : osRet += '=';
3026 5 : bool firstChoice = true;
3027 27 : for (const auto &choice : choices)
3028 : {
3029 22 : if (!firstChoice)
3030 17 : osRet += '|';
3031 22 : osRet += choice;
3032 22 : firstChoice = false;
3033 : }
3034 : }
3035 :
3036 324 : if (arg->GetType() == GAAT_DATASET)
3037 : {
3038 8 : auto &val = arg->Get<GDALArgDatasetValue>();
3039 9 : if (val.GetInputFlags() == GADV_NAME &&
3040 1 : val.GetOutputFlags() == GADV_OBJECT)
3041 : {
3042 1 : osRet += " (created by algorithm)";
3043 : }
3044 : }
3045 :
3046 324 : if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
3047 : {
3048 3 : osRet += " (default: ";
3049 3 : osRet += arg->GetDefault<std::string>();
3050 3 : osRet += ')';
3051 : }
3052 321 : else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
3053 : {
3054 5 : if (arg->GetDefault<bool>())
3055 0 : osRet += " (default: true)";
3056 : }
3057 316 : else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
3058 : {
3059 1 : osRet += " (default: ";
3060 1 : osRet += CPLSPrintf("%d", arg->GetDefault<int>());
3061 1 : osRet += ')';
3062 : }
3063 315 : else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
3064 : {
3065 1 : osRet += " (default: ";
3066 1 : osRet += CPLSPrintf("%g", arg->GetDefault<double>());
3067 1 : osRet += ')';
3068 : }
3069 :
3070 324 : if (arg->GetDisplayHintAboutRepetition())
3071 : {
3072 324 : if (arg->GetMinCount() > 0 &&
3073 6 : arg->GetMinCount() == arg->GetMaxCount())
3074 : {
3075 2 : osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
3076 : }
3077 320 : else if (arg->GetMinCount() > 0 &&
3078 4 : arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
3079 : {
3080 : osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
3081 4 : arg->GetMaxCount());
3082 : }
3083 312 : else if (arg->GetMinCount() > 0)
3084 : {
3085 0 : osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
3086 : }
3087 312 : else if (arg->GetMaxCount() > 1)
3088 : {
3089 66 : osRet += " [may be repeated]";
3090 : }
3091 : }
3092 :
3093 324 : if (arg->IsRequired())
3094 : {
3095 14 : osRet += " [required]";
3096 : }
3097 :
3098 324 : osRet += '\n';
3099 :
3100 324 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3101 324 : if (!mutualExclusionGroup.empty())
3102 : {
3103 14 : std::string otherArgs;
3104 97 : for (const auto &otherArg : m_args)
3105 : {
3106 90 : if (otherArg->IsHiddenForCLI() || otherArg.get() == arg)
3107 11 : continue;
3108 79 : if (otherArg->GetMutualExclusionGroup() ==
3109 : mutualExclusionGroup)
3110 : {
3111 10 : if (!otherArgs.empty())
3112 3 : otherArgs += ", ";
3113 10 : otherArgs += "--";
3114 10 : otherArgs += otherArg->GetName();
3115 : }
3116 : }
3117 7 : if (!otherArgs.empty())
3118 : {
3119 7 : osRet += " ";
3120 7 : osRet += " ";
3121 7 : osRet.append(maxOptLen, ' ');
3122 7 : osRet += "Mutually exclusive with ";
3123 7 : osRet += otherArgs;
3124 7 : osRet += '\n';
3125 : }
3126 : }
3127 324 : };
3128 :
3129 53 : if (!m_positionalArgs.empty())
3130 : {
3131 19 : osRet += "\nPositional arguments:\n";
3132 176 : for (const auto &[arg, opt] : options)
3133 : {
3134 157 : if (arg->IsPositional())
3135 26 : OutputArg(arg, opt);
3136 : }
3137 : }
3138 :
3139 53 : if (hasNonPositionals)
3140 : {
3141 53 : bool hasCommon = false;
3142 53 : bool hasBase = false;
3143 53 : bool hasAdvanced = false;
3144 53 : bool hasEsoteric = false;
3145 106 : std::vector<std::string> categories;
3146 422 : for (const auto &iter : options)
3147 : {
3148 369 : const auto &arg = iter.first;
3149 369 : if (!arg->IsPositional())
3150 : {
3151 343 : const auto &category = arg->GetCategory();
3152 343 : if (category == GAAC_COMMON)
3153 : {
3154 271 : hasCommon = true;
3155 : }
3156 72 : else if (category == GAAC_BASE)
3157 : {
3158 54 : hasBase = true;
3159 : }
3160 18 : else if (category == GAAC_ADVANCED)
3161 : {
3162 14 : hasAdvanced = true;
3163 : }
3164 4 : else if (category == GAAC_ESOTERIC)
3165 : {
3166 3 : hasEsoteric = true;
3167 : }
3168 1 : else if (std::find(categories.begin(), categories.end(),
3169 1 : category) == categories.end())
3170 : {
3171 1 : categories.push_back(category);
3172 : }
3173 : }
3174 : }
3175 53 : if (hasAdvanced)
3176 4 : categories.insert(categories.begin(), GAAC_ADVANCED);
3177 53 : if (hasBase)
3178 28 : categories.insert(categories.begin(), GAAC_BASE);
3179 53 : if (hasCommon && !usageOptions.isPipelineStep)
3180 44 : categories.insert(categories.begin(), GAAC_COMMON);
3181 53 : if (hasEsoteric)
3182 1 : categories.push_back(GAAC_ESOTERIC);
3183 :
3184 131 : for (const auto &category : categories)
3185 : {
3186 78 : osRet += "\n";
3187 78 : if (category != GAAC_BASE)
3188 : {
3189 50 : osRet += category;
3190 50 : osRet += ' ';
3191 : }
3192 78 : osRet += "Options:\n";
3193 654 : for (const auto &[arg, opt] : options)
3194 : {
3195 576 : if (!arg->IsPositional() && arg->GetCategory() == category)
3196 298 : OutputArg(arg, opt);
3197 : }
3198 : }
3199 : }
3200 : }
3201 :
3202 55 : if (!m_longDescription.empty())
3203 : {
3204 5 : osRet += '\n';
3205 5 : osRet += m_longDescription;
3206 5 : osRet += '\n';
3207 : }
3208 :
3209 55 : if (!m_helpURL.empty())
3210 : {
3211 55 : osRet += "\nFor more details, consult ";
3212 55 : osRet += GetHelpFullURL();
3213 55 : osRet += '\n';
3214 : }
3215 :
3216 55 : if (!m_callPath.empty() && m_callPath[0] == "gdal")
3217 : {
3218 : osRet += "\nWARNING: the gdal command is provisionally provided as an "
3219 : "alternative interface to GDAL and OGR command line "
3220 : "utilities.\nThe project reserves the right to modify, "
3221 : "rename, reorganize, and change the behavior of the utility\n"
3222 : "until it is officially frozen in a future feature release of "
3223 0 : "GDAL.\n";
3224 : }
3225 :
3226 55 : return osRet;
3227 : }
3228 :
3229 : /************************************************************************/
3230 : /* GDALAlgorithm::GetUsageAsJSON() */
3231 : /************************************************************************/
3232 :
3233 72 : std::string GDALAlgorithm::GetUsageAsJSON() const
3234 : {
3235 144 : CPLJSONDocument oDoc;
3236 144 : auto oRoot = oDoc.GetRoot();
3237 :
3238 72 : if (m_displayInJSONUsage)
3239 : {
3240 70 : oRoot.Add("name", m_name);
3241 70 : CPLJSONArray jFullPath;
3242 142 : for (const std::string &s : m_callPath)
3243 : {
3244 72 : jFullPath.Add(s);
3245 : }
3246 70 : oRoot.Add("full_path", jFullPath);
3247 : }
3248 :
3249 72 : oRoot.Add("description", m_description);
3250 72 : if (!m_helpURL.empty())
3251 : {
3252 72 : oRoot.Add("short_url", m_helpURL);
3253 72 : oRoot.Add("url", GetHelpFullURL());
3254 : }
3255 :
3256 144 : CPLJSONArray jSubAlgorithms;
3257 102 : for (const auto &subAlgName : GetSubAlgorithmNames())
3258 : {
3259 60 : auto subAlg = InstantiateSubAlgorithm(subAlgName);
3260 30 : assert(subAlg);
3261 30 : if (subAlg->m_displayInJSONUsage)
3262 : {
3263 27 : CPLJSONDocument oSubDoc;
3264 27 : CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
3265 27 : jSubAlgorithms.Add(oSubDoc.GetRoot());
3266 : }
3267 : }
3268 72 : oRoot.Add("sub_algorithms", jSubAlgorithms);
3269 :
3270 433 : const auto ProcessArg = [](const GDALAlgorithmArg *arg)
3271 : {
3272 433 : CPLJSONObject jArg;
3273 433 : jArg.Add("name", arg->GetName());
3274 433 : jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
3275 433 : jArg.Add("description", arg->GetDescription());
3276 433 : const auto &choices = arg->GetChoices();
3277 433 : if (!choices.empty())
3278 : {
3279 11 : CPLJSONArray jChoices;
3280 121 : for (const auto &choice : choices)
3281 110 : jChoices.Add(choice);
3282 11 : jArg.Add("choices", jChoices);
3283 : }
3284 433 : if (arg->HasDefaultValue())
3285 : {
3286 63 : switch (arg->GetType())
3287 : {
3288 57 : case GAAT_BOOLEAN:
3289 57 : jArg.Add("default", arg->GetDefault<bool>());
3290 57 : break;
3291 4 : case GAAT_STRING:
3292 4 : jArg.Add("default", arg->GetDefault<std::string>());
3293 4 : break;
3294 1 : case GAAT_INTEGER:
3295 1 : jArg.Add("default", arg->GetDefault<int>());
3296 1 : break;
3297 1 : case GAAT_REAL:
3298 1 : jArg.Add("default", arg->GetDefault<double>());
3299 1 : break;
3300 0 : case GAAT_DATASET:
3301 : case GAAT_STRING_LIST:
3302 : case GAAT_INTEGER_LIST:
3303 : case GAAT_REAL_LIST:
3304 : case GAAT_DATASET_LIST:
3305 0 : CPLError(CE_Warning, CPLE_AppDefined,
3306 : "Unhandled default value for arg %s",
3307 0 : arg->GetName().c_str());
3308 0 : break;
3309 : }
3310 : }
3311 433 : jArg.Add("required", arg->IsRequired());
3312 433 : if (GDALAlgorithmArgTypeIsList(arg->GetType()))
3313 : {
3314 158 : jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
3315 158 : jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
3316 158 : jArg.Add("min_count", arg->GetMinCount());
3317 158 : jArg.Add("max_count", arg->GetMaxCount());
3318 : }
3319 433 : jArg.Add("category", arg->GetCategory());
3320 :
3321 433 : if (arg->GetType() == GAAT_DATASET)
3322 : {
3323 67 : const auto &val = arg->Get<GDALArgDatasetValue>();
3324 : {
3325 67 : CPLJSONArray jAr;
3326 67 : if (val.GetType() & GDAL_OF_RASTER)
3327 48 : jAr.Add("raster");
3328 67 : if (val.GetType() & GDAL_OF_VECTOR)
3329 21 : jAr.Add("vector");
3330 67 : if (val.GetType() & GDAL_OF_MULTIDIM_RASTER)
3331 2 : jAr.Add("muldim_raster");
3332 67 : jArg.Add("dataset_type", jAr);
3333 : }
3334 :
3335 93 : const auto GetFlags = [](int flags)
3336 : {
3337 93 : CPLJSONArray jAr;
3338 93 : if (flags & GADV_NAME)
3339 67 : jAr.Add("name");
3340 93 : if (flags & GADV_OBJECT)
3341 88 : jAr.Add("dataset");
3342 93 : return jAr;
3343 : };
3344 :
3345 67 : if (arg->IsInput())
3346 : {
3347 67 : jArg.Add("input_flags", GetFlags(val.GetInputFlags()));
3348 : }
3349 67 : if (arg->IsOutput())
3350 : {
3351 26 : jArg.Add("output_flags", GetFlags(val.GetOutputFlags()));
3352 : }
3353 : }
3354 :
3355 433 : const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
3356 433 : if (!mutualExclusionGroup.empty())
3357 : {
3358 24 : jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
3359 : }
3360 :
3361 866 : const auto &metadata = arg->GetMetadata();
3362 433 : if (!metadata.empty())
3363 : {
3364 51 : CPLJSONObject jMetadata;
3365 102 : for (const auto &[key, values] : metadata)
3366 : {
3367 102 : CPLJSONArray jValue;
3368 126 : for (const auto &value : values)
3369 75 : jValue.Add(value);
3370 51 : jMetadata.Add(key, jValue);
3371 : }
3372 51 : jArg.Add("metadata", jMetadata);
3373 : }
3374 :
3375 866 : return jArg;
3376 : };
3377 :
3378 : {
3379 72 : CPLJSONArray jArgs;
3380 888 : for (const auto &arg : m_args)
3381 : {
3382 816 : if (!arg->IsOnlyForCLI() && arg->IsInput() && !arg->IsOutput())
3383 404 : jArgs.Add(ProcessArg(arg.get()));
3384 : }
3385 72 : oRoot.Add("input_arguments", jArgs);
3386 : }
3387 :
3388 : {
3389 72 : CPLJSONArray jArgs;
3390 888 : for (const auto &arg : m_args)
3391 : {
3392 816 : if (!arg->IsOnlyForCLI() && !arg->IsInput() && arg->IsOutput())
3393 3 : jArgs.Add(ProcessArg(arg.get()));
3394 : }
3395 72 : oRoot.Add("output_arguments", jArgs);
3396 : }
3397 :
3398 : {
3399 72 : CPLJSONArray jArgs;
3400 888 : for (const auto &arg : m_args)
3401 : {
3402 816 : if (!arg->IsOnlyForCLI() && arg->IsInput() && arg->IsOutput())
3403 26 : jArgs.Add(ProcessArg(arg.get()));
3404 : }
3405 72 : oRoot.Add("input_output_arguments", jArgs);
3406 : }
3407 :
3408 144 : return oDoc.SaveAsString();
3409 : }
3410 :
3411 : /************************************************************************/
3412 : /* GDALAlgorithm::GetAutoComplete() */
3413 : /************************************************************************/
3414 :
3415 : std::vector<std::string>
3416 43 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
3417 : bool showAllOptions)
3418 : {
3419 43 : std::vector<std::string> ret;
3420 :
3421 86 : std::string option;
3422 86 : std::string value;
3423 43 : ExtractLastOptionAndValue(args, option, value);
3424 :
3425 51 : if (option.empty() && !args.empty() && !args.back().empty() &&
3426 8 : args.back()[0] == '-')
3427 : {
3428 : // List available options
3429 88 : for (const auto &arg : GetArgs())
3430 : {
3431 160 : if (arg->IsHiddenForCLI() ||
3432 153 : (!showAllOptions &&
3433 204 : (arg->GetName() == "help" || arg->GetName() == "drivers" ||
3434 168 : arg->GetName() == "config" || arg->GetName() == "version" ||
3435 50 : arg->GetName() == "json-usage")))
3436 : {
3437 32 : continue;
3438 : }
3439 49 : if (!arg->GetShortName().empty())
3440 : {
3441 14 : ret.push_back(std::string("-").append(arg->GetShortName()));
3442 : }
3443 66 : for (const std::string &alias : arg->GetAliases())
3444 : {
3445 17 : ret.push_back(std::string("--").append(alias));
3446 : }
3447 49 : if (!arg->GetName().empty())
3448 : {
3449 49 : ret.push_back(std::string("--").append(arg->GetName()));
3450 : }
3451 : }
3452 : }
3453 36 : else if (!option.empty())
3454 : {
3455 : // List possible choices for current option
3456 33 : auto arg = GetArg(option);
3457 33 : if (arg && arg->GetType() != GAAT_BOOLEAN)
3458 : {
3459 33 : ret = arg->GetChoices();
3460 33 : if (ret.empty())
3461 : {
3462 : {
3463 29 : CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
3464 29 : SetParseForAutoCompletion();
3465 29 : CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
3466 : }
3467 29 : ret = arg->GetAutoCompleteChoices(value);
3468 : }
3469 33 : if (ret.empty())
3470 : {
3471 1 : ret.push_back("**");
3472 1 : ret.push_back(
3473 1 : std::string("description: ").append(arg->GetDescription()));
3474 : }
3475 : }
3476 : }
3477 3 : else if (!args.empty() && STARTS_WITH(args.back().c_str(), "/vsi"))
3478 : {
3479 1 : auto arg = GetArg(GDAL_ARG_NAME_INPUT);
3480 1 : if (arg)
3481 : {
3482 1 : ret = arg->GetAutoCompleteChoices(args.back());
3483 : }
3484 : }
3485 : else
3486 : {
3487 : // List possible sub-algorithms
3488 2 : ret = GetSubAlgorithmNames();
3489 : }
3490 :
3491 86 : return ret;
3492 : }
3493 :
3494 : /************************************************************************/
3495 : /* GDALAlgorithm::ExtractLastOptionAndValue() */
3496 : /************************************************************************/
3497 :
3498 43 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
3499 : std::string &option,
3500 : std::string &value) const
3501 : {
3502 43 : if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
3503 : {
3504 28 : const auto nPosEqual = args.back().find('=');
3505 28 : if (nPosEqual == std::string::npos)
3506 : {
3507 : // Deal with "gdal ... --option"
3508 17 : if (GetArg(args.back()))
3509 : {
3510 10 : option = args.back();
3511 10 : args.pop_back();
3512 : }
3513 : }
3514 : else
3515 : {
3516 : // Deal with "gdal ... --option=<value>"
3517 11 : if (GetArg(args.back().substr(0, nPosEqual)))
3518 : {
3519 11 : option = args.back().substr(0, nPosEqual);
3520 11 : value = args.back().substr(nPosEqual + 1);
3521 11 : args.pop_back();
3522 : }
3523 : }
3524 : }
3525 27 : else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
3526 12 : args[args.size() - 2][0] == '-')
3527 : {
3528 : // Deal with "gdal ... --option <value>"
3529 12 : auto arg = GetArg(args[args.size() - 2]);
3530 12 : if (arg && arg->GetType() != GAAT_BOOLEAN)
3531 : {
3532 12 : option = args[args.size() - 2];
3533 12 : value = args.back();
3534 12 : args.pop_back();
3535 : }
3536 : }
3537 :
3538 43 : const auto IsKeyValueOption = [](const std::string &osStr)
3539 : {
3540 100 : return osStr == "--co" || osStr == "--creation-option" ||
3541 81 : osStr == "--lco" || osStr == "--layer-creation-option" ||
3542 98 : osStr == "--oo" || osStr == "--open-option";
3543 : };
3544 :
3545 43 : if (IsKeyValueOption(option))
3546 : {
3547 19 : const auto nPosEqual = value.find('=');
3548 19 : if (nPosEqual != std::string::npos)
3549 : {
3550 10 : value.resize(nPosEqual);
3551 : }
3552 : }
3553 43 : }
3554 :
3555 : /************************************************************************/
3556 : /* GDALAlgorithmRelease() */
3557 : /************************************************************************/
3558 :
3559 : /** Release a handle to an algorithm.
3560 : *
3561 : * @since 3.11
3562 : */
3563 367 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
3564 : {
3565 367 : delete hAlg;
3566 367 : }
3567 :
3568 : /************************************************************************/
3569 : /* GDALAlgorithmGetName() */
3570 : /************************************************************************/
3571 :
3572 : /** Return the algorithm name.
3573 : *
3574 : * @param hAlg Handle to an algorithm. Must NOT be null.
3575 : * @return algorithm name whose lifetime is bound to hAlg and which must not
3576 : * be freed.
3577 : * @since 3.11
3578 : */
3579 2 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
3580 : {
3581 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3582 2 : return hAlg->ptr->GetName().c_str();
3583 : }
3584 :
3585 : /************************************************************************/
3586 : /* GDALAlgorithmGetDescription() */
3587 : /************************************************************************/
3588 :
3589 : /** Return the algorithm (short) description.
3590 : *
3591 : * @param hAlg Handle to an algorithm. Must NOT be null.
3592 : * @return algorithm description whose lifetime is bound to hAlg and which must
3593 : * not be freed.
3594 : * @since 3.11
3595 : */
3596 2 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
3597 : {
3598 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3599 2 : return hAlg->ptr->GetDescription().c_str();
3600 : }
3601 :
3602 : /************************************************************************/
3603 : /* GDALAlgorithmGetLongDescription() */
3604 : /************************************************************************/
3605 :
3606 : /** Return the algorithm (longer) description.
3607 : *
3608 : * @param hAlg Handle to an algorithm. Must NOT be null.
3609 : * @return algorithm description whose lifetime is bound to hAlg and which must
3610 : * not be freed.
3611 : * @since 3.11
3612 : */
3613 2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
3614 : {
3615 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3616 2 : return hAlg->ptr->GetLongDescription().c_str();
3617 : }
3618 :
3619 : /************************************************************************/
3620 : /* GDALAlgorithmGetHelpFullURL() */
3621 : /************************************************************************/
3622 :
3623 : /** Return the algorithm full URL.
3624 : *
3625 : * @param hAlg Handle to an algorithm. Must NOT be null.
3626 : * @return algorithm URL whose lifetime is bound to hAlg and which must
3627 : * not be freed.
3628 : * @since 3.11
3629 : */
3630 2 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
3631 : {
3632 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3633 2 : return hAlg->ptr->GetHelpFullURL().c_str();
3634 : }
3635 :
3636 : /************************************************************************/
3637 : /* GDALAlgorithmHasSubAlgorithms() */
3638 : /************************************************************************/
3639 :
3640 : /** Return whether the algorithm has sub-algorithms.
3641 : *
3642 : * @param hAlg Handle to an algorithm. Must NOT be null.
3643 : * @since 3.11
3644 : */
3645 2 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
3646 : {
3647 2 : VALIDATE_POINTER1(hAlg, __func__, false);
3648 2 : return hAlg->ptr->HasSubAlgorithms();
3649 : }
3650 :
3651 : /************************************************************************/
3652 : /* GDALAlgorithmGetSubAlgorithmNames() */
3653 : /************************************************************************/
3654 :
3655 : /** Get the names of registered algorithms.
3656 : *
3657 : * @param hAlg Handle to an algorithm. Must NOT be null.
3658 : * @return a NULL terminated list of names, which must be destroyed with
3659 : * CSLDestroy()
3660 : * @since 3.11
3661 : */
3662 2 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
3663 : {
3664 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3665 2 : return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
3666 : }
3667 :
3668 : /************************************************************************/
3669 : /* GDALAlgorithmInstantiateSubAlgorithm() */
3670 : /************************************************************************/
3671 :
3672 : /** Instantiate an algorithm by its name (or its alias).
3673 : *
3674 : * @param hAlg Handle to an algorithm. Must NOT be null.
3675 : * @param pszSubAlgName Algorithm name. Must NOT be null.
3676 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
3677 : * or NULL if the algorithm does not exist or another error occurred.
3678 : * @since 3.11
3679 : */
3680 176 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
3681 : const char *pszSubAlgName)
3682 : {
3683 176 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3684 176 : VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
3685 352 : auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
3686 : return subAlg
3687 352 : ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
3688 352 : : nullptr;
3689 : }
3690 :
3691 : /************************************************************************/
3692 : /* GDALAlgorithmParseCommandLineArguments() */
3693 : /************************************************************************/
3694 :
3695 : /** Parse a command line argument, which does not include the algorithm
3696 : * name, to set the value of corresponding arguments.
3697 : *
3698 : * @param hAlg Handle to an algorithm. Must NOT be null.
3699 : * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
3700 : * @return true if successful, false otherwise
3701 : * @since 3.11
3702 : */
3703 :
3704 140 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
3705 : CSLConstList papszArgs)
3706 : {
3707 140 : VALIDATE_POINTER1(hAlg, __func__, false);
3708 140 : return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
3709 : }
3710 :
3711 : /************************************************************************/
3712 : /* GDALAlgorithmGetActualAlgorithm() */
3713 : /************************************************************************/
3714 :
3715 : /** Return the actual algorithm that is going to be invoked, when the
3716 : * current algorithm has sub-algorithms.
3717 : *
3718 : * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
3719 : *
3720 : * Note that the lifetime of the returned algorithm does not exceed the one of
3721 : * the hAlg instance that owns it.
3722 : *
3723 : * @param hAlg Handle to an algorithm. Must NOT be null.
3724 : * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
3725 : * @since 3.11
3726 : */
3727 8 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
3728 : {
3729 8 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3730 8 : return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
3731 : }
3732 :
3733 : /************************************************************************/
3734 : /* GDALAlgorithmRun() */
3735 : /************************************************************************/
3736 :
3737 : /** Execute the algorithm, starting with ValidateArguments() and then
3738 : * calling RunImpl().
3739 : *
3740 : * @param hAlg Handle to an algorithm. Must NOT be null.
3741 : * @param pfnProgress Progress callback. May be null.
3742 : * @param pProgressData Progress callback user data. May be null.
3743 : * @return true if successful, false otherwise
3744 : * @since 3.11
3745 : */
3746 :
3747 151 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
3748 : void *pProgressData)
3749 : {
3750 151 : VALIDATE_POINTER1(hAlg, __func__, false);
3751 151 : return hAlg->ptr->Run(pfnProgress, pProgressData);
3752 : }
3753 :
3754 : /************************************************************************/
3755 : /* GDALAlgorithmFinalize() */
3756 : /************************************************************************/
3757 :
3758 : /** Complete any pending actions, and return the final status.
3759 : * This is typically useful for algorithm that generate an output dataset.
3760 : *
3761 : * Note that this function does *NOT* release memory associated with the
3762 : * algorithm. GDALAlgorithmRelease() must still be called afterwards.
3763 : *
3764 : * @param hAlg Handle to an algorithm. Must NOT be null.
3765 : * @return true if successful, false otherwise
3766 : * @since 3.11
3767 : */
3768 :
3769 95 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
3770 : {
3771 95 : VALIDATE_POINTER1(hAlg, __func__, false);
3772 95 : return hAlg->ptr->Finalize();
3773 : }
3774 :
3775 : /************************************************************************/
3776 : /* GDALAlgorithmGetUsageAsJSON() */
3777 : /************************************************************************/
3778 :
3779 : /** Return the usage of the algorithm as a JSON-serialized string.
3780 : *
3781 : * This can be used to dynamically generate interfaces to algorithms.
3782 : *
3783 : * @param hAlg Handle to an algorithm. Must NOT be null.
3784 : * @return a string that must be freed with CPLFree()
3785 : * @since 3.11
3786 : */
3787 4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
3788 : {
3789 4 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3790 4 : return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
3791 : }
3792 :
3793 : /************************************************************************/
3794 : /* GDALAlgorithmGetArgNames() */
3795 : /************************************************************************/
3796 :
3797 : /** Return the list of available argument names.
3798 : *
3799 : * @param hAlg Handle to an algorithm. Must NOT be null.
3800 : * @return a NULL terminated list of names, which must be destroyed with
3801 : * CSLDestroy()
3802 : * @since 3.11
3803 : */
3804 2 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
3805 : {
3806 2 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3807 4 : CPLStringList list;
3808 29 : for (const auto &arg : hAlg->ptr->GetArgs())
3809 27 : list.AddString(arg->GetName().c_str());
3810 2 : return list.StealList();
3811 : }
3812 :
3813 : /************************************************************************/
3814 : /* GDALAlgorithmGetArg() */
3815 : /************************************************************************/
3816 :
3817 : /** Return an argument from its name.
3818 : *
3819 : * The lifetime of the returned object does not exceed the one of hAlg.
3820 : *
3821 : * @param hAlg Handle to an algorithm. Must NOT be null.
3822 : * @param pszArgName Argument name. Must NOT be null.
3823 : * @return an argument that must be released with GDALAlgorithmArgRelease(),
3824 : * or nullptr in case of error
3825 : * @since 3.11
3826 : */
3827 154 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
3828 : const char *pszArgName)
3829 : {
3830 154 : VALIDATE_POINTER1(hAlg, __func__, nullptr);
3831 154 : VALIDATE_POINTER1(pszArgName, __func__, nullptr);
3832 154 : auto arg = hAlg->ptr->GetArg(pszArgName);
3833 154 : if (!arg)
3834 2 : return nullptr;
3835 152 : return std::make_unique<GDALAlgorithmArgHS>(arg).release();
3836 : }
3837 :
3838 : /************************************************************************/
3839 : /* GDALAlgorithmArgRelease() */
3840 : /************************************************************************/
3841 :
3842 : /** Release a handle to an argument.
3843 : *
3844 : * @since 3.11
3845 : */
3846 152 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
3847 : {
3848 152 : delete hArg;
3849 152 : }
3850 :
3851 : /************************************************************************/
3852 : /* GDALAlgorithmArgGetName() */
3853 : /************************************************************************/
3854 :
3855 : /** Return the name of an argument.
3856 : *
3857 : * @param hArg Handle to an argument. Must NOT be null.
3858 : * @return argument name whose lifetime is bound to hArg and which must not
3859 : * be freed.
3860 : * @since 3.11
3861 : */
3862 1 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
3863 : {
3864 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3865 1 : return hArg->ptr->GetName().c_str();
3866 : }
3867 :
3868 : /************************************************************************/
3869 : /* GDALAlgorithmArgGetType() */
3870 : /************************************************************************/
3871 :
3872 : /** Get the type of an argument
3873 : *
3874 : * @param hArg Handle to an argument. Must NOT be null.
3875 : * @since 3.11
3876 : */
3877 136 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
3878 : {
3879 136 : VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
3880 136 : return hArg->ptr->GetType();
3881 : }
3882 :
3883 : /************************************************************************/
3884 : /* GDALAlgorithmArgGetDescription() */
3885 : /************************************************************************/
3886 :
3887 : /** Return the description of an argument.
3888 : *
3889 : * @param hArg Handle to an argument. Must NOT be null.
3890 : * @return argument descriptioin whose lifetime is bound to hArg and which must not
3891 : * be freed.
3892 : * @since 3.11
3893 : */
3894 1 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
3895 : {
3896 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3897 1 : return hArg->ptr->GetDescription().c_str();
3898 : }
3899 :
3900 : /************************************************************************/
3901 : /* GDALAlgorithmArgGetShortName() */
3902 : /************************************************************************/
3903 :
3904 : /** Return the short name, or empty string if there is none
3905 : *
3906 : * @param hArg Handle to an argument. Must NOT be null.
3907 : * @return short name whose lifetime is bound to hArg and which must not
3908 : * be freed.
3909 : * @since 3.11
3910 : */
3911 1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
3912 : {
3913 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3914 1 : return hArg->ptr->GetShortName().c_str();
3915 : }
3916 :
3917 : /************************************************************************/
3918 : /* GDALAlgorithmArgGetAliases() */
3919 : /************************************************************************/
3920 :
3921 : /** Return the aliases (potentially none)
3922 : *
3923 : * @param hArg Handle to an argument. Must NOT be null.
3924 : * @return a NULL terminated list of names, which must be destroyed with
3925 : * CSLDestroy()
3926 :
3927 : * @since 3.11
3928 : */
3929 1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
3930 : {
3931 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3932 1 : return CPLStringList(hArg->ptr->GetAliases()).StealList();
3933 : }
3934 :
3935 : /************************************************************************/
3936 : /* GDALAlgorithmArgGetMetaVar() */
3937 : /************************************************************************/
3938 :
3939 : /** Return the "meta-var" hint.
3940 : *
3941 : * By default, the meta-var value is the long name of the argument in
3942 : * upper case.
3943 : *
3944 : * @param hArg Handle to an argument. Must NOT be null.
3945 : * @return meta-var hint whose lifetime is bound to hArg and which must not
3946 : * be freed.
3947 : * @since 3.11
3948 : */
3949 1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
3950 : {
3951 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3952 1 : return hArg->ptr->GetMetaVar().c_str();
3953 : }
3954 :
3955 : /************************************************************************/
3956 : /* GDALAlgorithmArgGetCategory() */
3957 : /************************************************************************/
3958 :
3959 : /** Return the argument category
3960 : *
3961 : * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
3962 : *
3963 : * @param hArg Handle to an argument. Must NOT be null.
3964 : * @return category whose lifetime is bound to hArg and which must not
3965 : * be freed.
3966 : * @since 3.11
3967 : */
3968 1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
3969 : {
3970 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
3971 1 : return hArg->ptr->GetCategory().c_str();
3972 : }
3973 :
3974 : /************************************************************************/
3975 : /* GDALAlgorithmArgIsPositional() */
3976 : /************************************************************************/
3977 :
3978 : /** Return if the argument is a positional one.
3979 : *
3980 : * @param hArg Handle to an argument. Must NOT be null.
3981 : * @since 3.11
3982 : */
3983 1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
3984 : {
3985 1 : VALIDATE_POINTER1(hArg, __func__, false);
3986 1 : return hArg->ptr->IsPositional();
3987 : }
3988 :
3989 : /************************************************************************/
3990 : /* GDALAlgorithmArgIsRequired() */
3991 : /************************************************************************/
3992 :
3993 : /** Return whether the argument is required. Defaults to false.
3994 : *
3995 : * @param hArg Handle to an argument. Must NOT be null.
3996 : * @since 3.11
3997 : */
3998 1 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
3999 : {
4000 1 : VALIDATE_POINTER1(hArg, __func__, false);
4001 1 : return hArg->ptr->IsRequired();
4002 : }
4003 :
4004 : /************************************************************************/
4005 : /* GDALAlgorithmArgGetMinCount() */
4006 : /************************************************************************/
4007 :
4008 : /** Return the minimum number of values for the argument.
4009 : *
4010 : * Defaults to 0.
4011 : * Only applies to list type of arguments.
4012 : *
4013 : * @param hArg Handle to an argument. Must NOT be null.
4014 : * @since 3.11
4015 : */
4016 1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
4017 : {
4018 1 : VALIDATE_POINTER1(hArg, __func__, 0);
4019 1 : return hArg->ptr->GetMinCount();
4020 : }
4021 :
4022 : /************************************************************************/
4023 : /* GDALAlgorithmArgGetMaxCount() */
4024 : /************************************************************************/
4025 :
4026 : /** Return the maximum number of values for the argument.
4027 : *
4028 : * Defaults to 1 for scalar types, and INT_MAX for list types.
4029 : * Only applies to list type of arguments.
4030 : *
4031 : * @param hArg Handle to an argument. Must NOT be null.
4032 : * @since 3.11
4033 : */
4034 1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
4035 : {
4036 1 : VALIDATE_POINTER1(hArg, __func__, 0);
4037 1 : return hArg->ptr->GetMaxCount();
4038 : }
4039 :
4040 : /************************************************************************/
4041 : /* GDALAlgorithmArgGetPackedValuesAllowed() */
4042 : /************************************************************************/
4043 :
4044 : /** Return whether, for list type of arguments, several values, space
4045 : * separated, may be specified. That is "--foo=bar,baz".
4046 : * The default is true.
4047 : *
4048 : * @param hArg Handle to an argument. Must NOT be null.
4049 : * @since 3.11
4050 : */
4051 1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
4052 : {
4053 1 : VALIDATE_POINTER1(hArg, __func__, false);
4054 1 : return hArg->ptr->GetPackedValuesAllowed();
4055 : }
4056 :
4057 : /************************************************************************/
4058 : /* GDALAlgorithmArgGetRepeatedArgAllowed() */
4059 : /************************************************************************/
4060 :
4061 : /** Return whether, for list type of arguments, the argument may be
4062 : * repeated. That is "--foo=bar --foo=baz".
4063 : * The default is true.
4064 : *
4065 : * @param hArg Handle to an argument. Must NOT be null.
4066 : * @since 3.11
4067 : */
4068 1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
4069 : {
4070 1 : VALIDATE_POINTER1(hArg, __func__, false);
4071 1 : return hArg->ptr->GetRepeatedArgAllowed();
4072 : }
4073 :
4074 : /************************************************************************/
4075 : /* GDALAlgorithmArgGetChoices() */
4076 : /************************************************************************/
4077 :
4078 : /** Return the allowed values (as strings) for the argument.
4079 : *
4080 : * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
4081 : *
4082 : * @param hArg Handle to an argument. Must NOT be null.
4083 : * @return a NULL terminated list of names, which must be destroyed with
4084 : * CSLDestroy()
4085 :
4086 : * @since 3.11
4087 : */
4088 1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
4089 : {
4090 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4091 1 : return CPLStringList(hArg->ptr->GetChoices()).StealList();
4092 : }
4093 :
4094 : /************************************************************************/
4095 : /* GDALAlgorithmArgIsExplicitlySet() */
4096 : /************************************************************************/
4097 :
4098 : /** Return whether the argument value has been explicitly set with Set()
4099 : *
4100 : * @param hArg Handle to an argument. Must NOT be null.
4101 : * @since 3.11
4102 : */
4103 1 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
4104 : {
4105 1 : VALIDATE_POINTER1(hArg, __func__, false);
4106 1 : return hArg->ptr->IsExplicitlySet();
4107 : }
4108 :
4109 : /************************************************************************/
4110 : /* GDALAlgorithmArgHasDefaultValue() */
4111 : /************************************************************************/
4112 :
4113 : /** Return if the argument has a declared default value.
4114 : *
4115 : * @param hArg Handle to an argument. Must NOT be null.
4116 : * @since 3.11
4117 : */
4118 1 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
4119 : {
4120 1 : VALIDATE_POINTER1(hArg, __func__, false);
4121 1 : return hArg->ptr->HasDefaultValue();
4122 : }
4123 :
4124 : /************************************************************************/
4125 : /* GDALAlgorithmArgIsHiddenForCLI() */
4126 : /************************************************************************/
4127 :
4128 : /** Return whether the argument must not be mentioned in CLI usage.
4129 : *
4130 : * For example, "output-value" for "gdal raster info", which is only
4131 : * meant when the algorithm is used from a non-CLI context.
4132 : *
4133 : * @param hArg Handle to an argument. Must NOT be null.
4134 : * @since 3.11
4135 : */
4136 1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
4137 : {
4138 1 : VALIDATE_POINTER1(hArg, __func__, false);
4139 1 : return hArg->ptr->IsHiddenForCLI();
4140 : }
4141 :
4142 : /************************************************************************/
4143 : /* GDALAlgorithmArgIsOnlyForCLI() */
4144 : /************************************************************************/
4145 :
4146 : /** Return whether the argument is only for CLI usage.
4147 : *
4148 : * For example "--help"
4149 : *
4150 : * @param hArg Handle to an argument. Must NOT be null.
4151 : * @since 3.11
4152 : */
4153 1 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
4154 : {
4155 1 : VALIDATE_POINTER1(hArg, __func__, false);
4156 1 : return hArg->ptr->IsOnlyForCLI();
4157 : }
4158 :
4159 : /************************************************************************/
4160 : /* GDALAlgorithmArgIsInput() */
4161 : /************************************************************************/
4162 :
4163 : /** Indicate whether the value of the argument is read-only during the
4164 : * execution of the algorithm.
4165 : *
4166 : * Default is true.
4167 : *
4168 : * @param hArg Handle to an argument. Must NOT be null.
4169 : * @since 3.11
4170 : */
4171 1 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
4172 : {
4173 1 : VALIDATE_POINTER1(hArg, __func__, false);
4174 1 : return hArg->ptr->IsInput();
4175 : }
4176 :
4177 : /************************************************************************/
4178 : /* GDALAlgorithmArgIsOutput() */
4179 : /************************************************************************/
4180 :
4181 : /** Return whether (at least part of) the value of the argument is set
4182 : * during the execution of the algorithm.
4183 : *
4184 : * For example, "output-value" for "gdal raster info"
4185 : * Default is false.
4186 : * An argument may return both IsInput() and IsOutput() as true.
4187 : * For example the "gdal raster convert" algorithm consumes the dataset
4188 : * name of its "output" argument, and sets the dataset object during its
4189 : * execution.
4190 : *
4191 : * @param hArg Handle to an argument. Must NOT be null.
4192 : * @since 3.11
4193 : */
4194 1 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
4195 : {
4196 1 : VALIDATE_POINTER1(hArg, __func__, false);
4197 1 : return hArg->ptr->IsOutput();
4198 : }
4199 :
4200 : /************************************************************************/
4201 : /* GDALAlgorithmArgGetMutualExclusionGroup() */
4202 : /************************************************************************/
4203 :
4204 : /** Return the name of the mutual exclusion group to which this argument
4205 : * belongs to.
4206 : *
4207 : * Or empty string if it does not belong to any exclusion group.
4208 : *
4209 : * @param hArg Handle to an argument. Must NOT be null.
4210 : * @return string whose lifetime is bound to hArg and which must not
4211 : * be freed.
4212 : * @since 3.11
4213 : */
4214 1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
4215 : {
4216 1 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4217 1 : return hArg->ptr->GetMutualExclusionGroup().c_str();
4218 : }
4219 :
4220 : /************************************************************************/
4221 : /* GDALAlgorithmArgGetAsBoolean() */
4222 : /************************************************************************/
4223 :
4224 : /** Return the argument value as a boolean.
4225 : *
4226 : * Must only be called on arguments whose type is GAAT_BOOLEAN.
4227 : *
4228 : * @param hArg Handle to an argument. Must NOT be null.
4229 : * @since 3.11
4230 : */
4231 4 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
4232 : {
4233 4 : VALIDATE_POINTER1(hArg, __func__, false);
4234 4 : if (hArg->ptr->GetType() != GAAT_BOOLEAN)
4235 : {
4236 1 : CPLError(CE_Failure, CPLE_AppDefined,
4237 : "%s must only be called on arguments of type GAAT_BOOLEAN",
4238 : __func__);
4239 1 : return false;
4240 : }
4241 3 : return hArg->ptr->Get<bool>();
4242 : }
4243 :
4244 : /************************************************************************/
4245 : /* GDALAlgorithmArgGetAsBoolean() */
4246 : /************************************************************************/
4247 :
4248 : /** Return the argument value as a string.
4249 : *
4250 : * Must only be called on arguments whose type is GAAT_STRING.
4251 : *
4252 : * @param hArg Handle to an argument. Must NOT be null.
4253 : * @return string whose lifetime is bound to hArg and which must not
4254 : * be freed.
4255 : * @since 3.11
4256 : */
4257 26 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
4258 : {
4259 26 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4260 26 : if (hArg->ptr->GetType() != GAAT_STRING)
4261 : {
4262 1 : CPLError(CE_Failure, CPLE_AppDefined,
4263 : "%s must only be called on arguments of type GAAT_STRING",
4264 : __func__);
4265 1 : return nullptr;
4266 : }
4267 25 : return hArg->ptr->Get<std::string>().c_str();
4268 : }
4269 :
4270 : /************************************************************************/
4271 : /* GDALAlgorithmArgGetAsDatasetValue() */
4272 : /************************************************************************/
4273 :
4274 : /** Return the argument value as a GDALArgDatasetValueH.
4275 : *
4276 : * Must only be called on arguments whose type is GAAT_DATASET
4277 : *
4278 : * @param hArg Handle to an argument. Must NOT be null.
4279 : * @return handle to a GDALArgDatasetValue that must be released with
4280 : * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
4281 : * the one of hArg.
4282 : * @since 3.11
4283 : */
4284 61 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
4285 : {
4286 61 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4287 61 : if (hArg->ptr->GetType() != GAAT_DATASET)
4288 : {
4289 1 : CPLError(CE_Failure, CPLE_AppDefined,
4290 : "%s must only be called on arguments of type GAAT_DATASET",
4291 : __func__);
4292 1 : return nullptr;
4293 : }
4294 60 : return std::make_unique<GDALArgDatasetValueHS>(
4295 120 : &(hArg->ptr->Get<GDALArgDatasetValue>()))
4296 60 : .release();
4297 : }
4298 :
4299 : /************************************************************************/
4300 : /* GDALAlgorithmArgGetAsInteger() */
4301 : /************************************************************************/
4302 :
4303 : /** Return the argument value as a integer.
4304 : *
4305 : * Must only be called on arguments whose type is GAAT_INTEGER
4306 : *
4307 : * @param hArg Handle to an argument. Must NOT be null.
4308 : * @since 3.11
4309 : */
4310 2 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
4311 : {
4312 2 : VALIDATE_POINTER1(hArg, __func__, 0);
4313 2 : if (hArg->ptr->GetType() != GAAT_INTEGER)
4314 : {
4315 1 : CPLError(CE_Failure, CPLE_AppDefined,
4316 : "%s must only be called on arguments of type GAAT_INTEGER",
4317 : __func__);
4318 1 : return 0;
4319 : }
4320 1 : return hArg->ptr->Get<int>();
4321 : }
4322 :
4323 : /************************************************************************/
4324 : /* GDALAlgorithmArgGetAsDouble() */
4325 : /************************************************************************/
4326 :
4327 : /** Return the argument value as a double.
4328 : *
4329 : * Must only be called on arguments whose type is GAAT_REAL
4330 : *
4331 : * @param hArg Handle to an argument. Must NOT be null.
4332 : * @since 3.11
4333 : */
4334 2 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
4335 : {
4336 2 : VALIDATE_POINTER1(hArg, __func__, 0);
4337 2 : if (hArg->ptr->GetType() != GAAT_REAL)
4338 : {
4339 1 : CPLError(CE_Failure, CPLE_AppDefined,
4340 : "%s must only be called on arguments of type GAAT_REAL",
4341 : __func__);
4342 1 : return 0;
4343 : }
4344 1 : return hArg->ptr->Get<double>();
4345 : }
4346 :
4347 : /************************************************************************/
4348 : /* GDALAlgorithmArgGetAsStringList() */
4349 : /************************************************************************/
4350 :
4351 : /** Return the argument value as a double.
4352 : *
4353 : * Must only be called on arguments whose type is GAAT_STRING_LIST.
4354 : *
4355 : * @param hArg Handle to an argument. Must NOT be null.
4356 : * @return a NULL terminated list of names, which must be destroyed with
4357 : * CSLDestroy()
4358 :
4359 : * @since 3.11
4360 : */
4361 2 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
4362 : {
4363 2 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4364 2 : if (hArg->ptr->GetType() != GAAT_STRING_LIST)
4365 : {
4366 1 : CPLError(CE_Failure, CPLE_AppDefined,
4367 : "%s must only be called on arguments of type GAAT_STRING_LIST",
4368 : __func__);
4369 1 : return nullptr;
4370 : }
4371 2 : return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
4372 1 : .StealList();
4373 : }
4374 :
4375 : /************************************************************************/
4376 : /* GDALAlgorithmArgGetAsIntegerList() */
4377 : /************************************************************************/
4378 :
4379 : /** Return the argument value as a integer.
4380 : *
4381 : * Must only be called on arguments whose type is GAAT_INTEGER
4382 : *
4383 : * @param hArg Handle to an argument. Must NOT be null.
4384 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
4385 : * @since 3.11
4386 : */
4387 2 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
4388 : size_t *pnCount)
4389 : {
4390 2 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4391 2 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
4392 2 : if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
4393 : {
4394 1 : CPLError(
4395 : CE_Failure, CPLE_AppDefined,
4396 : "%s must only be called on arguments of type GAAT_INTEGER_LIST",
4397 : __func__);
4398 1 : *pnCount = 0;
4399 1 : return nullptr;
4400 : }
4401 1 : const auto &val = hArg->ptr->Get<std::vector<int>>();
4402 1 : *pnCount = val.size();
4403 1 : return val.data();
4404 : }
4405 :
4406 : /************************************************************************/
4407 : /* GDALAlgorithmArgGetAsDoubleList() */
4408 : /************************************************************************/
4409 :
4410 : /** Return the argument value as a integer.
4411 : *
4412 : * Must only be called on arguments whose type is GAAT_INTEGER
4413 : *
4414 : * @param hArg Handle to an argument. Must NOT be null.
4415 : * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
4416 : * @since 3.11
4417 : */
4418 2 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
4419 : size_t *pnCount)
4420 : {
4421 2 : VALIDATE_POINTER1(hArg, __func__, nullptr);
4422 2 : VALIDATE_POINTER1(pnCount, __func__, nullptr);
4423 2 : if (hArg->ptr->GetType() != GAAT_REAL_LIST)
4424 : {
4425 1 : CPLError(CE_Failure, CPLE_AppDefined,
4426 : "%s must only be called on arguments of type GAAT_REAL_LIST",
4427 : __func__);
4428 1 : *pnCount = 0;
4429 1 : return nullptr;
4430 : }
4431 1 : const auto &val = hArg->ptr->Get<std::vector<double>>();
4432 1 : *pnCount = val.size();
4433 1 : return val.data();
4434 : }
4435 :
4436 : /************************************************************************/
4437 : /* GDALAlgorithmArgSetAsBoolean() */
4438 : /************************************************************************/
4439 :
4440 : /** Set the value for a GAAT_BOOLEAN argument.
4441 : *
4442 : * It cannot be called several times for a given argument.
4443 : * Validation checks and other actions are run.
4444 : *
4445 : * @param hArg Handle to an argument. Must NOT be null.
4446 : * @param value value.
4447 : * @return true if success.
4448 : * @since 3.11
4449 : */
4450 :
4451 4 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
4452 : {
4453 4 : VALIDATE_POINTER1(hArg, __func__, false);
4454 4 : return hArg->ptr->Set(value);
4455 : }
4456 :
4457 : /************************************************************************/
4458 : /* GDALAlgorithmArgSetAsString() */
4459 : /************************************************************************/
4460 :
4461 : /** Set the value for a GAAT_STRING argument.
4462 : *
4463 : * It cannot be called several times for a given argument.
4464 : * Validation checks and other actions are run.
4465 : *
4466 : * @param hArg Handle to an argument. Must NOT be null.
4467 : * @param value value (may be null)
4468 : * @return true if success.
4469 : * @since 3.11
4470 : */
4471 :
4472 22 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
4473 : {
4474 22 : VALIDATE_POINTER1(hArg, __func__, false);
4475 22 : return hArg->ptr->Set(value ? value : "");
4476 : }
4477 :
4478 : /************************************************************************/
4479 : /* GDALAlgorithmArgSetAsInteger() */
4480 : /************************************************************************/
4481 :
4482 : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
4483 : *
4484 : * It cannot be called several times for a given argument.
4485 : * Validation checks and other actions are run.
4486 : *
4487 : * @param hArg Handle to an argument. Must NOT be null.
4488 : * @param value value.
4489 : * @return true if success.
4490 : * @since 3.11
4491 : */
4492 :
4493 5 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
4494 : {
4495 5 : VALIDATE_POINTER1(hArg, __func__, false);
4496 5 : return hArg->ptr->Set(value);
4497 : }
4498 :
4499 : /************************************************************************/
4500 : /* GDALAlgorithmArgSetAsDouble() */
4501 : /************************************************************************/
4502 :
4503 : /** Set the value for a GAAT_REAL argument.
4504 : *
4505 : * It cannot be called several times for a given argument.
4506 : * Validation checks and other actions are run.
4507 : *
4508 : * @param hArg Handle to an argument. Must NOT be null.
4509 : * @param value value.
4510 : * @return true if success.
4511 : * @since 3.11
4512 : */
4513 :
4514 1 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
4515 : {
4516 1 : VALIDATE_POINTER1(hArg, __func__, false);
4517 1 : return hArg->ptr->Set(value);
4518 : }
4519 :
4520 : /************************************************************************/
4521 : /* GDALAlgorithmArgSetAsDatasetValue() */
4522 : /************************************************************************/
4523 :
4524 : /** Set the value for a GAAT_DATASET argument.
4525 : *
4526 : * It cannot be called several times for a given argument.
4527 : * Validation checks and other actions are run.
4528 : *
4529 : * @param hArg Handle to an argument. Must NOT be null.
4530 : * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
4531 : * @return true if success.
4532 : * @since 3.11
4533 : */
4534 2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
4535 : GDALArgDatasetValueH value)
4536 : {
4537 2 : VALIDATE_POINTER1(hArg, __func__, false);
4538 2 : VALIDATE_POINTER1(value, __func__, false);
4539 2 : return hArg->ptr->SetFrom(*(value->ptr));
4540 : }
4541 :
4542 : /************************************************************************/
4543 : /* GDALAlgorithmArgSetDataset() */
4544 : /************************************************************************/
4545 :
4546 : /** Set dataset object, increasing its reference counter.
4547 : *
4548 : * @param hArg Handle to an argument. Must NOT be null.
4549 : * @param hDS Dataset object. May be null.
4550 : * @return true if success.
4551 : * @since 3.11
4552 : */
4553 :
4554 11 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
4555 : {
4556 11 : VALIDATE_POINTER1(hArg, __func__, false);
4557 11 : return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
4558 : }
4559 :
4560 : /************************************************************************/
4561 : /* GDALAlgorithmArgSetAsStringList() */
4562 : /************************************************************************/
4563 :
4564 : /** Set the value for a GAAT_STRING_LIST argument.
4565 : *
4566 : * It cannot be called several times for a given argument.
4567 : * Validation checks and other actions are run.
4568 : *
4569 : * @param hArg Handle to an argument. Must NOT be null.
4570 : * @param value value as a NULL terminated list (may be null)
4571 : * @return true if success.
4572 : * @since 3.11
4573 : */
4574 :
4575 2 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
4576 : {
4577 2 : VALIDATE_POINTER1(hArg, __func__, false);
4578 2 : return hArg->ptr->Set(
4579 4 : static_cast<std::vector<std::string>>(CPLStringList(value)));
4580 : }
4581 :
4582 : /************************************************************************/
4583 : /* GDALAlgorithmArgSetAsIntegerList() */
4584 : /************************************************************************/
4585 :
4586 : /** Set the value for a GAAT_INTEGER_LIST argument.
4587 : *
4588 : * It cannot be called several times for a given argument.
4589 : * Validation checks and other actions are run.
4590 : *
4591 : * @param hArg Handle to an argument. Must NOT be null.
4592 : * @param nCount Number of values in pnValues.
4593 : * @param pnValues Pointer to an array of integer values of size nCount.
4594 : * @return true if success.
4595 : * @since 3.11
4596 : */
4597 4 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
4598 : const int *pnValues)
4599 : {
4600 4 : VALIDATE_POINTER1(hArg, __func__, false);
4601 4 : return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
4602 : }
4603 :
4604 : /************************************************************************/
4605 : /* GDALAlgorithmArgSetAsDoubleList() */
4606 : /************************************************************************/
4607 :
4608 : /** Set the value for a GAAT_REAL_LIST argument.
4609 : *
4610 : * It cannot be called several times for a given argument.
4611 : * Validation checks and other actions are run.
4612 : *
4613 : * @param hArg Handle to an argument. Must NOT be null.
4614 : * @param nCount Number of values in pnValues.
4615 : * @param pnValues Pointer to an array of double values of size nCount.
4616 : * @return true if success.
4617 : * @since 3.11
4618 : */
4619 5 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
4620 : const double *pnValues)
4621 : {
4622 5 : VALIDATE_POINTER1(hArg, __func__, false);
4623 5 : return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
4624 : }
4625 :
4626 : /************************************************************************/
4627 : /* GDALAlgorithmArgSetDatasets() */
4628 : /************************************************************************/
4629 :
4630 : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
4631 : *
4632 : * @param hArg Handle to an argument. Must NOT be null.
4633 : * @param nCount Number of values in pnValues.
4634 : * @param pahDS Pointer to an array of dataset of size nCount.
4635 : * @return true if success.
4636 : * @since 3.11
4637 : */
4638 :
4639 8 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
4640 : GDALDatasetH *pahDS)
4641 : {
4642 8 : VALIDATE_POINTER1(hArg, __func__, false);
4643 16 : std::vector<GDALArgDatasetValue> values;
4644 22 : for (size_t i = 0; i < nCount; ++i)
4645 : {
4646 14 : values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
4647 : }
4648 8 : return hArg->ptr->Set(std::move(values));
4649 : }
4650 :
4651 : /************************************************************************/
4652 : /* GDALAlgorithmArgSetDatasetNames() */
4653 : /************************************************************************/
4654 :
4655 : /** Set dataset names to a GAAT_DATASET_LIST argument.
4656 : *
4657 : * @param hArg Handle to an argument. Must NOT be null.
4658 : * @param names Dataset names as a NULL terminated list (may be null)
4659 : * @return true if success.
4660 : * @since 3.11
4661 : */
4662 :
4663 8 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
4664 : {
4665 8 : VALIDATE_POINTER1(hArg, __func__, false);
4666 16 : std::vector<GDALArgDatasetValue> values;
4667 16 : for (size_t i = 0; names[i]; ++i)
4668 : {
4669 8 : values.emplace_back(names[i]);
4670 : }
4671 8 : return hArg->ptr->Set(std::move(values));
4672 : }
4673 :
4674 : /************************************************************************/
4675 : /* GDALArgDatasetValueCreate() */
4676 : /************************************************************************/
4677 :
4678 : /** Instantiate an empty GDALArgDatasetValue
4679 : *
4680 : * @return new handle to free with GDALArgDatasetValueRelease()
4681 : * @since 3.11
4682 : */
4683 1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
4684 : {
4685 1 : return std::make_unique<GDALArgDatasetValueHS>().release();
4686 : }
4687 :
4688 : /************************************************************************/
4689 : /* GDALArgDatasetValueRelease() */
4690 : /************************************************************************/
4691 :
4692 : /** Release a handle to a GDALArgDatasetValue
4693 : *
4694 : * @since 3.11
4695 : */
4696 61 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
4697 : {
4698 61 : delete hValue;
4699 61 : }
4700 :
4701 : /************************************************************************/
4702 : /* GDALArgDatasetValueGetName() */
4703 : /************************************************************************/
4704 :
4705 : /** Return the name component of the GDALArgDatasetValue
4706 : *
4707 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4708 : * @return string whose lifetime is bound to hAlg and which must not
4709 : * be freed.
4710 : * @since 3.11
4711 : */
4712 1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
4713 : {
4714 1 : VALIDATE_POINTER1(hValue, __func__, nullptr);
4715 1 : return hValue->ptr->GetName().c_str();
4716 : }
4717 :
4718 : /************************************************************************/
4719 : /* GDALArgDatasetValueGetDatasetRef() */
4720 : /************************************************************************/
4721 :
4722 : /** Return the dataset component of the GDALArgDatasetValue.
4723 : *
4724 : * This does not modify the reference counter, hence the lifetime of the
4725 : * returned object is not guaranteed to exceed the one of hValue.
4726 : *
4727 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4728 : * @since 3.11
4729 : */
4730 3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
4731 : {
4732 3 : VALIDATE_POINTER1(hValue, __func__, nullptr);
4733 3 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
4734 : }
4735 :
4736 : /************************************************************************/
4737 : /* GDALArgDatasetValueGetDatasetIncreaseRefCount() */
4738 : /************************************************************************/
4739 :
4740 : /** Return the dataset component of the GDALArgDatasetValue, and increase its
4741 : * reference count if not null. Once done with the dataset, the caller should
4742 : * call GDALReleaseDataset().
4743 : *
4744 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4745 : * @since 3.11
4746 : */
4747 : GDALDatasetH
4748 24 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
4749 : {
4750 24 : VALIDATE_POINTER1(hValue, __func__, nullptr);
4751 24 : return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
4752 : }
4753 :
4754 : /************************************************************************/
4755 : /* GDALArgDatasetValueGetType() */
4756 : /************************************************************************/
4757 :
4758 : /** Get which type of dataset is allowed / generated.
4759 : *
4760 : * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
4761 : * GDAL_OF_MULTIDIM_RASTER.
4762 : *
4763 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4764 : * @since 3.11
4765 : */
4766 1 : GDALArgDatasetValueType GDALArgDatasetValueGetType(GDALArgDatasetValueH hValue)
4767 : {
4768 1 : VALIDATE_POINTER1(hValue, __func__, 0);
4769 1 : return hValue->ptr->GetType();
4770 : }
4771 :
4772 : /************************************************************************/
4773 : /* GDALArgDatasetValueGetInputFlags() */
4774 : /************************************************************************/
4775 :
4776 : /** Indicates which components among name and dataset are accepted as
4777 : * input, when this argument serves as an input.
4778 : *
4779 : * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
4780 : * input.
4781 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
4782 : * accepted as input.
4783 : * If both bits are set, the algorithm can accept either a name or a dataset
4784 : * object.
4785 : *
4786 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4787 : * @return string whose lifetime is bound to hAlg and which must not
4788 : * be freed.
4789 : * @since 3.11
4790 : */
4791 1 : int GDALArgDatasetValueGetInputFlags(GDALArgDatasetValueH hValue)
4792 : {
4793 1 : VALIDATE_POINTER1(hValue, __func__, 0);
4794 1 : return hValue->ptr->GetInputFlags();
4795 : }
4796 :
4797 : /************************************************************************/
4798 : /* GDALArgDatasetValueGetOutputFlags() */
4799 : /************************************************************************/
4800 :
4801 : /** Indicates which components among name and dataset are modified,
4802 : * when this argument serves as an output.
4803 : *
4804 : * If the GADV_NAME bit is set, it indicates a dataset name is generated as
4805 : * output (that is the algorithm will generate the name. Rarely used).
4806 : * If the GADV_OBJECT bit is set, it indicates a dataset object is
4807 : * generated as output, and available for use after the algorithm has
4808 : * completed.
4809 : *
4810 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4811 : * @return string whose lifetime is bound to hAlg and which must not
4812 : * be freed.
4813 : * @since 3.11
4814 : */
4815 1 : int GDALArgDatasetValueGetOutputFlags(GDALArgDatasetValueH hValue)
4816 : {
4817 1 : VALIDATE_POINTER1(hValue, __func__, 0);
4818 1 : return hValue->ptr->GetOutputFlags();
4819 : }
4820 :
4821 : /************************************************************************/
4822 : /* GDALArgDatasetValueSetName() */
4823 : /************************************************************************/
4824 :
4825 : /** Set dataset name
4826 : *
4827 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4828 : * @param pszName Dataset name. May be null.
4829 : * @since 3.11
4830 : */
4831 :
4832 22 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
4833 : const char *pszName)
4834 : {
4835 22 : VALIDATE_POINTER0(hValue, __func__);
4836 22 : hValue->ptr->Set(pszName ? pszName : "");
4837 : }
4838 :
4839 : /************************************************************************/
4840 : /* GDALArgDatasetValueSetDataset() */
4841 : /************************************************************************/
4842 :
4843 : /** Set dataset object, increasing its reference counter.
4844 : *
4845 : * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
4846 : * @param hDS Dataset object. May be null.
4847 : * @since 3.11
4848 : */
4849 :
4850 17 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
4851 : GDALDatasetH hDS)
4852 : {
4853 17 : VALIDATE_POINTER0(hValue, __func__);
4854 17 : hValue->ptr->Set(GDALDataset::FromHandle(hDS));
4855 : }
|