LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3737 3983 93.8 %
Date: 2026-04-24 03:46:56 Functions: 283 290 97.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDALAlgorithm class
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_error.h"
      16             : #include "cpl_json.h"
      17             : #include "cpl_levenshtein.h"
      18             : #include "cpl_minixml.h"
      19             : #include "cpl_multiproc.h"
      20             : 
      21             : #include "gdalalgorithm.h"
      22             : #include "gdalalg_abstract_pipeline.h"
      23             : #include "gdal_priv.h"
      24             : #include "gdal_thread_pool.h"
      25             : #include "ogrsf_frmts.h"
      26             : #include "ogr_spatialref.h"
      27             : #include "vrtdataset.h"
      28             : 
      29             : #include <algorithm>
      30             : #include <cassert>
      31             : #include <cerrno>
      32             : #include <cmath>
      33             : #include <cstdlib>
      34             : #include <limits>
      35             : #include <map>
      36             : #include <type_traits>
      37             : #include <string_view>
      38             : #include <regex>
      39             : 
      40             : #ifndef _
      41             : #define _(x) (x)
      42             : #endif
      43             : 
      44             : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
      45             : 
      46             : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
      47             : 
      48             : constexpr const char *GDAL_ARG_NAME_BAND = "band";
      49             : 
      50             : //! @cond Doxygen_Suppress
      51             : struct GDALAlgorithmArgHS
      52             : {
      53             :     GDALAlgorithmArg *ptr = nullptr;
      54             : 
      55      338973 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      56             :     {
      57      338973 :     }
      58             : };
      59             : 
      60             : //! @endcond
      61             : 
      62             : //! @cond Doxygen_Suppress
      63             : struct GDALArgDatasetValueHS
      64             : {
      65             :     GDALArgDatasetValue val{};
      66             :     GDALArgDatasetValue *ptr = nullptr;
      67             : 
      68           1 :     GDALArgDatasetValueHS() : ptr(&val)
      69             :     {
      70           1 :     }
      71             : 
      72        3166 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      73             :     {
      74        3166 :     }
      75             : 
      76             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      77             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      78             : };
      79             : 
      80             : //! @endcond
      81             : 
      82             : /************************************************************************/
      83             : /*                     GDALAlgorithmArgTypeIsList()                     */
      84             : /************************************************************************/
      85             : 
      86      407419 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      87             : {
      88      407419 :     switch (type)
      89             :     {
      90      268224 :         case GAAT_BOOLEAN:
      91             :         case GAAT_STRING:
      92             :         case GAAT_INTEGER:
      93             :         case GAAT_REAL:
      94             :         case GAAT_DATASET:
      95      268224 :             break;
      96             : 
      97      139195 :         case GAAT_STRING_LIST:
      98             :         case GAAT_INTEGER_LIST:
      99             :         case GAAT_REAL_LIST:
     100             :         case GAAT_DATASET_LIST:
     101      139195 :             return true;
     102             :     }
     103             : 
     104      268224 :     return false;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                      GDALAlgorithmArgTypeName()                      */
     109             : /************************************************************************/
     110             : 
     111        5459 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     112             : {
     113        5459 :     switch (type)
     114             :     {
     115        1355 :         case GAAT_BOOLEAN:
     116        1355 :             break;
     117        1469 :         case GAAT_STRING:
     118        1469 :             return "string";
     119         372 :         case GAAT_INTEGER:
     120         372 :             return "integer";
     121         477 :         case GAAT_REAL:
     122         477 :             return "real";
     123         251 :         case GAAT_DATASET:
     124         251 :             return "dataset";
     125        1016 :         case GAAT_STRING_LIST:
     126        1016 :             return "string_list";
     127          93 :         case GAAT_INTEGER_LIST:
     128          93 :             return "integer_list";
     129         221 :         case GAAT_REAL_LIST:
     130         221 :             return "real_list";
     131         205 :         case GAAT_DATASET_LIST:
     132         205 :             return "dataset_list";
     133             :     }
     134             : 
     135        1355 :     return "boolean";
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                  GDALAlgorithmArgDatasetTypeName()                   */
     140             : /************************************************************************/
     141             : 
     142       22125 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
     143             : {
     144       22125 :     std::string ret;
     145       22125 :     if ((type & GDAL_OF_RASTER) != 0)
     146       12147 :         ret = "raster";
     147       22125 :     if ((type & GDAL_OF_VECTOR) != 0)
     148             :     {
     149       10772 :         if (!ret.empty())
     150             :         {
     151        1187 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     152         257 :                 ret += ", ";
     153             :             else
     154         930 :                 ret += " or ";
     155             :         }
     156       10772 :         ret += "vector";
     157             :     }
     158       22125 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     159             :     {
     160         576 :         if (!ret.empty())
     161             :         {
     162         316 :             ret += " or ";
     163             :         }
     164         576 :         ret += "multidimensional raster";
     165             :     }
     166       22125 :     return ret;
     167             : }
     168             : 
     169             : /************************************************************************/
     170             : /*                        GDALAlgorithmArgDecl()                        */
     171             : /************************************************************************/
     172             : 
     173             : // cppcheck-suppress uninitMemberVar
     174      327690 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     175             :                                            char chShortName,
     176             :                                            const std::string &description,
     177      327690 :                                            GDALAlgorithmArgType type)
     178             :     : m_longName(longName),
     179      327690 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     180             :       m_description(description), m_type(type),
     181      655380 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     182      327690 :                     .toupper()),
     183      983070 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     184             : {
     185      327690 :     if (m_type == GAAT_BOOLEAN)
     186             :     {
     187      136630 :         m_defaultValue = false;
     188             :     }
     189      327690 : }
     190             : 
     191             : /************************************************************************/
     192             : /*                 GDALAlgorithmArgDecl::SetMinCount()                  */
     193             : /************************************************************************/
     194             : 
     195       18068 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     196             : {
     197       18068 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     198             :     {
     199           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     200             :                  "SetMinCount() illegal on scalar argument '%s'",
     201           1 :                  GetName().c_str());
     202             :     }
     203             :     else
     204             :     {
     205       18067 :         m_minCount = count;
     206             :     }
     207       18068 :     return *this;
     208             : }
     209             : 
     210             : /************************************************************************/
     211             : /*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
     212             : /************************************************************************/
     213             : 
     214       17100 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     215             : {
     216       17100 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     217             :     {
     218           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     219             :                  "SetMaxCount() illegal on scalar argument '%s'",
     220           1 :                  GetName().c_str());
     221             :     }
     222             :     else
     223             :     {
     224       17099 :         m_maxCount = count;
     225             :     }
     226       17100 :     return *this;
     227             : }
     228             : 
     229             : /************************************************************************/
     230             : /*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
     231             : /************************************************************************/
     232             : 
     233             : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
     234             : 
     235             : /************************************************************************/
     236             : /*                       GDALAlgorithmArg::Set()                        */
     237             : /************************************************************************/
     238             : 
     239        1196 : bool GDALAlgorithmArg::Set(bool value)
     240             : {
     241        1196 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     242             :     {
     243          14 :         CPLError(
     244             :             CE_Failure, CPLE_AppDefined,
     245             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     246           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     247           7 :         return false;
     248             :     }
     249        1189 :     return SetInternal(value);
     250             : }
     251             : 
     252        3963 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
     253             : {
     254        4008 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     255          45 :         value.front() == '@')
     256             :     {
     257           2 :         GByte *pabyData = nullptr;
     258           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     259           2 :                           10 * 1024 * 1024))
     260             :         {
     261             :             // Remove UTF-8 BOM
     262           1 :             size_t offset = 0;
     263           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     264           1 :                 pabyData[2] == 0xBF)
     265             :             {
     266           1 :                 offset = 3;
     267             :             }
     268           1 :             value = reinterpret_cast<const char *>(pabyData + offset);
     269           1 :             VSIFree(pabyData);
     270             :         }
     271             :         else
     272             :         {
     273           1 :             return false;
     274             :         }
     275             :     }
     276             : 
     277        3962 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     278          44 :         value = CPLRemoveSQLComments(value);
     279             : 
     280        3962 :     return true;
     281             : }
     282             : 
     283        3979 : bool GDALAlgorithmArg::Set(const std::string &value)
     284             : {
     285        3979 :     switch (m_decl.GetType())
     286             :     {
     287           9 :         case GAAT_BOOLEAN:
     288          17 :             if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
     289          17 :                 EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
     290             :             {
     291           4 :                 return Set(true);
     292             :             }
     293           5 :             else if (EQUAL(value.c_str(), "0") ||
     294           4 :                      EQUAL(value.c_str(), "FALSE") ||
     295           9 :                      EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
     296             :             {
     297           4 :                 return Set(false);
     298             :             }
     299           1 :             break;
     300             : 
     301           8 :         case GAAT_INTEGER:
     302             :         case GAAT_INTEGER_LIST:
     303             :         {
     304           8 :             errno = 0;
     305           8 :             char *endptr = nullptr;
     306           8 :             const auto v = std::strtoll(value.c_str(), &endptr, 10);
     307          13 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     308           5 :                 endptr == value.c_str() + value.size())
     309             :             {
     310           3 :                 if (m_decl.GetType() == GAAT_INTEGER)
     311           3 :                     return Set(static_cast<int>(v));
     312             :                 else
     313           1 :                     return Set(std::vector<int>{static_cast<int>(v)});
     314             :             }
     315           5 :             break;
     316             :         }
     317             : 
     318           5 :         case GAAT_REAL:
     319             :         case GAAT_REAL_LIST:
     320             :         {
     321           5 :             char *endptr = nullptr;
     322           5 :             const double v = CPLStrtod(value.c_str(), &endptr);
     323           5 :             if (endptr == value.c_str() + value.size())
     324             :             {
     325           3 :                 if (m_decl.GetType() == GAAT_REAL)
     326           3 :                     return Set(v);
     327             :                 else
     328           1 :                     return Set(std::vector<double>{v});
     329             :             }
     330           2 :             break;
     331             :         }
     332             : 
     333        3941 :         case GAAT_STRING:
     334        3941 :             break;
     335             : 
     336           1 :         case GAAT_STRING_LIST:
     337           2 :             return Set(std::vector<std::string>{value});
     338             : 
     339          15 :         case GAAT_DATASET:
     340          15 :             return SetDatasetName(value);
     341             : 
     342           0 :         case GAAT_DATASET_LIST:
     343             :         {
     344           0 :             std::vector<GDALArgDatasetValue> v;
     345           0 :             v.resize(1);
     346           0 :             v[0].Set(value);
     347           0 :             return Set(std::move(v));
     348             :         }
     349             :     }
     350             : 
     351        3949 :     if (m_decl.GetType() != GAAT_STRING)
     352             :     {
     353          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     354             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     355             :                  "supported",
     356           8 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     357           8 :         return false;
     358             :     }
     359             : 
     360        3941 :     std::string newValue(value);
     361        3941 :     return ProcessString(newValue) && SetInternal(newValue);
     362             : }
     363             : 
     364         848 : bool GDALAlgorithmArg::Set(int value)
     365             : {
     366         848 :     if (m_decl.GetType() == GAAT_BOOLEAN)
     367             :     {
     368           3 :         if (value == 1)
     369           1 :             return Set(true);
     370           2 :         else if (value == 0)
     371           1 :             return Set(false);
     372             :     }
     373         845 :     else if (m_decl.GetType() == GAAT_REAL)
     374             :     {
     375           3 :         return Set(static_cast<double>(value));
     376             :     }
     377         842 :     else if (m_decl.GetType() == GAAT_STRING)
     378             :     {
     379           2 :         return Set(std::to_string(value));
     380             :     }
     381         840 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST)
     382             :     {
     383           1 :         return Set(std::vector<int>{value});
     384             :     }
     385         839 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     386             :     {
     387           1 :         return Set(std::vector<double>{static_cast<double>(value)});
     388             :     }
     389         838 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     390             :     {
     391           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     392             :     }
     393             : 
     394         838 :     if (m_decl.GetType() != GAAT_INTEGER)
     395             :     {
     396           2 :         CPLError(
     397             :             CE_Failure, CPLE_AppDefined,
     398             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     399           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     400           1 :         return false;
     401             :     }
     402         837 :     return SetInternal(value);
     403             : }
     404             : 
     405         290 : bool GDALAlgorithmArg::Set(double value)
     406             : {
     407         293 :     if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
     408         293 :         value <= INT_MAX && static_cast<int>(value) == value)
     409             :     {
     410           2 :         return Set(static_cast<int>(value));
     411             :     }
     412         288 :     else if (m_decl.GetType() == GAAT_STRING)
     413             :     {
     414           2 :         return Set(std::to_string(value));
     415             :     }
     416         288 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
     417         288 :              value <= INT_MAX && static_cast<int>(value) == value)
     418             :     {
     419           1 :         return Set(std::vector<int>{static_cast<int>(value)});
     420             :     }
     421         285 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     422             :     {
     423           0 :         return Set(std::vector<double>{value});
     424             :     }
     425         285 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     426             :     {
     427           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     428             :     }
     429         284 :     else if (m_decl.GetType() != GAAT_REAL)
     430             :     {
     431           6 :         CPLError(
     432             :             CE_Failure, CPLE_AppDefined,
     433             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     434           3 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     435           3 :         return false;
     436             :     }
     437         281 :     return SetInternal(value);
     438             : }
     439             : 
     440        6127 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
     441             : {
     442        6130 :     if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
     443           3 :         arg->GetDatasetOutputFlags() == GADV_OBJECT)
     444             :     {
     445           3 :         CPLError(
     446             :             CE_Failure, CPLE_AppDefined,
     447             :             "Dataset object '%s' is created by algorithm and cannot be set "
     448             :             "as an input.",
     449           3 :             arg->GetName().c_str());
     450           3 :         return false;
     451             :     }
     452        6124 :     else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
     453             :     {
     454           8 :         CPLError(CE_Failure, CPLE_AppDefined,
     455             :                  "Dataset%s '%s' must be provided by name, not as object.",
     456           8 :                  arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
     457           4 :         return false;
     458             :     }
     459             : 
     460        6120 :     return true;
     461             : }
     462             : 
     463           9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     464             : {
     465           9 :     if (m_decl.GetType() != GAAT_DATASET)
     466             :     {
     467           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     468             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     469             :                  "is not supported",
     470           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     471           1 :         return false;
     472             :     }
     473           8 :     if (!CheckCanSetDatasetObject(this))
     474           2 :         return false;
     475           6 :     m_explicitlySet = true;
     476           6 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     477           6 :     val.Set(ds);
     478           6 :     return RunAllActions();
     479             : }
     480             : 
     481           3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     482             : {
     483           3 :     if (m_decl.GetType() != GAAT_DATASET)
     484             :     {
     485           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     486             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     487             :                  "is not supported",
     488           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     489           1 :         return false;
     490             :     }
     491           2 :     if (!CheckCanSetDatasetObject(this))
     492           1 :         return false;
     493           1 :     m_explicitlySet = true;
     494           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     495           1 :     val.Set(std::move(ds));
     496           1 :     return RunAllActions();
     497             : }
     498             : 
     499         557 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     500             : {
     501         557 :     if (m_decl.GetType() != GAAT_DATASET)
     502             :     {
     503           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     504             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     505             :                  "not supported",
     506           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     507           1 :         return false;
     508             :     }
     509         556 :     m_explicitlySet = true;
     510         556 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     511         556 :     return RunAllActions();
     512             : }
     513             : 
     514         961 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     515             : {
     516         961 :     if (m_decl.GetType() != GAAT_DATASET)
     517             :     {
     518           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     519             :                  "Calling SetFrom() on argument '%s' of type %s is "
     520             :                  "not supported",
     521           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     522           1 :         return false;
     523             :     }
     524         960 :     if (!CheckCanSetDatasetObject(this))
     525           1 :         return false;
     526         959 :     m_explicitlySet = true;
     527         959 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     528         959 :     return RunAllActions();
     529             : }
     530             : 
     531        1048 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     532             : {
     533        1048 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     534             :     {
     535           3 :         std::vector<int> v_i;
     536           4 :         for (const std::string &s : value)
     537             :         {
     538           3 :             errno = 0;
     539           3 :             char *endptr = nullptr;
     540           3 :             const auto v = std::strtoll(s.c_str(), &endptr, 10);
     541           5 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     542           2 :                 endptr == s.c_str() + s.size())
     543             :             {
     544           1 :                 v_i.push_back(static_cast<int>(v));
     545             :             }
     546             :             else
     547             :             {
     548           2 :                 break;
     549             :             }
     550             :         }
     551           3 :         if (v_i.size() == value.size())
     552           1 :             return Set(v_i);
     553             :     }
     554        1045 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     555             :     {
     556           2 :         std::vector<double> v_d;
     557           3 :         for (const std::string &s : value)
     558             :         {
     559           2 :             char *endptr = nullptr;
     560           2 :             const double v = CPLStrtod(s.c_str(), &endptr);
     561           2 :             if (endptr == s.c_str() + s.size())
     562             :             {
     563           1 :                 v_d.push_back(v);
     564             :             }
     565             :             else
     566             :             {
     567           1 :                 break;
     568             :             }
     569             :         }
     570           2 :         if (v_d.size() == value.size())
     571           1 :             return Set(v_d);
     572             :     }
     573        2084 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     574        2081 :               m_decl.GetType() == GAAT_REAL ||
     575        3126 :               m_decl.GetType() == GAAT_STRING) &&
     576           5 :              value.size() == 1)
     577             :     {
     578           4 :         return Set(value[0]);
     579             :     }
     580        1039 :     else if (m_decl.GetType() == GAAT_DATASET_LIST)
     581             :     {
     582          30 :         std::vector<GDALArgDatasetValue> dsVector;
     583          46 :         for (const std::string &s : value)
     584          31 :             dsVector.emplace_back(s);
     585          15 :         return Set(std::move(dsVector));
     586             :     }
     587             : 
     588        1027 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     589             :     {
     590          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     591             :                  "Calling Set(const std::vector<std::string> &) on argument "
     592             :                  "'%s' of type %s is not supported",
     593           5 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     594           5 :         return false;
     595             :     }
     596             : 
     597        2025 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
     598        1003 :         m_decl.IsRemoveSQLCommentsEnabled())
     599             :     {
     600          38 :         std::vector<std::string> newValue(value);
     601          41 :         for (auto &s : newValue)
     602             :         {
     603          22 :             if (!ProcessString(s))
     604           0 :                 return false;
     605             :         }
     606          19 :         return SetInternal(newValue);
     607             :     }
     608             :     else
     609             :     {
     610        1003 :         return SetInternal(value);
     611             :     }
     612             : }
     613             : 
     614         172 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     615             : {
     616         172 :     if (m_decl.GetType() == GAAT_REAL_LIST)
     617             :     {
     618           2 :         std::vector<double> v_d;
     619           2 :         for (int i : value)
     620           1 :             v_d.push_back(i);
     621           1 :         return Set(v_d);
     622             :     }
     623         171 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     624             :     {
     625           2 :         std::vector<std::string> v_s;
     626           3 :         for (int i : value)
     627           2 :             v_s.push_back(std::to_string(i));
     628           1 :         return Set(v_s);
     629             :     }
     630         338 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     631         334 :               m_decl.GetType() == GAAT_REAL ||
     632         506 :               m_decl.GetType() == GAAT_STRING) &&
     633           5 :              value.size() == 1)
     634             :     {
     635           3 :         return Set(value[0]);
     636             :     }
     637             : 
     638         167 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     639             :     {
     640           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     641             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     642             :                  "type %s is not supported",
     643           3 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     644           3 :         return false;
     645             :     }
     646         164 :     return SetInternal(value);
     647             : }
     648             : 
     649         266 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     650             : {
     651         266 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     652             :     {
     653           2 :         std::vector<int> v_i;
     654           3 :         for (double d : value)
     655             :         {
     656           2 :             if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
     657             :             {
     658           1 :                 v_i.push_back(static_cast<int>(d));
     659             :             }
     660             :             else
     661             :             {
     662             :                 break;
     663             :             }
     664             :         }
     665           2 :         if (v_i.size() == value.size())
     666           1 :             return Set(v_i);
     667             :     }
     668         264 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     669             :     {
     670           2 :         std::vector<std::string> v_s;
     671           3 :         for (double d : value)
     672           2 :             v_s.push_back(std::to_string(d));
     673           1 :         return Set(v_s);
     674             :     }
     675         525 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     676         523 :               m_decl.GetType() == GAAT_REAL ||
     677         787 :               m_decl.GetType() == GAAT_STRING) &&
     678           3 :              value.size() == 1)
     679             :     {
     680           3 :         return Set(value[0]);
     681             :     }
     682             : 
     683         261 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     684             :     {
     685           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     686             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     687             :                  "type %s is not supported",
     688           2 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     689           2 :         return false;
     690             :     }
     691         259 :     return SetInternal(value);
     692             : }
     693             : 
     694        3379 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     695             : {
     696        3379 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     697             :     {
     698           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     699             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     700             :                  "argument '%s' of type %s is not supported",
     701           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     702           1 :         return false;
     703             :     }
     704        3378 :     m_explicitlySet = true;
     705        3378 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     706        3378 :     return RunAllActions();
     707             : }
     708             : 
     709             : GDALAlgorithmArg &
     710           0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
     711             : {
     712           0 :     Set(std::move(value));
     713           0 :     return *this;
     714             : }
     715             : 
     716           1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
     717             : {
     718           1 :     const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
     719           1 :     return Set(value.exportToWkt(apszOptions));
     720             : }
     721             : 
     722        4153 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     723             : {
     724        4153 :     if (m_decl.GetType() != other.GetType())
     725             :     {
     726           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     727             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     728             :                  "other argument type is %s is not supported",
     729           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     730             :                  GDALAlgorithmArgTypeName(other.GetType()));
     731           1 :         return false;
     732             :     }
     733             : 
     734        4152 :     switch (m_decl.GetType())
     735             :     {
     736          90 :         case GAAT_BOOLEAN:
     737          90 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     738          90 :             break;
     739         802 :         case GAAT_STRING:
     740        1604 :             *std::get<std::string *>(m_value) =
     741         802 :                 *std::get<std::string *>(other.m_value);
     742         802 :             break;
     743           6 :         case GAAT_INTEGER:
     744           6 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     745           6 :             break;
     746           1 :         case GAAT_REAL:
     747           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     748           1 :             break;
     749         955 :         case GAAT_DATASET:
     750         955 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     751          39 :         case GAAT_STRING_LIST:
     752          78 :             *std::get<std::vector<std::string> *>(m_value) =
     753          39 :                 *std::get<std::vector<std::string> *>(other.m_value);
     754          39 :             break;
     755           1 :         case GAAT_INTEGER_LIST:
     756           2 :             *std::get<std::vector<int> *>(m_value) =
     757           1 :                 *std::get<std::vector<int> *>(other.m_value);
     758           1 :             break;
     759           1 :         case GAAT_REAL_LIST:
     760           2 :             *std::get<std::vector<double> *>(m_value) =
     761           1 :                 *std::get<std::vector<double> *>(other.m_value);
     762           1 :             break;
     763        2257 :         case GAAT_DATASET_LIST:
     764             :         {
     765        2257 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     766        2262 :             for (const auto &val :
     767        6781 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     768             :             {
     769        4524 :                 GDALArgDatasetValue v;
     770        2262 :                 v.SetFrom(val);
     771        2262 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     772        2262 :                     ->push_back(std::move(v));
     773             :             }
     774        2257 :             break;
     775             :         }
     776             :     }
     777        3197 :     m_explicitlySet = true;
     778        3197 :     return RunAllActions();
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     783             : /************************************************************************/
     784             : 
     785       15789 : bool GDALAlgorithmArg::RunAllActions()
     786             : {
     787       15789 :     if (!RunValidationActions())
     788         147 :         return false;
     789       15642 :     RunActions();
     790       15642 :     return true;
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*                    GDALAlgorithmArg::RunActions()                    */
     795             : /************************************************************************/
     796             : 
     797       15643 : void GDALAlgorithmArg::RunActions()
     798             : {
     799       15939 :     for (const auto &f : m_actions)
     800         296 :         f();
     801       15643 : }
     802             : 
     803             : /************************************************************************/
     804             : /*                  GDALAlgorithmArg::ValidateChoice()                  */
     805             : /************************************************************************/
     806             : 
     807             : // Returns the canonical value if matching a valid choice, or empty string
     808             : // otherwise.
     809        2574 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
     810             : {
     811       15272 :     for (const std::string &choice : GetChoices())
     812             :     {
     813       15154 :         if (EQUAL(value.c_str(), choice.c_str()))
     814             :         {
     815        2456 :             return choice;
     816             :         }
     817             :     }
     818             : 
     819         190 :     for (const std::string &choice : GetHiddenChoices())
     820             :     {
     821         172 :         if (EQUAL(value.c_str(), choice.c_str()))
     822             :         {
     823         100 :             return choice;
     824             :         }
     825             :     }
     826             : 
     827          36 :     std::string expected;
     828         220 :     for (const auto &choice : GetChoices())
     829             :     {
     830         202 :         if (!expected.empty())
     831         184 :             expected += ", ";
     832         202 :         expected += '\'';
     833         202 :         expected += choice;
     834         202 :         expected += '\'';
     835             :     }
     836          18 :     if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
     837             :     {
     838           6 :         return "?";
     839             :     }
     840          24 :     CPLError(CE_Failure, CPLE_IllegalArg,
     841             :              "Invalid value '%s' for string argument '%s'. Should be "
     842             :              "one among %s.",
     843          12 :              value.c_str(), GetName().c_str(), expected.c_str());
     844          12 :     return std::string();
     845             : }
     846             : 
     847             : /************************************************************************/
     848             : /*                 GDALAlgorithmArg::ValidateIntRange()                 */
     849             : /************************************************************************/
     850             : 
     851        2680 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
     852             : {
     853        2680 :     bool ret = true;
     854             : 
     855        2680 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     856        2680 :     if (!std::isnan(minVal))
     857             :     {
     858        2030 :         if (minValIsIncluded && val < minVal)
     859             :         {
     860           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
     861             :                      "Value of argument '%s' is %d, but should be >= %d",
     862           3 :                      GetName().c_str(), val, static_cast<int>(minVal));
     863           3 :             ret = false;
     864             :         }
     865        2027 :         else if (!minValIsIncluded && val <= minVal)
     866             :         {
     867           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     868             :                      "Value of argument '%s' is %d, but should be > %d",
     869           1 :                      GetName().c_str(), val, static_cast<int>(minVal));
     870           1 :             ret = false;
     871             :         }
     872             :     }
     873             : 
     874        2680 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     875        2680 :     if (!std::isnan(maxVal))
     876             :     {
     877             : 
     878         382 :         if (maxValIsIncluded && val > maxVal)
     879             :         {
     880           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     881             :                      "Value of argument '%s' is %d, but should be <= %d",
     882           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     883           1 :             ret = false;
     884             :         }
     885         381 :         else if (!maxValIsIncluded && val >= maxVal)
     886             :         {
     887           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     888             :                      "Value of argument '%s' is %d, but should be < %d",
     889           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     890           1 :             ret = false;
     891             :         }
     892             :     }
     893             : 
     894        2680 :     return ret;
     895             : }
     896             : 
     897             : /************************************************************************/
     898             : /*                GDALAlgorithmArg::ValidateRealRange()                 */
     899             : /************************************************************************/
     900             : 
     901        2126 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
     902             : {
     903        2126 :     bool ret = true;
     904             : 
     905        2126 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     906        2126 :     if (!std::isnan(minVal))
     907             :     {
     908         214 :         if (minValIsIncluded && !(val >= minVal))
     909             :         {
     910          11 :             CPLError(CE_Failure, CPLE_IllegalArg,
     911             :                      "Value of argument '%s' is %g, but should be >= %g",
     912          11 :                      GetName().c_str(), val, minVal);
     913          11 :             ret = false;
     914             :         }
     915         203 :         else if (!minValIsIncluded && !(val > minVal))
     916             :         {
     917           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
     918             :                      "Value of argument '%s' is %g, but should be > %g",
     919           4 :                      GetName().c_str(), val, minVal);
     920           4 :             ret = false;
     921             :         }
     922             :     }
     923             : 
     924        2126 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     925        2126 :     if (!std::isnan(maxVal))
     926             :     {
     927             : 
     928          58 :         if (maxValIsIncluded && !(val <= maxVal))
     929             :         {
     930           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     931             :                      "Value of argument '%s' is %g, but should be <= %g",
     932           2 :                      GetName().c_str(), val, maxVal);
     933           2 :             ret = false;
     934             :         }
     935          56 :         else if (!maxValIsIncluded && !(val < maxVal))
     936             :         {
     937           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     938             :                      "Value of argument '%s' is %g, but should be < %g",
     939           1 :                      GetName().c_str(), val, maxVal);
     940           1 :             ret = false;
     941             :         }
     942             :     }
     943             : 
     944        2126 :     return ret;
     945             : }
     946             : 
     947             : /************************************************************************/
     948             : /*                        CheckDuplicateValues()                        */
     949             : /************************************************************************/
     950             : 
     951             : template <class T>
     952          92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
     953             :                                  const std::vector<T> &values)
     954             : {
     955         184 :     auto tmpValues = values;
     956          92 :     bool bHasDupValues = false;
     957             :     if constexpr (std::is_floating_point_v<T>)
     958             :     {
     959             :         // Avoid undefined behavior with NaN values
     960           4 :         std::sort(tmpValues.begin(), tmpValues.end(),
     961          21 :                   [](T a, T b)
     962             :                   {
     963          21 :                       if (std::isnan(a) && !std::isnan(b))
     964           3 :                           return true;
     965          18 :                       if (std::isnan(b))
     966          10 :                           return false;
     967           8 :                       return a < b;
     968             :                   });
     969             : 
     970             :         bHasDupValues =
     971           4 :             std::adjacent_find(tmpValues.begin(), tmpValues.end(),
     972           6 :                                [](T a, T b)
     973             :                                {
     974           6 :                                    if (std::isnan(a) && std::isnan(b))
     975           1 :                                        return true;
     976           5 :                                    return a == b;
     977           8 :                                }) != tmpValues.end();
     978             :     }
     979             :     else
     980             :     {
     981          88 :         std::sort(tmpValues.begin(), tmpValues.end());
     982          88 :         bHasDupValues = std::adjacent_find(tmpValues.begin(),
     983         176 :                                            tmpValues.end()) != tmpValues.end();
     984             :     }
     985          92 :     if (bHasDupValues)
     986             :     {
     987          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     988             :                  "'%s' must be a list of unique values.",
     989          10 :                  arg->GetName().c_str());
     990          10 :         return false;
     991             :     }
     992          82 :     return true;
     993             : }
     994             : 
     995             : /************************************************************************/
     996             : /*               GDALAlgorithmArg::RunValidationActions()               */
     997             : /************************************************************************/
     998             : 
     999       34931 : bool GDALAlgorithmArg::RunValidationActions()
    1000             : {
    1001       34931 :     bool ret = true;
    1002             : 
    1003       34931 :     if (GetType() == GAAT_STRING && !GetChoices().empty())
    1004             :     {
    1005        1655 :         auto &val = Get<std::string>();
    1006        3310 :         std::string validVal = ValidateChoice(val);
    1007        1655 :         if (validVal.empty())
    1008           7 :             ret = false;
    1009             :         else
    1010        1648 :             val = std::move(validVal);
    1011             :     }
    1012       33276 :     else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
    1013             :     {
    1014         659 :         auto &values = Get<std::vector<std::string>>();
    1015        1578 :         for (std::string &val : values)
    1016             :         {
    1017        1838 :             std::string validVal = ValidateChoice(val);
    1018         919 :             if (validVal.empty())
    1019           5 :                 ret = false;
    1020             :             else
    1021         914 :                 val = std::move(validVal);
    1022             :         }
    1023             :     }
    1024             : 
    1025             :     const auto CheckMinCharCount =
    1026         946 :         [this, &ret](const std::string &val, int nMinCharCount)
    1027             :     {
    1028         934 :         if (val.size() < static_cast<size_t>(nMinCharCount))
    1029             :         {
    1030          12 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1031             :                      "Value of argument '%s' is '%s', but should have at least "
    1032             :                      "%d character%s",
    1033           6 :                      GetName().c_str(), val.c_str(), nMinCharCount,
    1034             :                      nMinCharCount > 1 ? "s" : "");
    1035           6 :             ret = false;
    1036             :         }
    1037       35865 :     };
    1038             : 
    1039             :     const auto CheckMaxCharCount =
    1040       12538 :         [this, &ret](const std::string &val, int nMaxCharCount)
    1041             :     {
    1042       12536 :         if (val.size() > static_cast<size_t>(nMaxCharCount))
    1043             :         {
    1044           2 :             CPLError(
    1045             :                 CE_Failure, CPLE_IllegalArg,
    1046             :                 "Value of argument '%s' is '%s', but should have no more than "
    1047             :                 "%d character%s",
    1048           1 :                 GetName().c_str(), val.c_str(), nMaxCharCount,
    1049             :                 nMaxCharCount > 1 ? "s" : "");
    1050           1 :             ret = false;
    1051             :         }
    1052       47467 :     };
    1053             : 
    1054       34931 :     switch (GetType())
    1055             :     {
    1056        2700 :         case GAAT_BOOLEAN:
    1057        2700 :             break;
    1058             : 
    1059        9821 :         case GAAT_STRING:
    1060             :         {
    1061        9821 :             const auto &val = Get<std::string>();
    1062        9821 :             const int nMinCharCount = GetMinCharCount();
    1063        9821 :             if (nMinCharCount > 0)
    1064             :             {
    1065         852 :                 CheckMinCharCount(val, nMinCharCount);
    1066             :             }
    1067             : 
    1068        9821 :             const int nMaxCharCount = GetMaxCharCount();
    1069        9821 :             CheckMaxCharCount(val, nMaxCharCount);
    1070        9821 :             break;
    1071             :         }
    1072             : 
    1073        2210 :         case GAAT_STRING_LIST:
    1074             :         {
    1075        2210 :             const int nMinCharCount = GetMinCharCount();
    1076        2210 :             const int nMaxCharCount = GetMaxCharCount();
    1077        2210 :             const auto &values = Get<std::vector<std::string>>();
    1078        4925 :             for (const auto &val : values)
    1079             :             {
    1080        2715 :                 if (nMinCharCount > 0)
    1081          82 :                     CheckMinCharCount(val, nMinCharCount);
    1082        2715 :                 CheckMaxCharCount(val, nMaxCharCount);
    1083             :             }
    1084             : 
    1085        2284 :             if (!GetDuplicateValuesAllowed() &&
    1086          74 :                 !CheckDuplicateValues(this, values))
    1087           2 :                 ret = false;
    1088        2210 :             break;
    1089             :         }
    1090             : 
    1091        1982 :         case GAAT_INTEGER:
    1092             :         {
    1093        1982 :             ret = ValidateIntRange(Get<int>()) && ret;
    1094        1982 :             break;
    1095             :         }
    1096             : 
    1097         344 :         case GAAT_INTEGER_LIST:
    1098             :         {
    1099         344 :             const auto &values = Get<std::vector<int>>();
    1100        1042 :             for (int v : values)
    1101         698 :                 ret = ValidateIntRange(v) && ret;
    1102             : 
    1103         347 :             if (!GetDuplicateValuesAllowed() &&
    1104           3 :                 !CheckDuplicateValues(this, values))
    1105           1 :                 ret = false;
    1106         344 :             break;
    1107             :         }
    1108             : 
    1109         547 :         case GAAT_REAL:
    1110             :         {
    1111         547 :             ret = ValidateRealRange(Get<double>()) && ret;
    1112         547 :             break;
    1113             :         }
    1114             : 
    1115         572 :         case GAAT_REAL_LIST:
    1116             :         {
    1117         572 :             const auto &values = Get<std::vector<double>>();
    1118        2151 :             for (double v : values)
    1119        1579 :                 ret = ValidateRealRange(v) && ret;
    1120             : 
    1121         576 :             if (!GetDuplicateValuesAllowed() &&
    1122           4 :                 !CheckDuplicateValues(this, values))
    1123           2 :                 ret = false;
    1124         572 :             break;
    1125             :         }
    1126             : 
    1127        5709 :         case GAAT_DATASET:
    1128        5709 :             break;
    1129             : 
    1130       11046 :         case GAAT_DATASET_LIST:
    1131             :         {
    1132       11046 :             if (!GetDuplicateValuesAllowed())
    1133             :             {
    1134          11 :                 const auto &values = Get<std::vector<GDALArgDatasetValue>>();
    1135          22 :                 std::vector<std::string> aosValues;
    1136          34 :                 for (const auto &v : values)
    1137             :                 {
    1138          23 :                     const GDALDataset *poDS = v.GetDatasetRef();
    1139          23 :                     if (poDS)
    1140             :                     {
    1141          16 :                         auto poDriver = poDS->GetDriver();
    1142             :                         // The dataset name for a MEM driver is not relevant,
    1143             :                         // so use the pointer address
    1144          32 :                         if ((poDriver &&
    1145          24 :                              EQUAL(poDriver->GetDescription(), "MEM")) ||
    1146           8 :                             poDS->GetDescription()[0] == 0)
    1147             :                         {
    1148           8 :                             aosValues.push_back(CPLSPrintf("%p", poDS));
    1149             :                         }
    1150             :                         else
    1151             :                         {
    1152           8 :                             aosValues.push_back(poDS->GetDescription());
    1153             :                         }
    1154             :                     }
    1155             :                     else
    1156             :                     {
    1157           7 :                         aosValues.push_back(v.GetName());
    1158             :                     }
    1159             :                 }
    1160          11 :                 if (!CheckDuplicateValues(this, aosValues))
    1161           5 :                     ret = false;
    1162             :             }
    1163       11046 :             break;
    1164             :         }
    1165             :     }
    1166             : 
    1167       34931 :     if (GDALAlgorithmArgTypeIsList(GetType()))
    1168             :     {
    1169       14172 :         int valueCount = 0;
    1170       14172 :         if (GetType() == GAAT_STRING_LIST)
    1171             :         {
    1172        2210 :             valueCount =
    1173        2210 :                 static_cast<int>(Get<std::vector<std::string>>().size());
    1174             :         }
    1175       11962 :         else if (GetType() == GAAT_INTEGER_LIST)
    1176             :         {
    1177         344 :             valueCount = static_cast<int>(Get<std::vector<int>>().size());
    1178             :         }
    1179       11618 :         else if (GetType() == GAAT_REAL_LIST)
    1180             :         {
    1181         572 :             valueCount = static_cast<int>(Get<std::vector<double>>().size());
    1182             :         }
    1183       11046 :         else if (GetType() == GAAT_DATASET_LIST)
    1184             :         {
    1185       11046 :             valueCount = static_cast<int>(
    1186       11046 :                 Get<std::vector<GDALArgDatasetValue>>().size());
    1187             :         }
    1188             : 
    1189       14172 :         if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
    1190             :         {
    1191          14 :             ReportError(CE_Failure, CPLE_AppDefined,
    1192             :                         "%d value%s been specified for argument '%s', "
    1193             :                         "whereas exactly %d %s expected.",
    1194             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1195           7 :                         GetName().c_str(), GetMinCount(),
    1196           7 :                         GetMinCount() > 1 ? "were" : "was");
    1197           7 :             ret = false;
    1198             :         }
    1199       14165 :         else if (valueCount < GetMinCount())
    1200             :         {
    1201           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1202             :                         "Only %d value%s been specified for argument '%s', "
    1203             :                         "whereas at least %d %s expected.",
    1204             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1205           3 :                         GetName().c_str(), GetMinCount(),
    1206           3 :                         GetMinCount() > 1 ? "were" : "was");
    1207           3 :             ret = false;
    1208             :         }
    1209       14162 :         else if (valueCount > GetMaxCount())
    1210             :         {
    1211           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    1212             :                         "%d value%s been specified for argument '%s', "
    1213             :                         "whereas at most %d %s expected.",
    1214             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1215           1 :                         GetName().c_str(), GetMaxCount(),
    1216           1 :                         GetMaxCount() > 1 ? "were" : "was");
    1217           1 :             ret = false;
    1218             :         }
    1219             :     }
    1220             : 
    1221       34931 :     if (ret)
    1222             :     {
    1223       41592 :         for (const auto &f : m_validationActions)
    1224             :         {
    1225        6725 :             if (!f())
    1226          92 :                 ret = false;
    1227             :         }
    1228             :     }
    1229             : 
    1230       34931 :     return ret;
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                   GDALAlgorithmArg::ReportError()                    */
    1235             : /************************************************************************/
    1236             : 
    1237          11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1238             :                                    const char *fmt, ...) const
    1239             : {
    1240             :     va_list args;
    1241          11 :     va_start(args, fmt);
    1242          11 :     if (m_owner)
    1243             :     {
    1244          11 :         m_owner->ReportError(eErrClass, err_no, "%s",
    1245          22 :                              CPLString().vPrintf(fmt, args).c_str());
    1246             :     }
    1247             :     else
    1248             :     {
    1249           0 :         CPLError(eErrClass, err_no, "%s",
    1250           0 :                  CPLString().vPrintf(fmt, args).c_str());
    1251             :     }
    1252          11 :     va_end(args);
    1253          11 : }
    1254             : 
    1255             : /************************************************************************/
    1256             : /*                 GDALAlgorithmArg::GetEscapedString()                 */
    1257             : /************************************************************************/
    1258             : 
    1259             : /* static */
    1260         134 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
    1261             : {
    1262         146 :     if (s.find_first_of("\" \\,") != std::string::npos &&
    1263           6 :         !(s.size() > 4 &&
    1264           6 :           s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
    1265           2 :           s[1] == ' ' && s[s.size() - 2] == ' ' &&
    1266           2 :           s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
    1267             :     {
    1268           8 :         return std::string("\"")
    1269             :             .append(
    1270           8 :                 CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
    1271           4 :             .append("\"");
    1272             :     }
    1273             :     else
    1274             :     {
    1275         130 :         return s;
    1276             :     }
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                    GDALAlgorithmArg::Serialize()                     */
    1281             : /************************************************************************/
    1282             : 
    1283          39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
    1284             :                                  bool absolutePath) const
    1285             : {
    1286          39 :     serializedArg.clear();
    1287             : 
    1288          39 :     if (!IsExplicitlySet())
    1289             :     {
    1290           0 :         return false;
    1291             :     }
    1292             : 
    1293          78 :     std::string ret = "--";
    1294          39 :     ret += GetName();
    1295          39 :     if (GetType() == GAAT_BOOLEAN)
    1296             :     {
    1297           0 :         serializedArg = std::move(ret);
    1298           0 :         return true;
    1299             :     }
    1300             : 
    1301           5 :     const auto AddListValueSeparator = [this, &ret]()
    1302             :     {
    1303           1 :         if (GetPackedValuesAllowed())
    1304             :         {
    1305           0 :             ret += ',';
    1306             :         }
    1307             :         else
    1308             :         {
    1309           1 :             ret += " --";
    1310           1 :             ret += GetName();
    1311           1 :             ret += ' ';
    1312             :         }
    1313          40 :     };
    1314             : 
    1315           0 :     const auto MakeAbsolutePath = [](const std::string &filename)
    1316             :     {
    1317             :         VSIStatBufL sStat;
    1318           0 :         if (VSIStatL(filename.c_str(), &sStat) != 0 ||
    1319           0 :             !CPLIsFilenameRelative(filename.c_str()))
    1320           0 :             return filename;
    1321           0 :         char *pszCWD = CPLGetCurrentDir();
    1322           0 :         if (!pszCWD)
    1323           0 :             return filename;
    1324             :         const auto absPath =
    1325           0 :             CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
    1326           0 :         CPLFree(pszCWD);
    1327           0 :         return absPath;
    1328             :     };
    1329             : 
    1330          39 :     ret += ' ';
    1331          39 :     switch (GetType())
    1332             :     {
    1333           0 :         case GAAT_BOOLEAN:
    1334           0 :             break;
    1335           8 :         case GAAT_STRING:
    1336             :         {
    1337           8 :             const auto &val = Get<std::string>();
    1338           8 :             ret += GetEscapedString(val);
    1339           8 :             break;
    1340             :         }
    1341           0 :         case GAAT_INTEGER:
    1342             :         {
    1343           0 :             ret += CPLSPrintf("%d", Get<int>());
    1344           0 :             break;
    1345             :         }
    1346           0 :         case GAAT_REAL:
    1347             :         {
    1348           0 :             ret += CPLSPrintf("%.17g", Get<double>());
    1349           0 :             break;
    1350             :         }
    1351           2 :         case GAAT_DATASET:
    1352             :         {
    1353           2 :             const auto &val = Get<GDALArgDatasetValue>();
    1354           2 :             const auto &str = val.GetName();
    1355           2 :             if (str.empty())
    1356             :             {
    1357           0 :                 return false;
    1358             :             }
    1359           2 :             ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
    1360           2 :             break;
    1361             :         }
    1362           4 :         case GAAT_STRING_LIST:
    1363             :         {
    1364           4 :             const auto &vals = Get<std::vector<std::string>>();
    1365           8 :             for (size_t i = 0; i < vals.size(); ++i)
    1366             :             {
    1367           4 :                 if (i > 0)
    1368           1 :                     AddListValueSeparator();
    1369           4 :                 ret += GetEscapedString(vals[i]);
    1370             :             }
    1371           4 :             break;
    1372             :         }
    1373           0 :         case GAAT_INTEGER_LIST:
    1374             :         {
    1375           0 :             const auto &vals = Get<std::vector<int>>();
    1376           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1377             :             {
    1378           0 :                 if (i > 0)
    1379           0 :                     AddListValueSeparator();
    1380           0 :                 ret += CPLSPrintf("%d", vals[i]);
    1381             :             }
    1382           0 :             break;
    1383             :         }
    1384           0 :         case GAAT_REAL_LIST:
    1385             :         {
    1386           0 :             const auto &vals = Get<std::vector<double>>();
    1387           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1388             :             {
    1389           0 :                 if (i > 0)
    1390           0 :                     AddListValueSeparator();
    1391           0 :                 ret += CPLSPrintf("%.17g", vals[i]);
    1392             :             }
    1393           0 :             break;
    1394             :         }
    1395          25 :         case GAAT_DATASET_LIST:
    1396             :         {
    1397          25 :             const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
    1398          49 :             for (size_t i = 0; i < vals.size(); ++i)
    1399             :             {
    1400          25 :                 if (i > 0)
    1401           0 :                     AddListValueSeparator();
    1402          25 :                 const auto &val = vals[i];
    1403          25 :                 const auto &str = val.GetName();
    1404          25 :                 if (str.empty())
    1405             :                 {
    1406           1 :                     return false;
    1407             :                 }
    1408          48 :                 ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
    1409          24 :                                                      : str);
    1410             :             }
    1411          24 :             break;
    1412             :         }
    1413             :     }
    1414             : 
    1415          38 :     serializedArg = std::move(ret);
    1416          38 :     return true;
    1417             : }
    1418             : 
    1419             : /************************************************************************/
    1420             : /*                  ~GDALInConstructionAlgorithmArg()                   */
    1421             : /************************************************************************/
    1422             : 
    1423             : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
    1424             : 
    1425             : /************************************************************************/
    1426             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
    1427             : /************************************************************************/
    1428             : 
    1429             : GDALInConstructionAlgorithmArg &
    1430       62676 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
    1431             : {
    1432       62676 :     m_decl.AddAlias(alias);
    1433       62676 :     if (m_owner)
    1434       62676 :         m_owner->AddAliasFor(this, alias);
    1435       62676 :     return *this;
    1436             : }
    1437             : 
    1438             : /************************************************************************/
    1439             : /*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
    1440             : /************************************************************************/
    1441             : 
    1442             : GDALInConstructionAlgorithmArg &
    1443       16627 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
    1444             : {
    1445       16627 :     m_decl.AddHiddenAlias(alias);
    1446       16627 :     if (m_owner)
    1447       16627 :         m_owner->AddAliasFor(this, alias);
    1448       16627 :     return *this;
    1449             : }
    1450             : 
    1451             : /************************************************************************/
    1452             : /*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
    1453             : /************************************************************************/
    1454             : 
    1455             : GDALInConstructionAlgorithmArg &
    1456          48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
    1457             : {
    1458          48 :     m_decl.AddShortNameAlias(shortNameAlias);
    1459          48 :     if (m_owner)
    1460          48 :         m_owner->AddShortNameAliasFor(this, shortNameAlias);
    1461          48 :     return *this;
    1462             : }
    1463             : 
    1464             : /************************************************************************/
    1465             : /*           GDALInConstructionAlgorithmArg::SetPositional()            */
    1466             : /************************************************************************/
    1467             : 
    1468       21395 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
    1469             : {
    1470       21395 :     m_decl.SetPositional();
    1471       21395 :     if (m_owner)
    1472       21395 :         m_owner->SetPositional(this);
    1473       21395 :     return *this;
    1474             : }
    1475             : 
    1476             : /************************************************************************/
    1477             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
    1478             : /************************************************************************/
    1479             : 
    1480        1297 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
    1481        2594 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
    1482        1297 :       m_nameSet(true)
    1483             : {
    1484        1297 :     if (m_poDS)
    1485        1297 :         m_poDS->Reference();
    1486        1297 : }
    1487             : 
    1488             : /************************************************************************/
    1489             : /*                      GDALArgDatasetValue::Set()                      */
    1490             : /************************************************************************/
    1491             : 
    1492        2250 : void GDALArgDatasetValue::Set(const std::string &name)
    1493             : {
    1494        2250 :     Close();
    1495        2250 :     m_name = name;
    1496        2250 :     m_nameSet = true;
    1497        2250 :     if (m_ownerArg)
    1498        2245 :         m_ownerArg->NotifyValueSet();
    1499        2250 : }
    1500             : 
    1501             : /************************************************************************/
    1502             : /*                      GDALArgDatasetValue::Set()                      */
    1503             : /************************************************************************/
    1504             : 
    1505        1954 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
    1506             : {
    1507        1954 :     Close();
    1508        1954 :     m_poDS = poDS.release();
    1509        1954 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1510        1954 :     m_nameSet = true;
    1511        1954 :     if (m_ownerArg)
    1512        1814 :         m_ownerArg->NotifyValueSet();
    1513        1954 : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                      GDALArgDatasetValue::Set()                      */
    1517             : /************************************************************************/
    1518             : 
    1519        7748 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
    1520             : {
    1521        7748 :     Close();
    1522        7748 :     m_poDS = poDS;
    1523        7748 :     if (m_poDS)
    1524        6904 :         m_poDS->Reference();
    1525        7748 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1526        7748 :     m_nameSet = true;
    1527        7748 :     if (m_ownerArg)
    1528        3255 :         m_ownerArg->NotifyValueSet();
    1529        7748 : }
    1530             : 
    1531             : /************************************************************************/
    1532             : /*                    GDALArgDatasetValue::SetFrom()                    */
    1533             : /************************************************************************/
    1534             : 
    1535        3221 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
    1536             : {
    1537        3221 :     Close();
    1538        3221 :     m_name = other.m_name;
    1539        3221 :     m_nameSet = other.m_nameSet;
    1540        3221 :     m_poDS = other.m_poDS;
    1541        3221 :     if (m_poDS)
    1542        2260 :         m_poDS->Reference();
    1543        3221 : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
    1547             : /************************************************************************/
    1548             : 
    1549       30942 : GDALArgDatasetValue::~GDALArgDatasetValue()
    1550             : {
    1551       30942 :     Close();
    1552       30942 : }
    1553             : 
    1554             : /************************************************************************/
    1555             : /*                     GDALArgDatasetValue::Close()                     */
    1556             : /************************************************************************/
    1557             : 
    1558       51173 : bool GDALArgDatasetValue::Close()
    1559             : {
    1560       51173 :     bool ret = true;
    1561       51173 :     if (m_poDS && m_poDS->Dereference() == 0)
    1562             :     {
    1563        3235 :         ret = m_poDS->Close() == CE_None;
    1564        3235 :         delete m_poDS;
    1565             :     }
    1566       51173 :     m_poDS = nullptr;
    1567       51173 :     return ret;
    1568             : }
    1569             : 
    1570             : /************************************************************************/
    1571             : /*                   GDALArgDatasetValue::operator=()                   */
    1572             : /************************************************************************/
    1573             : 
    1574           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
    1575             : {
    1576           2 :     Close();
    1577           2 :     m_poDS = other.m_poDS;
    1578           2 :     m_name = other.m_name;
    1579           2 :     m_nameSet = other.m_nameSet;
    1580           2 :     other.m_poDS = nullptr;
    1581           2 :     other.m_name.clear();
    1582           2 :     other.m_nameSet = false;
    1583           2 :     return *this;
    1584             : }
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                  GDALArgDatasetValue::GetDataset()                   */
    1588             : /************************************************************************/
    1589             : 
    1590        1046 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
    1591             : {
    1592        1046 :     if (m_poDS)
    1593        1046 :         m_poDS->Reference();
    1594        1046 :     return m_poDS;
    1595             : }
    1596             : 
    1597             : /************************************************************************/
    1598             : /*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
    1599             : /************************************************************************/
    1600             : 
    1601        3059 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
    1602        3059 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
    1603             : {
    1604        3059 :     other.m_poDS = nullptr;
    1605        3059 :     other.m_name.clear();
    1606        3059 : }
    1607             : 
    1608             : /************************************************************************/
    1609             : /*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
    1610             : /************************************************************************/
    1611             : 
    1612        3278 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
    1613             :     bool noneAllowed, const std::vector<std::string> &specialValues)
    1614             : {
    1615        3278 :     if (GetType() != GAAT_STRING)
    1616             :     {
    1617           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1618             :                  "SetIsCRSArg() can only be called on a String argument");
    1619           1 :         return *this;
    1620             :     }
    1621             :     AddValidationAction(
    1622         707 :         [this, noneAllowed, specialValues]()
    1623             :         {
    1624             :             const std::string &osVal =
    1625             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
    1626         350 :                     ->Get<std::string>();
    1627         350 :             if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
    1628           0 :                 return true;
    1629             : 
    1630         687 :             if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
    1631         337 :                 std::find(specialValues.begin(), specialValues.end(), osVal) ==
    1632         687 :                     specialValues.end())
    1633             :             {
    1634         329 :                 OGRSpatialReference oSRS;
    1635         329 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
    1636             :                 {
    1637           7 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
    1638             :                                          "Invalid value for '%s' argument",
    1639           7 :                                          GetName().c_str());
    1640           7 :                     return false;
    1641             :                 }
    1642             :             }
    1643         343 :             return true;
    1644        3277 :         });
    1645             : 
    1646             :     SetAutoCompleteFunction(
    1647          40 :         [this, noneAllowed, specialValues](const std::string &currentValue)
    1648             :         {
    1649          10 :             bool bIsRaster = false;
    1650          10 :             OGREnvelope sDatasetLongLatEnv;
    1651          20 :             std::string osCelestialBodyName;
    1652          10 :             if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
    1653             :             {
    1654          10 :                 auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
    1655          10 :                 if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    1656             :                 {
    1657             :                     auto &val =
    1658          10 :                         inputArg->Get<std::vector<GDALArgDatasetValue>>();
    1659          10 :                     if (val.size() == 1)
    1660             :                     {
    1661           4 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1662             :                         auto poDS = std::unique_ptr<GDALDataset>(
    1663           4 :                             GDALDataset::Open(val[0].GetName().c_str()));
    1664           2 :                         if (poDS)
    1665             :                         {
    1666           2 :                             bIsRaster = poDS->GetRasterCount() != 0;
    1667           2 :                             if (auto poCRS = poDS->GetSpatialRef())
    1668             :                             {
    1669             :                                 const char *pszCelestialBodyName =
    1670           2 :                                     poCRS->GetCelestialBodyName();
    1671           2 :                                 if (pszCelestialBodyName)
    1672           2 :                                     osCelestialBodyName = pszCelestialBodyName;
    1673             : 
    1674           2 :                                 if (!pszCelestialBodyName ||
    1675           2 :                                     !EQUAL(pszCelestialBodyName, "Earth"))
    1676             :                                 {
    1677           0 :                                     OGRSpatialReference oLongLat;
    1678           0 :                                     oLongLat.CopyGeogCSFrom(poCRS);
    1679           0 :                                     oLongLat.SetAxisMappingStrategy(
    1680             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    1681           0 :                                     poDS->GetExtent(&sDatasetLongLatEnv,
    1682           0 :                                                     &oLongLat);
    1683             :                                 }
    1684             :                                 else
    1685             :                                 {
    1686           2 :                                     poDS->GetExtentWGS84LongLat(
    1687           2 :                                         &sDatasetLongLatEnv);
    1688             :                                 }
    1689             :                             }
    1690             :                         }
    1691             :                     }
    1692             :                 }
    1693             :             }
    1694             : 
    1695             :             const auto IsCRSCompatible =
    1696       42959 :                 [bIsRaster, &sDatasetLongLatEnv,
    1697       73695 :                  &osCelestialBodyName](const OSRCRSInfo *crsInfo)
    1698             :             {
    1699       42959 :                 if (!sDatasetLongLatEnv.IsInit())
    1700       30685 :                     return true;
    1701       24108 :                 return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
    1702       11834 :                        !(bIsRaster &&
    1703        5917 :                          crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
    1704       11652 :                        crsInfo->dfWestLongitudeDeg <
    1705       11652 :                            crsInfo->dfEastLongitudeDeg &&
    1706       11517 :                        sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
    1707        5618 :                        sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
    1708         615 :                        sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
    1709       24437 :                        sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
    1710         329 :                        ((!osCelestialBodyName.empty() &&
    1711         658 :                          crsInfo->pszCelestialBodyName &&
    1712         329 :                          osCelestialBodyName ==
    1713         329 :                              crsInfo->pszCelestialBodyName) ||
    1714           0 :                         (osCelestialBodyName.empty() &&
    1715       12274 :                          !crsInfo->pszCelestialBodyName));
    1716          10 :             };
    1717             : 
    1718          10 :             std::vector<std::string> oRet;
    1719          10 :             if (noneAllowed)
    1720           0 :                 oRet.push_back("none");
    1721          10 :             oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
    1722          10 :             if (!currentValue.empty())
    1723             :             {
    1724             :                 const CPLStringList aosTokens(
    1725          14 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
    1726           7 :                 int nCount = 0;
    1727             :                 std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
    1728             :                     pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
    1729             :                                                            nullptr, &nCount),
    1730          14 :                              OSRDestroyCRSInfoList);
    1731          14 :                 std::string osCode;
    1732             : 
    1733          14 :                 std::vector<const OSRCRSInfo *> candidates;
    1734       46270 :                 for (int i = 0; i < nCount; ++i)
    1735             :                 {
    1736       46263 :                     const auto *entry = (pCRSList.get())[i];
    1737       46263 :                     if (!entry->bDeprecated && IsCRSCompatible(entry))
    1738             :                     {
    1739       49425 :                         if (aosTokens.size() == 1 ||
    1740       18411 :                             STARTS_WITH(entry->pszCode, aosTokens[1]))
    1741             :                         {
    1742       12666 :                             if (candidates.empty())
    1743           7 :                                 osCode = entry->pszCode;
    1744       12666 :                             candidates.push_back(entry);
    1745             :                         }
    1746             :                     }
    1747             :                 }
    1748           7 :                 if (candidates.size() == 1)
    1749             :                 {
    1750           1 :                     oRet.push_back(std::move(osCode));
    1751             :                 }
    1752             :                 else
    1753             :                 {
    1754           6 :                     if (sDatasetLongLatEnv.IsInit())
    1755             :                     {
    1756           2 :                         std::sort(
    1757             :                             candidates.begin(), candidates.end(),
    1758        2999 :                             [](const OSRCRSInfo *a, const OSRCRSInfo *b)
    1759             :                             {
    1760        2999 :                                 const double dfXa =
    1761        2999 :                                     a->dfWestLongitudeDeg >
    1762        2999 :                                             a->dfEastLongitudeDeg
    1763        2999 :                                         ? a->dfWestLongitudeDeg -
    1764           0 :                                               a->dfEastLongitudeDeg
    1765        2999 :                                         : (180 - a->dfWestLongitudeDeg) +
    1766        2999 :                                               (a->dfEastLongitudeDeg - -180);
    1767        2999 :                                 const double dfYa = a->dfNorthLatitudeDeg -
    1768        2999 :                                                     a->dfSouthLatitudeDeg;
    1769        2999 :                                 const double dfXb =
    1770        2999 :                                     b->dfWestLongitudeDeg >
    1771        2999 :                                             b->dfEastLongitudeDeg
    1772        2999 :                                         ? b->dfWestLongitudeDeg -
    1773           0 :                                               b->dfEastLongitudeDeg
    1774        2999 :                                         : (180 - b->dfWestLongitudeDeg) +
    1775        2999 :                                               (b->dfEastLongitudeDeg - -180);
    1776        2999 :                                 const double dfYb = b->dfNorthLatitudeDeg -
    1777        2999 :                                                     b->dfSouthLatitudeDeg;
    1778        2999 :                                 const double diffArea =
    1779        2999 :                                     dfXa * dfYa - dfXb * dfYb;
    1780        2999 :                                 if (diffArea < 0)
    1781         279 :                                     return true;
    1782        2720 :                                 if (diffArea == 0)
    1783             :                                 {
    1784        2506 :                                     if (std::string_view(a->pszName) ==
    1785        2506 :                                         b->pszName)
    1786             :                                     {
    1787          57 :                                         if (a->eType ==
    1788          13 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D &&
    1789          13 :                                             b->eType !=
    1790             :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1791          13 :                                             return true;
    1792          44 :                                         if (a->eType ==
    1793          32 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_3D &&
    1794          32 :                                             b->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1795           9 :                                             return true;
    1796          35 :                                         return false;
    1797             :                                     }
    1798        4898 :                                     return std::string_view(a->pszCode) <
    1799        4898 :                                            b->pszCode;
    1800             :                                 }
    1801         214 :                                 return false;
    1802             :                             });
    1803             :                     }
    1804             : 
    1805       12671 :                     for (const auto *entry : candidates)
    1806             :                     {
    1807       25330 :                         std::string val = std::string(entry->pszCode)
    1808       12665 :                                               .append(" -- ")
    1809       25330 :                                               .append(entry->pszName);
    1810       12665 :                         if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1811        1294 :                             val.append(" (geographic 2D)");
    1812       11371 :                         else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
    1813         446 :                             val.append(" (geographic 3D)");
    1814       10925 :                         else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1815         397 :                             val.append(" (geocentric)");
    1816       12665 :                         oRet.push_back(std::move(val));
    1817             :                     }
    1818             :                 }
    1819             :             }
    1820          10 :             if (currentValue.empty() || oRet.empty())
    1821             :             {
    1822             :                 const CPLStringList aosAuthorities(
    1823           6 :                     OSRGetAuthorityListFromDatabase());
    1824          18 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
    1825             :                 {
    1826          15 :                     int nCount = 0;
    1827          15 :                     OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
    1828             :                         pszAuth, nullptr, &nCount));
    1829          15 :                     if (nCount)
    1830          12 :                         oRet.push_back(std::string(pszAuth).append(":"));
    1831             :                 }
    1832             :             }
    1833          20 :             return oRet;
    1834        3277 :         });
    1835             : 
    1836        3277 :     return *this;
    1837             : }
    1838             : 
    1839             : /************************************************************************/
    1840             : /*                    GDALAlgorithm::GDALAlgorithm()                    */
    1841             : /************************************************************************/
    1842             : 
    1843       21448 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
    1844             :                              const std::string &description,
    1845       21448 :                              const std::string &helpURL)
    1846             :     : m_name(name), m_description(description), m_helpURL(helpURL),
    1847       42736 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
    1848       21448 :                         ? "https://gdal.org" + m_helpURL
    1849       63929 :                         : m_helpURL)
    1850             : {
    1851             :     auto &helpArg =
    1852             :         AddArg("help", 'h', _("Display help message and exit"),
    1853       42896 :                &m_helpRequested)
    1854       21448 :             .SetHiddenForAPI()
    1855       42896 :             .SetCategory(GAAC_COMMON)
    1856          14 :             .AddAction([this]()
    1857       21448 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1858             :     auto &helpDocArg =
    1859             :         AddArg("help-doc", 0,
    1860             :                _("Display help message for use by documentation"),
    1861       42896 :                &m_helpDocRequested)
    1862       21448 :             .SetHidden()
    1863          12 :             .AddAction([this]()
    1864       21448 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1865             :     auto &jsonUsageArg =
    1866             :         AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
    1867       42896 :                &m_JSONUsageRequested)
    1868       21448 :             .SetHiddenForAPI()
    1869       42896 :             .SetCategory(GAAC_COMMON)
    1870           4 :             .AddAction([this]()
    1871       21448 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1872       42896 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
    1873       42896 :         .SetMetaVar("<KEY>=<VALUE>")
    1874       21448 :         .SetHiddenForAPI()
    1875       42896 :         .SetCategory(GAAC_COMMON)
    1876             :         .AddAction(
    1877           2 :             [this]()
    1878             :             {
    1879           2 :                 ReportError(
    1880             :                     CE_Warning, CPLE_AppDefined,
    1881             :                     "Configuration options passed with the 'config' argument "
    1882             :                     "are ignored");
    1883       21448 :             });
    1884             : 
    1885       21448 :     AddValidationAction(
    1886       12895 :         [this, &helpArg, &helpDocArg, &jsonUsageArg]()
    1887             :         {
    1888        6644 :             if (!m_calledFromCommandLine && m_specialActionRequested)
    1889             :             {
    1890           0 :                 for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
    1891             :                 {
    1892           0 :                     if (arg->IsExplicitlySet())
    1893             :                     {
    1894           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    1895             :                                     "'%s' argument only available when called "
    1896             :                                     "from command line",
    1897           0 :                                     arg->GetName().c_str());
    1898           0 :                         return false;
    1899             :                     }
    1900             :                 }
    1901             :             }
    1902        6644 :             return true;
    1903             :         });
    1904       21448 : }
    1905             : 
    1906             : /************************************************************************/
    1907             : /*                   GDALAlgorithm::~GDALAlgorithm()                    */
    1908             : /************************************************************************/
    1909             : 
    1910             : GDALAlgorithm::~GDALAlgorithm() = default;
    1911             : 
    1912             : /************************************************************************/
    1913             : /*                    GDALAlgorithm::ParseArgument()                    */
    1914             : /************************************************************************/
    1915             : 
    1916        3099 : bool GDALAlgorithm::ParseArgument(
    1917             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
    1918             :     std::map<
    1919             :         GDALAlgorithmArg *,
    1920             :         std::variant<std::vector<std::string>, std::vector<int>,
    1921             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1922             :         &inConstructionValues)
    1923             : {
    1924             :     const bool isListArg =
    1925        3099 :         GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
    1926        3099 :     if (arg->IsExplicitlySet() && !isListArg)
    1927             :     {
    1928             :         // Hack for "gdal info" to be able to pass an opened raster dataset
    1929             :         // by "gdal raster info" to the "gdal vector info" algorithm.
    1930           4 :         if (arg->SkipIfAlreadySet())
    1931             :         {
    1932           1 :             arg->SetSkipIfAlreadySet(false);
    1933           1 :             return true;
    1934             :         }
    1935             : 
    1936           3 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1937             :                     "Argument '%s' has already been specified.", name.c_str());
    1938           3 :         return false;
    1939             :     }
    1940             : 
    1941        3163 :     if (!arg->GetRepeatedArgAllowed() &&
    1942          68 :         cpl::contains(inConstructionValues, arg))
    1943             :     {
    1944           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1945             :                     "Argument '%s' has already been specified.", name.c_str());
    1946           1 :         return false;
    1947             :     }
    1948             : 
    1949        3094 :     switch (arg->GetType())
    1950             :     {
    1951         320 :         case GAAT_BOOLEAN:
    1952             :         {
    1953         320 :             if (value.empty() || value == "true")
    1954         318 :                 return arg->Set(true);
    1955           2 :             else if (value == "false")
    1956           1 :                 return arg->Set(false);
    1957             :             else
    1958             :             {
    1959           1 :                 ReportError(
    1960             :                     CE_Failure, CPLE_IllegalArg,
    1961             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
    1962             :                     "'true' or 'false'.",
    1963             :                     value.c_str(), name.c_str());
    1964           1 :                 return false;
    1965             :             }
    1966             :         }
    1967             : 
    1968         767 :         case GAAT_STRING:
    1969             :         {
    1970         767 :             return arg->Set(value);
    1971             :         }
    1972             : 
    1973         356 :         case GAAT_INTEGER:
    1974             :         {
    1975         356 :             errno = 0;
    1976         356 :             char *endptr = nullptr;
    1977         356 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
    1978         355 :             if (errno == 0 && endptr &&
    1979         711 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
    1980             :                 val <= INT_MAX)
    1981             :             {
    1982         353 :                 return arg->Set(static_cast<int>(val));
    1983             :             }
    1984             :             else
    1985             :             {
    1986           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    1987             :                             "Expected integer value for argument '%s', "
    1988             :                             "but got '%s'.",
    1989             :                             name.c_str(), value.c_str());
    1990           3 :                 return false;
    1991             :             }
    1992             :         }
    1993             : 
    1994          32 :         case GAAT_REAL:
    1995             :         {
    1996          32 :             char *endptr = nullptr;
    1997          32 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
    1998          32 :             if (endptr != value.c_str() + value.size())
    1999             :             {
    2000           1 :                 ReportError(
    2001             :                     CE_Failure, CPLE_IllegalArg,
    2002             :                     "Expected real value for argument '%s', but got '%s'.",
    2003             :                     name.c_str(), value.c_str());
    2004           1 :                 return false;
    2005             :             }
    2006          31 :             return arg->Set(dfValue);
    2007             :         }
    2008             : 
    2009         540 :         case GAAT_DATASET:
    2010             :         {
    2011         540 :             return arg->SetDatasetName(value);
    2012             :         }
    2013             : 
    2014         268 :         case GAAT_STRING_LIST:
    2015             :         {
    2016             :             const CPLStringList aosTokens(
    2017         268 :                 arg->GetPackedValuesAllowed()
    2018         171 :                     ? CSLTokenizeString2(value.c_str(), ",",
    2019             :                                          CSLT_HONOURSTRINGS |
    2020             :                                              CSLT_PRESERVEQUOTES)
    2021         439 :                     : CSLAddString(nullptr, value.c_str()));
    2022         268 :             if (!cpl::contains(inConstructionValues, arg))
    2023             :             {
    2024         244 :                 inConstructionValues[arg] = std::vector<std::string>();
    2025             :             }
    2026             :             auto &valueVector =
    2027         268 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
    2028         568 :             for (const char *v : aosTokens)
    2029             :             {
    2030         300 :                 valueVector.push_back(v);
    2031             :             }
    2032         268 :             if (arg->GetMaxCount() == 1)
    2033             :             {
    2034           3 :                 bool ret = arg->Set(std::move(valueVector));
    2035           3 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2036           3 :                 return ret;
    2037             :             }
    2038             : 
    2039         265 :             break;
    2040             :         }
    2041             : 
    2042          65 :         case GAAT_INTEGER_LIST:
    2043             :         {
    2044             :             const CPLStringList aosTokens(
    2045          65 :                 arg->GetPackedValuesAllowed()
    2046          65 :                     ? CSLTokenizeString2(
    2047             :                           value.c_str(), ",",
    2048             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2049             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2050         130 :                     : CSLAddString(nullptr, value.c_str()));
    2051          65 :             if (!cpl::contains(inConstructionValues, arg))
    2052             :             {
    2053          61 :                 inConstructionValues[arg] = std::vector<int>();
    2054             :             }
    2055             :             auto &valueVector =
    2056          65 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    2057         199 :             for (const char *v : aosTokens)
    2058             :             {
    2059         140 :                 errno = 0;
    2060         140 :                 char *endptr = nullptr;
    2061         140 :                 const auto val = std::strtol(v, &endptr, 10);
    2062         140 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    2063         136 :                     val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
    2064             :                 {
    2065         134 :                     valueVector.push_back(static_cast<int>(val));
    2066             :                 }
    2067             :                 else
    2068             :                 {
    2069           6 :                     ReportError(
    2070             :                         CE_Failure, CPLE_IllegalArg,
    2071             :                         "Expected list of integer value for argument '%s', "
    2072             :                         "but got '%s'.",
    2073             :                         name.c_str(), value.c_str());
    2074           6 :                     return false;
    2075             :                 }
    2076             :             }
    2077          59 :             if (arg->GetMaxCount() == 1)
    2078             :             {
    2079           2 :                 bool ret = arg->Set(std::move(valueVector));
    2080           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2081           2 :                 return ret;
    2082             :             }
    2083             : 
    2084          57 :             break;
    2085             :         }
    2086             : 
    2087         102 :         case GAAT_REAL_LIST:
    2088             :         {
    2089             :             const CPLStringList aosTokens(
    2090         102 :                 arg->GetPackedValuesAllowed()
    2091         102 :                     ? CSLTokenizeString2(
    2092             :                           value.c_str(), ",",
    2093             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2094             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2095         204 :                     : CSLAddString(nullptr, value.c_str()));
    2096         102 :             if (!cpl::contains(inConstructionValues, arg))
    2097             :             {
    2098         100 :                 inConstructionValues[arg] = std::vector<double>();
    2099             :             }
    2100             :             auto &valueVector =
    2101         102 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    2102         410 :             for (const char *v : aosTokens)
    2103             :             {
    2104         312 :                 char *endptr = nullptr;
    2105         312 :                 double dfValue = CPLStrtod(v, &endptr);
    2106         312 :                 if (strlen(v) == 0 || endptr != v + strlen(v))
    2107             :                 {
    2108           4 :                     ReportError(
    2109             :                         CE_Failure, CPLE_IllegalArg,
    2110             :                         "Expected list of real value for argument '%s', "
    2111             :                         "but got '%s'.",
    2112             :                         name.c_str(), value.c_str());
    2113           4 :                     return false;
    2114             :                 }
    2115         308 :                 valueVector.push_back(dfValue);
    2116             :             }
    2117          98 :             if (arg->GetMaxCount() == 1)
    2118             :             {
    2119           2 :                 bool ret = arg->Set(std::move(valueVector));
    2120           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2121           2 :                 return ret;
    2122             :             }
    2123             : 
    2124          96 :             break;
    2125             :         }
    2126             : 
    2127         644 :         case GAAT_DATASET_LIST:
    2128             :         {
    2129         644 :             if (!cpl::contains(inConstructionValues, arg))
    2130             :             {
    2131         631 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    2132             :             }
    2133             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    2134         644 :                 inConstructionValues[arg]);
    2135         644 :             if (!value.empty() && value[0] == '{' && value.back() == '}')
    2136             :             {
    2137          12 :                 valueVector.push_back(GDALArgDatasetValue(value));
    2138             :             }
    2139             :             else
    2140             :             {
    2141             :                 const CPLStringList aosTokens(
    2142         632 :                     arg->GetPackedValuesAllowed()
    2143           6 :                         ? CSLTokenizeString2(value.c_str(), ",",
    2144             :                                              CSLT_HONOURSTRINGS |
    2145             :                                                  CSLT_STRIPLEADSPACES)
    2146        1270 :                         : CSLAddString(nullptr, value.c_str()));
    2147        1267 :                 for (const char *v : aosTokens)
    2148             :                 {
    2149         635 :                     valueVector.push_back(GDALArgDatasetValue(v));
    2150             :                 }
    2151             :             }
    2152         644 :             if (arg->GetMaxCount() == 1)
    2153             :             {
    2154         523 :                 bool ret = arg->Set(std::move(valueVector));
    2155         523 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2156         523 :                 return ret;
    2157             :             }
    2158             : 
    2159         121 :             break;
    2160             :         }
    2161             :     }
    2162             : 
    2163         539 :     return true;
    2164             : }
    2165             : 
    2166             : /************************************************************************/
    2167             : /*              GDALAlgorithm::ParseCommandLineArguments()              */
    2168             : /************************************************************************/
    2169             : 
    2170        2057 : bool GDALAlgorithm::ParseCommandLineArguments(
    2171             :     const std::vector<std::string> &args)
    2172             : {
    2173        2057 :     if (m_parsedSubStringAlreadyCalled)
    2174             :     {
    2175           6 :         ReportError(CE_Failure, CPLE_AppDefined,
    2176             :                     "ParseCommandLineArguments() can only be called once per "
    2177             :                     "instance.");
    2178           6 :         return false;
    2179             :     }
    2180        2051 :     m_parsedSubStringAlreadyCalled = true;
    2181             : 
    2182             :     // AWS like syntax supported too (not advertized)
    2183        2051 :     if (args.size() == 1 && args[0] == "help")
    2184             :     {
    2185           1 :         auto arg = GetArg("help");
    2186           1 :         assert(arg);
    2187           1 :         arg->Set(true);
    2188           1 :         arg->RunActions();
    2189           1 :         return true;
    2190             :     }
    2191             : 
    2192        2050 :     if (HasSubAlgorithms())
    2193             :     {
    2194         445 :         if (args.empty())
    2195             :         {
    2196           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    2197           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    2198           2 :             return false;
    2199             :         }
    2200         443 :         if (!args[0].empty() && args[0][0] == '-')
    2201             :         {
    2202             :             // go on argument parsing
    2203             :         }
    2204             :         else
    2205             :         {
    2206         440 :             const auto nCounter = CPLGetErrorCounter();
    2207         440 :             m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
    2208         440 :             if (m_selectedSubAlgHolder)
    2209             :             {
    2210         437 :                 m_selectedSubAlg = m_selectedSubAlgHolder.get();
    2211         437 :                 m_selectedSubAlg->SetReferencePathForRelativePaths(
    2212         437 :                     m_referencePath);
    2213         437 :                 m_selectedSubAlg->m_executionForStreamOutput =
    2214         437 :                     m_executionForStreamOutput;
    2215         437 :                 m_selectedSubAlg->m_calledFromCommandLine =
    2216         437 :                     m_calledFromCommandLine;
    2217         437 :                 m_selectedSubAlg->m_skipValidationInParseCommandLine =
    2218         437 :                     m_skipValidationInParseCommandLine;
    2219         437 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    2220         874 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    2221         437 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    2222         437 :                 return bRet;
    2223             :             }
    2224             :             else
    2225             :             {
    2226           4 :                 if (!(CPLGetErrorCounter() == nCounter + 1 &&
    2227           1 :                       strstr(CPLGetLastErrorMsg(), "Do you mean")))
    2228             :                 {
    2229           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2230           2 :                                 "Unknown command: '%s'", args[0].c_str());
    2231             :                 }
    2232           3 :                 return false;
    2233             :             }
    2234             :         }
    2235             :     }
    2236             : 
    2237             :     std::map<
    2238             :         GDALAlgorithmArg *,
    2239             :         std::variant<std::vector<std::string>, std::vector<int>,
    2240             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    2241        3216 :         inConstructionValues;
    2242             : 
    2243        3216 :     std::vector<std::string> lArgs(args);
    2244        1608 :     bool helpValueRequested = false;
    2245        4711 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    2246             :     {
    2247        3203 :         const auto &strArg = lArgs[i];
    2248        3203 :         GDALAlgorithmArg *arg = nullptr;
    2249        3203 :         std::string name;
    2250        3203 :         std::string value;
    2251        3203 :         bool hasValue = false;
    2252        3203 :         if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
    2253           5 :             helpValueRequested = true;
    2254        3203 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    2255             :         {
    2256        2044 :             const auto equalPos = strArg.find('=');
    2257        4088 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    2258        2044 :                                                    : strArg;
    2259        2044 :             const std::string nameWithoutDash = name.substr(2);
    2260        2044 :             auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2261        2097 :             if (m_arbitraryLongNameArgsAllowed &&
    2262        2097 :                 iterArg == m_mapLongNameToArg.end())
    2263             :             {
    2264          16 :                 GetArg(nameWithoutDash);
    2265          16 :                 iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2266             :             }
    2267        2044 :             if (iterArg == m_mapLongNameToArg.end())
    2268             :             {
    2269             :                 const std::string bestCandidate =
    2270          26 :                     GetSuggestionForArgumentName(nameWithoutDash);
    2271          26 :                 if (!bestCandidate.empty())
    2272             :                 {
    2273           2 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2274             :                                 "Option '%s' is unknown. Do you mean '--%s'?",
    2275             :                                 name.c_str(), bestCandidate.c_str());
    2276             :                 }
    2277             :                 else
    2278             :                 {
    2279          24 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2280             :                                 "Option '%s' is unknown.", name.c_str());
    2281             :                 }
    2282          26 :                 return false;
    2283             :             }
    2284        2018 :             arg = iterArg->second;
    2285        2018 :             if (equalPos != std::string::npos)
    2286             :             {
    2287         441 :                 hasValue = true;
    2288         441 :                 value = strArg.substr(equalPos + 1);
    2289             :             }
    2290             :         }
    2291        1233 :         else if (strArg.size() >= 2 && strArg[0] == '-' &&
    2292          74 :                  CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
    2293             :         {
    2294         143 :             for (size_t j = 1; j < strArg.size(); ++j)
    2295             :             {
    2296          74 :                 name.clear();
    2297          74 :                 name += strArg[j];
    2298          74 :                 const auto iterArg = m_mapShortNameToArg.find(name);
    2299          74 :                 if (iterArg == m_mapShortNameToArg.end())
    2300             :                 {
    2301           5 :                     const std::string nameWithoutDash = strArg.substr(1);
    2302           5 :                     if (m_mapLongNameToArg.find(nameWithoutDash) !=
    2303          10 :                         m_mapLongNameToArg.end())
    2304             :                     {
    2305           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2306             :                                     "Short name option '%s' is unknown. Do you "
    2307             :                                     "mean '--%s' (with leading double dash) ?",
    2308             :                                     name.c_str(), nameWithoutDash.c_str());
    2309             :                     }
    2310             :                     else
    2311             :                     {
    2312             :                         const std::string bestCandidate =
    2313           8 :                             GetSuggestionForArgumentName(nameWithoutDash);
    2314           4 :                         if (!bestCandidate.empty())
    2315             :                         {
    2316           1 :                             ReportError(
    2317             :                                 CE_Failure, CPLE_IllegalArg,
    2318             :                                 "Short name option '%s' is unknown. Do you "
    2319             :                                 "mean '--%s' (with leading double dash) ?",
    2320             :                                 name.c_str(), bestCandidate.c_str());
    2321             :                         }
    2322             :                         else
    2323             :                         {
    2324           3 :                             ReportError(CE_Failure, CPLE_IllegalArg,
    2325             :                                         "Short name option '%s' is unknown.",
    2326             :                                         name.c_str());
    2327             :                         }
    2328             :                     }
    2329           5 :                     return false;
    2330             :                 }
    2331          69 :                 arg = iterArg->second;
    2332          69 :                 if (strArg.size() > 2)
    2333             :                 {
    2334           0 :                     if (arg->GetType() != GAAT_BOOLEAN)
    2335             :                     {
    2336           0 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2337             :                                     "Invalid argument '%s'. Option '%s' is not "
    2338             :                                     "a boolean option.",
    2339             :                                     strArg.c_str(), name.c_str());
    2340           0 :                         return false;
    2341             :                     }
    2342             : 
    2343           0 :                     if (!ParseArgument(arg, name, "true", inConstructionValues))
    2344           0 :                         return false;
    2345             :                 }
    2346             :             }
    2347          69 :             if (strArg.size() > 2)
    2348             :             {
    2349           0 :                 lArgs.erase(lArgs.begin() + i);
    2350           0 :                 continue;
    2351             :             }
    2352             :         }
    2353             :         else
    2354             :         {
    2355        1085 :             ++i;
    2356        1085 :             continue;
    2357             :         }
    2358        2087 :         CPLAssert(arg);
    2359             : 
    2360        2087 :         if (arg && arg->GetType() == GAAT_BOOLEAN)
    2361             :         {
    2362         321 :             if (!hasValue)
    2363             :             {
    2364         318 :                 hasValue = true;
    2365         318 :                 value = "true";
    2366             :             }
    2367             :         }
    2368             : 
    2369        2087 :         if (!hasValue)
    2370             :         {
    2371        1328 :             if (i + 1 == lArgs.size())
    2372             :             {
    2373          30 :                 if (m_parseForAutoCompletion)
    2374             :                 {
    2375          24 :                     lArgs.erase(lArgs.begin() + i);
    2376          24 :                     break;
    2377             :                 }
    2378           6 :                 ReportError(
    2379             :                     CE_Failure, CPLE_IllegalArg,
    2380             :                     "Expected value for argument '%s', but ran short of tokens",
    2381             :                     name.c_str());
    2382           6 :                 return false;
    2383             :             }
    2384        1298 :             value = lArgs[i + 1];
    2385        1298 :             lArgs.erase(lArgs.begin() + i + 1);
    2386             :         }
    2387             : 
    2388        2057 :         if (arg && !ParseArgument(arg, name, value, inConstructionValues))
    2389          39 :             return false;
    2390             : 
    2391        2018 :         lArgs.erase(lArgs.begin() + i);
    2392             :     }
    2393             : 
    2394        1532 :     if (m_specialActionRequested)
    2395             :     {
    2396          23 :         return true;
    2397             :     }
    2398             : 
    2399        1978 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    2400             :     {
    2401        1944 :         for (auto &[arg, value] : inConstructionValues)
    2402             :         {
    2403         493 :             if (arg->GetType() == GAAT_STRING_LIST)
    2404             :             {
    2405         237 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    2406         237 :                         inConstructionValues[arg])))
    2407             :                 {
    2408          34 :                     return false;
    2409             :                 }
    2410             :             }
    2411         256 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    2412             :             {
    2413          54 :                 if (!arg->Set(
    2414          54 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    2415             :                 {
    2416           4 :                     return false;
    2417             :                 }
    2418             :             }
    2419         202 :             else if (arg->GetType() == GAAT_REAL_LIST)
    2420             :             {
    2421          94 :                 if (!arg->Set(std::get<std::vector<double>>(
    2422          94 :                         inConstructionValues[arg])))
    2423             :                 {
    2424          10 :                     return false;
    2425             :                 }
    2426             :             }
    2427         108 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    2428             :             {
    2429         108 :                 if (!arg->Set(
    2430             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    2431         108 :                             inConstructionValues[arg]))))
    2432             :                 {
    2433           2 :                     return false;
    2434             :                 }
    2435             :             }
    2436             :         }
    2437        1451 :         return true;
    2438        1509 :     };
    2439             : 
    2440             :     // Process positional arguments that have not been set through their
    2441             :     // option name.
    2442        1509 :     size_t i = 0;
    2443        1509 :     size_t iCurPosArg = 0;
    2444             : 
    2445             :     // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
    2446        1532 :     if (m_positionalArgs.size() == 3 &&
    2447          24 :         (m_positionalArgs[0]->IsRequired() ||
    2448          23 :          m_positionalArgs[0]->GetMinCount() == 1) &&
    2449          44 :         m_positionalArgs[0]->GetMaxCount() == 1 &&
    2450          29 :         (m_positionalArgs[1]->IsRequired() ||
    2451          29 :          m_positionalArgs[1]->GetMinCount() == 1) &&
    2452             :         /* Second argument may have several occurrences */
    2453          44 :         m_positionalArgs[1]->GetMaxCount() >= 1 &&
    2454          22 :         (m_positionalArgs[2]->IsRequired() ||
    2455          22 :          m_positionalArgs[2]->GetMinCount() == 1) &&
    2456          22 :         m_positionalArgs[2]->GetMaxCount() == 1 &&
    2457           9 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2458        1541 :         !m_positionalArgs[1]->IsExplicitlySet() &&
    2459           9 :         !m_positionalArgs[2]->IsExplicitlySet())
    2460             :     {
    2461           7 :         if (lArgs.size() - i < 3)
    2462             :         {
    2463           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    2464             :                         "Not enough positional values.");
    2465           1 :             return false;
    2466             :         }
    2467          12 :         bool ok = ParseArgument(m_positionalArgs[0],
    2468           6 :                                 m_positionalArgs[0]->GetName().c_str(),
    2469           6 :                                 lArgs[i], inConstructionValues);
    2470           6 :         if (ok)
    2471             :         {
    2472           5 :             ++i;
    2473          11 :             for (; i + 1 < lArgs.size() && ok; ++i)
    2474             :             {
    2475          12 :                 ok = ParseArgument(m_positionalArgs[1],
    2476           6 :                                    m_positionalArgs[1]->GetName().c_str(),
    2477           6 :                                    lArgs[i], inConstructionValues);
    2478             :             }
    2479             :         }
    2480           6 :         if (ok)
    2481             :         {
    2482          10 :             ok = ParseArgument(m_positionalArgs[2],
    2483          10 :                                m_positionalArgs[2]->GetName().c_str(), lArgs[i],
    2484             :                                inConstructionValues);
    2485           5 :             ++i;
    2486             :         }
    2487           6 :         if (!ok)
    2488             :         {
    2489           3 :             ProcessInConstructionValues();
    2490           3 :             return false;
    2491             :         }
    2492             :     }
    2493             : 
    2494         508 :     if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
    2495         587 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2496        2326 :         m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
    2497         106 :         (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
    2498          53 :          m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
    2499             :     {
    2500          53 :         ++iCurPosArg;
    2501             :     }
    2502             : 
    2503        2505 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    2504             :     {
    2505        1007 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    2506        1015 :         while (arg->IsExplicitlySet())
    2507             :         {
    2508           9 :             ++iCurPosArg;
    2509           9 :             if (iCurPosArg == m_positionalArgs.size())
    2510           1 :                 break;
    2511           8 :             arg = m_positionalArgs[iCurPosArg];
    2512             :         }
    2513        1007 :         if (iCurPosArg == m_positionalArgs.size())
    2514             :         {
    2515           1 :             break;
    2516             :         }
    2517        1550 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2518         544 :             arg->GetMinCount() != arg->GetMaxCount())
    2519             :         {
    2520         109 :             if (iCurPosArg == 0)
    2521             :             {
    2522          73 :                 size_t nCountAtEnd = 0;
    2523         102 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    2524             :                 {
    2525          31 :                     const auto *otherArg = m_positionalArgs[j];
    2526          31 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    2527             :                     {
    2528           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    2529             :                         {
    2530           2 :                             ReportError(
    2531             :                                 CE_Failure, CPLE_AppDefined,
    2532             :                                 "Ambiguity in definition of positional "
    2533             :                                 "argument "
    2534             :                                 "'%s' given it has a varying number of values, "
    2535             :                                 "but follows argument '%s' which also has a "
    2536             :                                 "varying number of values",
    2537           1 :                                 otherArg->GetName().c_str(),
    2538           1 :                                 arg->GetName().c_str());
    2539           1 :                             ProcessInConstructionValues();
    2540           1 :                             return false;
    2541             :                         }
    2542           3 :                         nCountAtEnd += otherArg->GetMinCount();
    2543             :                     }
    2544             :                     else
    2545             :                     {
    2546          27 :                         if (!otherArg->IsRequired())
    2547             :                         {
    2548           2 :                             ReportError(
    2549             :                                 CE_Failure, CPLE_AppDefined,
    2550             :                                 "Ambiguity in definition of positional "
    2551             :                                 "argument "
    2552             :                                 "'%s', given it is not required but follows "
    2553             :                                 "argument '%s' which has a varying number of "
    2554             :                                 "values",
    2555           1 :                                 otherArg->GetName().c_str(),
    2556           1 :                                 arg->GetName().c_str());
    2557           1 :                             ProcessInConstructionValues();
    2558           1 :                             return false;
    2559             :                         }
    2560          26 :                         nCountAtEnd++;
    2561             :                     }
    2562             :                 }
    2563          71 :                 if (lArgs.size() < nCountAtEnd)
    2564             :                 {
    2565           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2566             :                                 "Not enough positional values.");
    2567           1 :                     ProcessInConstructionValues();
    2568           1 :                     return false;
    2569             :                 }
    2570         148 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    2571             :                 {
    2572          78 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2573             :                                        inConstructionValues))
    2574             :                     {
    2575           0 :                         ProcessInConstructionValues();
    2576           0 :                         return false;
    2577             :                     }
    2578             :                 }
    2579             :             }
    2580          36 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    2581             :             {
    2582          82 :                 for (; i < lArgs.size(); ++i)
    2583             :                 {
    2584          47 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2585             :                                        inConstructionValues))
    2586             :                     {
    2587           0 :                         ProcessInConstructionValues();
    2588           0 :                         return false;
    2589             :                     }
    2590             :                 }
    2591             :             }
    2592             :             else
    2593             :             {
    2594           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2595             :                             "Ambiguity in definition of positional arguments: "
    2596             :                             "arguments with varying number of values must be "
    2597             :                             "first or last one.");
    2598           1 :                 return false;
    2599             :             }
    2600             :         }
    2601             :         else
    2602             :         {
    2603         897 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    2604             :             {
    2605           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2606             :                             "Not enough positional values.");
    2607           1 :                 return false;
    2608             :             }
    2609         896 :             const size_t iMax = i + arg->GetMaxCount();
    2610        1795 :             for (; i < iMax; ++i)
    2611             :             {
    2612         900 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2613             :                                    inConstructionValues))
    2614             :                 {
    2615           1 :                     ProcessInConstructionValues();
    2616           1 :                     return false;
    2617             :                 }
    2618             :             }
    2619             :         }
    2620        1000 :         ++iCurPosArg;
    2621             :     }
    2622             : 
    2623        1499 :     if (i < lArgs.size())
    2624             :     {
    2625          21 :         ReportError(CE_Failure, CPLE_AppDefined,
    2626             :                     "Positional values starting at '%s' are not expected.",
    2627          21 :                     lArgs[i].c_str());
    2628          21 :         return false;
    2629             :     }
    2630             : 
    2631        1478 :     if (!ProcessInConstructionValues())
    2632             :     {
    2633          33 :         return false;
    2634             :     }
    2635             : 
    2636             :     // Skip to first unset positional argument.
    2637        2474 :     while (iCurPosArg < m_positionalArgs.size() &&
    2638         555 :            m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    2639             :     {
    2640         474 :         ++iCurPosArg;
    2641             :     }
    2642             :     // Check if this positional argument is required.
    2643        1525 :     if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
    2644          80 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    2645          48 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    2646          32 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    2647             :     {
    2648          69 :         ReportError(CE_Failure, CPLE_AppDefined,
    2649             :                     "Positional arguments starting at '%s' have not been "
    2650             :                     "specified.",
    2651          69 :                     m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    2652          69 :         return false;
    2653             :     }
    2654             : 
    2655        1376 :     if (m_calledFromCommandLine)
    2656             :     {
    2657        4837 :         for (auto &arg : m_args)
    2658             :         {
    2659        6356 :             if (arg->IsExplicitlySet() &&
    2660        1021 :                 ((arg->GetType() == GAAT_STRING &&
    2661        1018 :                   arg->Get<std::string>() == "?") ||
    2662         936 :                  (arg->GetType() == GAAT_STRING_LIST &&
    2663         157 :                   arg->Get<std::vector<std::string>>().size() == 1 &&
    2664          78 :                   arg->Get<std::vector<std::string>>()[0] == "?")))
    2665             :             {
    2666             :                 {
    2667          10 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2668           5 :                     ValidateArguments();
    2669             :                 }
    2670             : 
    2671           5 :                 auto choices = arg->GetChoices();
    2672           5 :                 if (choices.empty())
    2673           2 :                     choices = arg->GetAutoCompleteChoices(std::string());
    2674           5 :                 if (!choices.empty())
    2675             :                 {
    2676           5 :                     if (choices.size() == 1)
    2677             :                     {
    2678           4 :                         ReportError(
    2679             :                             CE_Failure, CPLE_AppDefined,
    2680             :                             "Single potential value for argument '%s' is '%s'",
    2681           4 :                             arg->GetName().c_str(), choices.front().c_str());
    2682             :                     }
    2683             :                     else
    2684             :                     {
    2685           6 :                         std::string msg("Potential values for argument '");
    2686           3 :                         msg += arg->GetName();
    2687           3 :                         msg += "' are:";
    2688          45 :                         for (const auto &v : choices)
    2689             :                         {
    2690          42 :                             msg += "\n- ";
    2691          42 :                             msg += v;
    2692             :                         }
    2693           3 :                         ReportError(CE_Failure, CPLE_AppDefined, "%s",
    2694             :                                     msg.c_str());
    2695             :                     }
    2696           5 :                     return false;
    2697             :                 }
    2698             :             }
    2699             :         }
    2700             :     }
    2701             : 
    2702        1371 :     return m_skipValidationInParseCommandLine || ValidateArguments();
    2703             : }
    2704             : 
    2705             : /************************************************************************/
    2706             : /*                     GDALAlgorithm::ReportError()                     */
    2707             : /************************************************************************/
    2708             : 
    2709             : //! @cond Doxygen_Suppress
    2710         883 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    2711             :                                 const char *fmt, ...) const
    2712             : {
    2713             :     va_list args;
    2714         883 :     va_start(args, fmt);
    2715         883 :     CPLError(eErrClass, err_no, "%s",
    2716         883 :              std::string(m_name)
    2717         883 :                  .append(": ")
    2718        1766 :                  .append(CPLString().vPrintf(fmt, args))
    2719             :                  .c_str());
    2720         883 :     va_end(args);
    2721         883 : }
    2722             : 
    2723             : //! @endcond
    2724             : 
    2725             : /************************************************************************/
    2726             : /*                  GDALAlgorithm::ProcessDatasetArg()                  */
    2727             : /************************************************************************/
    2728             : 
    2729        9724 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    2730             :                                       GDALAlgorithm *algForOutput)
    2731             : {
    2732        9724 :     bool ret = true;
    2733             : 
    2734        9724 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    2735        9724 :     const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
    2736        9724 :     const bool update = hasUpdateArg && updateArg->Get<bool>();
    2737             : 
    2738        9724 :     const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
    2739        9724 :     const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
    2740        9724 :     const bool append = hasAppendArg && appendArg->Get<bool>();
    2741             : 
    2742        9724 :     const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
    2743             :     const bool overwrite =
    2744       16057 :         (arg->IsOutput() && overwriteArg &&
    2745       16057 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    2746             : 
    2747        9724 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    2748       19448 :     auto &val = [arg]() -> GDALArgDatasetValue &
    2749             :     {
    2750        9724 :         if (arg->GetType() == GAAT_DATASET_LIST)
    2751        5537 :             return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
    2752             :         else
    2753        4187 :             return arg->Get<GDALArgDatasetValue>();
    2754        9724 :     }();
    2755             :     const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
    2756       15360 :         arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
    2757       15368 :         !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
    2758           8 :         !overwrite;
    2759             : 
    2760             :     // Used for nested pipelines
    2761             :     const auto oIterDatasetNameToDataset =
    2762       19445 :         val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
    2763        9724 :                         : m_oMapDatasetNameToDataset.end();
    2764             : 
    2765        9724 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    2766             :     {
    2767           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    2768             :                     "Argument '%s' has no dataset object or dataset name.",
    2769           3 :                     arg->GetName().c_str());
    2770           3 :         ret = false;
    2771             :     }
    2772        9721 :     else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
    2773             :     {
    2774           3 :         return false;
    2775             :     }
    2776         217 :     else if (m_inputDatasetCanBeOmitted &&
    2777        9935 :              val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
    2778           8 :              !arg->IsOutput())
    2779             :     {
    2780           8 :         return true;
    2781             :     }
    2782       14386 :     else if (!val.GetDatasetRef() &&
    2783        4976 :              (arg->AutoOpenDataset() ||
    2784       14686 :               oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
    2785        4376 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
    2786             :               onlyInputSpecifiedInUpdateAndOutputNotRequired))
    2787             :     {
    2788        1334 :         int flags = arg->GetDatasetType();
    2789        1334 :         bool assignToOutputArg = false;
    2790             : 
    2791             :         // Check if input and output parameters point to the same
    2792             :         // filename (for vector datasets)
    2793        2466 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    2794        2466 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    2795             :         {
    2796          62 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    2797         121 :             if (!outputVal.GetDatasetRef() &&
    2798         121 :                 outputVal.GetName() == val.GetName() &&
    2799           2 :                 (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
    2800             :             {
    2801           2 :                 assignToOutputArg = true;
    2802           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2803             :             }
    2804          60 :             else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
    2805             :             {
    2806           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2807             :             }
    2808             :         }
    2809             : 
    2810        1334 :         if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
    2811        1251 :             flags |= GDAL_OF_VERBOSE_ERROR;
    2812        1334 :         if ((arg == outputArg || !outputArg) && update)
    2813             :         {
    2814          85 :             flags |= GDAL_OF_UPDATE;
    2815          85 :             if (!append)
    2816          64 :                 flags |= GDAL_OF_VERBOSE_ERROR;
    2817             :         }
    2818             : 
    2819        1334 :         const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
    2820             :         const bool readOnly =
    2821        1377 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    2822          43 :              readOnlyArg->Get<bool>());
    2823        1334 :         if (readOnly)
    2824          12 :             flags &= ~GDAL_OF_UPDATE;
    2825             : 
    2826        2668 :         CPLStringList aosOpenOptions;
    2827        2668 :         CPLStringList aosAllowedDrivers;
    2828        1334 :         if (arg->IsInput())
    2829             :         {
    2830        1334 :             if (arg == outputArg)
    2831             :             {
    2832          83 :                 if (update && !overwrite)
    2833             :                 {
    2834          83 :                     const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
    2835          83 :                     if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2836          31 :                         aosOpenOptions = CPLStringList(
    2837          31 :                             ooArg->Get<std::vector<std::string>>());
    2838             :                 }
    2839             :             }
    2840             :             else
    2841             :             {
    2842        1251 :                 const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    2843        1251 :                 if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2844             :                     aosOpenOptions =
    2845        1200 :                         CPLStringList(ooArg->Get<std::vector<std::string>>());
    2846             : 
    2847        1251 :                 const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2848        1251 :                 if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    2849             :                     aosAllowedDrivers =
    2850        1157 :                         CPLStringList(ifArg->Get<std::vector<std::string>>());
    2851             :             }
    2852             :         }
    2853             : 
    2854        2668 :         std::string osDatasetName = val.GetName();
    2855        1334 :         if (!m_referencePath.empty())
    2856             :         {
    2857          42 :             osDatasetName = GDALDataset::BuildFilename(
    2858          21 :                 osDatasetName.c_str(), m_referencePath.c_str(), true);
    2859             :         }
    2860        1334 :         if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
    2861           0 :             osDatasetName = "/vsistdin/";
    2862             : 
    2863             :         // Handle special case of overview delete in GTiff which would fail
    2864             :         // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
    2865         142 :         if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
    2866        1478 :             m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
    2867           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
    2868             :         {
    2869           4 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2870             :             GDALDriverH hDrv =
    2871           2 :                 GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
    2872           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
    2873             :             {
    2874             :                 // Cleaning does not break COG layout
    2875           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
    2876             :             }
    2877             :         }
    2878             : 
    2879             :         GDALDataset *poDS;
    2880             :         {
    2881             :             // The PostGISRaster may emit an error message, that is not
    2882             :             // relevant, if it is the vector driver that was intended
    2883        1334 :             std::unique_ptr<CPLErrorStateBackuper> poBackuper;
    2884        1334 :             if (cpl::starts_with(osDatasetName, "PG:") &&
    2885           0 :                 (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
    2886             :             {
    2887           0 :                 poBackuper = std::make_unique<CPLErrorStateBackuper>(
    2888           0 :                     CPLQuietErrorHandler);
    2889             :             }
    2890             : 
    2891        1334 :             CPL_IGNORE_RET_VAL(poBackuper);
    2892        1334 :             poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
    2893        1334 :                        ? oIterDatasetNameToDataset->second
    2894        1331 :                        : GDALDataset::Open(osDatasetName.c_str(), flags,
    2895        1331 :                                            aosAllowedDrivers.List(),
    2896        1331 :                                            aosOpenOptions.List());
    2897             : 
    2898             :             // Retry with PostGIS vector driver
    2899          59 :             if (!poDS && poBackuper &&
    2900           0 :                 GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
    2901        1393 :                 aosAllowedDrivers.empty() && aosOpenOptions.empty())
    2902             :             {
    2903           0 :                 poBackuper.reset();
    2904           0 :                 poDS = GDALDataset::Open(
    2905           0 :                     osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
    2906           0 :                     aosAllowedDrivers.List(), aosOpenOptions.List());
    2907             :             }
    2908             :         }
    2909             : 
    2910        1334 :         if (poDS)
    2911             :         {
    2912        1275 :             if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
    2913             :             {
    2914           3 :                 if (arg->GetType() == GAAT_DATASET)
    2915           3 :                     arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
    2916           3 :                 poDS->Reference();
    2917           3 :                 m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
    2918             :             }
    2919             : 
    2920             :             // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
    2921             :             // where the PG: dataset will be first opened with the PostGISRaster
    2922             :             // driver whereas the PostgreSQL (vector) one is actually wanted.
    2923        1739 :             if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
    2924        1829 :                 (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
    2925          90 :                 aosOpenOptions.empty())
    2926             :             {
    2927          86 :                 auto poDrv = poDS->GetDriver();
    2928          86 :                 if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
    2929             :                 {
    2930             :                     // Retry with PostgreSQL (vector) driver
    2931             :                     std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
    2932           0 :                         osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
    2933           0 :                     if (poTmpDS)
    2934             :                     {
    2935           0 :                         poDS->ReleaseRef();
    2936           0 :                         poDS = poTmpDS.release();
    2937             :                     }
    2938             :                 }
    2939             :             }
    2940             : 
    2941        1275 :             if (assignToOutputArg)
    2942             :             {
    2943             :                 // Avoid opening twice the same datasource if it is both
    2944             :                 // the input and output.
    2945             :                 // Known to cause problems with at least FGdb, SQLite
    2946             :                 // and GPKG drivers. See #4270
    2947             :                 // Restrict to those 3 drivers. For example it is known
    2948             :                 // to break with the PG driver due to the way it
    2949             :                 // manages transactions.
    2950           2 :                 auto poDriver = poDS->GetDriver();
    2951           4 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    2952           2 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    2953           2 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    2954             :                 {
    2955           2 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    2956             :                 }
    2957             :             }
    2958        1275 :             val.SetDatasetOpenedByAlgorithm();
    2959        1275 :             val.Set(poDS);
    2960        1275 :             poDS->ReleaseRef();
    2961             :         }
    2962          59 :         else if (!append)
    2963             :         {
    2964          57 :             ret = false;
    2965             :         }
    2966             :     }
    2967             : 
    2968             :     // Deal with overwriting the output dataset
    2969        9713 :     if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
    2970             :     {
    2971        3044 :         if (!append)
    2972             :         {
    2973             :             // If outputting to MEM, do not try to erase a real file of the same name!
    2974             :             const auto outputFormatArg =
    2975        3032 :                 algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    2976        9058 :             if (!(outputFormatArg &&
    2977        3013 :                   outputFormatArg->GetType() == GAAT_STRING &&
    2978        3013 :                   (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    2979        1952 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    2980        1026 :                          "stream") ||
    2981        1026 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    2982             :                          "Memory"))))
    2983             :             {
    2984        1045 :                 const char *pszType = "";
    2985        1045 :                 GDALDriver *poDriver = nullptr;
    2986        2044 :                 if (!val.GetName().empty() &&
    2987         999 :                     GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
    2988             :                                                &poDriver))
    2989             :                 {
    2990          78 :                     if (!overwrite)
    2991             :                     {
    2992          68 :                         std::string options;
    2993          34 :                         if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
    2994             :                         {
    2995          11 :                             options += "--";
    2996          11 :                             options += GDAL_ARG_NAME_OVERWRITE_LAYER;
    2997             :                         }
    2998          34 :                         if (hasAppendArg)
    2999             :                         {
    3000          22 :                             if (!options.empty())
    3001           8 :                                 options += '/';
    3002          22 :                             options += "--";
    3003          22 :                             options += GDAL_ARG_NAME_APPEND;
    3004             :                         }
    3005          34 :                         if (hasUpdateArg)
    3006             :                         {
    3007          15 :                             if (!options.empty())
    3008          12 :                                 options += '/';
    3009          15 :                             options += "--";
    3010          15 :                             options += GDAL_ARG_NAME_UPDATE;
    3011             :                         }
    3012             : 
    3013          34 :                         if (poDriver)
    3014             :                         {
    3015          68 :                             const char *pszPrefix = poDriver->GetMetadataItem(
    3016          34 :                                 GDAL_DMD_CONNECTION_PREFIX);
    3017          34 :                             if (pszPrefix &&
    3018           0 :                                 STARTS_WITH_CI(val.GetName().c_str(),
    3019             :                                                pszPrefix))
    3020             :                             {
    3021           0 :                                 bool bExists = false;
    3022             :                                 {
    3023             :                                     CPLErrorStateBackuper oBackuper(
    3024           0 :                                         CPLQuietErrorHandler);
    3025           0 :                                     bExists = std::unique_ptr<GDALDataset>(
    3026             :                                                   GDALDataset::Open(
    3027           0 :                                                       val.GetName().c_str())) !=
    3028             :                                               nullptr;
    3029             :                                 }
    3030           0 :                                 if (bExists)
    3031             :                                 {
    3032           0 :                                     if (!options.empty())
    3033           0 :                                         options = " You may specify the " +
    3034           0 :                                                   options + " option.";
    3035           0 :                                     ReportError(CE_Failure, CPLE_AppDefined,
    3036             :                                                 "%s '%s' already exists.%s",
    3037           0 :                                                 pszType, val.GetName().c_str(),
    3038             :                                                 options.c_str());
    3039           0 :                                     return false;
    3040             :                                 }
    3041             : 
    3042           0 :                                 return true;
    3043             :                             }
    3044             :                         }
    3045             : 
    3046          34 :                         if (!options.empty())
    3047          28 :                             options = '/' + options;
    3048          68 :                         ReportError(
    3049             :                             CE_Failure, CPLE_AppDefined,
    3050             :                             "%s '%s' already exists. You may specify the "
    3051             :                             "--overwrite%s option.",
    3052          34 :                             pszType, val.GetName().c_str(), options.c_str());
    3053          34 :                         return false;
    3054             :                     }
    3055          44 :                     else if (EQUAL(pszType, "File"))
    3056             :                     {
    3057           1 :                         if (VSIUnlink(val.GetName().c_str()) != 0)
    3058             :                         {
    3059           0 :                             ReportError(CE_Failure, CPLE_AppDefined,
    3060             :                                         "Deleting %s failed: %s",
    3061           0 :                                         val.GetName().c_str(),
    3062           0 :                                         VSIStrerror(errno));
    3063           0 :                             return false;
    3064             :                         }
    3065             :                     }
    3066          43 :                     else if (EQUAL(pszType, "Directory"))
    3067             :                     {
    3068             :                         // We don't want the user to accidentally erase a non-GDAL dataset
    3069           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3070             :                                     "Directory '%s' already exists, but is not "
    3071             :                                     "recognized as a valid GDAL dataset. "
    3072             :                                     "Please manually delete it before retrying",
    3073           1 :                                     val.GetName().c_str());
    3074           1 :                         return false;
    3075             :                     }
    3076          42 :                     else if (poDriver)
    3077             :                     {
    3078             :                         bool bDeleteOK;
    3079             :                         {
    3080             :                             CPLErrorStateBackuper oBackuper(
    3081          42 :                                 CPLQuietErrorHandler);
    3082          42 :                             bDeleteOK = (poDriver->Delete(
    3083          42 :                                              val.GetName().c_str()) == CE_None);
    3084             :                         }
    3085             :                         VSIStatBufL sStat;
    3086          45 :                         if (!bDeleteOK &&
    3087           3 :                             VSIStatL(val.GetName().c_str(), &sStat) == 0)
    3088             :                         {
    3089           3 :                             if (VSI_ISDIR(sStat.st_mode))
    3090             :                             {
    3091             :                                 // We don't want the user to accidentally erase a non-GDAL dataset
    3092           0 :                                 ReportError(
    3093             :                                     CE_Failure, CPLE_AppDefined,
    3094             :                                     "Directory '%s' already exists, but is not "
    3095             :                                     "recognized as a valid GDAL dataset. "
    3096             :                                     "Please manually delete it before retrying",
    3097           0 :                                     val.GetName().c_str());
    3098           2 :                                 return false;
    3099             :                             }
    3100           3 :                             else if (VSIUnlink(val.GetName().c_str()) != 0)
    3101             :                             {
    3102           2 :                                 ReportError(CE_Failure, CPLE_AppDefined,
    3103             :                                             "Deleting %s failed: %s",
    3104           2 :                                             val.GetName().c_str(),
    3105           2 :                                             VSIStrerror(errno));
    3106           2 :                                 return false;
    3107             :                             }
    3108             :                         }
    3109             :                     }
    3110             :                 }
    3111             :             }
    3112             :         }
    3113             :     }
    3114             : 
    3115             :     // If outputting to stdout, automatically turn off progress bar
    3116        9676 :     if (arg == outputArg && val.GetName() == "/vsistdout/")
    3117             :     {
    3118           8 :         auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
    3119           8 :         if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
    3120           5 :             quietArg->Set(true);
    3121             :     }
    3122             : 
    3123        9676 :     return ret;
    3124             : }
    3125             : 
    3126             : /************************************************************************/
    3127             : /*                  GDALAlgorithm::ValidateArguments()                  */
    3128             : /************************************************************************/
    3129             : 
    3130        6648 : bool GDALAlgorithm::ValidateArguments()
    3131             : {
    3132        6648 :     if (m_selectedSubAlg)
    3133           3 :         return m_selectedSubAlg->ValidateArguments();
    3134             : 
    3135        6645 :     if (m_specialActionRequested)
    3136           1 :         return true;
    3137             : 
    3138        6644 :     m_arbitraryLongNameArgsAllowed = false;
    3139             : 
    3140             :     // If only --output=format=MEM/stream is specified and not --output,
    3141             :     // then set empty name for --output.
    3142        6644 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    3143        6644 :     auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3144        3940 :     if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
    3145        2638 :         !outputArg->IsExplicitlySet() &&
    3146         358 :         outputFormatArg->GetType() == GAAT_STRING &&
    3147         358 :         (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3148         588 :          EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
    3149       10917 :         outputArg->GetType() == GAAT_DATASET &&
    3150         333 :         (outputArg->GetDatasetInputFlags() & GADV_NAME))
    3151             :     {
    3152         333 :         outputArg->Get<GDALArgDatasetValue>().Set("");
    3153             :     }
    3154             : 
    3155             :     // The method may emit several errors if several constraints are not met.
    3156        6644 :     bool ret = true;
    3157       13288 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    3158       13288 :     std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
    3159      125431 :     for (auto &arg : m_args)
    3160             :     {
    3161             :         // Check mutually exclusive/dependent arguments
    3162      118787 :         if (arg->IsExplicitlySet())
    3163             :         {
    3164             : 
    3165       19142 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3166       19142 :             if (!mutualExclusionGroup.empty())
    3167             :             {
    3168             :                 auto oIter =
    3169         759 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    3170         759 :                 if (oIter != mutualExclusionGroupUsed.end())
    3171             :                 {
    3172          13 :                     ret = false;
    3173          26 :                     ReportError(
    3174             :                         CE_Failure, CPLE_AppDefined,
    3175             :                         "Argument '%s' is mutually exclusive with '%s'.",
    3176          26 :                         arg->GetName().c_str(), oIter->second.c_str());
    3177             :                 }
    3178             :                 else
    3179             :                 {
    3180         746 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    3181        1492 :                         arg->GetName();
    3182             :                 }
    3183             :             }
    3184             : 
    3185       19142 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3186       19142 :             if (!mutualDependencyGroup.empty())
    3187             :             {
    3188          50 :                 if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
    3189         100 :                     mutualDependencyGroupUsed.end())
    3190             :                 {
    3191          87 :                     mutualDependencyGroupUsed[mutualDependencyGroup] = {
    3192          87 :                         arg->GetName()};
    3193             :                 }
    3194             :                 else
    3195             :                 {
    3196          42 :                     mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
    3197          21 :                         arg->GetName());
    3198             :                 }
    3199             :             }
    3200             : 
    3201             :             // Check direct dependencies
    3202       19151 :             for (const auto &dependency : arg->GetDirectDependencies())
    3203             :             {
    3204           9 :                 auto depArg = GetArg(dependency);
    3205           9 :                 if (!depArg)
    3206             :                 {
    3207           0 :                     ret = false;
    3208           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3209             :                                 "Argument '%s' depends on argument '%s' that "
    3210             :                                 "is not defined.",
    3211           0 :                                 arg->GetName().c_str(), dependency.c_str());
    3212             :                 }
    3213           9 :                 else if (!depArg->IsExplicitlySet())
    3214             :                 {
    3215           5 :                     ret = false;
    3216          10 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3217             :                                 "Argument '%s' depends on argument '%s' that "
    3218             :                                 "has not been specified.",
    3219           5 :                                 arg->GetName().c_str(),
    3220           5 :                                 depArg->GetName().c_str());
    3221             :                 }
    3222             :             }
    3223             :         }
    3224             : 
    3225      118941 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    3226         154 :             !arg->HasDefaultValue())
    3227             :         {
    3228         154 :             bool emitError = true;
    3229         154 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3230         154 :             if (!mutualExclusionGroup.empty())
    3231             :             {
    3232        1765 :                 for (const auto &otherArg : m_args)
    3233             :                 {
    3234        1751 :                     if (otherArg->GetMutualExclusionGroup() ==
    3235        1856 :                             mutualExclusionGroup &&
    3236         105 :                         otherArg->IsExplicitlySet())
    3237             :                     {
    3238          74 :                         emitError = false;
    3239          74 :                         break;
    3240             :                     }
    3241             :                 }
    3242             :             }
    3243         232 :             if (emitError && !(m_inputDatasetCanBeOmitted &&
    3244          48 :                                arg->GetName() == GDAL_ARG_NAME_INPUT &&
    3245          60 :                                (arg->GetType() == GAAT_DATASET ||
    3246          30 :                                 arg->GetType() == GAAT_DATASET_LIST)))
    3247             :             {
    3248          50 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3249             :                             "Required argument '%s' has not been specified.",
    3250          50 :                             arg->GetName().c_str());
    3251          50 :                 ret = false;
    3252             :             }
    3253             :         }
    3254      118633 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    3255             :         {
    3256        4187 :             if (!ProcessDatasetArg(arg.get(), this))
    3257          51 :                 ret = false;
    3258             :         }
    3259             : 
    3260      118787 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
    3261             :         {
    3262        5411 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    3263        5411 :             if (listVal.size() == 1)
    3264             :             {
    3265        5257 :                 if (!ProcessDatasetArg(arg.get(), this))
    3266          39 :                     ret = false;
    3267             :             }
    3268             :             else
    3269             :             {
    3270         473 :                 for (auto &val : listVal)
    3271             :                 {
    3272         319 :                     if (val.GetDatasetRef())
    3273             :                     {
    3274         120 :                         if (!CheckCanSetDatasetObject(arg.get()))
    3275             :                         {
    3276           0 :                             ret = false;
    3277             :                         }
    3278         315 :                         continue;
    3279             :                     }
    3280             : 
    3281         199 :                     if (val.GetName().empty())
    3282             :                     {
    3283           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3284             :                                     "Argument '%s' has no dataset object or "
    3285             :                                     "dataset name.",
    3286           0 :                                     arg->GetName().c_str());
    3287           0 :                         ret = false;
    3288           0 :                         continue;
    3289             :                     }
    3290             : 
    3291         199 :                     auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
    3292         199 :                     if (oIter != m_oMapDatasetNameToDataset.end())
    3293             :                     {
    3294           2 :                         auto poDS = oIter->second;
    3295           2 :                         val.SetDatasetOpenedByAlgorithm();
    3296           2 :                         val.Set(poDS);
    3297           2 :                         m_oMapDatasetNameToDataset.erase(oIter);
    3298           2 :                         continue;
    3299             :                     }
    3300             : 
    3301         197 :                     if (!arg->AutoOpenDataset())
    3302         193 :                         continue;
    3303             : 
    3304           4 :                     int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
    3305             : 
    3306           8 :                     CPLStringList aosOpenOptions;
    3307           8 :                     CPLStringList aosAllowedDrivers;
    3308           4 :                     if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    3309             :                     {
    3310           4 :                         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    3311           4 :                         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    3312             :                         {
    3313           4 :                             aosOpenOptions = CPLStringList(
    3314           4 :                                 ooArg->Get<std::vector<std::string>>());
    3315             :                         }
    3316             : 
    3317           4 :                         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    3318           4 :                         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    3319             :                         {
    3320           4 :                             aosAllowedDrivers = CPLStringList(
    3321           4 :                                 ifArg->Get<std::vector<std::string>>());
    3322             :                         }
    3323             : 
    3324           4 :                         const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3325           4 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    3326           0 :                             updateArg->Get<bool>())
    3327             :                         {
    3328           0 :                             flags |= GDAL_OF_UPDATE;
    3329             :                         }
    3330             :                     }
    3331             : 
    3332             :                     auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    3333           4 :                         val.GetName().c_str(), flags, aosAllowedDrivers.List(),
    3334          12 :                         aosOpenOptions.List()));
    3335           4 :                     if (poDS)
    3336             :                     {
    3337           3 :                         val.Set(std::move(poDS));
    3338             :                     }
    3339             :                     else
    3340             :                     {
    3341           1 :                         ret = false;
    3342             :                     }
    3343             :                 }
    3344             :             }
    3345             :         }
    3346             : 
    3347      118787 :         if (arg->IsExplicitlySet() && !arg->RunValidationActions())
    3348             :         {
    3349           8 :             ret = false;
    3350             :         }
    3351             :     }
    3352             : 
    3353             :     // Check mutual dependency groups
    3354        6644 :     std::vector<std::string> processedGroups;
    3355             :     // Loop through group map and check there are not required args in the group that are not set
    3356        6673 :     for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
    3357             :     {
    3358          29 :         if (std::find(processedGroups.begin(), processedGroups.end(),
    3359          29 :                       groupName) != processedGroups.end())
    3360           0 :             continue;
    3361          58 :         std::vector<std::string> missingArgs;
    3362         562 :         for (auto &arg : m_args)
    3363             :         {
    3364         533 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3365         598 :             if (mutualDependencyGroup == groupName &&
    3366          65 :                 std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
    3367         598 :                     argNames.end())
    3368             :             {
    3369          15 :                 missingArgs.push_back(arg->GetName());
    3370             :             }
    3371             :         }
    3372          29 :         if (!missingArgs.empty())
    3373             :         {
    3374          12 :             ret = false;
    3375          24 :             std::string missingArgsStr;
    3376          27 :             for (const auto &missingArg : missingArgs)
    3377             :             {
    3378          15 :                 if (!missingArgsStr.empty())
    3379           3 :                     missingArgsStr += ", ";
    3380          15 :                 missingArgsStr += missingArg;
    3381             :             }
    3382          24 :             std::string givenArgsStr;
    3383          27 :             for (const auto &givenArg : argNames)
    3384             :             {
    3385          15 :                 if (!givenArgsStr.empty())
    3386           3 :                     givenArgsStr += ", ";
    3387          15 :                 givenArgsStr += givenArg;
    3388             :             }
    3389          12 :             ReportError(CE_Failure, CPLE_AppDefined,
    3390             :                         "Argument(s) '%s' require(s) that the following "
    3391             :                         "argument(s) are also specified: %s.",
    3392             :                         givenArgsStr.c_str(), missingArgsStr.c_str());
    3393             :         }
    3394          29 :         processedGroups.push_back(groupName);
    3395             :     }
    3396             : 
    3397       28430 :     for (const auto &f : m_validationActions)
    3398             :     {
    3399       21786 :         if (!f())
    3400          80 :             ret = false;
    3401             :     }
    3402             : 
    3403        6644 :     return ret;
    3404             : }
    3405             : 
    3406             : /************************************************************************/
    3407             : /*                GDALAlgorithm::InstantiateSubAlgorithm                */
    3408             : /************************************************************************/
    3409             : 
    3410             : std::unique_ptr<GDALAlgorithm>
    3411        9933 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
    3412             :                                        bool suggestionAllowed) const
    3413             : {
    3414        9933 :     auto ret = m_subAlgRegistry.Instantiate(name);
    3415       19866 :     auto childCallPath = m_callPath;
    3416        9933 :     childCallPath.push_back(name);
    3417        9933 :     if (!ret)
    3418             :     {
    3419         916 :         ret = GDALGlobalAlgorithmRegistry::GetSingleton()
    3420         916 :                   .InstantiateDeclaredSubAlgorithm(childCallPath);
    3421             :     }
    3422        9933 :     if (ret)
    3423             :     {
    3424        9796 :         ret->SetCallPath(childCallPath);
    3425             :     }
    3426         137 :     else if (suggestionAllowed)
    3427             :     {
    3428          58 :         std::string bestCandidate;
    3429          29 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3430         470 :         for (const std::string &candidate : GetSubAlgorithmNames())
    3431             :         {
    3432             :             const size_t distance =
    3433         441 :                 CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
    3434             :                                        /* transpositionAllowed = */ true);
    3435         441 :             if (distance < bestDistance)
    3436             :             {
    3437          71 :                 bestCandidate = candidate;
    3438          71 :                 bestDistance = distance;
    3439             :             }
    3440         370 :             else if (distance == bestDistance)
    3441             :             {
    3442          45 :                 bestCandidate.clear();
    3443             :             }
    3444             :         }
    3445          29 :         if (!bestCandidate.empty() && bestDistance <= 2)
    3446             :         {
    3447           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3448             :                      "Algorithm '%s' is unknown. Do you mean '%s'?",
    3449             :                      name.c_str(), bestCandidate.c_str());
    3450             :         }
    3451             :     }
    3452       19866 :     return ret;
    3453             : }
    3454             : 
    3455             : /************************************************************************/
    3456             : /*            GDALAlgorithm::GetSuggestionForArgumentName()             */
    3457             : /************************************************************************/
    3458             : 
    3459             : std::string
    3460          37 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
    3461             : {
    3462          37 :     if (osName.size() >= 3)
    3463             :     {
    3464          34 :         std::string bestCandidate;
    3465          34 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3466         714 :         for (const auto &[key, value] : m_mapLongNameToArg)
    3467             :         {
    3468         680 :             CPL_IGNORE_RET_VAL(value);
    3469         680 :             const size_t distance = CPLLevenshteinDistance(
    3470             :                 osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
    3471         680 :             if (distance < bestDistance)
    3472             :             {
    3473          87 :                 bestCandidate = key;
    3474          87 :                 bestDistance = distance;
    3475             :             }
    3476         593 :             else if (distance == bestDistance)
    3477             :             {
    3478          83 :                 bestCandidate.clear();
    3479             :             }
    3480             :         }
    3481          48 :         if (!bestCandidate.empty() &&
    3482          14 :             bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
    3483             :         {
    3484           5 :             return bestCandidate;
    3485             :         }
    3486             :     }
    3487          32 :     return std::string();
    3488             : }
    3489             : 
    3490             : /************************************************************************/
    3491             : /*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
    3492             : /************************************************************************/
    3493             : 
    3494             : /* static */
    3495          22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
    3496             : {
    3497          66 :     return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
    3498          66 :            osName == GDAL_ARG_NAME_OVERWRITE ||
    3499          44 :            osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
    3500             : }
    3501             : 
    3502             : /************************************************************************/
    3503             : /*                   GDALAlgorithm::HasOutputString()                   */
    3504             : /************************************************************************/
    3505             : 
    3506          67 : bool GDALAlgorithm::HasOutputString() const
    3507             : {
    3508          67 :     auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
    3509          67 :     return outputStringArg && outputStringArg->IsOutput();
    3510             : }
    3511             : 
    3512             : /************************************************************************/
    3513             : /*                       GDALAlgorithm::GetArg()                        */
    3514             : /************************************************************************/
    3515             : 
    3516      461000 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
    3517             :                                         bool suggestionAllowed, bool isConst)
    3518             : {
    3519      461000 :     const auto nPos = osName.find_first_not_of('-');
    3520      461000 :     if (nPos == std::string::npos)
    3521          23 :         return nullptr;
    3522      921954 :     std::string osKey = osName.substr(nPos);
    3523             :     {
    3524      460977 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3525      460977 :         if (oIter != m_mapLongNameToArg.end())
    3526      429607 :             return oIter->second;
    3527             :     }
    3528             :     {
    3529       31370 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    3530       31370 :         if (oIter != m_mapShortNameToArg.end())
    3531           6 :             return oIter->second;
    3532             :     }
    3533             : 
    3534       31364 :     if (!isConst && m_arbitraryLongNameArgsAllowed)
    3535             :     {
    3536          22 :         const auto nDotPos = osKey.find('.');
    3537             :         const std::string osKeyEnd =
    3538          22 :             nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
    3539          22 :         if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
    3540             :         {
    3541             :             m_arbitraryLongNameArgsValuesBool.emplace_back(
    3542           0 :                 std::make_unique<bool>());
    3543           0 :             AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3544           0 :                    m_arbitraryLongNameArgsValuesBool.back().get())
    3545           0 :                 .SetUserProvided();
    3546             :         }
    3547             :         else
    3548             :         {
    3549          44 :             const std::string osKeyInit = osKey;
    3550          22 :             if (osKey == "oo")
    3551           0 :                 osKey = GDAL_ARG_NAME_OPEN_OPTION;
    3552          22 :             else if (osKey == "co")
    3553           0 :                 osKey = GDAL_ARG_NAME_CREATION_OPTION;
    3554          22 :             else if (osKey == "of")
    3555           0 :                 osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
    3556          22 :             else if (osKey == "if")
    3557           0 :                 osKey = GDAL_ARG_NAME_INPUT_FORMAT;
    3558             :             m_arbitraryLongNameArgsValuesStr.emplace_back(
    3559          22 :                 std::make_unique<std::string>());
    3560             :             auto &arg =
    3561          44 :                 AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3562          44 :                        m_arbitraryLongNameArgsValuesStr.back().get())
    3563          22 :                     .SetUserProvided();
    3564          22 :             if (osKey != osKeyInit)
    3565           0 :                 arg.AddAlias(osKeyInit);
    3566             :         }
    3567          22 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3568          22 :         CPLAssert(oIter != m_mapLongNameToArg.end());
    3569          22 :         return oIter->second;
    3570             :     }
    3571             : 
    3572       31342 :     if (suggestionAllowed)
    3573             :     {
    3574          14 :         const std::string bestCandidate = GetSuggestionForArgumentName(osName);
    3575           7 :         if (!bestCandidate.empty())
    3576             :         {
    3577           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3578             :                      "Argument '%s' is unknown. Do you mean '%s'?",
    3579             :                      osName.c_str(), bestCandidate.c_str());
    3580             :         }
    3581             :     }
    3582             : 
    3583       31342 :     return nullptr;
    3584             : }
    3585             : 
    3586             : /************************************************************************/
    3587             : /*                     GDALAlgorithm::AddAliasFor()                     */
    3588             : /************************************************************************/
    3589             : 
    3590             : //! @cond Doxygen_Suppress
    3591       79303 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    3592             :                                 const std::string &alias)
    3593             : {
    3594       79303 :     if (cpl::contains(m_mapLongNameToArg, alias))
    3595             :     {
    3596           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    3597             :                     alias.c_str());
    3598             :     }
    3599             :     else
    3600             :     {
    3601       79302 :         m_mapLongNameToArg[alias] = arg;
    3602             :     }
    3603       79303 : }
    3604             : 
    3605             : //! @endcond
    3606             : 
    3607             : /************************************************************************/
    3608             : /*                GDALAlgorithm::AddShortNameAliasFor()                 */
    3609             : /************************************************************************/
    3610             : 
    3611             : //! @cond Doxygen_Suppress
    3612          48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
    3613             :                                          char shortNameAlias)
    3614             : {
    3615          96 :     std::string alias;
    3616          48 :     alias += shortNameAlias;
    3617          48 :     if (cpl::contains(m_mapShortNameToArg, alias))
    3618             :     {
    3619           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    3620             :                     "Short name '%s' already declared.", alias.c_str());
    3621             :     }
    3622             :     else
    3623             :     {
    3624          48 :         m_mapShortNameToArg[alias] = arg;
    3625             :     }
    3626          48 : }
    3627             : 
    3628             : //! @endcond
    3629             : 
    3630             : /************************************************************************/
    3631             : /*                    GDALAlgorithm::SetPositional()                    */
    3632             : /************************************************************************/
    3633             : 
    3634             : //! @cond Doxygen_Suppress
    3635       21395 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    3636             : {
    3637       21395 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    3638             :                         arg) == m_positionalArgs.end());
    3639       21395 :     m_positionalArgs.push_back(arg);
    3640       21395 : }
    3641             : 
    3642             : //! @endcond
    3643             : 
    3644             : /************************************************************************/
    3645             : /*                  GDALAlgorithm::HasSubAlgorithms()                   */
    3646             : /************************************************************************/
    3647             : 
    3648       12566 : bool GDALAlgorithm::HasSubAlgorithms() const
    3649             : {
    3650       12566 :     if (!m_subAlgRegistry.empty())
    3651        3253 :         return true;
    3652        9313 :     return !GDALGlobalAlgorithmRegistry::GetSingleton()
    3653       18626 :                 .GetDeclaredSubAlgorithmNames(m_callPath)
    3654        9313 :                 .empty();
    3655             : }
    3656             : 
    3657             : /************************************************************************/
    3658             : /*                GDALAlgorithm::GetSubAlgorithmNames()                 */
    3659             : /************************************************************************/
    3660             : 
    3661        1330 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
    3662             : {
    3663        1330 :     std::vector<std::string> ret = m_subAlgRegistry.GetNames();
    3664        1330 :     const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
    3665        2660 :                            .GetDeclaredSubAlgorithmNames(m_callPath);
    3666        1330 :     ret.insert(ret.end(), other.begin(), other.end());
    3667        1330 :     if (!other.empty())
    3668         415 :         std::sort(ret.begin(), ret.end());
    3669        2660 :     return ret;
    3670             : }
    3671             : 
    3672             : /************************************************************************/
    3673             : /*                       GDALAlgorithm::AddArg()                        */
    3674             : /************************************************************************/
    3675             : 
    3676             : GDALInConstructionAlgorithmArg &
    3677      306212 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    3678             : {
    3679      306212 :     auto argRaw = arg.get();
    3680      306212 :     const auto &longName = argRaw->GetName();
    3681      306212 :     if (!longName.empty())
    3682             :     {
    3683      306199 :         if (longName[0] == '-')
    3684             :         {
    3685           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3686             :                         "Long name '%s' should not start with '-'",
    3687             :                         longName.c_str());
    3688             :         }
    3689      306199 :         if (longName.find('=') != std::string::npos)
    3690             :         {
    3691           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3692             :                         "Long name '%s' should not contain a '=' character",
    3693             :                         longName.c_str());
    3694             :         }
    3695      306199 :         if (cpl::contains(m_mapLongNameToArg, longName))
    3696             :         {
    3697           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3698             :                         "Long name '%s' already declared", longName.c_str());
    3699             :         }
    3700      306199 :         m_mapLongNameToArg[longName] = argRaw;
    3701             :     }
    3702      306212 :     const auto &shortName = argRaw->GetShortName();
    3703      306212 :     if (!shortName.empty())
    3704             :     {
    3705      148114 :         if (shortName.size() != 1 ||
    3706       74057 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    3707          65 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    3708           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    3709             :         {
    3710           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3711             :                         "Short name '%s' should be a single letter or digit",
    3712             :                         shortName.c_str());
    3713             :         }
    3714       74057 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    3715             :         {
    3716           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3717             :                         "Short name '%s' already declared", shortName.c_str());
    3718             :         }
    3719       74057 :         m_mapShortNameToArg[shortName] = argRaw;
    3720             :     }
    3721      306212 :     m_args.emplace_back(std::move(arg));
    3722             :     return *(
    3723      306212 :         cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    3724             : }
    3725             : 
    3726             : GDALInConstructionAlgorithmArg &
    3727      136626 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3728             :                       const std::string &helpMessage, bool *pValue)
    3729             : {
    3730      136626 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3731             :         this,
    3732      273252 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    3733      273252 :         pValue));
    3734             : }
    3735             : 
    3736             : GDALInConstructionAlgorithmArg &
    3737       49482 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3738             :                       const std::string &helpMessage, std::string *pValue)
    3739             : {
    3740       49482 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3741             :         this,
    3742       98964 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    3743       98964 :         pValue));
    3744             : }
    3745             : 
    3746             : GDALInConstructionAlgorithmArg &
    3747       11784 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3748             :                       const std::string &helpMessage, int *pValue)
    3749             : {
    3750       11784 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3751             :         this,
    3752       23568 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    3753       23568 :         pValue));
    3754             : }
    3755             : 
    3756             : GDALInConstructionAlgorithmArg &
    3757       10064 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3758             :                       const std::string &helpMessage, double *pValue)
    3759             : {
    3760       10064 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3761             :         this,
    3762       20128 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    3763       20128 :         pValue));
    3764             : }
    3765             : 
    3766             : GDALInConstructionAlgorithmArg &
    3767       11605 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3768             :                       const std::string &helpMessage,
    3769             :                       GDALArgDatasetValue *pValue, GDALArgDatasetType type)
    3770             : {
    3771       23210 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3772             :                            this,
    3773       23210 :                            GDALAlgorithmArgDecl(longName, chShortName,
    3774             :                                                 helpMessage, GAAT_DATASET),
    3775       11605 :                            pValue))
    3776       11605 :                     .SetDatasetType(type);
    3777       11605 :     pValue->SetOwnerArgument(&arg);
    3778       11605 :     return arg;
    3779             : }
    3780             : 
    3781             : GDALInConstructionAlgorithmArg &
    3782       65610 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3783             :                       const std::string &helpMessage,
    3784             :                       std::vector<std::string> *pValue)
    3785             : {
    3786       65610 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3787             :         this,
    3788      131220 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3789             :                              GAAT_STRING_LIST),
    3790      131220 :         pValue));
    3791             : }
    3792             : 
    3793             : GDALInConstructionAlgorithmArg &
    3794        2206 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3795             :                       const std::string &helpMessage, std::vector<int> *pValue)
    3796             : {
    3797        2206 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3798             :         this,
    3799        4412 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3800             :                              GAAT_INTEGER_LIST),
    3801        4412 :         pValue));
    3802             : }
    3803             : 
    3804             : GDALInConstructionAlgorithmArg &
    3805        5167 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3806             :                       const std::string &helpMessage,
    3807             :                       std::vector<double> *pValue)
    3808             : {
    3809        5167 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3810             :         this,
    3811       10334 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3812             :                              GAAT_REAL_LIST),
    3813       10334 :         pValue));
    3814             : }
    3815             : 
    3816             : GDALInConstructionAlgorithmArg &
    3817       13668 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3818             :                       const std::string &helpMessage,
    3819             :                       std::vector<GDALArgDatasetValue> *pValue,
    3820             :                       GDALArgDatasetType type)
    3821             : {
    3822       27336 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3823             :                       this,
    3824       27336 :                       GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3825             :                                            GAAT_DATASET_LIST),
    3826       13668 :                       pValue))
    3827       27336 :         .SetDatasetType(type);
    3828             : }
    3829             : 
    3830             : /************************************************************************/
    3831             : /*                            MsgOrDefault()                            */
    3832             : /************************************************************************/
    3833             : 
    3834      102716 : inline const char *MsgOrDefault(const char *helpMessage,
    3835             :                                 const char *defaultMessage)
    3836             : {
    3837      102716 :     return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
    3838             : }
    3839             : 
    3840             : /************************************************************************/
    3841             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
    3842             : /************************************************************************/
    3843             : 
    3844             : /* static */
    3845       16705 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
    3846             :     GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
    3847             : {
    3848             :     arg.SetAutoCompleteFunction(
    3849           7 :         [&arg,
    3850        2460 :          type](const std::string &currentValue) -> std::vector<std::string>
    3851             :         {
    3852          14 :             std::vector<std::string> oRet;
    3853             : 
    3854           7 :             if (arg.IsHidden())
    3855           0 :                 return oRet;
    3856             : 
    3857             :             {
    3858           7 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3859             :                 VSIStatBufL sStat;
    3860          10 :                 if (!currentValue.empty() && currentValue.back() != '/' &&
    3861           3 :                     VSIStatL(currentValue.c_str(), &sStat) == 0)
    3862             :                 {
    3863           0 :                     return oRet;
    3864             :                 }
    3865             :             }
    3866             : 
    3867           7 :             auto poDM = GetGDALDriverManager();
    3868          14 :             std::set<std::string> oExtensions;
    3869           7 :             if (type)
    3870             :             {
    3871        1368 :                 for (int i = 0; i < poDM->GetDriverCount(); ++i)
    3872             :                 {
    3873        1362 :                     auto poDriver = poDM->GetDriver(i);
    3874        3859 :                     if (((type & GDAL_OF_RASTER) != 0 &&
    3875        1135 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    3876         587 :                         ((type & GDAL_OF_VECTOR) != 0 &&
    3877        2861 :                          poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    3878         497 :                         ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    3879           0 :                          poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    3880             :                     {
    3881             :                         const char *pszExtensions =
    3882         865 :                             poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    3883         865 :                         if (pszExtensions)
    3884             :                         {
    3885             :                             const CPLStringList aosExts(
    3886        1142 :                                 CSLTokenizeString2(pszExtensions, " ", 0));
    3887        1291 :                             for (const char *pszExt : cpl::Iterate(aosExts))
    3888         720 :                                 oExtensions.insert(CPLString(pszExt).tolower());
    3889             :                         }
    3890             :                     }
    3891             :                 }
    3892             :             }
    3893             : 
    3894          14 :             std::string osDir;
    3895          14 :             const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
    3896          14 :             std::string osPrefix;
    3897           7 :             if (STARTS_WITH(currentValue.c_str(), "/vsi"))
    3898             :             {
    3899          79 :                 for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
    3900             :                 {
    3901          78 :                     if (STARTS_WITH(currentValue.c_str(), pszPrefix))
    3902             :                     {
    3903           2 :                         osPrefix = pszPrefix;
    3904           2 :                         break;
    3905             :                     }
    3906             :                 }
    3907           3 :                 if (osPrefix.empty())
    3908           1 :                     return aosVSIPrefixes;
    3909           2 :                 if (currentValue == osPrefix)
    3910           1 :                     osDir = osPrefix;
    3911             :             }
    3912           6 :             if (osDir.empty())
    3913             :             {
    3914           5 :                 osDir = CPLGetDirnameSafe(currentValue.c_str());
    3915           5 :                 if (!osPrefix.empty() && osDir.size() < osPrefix.size())
    3916           0 :                     osDir = std::move(osPrefix);
    3917             :             }
    3918             : 
    3919           6 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    3920          12 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    3921           6 :             if (currentValue.empty())
    3922           1 :                 osDir.clear();
    3923             :             const std::string currentFilename =
    3924          12 :                 CPLGetFilename(currentValue.c_str());
    3925           6 :             if (psDir)
    3926             :             {
    3927         442 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    3928             :                 {
    3929         437 :                     if ((currentFilename.empty() ||
    3930         218 :                          STARTS_WITH(psEntry->pszName,
    3931         220 :                                      currentFilename.c_str())) &&
    3932         220 :                         strcmp(psEntry->pszName, ".") != 0 &&
    3933        1313 :                         strcmp(psEntry->pszName, "..") != 0 &&
    3934         220 :                         (oExtensions.empty() ||
    3935         219 :                          !strstr(psEntry->pszName, ".aux.xml")))
    3936             :                     {
    3937         870 :                         if (oExtensions.empty() ||
    3938         217 :                             cpl::contains(
    3939             :                                 oExtensions,
    3940         435 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    3941         652 :                                     .tolower()) ||
    3942         186 :                             VSI_ISDIR(psEntry->nMode))
    3943             :                         {
    3944          72 :                             std::string osVal;
    3945          36 :                             if (osDir.empty() || osDir == ".")
    3946           4 :                                 osVal = psEntry->pszName;
    3947             :                             else
    3948          64 :                                 osVal = CPLFormFilenameSafe(
    3949          64 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    3950          36 :                             if (VSI_ISDIR(psEntry->nMode))
    3951           4 :                                 osVal += osSep;
    3952          36 :                             oRet.push_back(std::move(osVal));
    3953             :                         }
    3954             :                     }
    3955         437 :                 }
    3956           5 :                 VSICloseDir(psDir);
    3957             :             }
    3958           6 :             return oRet;
    3959       16705 :         });
    3960       16705 : }
    3961             : 
    3962             : /************************************************************************/
    3963             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    3964             : /************************************************************************/
    3965             : 
    3966         704 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    3967             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    3968             :     bool positionalAndRequired, const char *helpMessage)
    3969             : {
    3970             :     auto &arg = AddArg(
    3971             :         GDAL_ARG_NAME_INPUT, 'i',
    3972             :         MsgOrDefault(helpMessage,
    3973             :                      CPLSPrintf("Input %s dataset",
    3974         704 :                                 GDALAlgorithmArgDatasetTypeName(type).c_str())),
    3975        1408 :         pValue, type);
    3976         704 :     if (positionalAndRequired)
    3977         697 :         arg.SetPositional().SetRequired();
    3978             : 
    3979         704 :     SetAutoCompleteFunctionForFilename(arg, type);
    3980             : 
    3981         704 :     AddValidationAction(
    3982         155 :         [pValue]()
    3983             :         {
    3984         154 :             if (pValue->GetName() == "-")
    3985           1 :                 pValue->Set("/vsistdin/");
    3986         154 :             return true;
    3987             :         });
    3988             : 
    3989         704 :     return arg;
    3990             : }
    3991             : 
    3992             : /************************************************************************/
    3993             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    3994             : /************************************************************************/
    3995             : 
    3996       13199 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    3997             :     std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
    3998             :     bool positionalAndRequired, const char *helpMessage)
    3999             : {
    4000             :     auto &arg =
    4001             :         AddArg(GDAL_ARG_NAME_INPUT, 'i',
    4002             :                MsgOrDefault(
    4003             :                    helpMessage,
    4004             :                    CPLSPrintf("Input %s datasets",
    4005       13199 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4006       39597 :                pValue, type)
    4007       13199 :             .SetPackedValuesAllowed(false);
    4008       13199 :     if (positionalAndRequired)
    4009        1721 :         arg.SetPositional().SetRequired();
    4010             : 
    4011       13199 :     SetAutoCompleteFunctionForFilename(arg, type);
    4012             : 
    4013       13199 :     AddValidationAction(
    4014        6106 :         [pValue]()
    4015             :         {
    4016       11608 :             for (auto &val : *pValue)
    4017             :             {
    4018        5502 :                 if (val.GetName() == "-")
    4019           1 :                     val.Set("/vsistdin/");
    4020             :             }
    4021        6106 :             return true;
    4022             :         });
    4023       13199 :     return arg;
    4024             : }
    4025             : 
    4026             : /************************************************************************/
    4027             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    4028             : /************************************************************************/
    4029             : 
    4030        8215 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
    4031             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4032             :     bool positionalAndRequired, const char *helpMessage)
    4033             : {
    4034             :     auto &arg =
    4035             :         AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    4036             :                MsgOrDefault(
    4037             :                    helpMessage,
    4038             :                    CPLSPrintf("Output %s dataset",
    4039        8215 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4040       24645 :                pValue, type)
    4041        8215 :             .SetIsInput(true)
    4042        8215 :             .SetIsOutput(true)
    4043        8215 :             .SetDatasetInputFlags(GADV_NAME)
    4044        8215 :             .SetDatasetOutputFlags(GADV_OBJECT);
    4045        8215 :     if (positionalAndRequired)
    4046        4290 :         arg.SetPositional().SetRequired();
    4047             : 
    4048        8215 :     AddValidationAction(
    4049       11520 :         [this, &arg, pValue]()
    4050             :         {
    4051        3567 :             if (pValue->GetName() == "-")
    4052           4 :                 pValue->Set("/vsistdout/");
    4053             : 
    4054        3567 :             auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    4055        3515 :             if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    4056        5687 :                 (!outputFormatArg->IsExplicitlySet() ||
    4057        9254 :                  outputFormatArg->Get<std::string>().empty()) &&
    4058        1343 :                 arg.IsExplicitlySet())
    4059             :             {
    4060             :                 const auto vrtCompatible =
    4061        1002 :                     outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4062         182 :                 if (vrtCompatible && !vrtCompatible->empty() &&
    4063        1184 :                     vrtCompatible->front() == "false" &&
    4064        1093 :                     EQUAL(
    4065             :                         CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
    4066             :                         "VRT"))
    4067             :                 {
    4068           6 :                     ReportError(
    4069             :                         CE_Failure, CPLE_NotSupported,
    4070             :                         "VRT output is not supported.%s",
    4071           6 :                         outputFormatArg->GetDescription().find("GDALG") !=
    4072             :                                 std::string::npos
    4073             :                             ? " Consider using the GDALG driver instead (files "
    4074             :                               "with .gdalg.json extension)"
    4075             :                             : "");
    4076           6 :                     return false;
    4077             :                 }
    4078         996 :                 else if (pValue->GetName().size() > strlen(".gdalg.json") &&
    4079        1969 :                          EQUAL(pValue->GetName()
    4080             :                                    .substr(pValue->GetName().size() -
    4081             :                                            strlen(".gdalg.json"))
    4082             :                                    .c_str(),
    4083        2965 :                                ".gdalg.json") &&
    4084          27 :                          outputFormatArg->GetDescription().find("GDALG") ==
    4085             :                              std::string::npos)
    4086             :                 {
    4087           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4088             :                                 "GDALG output is not supported");
    4089           0 :                     return false;
    4090             :                 }
    4091             :             }
    4092        3561 :             return true;
    4093             :         });
    4094             : 
    4095        8215 :     return arg;
    4096             : }
    4097             : 
    4098             : /************************************************************************/
    4099             : /*                   GDALAlgorithm::AddOverwriteArg()                   */
    4100             : /************************************************************************/
    4101             : 
    4102             : GDALInConstructionAlgorithmArg &
    4103        8125 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
    4104             : {
    4105             :     return AddArg(
    4106             :                GDAL_ARG_NAME_OVERWRITE, 0,
    4107             :                MsgOrDefault(
    4108             :                    helpMessage,
    4109             :                    _("Whether overwriting existing output dataset is allowed")),
    4110       16250 :                pValue)
    4111       16250 :         .SetDefault(false);
    4112             : }
    4113             : 
    4114             : /************************************************************************/
    4115             : /*                GDALAlgorithm::AddOverwriteLayerArg()                 */
    4116             : /************************************************************************/
    4117             : 
    4118             : GDALInConstructionAlgorithmArg &
    4119        3402 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
    4120             : {
    4121        3402 :     AddValidationAction(
    4122        1532 :         [this]
    4123             :         {
    4124        1531 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4125        1531 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4126             :             {
    4127           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4128             :                             "--update argument must exist for "
    4129             :                             "--overwrite-layer, even if hidden");
    4130           1 :                 return false;
    4131             :             }
    4132        1530 :             return true;
    4133             :         });
    4134             :     return AddArg(
    4135             :                GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
    4136             :                MsgOrDefault(
    4137             :                    helpMessage,
    4138             :                    _("Whether overwriting existing output layer is allowed")),
    4139        6804 :                pValue)
    4140        3402 :         .SetDefault(false)
    4141             :         .AddAction(
    4142          19 :             [this]
    4143             :             {
    4144          19 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4145          19 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4146             :                 {
    4147          19 :                     updateArg->Set(true);
    4148             :                 }
    4149        6823 :             });
    4150             : }
    4151             : 
    4152             : /************************************************************************/
    4153             : /*                    GDALAlgorithm::AddUpdateArg()                     */
    4154             : /************************************************************************/
    4155             : 
    4156             : GDALInConstructionAlgorithmArg &
    4157        3953 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
    4158             : {
    4159             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    4160             :                   MsgOrDefault(
    4161             :                       helpMessage,
    4162             :                       _("Whether to open existing dataset in update mode")),
    4163        7906 :                   pValue)
    4164        7906 :         .SetDefault(false);
    4165             : }
    4166             : 
    4167             : /************************************************************************/
    4168             : /*                  GDALAlgorithm::AddAppendLayerArg()                  */
    4169             : /************************************************************************/
    4170             : 
    4171             : GDALInConstructionAlgorithmArg &
    4172        3176 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
    4173             : {
    4174        3176 :     AddValidationAction(
    4175        1487 :         [this]
    4176             :         {
    4177        1486 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4178        1486 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4179             :             {
    4180           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4181             :                             "--update argument must exist for --append, even "
    4182             :                             "if hidden");
    4183           1 :                 return false;
    4184             :             }
    4185        1485 :             return true;
    4186             :         });
    4187             :     return AddArg(GDAL_ARG_NAME_APPEND, 0,
    4188             :                   MsgOrDefault(
    4189             :                       helpMessage,
    4190             :                       _("Whether appending to existing layer is allowed")),
    4191        6352 :                   pValue)
    4192        3176 :         .SetDefault(false)
    4193             :         .AddAction(
    4194          25 :             [this]
    4195             :             {
    4196          25 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4197          25 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4198             :                 {
    4199          25 :                     updateArg->Set(true);
    4200             :                 }
    4201        6377 :             });
    4202             : }
    4203             : 
    4204             : /************************************************************************/
    4205             : /*                GDALAlgorithm::AddOptionsSuggestions()                */
    4206             : /************************************************************************/
    4207             : 
    4208             : /* static */
    4209          29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
    4210             :                                           const std::string &currentValue,
    4211             :                                           std::vector<std::string> &oRet)
    4212             : {
    4213          29 :     if (!pszXML)
    4214           0 :         return false;
    4215          58 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    4216          29 :     if (!poTree)
    4217           0 :         return false;
    4218             : 
    4219          58 :     std::string typedOptionName = currentValue;
    4220          29 :     const auto posEqual = typedOptionName.find('=');
    4221          58 :     std::string typedValue;
    4222          29 :     if (posEqual != 0 && posEqual != std::string::npos)
    4223             :     {
    4224           2 :         typedValue = currentValue.substr(posEqual + 1);
    4225           2 :         typedOptionName.resize(posEqual);
    4226             :     }
    4227             : 
    4228         405 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4229         376 :          psChild = psChild->psNext)
    4230             :     {
    4231         389 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4232         402 :         if (pszName && typedOptionName == pszName &&
    4233          13 :             (strcmp(psChild->pszValue, "Option") == 0 ||
    4234           2 :              strcmp(psChild->pszValue, "Argument") == 0))
    4235             :         {
    4236          13 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    4237          13 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    4238          13 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    4239          13 :             if (EQUAL(pszType, "string-select"))
    4240             :             {
    4241          90 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    4242          85 :                      psChild2 = psChild2->psNext)
    4243             :                 {
    4244          85 :                     if (EQUAL(psChild2->pszValue, "Value"))
    4245             :                     {
    4246          75 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    4247             :                     }
    4248             :                 }
    4249             :             }
    4250           8 :             else if (EQUAL(pszType, "boolean"))
    4251             :             {
    4252           3 :                 if (typedValue == "YES" || typedValue == "NO")
    4253             :                 {
    4254           1 :                     oRet.push_back(currentValue);
    4255           1 :                     return true;
    4256             :                 }
    4257           2 :                 oRet.push_back("NO");
    4258           2 :                 oRet.push_back("YES");
    4259             :             }
    4260           5 :             else if (EQUAL(pszType, "int"))
    4261             :             {
    4262           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    4263           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    4264             :                 {
    4265           1 :                     const int nMax = atoi(pszMax);
    4266          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    4267          12 :                         oRet.push_back(std::to_string(i));
    4268             :                 }
    4269             :             }
    4270             : 
    4271          12 :             if (oRet.empty())
    4272             :             {
    4273           4 :                 if (pszMin && pszMax)
    4274             :                 {
    4275           1 :                     oRet.push_back(std::string("##"));
    4276           2 :                     oRet.push_back(std::string("validity range: [")
    4277           1 :                                        .append(pszMin)
    4278           1 :                                        .append(",")
    4279           1 :                                        .append(pszMax)
    4280           1 :                                        .append("]"));
    4281             :                 }
    4282           3 :                 else if (pszMin)
    4283             :                 {
    4284           1 :                     oRet.push_back(std::string("##"));
    4285           1 :                     oRet.push_back(
    4286           1 :                         std::string("validity range: >= ").append(pszMin));
    4287             :                 }
    4288           2 :                 else if (pszMax)
    4289             :                 {
    4290           1 :                     oRet.push_back(std::string("##"));
    4291           1 :                     oRet.push_back(
    4292           1 :                         std::string("validity range: <= ").append(pszMax));
    4293             :                 }
    4294           1 :                 else if (const char *pszDescription =
    4295           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    4296             :                 {
    4297           1 :                     oRet.push_back(std::string("##"));
    4298           2 :                     oRet.push_back(std::string("type: ")
    4299           1 :                                        .append(pszType)
    4300           1 :                                        .append(", description: ")
    4301           1 :                                        .append(pszDescription));
    4302             :                 }
    4303             :             }
    4304             : 
    4305          12 :             return true;
    4306             :         }
    4307             :     }
    4308             : 
    4309         319 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4310         303 :          psChild = psChild->psNext)
    4311             :     {
    4312         303 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4313         303 :         if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
    4314           5 :                         strcmp(psChild->pszValue, "Argument") == 0))
    4315             :         {
    4316         300 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    4317         300 :             if (!pszScope ||
    4318          40 :                 (EQUAL(pszScope, "raster") &&
    4319          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    4320          20 :                 (EQUAL(pszScope, "vector") &&
    4321           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    4322             :             {
    4323         280 :                 oRet.push_back(std::string(pszName).append("="));
    4324             :             }
    4325             :         }
    4326             :     }
    4327             : 
    4328          16 :     return false;
    4329             : }
    4330             : 
    4331             : /************************************************************************/
    4332             : /*             GDALAlgorithm::OpenOptionCompleteFunction()              */
    4333             : /************************************************************************/
    4334             : 
    4335             : //! @cond Doxygen_Suppress
    4336             : std::vector<std::string>
    4337           2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
    4338             : {
    4339           2 :     std::vector<std::string> oRet;
    4340             : 
    4341           2 :     int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    4342           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4343           4 :     if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
    4344           2 :                      inputArg->GetType() == GAAT_DATASET_LIST))
    4345             :     {
    4346           2 :         datasetType = inputArg->GetDatasetType();
    4347             :     }
    4348             : 
    4349           2 :     auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4350           4 :     if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    4351           2 :         inputFormat->IsExplicitlySet())
    4352             :     {
    4353             :         const auto &aosAllowedDrivers =
    4354           1 :             inputFormat->Get<std::vector<std::string>>();
    4355           1 :         if (aosAllowedDrivers.size() == 1)
    4356             :         {
    4357           2 :             auto poDriver = GetGDALDriverManager()->GetDriverByName(
    4358           1 :                 aosAllowedDrivers[0].c_str());
    4359           1 :             if (poDriver)
    4360             :             {
    4361           1 :                 AddOptionsSuggestions(
    4362           1 :                     poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    4363             :                     datasetType, currentValue, oRet);
    4364             :             }
    4365           1 :             return oRet;
    4366             :         }
    4367             :     }
    4368             : 
    4369           1 :     const auto AddSuggestions = [datasetType, &currentValue,
    4370         372 :                                  &oRet](const GDALArgDatasetValue &datasetValue)
    4371             :     {
    4372           1 :         auto poDM = GetGDALDriverManager();
    4373             : 
    4374           1 :         const auto &osDSName = datasetValue.GetName();
    4375           1 :         const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    4376           1 :         if (!osExt.empty())
    4377             :         {
    4378           1 :             std::set<std::string> oVisitedExtensions;
    4379         228 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4380             :             {
    4381         227 :                 auto poDriver = poDM->GetDriver(i);
    4382         681 :                 if (((datasetType & GDAL_OF_RASTER) != 0 &&
    4383         227 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    4384          72 :                     ((datasetType & GDAL_OF_VECTOR) != 0 &&
    4385         454 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    4386          72 :                     ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    4387           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    4388             :                 {
    4389             :                     const char *pszExtensions =
    4390         155 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    4391         155 :                     if (pszExtensions)
    4392             :                     {
    4393             :                         const CPLStringList aosExts(
    4394         102 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    4395         225 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    4396             :                         {
    4397         127 :                             if (EQUAL(pszExt, osExt.c_str()) &&
    4398           3 :                                 !cpl::contains(oVisitedExtensions, pszExt))
    4399             :                             {
    4400           1 :                                 oVisitedExtensions.insert(pszExt);
    4401           1 :                                 if (AddOptionsSuggestions(
    4402             :                                         poDriver->GetMetadataItem(
    4403           1 :                                             GDAL_DMD_OPENOPTIONLIST),
    4404             :                                         datasetType, currentValue, oRet))
    4405             :                                 {
    4406           0 :                                     return;
    4407             :                                 }
    4408           1 :                                 break;
    4409             :                             }
    4410             :                         }
    4411             :                     }
    4412             :                 }
    4413             :             }
    4414             :         }
    4415           1 :     };
    4416             : 
    4417           1 :     if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4418             :     {
    4419           0 :         auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    4420           0 :         AddSuggestions(datasetValue);
    4421             :     }
    4422           1 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4423             :     {
    4424           1 :         auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4425           1 :         if (datasetValues.size() == 1)
    4426           1 :             AddSuggestions(datasetValues[0]);
    4427             :     }
    4428             : 
    4429           1 :     return oRet;
    4430             : }
    4431             : 
    4432             : //! @endcond
    4433             : 
    4434             : /************************************************************************/
    4435             : /*                  GDALAlgorithm::AddOpenOptionsArg()                  */
    4436             : /************************************************************************/
    4437             : 
    4438             : GDALInConstructionAlgorithmArg &
    4439        9052 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
    4440             :                                  const char *helpMessage)
    4441             : {
    4442             :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
    4443       18104 :                        MsgOrDefault(helpMessage, _("Open options")), pValue)
    4444       18104 :                     .AddAlias("oo")
    4445       18104 :                     .SetMetaVar("<KEY>=<VALUE>")
    4446        9052 :                     .SetPackedValuesAllowed(false)
    4447        9052 :                     .SetCategory(GAAC_ADVANCED);
    4448             : 
    4449          21 :     arg.AddValidationAction([this, &arg]()
    4450        9073 :                             { return ParseAndValidateKeyValue(arg); });
    4451             : 
    4452             :     arg.SetAutoCompleteFunction(
    4453           2 :         [this](const std::string &currentValue)
    4454        9054 :         { return OpenOptionCompleteFunction(currentValue); });
    4455             : 
    4456        9052 :     return arg;
    4457             : }
    4458             : 
    4459             : /************************************************************************/
    4460             : /*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
    4461             : /************************************************************************/
    4462             : 
    4463             : GDALInConstructionAlgorithmArg &
    4464        3173 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
    4465             :                                        const char *helpMessage)
    4466             : {
    4467             :     auto &arg =
    4468             :         AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
    4469        6346 :                MsgOrDefault(helpMessage, _("Output open options")), pValue)
    4470        6346 :             .AddAlias("output-oo")
    4471        6346 :             .SetMetaVar("<KEY>=<VALUE>")
    4472        3173 :             .SetPackedValuesAllowed(false)
    4473        3173 :             .SetCategory(GAAC_ADVANCED);
    4474             : 
    4475           0 :     arg.AddValidationAction([this, &arg]()
    4476        3173 :                             { return ParseAndValidateKeyValue(arg); });
    4477             : 
    4478             :     arg.SetAutoCompleteFunction(
    4479           0 :         [this](const std::string &currentValue)
    4480        3173 :         { return OpenOptionCompleteFunction(currentValue); });
    4481             : 
    4482        3173 :     return arg;
    4483             : }
    4484             : 
    4485             : /************************************************************************/
    4486             : /*                           ValidateFormat()                           */
    4487             : /************************************************************************/
    4488             : 
    4489        4617 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
    4490             :                                    bool bStreamAllowed,
    4491             :                                    bool bGDALGAllowed) const
    4492             : {
    4493        4617 :     if (arg.GetChoices().empty())
    4494             :     {
    4495             :         const auto Validate =
    4496       19761 :             [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
    4497             :         {
    4498        4512 :             if (const auto extraFormats =
    4499        4512 :                     arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4500             :             {
    4501          60 :                 for (const auto &extraFormat : *extraFormats)
    4502             :                 {
    4503          48 :                     if (EQUAL(val.c_str(), extraFormat.c_str()))
    4504          14 :                         return true;
    4505             :                 }
    4506             :             }
    4507             : 
    4508        4498 :             if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
    4509        1805 :                 return true;
    4510             : 
    4511        2699 :             if (EQUAL(val.c_str(), "GDALG") &&
    4512           6 :                 arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
    4513             :             {
    4514           2 :                 if (bGDALGAllowed)
    4515             :                 {
    4516           2 :                     return true;
    4517             :                 }
    4518             :                 else
    4519             :                 {
    4520           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4521             :                                 "GDALG output is not supported.");
    4522           0 :                     return false;
    4523             :                 }
    4524             :             }
    4525             : 
    4526             :             const auto vrtCompatible =
    4527        2691 :                 arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4528         440 :             if (vrtCompatible && !vrtCompatible->empty() &&
    4529        3131 :                 vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
    4530             :             {
    4531           7 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4532             :                             "VRT output is not supported.%s",
    4533             :                             bGDALGAllowed
    4534             :                                 ? " Consider using the GDALG driver instead "
    4535             :                                   "(files with .gdalg.json extension)."
    4536             :                                 : "");
    4537           7 :                 return false;
    4538             :             }
    4539             : 
    4540             :             const auto allowedFormats =
    4541        2684 :                 arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4542        2705 :             if (allowedFormats && !allowedFormats->empty() &&
    4543           0 :                 std::find(allowedFormats->begin(), allowedFormats->end(),
    4544        2705 :                           val) != allowedFormats->end())
    4545             :             {
    4546           9 :                 return true;
    4547             :             }
    4548             : 
    4549             :             const auto excludedFormats =
    4550        2675 :                 arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4551        2687 :             if (excludedFormats && !excludedFormats->empty() &&
    4552           0 :                 std::find(excludedFormats->begin(), excludedFormats->end(),
    4553        2687 :                           val) != excludedFormats->end())
    4554             :             {
    4555           0 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4556             :                             "%s output is not supported.", val.c_str());
    4557           0 :                 return false;
    4558             :             }
    4559             : 
    4560        2675 :             auto hDriver = GDALGetDriverByName(val.c_str());
    4561        2675 :             if (!hDriver)
    4562             :             {
    4563             :                 auto poMissingDriver =
    4564           4 :                     GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
    4565           4 :                 if (poMissingDriver)
    4566             :                 {
    4567             :                     const std::string msg =
    4568           0 :                         GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
    4569           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4570             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4571             :                                 "not found but is known. However plugin %s",
    4572           0 :                                 arg.GetName().c_str(), val.c_str(),
    4573             :                                 msg.c_str());
    4574             :                 }
    4575             :                 else
    4576             :                 {
    4577           8 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4578             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4579             :                                 "does not exist.",
    4580           4 :                                 arg.GetName().c_str(), val.c_str());
    4581             :                 }
    4582           4 :                 return false;
    4583             :             }
    4584             : 
    4585        2671 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4586        2671 :             if (caps)
    4587             :             {
    4588        8019 :                 for (const std::string &cap : *caps)
    4589             :                 {
    4590             :                     const char *pszVal =
    4591        5373 :                         GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
    4592        5373 :                     if (!(pszVal && pszVal[0]))
    4593             :                     {
    4594        1569 :                         if (cap == GDAL_DCAP_CREATECOPY &&
    4595           0 :                             std::find(caps->begin(), caps->end(),
    4596         783 :                                       GDAL_DCAP_RASTER) != caps->end() &&
    4597         783 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
    4598        1569 :                                                 nullptr) &&
    4599         783 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
    4600             :                                                 nullptr))
    4601             :                         {
    4602             :                             // if it supports Create, it supports CreateCopy
    4603             :                         }
    4604           3 :                         else if (cap == GDAL_DMD_EXTENSIONS)
    4605             :                         {
    4606           2 :                             ReportError(
    4607             :                                 CE_Failure, CPLE_AppDefined,
    4608             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4609             :                                 "does "
    4610             :                                 "not advertise any file format extension.",
    4611           1 :                                 arg.GetName().c_str(), val.c_str());
    4612           3 :                             return false;
    4613             :                         }
    4614             :                         else
    4615             :                         {
    4616           2 :                             if (cap == GDAL_DCAP_CREATE)
    4617             :                             {
    4618           1 :                                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4619           1 :                                 if (updateArg &&
    4620           2 :                                     updateArg->GetType() == GAAT_BOOLEAN &&
    4621           1 :                                     updateArg->IsExplicitlySet())
    4622             :                                 {
    4623           0 :                                     continue;
    4624             :                                 }
    4625             : 
    4626           2 :                                 ReportError(
    4627             :                                     CE_Failure, CPLE_AppDefined,
    4628             :                                     "Invalid value for argument '%s'. "
    4629             :                                     "Driver '%s' does not have write support.",
    4630           1 :                                     arg.GetName().c_str(), val.c_str());
    4631           1 :                                 return false;
    4632             :                             }
    4633             :                             else
    4634             :                             {
    4635           2 :                                 ReportError(
    4636             :                                     CE_Failure, CPLE_AppDefined,
    4637             :                                     "Invalid value for argument '%s'. Driver "
    4638             :                                     "'%s' "
    4639             :                                     "does "
    4640             :                                     "not expose the required '%s' capability.",
    4641           1 :                                     arg.GetName().c_str(), val.c_str(),
    4642             :                                     cap.c_str());
    4643           1 :                                 return false;
    4644             :                             }
    4645             :                         }
    4646             :                     }
    4647             :                 }
    4648             :             }
    4649        2668 :             return true;
    4650        4515 :         };
    4651             : 
    4652        4515 :         if (arg.GetType() == GAAT_STRING)
    4653             :         {
    4654        4505 :             return Validate(arg.Get<std::string>());
    4655             :         }
    4656          12 :         else if (arg.GetType() == GAAT_STRING_LIST)
    4657             :         {
    4658          19 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    4659             :             {
    4660           9 :                 if (!Validate(val))
    4661           2 :                     return false;
    4662             :             }
    4663             :         }
    4664             :     }
    4665             : 
    4666         112 :     return true;
    4667             : }
    4668             : 
    4669             : /************************************************************************/
    4670             : /*                     FormatAutoCompleteFunction()                     */
    4671             : /************************************************************************/
    4672             : 
    4673             : /* static */
    4674           7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
    4675             :     const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
    4676             : {
    4677           7 :     std::vector<std::string> res;
    4678           7 :     auto poDM = GetGDALDriverManager();
    4679           7 :     const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4680           7 :     const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4681           7 :     const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4682           7 :     const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4683           7 :     if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4684           0 :         res = std::move(*extraFormats);
    4685        1595 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4686             :     {
    4687        1588 :         auto poDriver = poDM->GetDriver(i);
    4688             : 
    4689           0 :         if (vrtCompatible && !vrtCompatible->empty() &&
    4690        1588 :             vrtCompatible->front() == "false" &&
    4691           0 :             EQUAL(poDriver->GetDescription(), "VRT"))
    4692             :         {
    4693             :             // do nothing
    4694             :         }
    4695        1588 :         else if (allowedFormats && !allowedFormats->empty() &&
    4696           0 :                  std::find(allowedFormats->begin(), allowedFormats->end(),
    4697        1588 :                            poDriver->GetDescription()) != allowedFormats->end())
    4698             :         {
    4699           0 :             res.push_back(poDriver->GetDescription());
    4700             :         }
    4701        1588 :         else if (excludedFormats && !excludedFormats->empty() &&
    4702           0 :                  std::find(excludedFormats->begin(), excludedFormats->end(),
    4703           0 :                            poDriver->GetDescription()) !=
    4704        1588 :                      excludedFormats->end())
    4705             :         {
    4706           0 :             continue;
    4707             :         }
    4708        1588 :         else if (caps)
    4709             :         {
    4710        1588 :             bool ok = true;
    4711        3136 :             for (const std::string &cap : *caps)
    4712             :             {
    4713        2362 :                 if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
    4714             :                 {
    4715           0 :                     if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    4716           0 :                         !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
    4717             :                     {
    4718           0 :                         ok = false;
    4719           0 :                         break;
    4720             :                     }
    4721             :                 }
    4722        2362 :                 else if (const char *pszVal =
    4723        2362 :                              poDriver->GetMetadataItem(cap.c_str());
    4724        1476 :                          pszVal && pszVal[0])
    4725             :                 {
    4726             :                 }
    4727        1274 :                 else if (cap == GDAL_DCAP_CREATECOPY &&
    4728           0 :                          (std::find(caps->begin(), caps->end(),
    4729         388 :                                     GDAL_DCAP_RASTER) != caps->end() &&
    4730        1662 :                           poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
    4731         388 :                          poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    4732             :                 {
    4733             :                     // if it supports Create, it supports CreateCopy
    4734             :                 }
    4735             :                 else
    4736             :                 {
    4737         814 :                     ok = false;
    4738         814 :                     break;
    4739             :                 }
    4740             :             }
    4741        1588 :             if (ok)
    4742             :             {
    4743         774 :                 res.push_back(poDriver->GetDescription());
    4744             :             }
    4745             :         }
    4746             :     }
    4747           7 :     if (bGDALGAllowed)
    4748           4 :         res.push_back("GDALG");
    4749           7 :     return res;
    4750             : }
    4751             : 
    4752             : /************************************************************************/
    4753             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    4754             : /************************************************************************/
    4755             : 
    4756             : GDALInConstructionAlgorithmArg &
    4757        8799 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
    4758             :                                   const char *helpMessage)
    4759             : {
    4760             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
    4761       17598 :                        MsgOrDefault(helpMessage, _("Input formats")), pValue)
    4762       17598 :                     .AddAlias("if")
    4763        8799 :                     .SetCategory(GAAC_ADVANCED);
    4764          12 :     arg.AddValidationAction([this, &arg]()
    4765        8811 :                             { return ValidateFormat(arg, false, false); });
    4766             :     arg.SetAutoCompleteFunction(
    4767           1 :         [&arg](const std::string &)
    4768        8800 :         { return FormatAutoCompleteFunction(arg, false, false); });
    4769        8799 :     return arg;
    4770             : }
    4771             : 
    4772             : /************************************************************************/
    4773             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    4774             : /************************************************************************/
    4775             : 
    4776             : GDALInConstructionAlgorithmArg &
    4777        9194 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
    4778             :                                   bool bGDALGAllowed, const char *helpMessage)
    4779             : {
    4780             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
    4781             :                        MsgOrDefault(helpMessage,
    4782             :                                     bGDALGAllowed
    4783             :                                         ? _("Output format (\"GDALG\" allowed)")
    4784             :                                         : _("Output format")),
    4785       18388 :                        pValue)
    4786       18388 :                     .AddAlias("of")
    4787        9194 :                     .AddAlias("format");
    4788             :     arg.AddValidationAction(
    4789        4601 :         [this, &arg, bStreamAllowed, bGDALGAllowed]()
    4790       13795 :         { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
    4791             :     arg.SetAutoCompleteFunction(
    4792           4 :         [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
    4793             :         {
    4794             :             return FormatAutoCompleteFunction(arg, bStreamAllowed,
    4795           4 :                                               bGDALGAllowed);
    4796        9194 :         });
    4797        9194 :     return arg;
    4798             : }
    4799             : 
    4800             : /************************************************************************/
    4801             : /*                GDALAlgorithm::AddOutputDataTypeArg()                 */
    4802             : /************************************************************************/
    4803             : GDALInConstructionAlgorithmArg &
    4804        1738 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
    4805             :                                     const char *helpMessage)
    4806             : {
    4807             :     auto &arg =
    4808             :         AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
    4809        3476 :                MsgOrDefault(helpMessage, _("Output data type")), pValue)
    4810        3476 :             .AddAlias("ot")
    4811        3476 :             .AddAlias("datatype")
    4812        5214 :             .AddMetadataItem("type", {"GDALDataType"})
    4813             :             .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
    4814             :                         "UInt64", "Int64", "CInt16", "CInt32", "Float16",
    4815        1738 :                         "Float32", "Float64", "CFloat32", "CFloat64")
    4816        1738 :             .SetHiddenChoices("Byte");
    4817        1738 :     return arg;
    4818             : }
    4819             : 
    4820             : /************************************************************************/
    4821             : /*                    GDALAlgorithm::AddNodataArg()                     */
    4822             : /************************************************************************/
    4823             : 
    4824             : GDALInConstructionAlgorithmArg &
    4825         646 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
    4826             :                             const std::string &optionName,
    4827             :                             const char *helpMessage)
    4828             : {
    4829             :     auto &arg = AddArg(
    4830             :         optionName, 0,
    4831             :         MsgOrDefault(helpMessage,
    4832             :                      noneAllowed
    4833             :                          ? _("Assign a specified nodata value to output bands "
    4834             :                              "('none', numeric value, 'nan', 'inf', '-inf')")
    4835             :                          : _("Assign a specified nodata value to output bands "
    4836             :                              "(numeric value, 'nan', 'inf', '-inf')")),
    4837         646 :         pValue);
    4838             :     arg.AddValidationAction(
    4839         356 :         [this, pValue, noneAllowed, optionName]()
    4840             :         {
    4841          77 :             if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
    4842             :             {
    4843          67 :                 char *endptr = nullptr;
    4844          67 :                 CPLStrtod(pValue->c_str(), &endptr);
    4845          67 :                 if (endptr != pValue->c_str() + pValue->size())
    4846             :                 {
    4847           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    4848             :                                 "Value of '%s' should be %sa "
    4849             :                                 "numeric value, 'nan', 'inf' or '-inf'",
    4850             :                                 optionName.c_str(),
    4851             :                                 noneAllowed ? "'none', " : "");
    4852           1 :                     return false;
    4853             :                 }
    4854             :             }
    4855          76 :             return true;
    4856         646 :         });
    4857         646 :     return arg;
    4858             : }
    4859             : 
    4860             : /************************************************************************/
    4861             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    4862             : /************************************************************************/
    4863             : 
    4864             : GDALInConstructionAlgorithmArg &
    4865        5888 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
    4866             : {
    4867             :     return AddArg(
    4868             :                GDAL_ARG_NAME_OUTPUT_STRING, 0,
    4869             :                MsgOrDefault(helpMessage,
    4870             :                             _("Output string, in which the result is placed")),
    4871       11776 :                pValue)
    4872        5888 :         .SetHiddenForCLI()
    4873        5888 :         .SetIsInput(false)
    4874       11776 :         .SetIsOutput(true);
    4875             : }
    4876             : 
    4877             : /************************************************************************/
    4878             : /*                    GDALAlgorithm::AddStdoutArg()                     */
    4879             : /************************************************************************/
    4880             : 
    4881             : GDALInConstructionAlgorithmArg &
    4882        1470 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
    4883             : {
    4884             :     return AddArg(GDAL_ARG_NAME_STDOUT, 0,
    4885             :                   MsgOrDefault(helpMessage,
    4886             :                                _("Directly output on stdout. If enabled, "
    4887             :                                  "output-string will be empty")),
    4888        2940 :                   pValue)
    4889        2940 :         .SetHidden();
    4890             : }
    4891             : 
    4892             : /************************************************************************/
    4893             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    4894             : /************************************************************************/
    4895             : 
    4896             : GDALInConstructionAlgorithmArg &
    4897         207 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
    4898             : {
    4899             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    4900         207 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    4901             : }
    4902             : 
    4903             : /************************************************************************/
    4904             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4905             : /************************************************************************/
    4906             : 
    4907             : GDALInConstructionAlgorithmArg &
    4908          50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
    4909             : {
    4910             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
    4911         100 :                   pValue)
    4912           2 :         .SetAutoCompleteFunction([this](const std::string &)
    4913         102 :                                  { return AutoCompleteArrayName(); });
    4914             : }
    4915             : 
    4916             : /************************************************************************/
    4917             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4918             : /************************************************************************/
    4919             : 
    4920             : GDALInConstructionAlgorithmArg &
    4921          76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
    4922             :                                const char *helpMessage)
    4923             : {
    4924             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
    4925         152 :                   pValue)
    4926           0 :         .SetAutoCompleteFunction([this](const std::string &)
    4927         152 :                                  { return AutoCompleteArrayName(); });
    4928             : }
    4929             : 
    4930             : /************************************************************************/
    4931             : /*                GDALAlgorithm::AutoCompleteArrayName()                */
    4932             : /************************************************************************/
    4933             : 
    4934           2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
    4935             : {
    4936           2 :     std::vector<std::string> ret;
    4937           4 :     std::string osDSName;
    4938           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4939           2 :     if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4940             :     {
    4941           0 :         auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4942           0 :         if (!inputDatasets.empty())
    4943             :         {
    4944           0 :             osDSName = inputDatasets[0].GetName();
    4945             :         }
    4946             :     }
    4947           2 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4948             :     {
    4949           2 :         auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
    4950           2 :         osDSName = inputDataset.GetName();
    4951             :     }
    4952             : 
    4953           2 :     if (!osDSName.empty())
    4954             :     {
    4955           4 :         CPLStringList aosAllowedDrivers;
    4956           2 :         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4957           2 :         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    4958             :             aosAllowedDrivers =
    4959           2 :                 CPLStringList(ifArg->Get<std::vector<std::string>>());
    4960             : 
    4961           4 :         CPLStringList aosOpenOptions;
    4962           2 :         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    4963           2 :         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    4964             :             aosOpenOptions =
    4965           2 :                 CPLStringList(ooArg->Get<std::vector<std::string>>());
    4966             : 
    4967           2 :         if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    4968             :                 osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
    4969           4 :                 aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
    4970             :         {
    4971           2 :             if (auto poRG = poDS->GetRootGroup())
    4972             :             {
    4973           1 :                 ret = poRG->GetMDArrayFullNamesRecursive();
    4974             :             }
    4975             :         }
    4976             :     }
    4977             : 
    4978           4 :     return ret;
    4979             : }
    4980             : 
    4981             : /************************************************************************/
    4982             : /*                  GDALAlgorithm::AddMemorySizeArg()                   */
    4983             : /************************************************************************/
    4984             : 
    4985             : GDALInConstructionAlgorithmArg &
    4986         224 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
    4987             :                                 const std::string &optionName,
    4988             :                                 const char *helpMessage)
    4989             : {
    4990         448 :     return AddArg(optionName, 0, helpMessage, pStrValue)
    4991         224 :         .SetDefault(*pStrValue)
    4992             :         .AddValidationAction(
    4993         139 :             [this, pValue, pStrValue]()
    4994             :             {
    4995          47 :                 CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
    4996             :                 GIntBig nBytes;
    4997             :                 bool bUnitSpecified;
    4998          47 :                 if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
    4999          47 :                                        &bUnitSpecified) != CE_None)
    5000             :                 {
    5001           2 :                     return false;
    5002             :                 }
    5003          45 :                 if (!bUnitSpecified)
    5004             :                 {
    5005           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5006             :                                 "Memory size must have a unit or be a "
    5007             :                                 "percentage of usable RAM (2GB, 5%%, etc.)");
    5008           1 :                     return false;
    5009             :                 }
    5010             :                 if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
    5011             :                 {
    5012             :                     // -1 to please CoverityScan
    5013             :                     if (static_cast<std::uint64_t>(nBytes) >
    5014             :                         std::numeric_limits<size_t>::max() - 1U)
    5015             :                     {
    5016             :                         ReportError(CE_Failure, CPLE_AppDefined,
    5017             :                                     "Memory size %s is too large.",
    5018             :                                     pStrValue->c_str());
    5019             :                         return false;
    5020             :                     }
    5021             :                 }
    5022             : 
    5023          44 :                 *pValue = static_cast<size_t>(nBytes);
    5024          44 :                 return true;
    5025         448 :             });
    5026             : }
    5027             : 
    5028             : /************************************************************************/
    5029             : /*                GDALAlgorithm::AddOutputLayerNameArg()                */
    5030             : /************************************************************************/
    5031             : 
    5032             : GDALInConstructionAlgorithmArg &
    5033         474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
    5034             :                                      const char *helpMessage)
    5035             : {
    5036             :     return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
    5037         474 :                   MsgOrDefault(helpMessage, _("Output layer name")), pValue);
    5038             : }
    5039             : 
    5040             : /************************************************************************/
    5041             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    5042             : /************************************************************************/
    5043             : 
    5044             : GDALInConstructionAlgorithmArg &
    5045         882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
    5046             :                                const char *helpMessage)
    5047             : {
    5048             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    5049         882 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    5050             : }
    5051             : 
    5052             : /************************************************************************/
    5053             : /*                 GDALAlgorithm::AddGeometryTypeArg()                  */
    5054             : /************************************************************************/
    5055             : 
    5056             : GDALInConstructionAlgorithmArg &
    5057         422 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
    5058             : {
    5059             :     return AddArg("geometry-type", 0,
    5060         844 :                   MsgOrDefault(helpMessage, _("Geometry type")), pValue)
    5061             :         .SetAutoCompleteFunction(
    5062           3 :             [](const std::string &currentValue)
    5063             :             {
    5064           3 :                 std::vector<std::string> oRet;
    5065          51 :                 for (const char *type :
    5066             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
    5067             :                       "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
    5068             :                       "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
    5069             :                       "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
    5070          54 :                       "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
    5071             :                 {
    5072          68 :                     if (currentValue.empty() ||
    5073          17 :                         STARTS_WITH(type, currentValue.c_str()))
    5074             :                     {
    5075          35 :                         oRet.push_back(type);
    5076          35 :                         oRet.push_back(std::string(type).append("Z"));
    5077          35 :                         oRet.push_back(std::string(type).append("M"));
    5078          35 :                         oRet.push_back(std::string(type).append("ZM"));
    5079             :                     }
    5080             :                 }
    5081           3 :                 return oRet;
    5082         844 :             })
    5083             :         .AddValidationAction(
    5084         118 :             [this, pValue]()
    5085             :             {
    5086         107 :                 if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
    5087         115 :                         wkbUnknown &&
    5088           8 :                     !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
    5089             :                 {
    5090           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5091             :                                 "Invalid geometry type '%s'", pValue->c_str());
    5092           3 :                     return false;
    5093             :                 }
    5094         104 :                 return true;
    5095         844 :             });
    5096             : }
    5097             : 
    5098             : /************************************************************************/
    5099             : /*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
    5100             : /************************************************************************/
    5101             : 
    5102             : /* static */
    5103        2895 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
    5104             :     GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
    5105             : {
    5106        2895 :     CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
    5107             :               datasetArg.GetType() == GAAT_DATASET_LIST);
    5108             : 
    5109             :     layerArg.SetAutoCompleteFunction(
    5110          18 :         [&datasetArg](const std::string &currentValue)
    5111             :         {
    5112           6 :             std::vector<std::string> ret;
    5113          12 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5114           6 :             GDALArgDatasetValue *dsVal = nullptr;
    5115           6 :             if (datasetArg.GetType() == GAAT_DATASET)
    5116             :             {
    5117           0 :                 dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
    5118             :             }
    5119             :             else
    5120             :             {
    5121           6 :                 auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
    5122           6 :                 if (val.size() == 1)
    5123             :                 {
    5124           6 :                     dsVal = &val[0];
    5125             :                 }
    5126             :             }
    5127           6 :             if (dsVal && !dsVal->GetName().empty())
    5128             :             {
    5129             :                 auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5130          12 :                     dsVal->GetName().c_str(), GDAL_OF_VECTOR));
    5131           6 :                 if (poDS)
    5132             :                 {
    5133          12 :                     for (auto &&poLayer : poDS->GetLayers())
    5134             :                     {
    5135           6 :                         if (currentValue == poLayer->GetDescription())
    5136             :                         {
    5137           1 :                             ret.clear();
    5138           1 :                             ret.push_back(poLayer->GetDescription());
    5139           1 :                             break;
    5140             :                         }
    5141           5 :                         ret.push_back(poLayer->GetDescription());
    5142             :                     }
    5143             :                 }
    5144             :             }
    5145          12 :             return ret;
    5146        2895 :         });
    5147        2895 : }
    5148             : 
    5149             : /************************************************************************/
    5150             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
    5151             : /************************************************************************/
    5152             : 
    5153         131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
    5154             :     GDALInConstructionAlgorithmArg &fieldArg,
    5155             :     GDALInConstructionAlgorithmArg &layerNameArg,
    5156             :     std::vector<GDALArgDatasetValue> &datasetArg)
    5157             : {
    5158             : 
    5159             :     fieldArg.SetAutoCompleteFunction(
    5160          12 :         [&datasetArg, &layerNameArg](const std::string &currentValue)
    5161             :         {
    5162           8 :             std::set<std::string> ret;
    5163           4 :             if (!datasetArg.empty())
    5164             :             {
    5165           4 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5166             : 
    5167          14 :                 auto getLayerFields = [&ret, &currentValue](OGRLayer *poLayer)
    5168             :                 {
    5169           2 :                     auto poDefn = poLayer->GetLayerDefn();
    5170           2 :                     const int nFieldCount = poDefn->GetFieldCount();
    5171           8 :                     for (int iField = 0; iField < nFieldCount; iField++)
    5172             :                     {
    5173             :                         const char *fieldName =
    5174           6 :                             poDefn->GetFieldDefn(iField)->GetNameRef();
    5175           6 :                         if (currentValue == fieldName)
    5176             :                         {
    5177           0 :                             ret.clear();
    5178           0 :                             ret.insert(fieldName);
    5179           0 :                             break;
    5180             :                         }
    5181           6 :                         ret.insert(fieldName);
    5182             :                     }
    5183           2 :                 };
    5184             : 
    5185           2 :                 GDALArgDatasetValue &dsVal = datasetArg[0];
    5186             : 
    5187           2 :                 if (!dsVal.GetName().empty())
    5188             :                 {
    5189             :                     auto poDS = std::unique_ptr<GDALDataset>(
    5190           2 :                         GDALDataset::Open(dsVal.GetName().c_str(),
    5191           4 :                                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
    5192           2 :                     if (poDS)
    5193             :                     {
    5194           2 :                         const auto &layerName = layerNameArg.Get<std::string>();
    5195           2 :                         if (layerName.empty())
    5196             :                         {
    5197             :                             // Loop through all layers
    5198           4 :                             for (auto &&poLayer : poDS->GetLayers())
    5199             :                             {
    5200           2 :                                 getLayerFields(poLayer);
    5201             :                             }
    5202             :                         }
    5203             :                         else
    5204             :                         {
    5205           0 :                             const auto poLayer = poDS->GetLayerByName(
    5206           0 :                                 layerNameArg.Get<std::string>().c_str());
    5207           0 :                             if (poLayer)
    5208             :                             {
    5209           0 :                                 getLayerFields(poLayer);
    5210             :                             }
    5211             :                         }
    5212             :                     }
    5213             :                 }
    5214             :             }
    5215           4 :             std::vector<std::string> retVector(ret.begin(), ret.end());
    5216           8 :             return retVector;
    5217         131 :         });
    5218         131 : }
    5219             : 
    5220             : /************************************************************************/
    5221             : /*                   GDALAlgorithm::AddFieldNameArg()                   */
    5222             : /************************************************************************/
    5223             : 
    5224             : GDALInConstructionAlgorithmArg &
    5225         131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
    5226             : {
    5227             :     return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
    5228         131 :                   pValue);
    5229             : }
    5230             : 
    5231             : /************************************************************************/
    5232             : /*                GDALAlgorithm::ParseFieldDefinition()                 */
    5233             : /************************************************************************/
    5234          67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
    5235             :                                          OGRFieldDefn *poFieldDefn,
    5236             :                                          std::string *posError)
    5237             : {
    5238             :     static const std::regex re(
    5239          67 :         R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
    5240         134 :     std::smatch match;
    5241          67 :     if (std::regex_match(posStrDef, match, re))
    5242             :     {
    5243         132 :         const std::string name = match[1];
    5244         132 :         const std::string type = match[2];
    5245          66 :         const int width = match[3].matched ? std::stoi(match[3]) : 0;
    5246          66 :         const int precision = match[4].matched ? std::stoi(match[4]) : 0;
    5247          66 :         poFieldDefn->SetName(name.c_str());
    5248             : 
    5249          66 :         const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
    5250          66 :         if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
    5251             :         {
    5252           1 :             if (posError)
    5253           1 :                 *posError = "Unsupported field type: " + type;
    5254             : 
    5255           1 :             return false;
    5256             :         }
    5257          65 :         poFieldDefn->SetType(typeEnum);
    5258          65 :         poFieldDefn->SetWidth(width);
    5259          65 :         poFieldDefn->SetPrecision(precision);
    5260          65 :         return true;
    5261             :     }
    5262             : 
    5263           1 :     if (posError)
    5264             :         *posError = "Invalid field definition format. Expected "
    5265           1 :                     "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
    5266             : 
    5267           1 :     return false;
    5268             : }
    5269             : 
    5270             : /************************************************************************/
    5271             : /*                GDALAlgorithm::AddFieldDefinitionArg()                */
    5272             : /************************************************************************/
    5273             : 
    5274             : GDALInConstructionAlgorithmArg &
    5275         129 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
    5276             :                                      std::vector<OGRFieldDefn> *pFieldDefns,
    5277             :                                      const char *helpMessage)
    5278             : {
    5279             :     auto &arg =
    5280             :         AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
    5281         258 :                pValues)
    5282         258 :             .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
    5283         129 :             .SetPackedValuesAllowed(true)
    5284         129 :             .SetRepeatedArgAllowed(true);
    5285             : 
    5286         132 :     auto validationFunction = [this, pFieldDefns, pValues]()
    5287             :     {
    5288          65 :         pFieldDefns->clear();
    5289         130 :         for (const auto &strValue : *pValues)
    5290             :         {
    5291          67 :             OGRFieldDefn fieldDefn("", OFTString);
    5292          67 :             std::string error;
    5293          67 :             if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
    5294             :                                                      &error))
    5295             :             {
    5296           2 :                 ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
    5297           2 :                 return false;
    5298             :             }
    5299             :             // Check uniqueness of field names
    5300          67 :             for (const auto &existingFieldDefn : *pFieldDefns)
    5301             :             {
    5302           2 :                 if (EQUAL(existingFieldDefn.GetNameRef(),
    5303             :                           fieldDefn.GetNameRef()))
    5304             :                 {
    5305           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5306             :                                 "Duplicate field name: '%s'",
    5307             :                                 fieldDefn.GetNameRef());
    5308           0 :                     return false;
    5309             :                 }
    5310             :             }
    5311          65 :             pFieldDefns->push_back(fieldDefn);
    5312             :         }
    5313          63 :         return true;
    5314         129 :     };
    5315             : 
    5316         129 :     arg.AddValidationAction(std::move(validationFunction));
    5317             : 
    5318         129 :     return arg;
    5319             : }
    5320             : 
    5321             : /************************************************************************/
    5322             : /*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
    5323             : /************************************************************************/
    5324             : 
    5325         262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
    5326             :     OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
    5327             :     std::string *pStrValue, const std::string &argName, const char *helpMessage)
    5328             : {
    5329             :     auto &arg =
    5330         524 :         AddArg(argName.empty() ? std::string("field-type") : argName, 0,
    5331         786 :                MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
    5332             :             .SetAutoCompleteFunction(
    5333           1 :                 [](const std::string &currentValue)
    5334             :                 {
    5335           1 :                     std::vector<std::string> oRet;
    5336           6 :                     for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
    5337             :                     {
    5338             :                         const char *pszSubType =
    5339           5 :                             OGRFieldDefn::GetFieldSubTypeName(
    5340             :                                 static_cast<OGRFieldSubType>(i));
    5341           5 :                         if (pszSubType != nullptr)
    5342             :                         {
    5343           5 :                             if (currentValue.empty() ||
    5344           0 :                                 STARTS_WITH(pszSubType, currentValue.c_str()))
    5345             :                             {
    5346           5 :                                 oRet.push_back(pszSubType);
    5347             :                             }
    5348             :                         }
    5349             :                     }
    5350             : 
    5351          15 :                     for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
    5352             :                     {
    5353             :                         // Skip deprecated
    5354          14 :                         if (static_cast<OGRFieldType>(i) ==
    5355          13 :                                 OGRFieldType::OFTWideString ||
    5356             :                             static_cast<OGRFieldType>(i) ==
    5357             :                                 OGRFieldType::OFTWideStringList)
    5358           2 :                             continue;
    5359          12 :                         const char *pszType = OGRFieldDefn::GetFieldTypeName(
    5360             :                             static_cast<OGRFieldType>(i));
    5361          12 :                         if (pszType != nullptr)
    5362             :                         {
    5363          12 :                             if (currentValue.empty() ||
    5364           0 :                                 STARTS_WITH(pszType, currentValue.c_str()))
    5365             :                             {
    5366          12 :                                 oRet.push_back(pszType);
    5367             :                             }
    5368             :                         }
    5369             :                     }
    5370           1 :                     return oRet;
    5371         262 :                 });
    5372             : 
    5373             :     auto validationFunction =
    5374         845 :         [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
    5375             :     {
    5376         120 :         bool isValid{true};
    5377         120 :         *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
    5378             : 
    5379             :         // String is returned for unknown types
    5380         120 :         if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
    5381             :         {
    5382          16 :             isValid = false;
    5383             :         }
    5384             : 
    5385         120 :         *pSubtypeValue =
    5386         120 :             OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
    5387             : 
    5388         120 :         if (*pSubtypeValue != OFSTNone)
    5389             :         {
    5390          15 :             isValid = true;
    5391          15 :             switch (*pSubtypeValue)
    5392             :             {
    5393           6 :                 case OFSTBoolean:
    5394             :                 case OFSTInt16:
    5395             :                 {
    5396           6 :                     *pTypeValue = OFTInteger;
    5397           6 :                     break;
    5398             :                 }
    5399           3 :                 case OFSTFloat32:
    5400             :                 {
    5401           3 :                     *pTypeValue = OFTReal;
    5402           3 :                     break;
    5403             :                 }
    5404           6 :                 default:
    5405             :                 {
    5406           6 :                     *pTypeValue = OFTString;
    5407           6 :                     break;
    5408             :                 }
    5409             :             }
    5410             :         }
    5411             : 
    5412         120 :         if (!isValid)
    5413             :         {
    5414           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    5415             :                         "Invalid value for argument '%s': '%s'",
    5416           1 :                         arg.GetName().c_str(), pStrValue->c_str());
    5417             :         }
    5418             : 
    5419         120 :         return isValid;
    5420         262 :     };
    5421             : 
    5422         262 :     if (!pStrValue->empty())
    5423             :     {
    5424           0 :         arg.SetDefault(*pStrValue);
    5425           0 :         validationFunction();
    5426             :     }
    5427             : 
    5428         262 :     arg.AddValidationAction(std::move(validationFunction));
    5429             : 
    5430         262 :     return arg;
    5431             : }
    5432             : 
    5433             : /************************************************************************/
    5434             : /*                   GDALAlgorithm::ValidateBandArg()                   */
    5435             : /************************************************************************/
    5436             : 
    5437        3953 : bool GDALAlgorithm::ValidateBandArg() const
    5438             : {
    5439        3953 :     bool ret = true;
    5440        3953 :     const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
    5441        3953 :     const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
    5442        1450 :     if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
    5443         292 :         (inputDatasetArg->GetType() == GAAT_DATASET ||
    5444        5397 :          inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
    5445         149 :         (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
    5446             :     {
    5447         104 :         const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
    5448             :         {
    5449          99 :             if (nBand > poDS->GetRasterCount())
    5450             :             {
    5451           5 :                 ReportError(CE_Failure, CPLE_AppDefined,
    5452             :                             "Value of 'band' should be greater or equal than "
    5453             :                             "1 and less or equal than %d.",
    5454             :                             poDS->GetRasterCount());
    5455           5 :                 return false;
    5456             :             }
    5457          94 :             return true;
    5458          92 :         };
    5459             : 
    5460             :         const auto ValidateForOneDataset =
    5461         304 :             [&bandArg, &CheckBand](const GDALDataset *poDS)
    5462             :         {
    5463          87 :             bool l_ret = true;
    5464          87 :             if (bandArg->GetType() == GAAT_INTEGER)
    5465             :             {
    5466          24 :                 l_ret = CheckBand(poDS, bandArg->Get<int>());
    5467             :             }
    5468          63 :             else if (bandArg->GetType() == GAAT_INTEGER_LIST)
    5469             :             {
    5470         130 :                 for (int nBand : bandArg->Get<std::vector<int>>())
    5471             :                 {
    5472          75 :                     l_ret = l_ret && CheckBand(poDS, nBand);
    5473             :                 }
    5474             :             }
    5475          87 :             return l_ret;
    5476          92 :         };
    5477             : 
    5478          92 :         if (inputDatasetArg->GetType() == GAAT_DATASET)
    5479             :         {
    5480             :             auto poDS =
    5481           6 :                 inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
    5482           6 :             if (poDS && !ValidateForOneDataset(poDS))
    5483           2 :                 ret = false;
    5484             :         }
    5485             :         else
    5486             :         {
    5487          86 :             CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
    5488          85 :             for (auto &datasetValue :
    5489         256 :                  inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
    5490             :             {
    5491          85 :                 auto poDS = datasetValue.GetDatasetRef();
    5492          85 :                 if (poDS && !ValidateForOneDataset(poDS))
    5493           3 :                     ret = false;
    5494             :             }
    5495             :         }
    5496             :     }
    5497        3953 :     return ret;
    5498             : }
    5499             : 
    5500             : /************************************************************************/
    5501             : /*            GDALAlgorithm::RunPreStepPipelineValidations()            */
    5502             : /************************************************************************/
    5503             : 
    5504        3157 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
    5505             : {
    5506        3157 :     return ValidateBandArg();
    5507             : }
    5508             : 
    5509             : /************************************************************************/
    5510             : /*                     GDALAlgorithm::AddBandArg()                      */
    5511             : /************************************************************************/
    5512             : 
    5513             : GDALInConstructionAlgorithmArg &
    5514        1450 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
    5515             : {
    5516        1769 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5517             : 
    5518             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5519             :                   MsgOrDefault(helpMessage, _("Input band (1-based index)")),
    5520        2900 :                   pValue)
    5521             :         .AddValidationAction(
    5522          34 :             [pValue]()
    5523             :             {
    5524          34 :                 if (*pValue <= 0)
    5525             :                 {
    5526           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5527             :                              "Value of 'band' should greater or equal to 1.");
    5528           1 :                     return false;
    5529             :                 }
    5530          33 :                 return true;
    5531        2900 :             });
    5532             : }
    5533             : 
    5534             : /************************************************************************/
    5535             : /*                     GDALAlgorithm::AddBandArg()                      */
    5536             : /************************************************************************/
    5537             : 
    5538             : GDALInConstructionAlgorithmArg &
    5539         852 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
    5540             : {
    5541        1329 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5542             : 
    5543             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5544             :                   MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
    5545        1704 :                   pValue)
    5546             :         .AddValidationAction(
    5547         126 :             [pValue]()
    5548             :             {
    5549         397 :                 for (int val : *pValue)
    5550             :                 {
    5551         272 :                     if (val <= 0)
    5552             :                     {
    5553           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    5554             :                                  "Value of 'band' should greater or equal "
    5555             :                                  "to 1.");
    5556           1 :                         return false;
    5557             :                     }
    5558             :                 }
    5559         125 :                 return true;
    5560        1704 :             });
    5561             : }
    5562             : 
    5563             : /************************************************************************/
    5564             : /*                      ParseAndValidateKeyValue()                      */
    5565             : /************************************************************************/
    5566             : 
    5567         418 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
    5568             : {
    5569         414 :     const auto Validate = [this, &arg](const std::string &val)
    5570             :     {
    5571         409 :         if (val.find('=') == std::string::npos)
    5572             :         {
    5573           5 :             ReportError(
    5574             :                 CE_Failure, CPLE_AppDefined,
    5575             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    5576           5 :                 arg.GetName().c_str());
    5577           5 :             return false;
    5578             :         }
    5579             : 
    5580         404 :         return true;
    5581         418 :     };
    5582             : 
    5583         418 :     if (arg.GetType() == GAAT_STRING)
    5584             :     {
    5585           0 :         return Validate(arg.Get<std::string>());
    5586             :     }
    5587         418 :     else if (arg.GetType() == GAAT_STRING_LIST)
    5588             :     {
    5589         418 :         std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
    5590         418 :         if (vals.size() == 1)
    5591             :         {
    5592             :             // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
    5593         750 :             std::vector<std::string> newVals;
    5594         750 :             std::string curToken;
    5595         375 :             bool canSplitOnComma = true;
    5596         375 :             char lastSep = 0;
    5597         375 :             bool inString = false;
    5598         375 :             bool equalFoundInLastToken = false;
    5599        5408 :             for (char c : vals[0])
    5600             :             {
    5601        5037 :                 if (!inString && c == ',')
    5602             :                 {
    5603          10 :                     if (lastSep != '=' || !equalFoundInLastToken)
    5604             :                     {
    5605           2 :                         canSplitOnComma = false;
    5606           2 :                         break;
    5607             :                     }
    5608           8 :                     lastSep = c;
    5609           8 :                     newVals.push_back(curToken);
    5610           8 :                     curToken.clear();
    5611           8 :                     equalFoundInLastToken = false;
    5612             :                 }
    5613        5027 :                 else if (!inString && c == '=')
    5614             :                 {
    5615         374 :                     if (lastSep == '=')
    5616             :                     {
    5617           2 :                         canSplitOnComma = false;
    5618           2 :                         break;
    5619             :                     }
    5620         372 :                     equalFoundInLastToken = true;
    5621         372 :                     lastSep = c;
    5622         372 :                     curToken += c;
    5623             :                 }
    5624        4653 :                 else if (c == '"')
    5625             :                 {
    5626           4 :                     inString = !inString;
    5627           4 :                     curToken += c;
    5628             :                 }
    5629             :                 else
    5630             :                 {
    5631        4649 :                     curToken += c;
    5632             :                 }
    5633             :             }
    5634         375 :             if (canSplitOnComma && !inString && equalFoundInLastToken)
    5635             :             {
    5636         362 :                 if (!curToken.empty())
    5637         362 :                     newVals.emplace_back(std::move(curToken));
    5638         362 :                 vals = std::move(newVals);
    5639             :             }
    5640             :         }
    5641             : 
    5642         822 :         for (const auto &val : vals)
    5643             :         {
    5644         409 :             if (!Validate(val))
    5645           5 :                 return false;
    5646             :         }
    5647             :     }
    5648             : 
    5649         413 :     return true;
    5650             : }
    5651             : 
    5652             : /************************************************************************/
    5653             : /*                           IsGDALGOutput()                            */
    5654             : /************************************************************************/
    5655             : 
    5656        2092 : bool GDALAlgorithm::IsGDALGOutput() const
    5657             : {
    5658        2092 :     bool isGDALGOutput = false;
    5659        2092 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5660        2092 :     const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5661        3587 :     if (outputArg && outputArg->GetType() == GAAT_DATASET &&
    5662        1495 :         outputArg->IsExplicitlySet())
    5663             :     {
    5664        2933 :         if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    5665        1454 :             outputFormatArg->IsExplicitlySet())
    5666             :         {
    5667             :             const auto &val =
    5668         949 :                 outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    5669         949 :             isGDALGOutput = EQUAL(val.c_str(), "GDALG");
    5670             :         }
    5671             :         else
    5672             :         {
    5673             :             const auto &filename =
    5674         530 :                 outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
    5675         530 :             isGDALGOutput =
    5676        1033 :                 filename.GetName().size() > strlen(".gdalg.json") &&
    5677         503 :                 EQUAL(filename.GetName().c_str() + filename.GetName().size() -
    5678             :                           strlen(".gdalg.json"),
    5679             :                       ".gdalg.json");
    5680             :         }
    5681             :     }
    5682        2092 :     return isGDALGOutput;
    5683             : }
    5684             : 
    5685             : /************************************************************************/
    5686             : /*                         ProcessGDALGOutput()                         */
    5687             : /************************************************************************/
    5688             : 
    5689        2437 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
    5690             : {
    5691        2437 :     if (!SupportsStreamedOutput())
    5692         798 :         return ProcessGDALGOutputRet::NOT_GDALG;
    5693             : 
    5694        1639 :     if (IsGDALGOutput())
    5695             :     {
    5696          11 :         const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5697             :         const auto &filename =
    5698          11 :             outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
    5699             :         VSIStatBufL sStat;
    5700          11 :         if (VSIStatL(filename.c_str(), &sStat) == 0)
    5701             :         {
    5702           0 :             const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
    5703           0 :             if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
    5704             :             {
    5705           0 :                 if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
    5706             :                 {
    5707           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5708             :                              "File '%s' already exists. Specify the "
    5709             :                              "--overwrite option to overwrite it.",
    5710             :                              filename.c_str());
    5711           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5712             :                 }
    5713             :             }
    5714             :         }
    5715             : 
    5716          22 :         std::string osCommandLine;
    5717             : 
    5718          44 :         for (const auto &path : GDALAlgorithm::m_callPath)
    5719             :         {
    5720          33 :             if (!osCommandLine.empty())
    5721          22 :                 osCommandLine += ' ';
    5722          33 :             osCommandLine += path;
    5723             :         }
    5724             : 
    5725         250 :         for (const auto &arg : GetArgs())
    5726             :         {
    5727         265 :             if (arg->IsExplicitlySet() &&
    5728          41 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
    5729          30 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
    5730         280 :                 arg->GetName() != GDAL_ARG_NAME_UPDATE &&
    5731          15 :                 arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
    5732             :             {
    5733          14 :                 osCommandLine += ' ';
    5734          14 :                 std::string strArg;
    5735          14 :                 if (!arg->Serialize(strArg))
    5736             :                 {
    5737           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5738             :                              "Cannot serialize argument %s",
    5739           0 :                              arg->GetName().c_str());
    5740           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5741             :                 }
    5742          14 :                 osCommandLine += strArg;
    5743             :             }
    5744             :         }
    5745             : 
    5746          11 :         osCommandLine += " --output-format stream --output streamed_dataset";
    5747             : 
    5748          11 :         std::string outStringUnused;
    5749          11 :         return SaveGDALG(filename, outStringUnused, osCommandLine)
    5750          11 :                    ? ProcessGDALGOutputRet::GDALG_OK
    5751          11 :                    : ProcessGDALGOutputRet::GDALG_ERROR;
    5752             :     }
    5753             : 
    5754        1628 :     return ProcessGDALGOutputRet::NOT_GDALG;
    5755             : }
    5756             : 
    5757             : /************************************************************************/
    5758             : /*                      GDALAlgorithm::SaveGDALG()                      */
    5759             : /************************************************************************/
    5760             : 
    5761          22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
    5762             :                                            std::string &outString,
    5763             :                                            const std::string &commandLine)
    5764             : {
    5765          44 :     CPLJSONDocument oDoc;
    5766          22 :     oDoc.GetRoot().Add("type", "gdal_streamed_alg");
    5767          22 :     oDoc.GetRoot().Add("command_line", commandLine);
    5768          22 :     oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
    5769             : 
    5770          22 :     if (!filename.empty())
    5771          21 :         return oDoc.Save(filename);
    5772             : 
    5773           1 :     outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
    5774           1 :     return true;
    5775             : }
    5776             : 
    5777             : /************************************************************************/
    5778             : /*                GDALAlgorithm::AddCreationOptionsArg()                */
    5779             : /************************************************************************/
    5780             : 
    5781             : GDALInConstructionAlgorithmArg &
    5782        8048 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
    5783             :                                      const char *helpMessage)
    5784             : {
    5785             :     auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
    5786       16096 :                        MsgOrDefault(helpMessage, _("Creation option")), pValue)
    5787       16096 :                     .AddAlias("co")
    5788       16096 :                     .SetMetaVar("<KEY>=<VALUE>")
    5789        8048 :                     .SetPackedValuesAllowed(false);
    5790         160 :     arg.AddValidationAction([this, &arg]()
    5791        8208 :                             { return ParseAndValidateKeyValue(arg); });
    5792             : 
    5793             :     arg.SetAutoCompleteFunction(
    5794          48 :         [this](const std::string &currentValue)
    5795             :         {
    5796          16 :             std::vector<std::string> oRet;
    5797             : 
    5798          16 :             int datasetType =
    5799             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    5800          16 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5801          16 :             if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
    5802           0 :                               outputArg->GetType() == GAAT_DATASET_LIST))
    5803             :             {
    5804          16 :                 datasetType = outputArg->GetDatasetType();
    5805             :             }
    5806             : 
    5807          16 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5808          32 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5809          16 :                 outputFormat->IsExplicitlySet())
    5810             :             {
    5811          12 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5812           6 :                     outputFormat->Get<std::string>().c_str());
    5813           6 :                 if (poDriver)
    5814             :                 {
    5815           6 :                     AddOptionsSuggestions(
    5816           6 :                         poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    5817             :                         datasetType, currentValue, oRet);
    5818             :                 }
    5819           6 :                 return oRet;
    5820             :             }
    5821             : 
    5822          10 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5823             :             {
    5824          10 :                 auto poDM = GetGDALDriverManager();
    5825          10 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5826          10 :                 const auto &osDSName = datasetValue.GetName();
    5827          10 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5828          10 :                 if (!osExt.empty())
    5829             :                 {
    5830          10 :                     std::set<std::string> oVisitedExtensions;
    5831         712 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5832             :                     {
    5833         709 :                         auto poDriver = poDM->GetDriver(i);
    5834        2127 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    5835         709 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    5836         216 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    5837        1418 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    5838         216 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    5839           0 :                              poDriver->GetMetadataItem(
    5840           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    5841             :                         {
    5842             :                             const char *pszExtensions =
    5843         493 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5844         493 :                             if (pszExtensions)
    5845             :                             {
    5846             :                                 const CPLStringList aosExts(
    5847         320 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5848         710 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5849             :                                 {
    5850         416 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5851          16 :                                         !cpl::contains(oVisitedExtensions,
    5852             :                                                        pszExt))
    5853             :                                     {
    5854          10 :                                         oVisitedExtensions.insert(pszExt);
    5855          10 :                                         if (AddOptionsSuggestions(
    5856             :                                                 poDriver->GetMetadataItem(
    5857          10 :                                                     GDAL_DMD_CREATIONOPTIONLIST),
    5858             :                                                 datasetType, currentValue,
    5859             :                                                 oRet))
    5860             :                                         {
    5861           7 :                                             return oRet;
    5862             :                                         }
    5863           3 :                                         break;
    5864             :                                     }
    5865             :                                 }
    5866             :                             }
    5867             :                         }
    5868             :                     }
    5869             :                 }
    5870             :             }
    5871             : 
    5872           3 :             return oRet;
    5873        8048 :         });
    5874             : 
    5875        8048 :     return arg;
    5876             : }
    5877             : 
    5878             : /************************************************************************/
    5879             : /*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
    5880             : /************************************************************************/
    5881             : 
    5882             : GDALInConstructionAlgorithmArg &
    5883        3871 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
    5884             :                                           const char *helpMessage)
    5885             : {
    5886             :     auto &arg =
    5887             :         AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
    5888        7742 :                MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
    5889        7742 :             .AddAlias("lco")
    5890        7742 :             .SetMetaVar("<KEY>=<VALUE>")
    5891        3871 :             .SetPackedValuesAllowed(false);
    5892          73 :     arg.AddValidationAction([this, &arg]()
    5893        3944 :                             { return ParseAndValidateKeyValue(arg); });
    5894             : 
    5895             :     arg.SetAutoCompleteFunction(
    5896           5 :         [this](const std::string &currentValue)
    5897             :         {
    5898           2 :             std::vector<std::string> oRet;
    5899             : 
    5900           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5901           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5902           2 :                 outputFormat->IsExplicitlySet())
    5903             :             {
    5904           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5905           1 :                     outputFormat->Get<std::string>().c_str());
    5906           1 :                 if (poDriver)
    5907             :                 {
    5908           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    5909           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5910             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    5911             :                 }
    5912           1 :                 return oRet;
    5913             :             }
    5914             : 
    5915           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5916           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5917             :             {
    5918           1 :                 auto poDM = GetGDALDriverManager();
    5919           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5920           1 :                 const auto &osDSName = datasetValue.GetName();
    5921           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5922           1 :                 if (!osExt.empty())
    5923             :                 {
    5924           1 :                     std::set<std::string> oVisitedExtensions;
    5925         228 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5926             :                     {
    5927         227 :                         auto poDriver = poDM->GetDriver(i);
    5928         227 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    5929             :                         {
    5930             :                             const char *pszExtensions =
    5931          90 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5932          90 :                             if (pszExtensions)
    5933             :                             {
    5934             :                                 const CPLStringList aosExts(
    5935          61 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5936         154 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5937             :                                 {
    5938          95 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5939           1 :                                         !cpl::contains(oVisitedExtensions,
    5940             :                                                        pszExt))
    5941             :                                     {
    5942           1 :                                         oVisitedExtensions.insert(pszExt);
    5943           1 :                                         if (AddOptionsSuggestions(
    5944             :                                                 poDriver->GetMetadataItem(
    5945           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5946             :                                                 GDAL_OF_VECTOR, currentValue,
    5947             :                                                 oRet))
    5948             :                                         {
    5949           0 :                                             return oRet;
    5950             :                                         }
    5951           1 :                                         break;
    5952             :                                     }
    5953             :                                 }
    5954             :                             }
    5955             :                         }
    5956             :                     }
    5957             :                 }
    5958             :             }
    5959             : 
    5960           1 :             return oRet;
    5961        3871 :         });
    5962             : 
    5963        3871 :     return arg;
    5964             : }
    5965             : 
    5966             : /************************************************************************/
    5967             : /*                     GDALAlgorithm::AddBBOXArg()                      */
    5968             : /************************************************************************/
    5969             : 
    5970             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    5971             : GDALInConstructionAlgorithmArg &
    5972        1838 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    5973             : {
    5974             :     auto &arg = AddArg("bbox", 0,
    5975             :                        MsgOrDefault(helpMessage,
    5976             :                                     _("Bounding box as xmin,ymin,xmax,ymax")),
    5977        3676 :                        pValue)
    5978        1838 :                     .SetRepeatedArgAllowed(false)
    5979        1838 :                     .SetMinCount(4)
    5980        1838 :                     .SetMaxCount(4)
    5981        1838 :                     .SetDisplayHintAboutRepetition(false);
    5982             :     arg.AddValidationAction(
    5983         165 :         [&arg]()
    5984             :         {
    5985         165 :             const auto &val = arg.Get<std::vector<double>>();
    5986         165 :             CPLAssert(val.size() == 4);
    5987         165 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    5988             :             {
    5989           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5990             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    5991             :                          "xmin <= xmax and ymin <= ymax");
    5992           5 :                 return false;
    5993             :             }
    5994         160 :             return true;
    5995        1838 :         });
    5996        1838 :     return arg;
    5997             : }
    5998             : 
    5999             : /************************************************************************/
    6000             : /*                  GDALAlgorithm::AddActiveLayerArg()                  */
    6001             : /************************************************************************/
    6002             : 
    6003             : GDALInConstructionAlgorithmArg &
    6004        1719 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
    6005             : {
    6006             :     return AddArg("active-layer", 0,
    6007             :                   MsgOrDefault(helpMessage,
    6008             :                                _("Set active layer (if not specified, all)")),
    6009        1719 :                   pValue);
    6010             : }
    6011             : 
    6012             : /************************************************************************/
    6013             : /*                  GDALAlgorithm::AddNumThreadsArg()                   */
    6014             : /************************************************************************/
    6015             : 
    6016             : GDALInConstructionAlgorithmArg &
    6017         678 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
    6018             :                                 const char *helpMessage)
    6019             : {
    6020             :     auto &arg =
    6021             :         AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
    6022             :                MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
    6023         678 :                pStrValue);
    6024             : 
    6025             :     AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
    6026        1356 :            _("Number of jobs (read-only, hidden argument)"), pValue)
    6027         678 :         .SetHidden();
    6028             : 
    6029        2589 :     auto lambda = [this, &arg, pValue, pStrValue]
    6030             :     {
    6031         863 :         bool bOK = false;
    6032         863 :         const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    6033             :         const int nLimit = std::clamp(
    6034         863 :             pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
    6035        1726 :             CPLGetNumCPUs());
    6036             :         const int nNumThreads =
    6037         863 :             GDALGetNumThreads(pStrValue->c_str(), nLimit,
    6038             :                               /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
    6039         863 :         if (bOK)
    6040             :         {
    6041         863 :             *pValue = nNumThreads;
    6042             :         }
    6043             :         else
    6044             :         {
    6045           0 :             ReportError(CE_Failure, CPLE_IllegalArg,
    6046             :                         "Invalid value for '%s' argument",
    6047           0 :                         arg.GetName().c_str());
    6048             :         }
    6049         863 :         return bOK;
    6050         678 :     };
    6051         678 :     if (!pStrValue->empty())
    6052             :     {
    6053         632 :         arg.SetDefault(*pStrValue);
    6054         632 :         lambda();
    6055             :     }
    6056         678 :     arg.AddValidationAction(std::move(lambda));
    6057         678 :     return arg;
    6058             : }
    6059             : 
    6060             : /************************************************************************/
    6061             : /*                 GDALAlgorithm::AddAbsolutePathArg()                  */
    6062             : /************************************************************************/
    6063             : 
    6064             : GDALInConstructionAlgorithmArg &
    6065         619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
    6066             : {
    6067             :     return AddArg(
    6068             :         "absolute-path", 0,
    6069             :         MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
    6070             :                                     "should be stored as an absolute path")),
    6071         619 :         pValue);
    6072             : }
    6073             : 
    6074             : /************************************************************************/
    6075             : /*               GDALAlgorithm::AddPixelFunctionNameArg()               */
    6076             : /************************************************************************/
    6077             : 
    6078             : GDALInConstructionAlgorithmArg &
    6079         137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
    6080             :                                        const char *helpMessage)
    6081             : {
    6082             : 
    6083             :     const auto pixelFunctionNames =
    6084         137 :         VRTDerivedRasterBand::GetPixelFunctionNames();
    6085             :     return AddArg(
    6086             :                "pixel-function", 0,
    6087             :                MsgOrDefault(
    6088             :                    helpMessage,
    6089             :                    _("Specify a pixel function to calculate output value from "
    6090             :                      "overlapping inputs")),
    6091         274 :                pValue)
    6092         274 :         .SetChoices(pixelFunctionNames);
    6093             : }
    6094             : 
    6095             : /************************************************************************/
    6096             : /*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
    6097             : /************************************************************************/
    6098             : 
    6099             : GDALInConstructionAlgorithmArg &
    6100         137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
    6101             :                                        const char *helpMessage)
    6102             : {
    6103             :     auto &pixelFunctionArgArg =
    6104             :         AddArg("pixel-function-arg", 0,
    6105             :                MsgOrDefault(
    6106             :                    helpMessage,
    6107             :                    _("Specify argument(s) to pass to the pixel function")),
    6108         274 :                pValue)
    6109         274 :             .SetMetaVar("<NAME>=<VALUE>")
    6110         137 :             .SetRepeatedArgAllowed(true);
    6111             :     pixelFunctionArgArg.AddValidationAction(
    6112           7 :         [this, &pixelFunctionArgArg]()
    6113         144 :         { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
    6114             : 
    6115             :     pixelFunctionArgArg.SetAutoCompleteFunction(
    6116          12 :         [this](const std::string &currentValue)
    6117             :         {
    6118          12 :             std::string pixelFunction;
    6119           6 :             const auto pixelFunctionArg = GetArg("pixel-function");
    6120           6 :             if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
    6121             :             {
    6122           6 :                 pixelFunction = pixelFunctionArg->Get<std::string>();
    6123             :             }
    6124             : 
    6125           6 :             std::vector<std::string> ret;
    6126             : 
    6127           6 :             if (!pixelFunction.empty())
    6128             :             {
    6129           5 :                 const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
    6130             :                     pixelFunction.c_str());
    6131           5 :                 if (!pair)
    6132             :                 {
    6133           1 :                     ret.push_back("**");
    6134             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6135           1 :                     ret.push_back(std::string("\xC2\xA0"
    6136             :                                               "Invalid pixel function name"));
    6137             :                 }
    6138           4 :                 else if (pair->second.find("Argument name=") ==
    6139             :                          std::string::npos)
    6140             :                 {
    6141           1 :                     ret.push_back("**");
    6142             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6143           1 :                     ret.push_back(
    6144           2 :                         std::string(
    6145             :                             "\xC2\xA0"
    6146             :                             "No pixel function arguments for pixel function '")
    6147           1 :                             .append(pixelFunction)
    6148           1 :                             .append("'"));
    6149             :                 }
    6150             :                 else
    6151             :                 {
    6152           3 :                     AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
    6153             :                                           ret);
    6154             :                 }
    6155             :             }
    6156             : 
    6157          12 :             return ret;
    6158         137 :         });
    6159             : 
    6160         137 :     return pixelFunctionArgArg;
    6161             : }
    6162             : 
    6163             : /************************************************************************/
    6164             : /*                   GDALAlgorithm::AddProgressArg()                    */
    6165             : /************************************************************************/
    6166             : 
    6167        8165 : void GDALAlgorithm::AddProgressArg()
    6168             : {
    6169             :     AddArg(GDAL_ARG_NAME_QUIET, 'q',
    6170       16330 :            _("Quiet mode (no progress bar or warning message)"), &m_quiet)
    6171        8165 :         .SetAvailableInPipelineStep(false)
    6172       16330 :         .SetCategory(GAAC_COMMON)
    6173        8165 :         .AddAction([this]() { m_progressBarRequested = false; });
    6174             : 
    6175       16330 :     AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
    6176        8165 :         .SetAvailableInPipelineStep(false)
    6177        8165 :         .SetHidden();
    6178        8165 : }
    6179             : 
    6180             : /************************************************************************/
    6181             : /*                         GDALAlgorithm::Run()                         */
    6182             : /************************************************************************/
    6183             : 
    6184        4545 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    6185             : {
    6186        4545 :     WarnIfDeprecated();
    6187             : 
    6188        4545 :     if (m_selectedSubAlg)
    6189             :     {
    6190         411 :         if (m_calledFromCommandLine)
    6191         233 :             m_selectedSubAlg->m_calledFromCommandLine = true;
    6192         411 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    6193             :     }
    6194             : 
    6195        4134 :     if (m_helpRequested || m_helpDocRequested)
    6196             :     {
    6197          16 :         if (m_calledFromCommandLine)
    6198          16 :             printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    6199          16 :         return true;
    6200             :     }
    6201             : 
    6202        4118 :     if (m_JSONUsageRequested)
    6203             :     {
    6204           3 :         if (m_calledFromCommandLine)
    6205           3 :             printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    6206           3 :         return true;
    6207             :     }
    6208             : 
    6209        4115 :     if (!ValidateArguments())
    6210         124 :         return false;
    6211             : 
    6212        3991 :     if (m_alreadyRun)
    6213             :     {
    6214           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    6215             :                     "Run() can be called only once per algorithm instance");
    6216           3 :         return false;
    6217             :     }
    6218        3988 :     m_alreadyRun = true;
    6219             : 
    6220        3988 :     switch (ProcessGDALGOutput())
    6221             :     {
    6222           0 :         case ProcessGDALGOutputRet::GDALG_ERROR:
    6223           0 :             return false;
    6224             : 
    6225          11 :         case ProcessGDALGOutputRet::GDALG_OK:
    6226          11 :             return true;
    6227             : 
    6228        3977 :         case ProcessGDALGOutputRet::NOT_GDALG:
    6229        3977 :             break;
    6230             :     }
    6231             : 
    6232        3977 :     if (m_executionForStreamOutput)
    6233             :     {
    6234          93 :         if (!CheckSafeForStreamOutput())
    6235             :         {
    6236           4 :             return false;
    6237             :         }
    6238             :     }
    6239             : 
    6240        3973 :     return RunImpl(pfnProgress, pProgressData);
    6241             : }
    6242             : 
    6243             : /************************************************************************/
    6244             : /*              GDALAlgorithm::CheckSafeForStreamOutput()               */
    6245             : /************************************************************************/
    6246             : 
    6247          48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
    6248             : {
    6249          48 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6250          48 :     if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
    6251             :     {
    6252          48 :         const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    6253          48 :         if (!EQUAL(val.c_str(), "stream"))
    6254             :         {
    6255             :             // For security reasons, to avoid that reading a .gdalg.json file
    6256             :             // writes a file on the file system.
    6257           4 :             ReportError(
    6258             :                 CE_Failure, CPLE_NotSupported,
    6259             :                 "in streamed execution, --format stream should be used");
    6260           4 :             return false;
    6261             :         }
    6262             :     }
    6263          44 :     return true;
    6264             : }
    6265             : 
    6266             : /************************************************************************/
    6267             : /*                      GDALAlgorithm::Finalize()                       */
    6268             : /************************************************************************/
    6269             : 
    6270        1655 : bool GDALAlgorithm::Finalize()
    6271             : {
    6272        1655 :     bool ret = true;
    6273        1655 :     if (m_selectedSubAlg)
    6274         239 :         ret = m_selectedSubAlg->Finalize();
    6275             : 
    6276       30272 :     for (auto &arg : m_args)
    6277             :     {
    6278       28617 :         if (arg->GetType() == GAAT_DATASET)
    6279             :         {
    6280        1326 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    6281             :         }
    6282       27291 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    6283             :         {
    6284        2540 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    6285             :             {
    6286        1200 :                 ret = ds.Close() && ret;
    6287             :             }
    6288             :         }
    6289             :     }
    6290        1655 :     return ret;
    6291             : }
    6292             : 
    6293             : /************************************************************************/
    6294             : /*                  GDALAlgorithm::GetArgNamesForCLI()                  */
    6295             : /************************************************************************/
    6296             : 
    6297             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    6298         693 : GDALAlgorithm::GetArgNamesForCLI() const
    6299             : {
    6300        1386 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6301             : 
    6302         693 :     size_t maxOptLen = 0;
    6303        8696 :     for (const auto &arg : m_args)
    6304             :     {
    6305        8003 :         if (arg->IsHidden() || arg->IsHiddenForCLI())
    6306        1705 :             continue;
    6307        6298 :         std::string opt;
    6308        6298 :         bool addComma = false;
    6309        6298 :         if (!arg->GetShortName().empty())
    6310             :         {
    6311        1431 :             opt += '-';
    6312        1431 :             opt += arg->GetShortName();
    6313        1431 :             addComma = true;
    6314             :         }
    6315        6298 :         for (char alias : arg->GetShortNameAliases())
    6316             :         {
    6317           0 :             if (addComma)
    6318           0 :                 opt += ", ";
    6319           0 :             opt += "-";
    6320           0 :             opt += alias;
    6321           0 :             addComma = true;
    6322             :         }
    6323        7050 :         for (const std::string &alias : arg->GetAliases())
    6324             :         {
    6325         752 :             if (addComma)
    6326         324 :                 opt += ", ";
    6327         752 :             opt += "--";
    6328         752 :             opt += alias;
    6329         752 :             addComma = true;
    6330             :         }
    6331        6298 :         if (!arg->GetName().empty())
    6332             :         {
    6333        6298 :             if (addComma)
    6334        1859 :                 opt += ", ";
    6335        6298 :             opt += "--";
    6336        6298 :             opt += arg->GetName();
    6337             :         }
    6338        6298 :         const auto &metaVar = arg->GetMetaVar();
    6339        6298 :         if (!metaVar.empty())
    6340             :         {
    6341        3936 :             opt += ' ';
    6342        3936 :             if (metaVar.front() != '<')
    6343        2817 :                 opt += '<';
    6344        3936 :             opt += metaVar;
    6345        3936 :             if (metaVar.back() != '>')
    6346        2811 :                 opt += '>';
    6347             :         }
    6348        6298 :         maxOptLen = std::max(maxOptLen, opt.size());
    6349        6298 :         options.emplace_back(arg.get(), opt);
    6350             :     }
    6351             : 
    6352        1386 :     return std::make_pair(std::move(options), maxOptLen);
    6353             : }
    6354             : 
    6355             : /************************************************************************/
    6356             : /*                   GDALAlgorithm::GetUsageForCLI()                    */
    6357             : /************************************************************************/
    6358             : 
    6359             : std::string
    6360         412 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    6361             :                               const UsageOptions &usageOptions) const
    6362             : {
    6363         412 :     if (m_selectedSubAlg)
    6364           7 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    6365             : 
    6366         810 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    6367         810 :     std::string osPath;
    6368         813 :     for (const std::string &s : m_callPath)
    6369             :     {
    6370         408 :         if (!osPath.empty())
    6371          49 :             osPath += ' ';
    6372         408 :         osPath += s;
    6373             :     }
    6374         405 :     osRet += ' ';
    6375         405 :     osRet += osPath;
    6376             : 
    6377         405 :     bool hasNonPositionals = false;
    6378        5042 :     for (const auto &arg : m_args)
    6379             :     {
    6380        4637 :         if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
    6381        3338 :             hasNonPositionals = true;
    6382             :     }
    6383             : 
    6384         405 :     if (HasSubAlgorithms())
    6385             :     {
    6386           9 :         if (m_callPath.size() == 1)
    6387             :         {
    6388           8 :             osRet += " <COMMAND>";
    6389           8 :             if (hasNonPositionals)
    6390           8 :                 osRet += " [OPTIONS]";
    6391           8 :             if (usageOptions.isPipelineStep)
    6392             :             {
    6393           5 :                 const size_t nLenFirstLine = osRet.size();
    6394           5 :                 osRet += '\n';
    6395           5 :                 osRet.append(nLenFirstLine, '-');
    6396           5 :                 osRet += '\n';
    6397             :             }
    6398           8 :             osRet += "\nwhere <COMMAND> is one of:\n";
    6399             :         }
    6400             :         else
    6401             :         {
    6402           1 :             osRet += " <SUBCOMMAND>";
    6403           1 :             if (hasNonPositionals)
    6404           1 :                 osRet += " [OPTIONS]";
    6405           1 :             if (usageOptions.isPipelineStep)
    6406             :             {
    6407           0 :                 const size_t nLenFirstLine = osRet.size();
    6408           0 :                 osRet += '\n';
    6409           0 :                 osRet.append(nLenFirstLine, '-');
    6410           0 :                 osRet += '\n';
    6411             :             }
    6412           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    6413             :         }
    6414           9 :         size_t maxNameLen = 0;
    6415          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6416             :         {
    6417          43 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    6418             :         }
    6419          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6420             :         {
    6421          86 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6422          43 :             if (subAlg && !subAlg->IsHidden())
    6423             :             {
    6424          43 :                 const std::string &name(subAlg->GetName());
    6425          43 :                 osRet += "  - ";
    6426          43 :                 osRet += name;
    6427          43 :                 osRet += ": ";
    6428          43 :                 osRet.append(maxNameLen - name.size(), ' ');
    6429          43 :                 osRet += subAlg->GetDescription();
    6430          43 :                 if (!subAlg->m_aliases.empty())
    6431             :                 {
    6432           6 :                     bool first = true;
    6433           6 :                     for (const auto &alias : subAlg->GetAliases())
    6434             :                     {
    6435           6 :                         if (alias ==
    6436             :                             GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    6437           6 :                             break;
    6438           0 :                         if (first)
    6439           0 :                             osRet += " (alias: ";
    6440             :                         else
    6441           0 :                             osRet += ", ";
    6442           0 :                         osRet += alias;
    6443           0 :                         first = false;
    6444             :                     }
    6445           6 :                     if (!first)
    6446             :                     {
    6447           0 :                         osRet += ')';
    6448             :                     }
    6449             :                 }
    6450          43 :                 osRet += '\n';
    6451             :             }
    6452             :         }
    6453             : 
    6454           9 :         if (shortUsage && hasNonPositionals)
    6455             :         {
    6456           2 :             osRet += "\nTry '";
    6457           2 :             osRet += osPath;
    6458           2 :             osRet += " --help' for help.\n";
    6459             :         }
    6460             :     }
    6461             :     else
    6462             :     {
    6463         396 :         if (!m_args.empty())
    6464             :         {
    6465         396 :             if (hasNonPositionals)
    6466         396 :                 osRet += " [OPTIONS]";
    6467         584 :             for (const auto *arg : m_positionalArgs)
    6468             :             {
    6469         261 :                 if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
    6470          73 :                     (GetName() == "pipeline" && arg->GetName() == "pipeline"))
    6471             :                 {
    6472             :                     const bool optional =
    6473         199 :                         (!arg->IsRequired() && !(GetName() == "pipeline" &&
    6474          28 :                                                  arg->GetName() == "pipeline"));
    6475         171 :                     osRet += ' ';
    6476         171 :                     if (optional)
    6477          25 :                         osRet += '[';
    6478         171 :                     const std::string &metavar = arg->GetMetaVar();
    6479         171 :                     if (!metavar.empty() && metavar[0] == '<')
    6480             :                     {
    6481           4 :                         osRet += metavar;
    6482             :                     }
    6483             :                     else
    6484             :                     {
    6485         167 :                         osRet += '<';
    6486         167 :                         osRet += metavar;
    6487         167 :                         osRet += '>';
    6488             :                     }
    6489         213 :                     if (arg->GetType() == GAAT_DATASET_LIST &&
    6490          42 :                         arg->GetMaxCount() > 1)
    6491             :                     {
    6492          28 :                         osRet += "...";
    6493             :                     }
    6494         171 :                     if (optional)
    6495          25 :                         osRet += ']';
    6496             :                 }
    6497             :             }
    6498             :         }
    6499             : 
    6500         396 :         const size_t nLenFirstLine = osRet.size();
    6501         396 :         osRet += '\n';
    6502         396 :         if (usageOptions.isPipelineStep)
    6503             :         {
    6504         309 :             osRet.append(nLenFirstLine, '-');
    6505         309 :             osRet += '\n';
    6506             :         }
    6507             : 
    6508         396 :         if (shortUsage)
    6509             :         {
    6510          21 :             osRet += "Try '";
    6511          21 :             osRet += osPath;
    6512          21 :             osRet += " --help' for help.\n";
    6513          21 :             return osRet;
    6514             :         }
    6515             : 
    6516         375 :         osRet += '\n';
    6517         375 :         osRet += m_description;
    6518         375 :         osRet += '\n';
    6519             :     }
    6520             : 
    6521         384 :     if (!m_args.empty() && !shortUsage)
    6522             :     {
    6523         764 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6524             :         size_t maxOptLen;
    6525         382 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    6526         382 :         if (usageOptions.maxOptLen)
    6527         311 :             maxOptLen = usageOptions.maxOptLen;
    6528             : 
    6529         764 :         const std::string userProvidedOpt = "--<user-provided-option>=<value>";
    6530         382 :         if (m_arbitraryLongNameArgsAllowed)
    6531           2 :             maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
    6532             : 
    6533             :         const auto OutputArg =
    6534        2368 :             [this, maxOptLen, &osRet,
    6535       23548 :              &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
    6536             :         {
    6537        2368 :             osRet += "  ";
    6538        2368 :             osRet += opt;
    6539        2368 :             osRet += "  ";
    6540        2368 :             osRet.append(maxOptLen - opt.size(), ' ');
    6541        2368 :             osRet += arg->GetDescription();
    6542             : 
    6543        2368 :             const auto &choices = arg->GetChoices();
    6544        2368 :             if (!choices.empty())
    6545             :             {
    6546         224 :                 osRet += ". ";
    6547         224 :                 osRet += arg->GetMetaVar();
    6548         224 :                 osRet += '=';
    6549         224 :                 bool firstChoice = true;
    6550        1725 :                 for (const auto &choice : choices)
    6551             :                 {
    6552        1501 :                     if (!firstChoice)
    6553        1277 :                         osRet += '|';
    6554        1501 :                     osRet += choice;
    6555        1501 :                     firstChoice = false;
    6556             :                 }
    6557             :             }
    6558             : 
    6559        4670 :             if (arg->GetType() == GAAT_DATASET ||
    6560        2302 :                 arg->GetType() == GAAT_DATASET_LIST)
    6561             :             {
    6562         144 :                 if (arg->IsOutput() &&
    6563         144 :                     arg->GetDatasetInputFlags() == GADV_NAME &&
    6564           9 :                     arg->GetDatasetOutputFlags() == GADV_OBJECT)
    6565             :                 {
    6566           9 :                     osRet += " (created by algorithm)";
    6567             :                 }
    6568             :             }
    6569             : 
    6570        2368 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    6571             :             {
    6572         190 :                 osRet += " (default: ";
    6573         190 :                 osRet += arg->GetDefault<std::string>();
    6574         190 :                 osRet += ')';
    6575             :             }
    6576        2178 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    6577             :             {
    6578          66 :                 if (arg->GetDefault<bool>())
    6579           0 :                     osRet += " (default: true)";
    6580             :             }
    6581        2112 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    6582             :             {
    6583          76 :                 osRet += " (default: ";
    6584          76 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    6585          76 :                 osRet += ')';
    6586             :             }
    6587        2036 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    6588             :             {
    6589          49 :                 osRet += " (default: ";
    6590          49 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    6591          49 :                 osRet += ')';
    6592             :             }
    6593        2399 :             else if (arg->GetType() == GAAT_STRING_LIST &&
    6594         412 :                      arg->HasDefaultValue())
    6595             :             {
    6596             :                 const auto &defaultVal =
    6597           9 :                     arg->GetDefault<std::vector<std::string>>();
    6598           9 :                 if (defaultVal.size() == 1)
    6599             :                 {
    6600           9 :                     osRet += " (default: ";
    6601           9 :                     osRet += defaultVal[0];
    6602           9 :                     osRet += ')';
    6603             :                 }
    6604             :             }
    6605        2005 :             else if (arg->GetType() == GAAT_INTEGER_LIST &&
    6606          27 :                      arg->HasDefaultValue())
    6607             :             {
    6608           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<int>>();
    6609           0 :                 if (defaultVal.size() == 1)
    6610             :                 {
    6611           0 :                     osRet += " (default: ";
    6612           0 :                     osRet += CPLSPrintf("%d", defaultVal[0]);
    6613           0 :                     osRet += ')';
    6614             :                 }
    6615             :             }
    6616        1978 :             else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
    6617             :             {
    6618           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<double>>();
    6619           0 :                 if (defaultVal.size() == 1)
    6620             :                 {
    6621           0 :                     osRet += " (default: ";
    6622           0 :                     osRet += CPLSPrintf("%g", defaultVal[0]);
    6623           0 :                     osRet += ')';
    6624             :                 }
    6625             :             }
    6626             : 
    6627        2368 :             if (arg->GetDisplayHintAboutRepetition())
    6628             :             {
    6629        2401 :                 if (arg->GetMinCount() > 0 &&
    6630          92 :                     arg->GetMinCount() == arg->GetMaxCount())
    6631             :                 {
    6632          18 :                     if (arg->GetMinCount() != 1)
    6633           5 :                         osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    6634             :                 }
    6635        2365 :                 else if (arg->GetMinCount() > 0 &&
    6636          74 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    6637             :                 {
    6638             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    6639           8 :                                         arg->GetMaxCount());
    6640             :                 }
    6641        2283 :                 else if (arg->GetMinCount() > 0)
    6642             :                 {
    6643          66 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    6644             :                 }
    6645        2217 :                 else if (arg->GetMaxCount() > 1)
    6646             :                 {
    6647         401 :                     osRet += " [may be repeated]";
    6648             :                 }
    6649             :             }
    6650             : 
    6651        2368 :             if (arg->IsRequired())
    6652             :             {
    6653         169 :                 osRet += " [required]";
    6654             :             }
    6655             : 
    6656        2622 :             if (!arg->IsAvailableInPipelineStep() &&
    6657         254 :                 !usageOptions.isPipelineStep)
    6658             :             {
    6659          28 :                 osRet += " [not available in pipelines]";
    6660             :             }
    6661             : 
    6662        2368 :             osRet += '\n';
    6663             : 
    6664        2368 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6665        2368 :             if (!mutualExclusionGroup.empty())
    6666             :             {
    6667         410 :                 std::string otherArgs;
    6668        3953 :                 for (const auto &otherArg : m_args)
    6669             :                 {
    6670        6840 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6671        3092 :                         otherArg.get() == arg)
    6672         861 :                         continue;
    6673        2887 :                     if (otherArg->GetMutualExclusionGroup() ==
    6674             :                         mutualExclusionGroup)
    6675             :                     {
    6676         278 :                         if (!otherArgs.empty())
    6677          77 :                             otherArgs += ", ";
    6678         278 :                         otherArgs += "--";
    6679         278 :                         otherArgs += otherArg->GetName();
    6680             :                     }
    6681             :                 }
    6682         205 :                 if (!otherArgs.empty())
    6683             :                 {
    6684         201 :                     osRet += "  ";
    6685         201 :                     osRet += "  ";
    6686         201 :                     osRet.append(maxOptLen, ' ');
    6687         201 :                     osRet += "Mutually exclusive with ";
    6688         201 :                     osRet += otherArgs;
    6689         201 :                     osRet += '\n';
    6690             :                 }
    6691             :             }
    6692             : 
    6693             :             // Check dependency
    6694        4736 :             std::string dependencyArgs;
    6695             : 
    6696          31 :             for (const auto &dependencyArgumentName :
    6697        2430 :                  GetArgDependencies(arg->GetName()))
    6698             :             {
    6699          31 :                 const auto otherArg{GetArg(dependencyArgumentName)};
    6700          31 :                 if (otherArg != nullptr)
    6701             :                 {
    6702          31 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6703             :                         otherArg == arg)
    6704             :                     {
    6705           0 :                         continue;
    6706             :                     }
    6707             : 
    6708          31 :                     if (!dependencyArgs.empty())
    6709             :                     {
    6710           3 :                         dependencyArgs += ", ";
    6711             :                     }
    6712             : 
    6713          31 :                     dependencyArgs += "--";
    6714          31 :                     dependencyArgs += otherArg->GetName();
    6715             :                 }
    6716             :                 else
    6717             :                 {
    6718           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6719             :                              "Argument '%s' depends on unknown argument '%s'",
    6720           0 :                              arg->GetName().c_str(),
    6721             :                              dependencyArgumentName.c_str());
    6722             :                 }
    6723             :             }
    6724             : 
    6725        2368 :             if (!dependencyArgs.empty())
    6726             :             {
    6727          28 :                 osRet += "  ";
    6728          28 :                 osRet += "  ";
    6729          28 :                 osRet.append(maxOptLen, ' ');
    6730          28 :                 osRet += "Depends on ";
    6731          28 :                 osRet += dependencyArgs;
    6732          28 :                 osRet += '\n';
    6733             :             }
    6734        2368 :         };
    6735             : 
    6736         382 :         if (!m_positionalArgs.empty())
    6737             :         {
    6738         150 :             osRet += "\nPositional arguments:\n";
    6739        1613 :             for (const auto &[arg, opt] : options)
    6740             :             {
    6741        1463 :                 if (arg->IsPositional())
    6742         141 :                     OutputArg(arg, opt);
    6743             :             }
    6744             :         }
    6745             : 
    6746         382 :         if (hasNonPositionals)
    6747             :         {
    6748         382 :             bool hasCommon = false;
    6749         382 :             bool hasBase = false;
    6750         382 :             bool hasAdvanced = false;
    6751         382 :             bool hasEsoteric = false;
    6752         764 :             std::vector<std::string> categories;
    6753        3721 :             for (const auto &iter : options)
    6754             :             {
    6755        3339 :                 const auto &arg = iter.first;
    6756        3339 :                 if (!arg->IsPositional())
    6757             :                 {
    6758        3198 :                     const auto &category = arg->GetCategory();
    6759        3198 :                     if (category == GAAC_COMMON)
    6760             :                     {
    6761        1189 :                         hasCommon = true;
    6762             :                     }
    6763        2009 :                     else if (category == GAAC_BASE)
    6764             :                     {
    6765        1769 :                         hasBase = true;
    6766             :                     }
    6767         240 :                     else if (category == GAAC_ADVANCED)
    6768             :                     {
    6769         178 :                         hasAdvanced = true;
    6770             :                     }
    6771          62 :                     else if (category == GAAC_ESOTERIC)
    6772             :                     {
    6773          29 :                         hasEsoteric = true;
    6774             :                     }
    6775          33 :                     else if (std::find(categories.begin(), categories.end(),
    6776          33 :                                        category) == categories.end())
    6777             :                     {
    6778           9 :                         categories.push_back(category);
    6779             :                     }
    6780             :                 }
    6781             :             }
    6782         382 :             if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
    6783          67 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    6784         382 :             if (hasBase)
    6785         336 :                 categories.insert(categories.begin(), GAAC_BASE);
    6786         382 :             if (hasCommon && !usageOptions.isPipelineStep)
    6787          68 :                 categories.insert(categories.begin(), GAAC_COMMON);
    6788         382 :             if (hasEsoteric)
    6789          11 :                 categories.push_back(GAAC_ESOTERIC);
    6790             : 
    6791         873 :             for (const auto &category : categories)
    6792             :             {
    6793         491 :                 osRet += "\n";
    6794         491 :                 if (category != GAAC_BASE)
    6795             :                 {
    6796         155 :                     osRet += category;
    6797         155 :                     osRet += ' ';
    6798             :                 }
    6799         491 :                 osRet += "Options:\n";
    6800        5344 :                 for (const auto &[arg, opt] : options)
    6801             :                 {
    6802        4853 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    6803        2227 :                         OutputArg(arg, opt);
    6804             :                 }
    6805         491 :                 if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
    6806             :                 {
    6807           2 :                     osRet += "  ";
    6808           2 :                     osRet += userProvidedOpt;
    6809           2 :                     osRet += "  ";
    6810           2 :                     if (userProvidedOpt.size() < maxOptLen)
    6811           0 :                         osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
    6812           2 :                     osRet += "Argument provided by user";
    6813           2 :                     osRet += '\n';
    6814             :                 }
    6815             :             }
    6816             :         }
    6817             :     }
    6818             : 
    6819         384 :     if (!m_longDescription.empty())
    6820             :     {
    6821           6 :         osRet += '\n';
    6822           6 :         osRet += m_longDescription;
    6823           6 :         osRet += '\n';
    6824             :     }
    6825             : 
    6826         384 :     if (!m_helpDocRequested && !usageOptions.isPipelineMain)
    6827             :     {
    6828         371 :         if (!m_helpURL.empty())
    6829             :         {
    6830         371 :             osRet += "\nFor more details, consult ";
    6831         371 :             osRet += GetHelpFullURL();
    6832         371 :             osRet += '\n';
    6833             :         }
    6834         371 :         osRet += GetUsageForCLIEnd();
    6835             :     }
    6836             : 
    6837         384 :     return osRet;
    6838             : }
    6839             : 
    6840             : /************************************************************************/
    6841             : /*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
    6842             : /************************************************************************/
    6843             : 
    6844             : //! @cond Doxygen_Suppress
    6845         378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
    6846             : {
    6847         378 :     std::string osRet;
    6848             : 
    6849         378 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    6850             :     {
    6851             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    6852             :                  "alternative interface to GDAL and OGR command line "
    6853             :                  "utilities.\nThe project reserves the right to modify, "
    6854             :                  "rename, reorganize, and change the behavior of the utility\n"
    6855             :                  "until it is officially frozen in a future feature release of "
    6856          13 :                  "GDAL.\n";
    6857             :     }
    6858         378 :     return osRet;
    6859             : }
    6860             : 
    6861             : //! @endcond
    6862             : 
    6863             : /************************************************************************/
    6864             : /*                   GDALAlgorithm::GetUsageAsJSON()                    */
    6865             : /************************************************************************/
    6866             : 
    6867         563 : std::string GDALAlgorithm::GetUsageAsJSON() const
    6868             : {
    6869        1126 :     CPLJSONDocument oDoc;
    6870        1126 :     auto oRoot = oDoc.GetRoot();
    6871             : 
    6872         563 :     if (m_displayInJSONUsage)
    6873             :     {
    6874         561 :         oRoot.Add("name", m_name);
    6875         561 :         CPLJSONArray jFullPath;
    6876        1168 :         for (const std::string &s : m_callPath)
    6877             :         {
    6878         607 :             jFullPath.Add(s);
    6879             :         }
    6880         561 :         oRoot.Add("full_path", jFullPath);
    6881             :     }
    6882             : 
    6883         563 :     oRoot.Add("description", m_description);
    6884         563 :     if (!m_helpURL.empty())
    6885             :     {
    6886         562 :         oRoot.Add("short_url", m_helpURL);
    6887         562 :         oRoot.Add("url", GetHelpFullURL());
    6888             :     }
    6889             : 
    6890        1126 :     CPLJSONArray jSubAlgorithms;
    6891         763 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    6892             :     {
    6893         400 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6894         200 :         if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
    6895             :         {
    6896         198 :             CPLJSONDocument oSubDoc;
    6897         198 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    6898         198 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    6899             :         }
    6900             :     }
    6901         563 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    6902             : 
    6903         563 :     if (m_arbitraryLongNameArgsAllowed)
    6904             :     {
    6905           1 :         oRoot.Add("user_provided_arguments_allowed", true);
    6906             :     }
    6907             : 
    6908       10828 :     const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
    6909             :     {
    6910        5414 :         CPLJSONObject jArg;
    6911        5414 :         jArg.Add("name", arg->GetName());
    6912        5414 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    6913        5414 :         jArg.Add("description", arg->GetDescription());
    6914             : 
    6915        5414 :         const auto &metaVar = arg->GetMetaVar();
    6916        5414 :         if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
    6917             :         {
    6918        1671 :             if (metaVar.front() == '<' && metaVar.back() == '>' &&
    6919        1671 :                 metaVar.substr(1, metaVar.size() - 2).find('>') ==
    6920             :                     std::string::npos)
    6921          32 :                 jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
    6922             :             else
    6923         888 :                 jArg.Add("metavar", metaVar);
    6924             :         }
    6925             : 
    6926        5414 :         if (!arg->IsAvailableInPipelineStep())
    6927             :         {
    6928        1622 :             jArg.Add("available_in_pipeline_step", false);
    6929             :         }
    6930             : 
    6931        5414 :         const auto &choices = arg->GetChoices();
    6932        5414 :         if (!choices.empty())
    6933             :         {
    6934         408 :             CPLJSONArray jChoices;
    6935        3500 :             for (const auto &choice : choices)
    6936        3092 :                 jChoices.Add(choice);
    6937         408 :             jArg.Add("choices", jChoices);
    6938             :         }
    6939        5414 :         if (arg->HasDefaultValue())
    6940             :         {
    6941        1188 :             switch (arg->GetType())
    6942             :             {
    6943         424 :                 case GAAT_BOOLEAN:
    6944         424 :                     jArg.Add("default", arg->GetDefault<bool>());
    6945         424 :                     break;
    6946         366 :                 case GAAT_STRING:
    6947         366 :                     jArg.Add("default", arg->GetDefault<std::string>());
    6948         366 :                     break;
    6949         200 :                 case GAAT_INTEGER:
    6950         200 :                     jArg.Add("default", arg->GetDefault<int>());
    6951         200 :                     break;
    6952         178 :                 case GAAT_REAL:
    6953         178 :                     jArg.Add("default", arg->GetDefault<double>());
    6954         178 :                     break;
    6955          18 :                 case GAAT_STRING_LIST:
    6956             :                 {
    6957             :                     const auto &val =
    6958          18 :                         arg->GetDefault<std::vector<std::string>>();
    6959          18 :                     if (val.size() == 1)
    6960             :                     {
    6961          17 :                         jArg.Add("default", val[0]);
    6962             :                     }
    6963             :                     else
    6964             :                     {
    6965           1 :                         CPLJSONArray jArr;
    6966           3 :                         for (const auto &s : val)
    6967             :                         {
    6968           2 :                             jArr.Add(s);
    6969             :                         }
    6970           1 :                         jArg.Add("default", jArr);
    6971             :                     }
    6972          18 :                     break;
    6973             :                 }
    6974           1 :                 case GAAT_INTEGER_LIST:
    6975             :                 {
    6976           1 :                     const auto &val = arg->GetDefault<std::vector<int>>();
    6977           1 :                     if (val.size() == 1)
    6978             :                     {
    6979           0 :                         jArg.Add("default", val[0]);
    6980             :                     }
    6981             :                     else
    6982             :                     {
    6983           1 :                         CPLJSONArray jArr;
    6984           3 :                         for (int i : val)
    6985             :                         {
    6986           2 :                             jArr.Add(i);
    6987             :                         }
    6988           1 :                         jArg.Add("default", jArr);
    6989             :                     }
    6990           1 :                     break;
    6991             :                 }
    6992           1 :                 case GAAT_REAL_LIST:
    6993             :                 {
    6994           1 :                     const auto &val = arg->GetDefault<std::vector<double>>();
    6995           1 :                     if (val.size() == 1)
    6996             :                     {
    6997           0 :                         jArg.Add("default", val[0]);
    6998             :                     }
    6999             :                     else
    7000             :                     {
    7001           1 :                         CPLJSONArray jArr;
    7002           3 :                         for (double d : val)
    7003             :                         {
    7004           2 :                             jArr.Add(d);
    7005             :                         }
    7006           1 :                         jArg.Add("default", jArr);
    7007             :                     }
    7008           1 :                     break;
    7009             :                 }
    7010           0 :                 case GAAT_DATASET:
    7011             :                 case GAAT_DATASET_LIST:
    7012           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    7013             :                              "Unhandled default value for arg %s",
    7014           0 :                              arg->GetName().c_str());
    7015           0 :                     break;
    7016             :             }
    7017             :         }
    7018             : 
    7019        5414 :         const auto [minVal, minValIsIncluded] = arg->GetMinValue();
    7020        5414 :         if (!std::isnan(minVal))
    7021             :         {
    7022         671 :             if (arg->GetType() == GAAT_INTEGER ||
    7023         261 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7024         173 :                 jArg.Add("min_value", static_cast<int>(minVal));
    7025             :             else
    7026         237 :                 jArg.Add("min_value", minVal);
    7027         410 :             jArg.Add("min_value_is_included", minValIsIncluded);
    7028             :         }
    7029             : 
    7030        5414 :         const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
    7031        5414 :         if (!std::isnan(maxVal))
    7032             :         {
    7033         199 :             if (arg->GetType() == GAAT_INTEGER ||
    7034          82 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7035          35 :                 jArg.Add("max_value", static_cast<int>(maxVal));
    7036             :             else
    7037          82 :                 jArg.Add("max_value", maxVal);
    7038         117 :             jArg.Add("max_value_is_included", maxValIsIncluded);
    7039             :         }
    7040             : 
    7041        5414 :         jArg.Add("required", arg->IsRequired());
    7042        5414 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    7043             :         {
    7044        1519 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    7045        1519 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    7046        1519 :             jArg.Add("min_count", arg->GetMinCount());
    7047        1519 :             jArg.Add("max_count", arg->GetMaxCount());
    7048             :         }
    7049             : 
    7050             :         // Process dependencies
    7051        5414 :         const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7052        5414 :         if (!mutualDependencyGroup.empty())
    7053             :         {
    7054          32 :             jArg.Add("mutual_dependency_group", mutualDependencyGroup);
    7055             :         }
    7056             : 
    7057       10828 :         CPLJSONArray jDependencies;
    7058          48 :         for (const auto &dependencyArgumentName :
    7059        5510 :              GetArgDependencies(arg->GetName()))
    7060             :         {
    7061          48 :             jDependencies.Add(dependencyArgumentName);
    7062             :         }
    7063             : 
    7064        5414 :         if (jDependencies.Size() > 0)
    7065             :         {
    7066          48 :             jArg.Add("depends_on", jDependencies);
    7067             :         }
    7068             : 
    7069        5414 :         jArg.Add("category", arg->GetCategory());
    7070             : 
    7071       10578 :         if (arg->GetType() == GAAT_DATASET ||
    7072        5164 :             arg->GetType() == GAAT_DATASET_LIST)
    7073             :         {
    7074             :             {
    7075         453 :                 CPLJSONArray jAr;
    7076         453 :                 if (arg->GetDatasetType() & GDAL_OF_RASTER)
    7077         311 :                     jAr.Add("raster");
    7078         453 :                 if (arg->GetDatasetType() & GDAL_OF_VECTOR)
    7079         179 :                     jAr.Add("vector");
    7080         453 :                 if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
    7081          28 :                     jAr.Add("multidim_raster");
    7082         453 :                 jArg.Add("dataset_type", jAr);
    7083             :             }
    7084             : 
    7085         620 :             const auto GetFlags = [](int flags)
    7086             :             {
    7087         620 :                 CPLJSONArray jAr;
    7088         620 :                 if (flags & GADV_NAME)
    7089         453 :                     jAr.Add("name");
    7090         620 :                 if (flags & GADV_OBJECT)
    7091         571 :                     jAr.Add("dataset");
    7092         620 :                 return jAr;
    7093             :             };
    7094             : 
    7095         453 :             if (arg->IsInput())
    7096             :             {
    7097         453 :                 jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
    7098             :             }
    7099         453 :             if (arg->IsOutput())
    7100             :             {
    7101         167 :                 jArg.Add("output_flags",
    7102         334 :                          GetFlags(arg->GetDatasetOutputFlags()));
    7103             :             }
    7104             :         }
    7105             : 
    7106        5414 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    7107        5414 :         if (!mutualExclusionGroup.empty())
    7108             :         {
    7109         659 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    7110             :         }
    7111             : 
    7112       10828 :         const auto &metadata = arg->GetMetadata();
    7113        5414 :         if (!metadata.empty())
    7114             :         {
    7115         450 :             CPLJSONObject jMetadata;
    7116         939 :             for (const auto &[key, values] : metadata)
    7117             :             {
    7118         978 :                 CPLJSONArray jValue;
    7119        1183 :                 for (const auto &value : values)
    7120         694 :                     jValue.Add(value);
    7121         489 :                 jMetadata.Add(key, jValue);
    7122             :             }
    7123         450 :             jArg.Add("metadata", jMetadata);
    7124             :         }
    7125             : 
    7126       10828 :         return jArg;
    7127         563 :     };
    7128             : 
    7129             :     {
    7130         563 :         CPLJSONArray jArgs;
    7131        8913 :         for (const auto &arg : m_args)
    7132             :         {
    7133        8350 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
    7134        5190 :                 jArgs.Add(ProcessArg(arg.get()));
    7135             :         }
    7136         563 :         oRoot.Add("input_arguments", jArgs);
    7137             :     }
    7138             : 
    7139             :     {
    7140         563 :         CPLJSONArray jArgs;
    7141        8913 :         for (const auto &arg : m_args)
    7142             :         {
    7143        8350 :             if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
    7144          57 :                 jArgs.Add(ProcessArg(arg.get()));
    7145             :         }
    7146         563 :         oRoot.Add("output_arguments", jArgs);
    7147             :     }
    7148             : 
    7149             :     {
    7150         563 :         CPLJSONArray jArgs;
    7151        8913 :         for (const auto &arg : m_args)
    7152             :         {
    7153        8350 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
    7154         167 :                 jArgs.Add(ProcessArg(arg.get()));
    7155             :         }
    7156         563 :         oRoot.Add("input_output_arguments", jArgs);
    7157             :     }
    7158             : 
    7159         563 :     if (m_supportsStreamedOutput)
    7160             :     {
    7161         117 :         oRoot.Add("supports_streamed_output", true);
    7162             :     }
    7163             : 
    7164        1126 :     return oDoc.SaveAsString();
    7165             : }
    7166             : 
    7167             : /************************************************************************/
    7168             : /*                   GDALAlgorithm::GetAutoComplete()                   */
    7169             : /************************************************************************/
    7170             : 
    7171             : std::vector<std::string>
    7172         246 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    7173             :                                bool lastWordIsComplete, bool showAllOptions)
    7174             : {
    7175         492 :     std::vector<std::string> ret;
    7176             : 
    7177             :     // Get inner-most algorithm
    7178         246 :     std::unique_ptr<GDALAlgorithm> curAlgHolder;
    7179         246 :     GDALAlgorithm *curAlg = this;
    7180         487 :     while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
    7181             :     {
    7182             :         auto subAlg = curAlg->InstantiateSubAlgorithm(
    7183         350 :             args.front(), /* suggestionAllowed = */ false);
    7184         350 :         if (!subAlg)
    7185         108 :             break;
    7186         242 :         if (args.size() == 1 && !lastWordIsComplete)
    7187             :         {
    7188           5 :             int nCount = 0;
    7189         115 :             for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
    7190             :             {
    7191         110 :                 if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
    7192           6 :                     nCount++;
    7193             :             }
    7194           5 :             if (nCount >= 2)
    7195             :             {
    7196          11 :                 for (const std::string &subAlgName :
    7197          23 :                      curAlg->GetSubAlgorithmNames())
    7198             :                 {
    7199          11 :                     subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
    7200          11 :                     if (subAlg && !subAlg->IsHidden())
    7201          11 :                         ret.push_back(subAlg->GetName());
    7202             :                 }
    7203           1 :                 return ret;
    7204             :             }
    7205             :         }
    7206         241 :         showAllOptions = false;
    7207         241 :         args.erase(args.begin());
    7208         241 :         curAlgHolder = std::move(subAlg);
    7209         241 :         curAlg = curAlgHolder.get();
    7210             :     }
    7211         245 :     if (curAlg != this)
    7212             :     {
    7213         131 :         curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
    7214             :         return curAlg->GetAutoComplete(args, lastWordIsComplete,
    7215         131 :                                        /* showAllOptions = */ false);
    7216             :     }
    7217             : 
    7218         228 :     std::string option;
    7219         228 :     std::string value;
    7220         114 :     ExtractLastOptionAndValue(args, option, value);
    7221             : 
    7222         141 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    7223          27 :         args.back()[0] == '-')
    7224             :     {
    7225          24 :         const auto &lastArg = args.back();
    7226             :         // List available options
    7227         349 :         for (const auto &arg : GetArgs())
    7228             :         {
    7229         603 :             if (arg->IsHidden() || arg->IsHiddenForCLI() ||
    7230         551 :                 (!showAllOptions &&
    7231         750 :                  (arg->GetName() == "help" || arg->GetName() == "config" ||
    7232         454 :                   arg->GetName() == "version" ||
    7233         227 :                   arg->GetName() == "json-usage")))
    7234             :             {
    7235         116 :                 continue;
    7236             :             }
    7237         209 :             if (!arg->GetShortName().empty())
    7238             :             {
    7239         126 :                 std::string str = std::string("-").append(arg->GetShortName());
    7240          42 :                 if (lastArg == str)
    7241           0 :                     ret.push_back(std::move(str));
    7242             :             }
    7243         209 :             if (lastArg != "-" && lastArg != "--")
    7244             :             {
    7245          54 :                 for (const std::string &alias : arg->GetAliases())
    7246             :                 {
    7247          48 :                     std::string str = std::string("--").append(alias);
    7248          16 :                     if (cpl::starts_with(str, lastArg))
    7249           3 :                         ret.push_back(std::move(str));
    7250             :                 }
    7251             :             }
    7252         209 :             if (!arg->GetName().empty())
    7253             :             {
    7254         627 :                 std::string str = std::string("--").append(arg->GetName());
    7255         209 :                 if (cpl::starts_with(str, lastArg))
    7256         173 :                     ret.push_back(std::move(str));
    7257             :             }
    7258             :         }
    7259          24 :         std::sort(ret.begin(), ret.end());
    7260             :     }
    7261          90 :     else if (!option.empty())
    7262             :     {
    7263             :         // List possible choices for current option
    7264          84 :         auto arg = GetArg(option);
    7265          84 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7266             :         {
    7267          84 :             ret = arg->GetChoices();
    7268          84 :             if (ret.empty())
    7269             :             {
    7270             :                 {
    7271          79 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7272          79 :                     SetParseForAutoCompletion();
    7273          79 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7274             :                 }
    7275          79 :                 ret = arg->GetAutoCompleteChoices(value);
    7276             :             }
    7277             :             else
    7278             :             {
    7279           5 :                 std::sort(ret.begin(), ret.end());
    7280             :             }
    7281          84 :             if (!ret.empty() && ret.back() == value)
    7282             :             {
    7283           2 :                 ret.clear();
    7284             :             }
    7285          82 :             else if (ret.empty())
    7286             :             {
    7287          10 :                 ret.push_back("**");
    7288             :                 // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    7289          20 :                 ret.push_back(std::string("\xC2\xA0"
    7290             :                                           "description: ")
    7291          10 :                                   .append(arg->GetDescription()));
    7292             :             }
    7293             :         }
    7294             :     }
    7295             :     else
    7296             :     {
    7297             :         // List possible sub-algorithms
    7298          59 :         for (const std::string &subAlgName : GetSubAlgorithmNames())
    7299             :         {
    7300         106 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7301          53 :             if (subAlg && !subAlg->IsHidden())
    7302          53 :                 ret.push_back(subAlg->GetName());
    7303             :         }
    7304           6 :         if (!ret.empty())
    7305             :         {
    7306           2 :             std::sort(ret.begin(), ret.end());
    7307             :         }
    7308             : 
    7309             :         // Try filenames
    7310           6 :         if (ret.empty() && !args.empty())
    7311             :         {
    7312             :             {
    7313           3 :                 CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7314           3 :                 SetParseForAutoCompletion();
    7315           3 :                 CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7316             :             }
    7317             : 
    7318           3 :             const std::string &lastArg = args.back();
    7319           3 :             GDALAlgorithmArg *arg = nullptr;
    7320          18 :             for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
    7321          21 :                                      "like", "source", "destination"})
    7322             :             {
    7323          18 :                 if (!arg)
    7324             :                 {
    7325           3 :                     auto newArg = GetArg(name);
    7326           3 :                     if (newArg)
    7327             :                     {
    7328           3 :                         if (!newArg->IsExplicitlySet())
    7329             :                         {
    7330           0 :                             arg = newArg;
    7331             :                         }
    7332           6 :                         else if (newArg->GetType() == GAAT_STRING ||
    7333           5 :                                  newArg->GetType() == GAAT_STRING_LIST ||
    7334           8 :                                  newArg->GetType() == GAAT_DATASET ||
    7335           2 :                                  newArg->GetType() == GAAT_DATASET_LIST)
    7336             :                         {
    7337             :                             VSIStatBufL sStat;
    7338           5 :                             if ((!lastArg.empty() && lastArg.back() == '/') ||
    7339           2 :                                 VSIStatL(lastArg.c_str(), &sStat) != 0)
    7340             :                             {
    7341           3 :                                 arg = newArg;
    7342             :                             }
    7343             :                         }
    7344             :                     }
    7345             :                 }
    7346             :             }
    7347           3 :             if (arg)
    7348             :             {
    7349           3 :                 ret = arg->GetAutoCompleteChoices(lastArg);
    7350             :             }
    7351             :         }
    7352             :     }
    7353             : 
    7354         114 :     return ret;
    7355             : }
    7356             : 
    7357             : /************************************************************************/
    7358             : /*                   GDALAlgorithm::GetFieldIndices()                   */
    7359             : /************************************************************************/
    7360             : 
    7361          44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
    7362             :                                     OGRLayerH hLayer, std::vector<int> &indices)
    7363             : {
    7364          44 :     VALIDATE_POINTER1(hLayer, __func__, false);
    7365             : 
    7366          44 :     const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
    7367             : 
    7368          44 :     if (names.size() == 1 && names[0] == "ALL")
    7369             :     {
    7370          12 :         const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
    7371          28 :         for (int i = 0; i < nSrcFieldCount; ++i)
    7372             :         {
    7373          16 :             indices.push_back(i);
    7374             :         }
    7375             :     }
    7376          32 :     else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
    7377             :     {
    7378           6 :         std::set<int> fieldsAdded;
    7379          14 :         for (const std::string &osFieldName : names)
    7380             :         {
    7381             : 
    7382             :             const int nIdx =
    7383          10 :                 layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
    7384             : 
    7385          10 :             if (nIdx < 0)
    7386             :             {
    7387           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7388             :                          "Field '%s' does not exist in layer '%s'",
    7389           2 :                          osFieldName.c_str(), layer.GetName());
    7390           2 :                 return false;
    7391             :             }
    7392             : 
    7393           8 :             if (fieldsAdded.insert(nIdx).second)
    7394             :             {
    7395           7 :                 indices.push_back(nIdx);
    7396             :             }
    7397             :         }
    7398             :     }
    7399             : 
    7400          42 :     return true;
    7401             : }
    7402             : 
    7403             : /************************************************************************/
    7404             : /*              GDALAlgorithm::ExtractLastOptionAndValue()              */
    7405             : /************************************************************************/
    7406             : 
    7407         114 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    7408             :                                               std::string &option,
    7409             :                                               std::string &value) const
    7410             : {
    7411         114 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    7412             :     {
    7413          84 :         const auto nPosEqual = args.back().find('=');
    7414          84 :         if (nPosEqual == std::string::npos)
    7415             :         {
    7416             :             // Deal with "gdal ... --option"
    7417          65 :             if (GetArg(args.back()))
    7418             :             {
    7419          41 :                 option = args.back();
    7420          41 :                 args.pop_back();
    7421             :             }
    7422             :         }
    7423             :         else
    7424             :         {
    7425             :             // Deal with "gdal ... --option=<value>"
    7426          19 :             if (GetArg(args.back().substr(0, nPosEqual)))
    7427             :             {
    7428          19 :                 option = args.back().substr(0, nPosEqual);
    7429          19 :                 value = args.back().substr(nPosEqual + 1);
    7430          19 :                 args.pop_back();
    7431             :             }
    7432             :         }
    7433             :     }
    7434          55 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    7435          25 :              args[args.size() - 2][0] == '-')
    7436             :     {
    7437             :         // Deal with "gdal ... --option <value>"
    7438          24 :         auto arg = GetArg(args[args.size() - 2]);
    7439          24 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7440             :         {
    7441          24 :             option = args[args.size() - 2];
    7442          24 :             value = args.back();
    7443          24 :             args.pop_back();
    7444             :         }
    7445             :     }
    7446             : 
    7447         114 :     const auto IsKeyValueOption = [](const std::string &osStr)
    7448             :     {
    7449         308 :         return osStr == "--co" || osStr == "--creation-option" ||
    7450         285 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    7451         306 :                osStr == "--oo" || osStr == "--open-option";
    7452             :     };
    7453             : 
    7454         114 :     if (IsKeyValueOption(option))
    7455             :     {
    7456          22 :         const auto nPosEqual = value.find('=');
    7457          22 :         if (nPosEqual != std::string::npos)
    7458             :         {
    7459          11 :             value.resize(nPosEqual);
    7460             :         }
    7461             :     }
    7462         114 : }
    7463             : 
    7464             : //! @cond Doxygen_Suppress
    7465             : 
    7466             : /************************************************************************/
    7467             : /*                  GDALContainerAlgorithm::RunImpl()                   */
    7468             : /************************************************************************/
    7469             : 
    7470           0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
    7471             : {
    7472           0 :     return false;
    7473             : }
    7474             : 
    7475             : //! @endcond
    7476             : 
    7477             : /************************************************************************/
    7478             : /*                        GDALAlgorithmRelease()                        */
    7479             : /************************************************************************/
    7480             : 
    7481             : /** Release a handle to an algorithm.
    7482             :  *
    7483             :  * @since 3.11
    7484             :  */
    7485       12337 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    7486             : {
    7487       12337 :     delete hAlg;
    7488       12337 : }
    7489             : 
    7490             : /************************************************************************/
    7491             : /*                        GDALAlgorithmGetName()                        */
    7492             : /************************************************************************/
    7493             : 
    7494             : /** Return the algorithm name.
    7495             :  *
    7496             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7497             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    7498             :  * be freed.
    7499             :  * @since 3.11
    7500             :  */
    7501        5699 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    7502             : {
    7503        5699 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7504        5699 :     return hAlg->ptr->GetName().c_str();
    7505             : }
    7506             : 
    7507             : /************************************************************************/
    7508             : /*                    GDALAlgorithmGetDescription()                     */
    7509             : /************************************************************************/
    7510             : 
    7511             : /** Return the algorithm (short) description.
    7512             :  *
    7513             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7514             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7515             :  * not be freed.
    7516             :  * @since 3.11
    7517             :  */
    7518        5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    7519             : {
    7520        5619 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7521        5619 :     return hAlg->ptr->GetDescription().c_str();
    7522             : }
    7523             : 
    7524             : /************************************************************************/
    7525             : /*                  GDALAlgorithmGetLongDescription()                   */
    7526             : /************************************************************************/
    7527             : 
    7528             : /** Return the algorithm (longer) description.
    7529             :  *
    7530             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7531             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7532             :  * not be freed.
    7533             :  * @since 3.11
    7534             :  */
    7535           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    7536             : {
    7537           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7538           2 :     return hAlg->ptr->GetLongDescription().c_str();
    7539             : }
    7540             : 
    7541             : /************************************************************************/
    7542             : /*                    GDALAlgorithmGetHelpFullURL()                     */
    7543             : /************************************************************************/
    7544             : 
    7545             : /** Return the algorithm full URL.
    7546             :  *
    7547             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7548             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    7549             :  * not be freed.
    7550             :  * @since 3.11
    7551             :  */
    7552        4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    7553             : {
    7554        4963 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7555        4963 :     return hAlg->ptr->GetHelpFullURL().c_str();
    7556             : }
    7557             : 
    7558             : /************************************************************************/
    7559             : /*                   GDALAlgorithmHasSubAlgorithms()                    */
    7560             : /************************************************************************/
    7561             : 
    7562             : /** Return whether the algorithm has sub-algorithms.
    7563             :  *
    7564             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7565             :  * @since 3.11
    7566             :  */
    7567        9105 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    7568             : {
    7569        9105 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7570        9105 :     return hAlg->ptr->HasSubAlgorithms();
    7571             : }
    7572             : 
    7573             : /************************************************************************/
    7574             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    7575             : /************************************************************************/
    7576             : 
    7577             : /** Get the names of registered algorithms.
    7578             :  *
    7579             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7580             :  * @return a NULL terminated list of names, which must be destroyed with
    7581             :  * CSLDestroy()
    7582             :  * @since 3.11
    7583             :  */
    7584         707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    7585             : {
    7586         707 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7587         707 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    7588             : }
    7589             : 
    7590             : /************************************************************************/
    7591             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    7592             : /************************************************************************/
    7593             : 
    7594             : /** Instantiate an algorithm by its name (or its alias).
    7595             :  *
    7596             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7597             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    7598             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    7599             :  * or NULL if the algorithm does not exist or another error occurred.
    7600             :  * @since 3.11
    7601             :  */
    7602        8568 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    7603             :                                                     const char *pszSubAlgName)
    7604             : {
    7605        8568 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7606        8568 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    7607       17136 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    7608             :     return subAlg
    7609       17136 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    7610       17136 :                : nullptr;
    7611             : }
    7612             : 
    7613             : /************************************************************************/
    7614             : /*               GDALAlgorithmParseCommandLineArguments()               */
    7615             : /************************************************************************/
    7616             : 
    7617             : /** Parse a command line argument, which does not include the algorithm
    7618             :  * name, to set the value of corresponding arguments.
    7619             :  *
    7620             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7621             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    7622             :  * @return true if successful, false otherwise
    7623             :  * @since 3.11
    7624             :  */
    7625             : 
    7626         352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    7627             :                                             CSLConstList papszArgs)
    7628             : {
    7629         352 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7630         352 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    7631             : }
    7632             : 
    7633             : /************************************************************************/
    7634             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    7635             : /************************************************************************/
    7636             : 
    7637             : /** Return the actual algorithm that is going to be invoked, when the
    7638             :  * current algorithm has sub-algorithms.
    7639             :  *
    7640             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    7641             :  *
    7642             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    7643             :  * the hAlg instance that owns it.
    7644             :  *
    7645             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7646             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    7647             :  * @since 3.11
    7648             :  */
    7649         914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    7650             : {
    7651         914 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7652         914 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    7653             : }
    7654             : 
    7655             : /************************************************************************/
    7656             : /*                          GDALAlgorithmRun()                          */
    7657             : /************************************************************************/
    7658             : 
    7659             : /** Execute the algorithm, starting with ValidateArguments() and then
    7660             :  * calling RunImpl().
    7661             :  *
    7662             :  * This function must be called at most once per instance.
    7663             :  *
    7664             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7665             :  * @param pfnProgress Progress callback. May be null.
    7666             :  * @param pProgressData Progress callback user data. May be null.
    7667             :  * @return true if successful, false otherwise
    7668             :  * @since 3.11
    7669             :  */
    7670             : 
    7671        2678 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    7672             :                       void *pProgressData)
    7673             : {
    7674        2678 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7675        2678 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    7676             : }
    7677             : 
    7678             : /************************************************************************/
    7679             : /*                       GDALAlgorithmFinalize()                        */
    7680             : /************************************************************************/
    7681             : 
    7682             : /** Complete any pending actions, and return the final status.
    7683             :  * This is typically useful for algorithm that generate an output dataset.
    7684             :  *
    7685             :  * Note that this function does *NOT* release memory associated with the
    7686             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    7687             :  *
    7688             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7689             :  * @return true if successful, false otherwise
    7690             :  * @since 3.11
    7691             :  */
    7692             : 
    7693         891 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    7694             : {
    7695         891 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7696         891 :     return hAlg->ptr->Finalize();
    7697             : }
    7698             : 
    7699             : /************************************************************************/
    7700             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    7701             : /************************************************************************/
    7702             : 
    7703             : /** Return the usage of the algorithm as a JSON-serialized string.
    7704             :  *
    7705             :  * This can be used to dynamically generate interfaces to algorithms.
    7706             :  *
    7707             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7708             :  * @return a string that must be freed with CPLFree()
    7709             :  * @since 3.11
    7710             :  */
    7711           6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    7712             : {
    7713           6 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7714           6 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    7715             : }
    7716             : 
    7717             : /************************************************************************/
    7718             : /*                      GDALAlgorithmGetArgNames()                      */
    7719             : /************************************************************************/
    7720             : 
    7721             : /** Return the list of available argument names.
    7722             :  *
    7723             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7724             :  * @return a NULL terminated list of names, which must be destroyed with
    7725             :  * CSLDestroy()
    7726             :  * @since 3.11
    7727             :  */
    7728       15428 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    7729             : {
    7730       15428 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7731       30856 :     CPLStringList list;
    7732      343849 :     for (const auto &arg : hAlg->ptr->GetArgs())
    7733      328421 :         list.AddString(arg->GetName().c_str());
    7734       15428 :     return list.StealList();
    7735             : }
    7736             : 
    7737             : /************************************************************************/
    7738             : /*                        GDALAlgorithmGetArg()                         */
    7739             : /************************************************************************/
    7740             : 
    7741             : /** Return an argument from its name.
    7742             :  *
    7743             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7744             :  *
    7745             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7746             :  * @param pszArgName Argument name. Must NOT be null.
    7747             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7748             :  * or nullptr in case of error
    7749             :  * @since 3.11
    7750             :  */
    7751      329331 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    7752             :                                       const char *pszArgName)
    7753             : {
    7754      329331 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7755      329331 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7756      658662 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7757      329331 :                                  /* isConst = */ true);
    7758      329331 :     if (!arg)
    7759           3 :         return nullptr;
    7760      329328 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7761             : }
    7762             : 
    7763             : /************************************************************************/
    7764             : /*                    GDALAlgorithmGetArgNonConst()                     */
    7765             : /************************************************************************/
    7766             : 
    7767             : /** Return an argument from its name, possibly allowing creation of user-provided
    7768             :  * argument if the algorithm allow it.
    7769             :  *
    7770             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7771             :  *
    7772             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7773             :  * @param pszArgName Argument name. Must NOT be null.
    7774             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7775             :  * or nullptr in case of error
    7776             :  * @since 3.12
    7777             :  */
    7778        9647 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
    7779             :                                               const char *pszArgName)
    7780             : {
    7781        9647 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7782        9647 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7783       19294 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7784        9647 :                                  /* isConst = */ false);
    7785        9647 :     if (!arg)
    7786           2 :         return nullptr;
    7787        9645 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7788             : }
    7789             : 
    7790             : /************************************************************************/
    7791             : /*                  GDALAlgorithmGetArgDependencies()                   */
    7792             : /************************************************************************/
    7793             : 
    7794             : /** Return the list of argument names the specified argument depends on.
    7795             :  *
    7796             :  *  This includes both regular dependencies and mutual dependencies.
    7797             :  *
    7798             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7799             :  * @param pszArgName Argument name. Must NOT be null.
    7800             :  * @return a NULL terminated list of names, which must be destroyed with
    7801             :  * CSLDestroy()
    7802             :  * @since 3.11
    7803             :  */
    7804           7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
    7805             :                                        const char *pszArgName)
    7806             : {
    7807           7 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7808           7 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7809           7 :     return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
    7810             : }
    7811             : 
    7812             : /************************************************************************/
    7813             : /*                      GDALAlgorithmArgRelease()                       */
    7814             : /************************************************************************/
    7815             : 
    7816             : /** Release a handle to an argument.
    7817             :  *
    7818             :  * @since 3.11
    7819             :  */
    7820      338973 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    7821             : {
    7822      338973 :     delete hArg;
    7823      338973 : }
    7824             : 
    7825             : /************************************************************************/
    7826             : /*                      GDALAlgorithmArgGetName()                       */
    7827             : /************************************************************************/
    7828             : 
    7829             : /** Return the name of an argument.
    7830             :  *
    7831             :  * @param hArg Handle to an argument. Must NOT be null.
    7832             :  * @return argument name whose lifetime is bound to hArg and which must not
    7833             :  * be freed.
    7834             :  * @since 3.11
    7835             :  */
    7836       18824 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    7837             : {
    7838       18824 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7839       18824 :     return hArg->ptr->GetName().c_str();
    7840             : }
    7841             : 
    7842             : /************************************************************************/
    7843             : /*                      GDALAlgorithmArgGetType()                       */
    7844             : /************************************************************************/
    7845             : 
    7846             : /** Get the type of an argument
    7847             :  *
    7848             :  * @param hArg Handle to an argument. Must NOT be null.
    7849             :  * @since 3.11
    7850             :  */
    7851      419148 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    7852             : {
    7853      419148 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    7854      419148 :     return hArg->ptr->GetType();
    7855             : }
    7856             : 
    7857             : /************************************************************************/
    7858             : /*                   GDALAlgorithmArgGetDescription()                   */
    7859             : /************************************************************************/
    7860             : 
    7861             : /** Return the description of an argument.
    7862             :  *
    7863             :  * @param hArg Handle to an argument. Must NOT be null.
    7864             :  * @return argument description whose lifetime is bound to hArg and which must not
    7865             :  * be freed.
    7866             :  * @since 3.11
    7867             :  */
    7868       83600 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    7869             : {
    7870       83600 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7871       83600 :     return hArg->ptr->GetDescription().c_str();
    7872             : }
    7873             : 
    7874             : /************************************************************************/
    7875             : /*                    GDALAlgorithmArgGetShortName()                    */
    7876             : /************************************************************************/
    7877             : 
    7878             : /** Return the short name, or empty string if there is none
    7879             :  *
    7880             :  * @param hArg Handle to an argument. Must NOT be null.
    7881             :  * @return short name whose lifetime is bound to hArg and which must not
    7882             :  * be freed.
    7883             :  * @since 3.11
    7884             :  */
    7885           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    7886             : {
    7887           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7888           1 :     return hArg->ptr->GetShortName().c_str();
    7889             : }
    7890             : 
    7891             : /************************************************************************/
    7892             : /*                     GDALAlgorithmArgGetAliases()                     */
    7893             : /************************************************************************/
    7894             : 
    7895             : /** Return the aliases (potentially none)
    7896             :  *
    7897             :  * @param hArg Handle to an argument. Must NOT be null.
    7898             :  * @return a NULL terminated list of names, which must be destroyed with
    7899             :  * CSLDestroy()
    7900             : 
    7901             :  * @since 3.11
    7902             :  */
    7903      158179 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    7904             : {
    7905      158179 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7906      158179 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    7907             : }
    7908             : 
    7909             : /************************************************************************/
    7910             : /*                     GDALAlgorithmArgGetMetaVar()                     */
    7911             : /************************************************************************/
    7912             : 
    7913             : /** Return the "meta-var" hint.
    7914             :  *
    7915             :  * By default, the meta-var value is the long name of the argument in
    7916             :  * upper case.
    7917             :  *
    7918             :  * @param hArg Handle to an argument. Must NOT be null.
    7919             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    7920             :  * be freed.
    7921             :  * @since 3.11
    7922             :  */
    7923           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    7924             : {
    7925           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7926           1 :     return hArg->ptr->GetMetaVar().c_str();
    7927             : }
    7928             : 
    7929             : /************************************************************************/
    7930             : /*                    GDALAlgorithmArgGetCategory()                     */
    7931             : /************************************************************************/
    7932             : 
    7933             : /** Return the argument category
    7934             :  *
    7935             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    7936             :  *
    7937             :  * @param hArg Handle to an argument. Must NOT be null.
    7938             :  * @return category whose lifetime is bound to hArg and which must not
    7939             :  * be freed.
    7940             :  * @since 3.11
    7941             :  */
    7942           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    7943             : {
    7944           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7945           1 :     return hArg->ptr->GetCategory().c_str();
    7946             : }
    7947             : 
    7948             : /************************************************************************/
    7949             : /*                    GDALAlgorithmArgIsPositional()                    */
    7950             : /************************************************************************/
    7951             : 
    7952             : /** Return if the argument is a positional one.
    7953             :  *
    7954             :  * @param hArg Handle to an argument. Must NOT be null.
    7955             :  * @since 3.11
    7956             :  */
    7957           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    7958             : {
    7959           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    7960           1 :     return hArg->ptr->IsPositional();
    7961             : }
    7962             : 
    7963             : /************************************************************************/
    7964             : /*                     GDALAlgorithmArgIsRequired()                     */
    7965             : /************************************************************************/
    7966             : 
    7967             : /** Return whether the argument is required. Defaults to false.
    7968             :  *
    7969             :  * @param hArg Handle to an argument. Must NOT be null.
    7970             :  * @since 3.11
    7971             :  */
    7972      158179 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    7973             : {
    7974      158179 :     VALIDATE_POINTER1(hArg, __func__, false);
    7975      158179 :     return hArg->ptr->IsRequired();
    7976             : }
    7977             : 
    7978             : /************************************************************************/
    7979             : /*                    GDALAlgorithmArgGetMinCount()                     */
    7980             : /************************************************************************/
    7981             : 
    7982             : /** Return the minimum number of values for the argument.
    7983             :  *
    7984             :  * Defaults to 0.
    7985             :  * Only applies to list type of arguments.
    7986             :  *
    7987             :  * @param hArg Handle to an argument. Must NOT be null.
    7988             :  * @since 3.11
    7989             :  */
    7990           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    7991             : {
    7992           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    7993           1 :     return hArg->ptr->GetMinCount();
    7994             : }
    7995             : 
    7996             : /************************************************************************/
    7997             : /*                    GDALAlgorithmArgGetMaxCount()                     */
    7998             : /************************************************************************/
    7999             : 
    8000             : /** Return the maximum number of values for the argument.
    8001             :  *
    8002             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    8003             :  * Only applies to list type of arguments.
    8004             :  *
    8005             :  * @param hArg Handle to an argument. Must NOT be null.
    8006             :  * @since 3.11
    8007             :  */
    8008           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    8009             : {
    8010           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8011           1 :     return hArg->ptr->GetMaxCount();
    8012             : }
    8013             : 
    8014             : /************************************************************************/
    8015             : /*               GDALAlgorithmArgGetPackedValuesAllowed()               */
    8016             : /************************************************************************/
    8017             : 
    8018             : /** Return whether, for list type of arguments, several values, space
    8019             :  * separated, may be specified. That is "--foo=bar,baz".
    8020             :  * The default is true.
    8021             :  *
    8022             :  * @param hArg Handle to an argument. Must NOT be null.
    8023             :  * @since 3.11
    8024             :  */
    8025           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    8026             : {
    8027           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8028           1 :     return hArg->ptr->GetPackedValuesAllowed();
    8029             : }
    8030             : 
    8031             : /************************************************************************/
    8032             : /*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
    8033             : /************************************************************************/
    8034             : 
    8035             : /** Return whether, for list type of arguments, the argument may be
    8036             :  * repeated. That is "--foo=bar --foo=baz".
    8037             :  * The default is true.
    8038             :  *
    8039             :  * @param hArg Handle to an argument. Must NOT be null.
    8040             :  * @since 3.11
    8041             :  */
    8042           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    8043             : {
    8044           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8045           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    8046             : }
    8047             : 
    8048             : /************************************************************************/
    8049             : /*                     GDALAlgorithmArgGetChoices()                     */
    8050             : /************************************************************************/
    8051             : 
    8052             : /** Return the allowed values (as strings) for the argument.
    8053             :  *
    8054             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    8055             :  *
    8056             :  * @param hArg Handle to an argument. Must NOT be null.
    8057             :  * @return a NULL terminated list of names, which must be destroyed with
    8058             :  * CSLDestroy()
    8059             : 
    8060             :  * @since 3.11
    8061             :  */
    8062           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    8063             : {
    8064           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8065           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    8066             : }
    8067             : 
    8068             : /************************************************************************/
    8069             : /*                  GDALAlgorithmArgGetMetadataItem()                   */
    8070             : /************************************************************************/
    8071             : 
    8072             : /** Return the values of the metadata item of an argument.
    8073             :  *
    8074             :  * @param hArg Handle to an argument. Must NOT be null.
    8075             :  * @param pszItem Name of the item. Must NOT be null.
    8076             :  * @return a NULL terminated list of values, which must be destroyed with
    8077             :  * CSLDestroy()
    8078             : 
    8079             :  * @since 3.11
    8080             :  */
    8081          63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
    8082             :                                        const char *pszItem)
    8083             : {
    8084          63 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8085          63 :     VALIDATE_POINTER1(pszItem, __func__, nullptr);
    8086          63 :     const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
    8087          63 :     return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
    8088             : }
    8089             : 
    8090             : /************************************************************************/
    8091             : /*                  GDALAlgorithmArgIsExplicitlySet()                   */
    8092             : /************************************************************************/
    8093             : 
    8094             : /** Return whether the argument value has been explicitly set with Set()
    8095             :  *
    8096             :  * @param hArg Handle to an argument. Must NOT be null.
    8097             :  * @since 3.11
    8098             :  */
    8099         626 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    8100             : {
    8101         626 :     VALIDATE_POINTER1(hArg, __func__, false);
    8102         626 :     return hArg->ptr->IsExplicitlySet();
    8103             : }
    8104             : 
    8105             : /************************************************************************/
    8106             : /*                  GDALAlgorithmArgHasDefaultValue()                   */
    8107             : /************************************************************************/
    8108             : 
    8109             : /** Return if the argument has a declared default value.
    8110             :  *
    8111             :  * @param hArg Handle to an argument. Must NOT be null.
    8112             :  * @since 3.11
    8113             :  */
    8114           2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    8115             : {
    8116           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8117           2 :     return hArg->ptr->HasDefaultValue();
    8118             : }
    8119             : 
    8120             : /************************************************************************/
    8121             : /*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
    8122             : /************************************************************************/
    8123             : 
    8124             : /** Return the argument default value as a integer.
    8125             :  *
    8126             :  * Must only be called on arguments whose type is GAAT_BOOLEAN
    8127             :  *
    8128             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8129             :  * argument has a default value.
    8130             :  *
    8131             :  * @param hArg Handle to an argument. Must NOT be null.
    8132             :  * @since 3.12
    8133             :  */
    8134           3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
    8135             : {
    8136           3 :     VALIDATE_POINTER1(hArg, __func__, false);
    8137           3 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8138             :     {
    8139           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8140             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8141             :                  __func__);
    8142           1 :         return false;
    8143             :     }
    8144           2 :     return hArg->ptr->GetDefault<bool>();
    8145             : }
    8146             : 
    8147             : /************************************************************************/
    8148             : /*                 GDALAlgorithmArgGetDefaultAsString()                 */
    8149             : /************************************************************************/
    8150             : 
    8151             : /** Return the argument default value as a string.
    8152             :  *
    8153             :  * Must only be called on arguments whose type is GAAT_STRING.
    8154             :  *
    8155             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8156             :  * argument has a default value.
    8157             :  *
    8158             :  * @param hArg Handle to an argument. Must NOT be null.
    8159             :  * @return string whose lifetime is bound to hArg and which must not
    8160             :  * be freed.
    8161             :  * @since 3.11
    8162             :  */
    8163           3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
    8164             : {
    8165           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8166           3 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8167             :     {
    8168           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8169             :                  "%s must only be called on arguments of type GAAT_STRING",
    8170             :                  __func__);
    8171           2 :         return nullptr;
    8172             :     }
    8173           1 :     return hArg->ptr->GetDefault<std::string>().c_str();
    8174             : }
    8175             : 
    8176             : /************************************************************************/
    8177             : /*                GDALAlgorithmArgGetDefaultAsInteger()                 */
    8178             : /************************************************************************/
    8179             : 
    8180             : /** Return the argument default value as a integer.
    8181             :  *
    8182             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8183             :  *
    8184             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8185             :  * argument has a default value.
    8186             :  *
    8187             :  * @param hArg Handle to an argument. Must NOT be null.
    8188             :  * @since 3.12
    8189             :  */
    8190           3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
    8191             : {
    8192           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8193           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8194             :     {
    8195           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8196             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8197             :                  __func__);
    8198           2 :         return 0;
    8199             :     }
    8200           1 :     return hArg->ptr->GetDefault<int>();
    8201             : }
    8202             : 
    8203             : /************************************************************************/
    8204             : /*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
    8205             : /************************************************************************/
    8206             : 
    8207             : /** Return the argument default value as a double.
    8208             :  *
    8209             :  * Must only be called on arguments whose type is GAAT_REAL
    8210             :  *
    8211             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8212             :  * argument has a default value.
    8213             :  *
    8214             :  * @param hArg Handle to an argument. Must NOT be null.
    8215             :  * @since 3.12
    8216             :  */
    8217           3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
    8218             : {
    8219           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8220           3 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8221             :     {
    8222           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8223             :                  "%s must only be called on arguments of type GAAT_REAL",
    8224             :                  __func__);
    8225           2 :         return 0;
    8226             :     }
    8227           1 :     return hArg->ptr->GetDefault<double>();
    8228             : }
    8229             : 
    8230             : /************************************************************************/
    8231             : /*               GDALAlgorithmArgGetDefaultAsStringList()               */
    8232             : /************************************************************************/
    8233             : 
    8234             : /** Return the argument default value as a string list.
    8235             :  *
    8236             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8237             :  *
    8238             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8239             :  * argument has a default value.
    8240             :  *
    8241             :  * @param hArg Handle to an argument. Must NOT be null.
    8242             :  * @return a NULL terminated list of names, which must be destroyed with
    8243             :  * CSLDestroy()
    8244             : 
    8245             :  * @since 3.12
    8246             :  */
    8247           3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
    8248             : {
    8249           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8250           3 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8251             :     {
    8252           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8253             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8254             :                  __func__);
    8255           2 :         return nullptr;
    8256             :     }
    8257           2 :     return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
    8258           1 :         .StealList();
    8259             : }
    8260             : 
    8261             : /************************************************************************/
    8262             : /*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
    8263             : /************************************************************************/
    8264             : 
    8265             : /** Return the argument default value as a integer list.
    8266             :  *
    8267             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8268             :  *
    8269             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8270             :  * argument has a default value.
    8271             :  *
    8272             :  * @param hArg Handle to an argument. Must NOT be null.
    8273             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8274             :  * @since 3.12
    8275             :  */
    8276           3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
    8277             :                                                    size_t *pnCount)
    8278             : {
    8279           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8280           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8281           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8282             :     {
    8283           2 :         CPLError(
    8284             :             CE_Failure, CPLE_AppDefined,
    8285             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8286             :             __func__);
    8287           2 :         *pnCount = 0;
    8288           2 :         return nullptr;
    8289             :     }
    8290           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
    8291           1 :     *pnCount = val.size();
    8292           1 :     return val.data();
    8293             : }
    8294             : 
    8295             : /************************************************************************/
    8296             : /*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
    8297             : /************************************************************************/
    8298             : 
    8299             : /** Return the argument default value as a real list.
    8300             :  *
    8301             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8302             :  *
    8303             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8304             :  * argument has a default value.
    8305             :  *
    8306             :  * @param hArg Handle to an argument. Must NOT be null.
    8307             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8308             :  * @since 3.12
    8309             :  */
    8310           3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
    8311             :                                                      size_t *pnCount)
    8312             : {
    8313           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8314           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8315           3 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8316             :     {
    8317           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8318             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8319             :                  __func__);
    8320           2 :         *pnCount = 0;
    8321           2 :         return nullptr;
    8322             :     }
    8323           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
    8324           1 :     *pnCount = val.size();
    8325           1 :     return val.data();
    8326             : }
    8327             : 
    8328             : /************************************************************************/
    8329             : /*                      GDALAlgorithmArgIsHidden()                      */
    8330             : /************************************************************************/
    8331             : 
    8332             : /** Return whether the argument is hidden (for GDAL internal use)
    8333             :  *
    8334             :  * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
    8335             :  * GDALAlgorithmArgIsHiddenForAPI().
    8336             :  *
    8337             :  * @param hArg Handle to an argument. Must NOT be null.
    8338             :  * @since 3.12
    8339             :  */
    8340           1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
    8341             : {
    8342           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8343           1 :     return hArg->ptr->IsHidden();
    8344             : }
    8345             : 
    8346             : /************************************************************************/
    8347             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    8348             : /************************************************************************/
    8349             : 
    8350             : /** Return whether the argument must not be mentioned in CLI usage.
    8351             :  *
    8352             :  * For example, "output-value" for "gdal raster info", which is only
    8353             :  * meant when the algorithm is used from a non-CLI context.
    8354             :  *
    8355             :  * @param hArg Handle to an argument. Must NOT be null.
    8356             :  * @since 3.11
    8357             :  */
    8358           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    8359             : {
    8360           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8361           1 :     return hArg->ptr->IsHiddenForCLI();
    8362             : }
    8363             : 
    8364             : /************************************************************************/
    8365             : /*                   GDALAlgorithmArgIsHiddenForAPI()                   */
    8366             : /************************************************************************/
    8367             : 
    8368             : /** Return whether the argument must not be mentioned in the context of an
    8369             :  * API use.
    8370             :  * Said otherwise, if it is only for CLI usage.
    8371             :  *
    8372             :  * For example "--help"
    8373             :  *
    8374             :  * @param hArg Handle to an argument. Must NOT be null.
    8375             :  * @since 3.12
    8376             :  */
    8377      213775 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
    8378             : {
    8379      213775 :     VALIDATE_POINTER1(hArg, __func__, false);
    8380      213775 :     return hArg->ptr->IsHiddenForAPI();
    8381             : }
    8382             : 
    8383             : /************************************************************************/
    8384             : /*                    GDALAlgorithmArgIsOnlyForCLI()                    */
    8385             : /************************************************************************/
    8386             : 
    8387             : /** Return whether the argument must not be mentioned in the context of an
    8388             :  * API use.
    8389             :  * Said otherwise, if it is only for CLI usage.
    8390             :  *
    8391             :  * For example "--help"
    8392             :  *
    8393             :  * @param hArg Handle to an argument. Must NOT be null.
    8394             :  * @since 3.11
    8395             :  * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
    8396             :  */
    8397           0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    8398             : {
    8399           0 :     VALIDATE_POINTER1(hArg, __func__, false);
    8400           0 :     return hArg->ptr->IsHiddenForAPI();
    8401             : }
    8402             : 
    8403             : /************************************************************************/
    8404             : /*             GDALAlgorithmArgIsAvailableInPipelineStep()              */
    8405             : /************************************************************************/
    8406             : 
    8407             : /** Return whether the argument is available in a pipeline step.
    8408             :  *
    8409             :  * If false, it is only available in standalone mode.
    8410             :  *
    8411             :  * @param hArg Handle to an argument. Must NOT be null.
    8412             :  * @since 3.13
    8413             :  */
    8414           2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
    8415             : {
    8416           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8417           2 :     return hArg->ptr->IsAvailableInPipelineStep();
    8418             : }
    8419             : 
    8420             : /************************************************************************/
    8421             : /*                      GDALAlgorithmArgIsInput()                       */
    8422             : /************************************************************************/
    8423             : 
    8424             : /** Indicate whether the value of the argument is read-only during the
    8425             :  * execution of the algorithm.
    8426             :  *
    8427             :  * Default is true.
    8428             :  *
    8429             :  * @param hArg Handle to an argument. Must NOT be null.
    8430             :  * @since 3.11
    8431             :  */
    8432      210905 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    8433             : {
    8434      210905 :     VALIDATE_POINTER1(hArg, __func__, false);
    8435      210905 :     return hArg->ptr->IsInput();
    8436             : }
    8437             : 
    8438             : /************************************************************************/
    8439             : /*                      GDALAlgorithmArgIsOutput()                      */
    8440             : /************************************************************************/
    8441             : 
    8442             : /** Return whether (at least part of) the value of the argument is set
    8443             :  * during the execution of the algorithm.
    8444             :  *
    8445             :  * For example, "output-value" for "gdal raster info"
    8446             :  * Default is false.
    8447             :  * An argument may return both IsInput() and IsOutput() as true.
    8448             :  * For example the "gdal raster convert" algorithm consumes the dataset
    8449             :  * name of its "output" argument, and sets the dataset object during its
    8450             :  * execution.
    8451             :  *
    8452             :  * @param hArg Handle to an argument. Must NOT be null.
    8453             :  * @since 3.11
    8454             :  */
    8455      117492 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    8456             : {
    8457      117492 :     VALIDATE_POINTER1(hArg, __func__, false);
    8458      117492 :     return hArg->ptr->IsOutput();
    8459             : }
    8460             : 
    8461             : /************************************************************************/
    8462             : /*                   GDALAlgorithmArgGetDatasetType()                   */
    8463             : /************************************************************************/
    8464             : 
    8465             : /** Get which type of dataset is allowed / generated.
    8466             :  *
    8467             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    8468             :  * GDAL_OF_MULTIDIM_RASTER.
    8469             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8470             :  *
    8471             :  * @param hArg Handle to an argument. Must NOT be null.
    8472             :  * @since 3.11
    8473             :  */
    8474           2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
    8475             : {
    8476           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8477           2 :     return hArg->ptr->GetDatasetType();
    8478             : }
    8479             : 
    8480             : /************************************************************************/
    8481             : /*                GDALAlgorithmArgGetDatasetInputFlags()                */
    8482             : /************************************************************************/
    8483             : 
    8484             : /** Indicates which components among name and dataset are accepted as
    8485             :  * input, when this argument serves as an input.
    8486             :  *
    8487             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    8488             :  * input.
    8489             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8490             :  * accepted as input.
    8491             :  * If both bits are set, the algorithm can accept either a name or a dataset
    8492             :  * object.
    8493             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8494             :  *
    8495             :  * @param hArg Handle to an argument. Must NOT be null.
    8496             :  * @return string whose lifetime is bound to hAlg and which must not
    8497             :  * be freed.
    8498             :  * @since 3.11
    8499             :  */
    8500           2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
    8501             : {
    8502           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8503           2 :     return hArg->ptr->GetDatasetInputFlags();
    8504             : }
    8505             : 
    8506             : /************************************************************************/
    8507             : /*               GDALAlgorithmArgGetDatasetOutputFlags()                */
    8508             : /************************************************************************/
    8509             : 
    8510             : /** Indicates which components among name and dataset are modified,
    8511             :  * when this argument serves as an output.
    8512             :  *
    8513             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    8514             :  * output (that is the algorithm will generate the name. Rarely used).
    8515             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8516             :  * generated as output, and available for use after the algorithm has
    8517             :  * completed.
    8518             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8519             :  *
    8520             :  * @param hArg Handle to an argument. Must NOT be null.
    8521             :  * @return string whose lifetime is bound to hAlg and which must not
    8522             :  * be freed.
    8523             :  * @since 3.11
    8524             :  */
    8525           2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
    8526             : {
    8527           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8528           2 :     return hArg->ptr->GetDatasetOutputFlags();
    8529             : }
    8530             : 
    8531             : /************************************************************************/
    8532             : /*              GDALAlgorithmArgGetMutualExclusionGroup()               */
    8533             : /************************************************************************/
    8534             : 
    8535             : /** Return the name of the mutual exclusion group to which this argument
    8536             :  * belongs to.
    8537             :  *
    8538             :  * Or empty string if it does not belong to any exclusion group.
    8539             :  *
    8540             :  * @param hArg Handle to an argument. Must NOT be null.
    8541             :  * @return string whose lifetime is bound to hArg and which must not
    8542             :  * be freed.
    8543             :  * @since 3.11
    8544             :  */
    8545           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    8546             : {
    8547           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8548           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    8549             : }
    8550             : 
    8551             : /************************************************************************/
    8552             : /*              GDALAlgorithmArgGetMutualDependencyGroup()              */
    8553             : /************************************************************************/
    8554             : 
    8555             : /** Return the name of the mutual dependency group to which this argument
    8556             :  * belongs to.
    8557             :  *
    8558             :  * Or empty string if it does not belong to any dependency group.
    8559             :  *
    8560             :  * @param hArg Handle to an argument. Must NOT be null.
    8561             :  * @return string whose lifetime is bound to hArg and which must not
    8562             :  * be freed.
    8563             :  * @since 3.13
    8564             :  */
    8565           5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
    8566             : {
    8567           5 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8568           5 :     return hArg->ptr->GetMutualDependencyGroup().c_str();
    8569             : }
    8570             : 
    8571             : /************************************************************************/
    8572             : /*               GDALAlgorithmArgGetDirectDependencies()                */
    8573             : /************************************************************************/
    8574             : 
    8575             : /** Return the list of names of arguments that this argument depends on.
    8576             :  *
    8577             :  *  This is not necessarily a symmetric relationship.
    8578             :  *  If argument A depends on argument B, it doesn't mean that B depends on A.
    8579             :  *  Mutual dependency groups are a special case of dependencies,
    8580             :  *  where all arguments of the group depend on each other and are not
    8581             :  *  returned by this method.
    8582             :  *
    8583             :  * @param hArg Handle to an argument. Must NOT be null.
    8584             :  * @return a NULL terminated list of names, which must be destroyed with
    8585             :  * CSLDestroy()
    8586             :  * @since 3.13
    8587             :  */
    8588           7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
    8589             : {
    8590           7 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8591           7 :     return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
    8592             : }
    8593             : 
    8594             : /************************************************************************/
    8595             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    8596             : /************************************************************************/
    8597             : 
    8598             : /** Return the argument value as a boolean.
    8599             :  *
    8600             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    8601             :  *
    8602             :  * @param hArg Handle to an argument. Must NOT be null.
    8603             :  * @since 3.11
    8604             :  */
    8605           8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    8606             : {
    8607           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    8608           8 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8609             :     {
    8610           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8611             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8612             :                  __func__);
    8613           1 :         return false;
    8614             :     }
    8615           7 :     return hArg->ptr->Get<bool>();
    8616             : }
    8617             : 
    8618             : /************************************************************************/
    8619             : /*                    GDALAlgorithmArgGetAsString()                     */
    8620             : /************************************************************************/
    8621             : 
    8622             : /** Return the argument value as a string.
    8623             :  *
    8624             :  * Must only be called on arguments whose type is GAAT_STRING.
    8625             :  *
    8626             :  * @param hArg Handle to an argument. Must NOT be null.
    8627             :  * @return string whose lifetime is bound to hArg and which must not
    8628             :  * be freed.
    8629             :  * @since 3.11
    8630             :  */
    8631         354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    8632             : {
    8633         354 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8634         354 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8635             :     {
    8636           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8637             :                  "%s must only be called on arguments of type GAAT_STRING",
    8638             :                  __func__);
    8639           1 :         return nullptr;
    8640             :     }
    8641         353 :     return hArg->ptr->Get<std::string>().c_str();
    8642             : }
    8643             : 
    8644             : /************************************************************************/
    8645             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    8646             : /************************************************************************/
    8647             : 
    8648             : /** Return the argument value as a GDALArgDatasetValueH.
    8649             :  *
    8650             :  * Must only be called on arguments whose type is GAAT_DATASET
    8651             :  *
    8652             :  * @param hArg Handle to an argument. Must NOT be null.
    8653             :  * @return handle to a GDALArgDatasetValue that must be released with
    8654             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    8655             :  * the one of hArg.
    8656             :  * @since 3.11
    8657             :  */
    8658        3167 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    8659             : {
    8660        3167 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8661        3167 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    8662             :     {
    8663           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8664             :                  "%s must only be called on arguments of type GAAT_DATASET",
    8665             :                  __func__);
    8666           1 :         return nullptr;
    8667             :     }
    8668        3166 :     return std::make_unique<GDALArgDatasetValueHS>(
    8669        6332 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    8670        3166 :         .release();
    8671             : }
    8672             : 
    8673             : /************************************************************************/
    8674             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    8675             : /************************************************************************/
    8676             : 
    8677             : /** Return the argument value as a integer.
    8678             :  *
    8679             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8680             :  *
    8681             :  * @param hArg Handle to an argument. Must NOT be null.
    8682             :  * @since 3.11
    8683             :  */
    8684          26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    8685             : {
    8686          26 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8687          26 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8688             :     {
    8689           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8690             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8691             :                  __func__);
    8692           1 :         return 0;
    8693             :     }
    8694          25 :     return hArg->ptr->Get<int>();
    8695             : }
    8696             : 
    8697             : /************************************************************************/
    8698             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    8699             : /************************************************************************/
    8700             : 
    8701             : /** Return the argument value as a double.
    8702             :  *
    8703             :  * Must only be called on arguments whose type is GAAT_REAL
    8704             :  *
    8705             :  * @param hArg Handle to an argument. Must NOT be null.
    8706             :  * @since 3.11
    8707             :  */
    8708           8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    8709             : {
    8710           8 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8711           8 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8712             :     {
    8713           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8714             :                  "%s must only be called on arguments of type GAAT_REAL",
    8715             :                  __func__);
    8716           1 :         return 0;
    8717             :     }
    8718           7 :     return hArg->ptr->Get<double>();
    8719             : }
    8720             : 
    8721             : /************************************************************************/
    8722             : /*                  GDALAlgorithmArgGetAsStringList()                   */
    8723             : /************************************************************************/
    8724             : 
    8725             : /** Return the argument value as a string list.
    8726             :  *
    8727             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8728             :  *
    8729             :  * @param hArg Handle to an argument. Must NOT be null.
    8730             :  * @return a NULL terminated list of names, which must be destroyed with
    8731             :  * CSLDestroy()
    8732             : 
    8733             :  * @since 3.11
    8734             :  */
    8735           4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    8736             : {
    8737           4 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8738           4 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8739             :     {
    8740           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8741             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8742             :                  __func__);
    8743           1 :         return nullptr;
    8744             :     }
    8745           6 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    8746           3 :         .StealList();
    8747             : }
    8748             : 
    8749             : /************************************************************************/
    8750             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    8751             : /************************************************************************/
    8752             : 
    8753             : /** Return the argument value as a integer list.
    8754             :  *
    8755             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8756             :  *
    8757             :  * @param hArg Handle to an argument. Must NOT be null.
    8758             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8759             :  * @since 3.11
    8760             :  */
    8761           8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    8762             :                                             size_t *pnCount)
    8763             : {
    8764           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8765           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8766           8 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8767             :     {
    8768           1 :         CPLError(
    8769             :             CE_Failure, CPLE_AppDefined,
    8770             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8771             :             __func__);
    8772           1 :         *pnCount = 0;
    8773           1 :         return nullptr;
    8774             :     }
    8775           7 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    8776           7 :     *pnCount = val.size();
    8777           7 :     return val.data();
    8778             : }
    8779             : 
    8780             : /************************************************************************/
    8781             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    8782             : /************************************************************************/
    8783             : 
    8784             : /** Return the argument value as a real list.
    8785             :  *
    8786             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8787             :  *
    8788             :  * @param hArg Handle to an argument. Must NOT be null.
    8789             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8790             :  * @since 3.11
    8791             :  */
    8792           8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    8793             :                                               size_t *pnCount)
    8794             : {
    8795           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8796           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8797           8 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8798             :     {
    8799           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8800             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8801             :                  __func__);
    8802           1 :         *pnCount = 0;
    8803           1 :         return nullptr;
    8804             :     }
    8805           7 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    8806           7 :     *pnCount = val.size();
    8807           7 :     return val.data();
    8808             : }
    8809             : 
    8810             : /************************************************************************/
    8811             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    8812             : /************************************************************************/
    8813             : 
    8814             : /** Set the value for a GAAT_BOOLEAN argument.
    8815             :  *
    8816             :  * It cannot be called several times for a given argument.
    8817             :  * Validation checks and other actions are run.
    8818             :  *
    8819             :  * @param hArg Handle to an argument. Must NOT be null.
    8820             :  * @param value value.
    8821             :  * @return true if success.
    8822             :  * @since 3.11
    8823             :  */
    8824             : 
    8825         694 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    8826             : {
    8827         694 :     VALIDATE_POINTER1(hArg, __func__, false);
    8828         694 :     return hArg->ptr->Set(value);
    8829             : }
    8830             : 
    8831             : /************************************************************************/
    8832             : /*                    GDALAlgorithmArgSetAsString()                     */
    8833             : /************************************************************************/
    8834             : 
    8835             : /** Set the value for a GAAT_STRING argument.
    8836             :  *
    8837             :  * It cannot be called several times for a given argument.
    8838             :  * Validation checks and other actions are run.
    8839             :  *
    8840             :  * @param hArg Handle to an argument. Must NOT be null.
    8841             :  * @param value value (may be null)
    8842             :  * @return true if success.
    8843             :  * @since 3.11
    8844             :  */
    8845             : 
    8846        3100 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    8847             : {
    8848        3100 :     VALIDATE_POINTER1(hArg, __func__, false);
    8849        3100 :     return hArg->ptr->Set(value ? value : "");
    8850             : }
    8851             : 
    8852             : /************************************************************************/
    8853             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    8854             : /************************************************************************/
    8855             : 
    8856             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    8857             :  *
    8858             :  * It cannot be called several times for a given argument.
    8859             :  * Validation checks and other actions are run.
    8860             :  *
    8861             :  * @param hArg Handle to an argument. Must NOT be null.
    8862             :  * @param value value.
    8863             :  * @return true if success.
    8864             :  * @since 3.11
    8865             :  */
    8866             : 
    8867         472 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    8868             : {
    8869         472 :     VALIDATE_POINTER1(hArg, __func__, false);
    8870         472 :     return hArg->ptr->Set(value);
    8871             : }
    8872             : 
    8873             : /************************************************************************/
    8874             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    8875             : /************************************************************************/
    8876             : 
    8877             : /** Set the value for a GAAT_REAL argument.
    8878             :  *
    8879             :  * It cannot be called several times for a given argument.
    8880             :  * Validation checks and other actions are run.
    8881             :  *
    8882             :  * @param hArg Handle to an argument. Must NOT be null.
    8883             :  * @param value value.
    8884             :  * @return true if success.
    8885             :  * @since 3.11
    8886             :  */
    8887             : 
    8888         242 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    8889             : {
    8890         242 :     VALIDATE_POINTER1(hArg, __func__, false);
    8891         242 :     return hArg->ptr->Set(value);
    8892             : }
    8893             : 
    8894             : /************************************************************************/
    8895             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    8896             : /************************************************************************/
    8897             : 
    8898             : /** Set the value for a GAAT_DATASET argument.
    8899             :  *
    8900             :  * It cannot be called several times for a given argument.
    8901             :  * Validation checks and other actions are run.
    8902             :  *
    8903             :  * @param hArg Handle to an argument. Must NOT be null.
    8904             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    8905             :  * @return true if success.
    8906             :  * @since 3.11
    8907             :  */
    8908           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    8909             :                                        GDALArgDatasetValueH value)
    8910             : {
    8911           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8912           2 :     VALIDATE_POINTER1(value, __func__, false);
    8913           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    8914             : }
    8915             : 
    8916             : /************************************************************************/
    8917             : /*                     GDALAlgorithmArgSetDataset()                     */
    8918             : /************************************************************************/
    8919             : 
    8920             : /** Set dataset object, increasing its reference counter.
    8921             :  *
    8922             :  * @param hArg Handle to an argument. Must NOT be null.
    8923             :  * @param hDS Dataset object. May be null.
    8924             :  * @return true if success.
    8925             :  * @since 3.11
    8926             :  */
    8927             : 
    8928           2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    8929             : {
    8930           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8931           2 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    8932             : }
    8933             : 
    8934             : /************************************************************************/
    8935             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    8936             : /************************************************************************/
    8937             : 
    8938             : /** Set the value for a GAAT_STRING_LIST argument.
    8939             :  *
    8940             :  * It cannot be called several times for a given argument.
    8941             :  * Validation checks and other actions are run.
    8942             :  *
    8943             :  * @param hArg Handle to an argument. Must NOT be null.
    8944             :  * @param value value as a NULL terminated list (may be null)
    8945             :  * @return true if success.
    8946             :  * @since 3.11
    8947             :  */
    8948             : 
    8949         766 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    8950             : {
    8951         766 :     VALIDATE_POINTER1(hArg, __func__, false);
    8952         766 :     return hArg->ptr->Set(
    8953        1532 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    8954             : }
    8955             : 
    8956             : /************************************************************************/
    8957             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    8958             : /************************************************************************/
    8959             : 
    8960             : /** Set the value for a GAAT_INTEGER_LIST argument.
    8961             :  *
    8962             :  * It cannot be called several times for a given argument.
    8963             :  * Validation checks and other actions are run.
    8964             :  *
    8965             :  * @param hArg Handle to an argument. Must NOT be null.
    8966             :  * @param nCount Number of values in pnValues.
    8967             :  * @param pnValues Pointer to an array of integer values of size nCount.
    8968             :  * @return true if success.
    8969             :  * @since 3.11
    8970             :  */
    8971         100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    8972             :                                       const int *pnValues)
    8973             : {
    8974         100 :     VALIDATE_POINTER1(hArg, __func__, false);
    8975         100 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    8976             : }
    8977             : 
    8978             : /************************************************************************/
    8979             : /*                  GDALAlgorithmArgSetAsDoubleList()                   */
    8980             : /************************************************************************/
    8981             : 
    8982             : /** Set the value for a GAAT_REAL_LIST argument.
    8983             :  *
    8984             :  * It cannot be called several times for a given argument.
    8985             :  * Validation checks and other actions are run.
    8986             :  *
    8987             :  * @param hArg Handle to an argument. Must NOT be null.
    8988             :  * @param nCount Number of values in pnValues.
    8989             :  * @param pnValues Pointer to an array of double values of size nCount.
    8990             :  * @return true if success.
    8991             :  * @since 3.11
    8992             :  */
    8993         152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    8994             :                                      const double *pnValues)
    8995             : {
    8996         152 :     VALIDATE_POINTER1(hArg, __func__, false);
    8997         152 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    8998             : }
    8999             : 
    9000             : /************************************************************************/
    9001             : /*                    GDALAlgorithmArgSetDatasets()                     */
    9002             : /************************************************************************/
    9003             : 
    9004             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    9005             :  *
    9006             :  * @param hArg Handle to an argument. Must NOT be null.
    9007             :  * @param nCount Number of values in pnValues.
    9008             :  * @param pahDS Pointer to an array of dataset of size nCount.
    9009             :  * @return true if success.
    9010             :  * @since 3.11
    9011             :  */
    9012             : 
    9013        1262 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    9014             :                                  GDALDatasetH *pahDS)
    9015             : {
    9016        1262 :     VALIDATE_POINTER1(hArg, __func__, false);
    9017        2524 :     std::vector<GDALArgDatasetValue> values;
    9018        2550 :     for (size_t i = 0; i < nCount; ++i)
    9019             :     {
    9020        1288 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    9021             :     }
    9022        1262 :     return hArg->ptr->Set(std::move(values));
    9023             : }
    9024             : 
    9025             : /************************************************************************/
    9026             : /*                  GDALAlgorithmArgSetDatasetNames()                   */
    9027             : /************************************************************************/
    9028             : 
    9029             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    9030             :  *
    9031             :  * @param hArg Handle to an argument. Must NOT be null.
    9032             :  * @param names Dataset names as a NULL terminated list (may be null)
    9033             :  * @return true if success.
    9034             :  * @since 3.11
    9035             :  */
    9036             : 
    9037         734 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    9038             : {
    9039         734 :     VALIDATE_POINTER1(hArg, __func__, false);
    9040        1468 :     std::vector<GDALArgDatasetValue> values;
    9041        1539 :     for (size_t i = 0; names[i]; ++i)
    9042             :     {
    9043         805 :         values.emplace_back(names[i]);
    9044             :     }
    9045         734 :     return hArg->ptr->Set(std::move(values));
    9046             : }
    9047             : 
    9048             : /************************************************************************/
    9049             : /*                     GDALArgDatasetValueCreate()                      */
    9050             : /************************************************************************/
    9051             : 
    9052             : /** Instantiate an empty GDALArgDatasetValue
    9053             :  *
    9054             :  * @return new handle to free with GDALArgDatasetValueRelease()
    9055             :  * @since 3.11
    9056             :  */
    9057           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    9058             : {
    9059           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    9060             : }
    9061             : 
    9062             : /************************************************************************/
    9063             : /*                     GDALArgDatasetValueRelease()                     */
    9064             : /************************************************************************/
    9065             : 
    9066             : /** Release a handle to a GDALArgDatasetValue
    9067             :  *
    9068             :  * @since 3.11
    9069             :  */
    9070        3167 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    9071             : {
    9072        3167 :     delete hValue;
    9073        3167 : }
    9074             : 
    9075             : /************************************************************************/
    9076             : /*                     GDALArgDatasetValueGetName()                     */
    9077             : /************************************************************************/
    9078             : 
    9079             : /** Return the name component of the GDALArgDatasetValue
    9080             :  *
    9081             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9082             :  * @return string whose lifetime is bound to hAlg and which must not
    9083             :  * be freed.
    9084             :  * @since 3.11
    9085             :  */
    9086           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    9087             : {
    9088           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9089           1 :     return hValue->ptr->GetName().c_str();
    9090             : }
    9091             : 
    9092             : /************************************************************************/
    9093             : /*                  GDALArgDatasetValueGetDatasetRef()                  */
    9094             : /************************************************************************/
    9095             : 
    9096             : /** Return the dataset component of the GDALArgDatasetValue.
    9097             :  *
    9098             :  * This does not modify the reference counter, hence the lifetime of the
    9099             :  * returned object is not guaranteed to exceed the one of hValue.
    9100             :  *
    9101             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9102             :  * @since 3.11
    9103             :  */
    9104           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    9105             : {
    9106           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9107           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    9108             : }
    9109             : 
    9110             : /************************************************************************/
    9111             : /*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
    9112             : /************************************************************************/
    9113             : 
    9114             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    9115             :  * reference count if not null. Once done with the dataset, the caller should
    9116             :  * call GDALReleaseDataset().
    9117             :  *
    9118             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9119             :  * @since 3.11
    9120             :  */
    9121             : GDALDatasetH
    9122        1046 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    9123             : {
    9124        1046 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9125        1046 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    9126             : }
    9127             : 
    9128             : /************************************************************************/
    9129             : /*                     GDALArgDatasetValueSetName()                     */
    9130             : /************************************************************************/
    9131             : 
    9132             : /** Set dataset name
    9133             :  *
    9134             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9135             :  * @param pszName Dataset name. May be null.
    9136             :  * @since 3.11
    9137             :  */
    9138             : 
    9139        1350 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    9140             :                                 const char *pszName)
    9141             : {
    9142        1350 :     VALIDATE_POINTER0(hValue, __func__);
    9143        1350 :     hValue->ptr->Set(pszName ? pszName : "");
    9144             : }
    9145             : 
    9146             : /************************************************************************/
    9147             : /*                   GDALArgDatasetValueSetDataset()                    */
    9148             : /************************************************************************/
    9149             : 
    9150             : /** Set dataset object, increasing its reference counter.
    9151             :  *
    9152             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9153             :  * @param hDS Dataset object. May be null.
    9154             :  * @since 3.11
    9155             :  */
    9156             : 
    9157         756 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    9158             :                                    GDALDatasetH hDS)
    9159             : {
    9160         756 :     VALIDATE_POINTER0(hValue, __func__);
    9161         756 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    9162             : }

Generated by: LCOV version 1.14