LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3306 3501 94.4 %
Date: 2025-09-10 17:48:50 Functions: 252 258 97.7 %

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

Generated by: LCOV version 1.14