LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3842 4082 94.1 %
Date: 2026-09-15 17:49:13 Functions: 288 295 97.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDALAlgorithm class
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2024, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "cpl_conv.h"
      15             : #include "cpl_enumerate.h"
      16             : #include "cpl_error.h"
      17             : #include "cpl_error_internal.h"
      18             : #include "cpl_json.h"
      19             : #include "cpl_levenshtein.h"
      20             : #include "cpl_minixml.h"
      21             : #include "cpl_multiproc.h"
      22             : 
      23             : #include "gdalalgorithm.h"
      24             : #include "gdalalg_abstract_pipeline.h"
      25             : #include "gdal_priv.h"
      26             : #include "gdal_thread_pool.h"
      27             : #include "memdataset.h"
      28             : #include "ogrsf_frmts.h"
      29             : #include "ogr_p.h"
      30             : #include "ogr_spatialref.h"
      31             : #include "vrtdataset.h"
      32             : 
      33             : #include <algorithm>
      34             : #include <cassert>
      35             : #include <cerrno>
      36             : #include <cmath>
      37             : #include <cstdlib>
      38             : #include <limits>
      39             : #include <map>
      40             : #include <type_traits>
      41             : #include <string_view>
      42             : #include <regex>
      43             : 
      44             : #ifndef _
      45             : #define _(x) (x)
      46             : #endif
      47             : 
      48             : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
      49             : 
      50             : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
      51             : 
      52             : constexpr const char *GDAL_ARG_NAME_BAND = "band";
      53             : 
      54             : //! @cond Doxygen_Suppress
      55             : struct GDALAlgorithmArgHS
      56             : {
      57             :     GDALAlgorithmArg *ptr = nullptr;
      58             : 
      59      366422 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      60             :     {
      61      366422 :     }
      62             : };
      63             : 
      64             : //! @endcond
      65             : 
      66             : //! @cond Doxygen_Suppress
      67             : struct GDALArgDatasetValueHS
      68             : {
      69             :     GDALArgDatasetValue val{};
      70             :     GDALArgDatasetValue *ptr = nullptr;
      71             : 
      72           1 :     GDALArgDatasetValueHS() : ptr(&val)
      73             :     {
      74           1 :     }
      75             : 
      76        3514 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      77             :     {
      78        3514 :     }
      79             : 
      80             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      81             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      82             : };
      83             : 
      84             : //! @endcond
      85             : 
      86             : /************************************************************************/
      87             : /*                     GDALAlgorithmArgTypeIsList()                     */
      88             : /************************************************************************/
      89             : 
      90      473671 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      91             : {
      92      473671 :     switch (type)
      93             :     {
      94      311676 :         case GAAT_BOOLEAN:
      95             :         case GAAT_STRING:
      96             :         case GAAT_INTEGER:
      97             :         case GAAT_REAL:
      98             :         case GAAT_DATASET:
      99      311676 :             break;
     100             : 
     101      161995 :         case GAAT_STRING_LIST:
     102             :         case GAAT_INTEGER_LIST:
     103             :         case GAAT_REAL_LIST:
     104             :         case GAAT_DATASET_LIST:
     105      161995 :             return true;
     106             :     }
     107             : 
     108      311676 :     return false;
     109             : }
     110             : 
     111             : /************************************************************************/
     112             : /*                      GDALAlgorithmArgTypeName()                      */
     113             : /************************************************************************/
     114             : 
     115        5792 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     116             : {
     117        5792 :     switch (type)
     118             :     {
     119        1426 :         case GAAT_BOOLEAN:
     120        1426 :             break;
     121        1578 :         case GAAT_STRING:
     122        1578 :             return "string";
     123         397 :         case GAAT_INTEGER:
     124         397 :             return "integer";
     125         491 :         case GAAT_REAL:
     126         491 :             return "real";
     127         273 :         case GAAT_DATASET:
     128         273 :             return "dataset";
     129        1102 :         case GAAT_STRING_LIST:
     130        1102 :             return "string_list";
     131          85 :         case GAAT_INTEGER_LIST:
     132          85 :             return "integer_list";
     133         221 :         case GAAT_REAL_LIST:
     134         221 :             return "real_list";
     135         219 :         case GAAT_DATASET_LIST:
     136         219 :             return "dataset_list";
     137             :     }
     138             : 
     139        1426 :     return "boolean";
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /*                  GDALAlgorithmArgDatasetTypeName()                   */
     144             : /************************************************************************/
     145             : 
     146       29975 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
     147             : {
     148       29975 :     std::string ret;
     149       29975 :     if ((type & GDAL_OF_RASTER) != 0)
     150       17849 :         ret = "raster";
     151       29975 :     if ((type & GDAL_OF_VECTOR) != 0)
     152             :     {
     153       12767 :         if (!ret.empty())
     154             :         {
     155        1806 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     156         294 :                 ret += ", ";
     157             :             else
     158        1512 :                 ret += " or ";
     159             :         }
     160       12767 :         ret += "vector";
     161             :     }
     162       29975 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     163             :     {
     164        1387 :         if (!ret.empty())
     165             :         {
     166         439 :             ret += " or ";
     167             :         }
     168        1387 :         ret += "multidimensional raster";
     169             :     }
     170       29975 :     return ret;
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                        GDALAlgorithmArgDecl()                        */
     175             : /************************************************************************/
     176             : 
     177             : // cppcheck-suppress uninitMemberVar
     178      379595 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     179             :                                            char chShortName,
     180             :                                            const std::string &description,
     181      379595 :                                            GDALAlgorithmArgType type)
     182             :     : m_longName(longName),
     183      379595 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     184             :       m_description(description), m_type(type),
     185      759190 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     186      379595 :                     .toupper()),
     187     1138780 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     188             : {
     189      379595 :     if (m_type == GAAT_BOOLEAN)
     190             :     {
     191      161510 :         m_defaultValue = false;
     192             :     }
     193      379595 : }
     194             : 
     195             : /************************************************************************/
     196             : /*                 GDALAlgorithmArgDecl::SetMinCount()                  */
     197             : /************************************************************************/
     198             : 
     199       20919 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     200             : {
     201       20919 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     202             :     {
     203           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     204             :                  "SetMinCount() illegal on scalar argument '%s'",
     205           1 :                  GetName().c_str());
     206             :     }
     207             :     else
     208             :     {
     209       20918 :         m_minCount = count;
     210             :     }
     211       20919 :     return *this;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
     216             : /************************************************************************/
     217             : 
     218       20094 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     219             : {
     220       20094 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     221             :     {
     222           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     223             :                  "SetMaxCount() illegal on scalar argument '%s'",
     224           1 :                  GetName().c_str());
     225             :     }
     226             :     else
     227             :     {
     228       20093 :         m_maxCount = count;
     229             :     }
     230       20094 :     return *this;
     231             : }
     232             : 
     233             : /************************************************************************/
     234             : /*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
     235             : /************************************************************************/
     236             : 
     237             : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
     238             : 
     239             : /************************************************************************/
     240             : /*                       GDALAlgorithmArg::Set()                        */
     241             : /************************************************************************/
     242             : 
     243        1329 : bool GDALAlgorithmArg::Set(bool value)
     244             : {
     245        1329 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     246             :     {
     247          14 :         CPLError(
     248             :             CE_Failure, CPLE_AppDefined,
     249             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     250           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     251           7 :         return false;
     252             :     }
     253        1322 :     return SetInternal(value);
     254             : }
     255             : 
     256        4555 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
     257             : {
     258        4607 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     259          52 :         value.front() == '@')
     260             :     {
     261           2 :         GByte *pabyData = nullptr;
     262           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     263           2 :                           10 * 1024 * 1024))
     264             :         {
     265             :             // Remove UTF-8 BOM
     266           1 :             size_t offset = 0;
     267           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     268           1 :                 pabyData[2] == 0xBF)
     269             :             {
     270           1 :                 offset = 3;
     271             :             }
     272           1 :             value = reinterpret_cast<const char *>(pabyData + offset);
     273           1 :             VSIFree(pabyData);
     274             :         }
     275             :         else
     276             :         {
     277           1 :             return false;
     278             :         }
     279             :     }
     280             : 
     281        4554 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     282          51 :         value = CPLRemoveSQLComments(value);
     283             : 
     284        4554 :     return true;
     285             : }
     286             : 
     287        4590 : bool GDALAlgorithmArg::Set(const std::string &value)
     288             : {
     289        4590 :     switch (m_decl.GetType())
     290             :     {
     291           9 :         case GAAT_BOOLEAN:
     292          17 :             if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
     293          17 :                 EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
     294             :             {
     295           4 :                 return Set(true);
     296             :             }
     297           5 :             else if (EQUAL(value.c_str(), "0") ||
     298           4 :                      EQUAL(value.c_str(), "FALSE") ||
     299           9 :                      EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
     300             :             {
     301           4 :                 return Set(false);
     302             :             }
     303           1 :             break;
     304             : 
     305           8 :         case GAAT_INTEGER:
     306             :         case GAAT_INTEGER_LIST:
     307             :         {
     308           8 :             errno = 0;
     309           8 :             char *endptr = nullptr;
     310           8 :             const auto v = std::strtoll(value.c_str(), &endptr, 10);
     311          13 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     312           5 :                 endptr == value.c_str() + value.size())
     313             :             {
     314           3 :                 if (m_decl.GetType() == GAAT_INTEGER)
     315           3 :                     return Set(static_cast<int>(v));
     316             :                 else
     317           1 :                     return Set(std::vector<int>{static_cast<int>(v)});
     318             :             }
     319           5 :             break;
     320             :         }
     321             : 
     322           5 :         case GAAT_REAL:
     323             :         case GAAT_REAL_LIST:
     324             :         {
     325           5 :             char *endptr = nullptr;
     326           5 :             const double v = CPLStrtod(value.c_str(), &endptr);
     327           5 :             if (endptr == value.c_str() + value.size())
     328             :             {
     329           3 :                 if (m_decl.GetType() == GAAT_REAL)
     330           3 :                     return Set(v);
     331             :                 else
     332           1 :                     return Set(std::vector<double>{v});
     333             :             }
     334           2 :             break;
     335             :         }
     336             : 
     337        4532 :         case GAAT_STRING:
     338        4532 :             break;
     339             : 
     340           8 :         case GAAT_STRING_LIST:
     341          16 :             return Set(std::vector<std::string>{value});
     342             : 
     343          28 :         case GAAT_DATASET:
     344          28 :             return SetDatasetName(value);
     345             : 
     346           0 :         case GAAT_DATASET_LIST:
     347             :         {
     348           0 :             std::vector<GDALArgDatasetValue> v;
     349           0 :             v.resize(1);
     350           0 :             v[0].Set(value);
     351           0 :             return Set(std::move(v));
     352             :         }
     353             :     }
     354             : 
     355        4540 :     if (m_decl.GetType() != GAAT_STRING)
     356             :     {
     357          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     358             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     359             :                  "supported",
     360           8 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     361           8 :         return false;
     362             :     }
     363             : 
     364        4532 :     std::string newValue(value);
     365        4532 :     return ProcessString(newValue) && SetInternal(newValue);
     366             : }
     367             : 
     368         897 : bool GDALAlgorithmArg::Set(int value)
     369             : {
     370         897 :     if (m_decl.GetType() == GAAT_BOOLEAN)
     371             :     {
     372           3 :         if (value == 1)
     373           1 :             return Set(true);
     374           2 :         else if (value == 0)
     375           1 :             return Set(false);
     376             :     }
     377         894 :     else if (m_decl.GetType() == GAAT_REAL)
     378             :     {
     379           3 :         return Set(static_cast<double>(value));
     380             :     }
     381         891 :     else if (m_decl.GetType() == GAAT_STRING)
     382             :     {
     383           2 :         return Set(std::to_string(value));
     384             :     }
     385         889 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST)
     386             :     {
     387           1 :         return Set(std::vector<int>{value});
     388             :     }
     389         888 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     390             :     {
     391           1 :         return Set(std::vector<double>{static_cast<double>(value)});
     392             :     }
     393         887 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     394             :     {
     395           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     396             :     }
     397             : 
     398         887 :     if (m_decl.GetType() != GAAT_INTEGER)
     399             :     {
     400           2 :         CPLError(
     401             :             CE_Failure, CPLE_AppDefined,
     402             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     403           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     404           1 :         return false;
     405             :     }
     406         886 :     return SetInternal(value);
     407             : }
     408             : 
     409         365 : bool GDALAlgorithmArg::Set(double value)
     410             : {
     411         368 :     if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
     412         368 :         value <= INT_MAX && static_cast<int>(value) == value)
     413             :     {
     414           2 :         return Set(static_cast<int>(value));
     415             :     }
     416         363 :     else if (m_decl.GetType() == GAAT_STRING)
     417             :     {
     418           2 :         return Set(std::to_string(value));
     419             :     }
     420         363 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
     421         363 :              value <= INT_MAX && static_cast<int>(value) == value)
     422             :     {
     423           1 :         return Set(std::vector<int>{static_cast<int>(value)});
     424             :     }
     425         360 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     426             :     {
     427           0 :         return Set(std::vector<double>{value});
     428             :     }
     429         360 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     430             :     {
     431           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     432             :     }
     433         359 :     else if (m_decl.GetType() != GAAT_REAL)
     434             :     {
     435           6 :         CPLError(
     436             :             CE_Failure, CPLE_AppDefined,
     437             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     438           3 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     439           3 :         return false;
     440             :     }
     441         356 :     return SetInternal(value);
     442             : }
     443             : 
     444        6338 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
     445             : {
     446        6341 :     if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
     447           3 :         arg->GetDatasetOutputFlags() == GADV_OBJECT)
     448             :     {
     449           3 :         CPLError(
     450             :             CE_Failure, CPLE_AppDefined,
     451             :             "Dataset object '%s' is created by algorithm and cannot be set "
     452             :             "as an input.",
     453           3 :             arg->GetName().c_str());
     454           3 :         return false;
     455             :     }
     456        6335 :     else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
     457             :     {
     458           8 :         CPLError(CE_Failure, CPLE_AppDefined,
     459             :                  "Dataset%s '%s' must be provided by name, not as object.",
     460           8 :                  arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
     461           4 :         return false;
     462             :     }
     463             : 
     464        6331 :     return true;
     465             : }
     466             : 
     467          32 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     468             : {
     469          56 :     if (m_decl.GetType() != GAAT_DATASET &&
     470          24 :         m_decl.GetType() != GAAT_DATASET_LIST)
     471             :     {
     472           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     473             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     474             :                  "is not supported",
     475           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     476           1 :         return false;
     477             :     }
     478          31 :     if (!CheckCanSetDatasetObject(this))
     479           2 :         return false;
     480          29 :     m_explicitlySet = true;
     481          29 :     if (m_decl.GetType() == GAAT_DATASET)
     482             :     {
     483           6 :         auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     484           6 :         val.Set(ds);
     485             :     }
     486             :     else
     487             :     {
     488          23 :         CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
     489          23 :         auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
     490          23 :         val.resize(1);
     491          23 :         val[0].Set(ds);
     492             :     }
     493          29 :     return RunAllActions();
     494             : }
     495             : 
     496           3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     497             : {
     498           3 :     if (m_decl.GetType() != GAAT_DATASET)
     499             :     {
     500           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     501             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     502             :                  "is not supported",
     503           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     504           1 :         return false;
     505             :     }
     506           2 :     if (!CheckCanSetDatasetObject(this))
     507           1 :         return false;
     508           1 :     m_explicitlySet = true;
     509           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     510           1 :     val.Set(std::move(ds));
     511           1 :     return RunAllActions();
     512             : }
     513             : 
     514         649 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     515             : {
     516         649 :     if (m_decl.GetType() != GAAT_DATASET)
     517             :     {
     518           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     519             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     520             :                  "not supported",
     521           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     522           1 :         return false;
     523             :     }
     524         648 :     m_explicitlySet = true;
     525         648 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     526         648 :     return RunAllActions();
     527             : }
     528             : 
     529        1146 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     530             : {
     531        1146 :     if (m_decl.GetType() != GAAT_DATASET)
     532             :     {
     533           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     534             :                  "Calling SetFrom() on argument '%s' of type %s is "
     535             :                  "not supported",
     536           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     537           1 :         return false;
     538             :     }
     539        1145 :     if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
     540           1 :         return false;
     541        1144 :     m_explicitlySet = true;
     542        1144 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     543        1144 :     return RunAllActions();
     544             : }
     545             : 
     546        1296 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     547             : {
     548        1296 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     549             :     {
     550           3 :         std::vector<int> v_i;
     551           4 :         for (const std::string &s : value)
     552             :         {
     553           3 :             errno = 0;
     554           3 :             char *endptr = nullptr;
     555           3 :             const auto v = std::strtoll(s.c_str(), &endptr, 10);
     556           5 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     557           2 :                 endptr == s.c_str() + s.size())
     558             :             {
     559           1 :                 v_i.push_back(static_cast<int>(v));
     560             :             }
     561             :             else
     562             :             {
     563           2 :                 break;
     564             :             }
     565             :         }
     566           3 :         if (v_i.size() == value.size())
     567           1 :             return Set(v_i);
     568             :     }
     569        1293 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     570             :     {
     571           2 :         std::vector<double> v_d;
     572           3 :         for (const std::string &s : value)
     573             :         {
     574           2 :             char *endptr = nullptr;
     575           2 :             const double v = CPLStrtod(s.c_str(), &endptr);
     576           2 :             if (endptr == s.c_str() + s.size())
     577             :             {
     578           1 :                 v_d.push_back(v);
     579             :             }
     580             :             else
     581             :             {
     582           1 :                 break;
     583             :             }
     584             :         }
     585           2 :         if (v_d.size() == value.size())
     586           1 :             return Set(v_d);
     587             :     }
     588        2580 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     589        2577 :               m_decl.GetType() == GAAT_REAL ||
     590        3870 :               m_decl.GetType() == GAAT_STRING) &&
     591           5 :              value.size() == 1)
     592             :     {
     593           4 :         return Set(value[0]);
     594             :     }
     595        1287 :     else if (m_decl.GetType() == GAAT_DATASET_LIST)
     596             :     {
     597          30 :         std::vector<GDALArgDatasetValue> dsVector;
     598          46 :         for (const std::string &s : value)
     599          31 :             dsVector.emplace_back(s);
     600          15 :         return Set(std::move(dsVector));
     601             :     }
     602             : 
     603        1275 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     604             :     {
     605          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     606             :                  "Calling Set(const std::vector<std::string> &) on argument "
     607             :                  "'%s' of type %s is not supported",
     608           5 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     609           5 :         return false;
     610             :     }
     611             : 
     612        2520 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
     613        1250 :         m_decl.IsRemoveSQLCommentsEnabled())
     614             :     {
     615          40 :         std::vector<std::string> newValue(value);
     616          43 :         for (auto &s : newValue)
     617             :         {
     618          23 :             if (!ProcessString(s))
     619           0 :                 return false;
     620             :         }
     621          20 :         return SetInternal(newValue);
     622             :     }
     623             :     else
     624             :     {
     625        1250 :         return SetInternal(value);
     626             :     }
     627             : }
     628             : 
     629         135 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     630             : {
     631         135 :     if (m_decl.GetType() == GAAT_REAL_LIST)
     632             :     {
     633           2 :         std::vector<double> v_d;
     634           2 :         for (int i : value)
     635           1 :             v_d.push_back(i);
     636           1 :         return Set(v_d);
     637             :     }
     638         134 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     639             :     {
     640           2 :         std::vector<std::string> v_s;
     641           3 :         for (int i : value)
     642           2 :             v_s.push_back(std::to_string(i));
     643           1 :         return Set(v_s);
     644             :     }
     645         264 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     646         260 :               m_decl.GetType() == GAAT_REAL ||
     647         395 :               m_decl.GetType() == GAAT_STRING) &&
     648           5 :              value.size() == 1)
     649             :     {
     650           3 :         return Set(value[0]);
     651             :     }
     652             : 
     653         130 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     654             :     {
     655           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     656             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     657             :                  "type %s is not supported",
     658           3 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     659           3 :         return false;
     660             :     }
     661         127 :     return SetInternal(value);
     662             : }
     663             : 
     664         361 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     665             : {
     666         361 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     667             :     {
     668           2 :         std::vector<int> v_i;
     669           3 :         for (double d : value)
     670             :         {
     671           2 :             if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
     672             :             {
     673           1 :                 v_i.push_back(static_cast<int>(d));
     674             :             }
     675             :             else
     676             :             {
     677             :                 break;
     678             :             }
     679             :         }
     680           2 :         if (v_i.size() == value.size())
     681           1 :             return Set(v_i);
     682             :     }
     683         359 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     684             :     {
     685           2 :         std::vector<std::string> v_s;
     686           3 :         for (double d : value)
     687           2 :             v_s.push_back(std::to_string(d));
     688           1 :         return Set(v_s);
     689             :     }
     690         715 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     691         713 :               m_decl.GetType() == GAAT_REAL ||
     692        1072 :               m_decl.GetType() == GAAT_STRING) &&
     693           3 :              value.size() == 1)
     694             :     {
     695           3 :         return Set(value[0]);
     696             :     }
     697             : 
     698         356 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     699             :     {
     700           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     701             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     702             :                  "type %s is not supported",
     703           2 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     704           2 :         return false;
     705             :     }
     706         354 :     return SetInternal(value);
     707             : }
     708             : 
     709        4025 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     710             : {
     711        4025 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     712             :     {
     713           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     714             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     715             :                  "argument '%s' of type %s is not supported",
     716           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     717           1 :         return false;
     718             :     }
     719        4024 :     m_explicitlySet = true;
     720        4024 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     721        4024 :     return RunAllActions();
     722             : }
     723             : 
     724             : GDALAlgorithmArg &
     725           0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
     726             : {
     727           0 :     Set(std::move(value));
     728           0 :     return *this;
     729             : }
     730             : 
     731           1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
     732             : {
     733           1 :     const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
     734           1 :     return Set(value.exportToWkt(apszOptions));
     735             : }
     736             : 
     737        5057 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     738             : {
     739        5057 :     if (m_decl.GetType() != other.GetType())
     740             :     {
     741           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     742             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     743             :                  "other argument type is %s is not supported",
     744           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     745             :                  GDALAlgorithmArgTypeName(other.GetType()));
     746           1 :         return false;
     747             :     }
     748             : 
     749        5056 :     switch (m_decl.GetType())
     750             :     {
     751         103 :         case GAAT_BOOLEAN:
     752         103 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     753         103 :             break;
     754         925 :         case GAAT_STRING:
     755        1850 :             *std::get<std::string *>(m_value) =
     756         925 :                 *std::get<std::string *>(other.m_value);
     757         925 :             break;
     758           7 :         case GAAT_INTEGER:
     759           7 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     760           7 :             break;
     761           1 :         case GAAT_REAL:
     762           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     763           1 :             break;
     764        1140 :         case GAAT_DATASET:
     765        1140 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     766          65 :         case GAAT_STRING_LIST:
     767         130 :             *std::get<std::vector<std::string> *>(m_value) =
     768          65 :                 *std::get<std::vector<std::string> *>(other.m_value);
     769          65 :             break;
     770           1 :         case GAAT_INTEGER_LIST:
     771           2 :             *std::get<std::vector<int> *>(m_value) =
     772           1 :                 *std::get<std::vector<int> *>(other.m_value);
     773           1 :             break;
     774           1 :         case GAAT_REAL_LIST:
     775           2 :             *std::get<std::vector<double> *>(m_value) =
     776           1 :                 *std::get<std::vector<double> *>(other.m_value);
     777           1 :             break;
     778        2813 :         case GAAT_DATASET_LIST:
     779             :         {
     780        2813 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     781        2819 :             for (const auto &val :
     782        8451 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     783             :             {
     784        5638 :                 GDALArgDatasetValue v;
     785        2819 :                 v.SetFrom(val);
     786        2819 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     787        2819 :                     ->push_back(std::move(v));
     788             :             }
     789        2813 :             break;
     790             :         }
     791             :     }
     792        3916 :     m_explicitlySet = true;
     793        3916 :     return RunAllActions();
     794             : }
     795             : 
     796             : /************************************************************************/
     797             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     798             : /************************************************************************/
     799             : 
     800       18608 : bool GDALAlgorithmArg::RunAllActions()
     801             : {
     802       18608 :     if (!RunValidationActions())
     803         156 :         return false;
     804       18452 :     RunActions();
     805       18452 :     return true;
     806             : }
     807             : 
     808             : /************************************************************************/
     809             : /*                    GDALAlgorithmArg::RunActions()                    */
     810             : /************************************************************************/
     811             : 
     812       18453 : void GDALAlgorithmArg::RunActions()
     813             : {
     814       18768 :     for (const auto &f : m_actions)
     815         315 :         f();
     816       18453 : }
     817             : 
     818             : /************************************************************************/
     819             : /*                  GDALAlgorithmArg::ValidateChoice()                  */
     820             : /************************************************************************/
     821             : 
     822             : // Returns the canonical value if matching a valid choice, or empty string
     823             : // otherwise.
     824        2801 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
     825             : {
     826       15492 :     for (const std::string &choice : GetChoices())
     827             :     {
     828       15348 :         if (EQUAL(value.c_str(), choice.c_str()))
     829             :         {
     830        2657 :             return choice;
     831             :         }
     832             :     }
     833             : 
     834         221 :     for (const std::string &choice : GetHiddenChoices())
     835             :     {
     836         198 :         if (EQUAL(value.c_str(), choice.c_str()))
     837             :         {
     838         121 :             return choice;
     839             :         }
     840             :     }
     841             : 
     842          46 :     std::string expected;
     843         262 :     for (const auto &choice : GetChoices())
     844             :     {
     845         239 :         if (!expected.empty())
     846         216 :             expected += ", ";
     847         239 :         expected += '\'';
     848         239 :         expected += choice;
     849         239 :         expected += '\'';
     850             :     }
     851          23 :     if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
     852             :     {
     853           6 :         return "?";
     854             :     }
     855          34 :     CPLError(CE_Failure, CPLE_IllegalArg,
     856             :              "Invalid value '%s' for string argument '%s'. Should be "
     857             :              "one among %s.",
     858          17 :              value.c_str(), GetName().c_str(), expected.c_str());
     859          17 :     return std::string();
     860             : }
     861             : 
     862             : /************************************************************************/
     863             : /*                 GDALAlgorithmArg::ValidateIntRange()                 */
     864             : /************************************************************************/
     865             : 
     866        2631 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
     867             : {
     868        2631 :     bool ret = true;
     869             : 
     870        2631 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     871        2631 :     if (!std::isnan(minVal))
     872             :     {
     873        1950 :         if (minValIsIncluded && val < minVal)
     874             :         {
     875           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
     876             :                      "Value of argument '%s' is %d, but should be >= %d",
     877           3 :                      GetName().c_str(), val, static_cast<int>(minVal));
     878           3 :             ret = false;
     879             :         }
     880        1947 :         else if (!minValIsIncluded && val <= minVal)
     881             :         {
     882           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     883             :                      "Value of argument '%s' is %d, but should be > %d",
     884           1 :                      GetName().c_str(), val, static_cast<int>(minVal));
     885           1 :             ret = false;
     886             :         }
     887             :     }
     888             : 
     889        2631 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     890        2631 :     if (!std::isnan(maxVal))
     891             :     {
     892             : 
     893         428 :         if (maxValIsIncluded && val > maxVal)
     894             :         {
     895           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     896             :                      "Value of argument '%s' is %d, but should be <= %d",
     897           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     898           1 :             ret = false;
     899             :         }
     900         427 :         else if (!maxValIsIncluded && val >= maxVal)
     901             :         {
     902           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     903             :                      "Value of argument '%s' is %d, but should be < %d",
     904           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     905           1 :             ret = false;
     906             :         }
     907             :     }
     908             : 
     909        2631 :     return ret;
     910             : }
     911             : 
     912             : /************************************************************************/
     913             : /*                GDALAlgorithmArg::ValidateRealRange()                 */
     914             : /************************************************************************/
     915             : 
     916        2791 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
     917             : {
     918        2791 :     bool ret = true;
     919             : 
     920        2791 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     921        2791 :     if (!std::isnan(minVal))
     922             :     {
     923         216 :         if (minValIsIncluded && !(val >= minVal))
     924             :         {
     925          11 :             CPLError(CE_Failure, CPLE_IllegalArg,
     926             :                      "Value of argument '%s' is %g, but should be >= %g",
     927          11 :                      GetName().c_str(), val, minVal);
     928          11 :             ret = false;
     929             :         }
     930         205 :         else if (!minValIsIncluded && !(val > minVal))
     931             :         {
     932           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
     933             :                      "Value of argument '%s' is %g, but should be > %g",
     934           4 :                      GetName().c_str(), val, minVal);
     935           4 :             ret = false;
     936             :         }
     937             :     }
     938             : 
     939        2791 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     940        2791 :     if (!std::isnan(maxVal))
     941             :     {
     942             : 
     943          58 :         if (maxValIsIncluded && !(val <= maxVal))
     944             :         {
     945           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     946             :                      "Value of argument '%s' is %g, but should be <= %g",
     947           2 :                      GetName().c_str(), val, maxVal);
     948           2 :             ret = false;
     949             :         }
     950          56 :         else if (!maxValIsIncluded && !(val < maxVal))
     951             :         {
     952           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     953             :                      "Value of argument '%s' is %g, but should be < %g",
     954           1 :                      GetName().c_str(), val, maxVal);
     955           1 :             ret = false;
     956             :         }
     957             :     }
     958             : 
     959        2791 :     return ret;
     960             : }
     961             : 
     962             : /************************************************************************/
     963             : /*                        CheckDuplicateValues()                        */
     964             : /************************************************************************/
     965             : 
     966             : template <class T>
     967         180 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
     968             :                                  const std::vector<T> &values)
     969             : {
     970         360 :     auto tmpValues = values;
     971         180 :     bool bHasDupValues = false;
     972             :     if constexpr (std::is_floating_point_v<T>)
     973             :     {
     974             :         // Avoid undefined behavior with NaN values
     975           4 :         std::sort(tmpValues.begin(), tmpValues.end(),
     976          21 :                   [](T a, T b)
     977             :                   {
     978          21 :                       if (std::isnan(a) && !std::isnan(b))
     979           3 :                           return true;
     980          18 :                       if (std::isnan(b))
     981          10 :                           return false;
     982           8 :                       return a < b;
     983             :                   });
     984             : 
     985             :         bHasDupValues =
     986           4 :             std::adjacent_find(tmpValues.begin(), tmpValues.end(),
     987           6 :                                [](T a, T b)
     988             :                                {
     989           6 :                                    if (std::isnan(a) && std::isnan(b))
     990           1 :                                        return true;
     991           5 :                                    return a == b;
     992           8 :                                }) != tmpValues.end();
     993             :     }
     994             :     else
     995             :     {
     996         176 :         std::sort(tmpValues.begin(), tmpValues.end());
     997         176 :         bHasDupValues = std::adjacent_find(tmpValues.begin(),
     998         352 :                                            tmpValues.end()) != tmpValues.end();
     999             :     }
    1000         180 :     if (bHasDupValues)
    1001             :     {
    1002          10 :         CPLError(CE_Failure, CPLE_AppDefined,
    1003             :                  "'%s' must be a list of unique values.",
    1004          10 :                  arg->GetName().c_str());
    1005          10 :         return false;
    1006             :     }
    1007         170 :     return true;
    1008             : }
    1009             : 
    1010             : /************************************************************************/
    1011             : /*               GDALAlgorithmArg::RunValidationActions()               */
    1012             : /************************************************************************/
    1013             : 
    1014       41076 : bool GDALAlgorithmArg::RunValidationActions()
    1015             : {
    1016       41076 :     bool ret = true;
    1017             : 
    1018       41076 :     if (GetType() == GAAT_STRING && !GetChoices().empty())
    1019             :     {
    1020        1864 :         auto &val = Get<std::string>();
    1021        3728 :         std::string validVal = ValidateChoice(val);
    1022        1864 :         if (validVal.empty())
    1023          12 :             ret = false;
    1024             :         else
    1025        1852 :             val = std::move(validVal);
    1026             :     }
    1027       39212 :     else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
    1028             :     {
    1029         677 :         auto &values = Get<std::vector<std::string>>();
    1030        1614 :         for (std::string &val : values)
    1031             :         {
    1032        1874 :             std::string validVal = ValidateChoice(val);
    1033         937 :             if (validVal.empty())
    1034           5 :                 ret = false;
    1035             :             else
    1036         932 :                 val = std::move(validVal);
    1037             :         }
    1038             :     }
    1039             : 
    1040             :     const auto CheckMinCharCount =
    1041        1149 :         [this, &ret](const std::string &val, int nMinCharCount)
    1042             :     {
    1043        1137 :         if (val.size() < static_cast<size_t>(nMinCharCount))
    1044             :         {
    1045          12 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1046             :                      "Value of argument '%s' is '%s', but should have at least "
    1047             :                      "%d character%s",
    1048           6 :                      GetName().c_str(), val.c_str(), nMinCharCount,
    1049             :                      nMinCharCount > 1 ? "s" : "");
    1050           6 :             ret = false;
    1051             :         }
    1052       42213 :     };
    1053             : 
    1054             :     const auto CheckMaxCharCount =
    1055       14839 :         [this, &ret](const std::string &val, int nMaxCharCount)
    1056             :     {
    1057       14837 :         if (val.size() > static_cast<size_t>(nMaxCharCount))
    1058             :         {
    1059           2 :             CPLError(
    1060             :                 CE_Failure, CPLE_IllegalArg,
    1061             :                 "Value of argument '%s' is '%s', but should have no more than "
    1062             :                 "%d character%s",
    1063           1 :                 GetName().c_str(), val.c_str(), nMaxCharCount,
    1064             :                 nMaxCharCount > 1 ? "s" : "");
    1065           1 :             ret = false;
    1066             :         }
    1067       55913 :     };
    1068             : 
    1069       41076 :     switch (GetType())
    1070             :     {
    1071        2987 :         case GAAT_BOOLEAN:
    1072        2987 :             break;
    1073             : 
    1074       11333 :         case GAAT_STRING:
    1075             :         {
    1076       11333 :             const auto &val = Get<std::string>();
    1077       11333 :             const int nMinCharCount = GetMinCharCount();
    1078       11333 :             if (nMinCharCount > 0)
    1079             :             {
    1080        1055 :                 CheckMinCharCount(val, nMinCharCount);
    1081             :             }
    1082             : 
    1083       11333 :             const int nMaxCharCount = GetMaxCharCount();
    1084       11333 :             CheckMaxCharCount(val, nMaxCharCount);
    1085       11333 :             break;
    1086             :         }
    1087             : 
    1088        2793 :         case GAAT_STRING_LIST:
    1089             :         {
    1090        2793 :             const int nMinCharCount = GetMinCharCount();
    1091        2793 :             const int nMaxCharCount = GetMaxCharCount();
    1092        2793 :             const auto &values = Get<std::vector<std::string>>();
    1093        6297 :             for (const auto &val : values)
    1094             :             {
    1095        3504 :                 if (nMinCharCount > 0)
    1096          82 :                     CheckMinCharCount(val, nMinCharCount);
    1097        3504 :                 CheckMaxCharCount(val, nMaxCharCount);
    1098             :             }
    1099             : 
    1100        2955 :             if (!GetDuplicateValuesAllowed() &&
    1101         162 :                 !CheckDuplicateValues(this, values))
    1102           2 :                 ret = false;
    1103        2793 :             break;
    1104             :         }
    1105             : 
    1106        2083 :         case GAAT_INTEGER:
    1107             :         {
    1108        2083 :             ret = ValidateIntRange(Get<int>()) && ret;
    1109        2083 :             break;
    1110             :         }
    1111             : 
    1112         270 :         case GAAT_INTEGER_LIST:
    1113             :         {
    1114         270 :             const auto &values = Get<std::vector<int>>();
    1115         818 :             for (int v : values)
    1116         548 :                 ret = ValidateIntRange(v) && ret;
    1117             : 
    1118         273 :             if (!GetDuplicateValuesAllowed() &&
    1119           3 :                 !CheckDuplicateValues(this, values))
    1120           1 :                 ret = false;
    1121         270 :             break;
    1122             :         }
    1123             : 
    1124         701 :         case GAAT_REAL:
    1125             :         {
    1126         701 :             ret = ValidateRealRange(Get<double>()) && ret;
    1127         701 :             break;
    1128             :         }
    1129             : 
    1130         767 :         case GAAT_REAL_LIST:
    1131             :         {
    1132         767 :             const auto &values = Get<std::vector<double>>();
    1133        2857 :             for (double v : values)
    1134        2090 :                 ret = ValidateRealRange(v) && ret;
    1135             : 
    1136         771 :             if (!GetDuplicateValuesAllowed() &&
    1137           4 :                 !CheckDuplicateValues(this, values))
    1138           2 :                 ret = false;
    1139         767 :             break;
    1140             :         }
    1141             : 
    1142        6615 :         case GAAT_DATASET:
    1143        6615 :             break;
    1144             : 
    1145       13527 :         case GAAT_DATASET_LIST:
    1146             :         {
    1147       13527 :             if (!GetDuplicateValuesAllowed())
    1148             :             {
    1149          11 :                 const auto &values = Get<std::vector<GDALArgDatasetValue>>();
    1150          22 :                 std::vector<std::string> aosValues;
    1151          34 :                 for (const auto &v : values)
    1152             :                 {
    1153          23 :                     const GDALDataset *poDS = v.GetDatasetRef();
    1154          23 :                     if (poDS)
    1155             :                     {
    1156          16 :                         auto poDriver = poDS->GetDriver();
    1157             :                         // The dataset name for a MEM driver is not relevant,
    1158             :                         // so use the pointer address
    1159          32 :                         if ((poDriver &&
    1160          24 :                              EQUAL(poDriver->GetDescription(), "MEM")) ||
    1161           8 :                             poDS->GetDescription()[0] == 0)
    1162             :                         {
    1163           8 :                             aosValues.push_back(CPLSPrintf("%p", poDS));
    1164             :                         }
    1165             :                         else
    1166             :                         {
    1167           8 :                             aosValues.push_back(poDS->GetDescription());
    1168             :                         }
    1169             :                     }
    1170             :                     else
    1171             :                     {
    1172           7 :                         aosValues.push_back(v.GetName());
    1173             :                     }
    1174             :                 }
    1175          11 :                 if (!CheckDuplicateValues(this, aosValues))
    1176           5 :                     ret = false;
    1177             :             }
    1178       13527 :             break;
    1179             :         }
    1180             :     }
    1181             : 
    1182       41076 :     if (GDALAlgorithmArgTypeIsList(GetType()))
    1183             :     {
    1184       17357 :         int valueCount = 0;
    1185       17357 :         if (GetType() == GAAT_STRING_LIST)
    1186             :         {
    1187        2793 :             valueCount =
    1188        2793 :                 static_cast<int>(Get<std::vector<std::string>>().size());
    1189             :         }
    1190       14564 :         else if (GetType() == GAAT_INTEGER_LIST)
    1191             :         {
    1192         270 :             valueCount = static_cast<int>(Get<std::vector<int>>().size());
    1193             :         }
    1194       14294 :         else if (GetType() == GAAT_REAL_LIST)
    1195             :         {
    1196         767 :             valueCount = static_cast<int>(Get<std::vector<double>>().size());
    1197             :         }
    1198       13527 :         else if (GetType() == GAAT_DATASET_LIST)
    1199             :         {
    1200       13527 :             valueCount = static_cast<int>(
    1201       13527 :                 Get<std::vector<GDALArgDatasetValue>>().size());
    1202             :         }
    1203             : 
    1204       17357 :         if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
    1205             :         {
    1206          14 :             ReportError(CE_Failure, CPLE_AppDefined,
    1207             :                         "%d value%s been specified for argument '%s', "
    1208             :                         "whereas exactly %d %s expected.",
    1209             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1210           7 :                         GetName().c_str(), GetMinCount(),
    1211           7 :                         GetMinCount() > 1 ? "were" : "was");
    1212           7 :             ret = false;
    1213             :         }
    1214       17350 :         else if (valueCount < GetMinCount())
    1215             :         {
    1216           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1217             :                         "Only %d value%s been specified for argument '%s', "
    1218             :                         "whereas at least %d %s expected.",
    1219             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1220           3 :                         GetName().c_str(), GetMinCount(),
    1221           3 :                         GetMinCount() > 1 ? "were" : "was");
    1222           3 :             ret = false;
    1223             :         }
    1224       17347 :         else if (valueCount > GetMaxCount())
    1225             :         {
    1226           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    1227             :                         "%d value%s been specified for argument '%s', "
    1228             :                         "whereas at most %d %s expected.",
    1229             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1230           1 :                         GetName().c_str(), GetMaxCount(),
    1231           1 :                         GetMaxCount() > 1 ? "were" : "was");
    1232           1 :             ret = false;
    1233             :         }
    1234             :     }
    1235             : 
    1236       41076 :     if (ret)
    1237             :     {
    1238       49048 :         for (const auto &f : m_validationActions)
    1239             :         {
    1240        8041 :             if (!f())
    1241          96 :                 ret = false;
    1242             :         }
    1243             :     }
    1244             : 
    1245       41076 :     return ret;
    1246             : }
    1247             : 
    1248             : /************************************************************************/
    1249             : /*                   GDALAlgorithmArg::ReportError()                    */
    1250             : /************************************************************************/
    1251             : 
    1252          11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1253             :                                    const char *fmt, ...) const
    1254             : {
    1255             :     va_list args;
    1256          11 :     va_start(args, fmt);
    1257          11 :     if (m_owner)
    1258             :     {
    1259          11 :         m_owner->ReportError(eErrClass, err_no, "%s",
    1260          22 :                              CPLString().vPrintf(fmt, args).c_str());
    1261             :     }
    1262             :     else
    1263             :     {
    1264           0 :         CPLError(eErrClass, err_no, "%s",
    1265           0 :                  CPLString().vPrintf(fmt, args).c_str());
    1266             :     }
    1267          11 :     va_end(args);
    1268          11 : }
    1269             : 
    1270             : /************************************************************************/
    1271             : /*                 GDALAlgorithmArg::GetEscapedString()                 */
    1272             : /************************************************************************/
    1273             : 
    1274             : /* static */
    1275         197 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
    1276             : {
    1277         215 :     if (s.find_first_of("\" \\,") != std::string::npos &&
    1278           9 :         !(s.size() > 4 &&
    1279           9 :           s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
    1280           2 :           s[1] == ' ' && s[s.size() - 2] == ' ' &&
    1281           2 :           s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
    1282             :     {
    1283          14 :         return std::string("\"")
    1284             :             .append(
    1285          14 :                 CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
    1286           7 :             .append("\"");
    1287             :     }
    1288             :     else
    1289             :     {
    1290         190 :         return s;
    1291             :     }
    1292             : }
    1293             : 
    1294             : /************************************************************************/
    1295             : /*                    GDALAlgorithmArg::Serialize()                     */
    1296             : /************************************************************************/
    1297             : 
    1298          43 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
    1299             :                                  bool absolutePath) const
    1300             : {
    1301          43 :     serializedArg.clear();
    1302             : 
    1303          43 :     if (!IsExplicitlySet())
    1304             :     {
    1305           0 :         return false;
    1306             :     }
    1307             : 
    1308          86 :     std::string ret = "--";
    1309          43 :     ret += GetName();
    1310          43 :     if (GetType() == GAAT_BOOLEAN)
    1311             :     {
    1312           0 :         serializedArg = std::move(ret);
    1313           0 :         return true;
    1314             :     }
    1315             : 
    1316           7 :     const auto AddListValueSeparator = [this, &ret]()
    1317             :     {
    1318           2 :         if (GetPackedValuesAllowed())
    1319             :         {
    1320           1 :             ret += ',';
    1321             :         }
    1322             :         else
    1323             :         {
    1324           1 :             ret += " --";
    1325           1 :             ret += GetName();
    1326           1 :             ret += ' ';
    1327             :         }
    1328          45 :     };
    1329             : 
    1330           0 :     const auto MakeAbsolutePath = [](const std::string &filename)
    1331             :     {
    1332             :         VSIStatBufL sStat;
    1333           0 :         if (VSIStatL(filename.c_str(), &sStat) != 0 ||
    1334           0 :             !CPLIsFilenameRelative(filename.c_str()))
    1335           0 :             return filename;
    1336           0 :         char *pszCWD = CPLGetCurrentDir();
    1337           0 :         if (!pszCWD)
    1338           0 :             return filename;
    1339             :         const auto absPath =
    1340           0 :             CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
    1341           0 :         CPLFree(pszCWD);
    1342           0 :         return absPath;
    1343             :     };
    1344             : 
    1345          43 :     ret += ' ';
    1346          43 :     switch (GetType())
    1347             :     {
    1348           0 :         case GAAT_BOOLEAN:
    1349           0 :             break;
    1350           8 :         case GAAT_STRING:
    1351             :         {
    1352           8 :             const auto &val = Get<std::string>();
    1353           8 :             ret += GetEscapedString(val);
    1354           8 :             break;
    1355             :         }
    1356           1 :         case GAAT_INTEGER:
    1357             :         {
    1358           1 :             ret += CPLSPrintf("%d", Get<int>());
    1359           1 :             break;
    1360             :         }
    1361           0 :         case GAAT_REAL:
    1362             :         {
    1363           0 :             ret += CPLSPrintf("%.17g", Get<double>());
    1364           0 :             break;
    1365             :         }
    1366           2 :         case GAAT_DATASET:
    1367             :         {
    1368           2 :             const auto &val = Get<GDALArgDatasetValue>();
    1369           2 :             const auto &str = val.GetName();
    1370           2 :             if (str.empty())
    1371             :             {
    1372           0 :                 return false;
    1373             :             }
    1374           2 :             ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
    1375           2 :             break;
    1376             :         }
    1377           5 :         case GAAT_STRING_LIST:
    1378             :         {
    1379           5 :             const auto &vals = Get<std::vector<std::string>>();
    1380          11 :             for (size_t i = 0; i < vals.size(); ++i)
    1381             :             {
    1382           6 :                 if (i > 0)
    1383           2 :                     AddListValueSeparator();
    1384           6 :                 ret += GetEscapedString(vals[i]);
    1385             :             }
    1386           5 :             break;
    1387             :         }
    1388           0 :         case GAAT_INTEGER_LIST:
    1389             :         {
    1390           0 :             const auto &vals = Get<std::vector<int>>();
    1391           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1392             :             {
    1393           0 :                 if (i > 0)
    1394           0 :                     AddListValueSeparator();
    1395           0 :                 ret += CPLSPrintf("%d", vals[i]);
    1396             :             }
    1397           0 :             break;
    1398             :         }
    1399           0 :         case GAAT_REAL_LIST:
    1400             :         {
    1401           0 :             const auto &vals = Get<std::vector<double>>();
    1402           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1403             :             {
    1404           0 :                 if (i > 0)
    1405           0 :                     AddListValueSeparator();
    1406           0 :                 ret += CPLSPrintf("%.17g", vals[i]);
    1407             :             }
    1408           0 :             break;
    1409             :         }
    1410          27 :         case GAAT_DATASET_LIST:
    1411             :         {
    1412          27 :             const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
    1413          53 :             for (size_t i = 0; i < vals.size(); ++i)
    1414             :             {
    1415          27 :                 if (i > 0)
    1416           0 :                     AddListValueSeparator();
    1417          27 :                 const auto &val = vals[i];
    1418          27 :                 const auto &str = val.GetName();
    1419          27 :                 if (str.empty())
    1420             :                 {
    1421           1 :                     return false;
    1422             :                 }
    1423          52 :                 ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
    1424          26 :                                                      : str);
    1425             :             }
    1426          26 :             break;
    1427             :         }
    1428             :     }
    1429             : 
    1430          42 :     serializedArg = std::move(ret);
    1431          42 :     return true;
    1432             : }
    1433             : 
    1434             : /************************************************************************/
    1435             : /*                  ~GDALInConstructionAlgorithmArg()                   */
    1436             : /************************************************************************/
    1437             : 
    1438             : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
    1439             : 
    1440             : /************************************************************************/
    1441             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
    1442             : /************************************************************************/
    1443             : 
    1444             : GDALInConstructionAlgorithmArg &
    1445       70350 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
    1446             : {
    1447       70350 :     m_decl.AddAlias(alias);
    1448       70350 :     if (m_owner)
    1449       70350 :         m_owner->AddAliasFor(this, alias);
    1450       70350 :     return *this;
    1451             : }
    1452             : 
    1453             : /************************************************************************/
    1454             : /*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
    1455             : /************************************************************************/
    1456             : 
    1457             : GDALInConstructionAlgorithmArg &
    1458       18894 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
    1459             : {
    1460       18894 :     m_decl.AddHiddenAlias(alias);
    1461       18894 :     if (m_owner)
    1462       18894 :         m_owner->AddAliasFor(this, alias);
    1463       18894 :     return *this;
    1464             : }
    1465             : 
    1466             : /************************************************************************/
    1467             : /*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
    1468             : /************************************************************************/
    1469             : 
    1470             : GDALInConstructionAlgorithmArg &
    1471          50 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
    1472             : {
    1473          50 :     m_decl.AddShortNameAlias(shortNameAlias);
    1474          50 :     if (m_owner)
    1475          50 :         m_owner->AddShortNameAliasFor(this, shortNameAlias);
    1476          50 :     return *this;
    1477             : }
    1478             : 
    1479             : /************************************************************************/
    1480             : /*           GDALInConstructionAlgorithmArg::SetPositional()            */
    1481             : /************************************************************************/
    1482             : 
    1483       24528 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
    1484             : {
    1485       24528 :     m_decl.SetPositional();
    1486       24528 :     if (m_owner)
    1487       24528 :         m_owner->SetPositional(this);
    1488       24528 :     return *this;
    1489             : }
    1490             : 
    1491             : /************************************************************************/
    1492             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
    1493             : /************************************************************************/
    1494             : 
    1495        1530 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
    1496        3060 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
    1497        1530 :       m_nameSet(true)
    1498             : {
    1499        1530 :     if (m_poDS)
    1500        1530 :         m_poDS->Reference();
    1501        1530 : }
    1502             : 
    1503             : /************************************************************************/
    1504             : /*                      GDALArgDatasetValue::Set()                      */
    1505             : /************************************************************************/
    1506             : 
    1507        2564 : void GDALArgDatasetValue::Set(const std::string &name)
    1508             : {
    1509        2564 :     Close();
    1510        2564 :     m_name = name;
    1511        2564 :     m_nameSet = true;
    1512        2564 :     if (m_ownerArg)
    1513        2558 :         m_ownerArg->NotifyValueSet();
    1514        2564 : }
    1515             : 
    1516             : /************************************************************************/
    1517             : /*                      GDALArgDatasetValue::Set()                      */
    1518             : /************************************************************************/
    1519             : 
    1520        2377 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
    1521             : {
    1522        2377 :     Close();
    1523        2377 :     m_poDS = poDS.release();
    1524        2377 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1525        2377 :     m_nameSet = true;
    1526        2377 :     if (m_ownerArg)
    1527        2160 :         m_ownerArg->NotifyValueSet();
    1528        2377 : }
    1529             : 
    1530             : /************************************************************************/
    1531             : /*                      GDALArgDatasetValue::Set()                      */
    1532             : /************************************************************************/
    1533             : 
    1534        9460 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
    1535             : {
    1536        9460 :     Close();
    1537        9460 :     m_poDS = poDS;
    1538        9460 :     if (m_poDS)
    1539        8445 :         m_poDS->Reference();
    1540        9460 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1541        9460 :     m_nameSet = true;
    1542        9460 :     if (m_ownerArg)
    1543        3765 :         m_ownerArg->NotifyValueSet();
    1544        9460 : }
    1545             : 
    1546             : /************************************************************************/
    1547             : /*                    GDALArgDatasetValue::SetFrom()                    */
    1548             : /************************************************************************/
    1549             : 
    1550        3963 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
    1551             : {
    1552        3963 :     Close();
    1553        3963 :     m_name = other.m_name;
    1554        3963 :     m_nameSet = other.m_nameSet;
    1555        3963 :     m_poDS = other.m_poDS;
    1556        3963 :     if (m_poDS)
    1557        2799 :         m_poDS->Reference();
    1558        3963 : }
    1559             : 
    1560             : /************************************************************************/
    1561             : /*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
    1562             : /************************************************************************/
    1563             : 
    1564       36588 : GDALArgDatasetValue::~GDALArgDatasetValue()
    1565             : {
    1566       36588 :     Close();
    1567       36588 : }
    1568             : 
    1569             : /************************************************************************/
    1570             : /*                     GDALArgDatasetValue::Close()                     */
    1571             : /************************************************************************/
    1572             : 
    1573       61362 : bool GDALArgDatasetValue::Close()
    1574             : {
    1575       61362 :     bool ret = true;
    1576       61362 :     if (m_poDS && m_poDS->Dereference() == 0)
    1577             :     {
    1578        3899 :         ret = m_poDS->Close() == CE_None;
    1579        3899 :         delete m_poDS;
    1580             :     }
    1581       61362 :     m_poDS = nullptr;
    1582       61362 :     return ret;
    1583             : }
    1584             : 
    1585             : /************************************************************************/
    1586             : /*                   GDALArgDatasetValue::operator=()                   */
    1587             : /************************************************************************/
    1588             : 
    1589           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
    1590             : {
    1591           2 :     Close();
    1592           2 :     m_poDS = other.m_poDS;
    1593           2 :     m_name = other.m_name;
    1594           2 :     m_nameSet = other.m_nameSet;
    1595           2 :     other.m_poDS = nullptr;
    1596           2 :     other.m_name.clear();
    1597           2 :     other.m_nameSet = false;
    1598           2 :     return *this;
    1599             : }
    1600             : 
    1601             : /************************************************************************/
    1602             : /*                  GDALArgDatasetValue::GetDataset()                   */
    1603             : /************************************************************************/
    1604             : 
    1605        1233 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
    1606             : {
    1607        1233 :     if (m_poDS)
    1608        1228 :         m_poDS->Reference();
    1609        1233 :     return m_poDS;
    1610             : }
    1611             : 
    1612             : /************************************************************************/
    1613             : /*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
    1614             : /************************************************************************/
    1615             : 
    1616        3770 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
    1617        3770 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
    1618             : {
    1619        3770 :     other.m_poDS = nullptr;
    1620        3770 :     other.m_name.clear();
    1621        3770 : }
    1622             : 
    1623             : /************************************************************************/
    1624             : /*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
    1625             : /************************************************************************/
    1626             : 
    1627        3818 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
    1628             :     bool noneAllowed, const std::vector<std::string> &specialValues)
    1629             : {
    1630        3818 :     if (GetType() != GAAT_STRING)
    1631             :     {
    1632           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1633             :                  "SetIsCRSArg() can only be called on a String argument");
    1634           1 :         return *this;
    1635             :     }
    1636             :     AddValidationAction(
    1637         921 :         [this, noneAllowed, specialValues]()
    1638             :         {
    1639             :             const std::string &osVal =
    1640             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
    1641         457 :                     ->Get<std::string>();
    1642         457 :             if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
    1643           0 :                 return true;
    1644             : 
    1645         901 :             if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
    1646         444 :                 std::find(specialValues.begin(), specialValues.end(), osVal) ==
    1647         901 :                     specialValues.end())
    1648             :             {
    1649         436 :                 OGRSpatialReference oSRS;
    1650         436 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
    1651             :                 {
    1652           7 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
    1653             :                                          "Invalid value for '%s' argument",
    1654           7 :                                          GetName().c_str());
    1655           7 :                     return false;
    1656             :                 }
    1657             :             }
    1658         450 :             return true;
    1659        3817 :         });
    1660             : 
    1661             :     SetAutoCompleteFunction(
    1662          44 :         [this, noneAllowed, specialValues](const std::string &currentValue)
    1663             :         {
    1664          11 :             bool bIsRaster = false;
    1665          11 :             OGREnvelope sDatasetLongLatEnv;
    1666          22 :             std::string osCelestialBodyName;
    1667          11 :             if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
    1668             :             {
    1669          11 :                 auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
    1670          11 :                 if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    1671             :                 {
    1672             :                     auto &val =
    1673          11 :                         inputArg->Get<std::vector<GDALArgDatasetValue>>();
    1674          11 :                     if (val.size() == 1)
    1675             :                     {
    1676           4 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1677             :                         auto poDS = std::unique_ptr<GDALDataset>(
    1678           4 :                             GDALDataset::Open(val[0].GetName().c_str()));
    1679           2 :                         if (poDS)
    1680             :                         {
    1681           2 :                             bIsRaster = poDS->GetRasterCount() != 0;
    1682           2 :                             if (auto poCRS = poDS->GetSpatialRef())
    1683             :                             {
    1684             :                                 const char *pszCelestialBodyName =
    1685           2 :                                     poCRS->GetCelestialBodyName();
    1686           2 :                                 if (pszCelestialBodyName)
    1687           2 :                                     osCelestialBodyName = pszCelestialBodyName;
    1688             : 
    1689           2 :                                 if (!pszCelestialBodyName ||
    1690           2 :                                     !EQUAL(pszCelestialBodyName, "Earth"))
    1691             :                                 {
    1692           0 :                                     OGRSpatialReference oLongLat;
    1693           0 :                                     oLongLat.CopyGeogCSFrom(poCRS);
    1694           0 :                                     oLongLat.SetAxisMappingStrategy(
    1695             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    1696           0 :                                     poDS->GetExtent(&sDatasetLongLatEnv,
    1697           0 :                                                     &oLongLat);
    1698             :                                 }
    1699             :                                 else
    1700             :                                 {
    1701           2 :                                     poDS->GetExtentWGS84LongLat(
    1702           2 :                                         &sDatasetLongLatEnv);
    1703             :                                 }
    1704             :                             }
    1705             :                         }
    1706             :                     }
    1707             :                 }
    1708             :             }
    1709             : 
    1710             :             const auto IsCRSCompatible =
    1711       42959 :                 [bIsRaster, &sDatasetLongLatEnv,
    1712       73695 :                  &osCelestialBodyName](const OSRCRSInfo *crsInfo)
    1713             :             {
    1714       42959 :                 if (!sDatasetLongLatEnv.IsInit())
    1715       30685 :                     return true;
    1716       24108 :                 return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
    1717       11834 :                        !(bIsRaster &&
    1718        5917 :                          crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
    1719       11652 :                        crsInfo->dfWestLongitudeDeg <
    1720       11652 :                            crsInfo->dfEastLongitudeDeg &&
    1721       11517 :                        sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
    1722        5618 :                        sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
    1723         615 :                        sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
    1724       24437 :                        sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
    1725         329 :                        ((!osCelestialBodyName.empty() &&
    1726         658 :                          crsInfo->pszCelestialBodyName &&
    1727         329 :                          osCelestialBodyName ==
    1728         329 :                              crsInfo->pszCelestialBodyName) ||
    1729           0 :                         (osCelestialBodyName.empty() &&
    1730       12274 :                          !crsInfo->pszCelestialBodyName));
    1731          11 :             };
    1732             : 
    1733          11 :             std::vector<std::string> oRet;
    1734          11 :             if (noneAllowed)
    1735           0 :                 oRet.push_back("none");
    1736          11 :             oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
    1737          11 :             if (!currentValue.empty())
    1738             :             {
    1739             :                 const CPLStringList aosTokens(
    1740          14 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
    1741           7 :                 int nCount = 0;
    1742             :                 std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
    1743             :                     pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
    1744             :                                                            nullptr, &nCount),
    1745          14 :                              OSRDestroyCRSInfoList);
    1746          14 :                 std::string osCode;
    1747             : 
    1748          14 :                 std::vector<const OSRCRSInfo *> candidates;
    1749       46270 :                 for (int i = 0; i < nCount; ++i)
    1750             :                 {
    1751       46263 :                     const auto *entry = (pCRSList.get())[i];
    1752       46263 :                     if (!entry->bDeprecated && IsCRSCompatible(entry))
    1753             :                     {
    1754       49425 :                         if (aosTokens.size() == 1 ||
    1755       18411 :                             STARTS_WITH(entry->pszCode, aosTokens[1]))
    1756             :                         {
    1757       12666 :                             if (candidates.empty())
    1758           7 :                                 osCode = entry->pszCode;
    1759       12666 :                             candidates.push_back(entry);
    1760             :                         }
    1761             :                     }
    1762             :                 }
    1763           7 :                 if (candidates.size() == 1)
    1764             :                 {
    1765           1 :                     oRet.push_back(std::move(osCode));
    1766             :                 }
    1767             :                 else
    1768             :                 {
    1769           6 :                     if (sDatasetLongLatEnv.IsInit())
    1770             :                     {
    1771           2 :                         std::sort(
    1772             :                             candidates.begin(), candidates.end(),
    1773        2999 :                             [](const OSRCRSInfo *a, const OSRCRSInfo *b)
    1774             :                             {
    1775        2999 :                                 const double dfXa =
    1776        2999 :                                     a->dfWestLongitudeDeg >
    1777        2999 :                                             a->dfEastLongitudeDeg
    1778        2999 :                                         ? a->dfWestLongitudeDeg -
    1779           0 :                                               a->dfEastLongitudeDeg
    1780        2999 :                                         : (180 - a->dfWestLongitudeDeg) +
    1781        2999 :                                               (a->dfEastLongitudeDeg - -180);
    1782        2999 :                                 const double dfYa = a->dfNorthLatitudeDeg -
    1783        2999 :                                                     a->dfSouthLatitudeDeg;
    1784        2999 :                                 const double dfXb =
    1785        2999 :                                     b->dfWestLongitudeDeg >
    1786        2999 :                                             b->dfEastLongitudeDeg
    1787        2999 :                                         ? b->dfWestLongitudeDeg -
    1788           0 :                                               b->dfEastLongitudeDeg
    1789        2999 :                                         : (180 - b->dfWestLongitudeDeg) +
    1790        2999 :                                               (b->dfEastLongitudeDeg - -180);
    1791        2999 :                                 const double dfYb = b->dfNorthLatitudeDeg -
    1792        2999 :                                                     b->dfSouthLatitudeDeg;
    1793        2999 :                                 const double diffArea =
    1794        2999 :                                     dfXa * dfYa - dfXb * dfYb;
    1795        2999 :                                 if (diffArea < 0)
    1796         279 :                                     return true;
    1797        2720 :                                 if (diffArea == 0)
    1798             :                                 {
    1799        2506 :                                     if (std::string_view(a->pszName) ==
    1800        2506 :                                         b->pszName)
    1801             :                                     {
    1802          57 :                                         if (a->eType ==
    1803          13 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D &&
    1804          13 :                                             b->eType !=
    1805             :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1806          13 :                                             return true;
    1807          44 :                                         if (a->eType ==
    1808          32 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_3D &&
    1809          32 :                                             b->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1810           9 :                                             return true;
    1811          35 :                                         return false;
    1812             :                                     }
    1813        4898 :                                     return std::string_view(a->pszCode) <
    1814        4898 :                                            b->pszCode;
    1815             :                                 }
    1816         214 :                                 return false;
    1817             :                             });
    1818             :                     }
    1819             : 
    1820       12671 :                     for (const auto *entry : candidates)
    1821             :                     {
    1822       25330 :                         std::string val = std::string(entry->pszCode)
    1823       12665 :                                               .append(" -- ")
    1824       25330 :                                               .append(entry->pszName);
    1825       12665 :                         if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1826        1294 :                             val.append(" (geographic 2D)");
    1827       11371 :                         else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
    1828         446 :                             val.append(" (geographic 3D)");
    1829       10925 :                         else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1830         397 :                             val.append(" (geocentric)");
    1831       12665 :                         oRet.push_back(std::move(val));
    1832             :                     }
    1833             :                 }
    1834             :             }
    1835          11 :             if (currentValue.empty() || oRet.empty())
    1836             :             {
    1837             :                 const CPLStringList aosAuthorities(
    1838           8 :                     OSRGetAuthorityListFromDatabase());
    1839          24 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
    1840             :                 {
    1841          20 :                     int nCount = 0;
    1842          20 :                     OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
    1843             :                         pszAuth, nullptr, &nCount));
    1844          20 :                     if (nCount)
    1845          16 :                         oRet.push_back(std::string(pszAuth).append(":"));
    1846             :                 }
    1847             :             }
    1848          22 :             return oRet;
    1849        3817 :         });
    1850             : 
    1851        3817 :     return *this;
    1852             : }
    1853             : 
    1854             : /************************************************************************/
    1855             : /*                    GDALAlgorithm::GDALAlgorithm()                    */
    1856             : /************************************************************************/
    1857             : 
    1858       25157 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
    1859             :                              const std::string &description,
    1860       25157 :                              const std::string &helpURL)
    1861             :     : m_name(name), m_description(description), m_helpURL(helpURL),
    1862       49724 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
    1863       25157 :                         ? "https://gdal.org" + m_helpURL
    1864       74622 :                         : m_helpURL)
    1865             : {
    1866             :     auto &helpArg =
    1867             :         AddArg("help", 'h', _("Display help message and exit"),
    1868       50314 :                &m_helpRequested)
    1869       25157 :             .SetHiddenForAPI()
    1870       50314 :             .SetCategory(GAAC_COMMON)
    1871          14 :             .AddAction([this]()
    1872       25157 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1873             :     auto &helpDocArg =
    1874             :         AddArg("help-doc", 0,
    1875             :                _("Display help message for use by documentation"),
    1876       50314 :                &m_helpDocRequested)
    1877       25157 :             .SetHidden()
    1878          16 :             .AddAction([this]()
    1879       25157 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1880             :     auto &jsonUsageArg =
    1881             :         AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
    1882       50314 :                &m_JSONUsageRequested)
    1883       25157 :             .SetHiddenForAPI()
    1884       50314 :             .SetCategory(GAAC_COMMON)
    1885           4 :             .AddAction([this]()
    1886       25157 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1887       50314 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
    1888       50314 :         .SetMetaVar("<KEY>=<VALUE>")
    1889       25157 :         .SetHiddenForAPI()
    1890       50314 :         .SetCategory(GAAC_COMMON)
    1891             :         .AddAction(
    1892           2 :             [this]()
    1893             :             {
    1894           2 :                 ReportError(
    1895             :                     CE_Warning, CPLE_AppDefined,
    1896             :                     "Configuration options passed with the 'config' argument "
    1897             :                     "are ignored");
    1898       25157 :             });
    1899             : 
    1900       25157 :     AddValidationAction(
    1901       15856 :         [this, &helpArg, &helpDocArg, &jsonUsageArg]()
    1902             :         {
    1903        8152 :             if (!m_calledFromCommandLine && m_specialActionRequested)
    1904             :             {
    1905           0 :                 for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
    1906             :                 {
    1907           0 :                     if (arg->IsExplicitlySet())
    1908             :                     {
    1909           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    1910             :                                     "'%s' argument only available when called "
    1911             :                                     "from command line",
    1912           0 :                                     arg->GetName().c_str());
    1913           0 :                         return false;
    1914             :                     }
    1915             :                 }
    1916             :             }
    1917        8152 :             return true;
    1918             :         });
    1919       25157 : }
    1920             : 
    1921             : /************************************************************************/
    1922             : /*                   GDALAlgorithm::~GDALAlgorithm()                    */
    1923             : /************************************************************************/
    1924             : 
    1925             : GDALAlgorithm::~GDALAlgorithm() = default;
    1926             : 
    1927             : /************************************************************************/
    1928             : /*                    GDALAlgorithm::ParseArgument()                    */
    1929             : /************************************************************************/
    1930             : 
    1931        3473 : bool GDALAlgorithm::ParseArgument(
    1932             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
    1933             :     std::map<
    1934             :         GDALAlgorithmArg *,
    1935             :         std::variant<std::vector<std::string>, std::vector<int>,
    1936             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1937             :         &inConstructionValues)
    1938             : {
    1939             :     const bool isListArg =
    1940        3473 :         GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
    1941        3473 :     if (arg->IsExplicitlySet() && !isListArg)
    1942             :     {
    1943             :         // Hack for "gdal info" to be able to pass an opened raster dataset
    1944             :         // by "gdal raster info" to the "gdal vector info" algorithm.
    1945           4 :         if (arg->SkipIfAlreadySet())
    1946             :         {
    1947           1 :             arg->SetSkipIfAlreadySet(false);
    1948           1 :             return true;
    1949             :         }
    1950             : 
    1951           3 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1952             :                     "Argument '%s' has already been specified.", name.c_str());
    1953           3 :         return false;
    1954             :     }
    1955             : 
    1956        3543 :     if (!arg->GetRepeatedArgAllowed() &&
    1957          74 :         cpl::contains(inConstructionValues, arg))
    1958             :     {
    1959           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1960             :                     "Argument '%s' has already been specified.", name.c_str());
    1961           1 :         return false;
    1962             :     }
    1963             : 
    1964        3468 :     switch (arg->GetType())
    1965             :     {
    1966         336 :         case GAAT_BOOLEAN:
    1967             :         {
    1968         336 :             if (value.empty() || value == "true")
    1969         334 :                 return arg->Set(true);
    1970           2 :             else if (value == "false")
    1971           1 :                 return arg->Set(false);
    1972             :             else
    1973             :             {
    1974           1 :                 ReportError(
    1975             :                     CE_Failure, CPLE_IllegalArg,
    1976             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
    1977             :                     "'true' or 'false'.",
    1978             :                     value.c_str(), name.c_str());
    1979           1 :                 return false;
    1980             :             }
    1981             :         }
    1982             : 
    1983         872 :         case GAAT_STRING:
    1984             :         {
    1985         872 :             return arg->Set(value);
    1986             :         }
    1987             : 
    1988         359 :         case GAAT_INTEGER:
    1989             :         {
    1990         359 :             errno = 0;
    1991         359 :             char *endptr = nullptr;
    1992         359 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
    1993         358 :             if (errno == 0 && endptr &&
    1994         717 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
    1995             :                 val <= INT_MAX)
    1996             :             {
    1997         356 :                 return arg->Set(static_cast<int>(val));
    1998             :             }
    1999             :             else
    2000             :             {
    2001           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    2002             :                             "Expected integer value for argument '%s', "
    2003             :                             "but got '%s'.",
    2004             :                             name.c_str(), value.c_str());
    2005           3 :                 return false;
    2006             :             }
    2007             :         }
    2008             : 
    2009          36 :         case GAAT_REAL:
    2010             :         {
    2011          36 :             char *endptr = nullptr;
    2012          36 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
    2013          36 :             if (endptr != value.c_str() + value.size())
    2014             :             {
    2015           1 :                 ReportError(
    2016             :                     CE_Failure, CPLE_IllegalArg,
    2017             :                     "Expected real value for argument '%s', but got '%s'.",
    2018             :                     name.c_str(), value.c_str());
    2019           1 :                 return false;
    2020             :             }
    2021          35 :             return arg->Set(dfValue);
    2022             :         }
    2023             : 
    2024         619 :         case GAAT_DATASET:
    2025             :         {
    2026         619 :             return arg->SetDatasetName(value);
    2027             :         }
    2028             : 
    2029         275 :         case GAAT_STRING_LIST:
    2030             :         {
    2031             :             const CPLStringList aosTokens(
    2032         275 :                 arg->GetPackedValuesAllowed()
    2033         184 :                     ? CSLTokenizeString2(value.c_str(), ",",
    2034             :                                          CSLT_HONOURSTRINGS |
    2035             :                                              CSLT_PRESERVEQUOTES)
    2036         459 :                     : CSLAddString(nullptr, value.c_str()));
    2037         275 :             if (!cpl::contains(inConstructionValues, arg))
    2038             :             {
    2039         250 :                 inConstructionValues[arg] = std::vector<std::string>();
    2040             :             }
    2041             :             auto &valueVector =
    2042         275 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
    2043         583 :             for (const char *v : aosTokens)
    2044             :             {
    2045         308 :                 valueVector.push_back(v);
    2046             :             }
    2047         275 :             if (arg->GetMaxCount() == 1)
    2048             :             {
    2049           4 :                 bool ret = arg->Set(std::move(valueVector));
    2050           4 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2051           4 :                 return ret;
    2052             :             }
    2053             : 
    2054         271 :             break;
    2055             :         }
    2056             : 
    2057          63 :         case GAAT_INTEGER_LIST:
    2058             :         {
    2059             :             const CPLStringList aosTokens(
    2060          63 :                 arg->GetPackedValuesAllowed()
    2061          63 :                     ? CSLTokenizeString2(
    2062             :                           value.c_str(), ",",
    2063             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2064             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2065         126 :                     : CSLAddString(nullptr, value.c_str()));
    2066          63 :             if (!cpl::contains(inConstructionValues, arg))
    2067             :             {
    2068          59 :                 inConstructionValues[arg] = std::vector<int>();
    2069             :             }
    2070             :             auto &valueVector =
    2071          63 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    2072         191 :             for (const char *v : aosTokens)
    2073             :             {
    2074         134 :                 errno = 0;
    2075         134 :                 char *endptr = nullptr;
    2076         134 :                 const auto val = std::strtol(v, &endptr, 10);
    2077         134 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    2078         130 :                     val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
    2079             :                 {
    2080         128 :                     valueVector.push_back(static_cast<int>(val));
    2081             :                 }
    2082             :                 else
    2083             :                 {
    2084           6 :                     ReportError(
    2085             :                         CE_Failure, CPLE_IllegalArg,
    2086             :                         "Expected list of integer value for argument '%s', "
    2087             :                         "but got '%s'.",
    2088             :                         name.c_str(), value.c_str());
    2089           6 :                     return false;
    2090             :                 }
    2091             :             }
    2092          57 :             if (arg->GetMaxCount() == 1)
    2093             :             {
    2094           2 :                 bool ret = arg->Set(std::move(valueVector));
    2095           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2096           2 :                 return ret;
    2097             :             }
    2098             : 
    2099          55 :             break;
    2100             :         }
    2101             : 
    2102         109 :         case GAAT_REAL_LIST:
    2103             :         {
    2104             :             const CPLStringList aosTokens(
    2105         109 :                 arg->GetPackedValuesAllowed()
    2106         109 :                     ? CSLTokenizeString2(
    2107             :                           value.c_str(), ",",
    2108             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2109             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2110         218 :                     : CSLAddString(nullptr, value.c_str()));
    2111         109 :             if (!cpl::contains(inConstructionValues, arg))
    2112             :             {
    2113         107 :                 inConstructionValues[arg] = std::vector<double>();
    2114             :             }
    2115             :             auto &valueVector =
    2116         109 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    2117         442 :             for (const char *v : aosTokens)
    2118             :             {
    2119         337 :                 char *endptr = nullptr;
    2120         337 :                 double dfValue = CPLStrtod(v, &endptr);
    2121         337 :                 if (strlen(v) == 0 || endptr != v + strlen(v))
    2122             :                 {
    2123           4 :                     ReportError(
    2124             :                         CE_Failure, CPLE_IllegalArg,
    2125             :                         "Expected list of real value for argument '%s', "
    2126             :                         "but got '%s'.",
    2127             :                         name.c_str(), value.c_str());
    2128           4 :                     return false;
    2129             :                 }
    2130         333 :                 valueVector.push_back(dfValue);
    2131             :             }
    2132         105 :             if (arg->GetMaxCount() == 1)
    2133             :             {
    2134           2 :                 bool ret = arg->Set(std::move(valueVector));
    2135           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2136           2 :                 return ret;
    2137             :             }
    2138             : 
    2139         103 :             break;
    2140             :         }
    2141             : 
    2142         799 :         case GAAT_DATASET_LIST:
    2143             :         {
    2144         799 :             if (!cpl::contains(inConstructionValues, arg))
    2145             :             {
    2146         790 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    2147             :             }
    2148             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    2149         799 :                 inConstructionValues[arg]);
    2150         799 :             if (!value.empty() && value[0] == '{' && value.back() == '}')
    2151             :             {
    2152          12 :                 valueVector.push_back(GDALArgDatasetValue(value));
    2153             :             }
    2154             :             else
    2155             :             {
    2156             :                 const CPLStringList aosTokens(
    2157         787 :                     arg->GetPackedValuesAllowed()
    2158           6 :                         ? CSLTokenizeString2(value.c_str(), ",",
    2159             :                                              CSLT_HONOURSTRINGS |
    2160             :                                                  CSLT_STRIPLEADSPACES)
    2161        1580 :                         : CSLAddString(nullptr, value.c_str()));
    2162        1577 :                 for (const char *v : aosTokens)
    2163             :                 {
    2164         790 :                     valueVector.push_back(GDALArgDatasetValue(v));
    2165             :                 }
    2166             :             }
    2167         799 :             if (arg->GetMaxCount() == 1)
    2168             :             {
    2169         689 :                 bool ret = arg->Set(std::move(valueVector));
    2170         689 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2171         689 :                 return ret;
    2172             :             }
    2173             : 
    2174         110 :             break;
    2175             :         }
    2176             :     }
    2177             : 
    2178         539 :     return true;
    2179             : }
    2180             : 
    2181             : /************************************************************************/
    2182             : /*                     FormatSuggestionsAsString()                      */
    2183             : /************************************************************************/
    2184             : 
    2185             : static std::string
    2186           6 : FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
    2187             :                           bool addDashDashPrefix)
    2188             : {
    2189           6 :     std::string ret;
    2190          14 :     for (auto [i, suggestion] : cpl::enumerate(suggestions))
    2191             :     {
    2192           8 :         if (i > 0)
    2193             :         {
    2194           2 :             ret += (i + 1 < suggestions.size()) ? ", " : " or ";
    2195             :         }
    2196           8 :         ret += '\'';
    2197           8 :         if (addDashDashPrefix)
    2198           6 :             ret += "--";
    2199           8 :         ret += suggestion;
    2200           8 :         ret += '\'';
    2201             :     }
    2202           6 :     return ret;
    2203             : }
    2204             : 
    2205             : /************************************************************************/
    2206             : /*              GDALAlgorithm::ParseCommandLineArguments()              */
    2207             : /************************************************************************/
    2208             : 
    2209        2375 : bool GDALAlgorithm::ParseCommandLineArguments(
    2210             :     const std::vector<std::string> &args)
    2211             : {
    2212        2375 :     if (m_parsedSubStringAlreadyCalled)
    2213             :     {
    2214           6 :         ReportError(CE_Failure, CPLE_AppDefined,
    2215             :                     "ParseCommandLineArguments() can only be called once per "
    2216             :                     "instance.");
    2217           6 :         return false;
    2218             :     }
    2219        2369 :     m_parsedSubStringAlreadyCalled = true;
    2220             : 
    2221             :     // AWS like syntax supported too (not advertized)
    2222        2369 :     if (args.size() == 1 && args[0] == "help")
    2223             :     {
    2224           1 :         auto arg = GetArg("help");
    2225           1 :         assert(arg);
    2226           1 :         arg->Set(true);
    2227           1 :         arg->RunActions();
    2228           1 :         return true;
    2229             :     }
    2230             : 
    2231        2368 :     if (HasSubAlgorithms())
    2232             :     {
    2233         495 :         if (args.empty())
    2234             :         {
    2235           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    2236           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    2237           2 :             return false;
    2238             :         }
    2239         493 :         if (!args[0].empty() && args[0][0] == '-')
    2240             :         {
    2241             :             // go on argument parsing
    2242             :         }
    2243             :         else
    2244             :         {
    2245         490 :             const auto nCounter = CPLGetErrorCounter();
    2246         490 :             m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
    2247         490 :             if (m_selectedSubAlgHolder)
    2248             :             {
    2249         487 :                 m_selectedSubAlg = m_selectedSubAlgHolder.get();
    2250         487 :                 m_selectedSubAlg->SetReferencePathForRelativePaths(
    2251         487 :                     m_referencePath);
    2252         487 :                 m_selectedSubAlg->m_executionForStreamOutput =
    2253         487 :                     m_executionForStreamOutput;
    2254         487 :                 m_selectedSubAlg->m_calledFromCommandLine =
    2255         487 :                     m_calledFromCommandLine;
    2256         487 :                 m_selectedSubAlg->m_skipValidationInParseCommandLine =
    2257         487 :                     m_skipValidationInParseCommandLine;
    2258         487 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    2259         974 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    2260         487 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    2261         487 :                 return bRet;
    2262             :             }
    2263             :             else
    2264             :             {
    2265           4 :                 if (!(CPLGetErrorCounter() == nCounter + 1 &&
    2266           1 :                       strstr(CPLGetLastErrorMsg(), "Do you mean")))
    2267             :                 {
    2268           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2269           2 :                                 "Unknown command: '%s'", args[0].c_str());
    2270             :                 }
    2271           3 :                 return false;
    2272             :             }
    2273             :         }
    2274             :     }
    2275             : 
    2276             :     std::map<
    2277             :         GDALAlgorithmArg *,
    2278             :         std::variant<std::vector<std::string>, std::vector<int>,
    2279             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    2280        3752 :         inConstructionValues;
    2281             : 
    2282        2245 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    2283             :     {
    2284        2210 :         for (auto &[arg, value] : inConstructionValues)
    2285             :         {
    2286         497 :             if (arg->GetType() == GAAT_STRING_LIST)
    2287             :             {
    2288         243 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    2289         243 :                         inConstructionValues[arg])))
    2290             :                 {
    2291          35 :                     return false;
    2292             :                 }
    2293             :             }
    2294         254 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    2295             :             {
    2296          52 :                 if (!arg->Set(
    2297          52 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    2298             :                 {
    2299           4 :                     return false;
    2300             :                 }
    2301             :             }
    2302         202 :             else if (arg->GetType() == GAAT_REAL_LIST)
    2303             :             {
    2304         101 :                 if (!arg->Set(std::get<std::vector<double>>(
    2305         101 :                         inConstructionValues[arg])))
    2306             :                 {
    2307          10 :                     return false;
    2308             :                 }
    2309             :             }
    2310         101 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    2311             :             {
    2312         101 :                 if (!arg->Set(
    2313             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    2314         101 :                             inConstructionValues[arg]))))
    2315             :                 {
    2316           2 :                     return false;
    2317             :                 }
    2318             :             }
    2319             :         }
    2320        1713 :         return true;
    2321        1876 :     };
    2322             : 
    2323        3752 :     std::vector<std::string> lArgs(args);
    2324        1876 :     bool helpValueRequested = false;
    2325        5352 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    2326             :     {
    2327        3589 :         const auto &strArg = lArgs[i];
    2328        3589 :         GDALAlgorithmArg *arg = nullptr;
    2329        3589 :         std::string name;
    2330        3589 :         std::string value;
    2331        3589 :         bool hasValue = false;
    2332        3589 :         if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
    2333           5 :             helpValueRequested = true;
    2334        3589 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    2335             :         {
    2336        2210 :             const auto equalPos = strArg.find('=');
    2337        4420 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    2338        2210 :                                                    : strArg;
    2339        2210 :             const std::string nameWithoutDash = name.substr(2);
    2340        2210 :             auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2341        2266 :             if (m_arbitraryLongNameArgsAllowed &&
    2342        2266 :                 iterArg == m_mapLongNameToArg.end())
    2343             :             {
    2344          17 :                 GetArg(nameWithoutDash);
    2345          17 :                 iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2346             :             }
    2347        2210 :             if (iterArg == m_mapLongNameToArg.end())
    2348             :             {
    2349             :                 const auto suggestions =
    2350          28 :                     GetSuggestionsForArgumentName(nameWithoutDash);
    2351          28 :                 if (!suggestions.empty())
    2352             :                 {
    2353           3 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2354             :                                 "Option '%s' is unknown. Do you mean %s?",
    2355             :                                 name.c_str(),
    2356           6 :                                 FormatSuggestionsAsString(
    2357             :                                     suggestions, /* addDashDashPrefix = */ true)
    2358             :                                     .c_str());
    2359             :                 }
    2360             :                 else
    2361             :                 {
    2362          25 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2363             :                                 "Option '%s' is unknown.", name.c_str());
    2364             :                 }
    2365          28 :                 return false;
    2366             :             }
    2367        2182 :             arg = iterArg->second;
    2368        2182 :             if (equalPos != std::string::npos)
    2369             :             {
    2370         472 :                 hasValue = true;
    2371         472 :                 value = strArg.substr(equalPos + 1);
    2372             :             }
    2373             :         }
    2374        1458 :         else if (strArg.size() >= 2 && strArg[0] == '-' &&
    2375          79 :                  CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
    2376             :         {
    2377         153 :             for (size_t j = 1; j < strArg.size(); ++j)
    2378             :             {
    2379          79 :                 name.clear();
    2380          79 :                 name += strArg[j];
    2381          79 :                 const auto iterArg = m_mapShortNameToArg.find(name);
    2382          79 :                 if (iterArg == m_mapShortNameToArg.end())
    2383             :                 {
    2384           5 :                     const std::string nameWithoutDash = strArg.substr(1);
    2385           5 :                     if (m_mapLongNameToArg.find(nameWithoutDash) !=
    2386          10 :                         m_mapLongNameToArg.end())
    2387             :                     {
    2388           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2389             :                                     "Short name option '%s' is unknown. Do you "
    2390             :                                     "mean '--%s' (with leading double dash) ?",
    2391             :                                     name.c_str(), nameWithoutDash.c_str());
    2392             :                     }
    2393             :                     else
    2394             :                     {
    2395             :                         const auto suggestions =
    2396           8 :                             GetSuggestionsForArgumentName(nameWithoutDash);
    2397           4 :                         if (!suggestions.empty())
    2398             :                         {
    2399           1 :                             ReportError(
    2400             :                                 CE_Failure, CPLE_IllegalArg,
    2401             :                                 "Short name option '%s' is unknown. Do you "
    2402             :                                 "mean %s (with leading double dash) ?",
    2403             :                                 name.c_str(),
    2404           2 :                                 FormatSuggestionsAsString(
    2405             :                                     suggestions, /* addDashDashPrefix = */ true)
    2406             :                                     .c_str());
    2407             :                         }
    2408             :                         else
    2409             :                         {
    2410           3 :                             ReportError(CE_Failure, CPLE_IllegalArg,
    2411             :                                         "Short name option '%s' is unknown.",
    2412             :                                         name.c_str());
    2413             :                         }
    2414             :                     }
    2415           5 :                     return false;
    2416             :                 }
    2417          74 :                 arg = iterArg->second;
    2418          74 :                 if (strArg.size() > 2)
    2419             :                 {
    2420           0 :                     if (arg->GetType() != GAAT_BOOLEAN)
    2421             :                     {
    2422           0 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2423             :                                     "Invalid argument '%s'. Option '%s' is not "
    2424             :                                     "a boolean option.",
    2425             :                                     strArg.c_str(), name.c_str());
    2426           0 :                         return false;
    2427             :                     }
    2428             : 
    2429           0 :                     if (!ParseArgument(arg, name, "true", inConstructionValues))
    2430           0 :                         return false;
    2431             :                 }
    2432             :             }
    2433          74 :             if (strArg.size() > 2)
    2434             :             {
    2435           0 :                 lArgs.erase(lArgs.begin() + i);
    2436           0 :                 continue;
    2437             :             }
    2438             :         }
    2439             :         else
    2440             :         {
    2441        1300 :             ++i;
    2442        1300 :             continue;
    2443             :         }
    2444        2256 :         CPLAssert(arg);
    2445             : 
    2446        2256 :         if (arg && arg->GetType() == GAAT_BOOLEAN)
    2447             :         {
    2448         337 :             if (!hasValue)
    2449             :             {
    2450         334 :                 hasValue = true;
    2451         334 :                 value = "true";
    2452             :             }
    2453             :         }
    2454             : 
    2455        2256 :         lArgs.erase(lArgs.begin() + i);
    2456             : 
    2457        2256 :         if (!hasValue)
    2458             :         {
    2459        1450 :             if (i == lArgs.size())
    2460             :             {
    2461          41 :                 if (m_parseForAutoCompletion)
    2462             :                 {
    2463          35 :                     break;
    2464             :                 }
    2465           6 :                 ReportError(
    2466             :                     CE_Failure, CPLE_IllegalArg,
    2467             :                     "Expected value for argument '%s', but ran short of tokens",
    2468             :                     name.c_str());
    2469           6 :                 return false;
    2470             :             }
    2471        1409 :             value = lArgs[i];
    2472        1409 :             lArgs.erase(lArgs.begin() + i);
    2473             :         }
    2474             : 
    2475        2215 :         if (arg && !ParseArgument(arg, name, value, inConstructionValues))
    2476             :         {
    2477          39 :             return false;
    2478             :         }
    2479             : 
    2480             :         // Consume next strings if it is a positional argument, until finding
    2481             :         // a value starting with dash.
    2482        2557 :         if (!hasValue && arg && GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2483         381 :             std::find(m_positionalArgs.begin(), m_positionalArgs.end(), arg) !=
    2484        2557 :                 m_positionalArgs.end())
    2485             :         {
    2486         113 :             int countVals = 1;
    2487         114 :             while (i < lArgs.size() && !lArgs[i].empty() && lArgs[i][0] != '-')
    2488             :             {
    2489           5 :                 if (countVals == arg->GetMaxCount())
    2490           4 :                     break;
    2491           1 :                 if (!ParseArgument(arg, name, lArgs[i], inConstructionValues))
    2492             :                 {
    2493           0 :                     ProcessInConstructionValues();
    2494           0 :                     return false;
    2495             :                 }
    2496           1 :                 lArgs.erase(lArgs.begin() + i);
    2497           1 :                 ++countVals;
    2498             :             }
    2499             :         }
    2500             :     }
    2501             : 
    2502        1798 :     if (m_specialActionRequested)
    2503             :     {
    2504          26 :         return true;
    2505             :     }
    2506             : 
    2507             :     // Process positional arguments that have not been set through their
    2508             :     // option name.
    2509        1772 :     size_t i = 0;
    2510        1772 :     size_t iCurPosArg = 0;
    2511             : 
    2512             :     // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
    2513        1804 :     if (m_positionalArgs.size() == 3 &&
    2514          33 :         (m_positionalArgs[0]->IsRequired() ||
    2515          32 :          m_positionalArgs[0]->GetMinCount() == 1) &&
    2516          62 :         m_positionalArgs[0]->GetMaxCount() == 1 &&
    2517          38 :         (m_positionalArgs[1]->IsRequired() ||
    2518          38 :          m_positionalArgs[1]->GetMinCount() == 1) &&
    2519             :         /* Second argument may have several occurrences */
    2520          62 :         m_positionalArgs[1]->GetMaxCount() >= 1 &&
    2521          49 :         (m_positionalArgs[2]->IsRequired() ||
    2522          31 :          m_positionalArgs[2]->GetMinCount() == 1) &&
    2523          13 :         m_positionalArgs[2]->GetMaxCount() == 1 &&
    2524           9 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2525        1813 :         !m_positionalArgs[1]->IsExplicitlySet() &&
    2526           9 :         !m_positionalArgs[2]->IsExplicitlySet())
    2527             :     {
    2528           7 :         if (lArgs.size() - i < 3)
    2529             :         {
    2530           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    2531             :                         "Not enough positional values.");
    2532           1 :             return false;
    2533             :         }
    2534          12 :         bool ok = ParseArgument(m_positionalArgs[0],
    2535           6 :                                 m_positionalArgs[0]->GetName().c_str(),
    2536           6 :                                 lArgs[i], inConstructionValues);
    2537           6 :         if (ok)
    2538             :         {
    2539           5 :             ++i;
    2540          11 :             for (; i + 1 < lArgs.size() && ok; ++i)
    2541             :             {
    2542          12 :                 ok = ParseArgument(m_positionalArgs[1],
    2543           6 :                                    m_positionalArgs[1]->GetName().c_str(),
    2544           6 :                                    lArgs[i], inConstructionValues);
    2545             :             }
    2546             :         }
    2547           6 :         if (ok)
    2548             :         {
    2549          10 :             ok = ParseArgument(m_positionalArgs[2],
    2550          10 :                                m_positionalArgs[2]->GetName().c_str(), lArgs[i],
    2551             :                                inConstructionValues);
    2552           5 :             ++i;
    2553             :         }
    2554           6 :         if (!ok)
    2555             :         {
    2556           3 :             ProcessInConstructionValues();
    2557           3 :             return false;
    2558             :         }
    2559             :     }
    2560             : 
    2561         592 :     if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
    2562         660 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2563        2694 :         m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
    2564          78 :         (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
    2565          39 :          m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
    2566             :     {
    2567          39 :         ++iCurPosArg;
    2568             :     }
    2569             : 
    2570        2987 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    2571             :     {
    2572        1226 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    2573        1237 :         while (arg->IsExplicitlySet())
    2574             :         {
    2575          12 :             ++iCurPosArg;
    2576          12 :             if (iCurPosArg == m_positionalArgs.size())
    2577           1 :                 break;
    2578          11 :             arg = m_positionalArgs[iCurPosArg];
    2579             :         }
    2580        1226 :         if (iCurPosArg == m_positionalArgs.size())
    2581             :         {
    2582           1 :             break;
    2583             :         }
    2584        1914 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2585         689 :             arg->GetMinCount() != arg->GetMaxCount())
    2586             :         {
    2587         103 :             if (iCurPosArg == 0)
    2588             :             {
    2589          81 :                 size_t nCountAtEnd = 0;
    2590         110 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    2591             :                 {
    2592          31 :                     const auto *otherArg = m_positionalArgs[j];
    2593          31 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    2594             :                     {
    2595           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    2596             :                         {
    2597           2 :                             ReportError(
    2598             :                                 CE_Failure, CPLE_AppDefined,
    2599             :                                 "Ambiguity in definition of positional "
    2600             :                                 "argument "
    2601             :                                 "'%s' given it has a varying number of values, "
    2602             :                                 "but follows argument '%s' which also has a "
    2603             :                                 "varying number of values",
    2604           1 :                                 otherArg->GetName().c_str(),
    2605           1 :                                 arg->GetName().c_str());
    2606           1 :                             ProcessInConstructionValues();
    2607           1 :                             return false;
    2608             :                         }
    2609           3 :                         nCountAtEnd += otherArg->GetMinCount();
    2610             :                     }
    2611             :                     else
    2612             :                     {
    2613          27 :                         if (!otherArg->IsRequired())
    2614             :                         {
    2615           2 :                             ReportError(
    2616             :                                 CE_Failure, CPLE_AppDefined,
    2617             :                                 "Ambiguity in definition of positional "
    2618             :                                 "argument "
    2619             :                                 "'%s', given it is not required but follows "
    2620             :                                 "argument '%s' which has a varying number of "
    2621             :                                 "values",
    2622           1 :                                 otherArg->GetName().c_str(),
    2623           1 :                                 arg->GetName().c_str());
    2624           1 :                             ProcessInConstructionValues();
    2625           1 :                             return false;
    2626             :                         }
    2627          26 :                         nCountAtEnd++;
    2628             :                     }
    2629             :                 }
    2630          79 :                 if (lArgs.size() < nCountAtEnd)
    2631             :                 {
    2632           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2633             :                                 "Not enough positional values.");
    2634           1 :                     ProcessInConstructionValues();
    2635           1 :                     return false;
    2636             :                 }
    2637         165 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    2638             :                 {
    2639          87 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2640             :                                        inConstructionValues))
    2641             :                     {
    2642           0 :                         ProcessInConstructionValues();
    2643           0 :                         return false;
    2644             :                     }
    2645             :                 }
    2646             :             }
    2647          22 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    2648             :             {
    2649          49 :                 for (; i < lArgs.size(); ++i)
    2650             :                 {
    2651          28 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2652             :                                        inConstructionValues))
    2653             :                     {
    2654           0 :                         ProcessInConstructionValues();
    2655           0 :                         return false;
    2656             :                     }
    2657             :                 }
    2658             :             }
    2659             :             else
    2660             :             {
    2661           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2662             :                             "Ambiguity in definition of positional arguments: "
    2663             :                             "arguments with varying number of values must be "
    2664             :                             "first or last one.");
    2665           1 :                 return false;
    2666             :             }
    2667             :         }
    2668             :         else
    2669             :         {
    2670        1122 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    2671             :             {
    2672           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2673             :                             "Not enough positional values.");
    2674           1 :                 return false;
    2675             :             }
    2676        1121 :             const size_t iMax = i + arg->GetMaxCount();
    2677        2245 :             for (; i < iMax; ++i)
    2678             :             {
    2679        1125 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2680             :                                    inConstructionValues))
    2681             :                 {
    2682           1 :                     ProcessInConstructionValues();
    2683           1 :                     return false;
    2684             :                 }
    2685             :             }
    2686             :         }
    2687        1219 :         ++iCurPosArg;
    2688             :     }
    2689             : 
    2690        1762 :     if (i < lArgs.size())
    2691             :     {
    2692          21 :         ReportError(CE_Failure, CPLE_AppDefined,
    2693             :                     "Positional values starting at '%s' are not expected.",
    2694          21 :                     lArgs[i].c_str());
    2695          21 :         return false;
    2696             :     }
    2697             : 
    2698        1741 :     if (!ProcessInConstructionValues())
    2699             :     {
    2700          34 :         return false;
    2701             :     }
    2702             : 
    2703             :     // Skip to first unset positional argument.
    2704        2807 :     while (iCurPosArg < m_positionalArgs.size() &&
    2705         604 :            m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    2706             :     {
    2707         496 :         ++iCurPosArg;
    2708             :     }
    2709             :     // Check if this positional argument is required.
    2710        1814 :     if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
    2711         107 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    2712          59 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    2713          48 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    2714             :     {
    2715          87 :         ReportError(CE_Failure, CPLE_AppDefined,
    2716             :                     "Positional arguments starting at '%s' have not been "
    2717             :                     "specified.",
    2718          87 :                     m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    2719          87 :         return false;
    2720             :     }
    2721             : 
    2722        1620 :     if (m_calledFromCommandLine)
    2723             :     {
    2724        5522 :         for (auto &arg : m_args)
    2725             :         {
    2726        7138 :             if (arg->IsExplicitlySet() &&
    2727        1105 :                 ((arg->GetType() == GAAT_STRING &&
    2728        1102 :                   arg->Get<std::string>() == "?") ||
    2729         999 :                  (arg->GetType() == GAAT_STRING_LIST &&
    2730         157 :                   arg->Get<std::vector<std::string>>().size() == 1 &&
    2731          78 :                   arg->Get<std::vector<std::string>>()[0] == "?")))
    2732             :             {
    2733             :                 {
    2734          10 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2735           5 :                     ValidateArguments();
    2736             :                 }
    2737             : 
    2738           5 :                 auto choices = arg->GetChoices();
    2739           5 :                 if (choices.empty())
    2740           2 :                     choices = arg->GetAutoCompleteChoices(std::string());
    2741           5 :                 if (!choices.empty())
    2742             :                 {
    2743           5 :                     if (choices.size() == 1)
    2744             :                     {
    2745           4 :                         ReportError(
    2746             :                             CE_Failure, CPLE_AppDefined,
    2747             :                             "Single potential value for argument '%s' is '%s'",
    2748           4 :                             arg->GetName().c_str(), choices.front().c_str());
    2749             :                     }
    2750             :                     else
    2751             :                     {
    2752           6 :                         std::string msg("Potential values for argument '");
    2753           3 :                         msg += arg->GetName();
    2754           3 :                         msg += "' are:";
    2755          45 :                         for (const auto &v : choices)
    2756             :                         {
    2757          42 :                             msg += "\n- ";
    2758          42 :                             msg += v;
    2759             :                         }
    2760           3 :                         ReportError(CE_Failure, CPLE_AppDefined, "%s",
    2761             :                                     msg.c_str());
    2762             :                     }
    2763           5 :                     return false;
    2764             :                 }
    2765             :             }
    2766             :         }
    2767             :     }
    2768             : 
    2769        1615 :     return m_skipValidationInParseCommandLine || ValidateArguments();
    2770             : }
    2771             : 
    2772             : /************************************************************************/
    2773             : /*                     GDALAlgorithm::ReportError()                     */
    2774             : /************************************************************************/
    2775             : 
    2776             : //! @cond Doxygen_Suppress
    2777         993 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    2778             :                                 const char *fmt, ...) const
    2779             : {
    2780             :     va_list args;
    2781         993 :     va_start(args, fmt);
    2782         993 :     CPLError(eErrClass, err_no, "%s",
    2783         993 :              std::string(m_name)
    2784         993 :                  .append(": ")
    2785        1986 :                  .append(CPLString().vPrintf(fmt, args))
    2786             :                  .c_str());
    2787         993 :     va_end(args);
    2788         993 : }
    2789             : 
    2790             : //! @endcond
    2791             : 
    2792             : /************************************************************************/
    2793             : /*                  GDALAlgorithm::ProcessDatasetArg()                  */
    2794             : /************************************************************************/
    2795             : 
    2796       11707 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    2797             :                                       GDALAlgorithm *algForOutput)
    2798             : {
    2799       11707 :     bool ret = true;
    2800             : 
    2801       11707 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    2802       11707 :     const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
    2803       11707 :     const bool update = hasUpdateArg && updateArg->Get<bool>();
    2804             : 
    2805       11707 :     const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
    2806       11707 :     const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
    2807       11707 :     const bool append = hasAppendArg && appendArg->Get<bool>();
    2808             : 
    2809       11707 :     const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
    2810             :     const bool overwrite =
    2811       19146 :         (arg->IsOutput() && overwriteArg &&
    2812       19146 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    2813             : 
    2814       11707 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    2815       23414 :     auto &val = [arg]() -> GDALArgDatasetValue &
    2816             :     {
    2817       11707 :         if (arg->GetType() == GAAT_DATASET_LIST)
    2818        6891 :             return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
    2819             :         else
    2820        4816 :             return arg->Get<GDALArgDatasetValue>();
    2821       11707 :     }();
    2822             :     const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
    2823       18618 :         arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
    2824       18626 :         !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
    2825           8 :         !overwrite;
    2826             : 
    2827             :     // Used for nested pipelines
    2828             :     const auto oIterDatasetNameToDataset =
    2829       23411 :         val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
    2830       11707 :                         : m_oMapDatasetNameToDataset.end();
    2831             : 
    2832       11707 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    2833             :     {
    2834           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    2835             :                     "Argument '%s' has no dataset object or dataset name.",
    2836           3 :                     arg->GetName().c_str());
    2837           3 :         ret = false;
    2838             :     }
    2839       11704 :     else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
    2840             :     {
    2841           3 :         return false;
    2842             :     }
    2843         319 :     else if (m_inputDatasetCanBeOmitted &&
    2844       12020 :              val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
    2845          17 :              !arg->IsOutput())
    2846             :     {
    2847          17 :         return true;
    2848             :     }
    2849       17200 :     else if (!val.GetDatasetRef() &&
    2850        5838 :              (arg->AutoOpenDataset() ||
    2851       17522 :               oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
    2852        5195 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
    2853             :               onlyInputSpecifiedInUpdateAndOutputNotRequired))
    2854             :     {
    2855        1599 :         int flags = arg->GetDatasetType();
    2856        1599 :         bool assignToOutputArg = false;
    2857             : 
    2858             :         // Check if input and output parameters point to the same
    2859             :         // filename (for vector datasets)
    2860        2953 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    2861        2953 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    2862             :         {
    2863          62 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    2864         121 :             if (!outputVal.GetDatasetRef() &&
    2865         121 :                 outputVal.GetName() == val.GetName() &&
    2866           2 :                 (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
    2867             :             {
    2868           2 :                 assignToOutputArg = true;
    2869           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2870             :             }
    2871          60 :             else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
    2872             :             {
    2873           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2874             :             }
    2875             :         }
    2876             : 
    2877        1599 :         if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
    2878        1516 :             flags |= GDAL_OF_VERBOSE_ERROR;
    2879        1599 :         if ((arg == outputArg || !outputArg) && update)
    2880             :         {
    2881          85 :             flags |= GDAL_OF_UPDATE;
    2882          85 :             if (!append)
    2883          64 :                 flags |= GDAL_OF_VERBOSE_ERROR;
    2884             :         }
    2885             : 
    2886        1599 :         const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
    2887             :         const bool readOnly =
    2888        1643 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    2889          44 :              readOnlyArg->Get<bool>());
    2890        1599 :         if (readOnly)
    2891          12 :             flags &= ~GDAL_OF_UPDATE;
    2892             : 
    2893        3198 :         CPLStringList aosOpenOptions;
    2894        3198 :         CPLStringList aosAllowedDrivers;
    2895        1599 :         if (arg->IsInput())
    2896             :         {
    2897        1599 :             if (arg == outputArg)
    2898             :             {
    2899          83 :                 if (update && !overwrite)
    2900             :                 {
    2901          83 :                     const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
    2902          83 :                     if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2903          46 :                         aosOpenOptions = CPLStringList(
    2904          46 :                             ooArg->Get<std::vector<std::string>>());
    2905             :                 }
    2906             :             }
    2907             :             else
    2908             :             {
    2909        1516 :                 const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    2910        1516 :                 if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2911             :                     aosOpenOptions =
    2912        1436 :                         CPLStringList(ooArg->Get<std::vector<std::string>>());
    2913             : 
    2914        1516 :                 const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2915        1516 :                 if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    2916             :                     aosAllowedDrivers =
    2917        1392 :                         CPLStringList(ifArg->Get<std::vector<std::string>>());
    2918             :             }
    2919             :         }
    2920             : 
    2921        3198 :         std::string osDatasetName = val.GetName();
    2922        1599 :         if (!m_referencePath.empty())
    2923             :         {
    2924          46 :             osDatasetName = GDALDataset::BuildFilename(
    2925          23 :                 osDatasetName.c_str(), m_referencePath.c_str(), true);
    2926             :         }
    2927        1599 :         if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
    2928           0 :             osDatasetName = "/vsistdin/";
    2929             : 
    2930             :         // Handle special case of overview delete in GTiff which would fail
    2931             :         // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
    2932         145 :         if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
    2933        1746 :             m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
    2934           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
    2935             :         {
    2936           4 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2937             :             GDALDriverH hDrv =
    2938           2 :                 GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
    2939           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
    2940             :             {
    2941             :                 // Cleaning does not break COG layout
    2942           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
    2943             :             }
    2944             :         }
    2945             : 
    2946             :         GDALDataset *poDS;
    2947        3198 :         CPLErrorAccumulator oAccumulator;
    2948             :         {
    2949        3198 :             auto oContext = oAccumulator.InstallForCurrentScope();
    2950             : 
    2951        1599 :             poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
    2952        1599 :                        ? oIterDatasetNameToDataset->second
    2953        1583 :                        : GDALDataset::Open(osDatasetName.c_str(), flags,
    2954        1583 :                                            aosAllowedDrivers.List(),
    2955        1583 :                                            aosOpenOptions.List());
    2956             : 
    2957          71 :             if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
    2958        1670 :                 !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
    2959             :             {
    2960          41 :                 auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
    2961          82 :                     osDatasetName.c_str(), nullptr);
    2962          41 :                 if (eErr == OGRERR_NONE)
    2963             :                 {
    2964          12 :                     auto poMemDS = std::make_unique<MEMDataset>();
    2965          12 :                     auto *poLayer = poMemDS->CreateLayer(
    2966             :                         "layer", poWktGeom->getSpatialReference(),
    2967           6 :                         poWktGeom->getGeometryType());
    2968             : 
    2969           6 :                     auto poFeatureDefn = poLayer->GetLayerDefn();
    2970          12 :                     OGRFeature oFeature(poFeatureDefn);
    2971             : 
    2972           6 :                     oFeature.SetGeometry(std::move(poWktGeom));
    2973           6 :                     if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
    2974             :                     {
    2975           6 :                         poDS = poMemDS.release();
    2976           6 :                         oAccumulator.ClearErrors();
    2977             :                     }
    2978             :                 }
    2979             :             }
    2980             : 
    2981             :             // Retry with PostGIS vector driver
    2982          65 :             if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
    2983          63 :                 cpl::starts_with(osDatasetName, "PG:") &&
    2984           0 :                 GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
    2985        1664 :                 aosAllowedDrivers.empty() && aosOpenOptions.empty())
    2986             :             {
    2987           0 :                 oAccumulator.ClearErrors();
    2988           0 :                 poDS = GDALDataset::Open(
    2989           0 :                     osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
    2990           0 :                     aosAllowedDrivers.List(), aosOpenOptions.List());
    2991             :             }
    2992             :         }
    2993        1599 :         oAccumulator.ReplayErrors();
    2994             : 
    2995        1599 :         if (poDS)
    2996             :         {
    2997        1534 :             if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
    2998             :             {
    2999          16 :                 if (arg->GetType() == GAAT_DATASET)
    3000           8 :                     arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
    3001          16 :                 poDS->Reference();
    3002          16 :                 m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
    3003             :             }
    3004             : 
    3005             :             // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
    3006             :             // where the PG: dataset will be first opened with the PostGISRaster
    3007             :             // driver whereas the PostgreSQL (vector) one is actually wanted.
    3008        2156 :             if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
    3009        2271 :                 (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
    3010         115 :                 aosOpenOptions.empty())
    3011             :             {
    3012         111 :                 auto poDrv = poDS->GetDriver();
    3013         111 :                 if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
    3014             :                 {
    3015             :                     // Retry with PostgreSQL (vector) driver
    3016             :                     std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
    3017           0 :                         osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
    3018           0 :                     if (poTmpDS)
    3019             :                     {
    3020           0 :                         poDS->ReleaseRef();
    3021           0 :                         poDS = poTmpDS.release();
    3022             :                     }
    3023             :                 }
    3024             :             }
    3025             : 
    3026        1534 :             if (assignToOutputArg)
    3027             :             {
    3028             :                 // Avoid opening twice the same datasource if it is both
    3029             :                 // the input and output.
    3030             :                 // Known to cause problems with at least FGdb, SQLite
    3031             :                 // and GPKG drivers. See #4270
    3032             :                 // Restrict to those 3 drivers. For example it is known
    3033             :                 // to break with the PG driver due to the way it
    3034             :                 // manages transactions.
    3035           2 :                 auto poDriver = poDS->GetDriver();
    3036           4 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    3037           2 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    3038           2 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    3039             :                 {
    3040           2 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    3041             :                 }
    3042             :             }
    3043        1534 :             val.SetDatasetOpenedByAlgorithm();
    3044        1534 :             val.Set(poDS);
    3045        1534 :             poDS->ReleaseRef();
    3046             :         }
    3047          65 :         else if (!append)
    3048             :         {
    3049          63 :             ret = false;
    3050             :         }
    3051             :     }
    3052             : 
    3053             :     // Deal with overwriting the output dataset
    3054       11687 :     if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
    3055             :     {
    3056        3598 :         if (!append)
    3057             :         {
    3058             :             // If outputting to MEM, do not try to erase a real file of the same name!
    3059             :             const auto outputFormatArg =
    3060        3586 :                 algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3061       10720 :             if (!(outputFormatArg &&
    3062        3567 :                   outputFormatArg->GetType() == GAAT_STRING &&
    3063        3567 :                   (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3064        2311 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3065        1305 :                          "stream") ||
    3066        1305 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3067             :                          "Memory"))))
    3068             :             {
    3069        1324 :                 const char *pszType = "";
    3070        1324 :                 GDALDriver *poDriver = nullptr;
    3071        2602 :                 if (!val.GetName().empty() &&
    3072        1278 :                     GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
    3073             :                                                &poDriver))
    3074             :                 {
    3075          79 :                     if (!overwrite)
    3076             :                     {
    3077          68 :                         std::string options;
    3078          34 :                         if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
    3079             :                         {
    3080          11 :                             options += "--";
    3081          11 :                             options += GDAL_ARG_NAME_OVERWRITE_LAYER;
    3082             :                         }
    3083          34 :                         if (hasAppendArg)
    3084             :                         {
    3085          22 :                             if (!options.empty())
    3086           8 :                                 options += '/';
    3087          22 :                             options += "--";
    3088          22 :                             options += GDAL_ARG_NAME_APPEND;
    3089             :                         }
    3090          34 :                         if (hasUpdateArg)
    3091             :                         {
    3092          15 :                             if (!options.empty())
    3093          12 :                                 options += '/';
    3094          15 :                             options += "--";
    3095          15 :                             options += GDAL_ARG_NAME_UPDATE;
    3096             :                         }
    3097             : 
    3098          34 :                         if (poDriver)
    3099             :                         {
    3100          68 :                             const char *pszPrefix = poDriver->GetMetadataItem(
    3101          34 :                                 GDAL_DMD_CONNECTION_PREFIX);
    3102          34 :                             if (pszPrefix &&
    3103           0 :                                 STARTS_WITH_CI(val.GetName().c_str(),
    3104             :                                                pszPrefix))
    3105             :                             {
    3106           0 :                                 bool bExists = false;
    3107             :                                 {
    3108             :                                     CPLErrorStateBackuper oBackuper(
    3109           0 :                                         CPLQuietErrorHandler);
    3110           0 :                                     bExists = std::unique_ptr<GDALDataset>(
    3111             :                                                   GDALDataset::Open(
    3112           0 :                                                       val.GetName().c_str())) !=
    3113             :                                               nullptr;
    3114             :                                 }
    3115           0 :                                 if (bExists)
    3116             :                                 {
    3117           0 :                                     if (!options.empty())
    3118           0 :                                         options = " You may specify the " +
    3119           0 :                                                   options + " option.";
    3120           0 :                                     ReportError(CE_Failure, CPLE_AppDefined,
    3121             :                                                 "%s '%s' already exists.%s",
    3122           0 :                                                 pszType, val.GetName().c_str(),
    3123             :                                                 options.c_str());
    3124           0 :                                     return false;
    3125             :                                 }
    3126             : 
    3127           0 :                                 return true;
    3128             :                             }
    3129             :                         }
    3130             : 
    3131          34 :                         if (!options.empty())
    3132          28 :                             options = '/' + options;
    3133          68 :                         ReportError(
    3134             :                             CE_Failure, CPLE_AppDefined,
    3135             :                             "%s '%s' already exists. You may specify the "
    3136             :                             "--overwrite%s option.",
    3137          34 :                             pszType, val.GetName().c_str(), options.c_str());
    3138          34 :                         return false;
    3139             :                     }
    3140          45 :                     else if (EQUAL(pszType, "File"))
    3141             :                     {
    3142           1 :                         if (VSIUnlink(val.GetName().c_str()) != 0)
    3143             :                         {
    3144           0 :                             ReportError(CE_Failure, CPLE_AppDefined,
    3145             :                                         "Deleting %s failed: %s",
    3146           0 :                                         val.GetName().c_str(),
    3147           0 :                                         VSIStrerror(errno));
    3148           0 :                             return false;
    3149             :                         }
    3150             :                     }
    3151          44 :                     else if (EQUAL(pszType, "Directory"))
    3152             :                     {
    3153             :                         // We don't want the user to accidentally erase a non-GDAL dataset
    3154           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3155             :                                     "Directory '%s' already exists, but is not "
    3156             :                                     "recognized as a valid GDAL dataset. "
    3157             :                                     "Please manually delete it before retrying",
    3158           1 :                                     val.GetName().c_str());
    3159           1 :                         return false;
    3160             :                     }
    3161          43 :                     else if (poDriver)
    3162             :                     {
    3163             :                         bool bDeleteOK;
    3164             :                         {
    3165             :                             CPLErrorStateBackuper oBackuper(
    3166          43 :                                 CPLQuietErrorHandler);
    3167          43 :                             bDeleteOK = (poDriver->Delete(
    3168          43 :                                              val.GetName().c_str()) == CE_None);
    3169             :                         }
    3170             :                         VSIStatBufL sStat;
    3171          46 :                         if (!bDeleteOK &&
    3172           3 :                             VSIStatL(val.GetName().c_str(), &sStat) == 0)
    3173             :                         {
    3174           3 :                             if (VSI_ISDIR(sStat.st_mode))
    3175             :                             {
    3176             :                                 // We don't want the user to accidentally erase a non-GDAL dataset
    3177           0 :                                 ReportError(
    3178             :                                     CE_Failure, CPLE_AppDefined,
    3179             :                                     "Directory '%s' already exists, but is not "
    3180             :                                     "recognized as a valid GDAL dataset. "
    3181             :                                     "Please manually delete it before retrying",
    3182           0 :                                     val.GetName().c_str());
    3183           2 :                                 return false;
    3184             :                             }
    3185           3 :                             else if (VSIUnlink(val.GetName().c_str()) != 0)
    3186             :                             {
    3187           2 :                                 ReportError(CE_Failure, CPLE_AppDefined,
    3188             :                                             "Deleting %s failed: %s",
    3189           2 :                                             val.GetName().c_str(),
    3190           2 :                                             VSIStrerror(errno));
    3191           2 :                                 return false;
    3192             :                             }
    3193             :                         }
    3194             :                     }
    3195             :                 }
    3196             :             }
    3197             :         }
    3198             :     }
    3199             : 
    3200             :     // If outputting to stdout, automatically turn off progress bar
    3201       11650 :     if (arg == outputArg && val.GetName() == "/vsistdout/")
    3202             :     {
    3203           8 :         auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
    3204           8 :         if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
    3205           5 :             quietArg->Set(true);
    3206             :     }
    3207             : 
    3208       11650 :     return ret;
    3209             : }
    3210             : 
    3211             : /************************************************************************/
    3212             : /*                  GDALAlgorithm::ValidateArguments()                  */
    3213             : /************************************************************************/
    3214             : 
    3215        8156 : bool GDALAlgorithm::ValidateArguments()
    3216             : {
    3217        8156 :     if (m_selectedSubAlg)
    3218           3 :         return m_selectedSubAlg->ValidateArguments();
    3219             : 
    3220        8153 :     if (m_specialActionRequested)
    3221           1 :         return true;
    3222             : 
    3223        8152 :     m_arbitraryLongNameArgsAllowed = false;
    3224             : 
    3225             :     // If only --output=format=MEM/stream is specified and not --output,
    3226             :     // then set empty name for --output.
    3227        8152 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    3228        8152 :     auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3229        4705 :     if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
    3230        3034 :         !outputArg->IsExplicitlySet() &&
    3231         418 :         outputFormatArg->GetType() == GAAT_STRING &&
    3232         418 :         (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3233         660 :          EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
    3234       13246 :         outputArg->GetType() == GAAT_DATASET &&
    3235         389 :         (outputArg->GetDatasetInputFlags() & GADV_NAME))
    3236             :     {
    3237         389 :         outputArg->Get<GDALArgDatasetValue>().Set("");
    3238             :     }
    3239             : 
    3240             :     // The method may emit several errors if several constraints are not met.
    3241        8152 :     bool ret = true;
    3242       16304 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    3243       16304 :     std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
    3244      150919 :     for (auto &arg : m_args)
    3245             :     {
    3246             :         // Check mutually exclusive/dependent arguments
    3247      142767 :         if (arg->IsExplicitlySet())
    3248             :         {
    3249             : 
    3250       22468 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3251       22468 :             if (!mutualExclusionGroup.empty())
    3252             :             {
    3253             :                 auto oIter =
    3254         992 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    3255         992 :                 if (oIter != mutualExclusionGroupUsed.end())
    3256             :                 {
    3257          13 :                     ret = false;
    3258          26 :                     ReportError(
    3259             :                         CE_Failure, CPLE_AppDefined,
    3260             :                         "Argument '%s' is mutually exclusive with '%s'.",
    3261          26 :                         arg->GetName().c_str(), oIter->second.c_str());
    3262             :                 }
    3263             :                 else
    3264             :                 {
    3265         979 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    3266        1958 :                         arg->GetName();
    3267             :                 }
    3268             :             }
    3269             : 
    3270       22468 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3271       22468 :             if (!mutualDependencyGroup.empty())
    3272             :             {
    3273          78 :                 if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
    3274         156 :                     mutualDependencyGroupUsed.end())
    3275             :                 {
    3276         129 :                     mutualDependencyGroupUsed[mutualDependencyGroup] = {
    3277         129 :                         arg->GetName()};
    3278             :                 }
    3279             :                 else
    3280             :                 {
    3281          70 :                     mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
    3282          35 :                         arg->GetName());
    3283             :                 }
    3284             :             }
    3285             : 
    3286             :             // Check direct dependencies
    3287       22480 :             for (const auto &dependency : arg->GetDirectDependencies())
    3288             :             {
    3289          12 :                 auto depArg = GetArg(dependency);
    3290          12 :                 if (!depArg)
    3291             :                 {
    3292           0 :                     ret = false;
    3293           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3294             :                                 "Argument '%s' depends on argument '%s' that "
    3295             :                                 "is not defined.",
    3296           0 :                                 arg->GetName().c_str(), dependency.c_str());
    3297             :                 }
    3298          12 :                 else if (!depArg->IsExplicitlySet())
    3299             :                 {
    3300           6 :                     ret = false;
    3301          12 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3302             :                                 "Argument '%s' depends on argument '%s' that "
    3303             :                                 "has not been specified.",
    3304           6 :                                 arg->GetName().c_str(),
    3305           6 :                                 depArg->GetName().c_str());
    3306             :                 }
    3307             :             }
    3308             :         }
    3309             : 
    3310      142939 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    3311         172 :             !arg->HasDefaultValue())
    3312             :         {
    3313         172 :             bool emitError = true;
    3314         172 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3315         172 :             if (!mutualExclusionGroup.empty())
    3316             :             {
    3317        1885 :                 for (const auto &otherArg : m_args)
    3318             :                 {
    3319        1859 :                     if (otherArg->GetMutualExclusionGroup() ==
    3320        1988 :                             mutualExclusionGroup &&
    3321         129 :                         otherArg->IsExplicitlySet())
    3322             :                     {
    3323          74 :                         emitError = false;
    3324          74 :                         break;
    3325             :                     }
    3326             :                 }
    3327             :             }
    3328         262 :             if (emitError && !(m_inputDatasetCanBeOmitted &&
    3329          55 :                                arg->GetName() == GDAL_ARG_NAME_INPUT &&
    3330          70 :                                (arg->GetType() == GAAT_DATASET ||
    3331          35 :                                 arg->GetType() == GAAT_DATASET_LIST)))
    3332             :             {
    3333          63 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3334             :                             "Required argument '%s' has not been specified.",
    3335          63 :                             arg->GetName().c_str());
    3336          63 :                 ret = false;
    3337             :             }
    3338             :         }
    3339      142595 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    3340             :         {
    3341        4816 :             if (!ProcessDatasetArg(arg.get(), this))
    3342          50 :                 ret = false;
    3343             :         }
    3344             : 
    3345      142767 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
    3346             :         {
    3347        6667 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    3348        6667 :             if (listVal.size() == 1)
    3349             :             {
    3350        6507 :                 if (!ProcessDatasetArg(arg.get(), this))
    3351          42 :                     ret = false;
    3352             :             }
    3353             :             else
    3354             :             {
    3355         491 :                 for (auto &val : listVal)
    3356             :                 {
    3357         331 :                     if (val.GetDatasetRef())
    3358             :                     {
    3359         120 :                         if (!CheckCanSetDatasetObject(arg.get()))
    3360             :                         {
    3361           0 :                             ret = false;
    3362             :                         }
    3363         327 :                         continue;
    3364             :                     }
    3365             : 
    3366         211 :                     if (val.GetName().empty())
    3367             :                     {
    3368           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3369             :                                     "Argument '%s' has no dataset object or "
    3370             :                                     "dataset name.",
    3371           0 :                                     arg->GetName().c_str());
    3372           0 :                         ret = false;
    3373           0 :                         continue;
    3374             :                     }
    3375             : 
    3376         211 :                     auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
    3377         211 :                     if (oIter != m_oMapDatasetNameToDataset.end())
    3378             :                     {
    3379           2 :                         auto poDS = oIter->second;
    3380           2 :                         val.SetDatasetOpenedByAlgorithm();
    3381           2 :                         val.Set(poDS);
    3382           2 :                         m_oMapDatasetNameToDataset.erase(oIter);
    3383           2 :                         continue;
    3384             :                     }
    3385             : 
    3386         209 :                     if (!arg->AutoOpenDataset())
    3387         205 :                         continue;
    3388             : 
    3389           4 :                     int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
    3390             : 
    3391           8 :                     CPLStringList aosOpenOptions;
    3392           8 :                     CPLStringList aosAllowedDrivers;
    3393           4 :                     if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    3394             :                     {
    3395           4 :                         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    3396           4 :                         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    3397             :                         {
    3398           4 :                             aosOpenOptions = CPLStringList(
    3399           4 :                                 ooArg->Get<std::vector<std::string>>());
    3400             :                         }
    3401             : 
    3402           4 :                         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    3403           4 :                         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    3404             :                         {
    3405           4 :                             aosAllowedDrivers = CPLStringList(
    3406           4 :                                 ifArg->Get<std::vector<std::string>>());
    3407             :                         }
    3408             : 
    3409           4 :                         const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3410           4 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    3411           0 :                             updateArg->Get<bool>())
    3412             :                         {
    3413           0 :                             flags |= GDAL_OF_UPDATE;
    3414             :                         }
    3415             :                     }
    3416             : 
    3417             :                     auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    3418           4 :                         val.GetName().c_str(), flags, aosAllowedDrivers.List(),
    3419          12 :                         aosOpenOptions.List()));
    3420           4 :                     if (poDS)
    3421             :                     {
    3422           3 :                         val.Set(std::move(poDS));
    3423             :                     }
    3424             :                     else
    3425             :                     {
    3426           1 :                         ret = false;
    3427             :                     }
    3428             :                 }
    3429             :             }
    3430             :         }
    3431             : 
    3432      142767 :         if (arg->IsExplicitlySet() && !arg->RunValidationActions())
    3433             :         {
    3434           8 :             ret = false;
    3435             :         }
    3436             :     }
    3437             : 
    3438             :     // Check mutual dependency groups
    3439        8152 :     std::vector<std::string> processedGroups;
    3440             :     // Loop through group map and check there are not required args in the group that are not set
    3441        8195 :     for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
    3442             :     {
    3443          43 :         if (std::find(processedGroups.begin(), processedGroups.end(),
    3444          43 :                       groupName) != processedGroups.end())
    3445           0 :             continue;
    3446          86 :         std::vector<std::string> missingArgs;
    3447         848 :         for (auto &arg : m_args)
    3448             :         {
    3449         805 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3450         898 :             if (mutualDependencyGroup == groupName &&
    3451          93 :                 std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
    3452         898 :                     argNames.end())
    3453             :             {
    3454          15 :                 missingArgs.push_back(arg->GetName());
    3455             :             }
    3456             :         }
    3457          43 :         if (!missingArgs.empty())
    3458             :         {
    3459          12 :             ret = false;
    3460          24 :             std::string missingArgsStr;
    3461          27 :             for (const auto &missingArg : missingArgs)
    3462             :             {
    3463          15 :                 if (!missingArgsStr.empty())
    3464           3 :                     missingArgsStr += ", ";
    3465          15 :                 missingArgsStr += missingArg;
    3466             :             }
    3467          24 :             std::string givenArgsStr;
    3468          27 :             for (const auto &givenArg : argNames)
    3469             :             {
    3470          15 :                 if (!givenArgsStr.empty())
    3471           3 :                     givenArgsStr += ", ";
    3472          15 :                 givenArgsStr += givenArg;
    3473             :             }
    3474          12 :             ReportError(CE_Failure, CPLE_AppDefined,
    3475             :                         "Argument(s) '%s' require(s) that the following "
    3476             :                         "argument(s) are also specified: %s.",
    3477             :                         givenArgsStr.c_str(), missingArgsStr.c_str());
    3478             :         }
    3479          43 :         processedGroups.push_back(groupName);
    3480             :     }
    3481             : 
    3482       34507 :     for (const auto &f : m_validationActions)
    3483             :     {
    3484       26355 :         if (!f())
    3485          82 :             ret = false;
    3486             :     }
    3487             : 
    3488        8152 :     return ret;
    3489             : }
    3490             : 
    3491             : /************************************************************************/
    3492             : /*                GDALAlgorithm::InstantiateSubAlgorithm                */
    3493             : /************************************************************************/
    3494             : 
    3495             : std::unique_ptr<GDALAlgorithm>
    3496       11150 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
    3497             :                                        bool suggestionAllowed) const
    3498             : {
    3499       11150 :     auto ret = m_subAlgRegistry.Instantiate(name);
    3500       22300 :     auto childCallPath = m_callPath;
    3501       11150 :     childCallPath.push_back(name);
    3502       11150 :     if (!ret)
    3503             :     {
    3504        1214 :         ret = GDALGlobalAlgorithmRegistry::GetSingleton()
    3505        1214 :                   .InstantiateDeclaredSubAlgorithm(childCallPath);
    3506             :     }
    3507       11150 :     if (ret)
    3508             :     {
    3509       10969 :         ret->SetCallPath(childCallPath);
    3510             :     }
    3511         181 :     else if (suggestionAllowed)
    3512             :     {
    3513          72 :         std::string bestCandidate;
    3514          36 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3515         533 :         for (const std::string &candidate : GetSubAlgorithmNames())
    3516             :         {
    3517             :             const size_t distance =
    3518         497 :                 CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
    3519             :                                        /* transpositionAllowed = */ true);
    3520         497 :             if (distance < bestDistance)
    3521             :             {
    3522          83 :                 bestCandidate = candidate;
    3523          83 :                 bestDistance = distance;
    3524             :             }
    3525         414 :             else if (distance == bestDistance)
    3526             :             {
    3527          51 :                 bestCandidate.clear();
    3528             :             }
    3529             :         }
    3530          36 :         if (!bestCandidate.empty() && bestDistance <= 2)
    3531             :         {
    3532           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3533             :                      "Algorithm '%s' is unknown. Do you mean '%s'?",
    3534             :                      name.c_str(), bestCandidate.c_str());
    3535             :         }
    3536             :     }
    3537       22300 :     return ret;
    3538             : }
    3539             : 
    3540             : /************************************************************************/
    3541             : /*            GDALAlgorithm::GetSuggestionForArgumentName()             */
    3542             : /************************************************************************/
    3543             : 
    3544             : std::string
    3545          39 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
    3546             : {
    3547          39 :     if (osName.size() >= 3)
    3548             :     {
    3549          34 :         std::string bestCandidate;
    3550          34 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3551         776 :         for (const auto &[key, value] : m_mapLongNameToArg)
    3552             :         {
    3553         742 :             CPL_IGNORE_RET_VAL(value);
    3554         742 :             const size_t distance = CPLLevenshteinDistance(
    3555             :                 osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
    3556         742 :             if (distance < bestDistance)
    3557             :             {
    3558          89 :                 bestCandidate = key;
    3559          89 :                 bestDistance = distance;
    3560             :             }
    3561         653 :             else if (distance == bestDistance)
    3562             :             {
    3563          78 :                 bestCandidate.clear();
    3564             :             }
    3565             :         }
    3566          48 :         if (!bestCandidate.empty() &&
    3567          14 :             bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
    3568             :         {
    3569           5 :             return bestCandidate;
    3570             :         }
    3571             :     }
    3572          34 :     return std::string();
    3573             : }
    3574             : 
    3575             : /************************************************************************/
    3576             : /*            GDALAlgorithm::GetSuggestionsForArgumentName()            */
    3577             : /************************************************************************/
    3578             : 
    3579             : std::vector<std::string>
    3580          39 : GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
    3581             : {
    3582          39 :     std::vector<std::string> ret;
    3583          78 :     std::string suggestion = GetSuggestionForArgumentName(osName);
    3584          39 :     if (!suggestion.empty())
    3585             :     {
    3586           5 :         ret.push_back(std::move(suggestion));
    3587             :     }
    3588          34 :     else if (osName.size() >= 3)
    3589             :     {
    3590             :         // e.g "crs" for reproject will match "input-crs" and "target-crs"
    3591          87 :         const std::string dashName = std::string("-").append(osName);
    3592         554 :         for (const auto &arg : m_args)
    3593             :         {
    3594         525 :             if (cpl::ends_with(arg->GetName(), dashName))
    3595             :             {
    3596           3 :                 ret.push_back(arg->GetName());
    3597             :             }
    3598             :         }
    3599             :     }
    3600          78 :     return ret;
    3601             : }
    3602             : 
    3603             : /************************************************************************/
    3604             : /*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
    3605             : /************************************************************************/
    3606             : 
    3607             : /* static */
    3608          23 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
    3609             : {
    3610          69 :     return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
    3611          69 :            osName == GDAL_ARG_NAME_OVERWRITE ||
    3612          46 :            osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
    3613             : }
    3614             : 
    3615             : /************************************************************************/
    3616             : /*                   GDALAlgorithm::HasOutputString()                   */
    3617             : /************************************************************************/
    3618             : 
    3619          74 : bool GDALAlgorithm::HasOutputString() const
    3620             : {
    3621          74 :     auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
    3622          74 :     return outputStringArg && outputStringArg->IsOutput();
    3623             : }
    3624             : 
    3625             : /************************************************************************/
    3626             : /*                       GDALAlgorithm::GetArg()                        */
    3627             : /************************************************************************/
    3628             : 
    3629      514683 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
    3630             :                                         bool suggestionAllowed, bool isConst)
    3631             : {
    3632      514683 :     const auto nPos = osName.find_first_not_of('-');
    3633      514683 :     if (nPos == std::string::npos)
    3634          27 :         return nullptr;
    3635     1029310 :     std::string osKey = osName.substr(nPos);
    3636             :     {
    3637      514656 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3638      514656 :         if (oIter != m_mapLongNameToArg.end())
    3639      474711 :             return oIter->second;
    3640             :     }
    3641             :     {
    3642       39945 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    3643       39945 :         if (oIter != m_mapShortNameToArg.end())
    3644           8 :             return oIter->second;
    3645             :     }
    3646             : 
    3647       39937 :     if (!isConst && m_arbitraryLongNameArgsAllowed)
    3648             :     {
    3649          23 :         const auto nDotPos = osKey.find('.');
    3650             :         const std::string osKeyEnd =
    3651          23 :             nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
    3652          23 :         if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
    3653             :         {
    3654             :             m_arbitraryLongNameArgsValuesBool.emplace_back(
    3655           0 :                 std::make_unique<bool>());
    3656           0 :             AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3657           0 :                    m_arbitraryLongNameArgsValuesBool.back().get())
    3658           0 :                 .SetUserProvided();
    3659             :         }
    3660             :         else
    3661             :         {
    3662          46 :             const std::string osKeyInit = osKey;
    3663          23 :             if (osKey == "oo")
    3664           0 :                 osKey = GDAL_ARG_NAME_OPEN_OPTION;
    3665          23 :             else if (osKey == "co")
    3666           0 :                 osKey = GDAL_ARG_NAME_CREATION_OPTION;
    3667          23 :             else if (osKey == "of")
    3668           0 :                 osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
    3669          23 :             else if (osKey == "if")
    3670           0 :                 osKey = GDAL_ARG_NAME_INPUT_FORMAT;
    3671             :             m_arbitraryLongNameArgsValuesStr.emplace_back(
    3672          23 :                 std::make_unique<std::string>());
    3673             :             auto &arg =
    3674          46 :                 AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3675          46 :                        m_arbitraryLongNameArgsValuesStr.back().get())
    3676          23 :                     .SetUserProvided();
    3677          23 :             if (osKey != osKeyInit)
    3678           0 :                 arg.AddAlias(osKeyInit);
    3679             :         }
    3680          23 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3681          23 :         CPLAssert(oIter != m_mapLongNameToArg.end());
    3682          23 :         return oIter->second;
    3683             :     }
    3684             : 
    3685       39914 :     if (suggestionAllowed)
    3686             :     {
    3687          14 :         const auto suggestions = GetSuggestionsForArgumentName(osName);
    3688           7 :         if (!suggestions.empty())
    3689             :         {
    3690           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3691             :                      "Argument '%s' is unknown. Do you mean %s?",
    3692             :                      osName.c_str(),
    3693           4 :                      FormatSuggestionsAsString(suggestions,
    3694             :                                                /* addDashDashPrefix = */ false)
    3695             :                          .c_str());
    3696             :         }
    3697             :     }
    3698             : 
    3699       39914 :     return nullptr;
    3700             : }
    3701             : 
    3702             : /************************************************************************/
    3703             : /*                     GDALAlgorithm::AddAliasFor()                     */
    3704             : /************************************************************************/
    3705             : 
    3706             : //! @cond Doxygen_Suppress
    3707       89244 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    3708             :                                 const std::string &alias)
    3709             : {
    3710       89244 :     if (cpl::contains(m_mapLongNameToArg, alias))
    3711             :     {
    3712           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    3713             :                     alias.c_str());
    3714             :     }
    3715             :     else
    3716             :     {
    3717       89243 :         m_mapLongNameToArg[alias] = arg;
    3718             :     }
    3719       89244 : }
    3720             : 
    3721             : //! @endcond
    3722             : 
    3723             : /************************************************************************/
    3724             : /*                GDALAlgorithm::AddShortNameAliasFor()                 */
    3725             : /************************************************************************/
    3726             : 
    3727             : //! @cond Doxygen_Suppress
    3728          50 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
    3729             :                                          char shortNameAlias)
    3730             : {
    3731         100 :     std::string alias;
    3732          50 :     alias += shortNameAlias;
    3733          50 :     if (cpl::contains(m_mapShortNameToArg, alias))
    3734             :     {
    3735           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    3736             :                     "Short name '%s' already declared.", alias.c_str());
    3737             :     }
    3738             :     else
    3739             :     {
    3740          50 :         m_mapShortNameToArg[alias] = arg;
    3741             :     }
    3742          50 : }
    3743             : 
    3744             : //! @endcond
    3745             : 
    3746             : /************************************************************************/
    3747             : /*                    GDALAlgorithm::SetPositional()                    */
    3748             : /************************************************************************/
    3749             : 
    3750             : //! @cond Doxygen_Suppress
    3751       24528 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    3752             : {
    3753       24528 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    3754             :                         arg) == m_positionalArgs.end());
    3755       24528 :     m_positionalArgs.push_back(arg);
    3756       24528 : }
    3757             : 
    3758             : //! @endcond
    3759             : 
    3760             : /************************************************************************/
    3761             : /*                  GDALAlgorithm::HasSubAlgorithms()                   */
    3762             : /************************************************************************/
    3763             : 
    3764       14479 : bool GDALAlgorithm::HasSubAlgorithms() const
    3765             : {
    3766       14479 :     if (!m_subAlgRegistry.empty())
    3767        3808 :         return true;
    3768       10671 :     return !GDALGlobalAlgorithmRegistry::GetSingleton()
    3769       21342 :                 .GetDeclaredSubAlgorithmNames(m_callPath)
    3770       10671 :                 .empty();
    3771             : }
    3772             : 
    3773             : /************************************************************************/
    3774             : /*                GDALAlgorithm::GetSubAlgorithmNames()                 */
    3775             : /************************************************************************/
    3776             : 
    3777        1613 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
    3778             : {
    3779        1613 :     std::vector<std::string> ret = m_subAlgRegistry.GetNames();
    3780        1613 :     const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
    3781        3226 :                            .GetDeclaredSubAlgorithmNames(m_callPath);
    3782        1613 :     ret.insert(ret.end(), other.begin(), other.end());
    3783        1613 :     if (!other.empty())
    3784         521 :         std::sort(ret.begin(), ret.end());
    3785        3226 :     return ret;
    3786             : }
    3787             : 
    3788             : /************************************************************************/
    3789             : /*                       GDALAlgorithm::AddArg()                        */
    3790             : /************************************************************************/
    3791             : 
    3792             : GDALInConstructionAlgorithmArg &
    3793      354408 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    3794             : {
    3795      354408 :     auto argRaw = arg.get();
    3796      354408 :     const auto &longName = argRaw->GetName();
    3797      354408 :     if (!longName.empty())
    3798             :     {
    3799      354395 :         if (longName[0] == '-')
    3800             :         {
    3801           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3802             :                         "Long name '%s' should not start with '-'",
    3803             :                         longName.c_str());
    3804             :         }
    3805      354395 :         if (longName.find('=') != std::string::npos)
    3806             :         {
    3807           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3808             :                         "Long name '%s' should not contain a '=' character",
    3809             :                         longName.c_str());
    3810             :         }
    3811      354395 :         if (cpl::contains(m_mapLongNameToArg, longName))
    3812             :         {
    3813           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3814             :                         "Long name '%s' already declared", longName.c_str());
    3815             :         }
    3816      354395 :         m_mapLongNameToArg[longName] = argRaw;
    3817             :     }
    3818      354408 :     const auto &shortName = argRaw->GetShortName();
    3819      354408 :     if (!shortName.empty())
    3820             :     {
    3821      174022 :         if (shortName.size() != 1 ||
    3822       87011 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    3823          66 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    3824           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    3825             :         {
    3826           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3827             :                         "Short name '%s' should be a single letter or digit",
    3828             :                         shortName.c_str());
    3829             :         }
    3830       87011 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    3831             :         {
    3832           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3833             :                         "Short name '%s' already declared", shortName.c_str());
    3834             :         }
    3835       87011 :         m_mapShortNameToArg[shortName] = argRaw;
    3836             :     }
    3837      354408 :     m_args.emplace_back(std::move(arg));
    3838             :     return *(
    3839      354408 :         cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    3840             : }
    3841             : 
    3842             : GDALInConstructionAlgorithmArg &
    3843      161506 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3844             :                       const std::string &helpMessage, bool *pValue)
    3845             : {
    3846      161506 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3847             :         this,
    3848      323012 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    3849      323012 :         pValue));
    3850             : }
    3851             : 
    3852             : GDALInConstructionAlgorithmArg &
    3853       56075 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3854             :                       const std::string &helpMessage, std::string *pValue)
    3855             : {
    3856       56075 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3857             :         this,
    3858      112150 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    3859      112150 :         pValue));
    3860             : }
    3861             : 
    3862             : GDALInConstructionAlgorithmArg &
    3863       13058 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3864             :                       const std::string &helpMessage, int *pValue)
    3865             : {
    3866       13058 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3867             :         this,
    3868       26116 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    3869       26116 :         pValue));
    3870             : }
    3871             : 
    3872             : GDALInConstructionAlgorithmArg &
    3873       10667 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3874             :                       const std::string &helpMessage, double *pValue)
    3875             : {
    3876       10667 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3877             :         this,
    3878       21334 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    3879       21334 :         pValue));
    3880             : }
    3881             : 
    3882             : GDALInConstructionAlgorithmArg &
    3883       13477 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3884             :                       const std::string &helpMessage,
    3885             :                       GDALArgDatasetValue *pValue, GDALArgDatasetType type)
    3886             : {
    3887       26954 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3888             :                            this,
    3889       26954 :                            GDALAlgorithmArgDecl(longName, chShortName,
    3890             :                                                 helpMessage, GAAT_DATASET),
    3891       13477 :                            pValue))
    3892       13477 :                     .SetDatasetType(type);
    3893       13477 :     pValue->SetOwnerArgument(&arg);
    3894       13477 :     return arg;
    3895             : }
    3896             : 
    3897             : GDALInConstructionAlgorithmArg &
    3898       76013 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3899             :                       const std::string &helpMessage,
    3900             :                       std::vector<std::string> *pValue)
    3901             : {
    3902       76013 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3903             :         this,
    3904      152026 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3905             :                              GAAT_STRING_LIST),
    3906      152026 :         pValue));
    3907             : }
    3908             : 
    3909             : GDALInConstructionAlgorithmArg &
    3910        2144 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3911             :                       const std::string &helpMessage, std::vector<int> *pValue)
    3912             : {
    3913        2144 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3914             :         this,
    3915        4288 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3916             :                              GAAT_INTEGER_LIST),
    3917        4288 :         pValue));
    3918             : }
    3919             : 
    3920             : GDALInConstructionAlgorithmArg &
    3921        5490 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3922             :                       const std::string &helpMessage,
    3923             :                       std::vector<double> *pValue)
    3924             : {
    3925        5490 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3926             :         this,
    3927       10980 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3928             :                              GAAT_REAL_LIST),
    3929       10980 :         pValue));
    3930             : }
    3931             : 
    3932             : GDALInConstructionAlgorithmArg &
    3933       15978 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3934             :                       const std::string &helpMessage,
    3935             :                       std::vector<GDALArgDatasetValue> *pValue,
    3936             :                       GDALArgDatasetType type)
    3937             : {
    3938       31956 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3939             :                       this,
    3940       31956 :                       GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3941             :                                            GAAT_DATASET_LIST),
    3942       15978 :                       pValue))
    3943       31956 :         .SetDatasetType(type);
    3944             : }
    3945             : 
    3946             : /************************************************************************/
    3947             : /*                            MsgOrDefault()                            */
    3948             : /************************************************************************/
    3949             : 
    3950      116813 : inline const char *MsgOrDefault(const char *helpMessage,
    3951             :                                 const char *defaultMessage)
    3952             : {
    3953      116813 :     return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
    3954             : }
    3955             : 
    3956             : /************************************************************************/
    3957             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
    3958             : /************************************************************************/
    3959             : 
    3960             : /* static */
    3961       19560 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
    3962             :     GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
    3963             : {
    3964             :     arg.SetAutoCompleteFunction(
    3965           7 :         [&arg,
    3966        2483 :          type](const std::string &currentValue) -> std::vector<std::string>
    3967             :         {
    3968          14 :             std::vector<std::string> oRet;
    3969             : 
    3970           7 :             if (arg.IsHidden())
    3971           0 :                 return oRet;
    3972             : 
    3973             :             {
    3974           7 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3975             :                 VSIStatBufL sStat;
    3976          10 :                 if (!currentValue.empty() && currentValue.back() != '/' &&
    3977           3 :                     VSIStatL(currentValue.c_str(), &sStat) == 0)
    3978             :                 {
    3979           0 :                     return oRet;
    3980             :                 }
    3981             :             }
    3982             : 
    3983           7 :             auto poDM = GetGDALDriverManager();
    3984          14 :             std::set<std::string> oExtensions;
    3985           7 :             if (type)
    3986             :             {
    3987        1386 :                 for (int i = 0; i < poDM->GetDriverCount(); ++i)
    3988             :                 {
    3989        1380 :                     auto poDriver = poDM->GetDriver(i);
    3990        3910 :                     if (((type & GDAL_OF_RASTER) != 0 &&
    3991        1150 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    3992         590 :                         ((type & GDAL_OF_VECTOR) != 0 &&
    3993        2899 :                          poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    3994         499 :                         ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    3995           0 :                          poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    3996             :                     {
    3997             :                         const char *pszExtensions =
    3998         881 :                             poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    3999         881 :                         if (pszExtensions)
    4000             :                         {
    4001             :                             const CPLStringList aosExts(
    4002        1164 :                                 CSLTokenizeString2(pszExtensions, " ", 0));
    4003        1313 :                             for (const char *pszExt : cpl::Iterate(aosExts))
    4004         731 :                                 oExtensions.insert(CPLString(pszExt).tolower());
    4005             :                         }
    4006             :                     }
    4007             :                 }
    4008             :             }
    4009             : 
    4010          14 :             std::string osDir;
    4011          14 :             const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
    4012          14 :             std::string osPrefix;
    4013           7 :             if (STARTS_WITH(currentValue.c_str(), "/vsi"))
    4014             :             {
    4015          82 :                 for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
    4016             :                 {
    4017          81 :                     if (STARTS_WITH(currentValue.c_str(), pszPrefix))
    4018             :                     {
    4019           2 :                         osPrefix = pszPrefix;
    4020           2 :                         break;
    4021             :                     }
    4022             :                 }
    4023           3 :                 if (osPrefix.empty())
    4024           1 :                     return aosVSIPrefixes;
    4025           2 :                 if (currentValue == osPrefix)
    4026           1 :                     osDir = osPrefix;
    4027             :             }
    4028           6 :             if (osDir.empty())
    4029             :             {
    4030           5 :                 osDir = CPLGetDirnameSafe(currentValue.c_str());
    4031           5 :                 if (!osPrefix.empty() && osDir.size() < osPrefix.size())
    4032           0 :                     osDir = std::move(osPrefix);
    4033             :             }
    4034             : 
    4035           6 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    4036          12 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    4037           6 :             if (currentValue.empty())
    4038           1 :                 osDir.clear();
    4039             :             const std::string currentFilename =
    4040          12 :                 CPLGetFilename(currentValue.c_str());
    4041           6 :             if (psDir)
    4042             :             {
    4043         460 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    4044             :                 {
    4045         455 :                     if ((currentFilename.empty() ||
    4046         227 :                          STARTS_WITH(psEntry->pszName,
    4047         229 :                                      currentFilename.c_str())) &&
    4048         229 :                         strcmp(psEntry->pszName, ".") != 0 &&
    4049        1367 :                         strcmp(psEntry->pszName, "..") != 0 &&
    4050         229 :                         (oExtensions.empty() ||
    4051         228 :                          !strstr(psEntry->pszName, ".aux.xml")))
    4052             :                     {
    4053         906 :                         if (oExtensions.empty() ||
    4054         226 :                             cpl::contains(
    4055             :                                 oExtensions,
    4056         453 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    4057         679 :                                     .tolower()) ||
    4058         194 :                             VSI_ISDIR(psEntry->nMode))
    4059             :                         {
    4060          74 :                             std::string osVal;
    4061          37 :                             if (osDir.empty() || osDir == ".")
    4062           4 :                                 osVal = psEntry->pszName;
    4063             :                             else
    4064          66 :                                 osVal = CPLFormFilenameSafe(
    4065          66 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    4066          37 :                             if (VSI_ISDIR(psEntry->nMode))
    4067           4 :                                 osVal += osSep;
    4068          37 :                             oRet.push_back(std::move(osVal));
    4069             :                         }
    4070             :                     }
    4071         455 :                 }
    4072           5 :                 VSICloseDir(psDir);
    4073             :             }
    4074           6 :             return oRet;
    4075       19560 :         });
    4076       19560 : }
    4077             : 
    4078             : /************************************************************************/
    4079             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    4080             : /************************************************************************/
    4081             : 
    4082         909 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    4083             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4084             :     bool positionalAndRequired, const char *helpMessage)
    4085             : {
    4086             :     auto &arg = AddArg(
    4087             :         GDAL_ARG_NAME_INPUT, 'i',
    4088             :         MsgOrDefault(helpMessage,
    4089             :                      CPLSPrintf("Input %s dataset",
    4090         909 :                                 GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4091        1818 :         pValue, type);
    4092         909 :     if (positionalAndRequired)
    4093         902 :         arg.SetPositional().SetRequired();
    4094             : 
    4095         909 :     SetAutoCompleteFunctionForFilename(arg, type);
    4096             : 
    4097         909 :     AddValidationAction(
    4098          80 :         [pValue]()
    4099             :         {
    4100          79 :             if (pValue->GetName() == "-")
    4101           1 :                 pValue->Set("/vsistdin/");
    4102          79 :             return true;
    4103             :         });
    4104             : 
    4105         909 :     return arg;
    4106             : }
    4107             : 
    4108             : /************************************************************************/
    4109             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    4110             : /************************************************************************/
    4111             : 
    4112       15509 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    4113             :     std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
    4114             :     bool positionalAndRequired, const char *helpMessage)
    4115             : {
    4116             :     auto &arg =
    4117             :         AddArg(GDAL_ARG_NAME_INPUT, 'i',
    4118             :                MsgOrDefault(
    4119             :                    helpMessage,
    4120             :                    CPLSPrintf("Input %s datasets",
    4121       15509 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4122       46527 :                pValue, type)
    4123       15509 :             .SetPackedValuesAllowed(false);
    4124       15509 :     if (positionalAndRequired)
    4125        1685 :         arg.SetPositional().SetRequired();
    4126             : 
    4127       15509 :     SetAutoCompleteFunctionForFilename(arg, type);
    4128             : 
    4129       15509 :     AddValidationAction(
    4130        7657 :         [pValue]()
    4131             :         {
    4132       14419 :             for (auto &val : *pValue)
    4133             :             {
    4134        6762 :                 if (val.GetName() == "-")
    4135           1 :                     val.Set("/vsistdin/");
    4136             :             }
    4137        7657 :             return true;
    4138             :         });
    4139       15509 :     return arg;
    4140             : }
    4141             : 
    4142             : /************************************************************************/
    4143             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    4144             : /************************************************************************/
    4145             : 
    4146        9294 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
    4147             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4148             :     bool positionalAndRequired, const char *helpMessage)
    4149             : {
    4150             :     auto &arg =
    4151             :         AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    4152             :                MsgOrDefault(
    4153             :                    helpMessage,
    4154             :                    CPLSPrintf("Output %s dataset",
    4155        9294 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4156       27882 :                pValue, type)
    4157        9294 :             .SetIsInput(true)
    4158        9294 :             .SetIsOutput(true)
    4159        9294 :             .SetDatasetInputFlags(GADV_NAME)
    4160        9294 :             .SetDatasetOutputFlags(GADV_OBJECT);
    4161        9294 :     if (positionalAndRequired)
    4162        4665 :         arg.SetPositional().SetRequired();
    4163             : 
    4164        9294 :     AddValidationAction(
    4165       14101 :         [this, &arg, pValue]()
    4166             :         {
    4167        4283 :             if (pValue->GetName() == "-")
    4168           4 :                 pValue->Set("/vsistdout/");
    4169             : 
    4170        4283 :             auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    4171        4231 :             if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    4172        6715 :                 (!outputFormatArg->IsExplicitlySet() ||
    4173       10998 :                  outputFormatArg->Get<std::string>().empty()) &&
    4174        1747 :                 arg.IsExplicitlySet())
    4175             :             {
    4176             :                 const auto vrtCompatible =
    4177        1248 :                     outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4178         196 :                 if (vrtCompatible && !vrtCompatible->empty() &&
    4179        1444 :                     vrtCompatible->front() == "false" &&
    4180        1346 :                     EQUAL(
    4181             :                         CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
    4182             :                         "VRT"))
    4183             :                 {
    4184           6 :                     ReportError(
    4185             :                         CE_Failure, CPLE_NotSupported,
    4186             :                         "VRT output is not supported.%s",
    4187           6 :                         outputFormatArg->GetDescription().find("GDALG") !=
    4188             :                                 std::string::npos
    4189             :                             ? " Consider using the GDALG driver instead (files "
    4190             :                               "with .gdalg.json extension)"
    4191             :                             : "");
    4192           6 :                     return false;
    4193             :                 }
    4194        1242 :                 else if (pValue->GetName().size() > strlen(".gdalg.json") &&
    4195        2461 :                          EQUAL(pValue->GetName()
    4196             :                                    .substr(pValue->GetName().size() -
    4197             :                                            strlen(".gdalg.json"))
    4198             :                                    .c_str(),
    4199        3703 :                                ".gdalg.json") &&
    4200          28 :                          outputFormatArg->GetDescription().find("GDALG") ==
    4201             :                              std::string::npos)
    4202             :                 {
    4203           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4204             :                                 "GDALG output is not supported");
    4205           0 :                     return false;
    4206             :                 }
    4207             :             }
    4208        4277 :             return true;
    4209             :         });
    4210             : 
    4211        9294 :     return arg;
    4212             : }
    4213             : 
    4214             : /************************************************************************/
    4215             : /*                   GDALAlgorithm::AddOverwriteArg()                   */
    4216             : /************************************************************************/
    4217             : 
    4218             : GDALInConstructionAlgorithmArg &
    4219        9162 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
    4220             : {
    4221             :     return AddArg(
    4222             :                GDAL_ARG_NAME_OVERWRITE, 0,
    4223             :                MsgOrDefault(
    4224             :                    helpMessage,
    4225             :                    _("Whether overwriting existing output dataset is allowed")),
    4226       18324 :                pValue)
    4227       18324 :         .SetDefault(false);
    4228             : }
    4229             : 
    4230             : /************************************************************************/
    4231             : /*                GDALAlgorithm::AddOverwriteLayerArg()                 */
    4232             : /************************************************************************/
    4233             : 
    4234             : GDALInConstructionAlgorithmArg &
    4235        3812 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
    4236             : {
    4237        3812 :     AddValidationAction(
    4238        1793 :         [this]
    4239             :         {
    4240        1792 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4241        1792 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4242             :             {
    4243           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4244             :                             "--update argument must exist for "
    4245             :                             "--overwrite-layer, even if hidden");
    4246           1 :                 return false;
    4247             :             }
    4248        1791 :             return true;
    4249             :         });
    4250             :     return AddArg(
    4251             :                GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
    4252             :                MsgOrDefault(
    4253             :                    helpMessage,
    4254             :                    _("Whether overwriting existing output layer is allowed")),
    4255        7624 :                pValue)
    4256        3812 :         .SetDefault(false)
    4257             :         .AddAction(
    4258          19 :             [this]
    4259             :             {
    4260          19 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4261          19 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4262             :                 {
    4263          19 :                     updateArg->Set(true);
    4264             :                 }
    4265        7643 :             });
    4266             : }
    4267             : 
    4268             : /************************************************************************/
    4269             : /*                    GDALAlgorithm::AddUpdateArg()                     */
    4270             : /************************************************************************/
    4271             : 
    4272             : GDALInConstructionAlgorithmArg &
    4273        4384 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
    4274             : {
    4275             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    4276             :                   MsgOrDefault(
    4277             :                       helpMessage,
    4278             :                       _("Whether to open existing dataset in update mode")),
    4279        8768 :                   pValue)
    4280        8768 :         .SetDefault(false);
    4281             : }
    4282             : 
    4283             : /************************************************************************/
    4284             : /*                  GDALAlgorithm::AddAppendLayerArg()                  */
    4285             : /************************************************************************/
    4286             : 
    4287             : GDALInConstructionAlgorithmArg &
    4288        3584 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
    4289             : {
    4290        3584 :     AddValidationAction(
    4291        1749 :         [this]
    4292             :         {
    4293        1748 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4294        1748 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4295             :             {
    4296           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4297             :                             "--update argument must exist for --append, even "
    4298             :                             "if hidden");
    4299           1 :                 return false;
    4300             :             }
    4301        1747 :             return true;
    4302             :         });
    4303             :     return AddArg(GDAL_ARG_NAME_APPEND, 0,
    4304             :                   MsgOrDefault(
    4305             :                       helpMessage,
    4306             :                       _("Whether appending to existing layer is allowed")),
    4307        7168 :                   pValue)
    4308        3584 :         .SetDefault(false)
    4309             :         .AddAction(
    4310          25 :             [this]
    4311             :             {
    4312          25 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4313          25 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4314             :                 {
    4315          25 :                     updateArg->Set(true);
    4316             :                 }
    4317        7193 :             });
    4318             : }
    4319             : 
    4320             : /************************************************************************/
    4321             : /*                GDALAlgorithm::AddOptionsSuggestions()                */
    4322             : /************************************************************************/
    4323             : 
    4324             : /* static */
    4325          30 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
    4326             :                                           const std::string &currentValue,
    4327             :                                           std::vector<std::string> &oRet)
    4328             : {
    4329          30 :     if (!pszXML)
    4330           0 :         return false;
    4331          60 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    4332          30 :     if (!poTree)
    4333           0 :         return false;
    4334             : 
    4335          60 :     std::string typedOptionName = currentValue;
    4336          30 :     const auto posEqual = typedOptionName.find('=');
    4337          60 :     std::string typedValue;
    4338          30 :     if (posEqual != 0 && posEqual != std::string::npos)
    4339             :     {
    4340           2 :         typedValue = currentValue.substr(posEqual + 1);
    4341           2 :         typedOptionName.resize(posEqual);
    4342             :     }
    4343             : 
    4344         453 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4345         423 :          psChild = psChild->psNext)
    4346             :     {
    4347         436 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4348         449 :         if (pszName && typedOptionName == pszName &&
    4349          13 :             (strcmp(psChild->pszValue, "Option") == 0 ||
    4350           2 :              strcmp(psChild->pszValue, "Argument") == 0))
    4351             :         {
    4352          13 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    4353          13 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    4354          13 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    4355          13 :             if (EQUAL(pszType, "string-select"))
    4356             :             {
    4357          90 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    4358          85 :                      psChild2 = psChild2->psNext)
    4359             :                 {
    4360          85 :                     if (EQUAL(psChild2->pszValue, "Value"))
    4361             :                     {
    4362          75 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    4363             :                     }
    4364             :                 }
    4365             :             }
    4366           8 :             else if (EQUAL(pszType, "boolean"))
    4367             :             {
    4368           3 :                 if (typedValue == "YES" || typedValue == "NO")
    4369             :                 {
    4370           1 :                     oRet.push_back(currentValue);
    4371           1 :                     return true;
    4372             :                 }
    4373           2 :                 oRet.push_back("NO");
    4374           2 :                 oRet.push_back("YES");
    4375             :             }
    4376           5 :             else if (EQUAL(pszType, "int"))
    4377             :             {
    4378           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    4379           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    4380             :                 {
    4381           1 :                     const int nMax = atoi(pszMax);
    4382          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    4383          12 :                         oRet.push_back(std::to_string(i));
    4384             :                 }
    4385             :             }
    4386             : 
    4387          12 :             if (oRet.empty())
    4388             :             {
    4389           4 :                 if (pszMin && pszMax)
    4390             :                 {
    4391           1 :                     oRet.push_back(std::string("##"));
    4392           2 :                     oRet.push_back(std::string("validity range: [")
    4393           1 :                                        .append(pszMin)
    4394           1 :                                        .append(",")
    4395           1 :                                        .append(pszMax)
    4396           1 :                                        .append("]"));
    4397             :                 }
    4398           3 :                 else if (pszMin)
    4399             :                 {
    4400           1 :                     oRet.push_back(std::string("##"));
    4401           1 :                     oRet.push_back(
    4402           1 :                         std::string("validity range: >= ").append(pszMin));
    4403             :                 }
    4404           2 :                 else if (pszMax)
    4405             :                 {
    4406           1 :                     oRet.push_back(std::string("##"));
    4407           1 :                     oRet.push_back(
    4408           1 :                         std::string("validity range: <= ").append(pszMax));
    4409             :                 }
    4410           1 :                 else if (const char *pszDescription =
    4411           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    4412             :                 {
    4413           1 :                     oRet.push_back(std::string("##"));
    4414           2 :                     oRet.push_back(std::string("type: ")
    4415           1 :                                        .append(pszType)
    4416           1 :                                        .append(", description: ")
    4417           1 :                                        .append(pszDescription));
    4418             :                 }
    4419             :             }
    4420             : 
    4421          12 :             return true;
    4422             :         }
    4423             :     }
    4424             : 
    4425         367 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4426         350 :          psChild = psChild->psNext)
    4427             :     {
    4428         350 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4429         350 :         if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
    4430           5 :                         strcmp(psChild->pszValue, "Argument") == 0))
    4431             :         {
    4432         347 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    4433         347 :             if (!pszScope ||
    4434          40 :                 (EQUAL(pszScope, "raster") &&
    4435          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    4436          20 :                 (EQUAL(pszScope, "vector") &&
    4437           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    4438             :             {
    4439         327 :                 oRet.push_back(std::string(pszName).append("="));
    4440             :             }
    4441             :         }
    4442             :     }
    4443             : 
    4444          17 :     return false;
    4445             : }
    4446             : 
    4447             : /************************************************************************/
    4448             : /*             GDALAlgorithm::OpenOptionCompleteFunction()              */
    4449             : /************************************************************************/
    4450             : 
    4451             : //! @cond Doxygen_Suppress
    4452             : std::vector<std::string>
    4453           2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
    4454             : {
    4455           2 :     std::vector<std::string> oRet;
    4456             : 
    4457           2 :     int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    4458           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4459           4 :     if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
    4460           2 :                      inputArg->GetType() == GAAT_DATASET_LIST))
    4461             :     {
    4462           2 :         datasetType = inputArg->GetDatasetType();
    4463             :     }
    4464             : 
    4465           2 :     auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4466           4 :     if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    4467           2 :         inputFormat->IsExplicitlySet())
    4468             :     {
    4469             :         const auto &aosAllowedDrivers =
    4470           1 :             inputFormat->Get<std::vector<std::string>>();
    4471           1 :         if (aosAllowedDrivers.size() == 1)
    4472             :         {
    4473           2 :             auto poDriver = GetGDALDriverManager()->GetDriverByName(
    4474           1 :                 aosAllowedDrivers[0].c_str());
    4475           1 :             if (poDriver)
    4476             :             {
    4477           1 :                 AddOptionsSuggestions(
    4478           1 :                     poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    4479             :                     datasetType, currentValue, oRet);
    4480             :             }
    4481           1 :             return oRet;
    4482             :         }
    4483             :     }
    4484             : 
    4485           1 :     const auto AddSuggestions = [datasetType, &currentValue,
    4486         375 :                                  &oRet](const GDALArgDatasetValue &datasetValue)
    4487             :     {
    4488           1 :         auto poDM = GetGDALDriverManager();
    4489             : 
    4490           1 :         const auto &osDSName = datasetValue.GetName();
    4491           1 :         const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    4492           1 :         if (!osExt.empty())
    4493             :         {
    4494           1 :             std::set<std::string> oVisitedExtensions;
    4495         231 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4496             :             {
    4497         230 :                 auto poDriver = poDM->GetDriver(i);
    4498         690 :                 if (((datasetType & GDAL_OF_RASTER) != 0 &&
    4499         230 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    4500          72 :                     ((datasetType & GDAL_OF_VECTOR) != 0 &&
    4501         460 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    4502          72 :                     ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    4503           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    4504             :                 {
    4505             :                     const char *pszExtensions =
    4506         158 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    4507         158 :                     if (pszExtensions)
    4508             :                     {
    4509             :                         const CPLStringList aosExts(
    4510         104 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    4511         229 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    4512             :                         {
    4513         129 :                             if (EQUAL(pszExt, osExt.c_str()) &&
    4514           3 :                                 !cpl::contains(oVisitedExtensions, pszExt))
    4515             :                             {
    4516           1 :                                 oVisitedExtensions.insert(pszExt);
    4517           1 :                                 if (AddOptionsSuggestions(
    4518             :                                         poDriver->GetMetadataItem(
    4519           1 :                                             GDAL_DMD_OPENOPTIONLIST),
    4520             :                                         datasetType, currentValue, oRet))
    4521             :                                 {
    4522           0 :                                     return;
    4523             :                                 }
    4524           1 :                                 break;
    4525             :                             }
    4526             :                         }
    4527             :                     }
    4528             :                 }
    4529             :             }
    4530             :         }
    4531           1 :     };
    4532             : 
    4533           1 :     if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4534             :     {
    4535           0 :         auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    4536           0 :         AddSuggestions(datasetValue);
    4537             :     }
    4538           1 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4539             :     {
    4540           1 :         auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4541           1 :         if (datasetValues.size() == 1)
    4542           1 :             AddSuggestions(datasetValues[0]);
    4543             :     }
    4544             : 
    4545           1 :     return oRet;
    4546             : }
    4547             : 
    4548             : //! @endcond
    4549             : 
    4550             : /************************************************************************/
    4551             : /*                  GDALAlgorithm::AddOpenOptionsArg()                  */
    4552             : /************************************************************************/
    4553             : 
    4554             : GDALInConstructionAlgorithmArg &
    4555       10288 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
    4556             :                                  const char *helpMessage)
    4557             : {
    4558             :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
    4559       20576 :                        MsgOrDefault(helpMessage, _("Open options")), pValue)
    4560       20576 :                     .AddAlias("oo")
    4561       20576 :                     .SetMetaVar("<KEY>=<VALUE>")
    4562       10288 :                     .SetPackedValuesAllowed(false)
    4563       10288 :                     .SetCategory(GAAC_ADVANCED);
    4564             : 
    4565          31 :     arg.AddValidationAction([this, &arg]()
    4566       10319 :                             { return ParseAndValidateKeyValue(arg); });
    4567             : 
    4568             :     arg.SetAutoCompleteFunction(
    4569           2 :         [this](const std::string &currentValue)
    4570       10290 :         { return OpenOptionCompleteFunction(currentValue); });
    4571             : 
    4572       10288 :     return arg;
    4573             : }
    4574             : 
    4575             : /************************************************************************/
    4576             : /*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
    4577             : /************************************************************************/
    4578             : 
    4579             : GDALInConstructionAlgorithmArg &
    4580        3660 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
    4581             :                                        const char *helpMessage)
    4582             : {
    4583             :     auto &arg =
    4584             :         AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
    4585        7320 :                MsgOrDefault(helpMessage, _("Output open options")), pValue)
    4586        7320 :             .AddAlias("output-oo")
    4587        7320 :             .SetMetaVar("<KEY>=<VALUE>")
    4588        3660 :             .SetPackedValuesAllowed(false)
    4589        3660 :             .SetCategory(GAAC_ADVANCED);
    4590             : 
    4591           0 :     arg.AddValidationAction([this, &arg]()
    4592        3660 :                             { return ParseAndValidateKeyValue(arg); });
    4593             : 
    4594             :     arg.SetAutoCompleteFunction(
    4595           0 :         [this](const std::string &currentValue)
    4596        3660 :         { return OpenOptionCompleteFunction(currentValue); });
    4597             : 
    4598        3660 :     return arg;
    4599             : }
    4600             : 
    4601             : /************************************************************************/
    4602             : /*                    NormalizeRequiredCapability()                     */
    4603             : /************************************************************************/
    4604             : 
    4605             : /** Expands the GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER alias into the
    4606             :  * generic form of alternatives separated by '|'.
    4607             :  */
    4608        8872 : static std::string NormalizeRequiredCapability(const std::string &osRequiredCap)
    4609             : {
    4610        8872 :     return osRequiredCap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER
    4611             :                ? GDAL_DCAP_RASTER "|" GDAL_DCAP_MULTIDIM_RASTER
    4612        8872 :                : osRequiredCap;
    4613             : }
    4614             : 
    4615             : /************************************************************************/
    4616             : /*                        DriverHasCapability()                         */
    4617             : /************************************************************************/
    4618             : 
    4619             : /** Returns whether poDriver meets the osRequiredCap requirement, which may
    4620             :  * express alternatives separated by '|', among the AND-ed list of
    4621             :  * requirements requiredCaps.
    4622             :  */
    4623        8869 : static bool DriverHasCapability(GDALDriver *poDriver,
    4624             :                                 const std::string &osRequiredCap,
    4625             :                                 const std::vector<std::string> &requiredCaps)
    4626             : {
    4627             :     const CPLStringList aosAlternatives(CSLTokenizeString2(
    4628       17738 :         NormalizeRequiredCapability(osRequiredCap).c_str(), "|", 0));
    4629        9702 :     for (const char *pszCap : cpl::Iterate(aosAlternatives))
    4630             :     {
    4631        8873 :         const char *pszVal = poDriver->GetMetadataItem(pszCap);
    4632        8873 :         if (pszVal && pszVal[0])
    4633             :         {
    4634        8040 :             return true;
    4635             :         }
    4636             :         // if it supports Create, it supports CreateCopy. GDAL_DCAP_RASTER is
    4637             :         // matched as a whole entry, i.e. not as one of the '|' alternatives of
    4638             :         // an entry, which is how all requirement lists spell it.
    4639        1421 :         else if (EQUAL(pszCap, GDAL_DCAP_CREATECOPY) &&
    4640           0 :                  std::find(requiredCaps.begin(), requiredCaps.end(),
    4641        1421 :                            GDAL_DCAP_RASTER) != requiredCaps.end() &&
    4642        4772 :                  poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    4643        1421 :                  poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    4644             :         {
    4645        1097 :             return true;
    4646             :         }
    4647             :     }
    4648         829 :     return false;
    4649             : }
    4650             : 
    4651             : /************************************************************************/
    4652             : /*                           ValidateFormat()                           */
    4653             : /************************************************************************/
    4654             : 
    4655        5297 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
    4656             :                                    bool bStreamAllowed,
    4657             :                                    bool bGDALGAllowed) const
    4658             : {
    4659        5297 :     if (arg.GetChoices().empty())
    4660             :     {
    4661             :         const auto Validate =
    4662       23131 :             [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
    4663             :         {
    4664        5176 :             if (const auto extraFormats =
    4665        5176 :                     arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4666             :             {
    4667          60 :                 for (const auto &extraFormat : *extraFormats)
    4668             :                 {
    4669          48 :                     if (EQUAL(val.c_str(), extraFormat.c_str()))
    4670          14 :                         return true;
    4671             :                 }
    4672             :             }
    4673             : 
    4674        5162 :             if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
    4675        1957 :                 return true;
    4676             : 
    4677        3213 :             if (EQUAL(val.c_str(), "GDALG") &&
    4678           8 :                 arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
    4679             :             {
    4680           4 :                 if (bGDALGAllowed)
    4681             :                 {
    4682           4 :                     return true;
    4683             :                 }
    4684             :                 else
    4685             :                 {
    4686           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4687             :                                 "GDALG output is not supported.");
    4688           0 :                     return false;
    4689             :                 }
    4690             :             }
    4691             : 
    4692             :             const auto vrtCompatible =
    4693        3201 :                 arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4694         536 :             if (vrtCompatible && !vrtCompatible->empty() &&
    4695        3737 :                 vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
    4696             :             {
    4697           7 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4698             :                             "VRT output is not supported.%s",
    4699             :                             bGDALGAllowed
    4700             :                                 ? " Consider using the GDALG driver instead "
    4701             :                                   "(files with .gdalg.json extension)."
    4702             :                                 : "");
    4703           7 :                 return false;
    4704             :             }
    4705             : 
    4706             :             const auto allowedFormats =
    4707        3194 :                 arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4708        3247 :             if (allowedFormats && !allowedFormats->empty() &&
    4709           0 :                 std::find(allowedFormats->begin(), allowedFormats->end(),
    4710        3247 :                           val) != allowedFormats->end())
    4711             :             {
    4712          12 :                 return true;
    4713             :             }
    4714             : 
    4715             :             const auto excludedFormats =
    4716        3182 :                 arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4717        3229 :             if (excludedFormats && !excludedFormats->empty() &&
    4718           0 :                 std::find(excludedFormats->begin(), excludedFormats->end(),
    4719        3229 :                           val) != excludedFormats->end())
    4720             :             {
    4721           0 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4722             :                             "%s output is not supported.", val.c_str());
    4723           0 :                 return false;
    4724             :             }
    4725             : 
    4726        3182 :             auto hDriver = GDALGetDriverByName(val.c_str());
    4727        3182 :             if (!hDriver)
    4728             :             {
    4729             :                 auto poMissingDriver =
    4730           4 :                     GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
    4731           4 :                 if (poMissingDriver)
    4732             :                 {
    4733             :                     const std::string msg =
    4734           0 :                         GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
    4735           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4736             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4737             :                                 "not found but is known. However plugin %s",
    4738           0 :                                 arg.GetName().c_str(), val.c_str(),
    4739             :                                 msg.c_str());
    4740             :                 }
    4741             :                 else
    4742             :                 {
    4743           8 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4744             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4745             :                                 "does not exist.",
    4746           4 :                                 arg.GetName().c_str(), val.c_str());
    4747             :                 }
    4748           4 :                 return false;
    4749             :             }
    4750             : 
    4751        3178 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4752        3178 :             if (caps)
    4753             :             {
    4754        3150 :                 auto poDriver = GDALDriver::FromHandle(hDriver);
    4755        9616 :                 for (const std::string &cap : *caps)
    4756             :                 {
    4757        6471 :                     if (DriverHasCapability(poDriver, cap, *caps))
    4758        6466 :                         continue;
    4759             : 
    4760           5 :                     if (cap == GDAL_DMD_EXTENSIONS)
    4761             :                     {
    4762           2 :                         ReportError(CE_Failure, CPLE_AppDefined,
    4763             :                                     "Invalid value for argument '%s'. Driver "
    4764             :                                     "'%s' does not advertise any file format "
    4765             :                                     "extension.",
    4766           1 :                                     arg.GetName().c_str(), val.c_str());
    4767           5 :                         return false;
    4768             :                     }
    4769           4 :                     else if (cap == GDAL_DCAP_CREATE)
    4770             :                     {
    4771           1 :                         auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4772           2 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    4773           1 :                             updateArg->IsExplicitlySet())
    4774             :                         {
    4775           0 :                             continue;
    4776             :                         }
    4777             : 
    4778           2 :                         ReportError(CE_Failure, CPLE_AppDefined,
    4779             :                                     "Invalid value for argument '%s'. "
    4780             :                                     "Driver '%s' does not have write support.",
    4781           1 :                                     arg.GetName().c_str(), val.c_str());
    4782           1 :                         return false;
    4783             :                     }
    4784             :                     else
    4785             :                     {
    4786           3 :                         CPLString osCap(NormalizeRequiredCapability(cap));
    4787           3 :                         osCap.replaceAll("|", " or ");
    4788           6 :                         ReportError(CE_Failure, CPLE_AppDefined,
    4789             :                                     "Invalid value for argument '%s'. Driver "
    4790             :                                     "'%s' does not expose the required '%s' "
    4791             :                                     "capability.",
    4792           3 :                                     arg.GetName().c_str(), val.c_str(),
    4793             :                                     osCap.c_str());
    4794           3 :                         return false;
    4795             :                     }
    4796             :                 }
    4797             :             }
    4798        3173 :             return true;
    4799        5179 :         };
    4800             : 
    4801        5179 :         if (arg.GetType() == GAAT_STRING)
    4802             :         {
    4803        5160 :             return Validate(arg.Get<std::string>());
    4804             :         }
    4805          22 :         else if (arg.GetType() == GAAT_STRING_LIST)
    4806             :         {
    4807          38 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    4808             :             {
    4809          19 :                 if (!Validate(val))
    4810           3 :                     return false;
    4811             :             }
    4812             :         }
    4813             :     }
    4814             : 
    4815         137 :     return true;
    4816             : }
    4817             : 
    4818             : /************************************************************************/
    4819             : /*                     FormatAutoCompleteFunction()                     */
    4820             : /************************************************************************/
    4821             : 
    4822             : /* static */
    4823           7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
    4824             :     const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
    4825             : {
    4826           7 :     std::vector<std::string> res;
    4827           7 :     auto poDM = GetGDALDriverManager();
    4828           7 :     const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4829           7 :     const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4830           7 :     const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4831           7 :     const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4832           7 :     if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4833           0 :         res = std::move(*extraFormats);
    4834        1616 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4835             :     {
    4836        1609 :         auto poDriver = poDM->GetDriver(i);
    4837             : 
    4838           0 :         if (vrtCompatible && !vrtCompatible->empty() &&
    4839        1609 :             vrtCompatible->front() == "false" &&
    4840           0 :             EQUAL(poDriver->GetDescription(), "VRT"))
    4841             :         {
    4842             :             // do nothing
    4843             :         }
    4844        1609 :         else if (allowedFormats && !allowedFormats->empty() &&
    4845           0 :                  std::find(allowedFormats->begin(), allowedFormats->end(),
    4846        1609 :                            poDriver->GetDescription()) != allowedFormats->end())
    4847             :         {
    4848           0 :             res.push_back(poDriver->GetDescription());
    4849             :         }
    4850        1609 :         else if (excludedFormats && !excludedFormats->empty() &&
    4851           0 :                  std::find(excludedFormats->begin(), excludedFormats->end(),
    4852           0 :                            poDriver->GetDescription()) !=
    4853        1609 :                      excludedFormats->end())
    4854             :         {
    4855           0 :             continue;
    4856             :         }
    4857        1609 :         else if (caps)
    4858             :         {
    4859        1609 :             bool ok = true;
    4860        3183 :             for (const std::string &cap : *caps)
    4861             :             {
    4862        2398 :                 if (!DriverHasCapability(poDriver, cap, *caps))
    4863             :                 {
    4864         824 :                     ok = false;
    4865         824 :                     break;
    4866             :                 }
    4867             :             }
    4868        1609 :             if (ok)
    4869             :             {
    4870         785 :                 res.push_back(poDriver->GetDescription());
    4871             :             }
    4872             :         }
    4873             :     }
    4874           7 :     if (bGDALGAllowed)
    4875           4 :         res.push_back("GDALG");
    4876           7 :     return res;
    4877             : }
    4878             : 
    4879             : /************************************************************************/
    4880             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    4881             : /************************************************************************/
    4882             : 
    4883             : GDALInConstructionAlgorithmArg &
    4884       10072 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
    4885             :                                   const char *helpMessage)
    4886             : {
    4887             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
    4888       20144 :                        MsgOrDefault(helpMessage, _("Input formats")), pValue)
    4889       20144 :                     .AddAlias("if")
    4890       10072 :                     .SetCategory(GAAC_ADVANCED);
    4891          22 :     arg.AddValidationAction([this, &arg]()
    4892       10094 :                             { return ValidateFormat(arg, false, false); });
    4893             :     arg.SetAutoCompleteFunction(
    4894           1 :         [&arg](const std::string &)
    4895       10073 :         { return FormatAutoCompleteFunction(arg, false, false); });
    4896       10072 :     return arg;
    4897             : }
    4898             : 
    4899             : /************************************************************************/
    4900             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    4901             : /************************************************************************/
    4902             : 
    4903             : GDALInConstructionAlgorithmArg &
    4904       10351 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
    4905             :                                   bool bGDALGAllowed, const char *helpMessage)
    4906             : {
    4907             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
    4908             :                        MsgOrDefault(helpMessage,
    4909             :                                     bGDALGAllowed
    4910             :                                         ? _("Output format (\"GDALG\" allowed)")
    4911             :                                         : _("Output format")),
    4912       20702 :                        pValue)
    4913       20702 :                     .AddAlias("of")
    4914       10351 :                     .AddAlias("format");
    4915             :     arg.AddValidationAction(
    4916        5271 :         [this, &arg, bStreamAllowed, bGDALGAllowed]()
    4917       15622 :         { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
    4918             :     arg.SetAutoCompleteFunction(
    4919           4 :         [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
    4920             :         {
    4921             :             return FormatAutoCompleteFunction(arg, bStreamAllowed,
    4922           4 :                                               bGDALGAllowed);
    4923       10351 :         });
    4924       10351 :     return arg;
    4925             : }
    4926             : 
    4927             : /************************************************************************/
    4928             : /*                GDALAlgorithm::AddOutputDataTypeArg()                 */
    4929             : /************************************************************************/
    4930             : GDALInConstructionAlgorithmArg &
    4931        1863 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
    4932             :                                     const char *helpMessage)
    4933             : {
    4934             :     auto &arg =
    4935             :         AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
    4936        3726 :                MsgOrDefault(helpMessage, _("Output data type")), pValue)
    4937        3726 :             .AddAlias("ot")
    4938        3726 :             .AddAlias("datatype")
    4939        5589 :             .AddMetadataItem("type", {"GDALDataType"})
    4940             :             .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
    4941             :                         "UInt64", "Int64", "CInt16", "CInt32", "Float16",
    4942        1863 :                         "Float32", "Float64", "CFloat32", "CFloat64")
    4943        1863 :             .SetHiddenChoices("Byte");
    4944        1863 :     return arg;
    4945             : }
    4946             : 
    4947             : /************************************************************************/
    4948             : /*                    GDALAlgorithm::AddNodataArg()                     */
    4949             : /************************************************************************/
    4950             : 
    4951             : GDALInConstructionAlgorithmArg &
    4952         809 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
    4953             :                             const std::string &optionName,
    4954             :                             const char *helpMessage)
    4955             : {
    4956             :     auto &arg = AddArg(
    4957             :         optionName, 0,
    4958             :         MsgOrDefault(helpMessage,
    4959             :                      noneAllowed
    4960             :                          ? _("Assign a specified nodata value to output bands "
    4961             :                              "('none', numeric value, 'nan', 'inf', '-inf')")
    4962             :                          : _("Assign a specified nodata value to output bands "
    4963             :                              "(numeric value, 'nan', 'inf', '-inf')")),
    4964         809 :         pValue);
    4965             :     arg.AddValidationAction(
    4966         566 :         [this, pValue, noneAllowed, optionName]()
    4967             :         {
    4968         119 :             if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
    4969             :             {
    4970         109 :                 char *endptr = nullptr;
    4971         109 :                 CPLStrtod(pValue->c_str(), &endptr);
    4972         109 :                 if (endptr != pValue->c_str() + pValue->size())
    4973             :                 {
    4974           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    4975             :                                 "Value of '%s' should be %sa "
    4976             :                                 "numeric value, 'nan', 'inf' or '-inf'",
    4977             :                                 optionName.c_str(),
    4978             :                                 noneAllowed ? "'none', " : "");
    4979           1 :                     return false;
    4980             :                 }
    4981             :             }
    4982         118 :             return true;
    4983         809 :         });
    4984         809 :     return arg;
    4985             : }
    4986             : 
    4987             : /************************************************************************/
    4988             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    4989             : /************************************************************************/
    4990             : 
    4991             : GDALInConstructionAlgorithmArg &
    4992        7131 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
    4993             : {
    4994             :     return AddArg(
    4995             :                GDAL_ARG_NAME_OUTPUT_STRING, 0,
    4996             :                MsgOrDefault(helpMessage,
    4997             :                             _("Output string, in which the result is placed")),
    4998       14262 :                pValue)
    4999        7131 :         .SetHiddenForCLI()
    5000        7131 :         .SetIsInput(false)
    5001       14262 :         .SetIsOutput(true);
    5002             : }
    5003             : 
    5004             : /************************************************************************/
    5005             : /*                    GDALAlgorithm::AddStdoutArg()                     */
    5006             : /************************************************************************/
    5007             : 
    5008             : GDALInConstructionAlgorithmArg &
    5009        1687 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
    5010             : {
    5011             :     return AddArg(GDAL_ARG_NAME_STDOUT, 0,
    5012             :                   MsgOrDefault(helpMessage,
    5013             :                                _("Directly output on stdout. If enabled, "
    5014             :                                  "output-string will be empty")),
    5015        3374 :                   pValue)
    5016        3374 :         .SetHidden();
    5017             : }
    5018             : 
    5019             : /************************************************************************/
    5020             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    5021             : /************************************************************************/
    5022             : 
    5023             : GDALInConstructionAlgorithmArg &
    5024         222 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
    5025             : {
    5026             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    5027         222 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    5028             : }
    5029             : 
    5030             : /************************************************************************/
    5031             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    5032             : /************************************************************************/
    5033             : 
    5034             : GDALInConstructionAlgorithmArg &
    5035          69 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
    5036             : {
    5037             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
    5038         138 :                   pValue)
    5039           2 :         .SetAutoCompleteFunction([this](const std::string &)
    5040         140 :                                  { return AutoCompleteArrayName(); });
    5041             : }
    5042             : 
    5043             : /************************************************************************/
    5044             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    5045             : /************************************************************************/
    5046             : 
    5047             : GDALInConstructionAlgorithmArg &
    5048         136 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
    5049             :                                const char *helpMessage)
    5050             : {
    5051             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
    5052         272 :                   pValue)
    5053           0 :         .SetAutoCompleteFunction([this](const std::string &)
    5054         272 :                                  { return AutoCompleteArrayName(); });
    5055             : }
    5056             : 
    5057             : /************************************************************************/
    5058             : /*                GDALAlgorithm::AutoCompleteArrayName()                */
    5059             : /************************************************************************/
    5060             : 
    5061           2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
    5062             : {
    5063           2 :     std::vector<std::string> ret;
    5064           4 :     std::string osDSName;
    5065           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    5066           2 :     if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    5067             :     {
    5068           2 :         auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    5069           2 :         if (!inputDatasets.empty())
    5070             :         {
    5071           2 :             osDSName = inputDatasets[0].GetName();
    5072             :         }
    5073             :     }
    5074           0 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET)
    5075             :     {
    5076           0 :         auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
    5077           0 :         osDSName = inputDataset.GetName();
    5078             :     }
    5079             : 
    5080           2 :     if (!osDSName.empty())
    5081             :     {
    5082           4 :         CPLStringList aosAllowedDrivers;
    5083           2 :         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    5084           2 :         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    5085             :             aosAllowedDrivers =
    5086           2 :                 CPLStringList(ifArg->Get<std::vector<std::string>>());
    5087             : 
    5088           4 :         CPLStringList aosOpenOptions;
    5089           2 :         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    5090           2 :         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    5091             :             aosOpenOptions =
    5092           2 :                 CPLStringList(ooArg->Get<std::vector<std::string>>());
    5093             : 
    5094           2 :         if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5095             :                 osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
    5096           4 :                 aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
    5097             :         {
    5098           2 :             if (auto poRG = poDS->GetRootGroup())
    5099             :             {
    5100           1 :                 ret = poRG->GetMDArrayFullNamesRecursive();
    5101             :             }
    5102             :         }
    5103             :     }
    5104             : 
    5105           4 :     return ret;
    5106             : }
    5107             : 
    5108             : /************************************************************************/
    5109             : /*                  GDALAlgorithm::AddMemorySizeArg()                   */
    5110             : /************************************************************************/
    5111             : 
    5112             : GDALInConstructionAlgorithmArg &
    5113         227 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
    5114             :                                 const std::string &optionName,
    5115             :                                 const char *helpMessage)
    5116             : {
    5117         454 :     return AddArg(optionName, 0, helpMessage, pStrValue)
    5118         227 :         .SetDefault(*pStrValue)
    5119             :         .AddValidationAction(
    5120         139 :             [this, pValue, pStrValue]()
    5121             :             {
    5122          47 :                 CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
    5123             :                 GIntBig nBytes;
    5124             :                 bool bUnitSpecified;
    5125          47 :                 if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
    5126          47 :                                        &bUnitSpecified) != CE_None)
    5127             :                 {
    5128           2 :                     return false;
    5129             :                 }
    5130          45 :                 if (!bUnitSpecified)
    5131             :                 {
    5132           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5133             :                                 "Memory size must have a unit or be a "
    5134             :                                 "percentage of usable RAM (2GB, 5%%, etc.)");
    5135           1 :                     return false;
    5136             :                 }
    5137             :                 if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
    5138             :                 {
    5139             :                     // -1 to please CoverityScan
    5140             :                     if (static_cast<std::uint64_t>(nBytes) >
    5141             :                         std::numeric_limits<size_t>::max() - 1U)
    5142             :                     {
    5143             :                         ReportError(CE_Failure, CPLE_AppDefined,
    5144             :                                     "Memory size %s is too large.",
    5145             :                                     pStrValue->c_str());
    5146             :                         return false;
    5147             :                     }
    5148             :                 }
    5149             : 
    5150          44 :                 *pValue = static_cast<size_t>(nBytes);
    5151          44 :                 return true;
    5152         454 :             });
    5153             : }
    5154             : 
    5155             : /************************************************************************/
    5156             : /*                GDALAlgorithm::AddOutputLayerNameArg()                */
    5157             : /************************************************************************/
    5158             : 
    5159             : GDALInConstructionAlgorithmArg &
    5160         404 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
    5161             :                                      const char *helpMessage)
    5162             : {
    5163             :     return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
    5164         404 :                   MsgOrDefault(helpMessage, _("Output layer name")), pValue);
    5165             : }
    5166             : 
    5167             : /************************************************************************/
    5168             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    5169             : /************************************************************************/
    5170             : 
    5171             : GDALInConstructionAlgorithmArg &
    5172         910 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
    5173             :                                const char *helpMessage)
    5174             : {
    5175             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    5176         910 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    5177             : }
    5178             : 
    5179             : /************************************************************************/
    5180             : /*                 GDALAlgorithm::AddGeometryTypeArg()                  */
    5181             : /************************************************************************/
    5182             : 
    5183             : GDALInConstructionAlgorithmArg &
    5184         489 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
    5185             : {
    5186             :     return AddArg("geometry-type", 0,
    5187         978 :                   MsgOrDefault(helpMessage, _("Geometry type")), pValue)
    5188             :         .SetAutoCompleteFunction(
    5189           3 :             [](const std::string &currentValue)
    5190             :             {
    5191           3 :                 std::vector<std::string> oRet;
    5192          51 :                 for (const char *type :
    5193             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
    5194             :                       "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
    5195             :                       "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
    5196             :                       "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
    5197          54 :                       "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
    5198             :                 {
    5199          68 :                     if (currentValue.empty() ||
    5200          17 :                         STARTS_WITH(type, currentValue.c_str()))
    5201             :                     {
    5202          35 :                         oRet.push_back(type);
    5203          35 :                         oRet.push_back(std::string(type).append("Z"));
    5204          35 :                         oRet.push_back(std::string(type).append("M"));
    5205          35 :                         oRet.push_back(std::string(type).append("ZM"));
    5206             :                     }
    5207             :                 }
    5208           3 :                 return oRet;
    5209         978 :             })
    5210             :         .AddValidationAction(
    5211         123 :             [this, pValue]()
    5212             :             {
    5213         112 :                 if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
    5214         120 :                         wkbUnknown &&
    5215           8 :                     !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
    5216             :                 {
    5217           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5218             :                                 "Invalid geometry type '%s'", pValue->c_str());
    5219           3 :                     return false;
    5220             :                 }
    5221         109 :                 return true;
    5222         978 :             });
    5223             : }
    5224             : 
    5225             : /************************************************************************/
    5226             : /*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
    5227             : /************************************************************************/
    5228             : 
    5229             : /* static */
    5230        3402 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
    5231             :     GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
    5232             : {
    5233        3402 :     CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
    5234             :               datasetArg.GetType() == GAAT_DATASET_LIST);
    5235             : 
    5236             :     layerArg.SetAutoCompleteFunction(
    5237          18 :         [&datasetArg](const std::string &currentValue)
    5238             :         {
    5239           6 :             std::vector<std::string> ret;
    5240          12 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5241           6 :             GDALArgDatasetValue *dsVal = nullptr;
    5242           6 :             if (datasetArg.GetType() == GAAT_DATASET)
    5243             :             {
    5244           0 :                 dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
    5245             :             }
    5246             :             else
    5247             :             {
    5248           6 :                 auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
    5249           6 :                 if (val.size() == 1)
    5250             :                 {
    5251           6 :                     dsVal = &val[0];
    5252             :                 }
    5253             :             }
    5254           6 :             if (dsVal && !dsVal->GetName().empty())
    5255             :             {
    5256             :                 auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5257          12 :                     dsVal->GetName().c_str(), GDAL_OF_VECTOR));
    5258           6 :                 if (poDS)
    5259             :                 {
    5260          12 :                     for (auto &&poLayer : poDS->GetLayers())
    5261             :                     {
    5262           6 :                         if (currentValue == poLayer->GetDescription())
    5263             :                         {
    5264           1 :                             ret.clear();
    5265           1 :                             ret.push_back(poLayer->GetDescription());
    5266           1 :                             break;
    5267             :                         }
    5268           5 :                         ret.push_back(poLayer->GetDescription());
    5269             :                     }
    5270             :                 }
    5271             :             }
    5272          12 :             return ret;
    5273        3402 :         });
    5274        3402 : }
    5275             : 
    5276             : /************************************************************************/
    5277             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
    5278             : /************************************************************************/
    5279             : 
    5280         804 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
    5281             :     GDALInConstructionAlgorithmArg &fieldArg,
    5282             :     const GDALAlgorithmArg *layerNameArg, bool attributeFields,
    5283             :     bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg,
    5284             :     const std::vector<std::string> &extraValues,
    5285             :     std::function<bool(const OGRFieldDefn *)> filterFn)
    5286             : {
    5287             : 
    5288             :     fieldArg.SetAutoCompleteFunction(
    5289          11 :         [&datasetArg, layerNameArg, attributeFields, geometryFields,
    5290             :          extraValues,
    5291         804 :          filterFn = std::move(filterFn)](const std::string &currentValue)
    5292             :         {
    5293          22 :             std::set<std::string> ret{};
    5294          11 :             if (!datasetArg.empty())
    5295             :             {
    5296          18 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5297             : 
    5298             :                 const auto getLayerFields =
    5299           7 :                     [&ret, &currentValue, attributeFields, geometryFields,
    5300          87 :                      &extraValues, &filterFn](const OGRLayer *poLayer)
    5301             :                 {
    5302           7 :                     const auto poDefn = poLayer->GetLayerDefn();
    5303           7 :                     if (attributeFields)
    5304             :                     {
    5305          27 :                         for (const auto poFieldDefn : poDefn->GetFields())
    5306             :                         {
    5307          20 :                             if (filterFn && !filterFn(poFieldDefn))
    5308             :                             {
    5309           1 :                                 continue;
    5310             :                             }
    5311             : 
    5312          19 :                             const char *fieldName = poFieldDefn->GetNameRef();
    5313             : 
    5314          19 :                             if (currentValue == fieldName)
    5315             :                             {
    5316           0 :                                 ret.clear();
    5317           0 :                                 ret.insert(fieldName);
    5318           0 :                                 break;
    5319             :                             }
    5320          19 :                             ret.insert(fieldName);
    5321             :                         }
    5322             :                     }
    5323           7 :                     if (geometryFields)
    5324             :                     {
    5325           2 :                         for (const auto poFieldDefn : poDefn->GetGeomFields())
    5326             :                         {
    5327           1 :                             const char *fieldName = poFieldDefn->GetNameRef();
    5328           1 :                             if (fieldName[0] == 0)
    5329           1 :                                 fieldName = OGR_GEOMETRY_DEFAULT_NON_EMPTY_NAME;
    5330           1 :                             if (currentValue == fieldName)
    5331             :                             {
    5332           0 :                                 ret.clear();
    5333           0 :                                 ret.insert(fieldName);
    5334           0 :                                 break;
    5335             :                             }
    5336           1 :                             ret.insert(fieldName);
    5337             :                         }
    5338             :                     }
    5339           8 :                     for (const auto &value : extraValues)
    5340             :                     {
    5341           1 :                         if (currentValue == value)
    5342             :                         {
    5343           0 :                             ret.clear();
    5344           0 :                             ret.insert(value);
    5345           0 :                             break;
    5346             :                         }
    5347           1 :                         ret.insert(value);
    5348             :                     }
    5349           7 :                 };
    5350             : 
    5351           9 :                 const GDALArgDatasetValue &dsVal = datasetArg[0];
    5352             : 
    5353           9 :                 if (!dsVal.GetName().empty())
    5354             :                 {
    5355             :                     auto poDS = std::unique_ptr<GDALDataset>(
    5356           9 :                         GDALDataset::Open(dsVal.GetName().c_str(),
    5357          18 :                                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
    5358           9 :                     if (poDS)
    5359             :                     {
    5360          18 :                         std::vector<std::string> layerNames;
    5361           9 :                         if (layerNameArg && layerNameArg->IsExplicitlySet())
    5362             :                         {
    5363           4 :                             if (layerNameArg->GetType() == GAAT_STRING_LIST)
    5364             :                             {
    5365             :                                 layerNames =
    5366             :                                     layerNameArg
    5367           2 :                                         ->Get<std::vector<std::string>>();
    5368             :                             }
    5369           2 :                             else if (layerNameArg->GetType() == GAAT_STRING)
    5370             :                             {
    5371           2 :                                 layerNames.push_back(
    5372           2 :                                     layerNameArg->Get<std::string>());
    5373             :                             }
    5374             :                         }
    5375           9 :                         if (layerNames.empty())
    5376             :                         {
    5377             :                             // Loop through all layers
    5378          10 :                             for (const auto *poLayer : poDS->GetLayers())
    5379             :                             {
    5380           5 :                                 getLayerFields(poLayer);
    5381             :                             }
    5382             :                         }
    5383             :                         else
    5384             :                         {
    5385           8 :                             for (const std::string &layerName : layerNames)
    5386             :                             {
    5387             :                                 const auto poLayer =
    5388           4 :                                     poDS->GetLayerByName(layerName.c_str());
    5389           4 :                                 if (poLayer)
    5390             :                                 {
    5391           2 :                                     getLayerFields(poLayer);
    5392             :                                 }
    5393             :                             }
    5394             :                         }
    5395             :                     }
    5396             :                 }
    5397             :             }
    5398          11 :             std::vector<std::string> retVector(ret.begin(), ret.end());
    5399          22 :             return retVector;
    5400        1608 :         });
    5401         804 : }
    5402             : 
    5403             : /************************************************************************/
    5404             : /*                   GDALAlgorithm::AddFieldNameArg()                   */
    5405             : /************************************************************************/
    5406             : 
    5407             : GDALInConstructionAlgorithmArg &
    5408         138 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
    5409             : {
    5410             :     return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
    5411         138 :                   pValue);
    5412             : }
    5413             : 
    5414             : /************************************************************************/
    5415             : /*                GDALAlgorithm::ParseFieldDefinition()                 */
    5416             : /************************************************************************/
    5417          67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
    5418             :                                          OGRFieldDefn *poFieldDefn,
    5419             :                                          std::string *posError)
    5420             : {
    5421             :     static const std::regex re(
    5422          67 :         R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
    5423         134 :     std::smatch match;
    5424          67 :     if (std::regex_match(posStrDef, match, re))
    5425             :     {
    5426         132 :         const std::string name = match[1];
    5427         132 :         const std::string type = match[2];
    5428          66 :         const int width = match[3].matched ? std::stoi(match[3]) : 0;
    5429          66 :         const int precision = match[4].matched ? std::stoi(match[4]) : 0;
    5430          66 :         poFieldDefn->SetName(name.c_str());
    5431             : 
    5432          66 :         const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
    5433          66 :         if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
    5434             :         {
    5435           1 :             if (posError)
    5436           1 :                 *posError = "Unsupported field type: " + type;
    5437             : 
    5438           1 :             return false;
    5439             :         }
    5440          65 :         poFieldDefn->SetType(typeEnum);
    5441          65 :         poFieldDefn->SetWidth(width);
    5442          65 :         poFieldDefn->SetPrecision(precision);
    5443          65 :         return true;
    5444             :     }
    5445             : 
    5446           1 :     if (posError)
    5447             :         *posError = "Invalid field definition format. Expected "
    5448           1 :                     "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
    5449             : 
    5450           1 :     return false;
    5451             : }
    5452             : 
    5453             : /************************************************************************/
    5454             : /*                GDALAlgorithm::AddFieldDefinitionArg()                */
    5455             : /************************************************************************/
    5456             : 
    5457             : GDALInConstructionAlgorithmArg &
    5458         132 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
    5459             :                                      std::vector<OGRFieldDefn> *pFieldDefns,
    5460             :                                      const char *helpMessage)
    5461             : {
    5462             :     auto &arg =
    5463             :         AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
    5464         264 :                pValues)
    5465         264 :             .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
    5466         132 :             .SetPackedValuesAllowed(true)
    5467         132 :             .SetRepeatedArgAllowed(true);
    5468             : 
    5469         132 :     auto validationFunction = [this, pFieldDefns, pValues]()
    5470             :     {
    5471          65 :         pFieldDefns->clear();
    5472         130 :         for (const auto &strValue : *pValues)
    5473             :         {
    5474          67 :             OGRFieldDefn fieldDefn("", OFTString);
    5475          67 :             std::string error;
    5476          67 :             if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
    5477             :                                                      &error))
    5478             :             {
    5479           2 :                 ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
    5480           2 :                 return false;
    5481             :             }
    5482             :             // Check uniqueness of field names
    5483          67 :             for (const auto &existingFieldDefn : *pFieldDefns)
    5484             :             {
    5485           2 :                 if (EQUAL(existingFieldDefn.GetNameRef(),
    5486             :                           fieldDefn.GetNameRef()))
    5487             :                 {
    5488           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5489             :                                 "Duplicate field name: '%s'",
    5490             :                                 fieldDefn.GetNameRef());
    5491           0 :                     return false;
    5492             :                 }
    5493             :             }
    5494          65 :             pFieldDefns->push_back(fieldDefn);
    5495             :         }
    5496          63 :         return true;
    5497         132 :     };
    5498             : 
    5499         132 :     arg.AddValidationAction(std::move(validationFunction));
    5500             : 
    5501         132 :     return arg;
    5502             : }
    5503             : 
    5504             : /************************************************************************/
    5505             : /*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
    5506             : /************************************************************************/
    5507             : 
    5508         276 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
    5509             :     OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
    5510             :     std::string *pStrValue, const std::string &argName, const char *helpMessage)
    5511             : {
    5512             :     auto &arg =
    5513         552 :         AddArg(argName.empty() ? std::string("field-type") : argName, 0,
    5514         828 :                MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
    5515             :             .SetAutoCompleteFunction(
    5516           1 :                 [](const std::string &currentValue)
    5517             :                 {
    5518           1 :                     std::vector<std::string> oRet;
    5519           6 :                     for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
    5520             :                     {
    5521             :                         const char *pszSubType =
    5522           5 :                             OGRFieldDefn::GetFieldSubTypeName(
    5523             :                                 static_cast<OGRFieldSubType>(i));
    5524           5 :                         if (pszSubType != nullptr)
    5525             :                         {
    5526           5 :                             if (currentValue.empty() ||
    5527           0 :                                 STARTS_WITH(pszSubType, currentValue.c_str()))
    5528             :                             {
    5529           5 :                                 oRet.push_back(pszSubType);
    5530             :                             }
    5531             :                         }
    5532             :                     }
    5533             : 
    5534          15 :                     for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
    5535             :                     {
    5536             :                         // Skip deprecated
    5537          14 :                         if (static_cast<OGRFieldType>(i) ==
    5538          13 :                                 OGRFieldType::OFTWideString ||
    5539             :                             static_cast<OGRFieldType>(i) ==
    5540             :                                 OGRFieldType::OFTWideStringList)
    5541           2 :                             continue;
    5542          12 :                         const char *pszType = OGRFieldDefn::GetFieldTypeName(
    5543             :                             static_cast<OGRFieldType>(i));
    5544          12 :                         if (pszType != nullptr)
    5545             :                         {
    5546          12 :                             if (currentValue.empty() ||
    5547           0 :                                 STARTS_WITH(pszType, currentValue.c_str()))
    5548             :                             {
    5549          12 :                                 oRet.push_back(pszType);
    5550             :                             }
    5551             :                         }
    5552             :                     }
    5553           1 :                     return oRet;
    5554         276 :                 });
    5555             : 
    5556             :     auto validationFunction =
    5557         845 :         [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
    5558             :     {
    5559         120 :         bool isValid{true};
    5560         120 :         *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
    5561             : 
    5562             :         // String is returned for unknown types
    5563         120 :         if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
    5564             :         {
    5565          16 :             isValid = false;
    5566             :         }
    5567             : 
    5568         120 :         *pSubtypeValue =
    5569         120 :             OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
    5570             : 
    5571         120 :         if (*pSubtypeValue != OFSTNone)
    5572             :         {
    5573          15 :             isValid = true;
    5574          15 :             switch (*pSubtypeValue)
    5575             :             {
    5576           6 :                 case OFSTBoolean:
    5577             :                 case OFSTInt16:
    5578             :                 {
    5579           6 :                     *pTypeValue = OFTInteger;
    5580           6 :                     break;
    5581             :                 }
    5582           3 :                 case OFSTFloat32:
    5583             :                 {
    5584           3 :                     *pTypeValue = OFTReal;
    5585           3 :                     break;
    5586             :                 }
    5587           6 :                 default:
    5588             :                 {
    5589           6 :                     *pTypeValue = OFTString;
    5590           6 :                     break;
    5591             :                 }
    5592             :             }
    5593             :         }
    5594             : 
    5595         120 :         if (!isValid)
    5596             :         {
    5597           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    5598             :                         "Invalid value for argument '%s': '%s'",
    5599           1 :                         arg.GetName().c_str(), pStrValue->c_str());
    5600             :         }
    5601             : 
    5602         120 :         return isValid;
    5603         276 :     };
    5604             : 
    5605         276 :     if (!pStrValue->empty())
    5606             :     {
    5607           0 :         arg.SetDefault(*pStrValue);
    5608           0 :         validationFunction();
    5609             :     }
    5610             : 
    5611         276 :     arg.AddValidationAction(std::move(validationFunction));
    5612             : 
    5613         276 :     return arg;
    5614             : }
    5615             : 
    5616             : /************************************************************************/
    5617             : /*                   GDALAlgorithm::ValidateBandArg()                   */
    5618             : /************************************************************************/
    5619             : 
    5620        4920 : bool GDALAlgorithm::ValidateBandArg() const
    5621             : {
    5622        4920 :     bool ret = true;
    5623        4920 :     const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
    5624        4920 :     const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
    5625        1768 :     if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
    5626         344 :         (inputDatasetArg->GetType() == GAAT_DATASET ||
    5627        6682 :          inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
    5628         175 :         (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
    5629             :     {
    5630         104 :         const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
    5631             :         {
    5632          99 :             if (nBand > poDS->GetRasterCount())
    5633             :             {
    5634           5 :                 ReportError(CE_Failure, CPLE_AppDefined,
    5635             :                             "Value of 'band' should be greater or equal than "
    5636             :                             "1 and less or equal than %d.",
    5637             :                             poDS->GetRasterCount());
    5638           5 :                 return false;
    5639             :             }
    5640          94 :             return true;
    5641         118 :         };
    5642             : 
    5643             :         const auto ValidateForOneDataset =
    5644         356 :             [&bandArg, &CheckBand](const GDALDataset *poDS)
    5645             :         {
    5646         113 :             bool l_ret = true;
    5647         113 :             if (bandArg->GetType() == GAAT_INTEGER)
    5648             :             {
    5649          24 :                 l_ret = CheckBand(poDS, bandArg->Get<int>());
    5650             :             }
    5651          89 :             else if (bandArg->GetType() == GAAT_INTEGER_LIST)
    5652             :             {
    5653         130 :                 for (int nBand : bandArg->Get<std::vector<int>>())
    5654             :                 {
    5655          75 :                     l_ret = l_ret && CheckBand(poDS, nBand);
    5656             :                 }
    5657             :             }
    5658         113 :             return l_ret;
    5659         118 :         };
    5660             : 
    5661         118 :         if (inputDatasetArg->GetType() == GAAT_DATASET)
    5662             :         {
    5663             :             auto poDS =
    5664           6 :                 inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
    5665           6 :             if (poDS && !ValidateForOneDataset(poDS))
    5666           2 :                 ret = false;
    5667             :         }
    5668             :         else
    5669             :         {
    5670         112 :             CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
    5671         111 :             for (auto &datasetValue :
    5672         334 :                  inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
    5673             :             {
    5674         111 :                 auto poDS = datasetValue.GetDatasetRef();
    5675         111 :                 if (poDS && !ValidateForOneDataset(poDS))
    5676           3 :                     ret = false;
    5677             :             }
    5678             :         }
    5679             :     }
    5680        4920 :     return ret;
    5681             : }
    5682             : 
    5683             : /************************************************************************/
    5684             : /*            GDALAlgorithm::RunPreStepPipelineValidations()            */
    5685             : /************************************************************************/
    5686             : 
    5687        3955 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
    5688             : {
    5689        3955 :     return ValidateBandArg();
    5690             : }
    5691             : 
    5692             : /************************************************************************/
    5693             : /*                     GDALAlgorithm::AddBandArg()                      */
    5694             : /************************************************************************/
    5695             : 
    5696             : GDALInConstructionAlgorithmArg &
    5697        1728 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
    5698             : {
    5699        2202 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5700             : 
    5701             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5702             :                   MsgOrDefault(helpMessage, _("Input band (1-based index)")),
    5703        3456 :                   pValue)
    5704             :         .AddValidationAction(
    5705          34 :             [pValue]()
    5706             :             {
    5707          34 :                 if (*pValue <= 0)
    5708             :                 {
    5709           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5710             :                              "Value of 'band' should greater or equal to 1.");
    5711           1 :                     return false;
    5712             :                 }
    5713          33 :                 return true;
    5714        3456 :             });
    5715             : }
    5716             : 
    5717             : /************************************************************************/
    5718             : /*                     GDALAlgorithm::AddBandArg()                      */
    5719             : /************************************************************************/
    5720             : 
    5721             : GDALInConstructionAlgorithmArg &
    5722         879 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
    5723             : {
    5724        1370 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5725             : 
    5726             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5727             :                   MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
    5728        1758 :                   pValue)
    5729             :         .AddValidationAction(
    5730         126 :             [pValue]()
    5731             :             {
    5732         397 :                 for (int val : *pValue)
    5733             :                 {
    5734         272 :                     if (val <= 0)
    5735             :                     {
    5736           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    5737             :                                  "Value of 'band' should greater or equal "
    5738             :                                  "to 1.");
    5739           1 :                         return false;
    5740             :                     }
    5741             :                 }
    5742         125 :                 return true;
    5743        1758 :             });
    5744             : }
    5745             : 
    5746             : /************************************************************************/
    5747             : /*                      ParseAndValidateKeyValue()                      */
    5748             : /************************************************************************/
    5749             : 
    5750         589 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
    5751             : {
    5752         555 :     const auto Validate = [this, &arg](const std::string &val)
    5753             :     {
    5754         550 :         if (val.find('=') == std::string::npos)
    5755             :         {
    5756           5 :             ReportError(
    5757             :                 CE_Failure, CPLE_AppDefined,
    5758             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    5759           5 :                 arg.GetName().c_str());
    5760           5 :             return false;
    5761             :         }
    5762             : 
    5763         545 :         return true;
    5764         589 :     };
    5765             : 
    5766         589 :     if (arg.GetType() == GAAT_STRING)
    5767             :     {
    5768           0 :         return Validate(arg.Get<std::string>());
    5769             :     }
    5770         589 :     else if (arg.GetType() == GAAT_STRING_LIST)
    5771             :     {
    5772         589 :         std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
    5773         589 :         if (vals.size() == 1)
    5774             :         {
    5775             :             // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
    5776         968 :             std::vector<std::string> newVals;
    5777         968 :             std::string curToken;
    5778         484 :             bool canSplitOnComma = true;
    5779         484 :             char lastSep = 0;
    5780         484 :             bool inString = false;
    5781         484 :             bool equalFoundInLastToken = false;
    5782        7315 :             for (char c : vals[0])
    5783             :             {
    5784        6835 :                 if (!inString && c == ',')
    5785             :                 {
    5786          10 :                     if (lastSep != '=' || !equalFoundInLastToken)
    5787             :                     {
    5788           2 :                         canSplitOnComma = false;
    5789           2 :                         break;
    5790             :                     }
    5791           8 :                     lastSep = c;
    5792           8 :                     newVals.push_back(curToken);
    5793           8 :                     curToken.clear();
    5794           8 :                     equalFoundInLastToken = false;
    5795             :                 }
    5796        6825 :                 else if (!inString && c == '=')
    5797             :                 {
    5798         483 :                     if (lastSep == '=')
    5799             :                     {
    5800           2 :                         canSplitOnComma = false;
    5801           2 :                         break;
    5802             :                     }
    5803         481 :                     equalFoundInLastToken = true;
    5804         481 :                     lastSep = c;
    5805         481 :                     curToken += c;
    5806             :                 }
    5807        6342 :                 else if (c == '"')
    5808             :                 {
    5809           4 :                     inString = !inString;
    5810           4 :                     curToken += c;
    5811             :                 }
    5812             :                 else
    5813             :                 {
    5814        6338 :                     curToken += c;
    5815             :                 }
    5816             :             }
    5817         484 :             if (canSplitOnComma && !inString && equalFoundInLastToken)
    5818             :             {
    5819         471 :                 if (!curToken.empty())
    5820         471 :                     newVals.emplace_back(std::move(curToken));
    5821         471 :                 vals = std::move(newVals);
    5822             :             }
    5823             :         }
    5824             : 
    5825        1134 :         for (const auto &val : vals)
    5826             :         {
    5827         550 :             if (!Validate(val))
    5828           5 :                 return false;
    5829             :         }
    5830             :     }
    5831             : 
    5832         584 :     return true;
    5833             : }
    5834             : 
    5835             : /************************************************************************/
    5836             : /*                           IsGDALGOutput()                            */
    5837             : /************************************************************************/
    5838             : 
    5839        2675 : bool GDALAlgorithm::IsGDALGOutput() const
    5840             : {
    5841        2675 :     bool isGDALGOutput = false;
    5842        2675 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5843        2675 :     const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5844        4620 :     if (outputArg && outputArg->GetType() == GAAT_DATASET &&
    5845        1945 :         outputArg->IsExplicitlySet())
    5846             :     {
    5847        3813 :         if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    5848        1894 :             outputFormatArg->IsExplicitlySet())
    5849             :         {
    5850             :             const auto &val =
    5851        1186 :                 outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    5852        1186 :             isGDALGOutput = EQUAL(val.c_str(), "GDALG");
    5853             :         }
    5854             :         else
    5855             :         {
    5856             :             const auto &filename =
    5857         733 :                 outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
    5858         733 :             isGDALGOutput =
    5859        1437 :                 filename.GetName().size() > strlen(".gdalg.json") &&
    5860         704 :                 EQUAL(filename.GetName().c_str() + filename.GetName().size() -
    5861             :                           strlen(".gdalg.json"),
    5862             :                       ".gdalg.json");
    5863             :         }
    5864             :     }
    5865        2675 :     return isGDALGOutput;
    5866             : }
    5867             : 
    5868             : /************************************************************************/
    5869             : /*                         ProcessGDALGOutput()                         */
    5870             : /************************************************************************/
    5871             : 
    5872        2797 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
    5873             : {
    5874        2797 :     if (!SupportsStreamedOutput())
    5875         732 :         return ProcessGDALGOutputRet::NOT_GDALG;
    5876             : 
    5877        2065 :     if (IsGDALGOutput())
    5878             :     {
    5879          12 :         const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5880             :         const auto &filename =
    5881          12 :             outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
    5882             :         VSIStatBufL sStat;
    5883          12 :         if (VSIStatL(filename.c_str(), &sStat) == 0)
    5884             :         {
    5885           0 :             const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
    5886           0 :             if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
    5887             :             {
    5888           0 :                 if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
    5889             :                 {
    5890           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5891             :                              "File '%s' already exists. Specify the "
    5892             :                              "--overwrite option to overwrite it.",
    5893             :                              filename.c_str());
    5894           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5895             :                 }
    5896             :             }
    5897             :         }
    5898             : 
    5899          24 :         std::string osCommandLine;
    5900             : 
    5901          48 :         for (const auto &path : GDALAlgorithm::m_callPath)
    5902             :         {
    5903          36 :             if (!osCommandLine.empty())
    5904          24 :                 osCommandLine += ' ';
    5905          36 :             osCommandLine += path;
    5906             :         }
    5907             : 
    5908         278 :         for (const auto &arg : GetArgs())
    5909             :         {
    5910         296 :             if (arg->IsExplicitlySet() &&
    5911          48 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
    5912          35 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
    5913         313 :                 arg->GetName() != GDAL_ARG_NAME_UPDATE &&
    5914          17 :                 arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
    5915             :             {
    5916          16 :                 osCommandLine += ' ';
    5917          16 :                 std::string strArg;
    5918          16 :                 if (!arg->Serialize(strArg))
    5919             :                 {
    5920           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5921             :                              "Cannot serialize argument %s",
    5922           0 :                              arg->GetName().c_str());
    5923           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5924             :                 }
    5925          16 :                 osCommandLine += strArg;
    5926             :             }
    5927             :         }
    5928             : 
    5929          12 :         osCommandLine += " --output-format stream --output streamed_dataset";
    5930             : 
    5931          12 :         std::string outStringUnused;
    5932          12 :         return SaveGDALG(filename, outStringUnused, osCommandLine)
    5933          12 :                    ? ProcessGDALGOutputRet::GDALG_OK
    5934          12 :                    : ProcessGDALGOutputRet::GDALG_ERROR;
    5935             :     }
    5936             : 
    5937        2053 :     return ProcessGDALGOutputRet::NOT_GDALG;
    5938             : }
    5939             : 
    5940             : /************************************************************************/
    5941             : /*                      GDALAlgorithm::SaveGDALG()                      */
    5942             : /************************************************************************/
    5943             : 
    5944          24 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
    5945             :                                            std::string &outString,
    5946             :                                            const std::string &commandLine)
    5947             : {
    5948          48 :     CPLJSONDocument oDoc;
    5949          24 :     oDoc.GetRoot().Add("type", "gdal_streamed_alg");
    5950          24 :     oDoc.GetRoot().Add("command_line", commandLine);
    5951          24 :     oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
    5952             : 
    5953          24 :     if (!filename.empty())
    5954          23 :         return oDoc.Save(filename);
    5955             : 
    5956           1 :     outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
    5957           1 :     return true;
    5958             : }
    5959             : 
    5960             : /************************************************************************/
    5961             : /*                GDALAlgorithm::AddCreationOptionsArg()                */
    5962             : /************************************************************************/
    5963             : 
    5964             : GDALInConstructionAlgorithmArg &
    5965        9027 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
    5966             :                                      const char *helpMessage)
    5967             : {
    5968             :     auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
    5969       18054 :                        MsgOrDefault(helpMessage, _("Creation option")), pValue)
    5970       18054 :                     .AddAlias("co")
    5971       18054 :                     .SetMetaVar("<KEY>=<VALUE>")
    5972        9027 :                     .SetPackedValuesAllowed(false);
    5973         292 :     arg.AddValidationAction([this, &arg]()
    5974        9319 :                             { return ParseAndValidateKeyValue(arg); });
    5975             : 
    5976             :     arg.SetAutoCompleteFunction(
    5977          51 :         [this](const std::string &currentValue)
    5978             :         {
    5979          17 :             std::vector<std::string> oRet;
    5980             : 
    5981          17 :             int datasetType =
    5982             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    5983          17 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5984          17 :             if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
    5985           0 :                               outputArg->GetType() == GAAT_DATASET_LIST))
    5986             :             {
    5987          17 :                 datasetType = outputArg->GetDatasetType();
    5988             :             }
    5989             : 
    5990          17 :             const char *pszMDCreationOptionList =
    5991             :                 (datasetType == GDAL_OF_MULTIDIM_RASTER)
    5992          17 :                     ? GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST
    5993             :                     : GDAL_DMD_CREATIONOPTIONLIST;
    5994             : 
    5995          17 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5996          34 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5997          17 :                 outputFormat->IsExplicitlySet())
    5998             :             {
    5999          14 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    6000           7 :                     outputFormat->Get<std::string>().c_str());
    6001           7 :                 if (poDriver)
    6002             :                 {
    6003           7 :                     AddOptionsSuggestions(
    6004           7 :                         poDriver->GetMetadataItem(pszMDCreationOptionList),
    6005             :                         datasetType, currentValue, oRet);
    6006             :                 }
    6007           7 :                 return oRet;
    6008             :             }
    6009             : 
    6010          10 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    6011             :             {
    6012          10 :                 auto poDM = GetGDALDriverManager();
    6013          10 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    6014          10 :                 const auto &osDSName = datasetValue.GetName();
    6015          10 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    6016          10 :                 if (!osExt.empty())
    6017             :                 {
    6018          10 :                     std::set<std::string> oVisitedExtensions;
    6019         721 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    6020             :                     {
    6021         718 :                         auto poDriver = poDM->GetDriver(i);
    6022        2154 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    6023         718 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    6024         216 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    6025        1436 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    6026         216 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    6027           0 :                              poDriver->GetMetadataItem(
    6028           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    6029             :                         {
    6030             :                             const char *pszExtensions =
    6031         502 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    6032         502 :                             if (pszExtensions)
    6033             :                             {
    6034             :                                 const CPLStringList aosExts(
    6035         326 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    6036         722 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    6037             :                                 {
    6038         422 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    6039          16 :                                         !cpl::contains(oVisitedExtensions,
    6040             :                                                        pszExt))
    6041             :                                     {
    6042          10 :                                         oVisitedExtensions.insert(pszExt);
    6043          10 :                                         if (AddOptionsSuggestions(
    6044             :                                                 poDriver->GetMetadataItem(
    6045          10 :                                                     pszMDCreationOptionList),
    6046             :                                                 datasetType, currentValue,
    6047             :                                                 oRet))
    6048             :                                         {
    6049           7 :                                             return oRet;
    6050             :                                         }
    6051           3 :                                         break;
    6052             :                                     }
    6053             :                                 }
    6054             :                             }
    6055             :                         }
    6056             :                     }
    6057             :                 }
    6058             :             }
    6059             : 
    6060           3 :             return oRet;
    6061        9027 :         });
    6062             : 
    6063        9027 :     return arg;
    6064             : }
    6065             : 
    6066             : /************************************************************************/
    6067             : /*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
    6068             : /************************************************************************/
    6069             : 
    6070             : GDALInConstructionAlgorithmArg &
    6071        4302 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
    6072             :                                           const char *helpMessage)
    6073             : {
    6074             :     auto &arg =
    6075             :         AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
    6076        8604 :                MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
    6077        8604 :             .AddAlias("lco")
    6078        8604 :             .SetMetaVar("<KEY>=<VALUE>")
    6079        4302 :             .SetPackedValuesAllowed(false);
    6080          76 :     arg.AddValidationAction([this, &arg]()
    6081        4378 :                             { return ParseAndValidateKeyValue(arg); });
    6082             : 
    6083             :     arg.SetAutoCompleteFunction(
    6084           5 :         [this](const std::string &currentValue)
    6085             :         {
    6086           2 :             std::vector<std::string> oRet;
    6087             : 
    6088           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6089           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    6090           2 :                 outputFormat->IsExplicitlySet())
    6091             :             {
    6092           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    6093           1 :                     outputFormat->Get<std::string>().c_str());
    6094           1 :                 if (poDriver)
    6095             :                 {
    6096           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    6097           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    6098             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    6099             :                 }
    6100           1 :                 return oRet;
    6101             :             }
    6102             : 
    6103           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    6104           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    6105             :             {
    6106           1 :                 auto poDM = GetGDALDriverManager();
    6107           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    6108           1 :                 const auto &osDSName = datasetValue.GetName();
    6109           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    6110           1 :                 if (!osExt.empty())
    6111             :                 {
    6112           1 :                     std::set<std::string> oVisitedExtensions;
    6113         231 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    6114             :                     {
    6115         230 :                         auto poDriver = poDM->GetDriver(i);
    6116         230 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    6117             :                         {
    6118             :                             const char *pszExtensions =
    6119          91 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    6120          91 :                             if (pszExtensions)
    6121             :                             {
    6122             :                                 const CPLStringList aosExts(
    6123          62 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    6124         156 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    6125             :                                 {
    6126          96 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    6127           1 :                                         !cpl::contains(oVisitedExtensions,
    6128             :                                                        pszExt))
    6129             :                                     {
    6130           1 :                                         oVisitedExtensions.insert(pszExt);
    6131           1 :                                         if (AddOptionsSuggestions(
    6132             :                                                 poDriver->GetMetadataItem(
    6133           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    6134             :                                                 GDAL_OF_VECTOR, currentValue,
    6135             :                                                 oRet))
    6136             :                                         {
    6137           0 :                                             return oRet;
    6138             :                                         }
    6139           1 :                                         break;
    6140             :                                     }
    6141             :                                 }
    6142             :                             }
    6143             :                         }
    6144             :                     }
    6145             :                 }
    6146             :             }
    6147             : 
    6148           1 :             return oRet;
    6149        4302 :         });
    6150             : 
    6151        4302 :     return arg;
    6152             : }
    6153             : 
    6154             : /************************************************************************/
    6155             : /*                     GDALAlgorithm::AddBBOXArg()                      */
    6156             : /************************************************************************/
    6157             : 
    6158             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    6159             : GDALInConstructionAlgorithmArg &
    6160        1985 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    6161             : {
    6162             :     auto &arg = AddArg("bbox", 0,
    6163             :                        MsgOrDefault(helpMessage,
    6164             :                                     _("Bounding box as xmin,ymin,xmax,ymax")),
    6165        3970 :                        pValue)
    6166        1985 :                     .SetRepeatedArgAllowed(false)
    6167        1985 :                     .SetMinCount(4)
    6168        1985 :                     .SetMaxCount(4)
    6169        1985 :                     .SetDisplayHintAboutRepetition(false);
    6170             :     arg.AddValidationAction(
    6171         247 :         [&arg]()
    6172             :         {
    6173         247 :             const auto &val = arg.Get<std::vector<double>>();
    6174         247 :             CPLAssert(val.size() == 4);
    6175         247 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    6176             :             {
    6177           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6178             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    6179             :                          "xmin <= xmax and ymin <= ymax");
    6180           5 :                 return false;
    6181             :             }
    6182         242 :             return true;
    6183        1985 :         });
    6184        1985 :     return arg;
    6185             : }
    6186             : 
    6187             : /************************************************************************/
    6188             : /*                  GDALAlgorithm::AddActiveLayerArg()                  */
    6189             : /************************************************************************/
    6190             : 
    6191             : GDALInConstructionAlgorithmArg &
    6192        1962 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
    6193             : {
    6194             :     return AddArg("active-layer", 0,
    6195             :                   MsgOrDefault(helpMessage,
    6196             :                                _("Set active layer (if not specified, all)")),
    6197        1962 :                   pValue);
    6198             : }
    6199             : 
    6200             : /************************************************************************/
    6201             : /*                  GDALAlgorithm::AddNumThreadsArg()                   */
    6202             : /************************************************************************/
    6203             : 
    6204             : GDALInConstructionAlgorithmArg &
    6205         730 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
    6206             :                                 const char *helpMessage)
    6207             : {
    6208             :     auto &arg =
    6209             :         AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
    6210             :                MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
    6211         730 :                pStrValue);
    6212             : 
    6213             :     AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
    6214        1460 :            _("Number of jobs (read-only, hidden argument)"), pValue)
    6215         730 :         .SetHidden();
    6216             : 
    6217        2742 :     auto lambda = [this, &arg, pValue, pStrValue]
    6218             :     {
    6219         914 :         bool bOK = false;
    6220         914 :         const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    6221             :         const int nLimit = std::clamp(
    6222         914 :             pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
    6223        1828 :             CPLGetNumCPUs());
    6224             :         const int nNumThreads =
    6225         914 :             GDALGetNumThreads(pStrValue->c_str(), nLimit,
    6226             :                               /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
    6227         914 :         if (bOK)
    6228             :         {
    6229         914 :             *pValue = nNumThreads;
    6230             :         }
    6231             :         else
    6232             :         {
    6233           0 :             ReportError(CE_Failure, CPLE_IllegalArg,
    6234             :                         "Invalid value for '%s' argument",
    6235           0 :                         arg.GetName().c_str());
    6236             :         }
    6237         914 :         return bOK;
    6238         730 :     };
    6239         730 :     if (!pStrValue->empty())
    6240             :     {
    6241         683 :         arg.SetDefault(*pStrValue);
    6242         683 :         lambda();
    6243             :     }
    6244         730 :     arg.AddValidationAction(std::move(lambda));
    6245         730 :     return arg;
    6246             : }
    6247             : 
    6248             : /************************************************************************/
    6249             : /*                 GDALAlgorithm::AddAbsolutePathArg()                  */
    6250             : /************************************************************************/
    6251             : 
    6252             : GDALInConstructionAlgorithmArg &
    6253         631 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
    6254             : {
    6255             :     return AddArg(
    6256             :         "absolute-path", 0,
    6257             :         MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
    6258             :                                     "should be stored as an absolute path")),
    6259         631 :         pValue);
    6260             : }
    6261             : 
    6262             : /************************************************************************/
    6263             : /*               GDALAlgorithm::AddPixelFunctionNameArg()               */
    6264             : /************************************************************************/
    6265             : 
    6266             : GDALInConstructionAlgorithmArg &
    6267         139 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
    6268             :                                        const char *helpMessage)
    6269             : {
    6270             : 
    6271             :     const auto pixelFunctionNames =
    6272         139 :         VRTDerivedRasterBand::GetPixelFunctionNames();
    6273             :     return AddArg(
    6274             :                "pixel-function", 0,
    6275             :                MsgOrDefault(
    6276             :                    helpMessage,
    6277             :                    _("Specify a pixel function to calculate output value from "
    6278             :                      "overlapping inputs")),
    6279         278 :                pValue)
    6280         278 :         .SetChoices(pixelFunctionNames);
    6281             : }
    6282             : 
    6283             : /************************************************************************/
    6284             : /*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
    6285             : /************************************************************************/
    6286             : 
    6287             : GDALInConstructionAlgorithmArg &
    6288         139 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
    6289             :                                        const char *helpMessage)
    6290             : {
    6291             :     auto &pixelFunctionArgArg =
    6292             :         AddArg("pixel-function-arg", 0,
    6293             :                MsgOrDefault(
    6294             :                    helpMessage,
    6295             :                    _("Specify argument(s) to pass to the pixel function")),
    6296         278 :                pValue)
    6297         278 :             .SetMetaVar("<NAME>=<VALUE>")
    6298         139 :             .SetRepeatedArgAllowed(true);
    6299             :     pixelFunctionArgArg.AddValidationAction(
    6300           7 :         [this, &pixelFunctionArgArg]()
    6301         146 :         { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
    6302             : 
    6303             :     pixelFunctionArgArg.SetAutoCompleteFunction(
    6304          12 :         [this](const std::string &currentValue)
    6305             :         {
    6306          12 :             std::string pixelFunction;
    6307           6 :             const auto pixelFunctionArg = GetArg("pixel-function");
    6308           6 :             if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
    6309             :             {
    6310           6 :                 pixelFunction = pixelFunctionArg->Get<std::string>();
    6311             :             }
    6312             : 
    6313           6 :             std::vector<std::string> ret;
    6314             : 
    6315           6 :             if (!pixelFunction.empty())
    6316             :             {
    6317           5 :                 const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
    6318             :                     pixelFunction.c_str());
    6319           5 :                 if (!pair)
    6320             :                 {
    6321           1 :                     ret.push_back("**");
    6322             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6323           1 :                     ret.push_back(std::string("\xC2\xA0"
    6324             :                                               "Invalid pixel function name"));
    6325             :                 }
    6326           4 :                 else if (pair->second.find("Argument name=") ==
    6327             :                          std::string::npos)
    6328             :                 {
    6329           1 :                     ret.push_back("**");
    6330             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6331           1 :                     ret.push_back(
    6332           2 :                         std::string(
    6333             :                             "\xC2\xA0"
    6334             :                             "No pixel function arguments for pixel function '")
    6335           1 :                             .append(pixelFunction)
    6336           1 :                             .append("'"));
    6337             :                 }
    6338             :                 else
    6339             :                 {
    6340           3 :                     AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
    6341             :                                           ret);
    6342             :                 }
    6343             :             }
    6344             : 
    6345          12 :             return ret;
    6346         139 :         });
    6347             : 
    6348         139 :     return pixelFunctionArgArg;
    6349             : }
    6350             : 
    6351             : /************************************************************************/
    6352             : /*                   GDALAlgorithm::AddProgressArg()                    */
    6353             : /************************************************************************/
    6354             : 
    6355       10937 : void GDALAlgorithm::AddProgressArg(bool hidden)
    6356             : {
    6357             :     auto &arg =
    6358             :         AddArg(GDAL_ARG_NAME_QUIET, 'q',
    6359       21874 :                _("Quiet mode (no progress bar or warning message)"), &m_quiet)
    6360       10937 :             .SetAvailableInPipelineStep(false)
    6361       21874 :             .SetCategory(GAAC_COMMON)
    6362       10937 :             .AddAction([this]() { m_progressBarRequested = false; });
    6363       10937 :     if (hidden)
    6364        2157 :         arg.SetHidden();
    6365             : 
    6366       21874 :     AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
    6367       10937 :         .SetAvailableInPipelineStep(false)
    6368       10937 :         .SetHidden();
    6369       10937 : }
    6370             : 
    6371             : /************************************************************************/
    6372             : /*                         GDALAlgorithm::Run()                         */
    6373             : /************************************************************************/
    6374             : 
    6375        5392 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    6376             : {
    6377        5392 :     WarnIfDeprecated();
    6378             : 
    6379        5392 :     if (m_selectedSubAlg)
    6380             :     {
    6381         464 :         if (m_calledFromCommandLine)
    6382         276 :             m_selectedSubAlg->m_calledFromCommandLine = true;
    6383         464 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    6384             :     }
    6385             : 
    6386        4928 :     if (m_helpRequested || m_helpDocRequested)
    6387             :     {
    6388          19 :         if (m_calledFromCommandLine)
    6389          19 :             printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    6390          19 :         return true;
    6391             :     }
    6392             : 
    6393        4909 :     if (m_JSONUsageRequested)
    6394             :     {
    6395           3 :         if (m_calledFromCommandLine)
    6396           3 :             printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    6397           3 :         return true;
    6398             :     }
    6399             : 
    6400        4906 :     if (!ValidateArguments())
    6401         127 :         return false;
    6402             : 
    6403        4779 :     if (m_alreadyRun)
    6404             :     {
    6405           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    6406             :                     "Run() can be called only once per algorithm instance");
    6407           3 :         return false;
    6408             :     }
    6409        4776 :     m_alreadyRun = true;
    6410             : 
    6411        4776 :     switch (ProcessGDALGOutput())
    6412             :     {
    6413           0 :         case ProcessGDALGOutputRet::GDALG_ERROR:
    6414           0 :             return false;
    6415             : 
    6416          12 :         case ProcessGDALGOutputRet::GDALG_OK:
    6417          12 :             return true;
    6418             : 
    6419        4764 :         case ProcessGDALGOutputRet::NOT_GDALG:
    6420        4764 :             break;
    6421             :     }
    6422             : 
    6423        4764 :     if (m_executionForStreamOutput)
    6424             :     {
    6425          98 :         if (!CheckSafeForStreamOutput())
    6426             :         {
    6427           4 :             return false;
    6428             :         }
    6429             :     }
    6430             : 
    6431        4760 :     return RunImpl(pfnProgress, pProgressData);
    6432             : }
    6433             : 
    6434             : /************************************************************************/
    6435             : /*              GDALAlgorithm::CheckSafeForStreamOutput()               */
    6436             : /************************************************************************/
    6437             : 
    6438          50 : bool GDALAlgorithm::CheckSafeForStreamOutput()
    6439             : {
    6440          50 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6441          50 :     if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
    6442             :     {
    6443          50 :         const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    6444          50 :         if (!EQUAL(val.c_str(), "stream"))
    6445             :         {
    6446             :             // For security reasons, to avoid that reading a .gdalg.json file
    6447             :             // writes a file on the file system.
    6448           4 :             ReportError(
    6449             :                 CE_Failure, CPLE_NotSupported,
    6450             :                 "in streamed execution, --format stream should be used");
    6451           4 :             return false;
    6452             :         }
    6453             :     }
    6454          46 :     return true;
    6455             : }
    6456             : 
    6457             : /************************************************************************/
    6458             : /*                      GDALAlgorithm::Finalize()                       */
    6459             : /************************************************************************/
    6460             : 
    6461        2066 : bool GDALAlgorithm::Finalize()
    6462             : {
    6463        2066 :     bool ret = true;
    6464        2066 :     if (m_selectedSubAlg)
    6465         282 :         ret = m_selectedSubAlg->Finalize();
    6466             : 
    6467       37617 :     for (auto &arg : m_args)
    6468             :     {
    6469       35551 :         if (arg->GetType() == GAAT_DATASET)
    6470             :         {
    6471        1622 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    6472             :         }
    6473       33929 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    6474             :         {
    6475        3256 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    6476             :             {
    6477        1530 :                 ret = ds.Close() && ret;
    6478             :             }
    6479             :         }
    6480             :     }
    6481        2066 :     return ret;
    6482             : }
    6483             : 
    6484             : /************************************************************************/
    6485             : /*                  GDALAlgorithm::GetArgNamesForCLI()                  */
    6486             : /************************************************************************/
    6487             : 
    6488             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    6489         735 : GDALAlgorithm::GetArgNamesForCLI() const
    6490             : {
    6491        1470 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6492             : 
    6493         735 :     size_t maxOptLen = 0;
    6494        9349 :     for (const auto &arg : m_args)
    6495             :     {
    6496        8614 :         if (arg->IsHidden() || arg->IsHiddenForCLI())
    6497        1814 :             continue;
    6498        6800 :         std::string opt;
    6499        6800 :         bool addComma = false;
    6500        6800 :         if (!arg->GetShortName().empty())
    6501             :         {
    6502        1443 :             opt += '-';
    6503        1443 :             opt += arg->GetShortName();
    6504        1443 :             addComma = true;
    6505             :         }
    6506        6800 :         for (char alias : arg->GetShortNameAliases())
    6507             :         {
    6508           0 :             if (addComma)
    6509           0 :                 opt += ", ";
    6510           0 :             opt += "-";
    6511           0 :             opt += alias;
    6512           0 :             addComma = true;
    6513             :         }
    6514        7554 :         for (const std::string &alias : arg->GetAliases())
    6515             :         {
    6516         754 :             if (addComma)
    6517         326 :                 opt += ", ";
    6518         754 :             opt += "--";
    6519         754 :             opt += alias;
    6520         754 :             addComma = true;
    6521             :         }
    6522        6800 :         if (!arg->GetName().empty())
    6523             :         {
    6524        6800 :             if (addComma)
    6525        1871 :                 opt += ", ";
    6526        6800 :             opt += "--";
    6527        6800 :             opt += arg->GetName();
    6528             :         }
    6529        6800 :         const auto &metaVar = arg->GetMetaVar();
    6530        6800 :         if (!metaVar.empty())
    6531             :         {
    6532        4253 :             opt += ' ';
    6533        4253 :             if (metaVar.front() != '<')
    6534        3083 :                 opt += '<';
    6535        4253 :             opt += metaVar;
    6536        4253 :             if (metaVar.back() != '>')
    6537        3077 :                 opt += '>';
    6538             :         }
    6539        6800 :         maxOptLen = std::max(maxOptLen, opt.size());
    6540        6800 :         options.emplace_back(arg.get(), opt);
    6541             :     }
    6542             : 
    6543        1470 :     return std::make_pair(std::move(options), maxOptLen);
    6544             : }
    6545             : 
    6546             : /************************************************************************/
    6547             : /*                   GDALAlgorithm::GetUsageForCLI()                    */
    6548             : /************************************************************************/
    6549             : 
    6550             : std::string
    6551         437 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    6552             :                               const UsageOptions &usageOptions) const
    6553             : {
    6554         437 :     if (m_selectedSubAlg)
    6555           7 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    6556             : 
    6557         860 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    6558         860 :     std::string osPath;
    6559         867 :     for (const std::string &s : m_callPath)
    6560             :     {
    6561         437 :         if (!osPath.empty())
    6562          53 :             osPath += ' ';
    6563         437 :         osPath += s;
    6564             :     }
    6565         430 :     osRet += ' ';
    6566         430 :     osRet += osPath;
    6567             : 
    6568         430 :     bool hasNonPositionals = false;
    6569        5425 :     for (const auto &arg : m_args)
    6570             :     {
    6571        4995 :         if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
    6572        3615 :             hasNonPositionals = true;
    6573             :     }
    6574             : 
    6575         430 :     if (HasSubAlgorithms())
    6576             :     {
    6577          10 :         if (m_callPath.size() == 1)
    6578             :         {
    6579           9 :             osRet += " <COMMAND>";
    6580           9 :             if (hasNonPositionals)
    6581           9 :                 osRet += " [OPTIONS]";
    6582           9 :             if (usageOptions.isPipelineStep)
    6583             :             {
    6584           5 :                 const size_t nLenFirstLine = osRet.size();
    6585           5 :                 osRet += '\n';
    6586           5 :                 osRet.append(nLenFirstLine, '-');
    6587           5 :                 osRet += '\n';
    6588             :             }
    6589           9 :             osRet += "\nwhere <COMMAND> is one of:\n";
    6590             :         }
    6591             :         else
    6592             :         {
    6593           1 :             osRet += " <SUBCOMMAND>";
    6594           1 :             if (hasNonPositionals)
    6595           1 :                 osRet += " [OPTIONS]";
    6596           1 :             if (usageOptions.isPipelineStep)
    6597             :             {
    6598           0 :                 const size_t nLenFirstLine = osRet.size();
    6599           0 :                 osRet += '\n';
    6600           0 :                 osRet.append(nLenFirstLine, '-');
    6601           0 :                 osRet += '\n';
    6602             :             }
    6603           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    6604             :         }
    6605          10 :         size_t maxNameLen = 0;
    6606          62 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6607             :         {
    6608          52 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    6609             :         }
    6610          62 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6611             :         {
    6612         104 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6613          52 :             if (subAlg && !subAlg->IsHidden())
    6614             :             {
    6615          52 :                 const std::string &name(subAlg->GetName());
    6616          52 :                 osRet += "  - ";
    6617          52 :                 osRet += name;
    6618          52 :                 osRet += ": ";
    6619          52 :                 osRet.append(maxNameLen - name.size(), ' ');
    6620          52 :                 osRet += subAlg->GetDescription();
    6621          52 :                 if (!subAlg->m_aliases.empty())
    6622             :                 {
    6623           8 :                     bool first = true;
    6624           8 :                     for (const auto &alias : subAlg->GetAliases())
    6625             :                     {
    6626           8 :                         if (alias ==
    6627             :                             GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    6628           8 :                             break;
    6629           0 :                         if (first)
    6630           0 :                             osRet += " (alias: ";
    6631             :                         else
    6632           0 :                             osRet += ", ";
    6633           0 :                         osRet += alias;
    6634           0 :                         first = false;
    6635             :                     }
    6636           8 :                     if (!first)
    6637             :                     {
    6638           0 :                         osRet += ')';
    6639             :                     }
    6640             :                 }
    6641          52 :                 osRet += '\n';
    6642             :             }
    6643             :         }
    6644             : 
    6645          10 :         if (shortUsage && hasNonPositionals)
    6646             :         {
    6647           3 :             osRet += "\nTry '";
    6648           3 :             osRet += osPath;
    6649           3 :             osRet += " --help' for help.\n";
    6650             :         }
    6651             :     }
    6652             :     else
    6653             :     {
    6654         420 :         if (!m_args.empty())
    6655             :         {
    6656         420 :             if (hasNonPositionals)
    6657         420 :                 osRet += " [OPTIONS]";
    6658         615 :             for (const auto *arg : m_positionalArgs)
    6659             :             {
    6660         277 :                 if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
    6661          82 :                     (GetName() == "pipeline" && arg->GetName() == "pipeline"))
    6662             :                 {
    6663             :                     const bool optional =
    6664         203 :                         (!arg->IsRequired() && !(GetName() == "pipeline" &&
    6665          30 :                                                  arg->GetName() == "pipeline"));
    6666         173 :                     osRet += ' ';
    6667         173 :                     if (optional)
    6668          25 :                         osRet += '[';
    6669         173 :                     const std::string &metavar = arg->GetMetaVar();
    6670         173 :                     if (!metavar.empty() && metavar[0] == '<')
    6671             :                     {
    6672           4 :                         osRet += metavar;
    6673             :                     }
    6674             :                     else
    6675             :                     {
    6676         169 :                         osRet += '<';
    6677         169 :                         osRet += metavar;
    6678         169 :                         osRet += '>';
    6679             :                     }
    6680         215 :                     if (arg->GetType() == GAAT_DATASET_LIST &&
    6681          42 :                         arg->GetMaxCount() > 1)
    6682             :                     {
    6683          28 :                         osRet += "...";
    6684             :                     }
    6685         173 :                     if (optional)
    6686          25 :                         osRet += ']';
    6687             :                 }
    6688             :             }
    6689             :         }
    6690             : 
    6691         420 :         const size_t nLenFirstLine = osRet.size();
    6692         420 :         osRet += '\n';
    6693         420 :         if (usageOptions.isPipelineStep)
    6694             :         {
    6695         330 :             osRet.append(nLenFirstLine, '-');
    6696         330 :             osRet += '\n';
    6697             :         }
    6698             : 
    6699         420 :         if (shortUsage)
    6700             :         {
    6701          23 :             osRet += "Try '";
    6702          23 :             osRet += osPath;
    6703          23 :             osRet += " --help' for help.\n";
    6704          23 :             return osRet;
    6705             :         }
    6706             : 
    6707         397 :         osRet += '\n';
    6708         397 :         osRet += m_description;
    6709         397 :         osRet += '\n';
    6710             :     }
    6711             : 
    6712         407 :     if (!m_args.empty() && !shortUsage)
    6713             :     {
    6714         808 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6715             :         size_t maxOptLen;
    6716         404 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    6717         404 :         if (usageOptions.maxOptLen)
    6718         331 :             maxOptLen = usageOptions.maxOptLen;
    6719             : 
    6720         808 :         const std::string userProvidedOpt = "--<user-provided-option>=<value>";
    6721         404 :         if (m_arbitraryLongNameArgsAllowed)
    6722           2 :             maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
    6723             : 
    6724             :         const auto OutputArg =
    6725        2592 :             [this, maxOptLen, &osRet,
    6726       25751 :              &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
    6727             :         {
    6728        2592 :             osRet += "  ";
    6729        2592 :             osRet += opt;
    6730        2592 :             osRet += "  ";
    6731        2592 :             osRet.append(maxOptLen - opt.size(), ' ');
    6732        2592 :             osRet += arg->GetDescription();
    6733             : 
    6734        2592 :             const auto &choices = arg->GetChoices();
    6735        2592 :             if (!choices.empty())
    6736             :             {
    6737         237 :                 osRet += ". ";
    6738         237 :                 osRet += arg->GetMetaVar();
    6739         237 :                 osRet += '=';
    6740         237 :                 bool firstChoice = true;
    6741        1739 :                 for (const auto &choice : choices)
    6742             :                 {
    6743        1502 :                     if (!firstChoice)
    6744        1265 :                         osRet += '|';
    6745        1502 :                     osRet += choice;
    6746        1502 :                     firstChoice = false;
    6747             :                 }
    6748             :             }
    6749             : 
    6750        5105 :             if (arg->GetType() == GAAT_DATASET ||
    6751        2513 :                 arg->GetType() == GAAT_DATASET_LIST)
    6752             :             {
    6753         157 :                 if (arg->IsOutput() &&
    6754         157 :                     arg->GetDatasetInputFlags() == GADV_NAME &&
    6755           9 :                     arg->GetDatasetOutputFlags() == GADV_OBJECT)
    6756             :                 {
    6757           9 :                     osRet += " (created by algorithm)";
    6758             :                 }
    6759             :             }
    6760             : 
    6761        2592 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    6762             :             {
    6763         198 :                 osRet += " (default: ";
    6764         198 :                 osRet += arg->GetDefault<std::string>();
    6765         198 :                 osRet += ')';
    6766             :             }
    6767        2394 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    6768             :             {
    6769          70 :                 if (arg->GetDefault<bool>())
    6770           0 :                     osRet += " (default: true)";
    6771             :             }
    6772        2324 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    6773             :             {
    6774          84 :                 osRet += " (default: ";
    6775          84 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    6776          84 :                 osRet += ')';
    6777             :             }
    6778        2240 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    6779             :             {
    6780          49 :                 osRet += " (default: ";
    6781          49 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    6782          49 :                 osRet += ')';
    6783             :             }
    6784        2642 :             else if (arg->GetType() == GAAT_STRING_LIST &&
    6785         451 :                      arg->HasDefaultValue())
    6786             :             {
    6787             :                 const auto &defaultVal =
    6788          17 :                     arg->GetDefault<std::vector<std::string>>();
    6789          17 :                 if (defaultVal.size() == 1)
    6790             :                 {
    6791          17 :                     osRet += " (default: ";
    6792          17 :                     osRet += defaultVal[0];
    6793          17 :                     osRet += ')';
    6794             :                 }
    6795             :             }
    6796        2197 :             else if (arg->GetType() == GAAT_INTEGER_LIST &&
    6797          23 :                      arg->HasDefaultValue())
    6798             :             {
    6799           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<int>>();
    6800           0 :                 if (defaultVal.size() == 1)
    6801             :                 {
    6802           0 :                     osRet += " (default: ";
    6803           0 :                     osRet += CPLSPrintf("%d", defaultVal[0]);
    6804           0 :                     osRet += ')';
    6805             :                 }
    6806             :             }
    6807        2174 :             else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
    6808             :             {
    6809           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<double>>();
    6810           0 :                 if (defaultVal.size() == 1)
    6811             :                 {
    6812           0 :                     osRet += " (default: ";
    6813           0 :                     osRet += CPLSPrintf("%g", defaultVal[0]);
    6814           0 :                     osRet += ')';
    6815             :                 }
    6816             :             }
    6817             : 
    6818        2592 :             if (arg->GetDisplayHintAboutRepetition())
    6819             :             {
    6820        2621 :                 if (arg->GetMinCount() > 0 &&
    6821          92 :                     arg->GetMinCount() == arg->GetMaxCount())
    6822             :                 {
    6823          18 :                     if (arg->GetMinCount() != 1)
    6824           5 :                         osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    6825             :                 }
    6826        2585 :                 else if (arg->GetMinCount() > 0 &&
    6827          74 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    6828             :                 {
    6829             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    6830           8 :                                         arg->GetMaxCount());
    6831             :                 }
    6832        2503 :                 else if (arg->GetMinCount() > 0)
    6833             :                 {
    6834          66 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    6835             :                 }
    6836        2437 :                 else if (arg->GetMaxCount() > 1)
    6837             :                 {
    6838         432 :                     osRet += " [may be repeated]";
    6839             :                 }
    6840             :             }
    6841             : 
    6842        2592 :             if (arg->IsRequired())
    6843             :             {
    6844         180 :                 osRet += " [required]";
    6845             :             }
    6846             : 
    6847        2841 :             if (!arg->IsAvailableInPipelineStep() &&
    6848         249 :                 !usageOptions.isPipelineStep)
    6849             :             {
    6850          29 :                 osRet += " [not available in pipelines]";
    6851             :             }
    6852             : 
    6853        2592 :             osRet += '\n';
    6854             : 
    6855        2592 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6856        2592 :             if (!mutualExclusionGroup.empty())
    6857             :             {
    6858         550 :                 std::string otherArgs;
    6859        5159 :                 for (const auto &otherArg : m_args)
    6860             :                 {
    6861        8963 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6862        4079 :                         otherArg.get() == arg)
    6863        1080 :                         continue;
    6864        3804 :                     if (otherArg->GetMutualExclusionGroup() ==
    6865             :                         mutualExclusionGroup)
    6866             :                     {
    6867         372 :                         if (!otherArgs.empty())
    6868         101 :                             otherArgs += ", ";
    6869         372 :                         otherArgs += "--";
    6870         372 :                         otherArgs += otherArg->GetName();
    6871             :                     }
    6872             :                 }
    6873         275 :                 if (!otherArgs.empty())
    6874             :                 {
    6875         271 :                     osRet += "  ";
    6876         271 :                     osRet += "  ";
    6877         271 :                     osRet.append(maxOptLen, ' ');
    6878         271 :                     osRet += "Mutually exclusive with ";
    6879         271 :                     osRet += otherArgs;
    6880         271 :                     osRet += '\n';
    6881             :                 }
    6882             :             }
    6883             : 
    6884             :             // Check dependency
    6885        5184 :             std::string dependencyArgs;
    6886             : 
    6887          32 :             for (const auto &dependencyArgumentName :
    6888        2656 :                  GetArgDependencies(arg->GetName()))
    6889             :             {
    6890          32 :                 const auto otherArg{GetArg(dependencyArgumentName)};
    6891          32 :                 if (otherArg != nullptr)
    6892             :                 {
    6893          32 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6894             :                         otherArg == arg)
    6895             :                     {
    6896           0 :                         continue;
    6897             :                     }
    6898             : 
    6899          32 :                     if (!dependencyArgs.empty())
    6900             :                     {
    6901           3 :                         dependencyArgs += ", ";
    6902             :                     }
    6903             : 
    6904          32 :                     dependencyArgs += "--";
    6905          32 :                     dependencyArgs += otherArg->GetName();
    6906             :                 }
    6907             :                 else
    6908             :                 {
    6909           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6910             :                              "Argument '%s' depends on unknown argument '%s'",
    6911           0 :                              arg->GetName().c_str(),
    6912             :                              dependencyArgumentName.c_str());
    6913             :                 }
    6914             :             }
    6915             : 
    6916        2592 :             if (!dependencyArgs.empty())
    6917             :             {
    6918          29 :                 osRet += "  ";
    6919          29 :                 osRet += "  ";
    6920          29 :                 osRet.append(maxOptLen, ' ');
    6921          29 :                 osRet += "Depends on ";
    6922          29 :                 osRet += dependencyArgs;
    6923          29 :                 osRet += '\n';
    6924             :             }
    6925        2592 :         };
    6926             : 
    6927         404 :         if (!m_positionalArgs.empty())
    6928             :         {
    6929         155 :             osRet += "\nPositional arguments:\n";
    6930        1663 :             for (const auto &[arg, opt] : options)
    6931             :             {
    6932        1508 :                 if (arg->IsPositional())
    6933         140 :                     OutputArg(arg, opt);
    6934             :             }
    6935             :         }
    6936             : 
    6937         404 :         if (hasNonPositionals)
    6938             :         {
    6939         404 :             bool hasCommon = false;
    6940         404 :             bool hasBase = false;
    6941         404 :             bool hasAdvanced = false;
    6942         404 :             bool hasEsoteric = false;
    6943         808 :             std::vector<std::string> categories;
    6944        4005 :             for (const auto &iter : options)
    6945             :             {
    6946        3601 :                 const auto &arg = iter.first;
    6947        3601 :                 if (!arg->IsPositional())
    6948             :                 {
    6949        3461 :                     const auto &category = arg->GetCategory();
    6950        3461 :                     if (category == GAAC_COMMON)
    6951             :                     {
    6952        1231 :                         hasCommon = true;
    6953             :                     }
    6954        2230 :                     else if (category == GAAC_BASE)
    6955             :                     {
    6956        1958 :                         hasBase = true;
    6957             :                     }
    6958         272 :                     else if (category == GAAC_ADVANCED)
    6959             :                     {
    6960         210 :                         hasAdvanced = true;
    6961             :                     }
    6962          62 :                     else if (category == GAAC_ESOTERIC)
    6963             :                     {
    6964          29 :                         hasEsoteric = true;
    6965             :                     }
    6966          33 :                     else if (std::find(categories.begin(), categories.end(),
    6967          33 :                                        category) == categories.end())
    6968             :                     {
    6969           9 :                         categories.push_back(category);
    6970             :                     }
    6971             :                 }
    6972             :             }
    6973         404 :             if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
    6974          71 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    6975         404 :             if (hasBase)
    6976         357 :                 categories.insert(categories.begin(), GAAC_BASE);
    6977         404 :             if (hasCommon && !usageOptions.isPipelineStep)
    6978          69 :                 categories.insert(categories.begin(), GAAC_COMMON);
    6979         404 :             if (hasEsoteric)
    6980          11 :                 categories.push_back(GAAC_ESOTERIC);
    6981             : 
    6982         921 :             for (const auto &category : categories)
    6983             :             {
    6984         517 :                 osRet += "\n";
    6985         517 :                 if (category != GAAC_BASE)
    6986             :                 {
    6987         160 :                     osRet += category;
    6988         160 :                     osRet += ' ';
    6989             :                 }
    6990         517 :                 osRet += "Options:\n";
    6991        5689 :                 for (const auto &[arg, opt] : options)
    6992             :                 {
    6993        5172 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    6994        2452 :                         OutputArg(arg, opt);
    6995             :                 }
    6996         517 :                 if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
    6997             :                 {
    6998           2 :                     osRet += "  ";
    6999           2 :                     osRet += userProvidedOpt;
    7000           2 :                     osRet += "  ";
    7001           2 :                     if (userProvidedOpt.size() < maxOptLen)
    7002           0 :                         osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
    7003           2 :                     osRet += "Argument provided by user";
    7004           2 :                     osRet += '\n';
    7005             :                 }
    7006             :             }
    7007             :         }
    7008             :     }
    7009             : 
    7010         407 :     if (!m_longDescription.empty())
    7011             :     {
    7012           7 :         osRet += '\n';
    7013           7 :         osRet += m_longDescription;
    7014           7 :         osRet += '\n';
    7015             :     }
    7016             : 
    7017         407 :     if (!m_helpDocRequested && !usageOptions.isPipelineMain)
    7018             :     {
    7019         392 :         if (!m_helpURL.empty())
    7020             :         {
    7021         392 :             osRet += "\nFor more details, consult ";
    7022         392 :             osRet += GetHelpFullURL();
    7023         392 :             osRet += '\n';
    7024             :         }
    7025         392 :         osRet += GetUsageForCLIEnd();
    7026             :     }
    7027             : 
    7028         407 :     return osRet;
    7029             : }
    7030             : 
    7031             : /************************************************************************/
    7032             : /*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
    7033             : /************************************************************************/
    7034             : 
    7035             : //! @cond Doxygen_Suppress
    7036         399 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
    7037             : {
    7038         399 :     std::string osRet;
    7039             : 
    7040         399 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    7041             :     {
    7042             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    7043             :                  "alternative interface to GDAL and OGR command line "
    7044             :                  "utilities.\nThe project reserves the right to modify, "
    7045             :                  "rename, reorganize, and change the behavior of the utility\n"
    7046             :                  "until it is officially frozen in a future feature release of "
    7047          14 :                  "GDAL.\n";
    7048             :     }
    7049         399 :     return osRet;
    7050             : }
    7051             : 
    7052             : //! @endcond
    7053             : 
    7054             : /************************************************************************/
    7055             : /*                   GDALAlgorithm::GetUsageAsJSON()                    */
    7056             : /************************************************************************/
    7057             : 
    7058         603 : std::string GDALAlgorithm::GetUsageAsJSON() const
    7059             : {
    7060        1206 :     CPLJSONDocument oDoc;
    7061        1206 :     auto oRoot = oDoc.GetRoot();
    7062             : 
    7063         603 :     if (m_displayInJSONUsage)
    7064             :     {
    7065         601 :         oRoot.Add("name", m_name);
    7066         601 :         CPLJSONArray jFullPath;
    7067        1247 :         for (const std::string &s : m_callPath)
    7068             :         {
    7069         646 :             jFullPath.Add(s);
    7070             :         }
    7071         601 :         oRoot.Add("full_path", jFullPath);
    7072             :     }
    7073             : 
    7074         603 :     oRoot.Add("description", m_description);
    7075         603 :     if (!m_helpURL.empty())
    7076             :     {
    7077         600 :         oRoot.Add("short_url", m_helpURL);
    7078         600 :         oRoot.Add("url", GetHelpFullURL());
    7079             :     }
    7080             : 
    7081        1206 :     CPLJSONArray jSubAlgorithms;
    7082         815 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    7083             :     {
    7084         424 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7085         212 :         if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
    7086             :         {
    7087         210 :             CPLJSONDocument oSubDoc;
    7088         210 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    7089         210 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    7090             :         }
    7091             :     }
    7092         603 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    7093             : 
    7094         603 :     if (m_arbitraryLongNameArgsAllowed)
    7095             :     {
    7096           1 :         oRoot.Add("user_provided_arguments_allowed", true);
    7097             :     }
    7098             : 
    7099       11494 :     const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
    7100             :     {
    7101        5747 :         CPLJSONObject jArg;
    7102        5747 :         jArg.Add("name", arg->GetName());
    7103        5747 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    7104        5747 :         jArg.Add("description", arg->GetDescription());
    7105             : 
    7106        5747 :         const auto &metaVar = arg->GetMetaVar();
    7107        5747 :         if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
    7108             :         {
    7109        1737 :             if (metaVar.front() == '<' && metaVar.back() == '>' &&
    7110        1737 :                 metaVar.substr(1, metaVar.size() - 2).find('>') ==
    7111             :                     std::string::npos)
    7112          32 :                 jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
    7113             :             else
    7114         927 :                 jArg.Add("metavar", metaVar);
    7115             :         }
    7116             : 
    7117        5747 :         if (!arg->IsAvailableInPipelineStep())
    7118             :         {
    7119        1682 :             jArg.Add("available_in_pipeline_step", false);
    7120             :         }
    7121             : 
    7122        5747 :         const auto &choices = arg->GetChoices();
    7123        5747 :         if (!choices.empty())
    7124             :         {
    7125         431 :             CPLJSONArray jChoices;
    7126        3539 :             for (const auto &choice : choices)
    7127        3108 :                 jChoices.Add(choice);
    7128         431 :             jArg.Add("choices", jChoices);
    7129             :         }
    7130        5747 :         if (arg->HasDefaultValue())
    7131             :         {
    7132        1240 :             switch (arg->GetType())
    7133             :             {
    7134         440 :                 case GAAT_BOOLEAN:
    7135         440 :                     jArg.Add("default", arg->GetDefault<bool>());
    7136         440 :                     break;
    7137         378 :                 case GAAT_STRING:
    7138         378 :                     jArg.Add("default", arg->GetDefault<std::string>());
    7139         378 :                     break;
    7140         210 :                 case GAAT_INTEGER:
    7141         210 :                     jArg.Add("default", arg->GetDefault<int>());
    7142         210 :                     break;
    7143         178 :                 case GAAT_REAL:
    7144         178 :                     jArg.Add("default", arg->GetDefault<double>());
    7145         178 :                     break;
    7146          32 :                 case GAAT_STRING_LIST:
    7147             :                 {
    7148             :                     const auto &val =
    7149          32 :                         arg->GetDefault<std::vector<std::string>>();
    7150          32 :                     if (val.size() == 1)
    7151             :                     {
    7152          31 :                         jArg.Add("default", val[0]);
    7153             :                     }
    7154             :                     else
    7155             :                     {
    7156           1 :                         CPLJSONArray jArr;
    7157           3 :                         for (const auto &s : val)
    7158             :                         {
    7159           2 :                             jArr.Add(s);
    7160             :                         }
    7161           1 :                         jArg.Add("default", jArr);
    7162             :                     }
    7163          32 :                     break;
    7164             :                 }
    7165           1 :                 case GAAT_INTEGER_LIST:
    7166             :                 {
    7167           1 :                     const auto &val = arg->GetDefault<std::vector<int>>();
    7168           1 :                     if (val.size() == 1)
    7169             :                     {
    7170           0 :                         jArg.Add("default", val[0]);
    7171             :                     }
    7172             :                     else
    7173             :                     {
    7174           1 :                         CPLJSONArray jArr;
    7175           3 :                         for (int i : val)
    7176             :                         {
    7177           2 :                             jArr.Add(i);
    7178             :                         }
    7179           1 :                         jArg.Add("default", jArr);
    7180             :                     }
    7181           1 :                     break;
    7182             :                 }
    7183           1 :                 case GAAT_REAL_LIST:
    7184             :                 {
    7185           1 :                     const auto &val = arg->GetDefault<std::vector<double>>();
    7186           1 :                     if (val.size() == 1)
    7187             :                     {
    7188           0 :                         jArg.Add("default", val[0]);
    7189             :                     }
    7190             :                     else
    7191             :                     {
    7192           1 :                         CPLJSONArray jArr;
    7193           3 :                         for (double d : val)
    7194             :                         {
    7195           2 :                             jArr.Add(d);
    7196             :                         }
    7197           1 :                         jArg.Add("default", jArr);
    7198             :                     }
    7199           1 :                     break;
    7200             :                 }
    7201           0 :                 case GAAT_DATASET:
    7202             :                 case GAAT_DATASET_LIST:
    7203           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    7204             :                              "Unhandled default value for arg %s",
    7205           0 :                              arg->GetName().c_str());
    7206           0 :                     break;
    7207             :             }
    7208             :         }
    7209             : 
    7210        5747 :         const auto [minVal, minValIsIncluded] = arg->GetMinValue();
    7211        5747 :         if (!std::isnan(minVal))
    7212             :         {
    7213         697 :             if (arg->GetType() == GAAT_INTEGER ||
    7214         269 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7215         175 :                 jArg.Add("min_value", static_cast<int>(minVal));
    7216             :             else
    7217         253 :                 jArg.Add("min_value", minVal);
    7218         428 :             jArg.Add("min_value_is_included", minValIsIncluded);
    7219             :         }
    7220             : 
    7221        5747 :         const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
    7222        5747 :         if (!std::isnan(maxVal))
    7223             :         {
    7224         199 :             if (arg->GetType() == GAAT_INTEGER ||
    7225          82 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7226          35 :                 jArg.Add("max_value", static_cast<int>(maxVal));
    7227             :             else
    7228          82 :                 jArg.Add("max_value", maxVal);
    7229         117 :             jArg.Add("max_value_is_included", maxValIsIncluded);
    7230             :         }
    7231             : 
    7232        5747 :         jArg.Add("required", arg->IsRequired());
    7233        5747 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    7234             :         {
    7235        1611 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    7236        1611 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    7237        1611 :             jArg.Add("min_count", arg->GetMinCount());
    7238        1611 :             jArg.Add("max_count", arg->GetMaxCount());
    7239             :         }
    7240             : 
    7241             :         // Process dependencies
    7242        5747 :         const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7243        5747 :         if (!mutualDependencyGroup.empty())
    7244             :         {
    7245          32 :             jArg.Add("mutual_dependency_group", mutualDependencyGroup);
    7246             :         }
    7247             : 
    7248       11494 :         CPLJSONArray jDependencies;
    7249          50 :         for (const auto &dependencyArgumentName :
    7250        5847 :              GetArgDependencies(arg->GetName()))
    7251             :         {
    7252          50 :             jDependencies.Add(dependencyArgumentName);
    7253             :         }
    7254             : 
    7255        5747 :         if (jDependencies.Size() > 0)
    7256             :         {
    7257          50 :             jArg.Add("depends_on", jDependencies);
    7258             :         }
    7259             : 
    7260        5747 :         jArg.Add("category", arg->GetCategory());
    7261             : 
    7262       11222 :         if (arg->GetType() == GAAT_DATASET ||
    7263        5475 :             arg->GetType() == GAAT_DATASET_LIST)
    7264             :         {
    7265             :             {
    7266         489 :                 CPLJSONArray jAr;
    7267         489 :                 if (arg->GetDatasetType() & GDAL_OF_RASTER)
    7268         324 :                     jAr.Add("raster");
    7269         489 :                 if (arg->GetDatasetType() & GDAL_OF_VECTOR)
    7270         198 :                     jAr.Add("vector");
    7271         489 :                 if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
    7272          41 :                     jAr.Add("multidim_raster");
    7273         489 :                 jArg.Add("dataset_type", jAr);
    7274             :             }
    7275             : 
    7276         662 :             const auto GetFlags = [](int flags)
    7277             :             {
    7278         662 :                 CPLJSONArray jAr;
    7279         662 :                 if (flags & GADV_NAME)
    7280         489 :                     jAr.Add("name");
    7281         662 :                 if (flags & GADV_OBJECT)
    7282         621 :                     jAr.Add("dataset");
    7283         662 :                 return jAr;
    7284             :             };
    7285             : 
    7286         489 :             if (arg->IsInput())
    7287             :             {
    7288         489 :                 jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
    7289             :             }
    7290         489 :             if (arg->IsOutput())
    7291             :             {
    7292         173 :                 jArg.Add("output_flags",
    7293         346 :                          GetFlags(arg->GetDatasetOutputFlags()));
    7294             :             }
    7295             :         }
    7296             : 
    7297        5747 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    7298        5747 :         if (!mutualExclusionGroup.empty())
    7299             :         {
    7300         752 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    7301             :         }
    7302             : 
    7303       11494 :         const auto &metadata = arg->GetMetadata();
    7304        5747 :         if (!metadata.empty())
    7305             :         {
    7306         465 :             CPLJSONObject jMetadata;
    7307         969 :             for (const auto &[key, values] : metadata)
    7308             :             {
    7309        1008 :                 CPLJSONArray jValue;
    7310        1219 :                 for (const auto &value : values)
    7311         715 :                     jValue.Add(value);
    7312         504 :                 jMetadata.Add(key, jValue);
    7313             :             }
    7314         465 :             jArg.Add("metadata", jMetadata);
    7315             :         }
    7316             : 
    7317       11494 :         return jArg;
    7318         603 :     };
    7319             : 
    7320             :     {
    7321         603 :         CPLJSONArray jArgs;
    7322        9509 :         for (const auto &arg : m_args)
    7323             :         {
    7324        8906 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
    7325        5499 :                 jArgs.Add(ProcessArg(arg.get()));
    7326             :         }
    7327         603 :         oRoot.Add("input_arguments", jArgs);
    7328             :     }
    7329             : 
    7330             :     {
    7331         603 :         CPLJSONArray jArgs;
    7332        9509 :         for (const auto &arg : m_args)
    7333             :         {
    7334        8906 :             if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
    7335          75 :                 jArgs.Add(ProcessArg(arg.get()));
    7336             :         }
    7337         603 :         oRoot.Add("output_arguments", jArgs);
    7338             :     }
    7339             : 
    7340             :     {
    7341         603 :         CPLJSONArray jArgs;
    7342        9509 :         for (const auto &arg : m_args)
    7343             :         {
    7344        8906 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
    7345         173 :                 jArgs.Add(ProcessArg(arg.get()));
    7346             :         }
    7347         603 :         oRoot.Add("input_output_arguments", jArgs);
    7348             :     }
    7349             : 
    7350         603 :     if (m_supportsStreamedOutput)
    7351             :     {
    7352         130 :         oRoot.Add("supports_streamed_output", true);
    7353             :     }
    7354             : 
    7355        1206 :     return oDoc.SaveAsString();
    7356             : }
    7357             : 
    7358             : /************************************************************************/
    7359             : /*                   GDALAlgorithm::GetAutoComplete()                   */
    7360             : /************************************************************************/
    7361             : 
    7362             : std::vector<std::string>
    7363         295 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    7364             :                                bool lastWordIsComplete, bool showAllOptions)
    7365             : {
    7366         590 :     std::vector<std::string> ret;
    7367             : 
    7368             :     // Get inner-most algorithm
    7369         295 :     std::unique_ptr<GDALAlgorithm> curAlgHolder;
    7370         295 :     GDALAlgorithm *curAlg = this;
    7371         580 :     while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
    7372             :     {
    7373             :         auto subAlg = curAlg->InstantiateSubAlgorithm(
    7374         431 :             args.front(), /* suggestionAllowed = */ false);
    7375         431 :         if (!subAlg)
    7376         145 :             break;
    7377         286 :         if (args.size() == 1 && !lastWordIsComplete)
    7378             :         {
    7379           5 :             int nCount = 0;
    7380         118 :             for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
    7381             :             {
    7382         113 :                 if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
    7383           6 :                     nCount++;
    7384             :             }
    7385           5 :             if (nCount >= 2)
    7386             :             {
    7387          11 :                 for (const std::string &subAlgName :
    7388          23 :                      curAlg->GetSubAlgorithmNames())
    7389             :                 {
    7390          11 :                     subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
    7391          11 :                     if (subAlg && !subAlg->IsHidden())
    7392          11 :                         ret.push_back(subAlg->GetName());
    7393             :                 }
    7394           1 :                 return ret;
    7395             :             }
    7396             :         }
    7397         285 :         showAllOptions = false;
    7398         285 :         args.erase(args.begin());
    7399         285 :         curAlgHolder = std::move(subAlg);
    7400         285 :         curAlg = curAlgHolder.get();
    7401             :     }
    7402         294 :     if (curAlg != this)
    7403             :     {
    7404         155 :         curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
    7405             :         return curAlg->GetAutoComplete(args, lastWordIsComplete,
    7406         155 :                                        /* showAllOptions = */ false);
    7407             :     }
    7408             : 
    7409         278 :     std::string option;
    7410         278 :     std::string value;
    7411         139 :     ExtractLastOptionAndValue(args, option, value);
    7412             : 
    7413         170 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    7414          31 :         args.back()[0] == '-')
    7415             :     {
    7416          28 :         const auto &lastArg = args.back();
    7417             :         // List available options
    7418         419 :         for (const auto &arg : GetArgs())
    7419             :         {
    7420         721 :             if (arg->IsHidden() || arg->IsHiddenForCLI() ||
    7421         655 :                 (!showAllOptions &&
    7422         894 :                  (arg->GetName() == "help" || arg->GetName() == "config" ||
    7423         542 :                   arg->GetName() == "version" ||
    7424         271 :                   arg->GetName() == "json-usage")))
    7425             :             {
    7426         142 :                 continue;
    7427             :             }
    7428         249 :             if (!arg->GetShortName().empty())
    7429             :             {
    7430         153 :                 std::string str = std::string("-").append(arg->GetShortName());
    7431          51 :                 if (lastArg == str)
    7432           0 :                     ret.push_back(std::move(str));
    7433             :             }
    7434         249 :             if (lastArg != "-" && lastArg != "--")
    7435             :             {
    7436          54 :                 for (const std::string &alias : arg->GetAliases())
    7437             :                 {
    7438          48 :                     std::string str = std::string("--").append(alias);
    7439          16 :                     if (cpl::starts_with(str, lastArg))
    7440           3 :                         ret.push_back(std::move(str));
    7441             :                 }
    7442             :             }
    7443         249 :             if (!arg->GetName().empty())
    7444             :             {
    7445         747 :                 std::string str = std::string("--").append(arg->GetName());
    7446         249 :                 if (cpl::starts_with(str, lastArg))
    7447         213 :                     ret.push_back(std::move(str));
    7448             :             }
    7449             :         }
    7450          28 :         std::sort(ret.begin(), ret.end());
    7451             :     }
    7452         111 :     else if (!option.empty())
    7453             :     {
    7454             :         // List possible choices for current option
    7455         104 :         auto arg = GetArg(option);
    7456         104 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7457             :         {
    7458         104 :             ret = arg->GetChoices();
    7459         104 :             if (ret.empty())
    7460             :             {
    7461             :                 {
    7462          99 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7463          99 :                     SetParseForAutoCompletion();
    7464          99 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7465             :                 }
    7466          99 :                 ret = arg->GetAutoCompleteChoices(value);
    7467             :             }
    7468             :             else
    7469             :             {
    7470           5 :                 std::sort(ret.begin(), ret.end());
    7471             :             }
    7472         104 :             if (!ret.empty() && ret.back() == value)
    7473             :             {
    7474           2 :                 ret.clear();
    7475             :             }
    7476         102 :             else if (ret.empty())
    7477             :             {
    7478          13 :                 ret.push_back("**");
    7479             :                 // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    7480          26 :                 ret.push_back(std::string("\xC2\xA0"
    7481             :                                           "description: ")
    7482          13 :                                   .append(arg->GetDescription()));
    7483             :             }
    7484             :         }
    7485             :     }
    7486             :     else
    7487             :     {
    7488             :         // List possible sub-algorithms
    7489          70 :         for (const std::string &subAlgName : GetSubAlgorithmNames())
    7490             :         {
    7491         126 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7492          63 :             if (subAlg && !subAlg->IsHidden())
    7493          63 :                 ret.push_back(subAlg->GetName());
    7494             :         }
    7495           7 :         if (!ret.empty())
    7496             :         {
    7497           3 :             std::sort(ret.begin(), ret.end());
    7498             :         }
    7499             : 
    7500             :         // Try filenames
    7501           7 :         if (ret.empty() && !args.empty())
    7502             :         {
    7503             :             {
    7504           3 :                 CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7505           3 :                 SetParseForAutoCompletion();
    7506           3 :                 CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7507             :             }
    7508             : 
    7509           3 :             const std::string &lastArg = args.back();
    7510           3 :             GDALAlgorithmArg *arg = nullptr;
    7511          18 :             for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
    7512          21 :                                      "like", "source", "destination"})
    7513             :             {
    7514          18 :                 if (!arg)
    7515             :                 {
    7516           3 :                     auto newArg = GetArg(name);
    7517           3 :                     if (newArg)
    7518             :                     {
    7519           3 :                         if (!newArg->IsExplicitlySet())
    7520             :                         {
    7521           0 :                             arg = newArg;
    7522             :                         }
    7523           6 :                         else if (newArg->GetType() == GAAT_STRING ||
    7524           5 :                                  newArg->GetType() == GAAT_STRING_LIST ||
    7525           8 :                                  newArg->GetType() == GAAT_DATASET ||
    7526           2 :                                  newArg->GetType() == GAAT_DATASET_LIST)
    7527             :                         {
    7528             :                             VSIStatBufL sStat;
    7529           5 :                             if ((!lastArg.empty() && lastArg.back() == '/') ||
    7530           2 :                                 VSIStatL(lastArg.c_str(), &sStat) != 0)
    7531             :                             {
    7532           3 :                                 arg = newArg;
    7533             :                             }
    7534             :                         }
    7535             :                     }
    7536             :                 }
    7537             :             }
    7538           3 :             if (arg)
    7539             :             {
    7540           3 :                 ret = arg->GetAutoCompleteChoices(lastArg);
    7541             :             }
    7542             :         }
    7543             :     }
    7544             : 
    7545         139 :     return ret;
    7546             : }
    7547             : 
    7548             : /************************************************************************/
    7549             : /*                   GDALAlgorithm::GetFieldIndices()                   */
    7550             : /************************************************************************/
    7551             : 
    7552          44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
    7553             :                                     OGRLayerH hLayer, std::vector<int> &indices)
    7554             : {
    7555          44 :     VALIDATE_POINTER1(hLayer, __func__, false);
    7556             : 
    7557          44 :     const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
    7558             : 
    7559          44 :     if (names.size() == 1 && names[0] == "ALL")
    7560             :     {
    7561          12 :         const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
    7562          28 :         for (int i = 0; i < nSrcFieldCount; ++i)
    7563             :         {
    7564          16 :             indices.push_back(i);
    7565             :         }
    7566             :     }
    7567          32 :     else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
    7568             :     {
    7569           6 :         std::set<int> fieldsAdded;
    7570          14 :         for (const std::string &osFieldName : names)
    7571             :         {
    7572             : 
    7573             :             const int nIdx =
    7574          10 :                 layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
    7575             : 
    7576          10 :             if (nIdx < 0)
    7577             :             {
    7578           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7579             :                          "Field '%s' does not exist in layer '%s'",
    7580           2 :                          osFieldName.c_str(), layer.GetName());
    7581           2 :                 return false;
    7582             :             }
    7583             : 
    7584           8 :             if (fieldsAdded.insert(nIdx).second)
    7585             :             {
    7586           7 :                 indices.push_back(nIdx);
    7587             :             }
    7588             :         }
    7589             :     }
    7590             : 
    7591          42 :     return true;
    7592             : }
    7593             : 
    7594             : /************************************************************************/
    7595             : /*              GDALAlgorithm::ExtractLastOptionAndValue()              */
    7596             : /************************************************************************/
    7597             : 
    7598         139 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    7599             :                                               std::string &option,
    7600             :                                               std::string &value) const
    7601             : {
    7602         139 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    7603             :     {
    7604          97 :         const auto nPosEqual = args.back().find('=');
    7605          97 :         if (nPosEqual == std::string::npos)
    7606             :         {
    7607             :             // Deal with "gdal ... --option"
    7608          78 :             if (GetArg(args.back()))
    7609             :             {
    7610          50 :                 option = args.back();
    7611          50 :                 args.pop_back();
    7612             :             }
    7613             :         }
    7614             :         else
    7615             :         {
    7616             :             // Deal with "gdal ... --option=<value>"
    7617          19 :             if (GetArg(args.back().substr(0, nPosEqual)))
    7618             :             {
    7619          19 :                 option = args.back().substr(0, nPosEqual);
    7620          19 :                 value = args.back().substr(nPosEqual + 1);
    7621          19 :                 args.pop_back();
    7622             :             }
    7623             :         }
    7624             :     }
    7625          78 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    7626          36 :              args[args.size() - 2][0] == '-')
    7627             :     {
    7628             :         // Deal with "gdal ... --option <value>"
    7629          35 :         auto arg = GetArg(args[args.size() - 2]);
    7630          35 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7631             :         {
    7632          35 :             option = args[args.size() - 2];
    7633          35 :             value = args.back();
    7634          35 :             args.pop_back();
    7635             :         }
    7636             :     }
    7637             : 
    7638         139 :     const auto IsKeyValueOption = [](const std::string &osStr)
    7639             :     {
    7640         382 :         return osStr == "--co" || osStr == "--creation-option" ||
    7641         357 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    7642         380 :                osStr == "--oo" || osStr == "--open-option";
    7643             :     };
    7644             : 
    7645         139 :     if (IsKeyValueOption(option))
    7646             :     {
    7647          23 :         const auto nPosEqual = value.find('=');
    7648          23 :         if (nPosEqual != std::string::npos)
    7649             :         {
    7650          11 :             value.resize(nPosEqual);
    7651             :         }
    7652             :     }
    7653         139 : }
    7654             : 
    7655             : /************************************************************************/
    7656             : /*                 GDALAlgorithm::GetArgDependencies()                  */
    7657             : /************************************************************************/
    7658             : 
    7659             : std::vector<std::string>
    7660        8348 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
    7661             : {
    7662        8348 :     const auto arg = GetArg(osName, false);
    7663        8348 :     if (!arg)
    7664             :     {
    7665           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
    7666             :                     osName.c_str());
    7667           0 :         return {};
    7668             :     }
    7669       16696 :     std::vector<std::string> dependencies = arg->GetDirectDependencies();
    7670        8348 :     if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7671        8348 :         !mutualDependencyGroup.empty())
    7672             :     {
    7673         896 :         for (const auto &otherArg : m_args)
    7674             :         {
    7675        1627 :             if (otherArg.get() == arg ||
    7676         786 :                 mutualDependencyGroup.compare(
    7677         786 :                     otherArg->GetMutualDependencyGroup()) != 0)
    7678         783 :                 continue;
    7679          58 :             dependencies.push_back(otherArg->GetName());
    7680             :         }
    7681             :     }
    7682        8348 :     return dependencies;
    7683             : }
    7684             : 
    7685             : //! @cond Doxygen_Suppress
    7686             : 
    7687             : /************************************************************************/
    7688             : /*                  GDALContainerAlgorithm::RunImpl()                   */
    7689             : /************************************************************************/
    7690             : 
    7691           0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
    7692             : {
    7693           0 :     return false;
    7694             : }
    7695             : 
    7696             : //! @endcond
    7697             : 
    7698             : /************************************************************************/
    7699             : /*                        GDALAlgorithmRelease()                        */
    7700             : /************************************************************************/
    7701             : 
    7702             : /** Release a handle to an algorithm.
    7703             :  *
    7704             :  * @since 3.11
    7705             :  */
    7706       13927 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    7707             : {
    7708       13927 :     delete hAlg;
    7709       13927 : }
    7710             : 
    7711             : /************************************************************************/
    7712             : /*                        GDALAlgorithmGetName()                        */
    7713             : /************************************************************************/
    7714             : 
    7715             : /** Return the algorithm name.
    7716             :  *
    7717             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7718             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    7719             :  * be freed.
    7720             :  * @since 3.11
    7721             :  */
    7722        6304 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    7723             : {
    7724        6304 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7725        6304 :     return hAlg->ptr->GetName().c_str();
    7726             : }
    7727             : 
    7728             : /************************************************************************/
    7729             : /*                    GDALAlgorithmGetDescription()                     */
    7730             : /************************************************************************/
    7731             : 
    7732             : /** Return the algorithm (short) description.
    7733             :  *
    7734             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7735             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7736             :  * not be freed.
    7737             :  * @since 3.11
    7738             :  */
    7739        6070 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    7740             : {
    7741        6070 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7742        6070 :     return hAlg->ptr->GetDescription().c_str();
    7743             : }
    7744             : 
    7745             : /************************************************************************/
    7746             : /*                  GDALAlgorithmGetLongDescription()                   */
    7747             : /************************************************************************/
    7748             : 
    7749             : /** Return the algorithm (longer) description.
    7750             :  *
    7751             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7752             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7753             :  * not be freed.
    7754             :  * @since 3.11
    7755             :  */
    7756           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    7757             : {
    7758           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7759           2 :     return hAlg->ptr->GetLongDescription().c_str();
    7760             : }
    7761             : 
    7762             : /************************************************************************/
    7763             : /*                    GDALAlgorithmGetHelpFullURL()                     */
    7764             : /************************************************************************/
    7765             : 
    7766             : /** Return the algorithm full URL.
    7767             :  *
    7768             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7769             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    7770             :  * not be freed.
    7771             :  * @since 3.11
    7772             :  */
    7773        5332 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    7774             : {
    7775        5332 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7776        5332 :     return hAlg->ptr->GetHelpFullURL().c_str();
    7777             : }
    7778             : 
    7779             : /************************************************************************/
    7780             : /*                   GDALAlgorithmHasSubAlgorithms()                    */
    7781             : /************************************************************************/
    7782             : 
    7783             : /** Return whether the algorithm has sub-algorithms.
    7784             :  *
    7785             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7786             :  * @since 3.11
    7787             :  */
    7788       10221 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    7789             : {
    7790       10221 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7791       10221 :     return hAlg->ptr->HasSubAlgorithms();
    7792             : }
    7793             : 
    7794             : /************************************************************************/
    7795             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    7796             : /************************************************************************/
    7797             : 
    7798             : /** Get the names of registered algorithms.
    7799             :  *
    7800             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7801             :  * @return a NULL terminated list of names, which must be destroyed with
    7802             :  * CSLDestroy()
    7803             :  * @since 3.11
    7804             :  */
    7805         940 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    7806             : {
    7807         940 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7808         940 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    7809             : }
    7810             : 
    7811             : /************************************************************************/
    7812             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    7813             : /************************************************************************/
    7814             : 
    7815             : /** Instantiate an algorithm by its name (or its alias).
    7816             :  *
    7817             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7818             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    7819             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    7820             :  * or NULL if the algorithm does not exist or another error occurred.
    7821             :  * @since 3.11
    7822             :  */
    7823        9592 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    7824             :                                                     const char *pszSubAlgName)
    7825             : {
    7826        9592 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7827        9592 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    7828       19184 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    7829             :     return subAlg
    7830       19184 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    7831       19184 :                : nullptr;
    7832             : }
    7833             : 
    7834             : /************************************************************************/
    7835             : /*               GDALAlgorithmParseCommandLineArguments()               */
    7836             : /************************************************************************/
    7837             : 
    7838             : /** Parse a command line argument, which does not include the algorithm
    7839             :  * name, to set the value of corresponding arguments.
    7840             :  *
    7841             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7842             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    7843             :  * @return true if successful, false otherwise
    7844             :  * @since 3.11
    7845             :  */
    7846             : 
    7847         371 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    7848             :                                             CSLConstList papszArgs)
    7849             : {
    7850         371 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7851         371 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    7852             : }
    7853             : 
    7854             : /************************************************************************/
    7855             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    7856             : /************************************************************************/
    7857             : 
    7858             : /** Return the actual algorithm that is going to be invoked, when the
    7859             :  * current algorithm has sub-algorithms.
    7860             :  *
    7861             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    7862             :  *
    7863             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    7864             :  * the hAlg instance that owns it.
    7865             :  *
    7866             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7867             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    7868             :  * @since 3.11
    7869             :  */
    7870        1030 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    7871             : {
    7872        1030 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7873        1030 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    7874             : }
    7875             : 
    7876             : /************************************************************************/
    7877             : /*                          GDALAlgorithmRun()                          */
    7878             : /************************************************************************/
    7879             : 
    7880             : /** Execute the algorithm, starting with ValidateArguments() and then
    7881             :  * calling RunImpl().
    7882             :  *
    7883             :  * This function must be called at most once per instance.
    7884             :  *
    7885             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7886             :  * @param pfnProgress Progress callback. May be null.
    7887             :  * @param pProgressData Progress callback user data. May be null.
    7888             :  * @return true if successful, false otherwise
    7889             :  * @since 3.11
    7890             :  */
    7891             : 
    7892        3123 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    7893             :                       void *pProgressData)
    7894             : {
    7895        3123 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7896        3123 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    7897             : }
    7898             : 
    7899             : /************************************************************************/
    7900             : /*                       GDALAlgorithmFinalize()                        */
    7901             : /************************************************************************/
    7902             : 
    7903             : /** Complete any pending actions, and return the final status.
    7904             :  * This is typically useful for algorithm that generate an output dataset.
    7905             :  *
    7906             :  * Note that this function does *NOT* release memory associated with the
    7907             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    7908             :  *
    7909             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7910             :  * @return true if successful, false otherwise
    7911             :  * @since 3.11
    7912             :  */
    7913             : 
    7914        1054 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    7915             : {
    7916        1054 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7917        1054 :     return hAlg->ptr->Finalize();
    7918             : }
    7919             : 
    7920             : /************************************************************************/
    7921             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    7922             : /************************************************************************/
    7923             : 
    7924             : /** Return the usage of the algorithm as a JSON-serialized string.
    7925             :  *
    7926             :  * This can be used to dynamically generate interfaces to algorithms.
    7927             :  *
    7928             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7929             :  * @return a string that must be freed with CPLFree()
    7930             :  * @since 3.11
    7931             :  */
    7932           6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    7933             : {
    7934           6 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7935           6 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    7936             : }
    7937             : 
    7938             : /************************************************************************/
    7939             : /*                      GDALAlgorithmGetArgNames()                      */
    7940             : /************************************************************************/
    7941             : 
    7942             : /** Return the list of available argument names.
    7943             :  *
    7944             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7945             :  * @return a NULL terminated list of names, which must be destroyed with
    7946             :  * CSLDestroy()
    7947             :  * @since 3.11
    7948             :  */
    7949       16650 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    7950             : {
    7951       16650 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7952       33300 :     CPLStringList list;
    7953      370882 :     for (const auto &arg : hAlg->ptr->GetArgs())
    7954      354232 :         list.AddString(arg->GetName().c_str());
    7955       16650 :     return list.StealList();
    7956             : }
    7957             : 
    7958             : /************************************************************************/
    7959             : /*                        GDALAlgorithmGetArg()                         */
    7960             : /************************************************************************/
    7961             : 
    7962             : /** Return an argument from its name.
    7963             :  *
    7964             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7965             :  *
    7966             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7967             :  * @param pszArgName Argument name. Must NOT be null.
    7968             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7969             :  * or nullptr in case of error
    7970             :  * @since 3.11
    7971             :  */
    7972      355400 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    7973             :                                       const char *pszArgName)
    7974             : {
    7975      355400 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7976      355400 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7977      710800 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7978      355400 :                                  /* isConst = */ true);
    7979      355400 :     if (!arg)
    7980           3 :         return nullptr;
    7981      355397 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7982             : }
    7983             : 
    7984             : /************************************************************************/
    7985             : /*                    GDALAlgorithmGetArgNonConst()                     */
    7986             : /************************************************************************/
    7987             : 
    7988             : /** Return an argument from its name, possibly allowing creation of user-provided
    7989             :  * argument if the algorithm allow it.
    7990             :  *
    7991             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7992             :  *
    7993             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7994             :  * @param pszArgName Argument name. Must NOT be null.
    7995             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7996             :  * or nullptr in case of error
    7997             :  * @since 3.12
    7998             :  */
    7999       11027 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
    8000             :                                               const char *pszArgName)
    8001             : {
    8002       11027 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    8003       11027 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    8004       22054 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    8005       11027 :                                  /* isConst = */ false);
    8006       11027 :     if (!arg)
    8007           2 :         return nullptr;
    8008       11025 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    8009             : }
    8010             : 
    8011             : /************************************************************************/
    8012             : /*                  GDALAlgorithmGetArgDependencies()                   */
    8013             : /************************************************************************/
    8014             : 
    8015             : /** Return the list of argument names the specified argument depends on.
    8016             :  *
    8017             :  *  This includes both regular dependencies and mutual dependencies.
    8018             :  *
    8019             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    8020             :  * @param pszArgName Argument name. Must NOT be null.
    8021             :  * @return a NULL terminated list of names, which must be destroyed with
    8022             :  * CSLDestroy()
    8023             :  * @since 3.11
    8024             :  */
    8025           7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
    8026             :                                        const char *pszArgName)
    8027             : {
    8028           7 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    8029           7 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    8030           7 :     return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
    8031             : }
    8032             : 
    8033             : /************************************************************************/
    8034             : /*                      GDALAlgorithmArgRelease()                       */
    8035             : /************************************************************************/
    8036             : 
    8037             : /** Release a handle to an argument.
    8038             :  *
    8039             :  * @since 3.11
    8040             :  */
    8041      366422 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    8042             : {
    8043      366422 :     delete hArg;
    8044      366422 : }
    8045             : 
    8046             : /************************************************************************/
    8047             : /*                      GDALAlgorithmArgGetName()                       */
    8048             : /************************************************************************/
    8049             : 
    8050             : /** Return the name of an argument.
    8051             :  *
    8052             :  * @param hArg Handle to an argument. Must NOT be null.
    8053             :  * @return argument name whose lifetime is bound to hArg and which must not
    8054             :  * be freed.
    8055             :  * @since 3.11
    8056             :  */
    8057       20268 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    8058             : {
    8059       20268 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8060       20268 :     return hArg->ptr->GetName().c_str();
    8061             : }
    8062             : 
    8063             : /************************************************************************/
    8064             : /*                      GDALAlgorithmArgGetType()                       */
    8065             : /************************************************************************/
    8066             : 
    8067             : /** Get the type of an argument
    8068             :  *
    8069             :  * @param hArg Handle to an argument. Must NOT be null.
    8070             :  * @since 3.11
    8071             :  */
    8072      444154 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    8073             : {
    8074      444154 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    8075      444154 :     return hArg->ptr->GetType();
    8076             : }
    8077             : 
    8078             : /************************************************************************/
    8079             : /*                   GDALAlgorithmArgGetDescription()                   */
    8080             : /************************************************************************/
    8081             : 
    8082             : /** Return the description of an argument.
    8083             :  *
    8084             :  * @param hArg Handle to an argument. Must NOT be null.
    8085             :  * @return argument description whose lifetime is bound to hArg and which must not
    8086             :  * be freed.
    8087             :  * @since 3.11
    8088             :  */
    8089       87987 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    8090             : {
    8091       87987 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8092       87987 :     return hArg->ptr->GetDescription().c_str();
    8093             : }
    8094             : 
    8095             : /************************************************************************/
    8096             : /*                    GDALAlgorithmArgGetShortName()                    */
    8097             : /************************************************************************/
    8098             : 
    8099             : /** Return the short name, or empty string if there is none
    8100             :  *
    8101             :  * @param hArg Handle to an argument. Must NOT be null.
    8102             :  * @return short name whose lifetime is bound to hArg and which must not
    8103             :  * be freed.
    8104             :  * @since 3.11
    8105             :  */
    8106           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    8107             : {
    8108           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8109           1 :     return hArg->ptr->GetShortName().c_str();
    8110             : }
    8111             : 
    8112             : /************************************************************************/
    8113             : /*                     GDALAlgorithmArgGetAliases()                     */
    8114             : /************************************************************************/
    8115             : 
    8116             : /** Return the aliases (potentially none)
    8117             :  *
    8118             :  * @param hArg Handle to an argument. Must NOT be null.
    8119             :  * @return a NULL terminated list of names, which must be destroyed with
    8120             :  * CSLDestroy()
    8121             : 
    8122             :  * @since 3.11
    8123             :  */
    8124      166051 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    8125             : {
    8126      166051 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8127      166051 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    8128             : }
    8129             : 
    8130             : /************************************************************************/
    8131             : /*                     GDALAlgorithmArgGetMetaVar()                     */
    8132             : /************************************************************************/
    8133             : 
    8134             : /** Return the "meta-var" hint.
    8135             :  *
    8136             :  * By default, the meta-var value is the long name of the argument in
    8137             :  * upper case.
    8138             :  *
    8139             :  * @param hArg Handle to an argument. Must NOT be null.
    8140             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    8141             :  * be freed.
    8142             :  * @since 3.11
    8143             :  */
    8144           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    8145             : {
    8146           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8147           1 :     return hArg->ptr->GetMetaVar().c_str();
    8148             : }
    8149             : 
    8150             : /************************************************************************/
    8151             : /*                    GDALAlgorithmArgGetCategory()                     */
    8152             : /************************************************************************/
    8153             : 
    8154             : /** Return the argument category
    8155             :  *
    8156             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    8157             :  *
    8158             :  * @param hArg Handle to an argument. Must NOT be null.
    8159             :  * @return category whose lifetime is bound to hArg and which must not
    8160             :  * be freed.
    8161             :  * @since 3.11
    8162             :  */
    8163           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    8164             : {
    8165           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8166           1 :     return hArg->ptr->GetCategory().c_str();
    8167             : }
    8168             : 
    8169             : /************************************************************************/
    8170             : /*                    GDALAlgorithmArgIsPositional()                    */
    8171             : /************************************************************************/
    8172             : 
    8173             : /** Return if the argument is a positional one.
    8174             :  *
    8175             :  * @param hArg Handle to an argument. Must NOT be null.
    8176             :  * @since 3.11
    8177             :  */
    8178           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    8179             : {
    8180           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8181           1 :     return hArg->ptr->IsPositional();
    8182             : }
    8183             : 
    8184             : /************************************************************************/
    8185             : /*                     GDALAlgorithmArgIsRequired()                     */
    8186             : /************************************************************************/
    8187             : 
    8188             : /** Return whether the argument is required. Defaults to false.
    8189             :  *
    8190             :  * @param hArg Handle to an argument. Must NOT be null.
    8191             :  * @since 3.11
    8192             :  */
    8193      166051 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    8194             : {
    8195      166051 :     VALIDATE_POINTER1(hArg, __func__, false);
    8196      166051 :     return hArg->ptr->IsRequired();
    8197             : }
    8198             : 
    8199             : /************************************************************************/
    8200             : /*                    GDALAlgorithmArgGetMinCount()                     */
    8201             : /************************************************************************/
    8202             : 
    8203             : /** Return the minimum number of values for the argument.
    8204             :  *
    8205             :  * Defaults to 0.
    8206             :  * Only applies to list type of arguments.
    8207             :  *
    8208             :  * @param hArg Handle to an argument. Must NOT be null.
    8209             :  * @since 3.11
    8210             :  */
    8211           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    8212             : {
    8213           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8214           1 :     return hArg->ptr->GetMinCount();
    8215             : }
    8216             : 
    8217             : /************************************************************************/
    8218             : /*                    GDALAlgorithmArgGetMaxCount()                     */
    8219             : /************************************************************************/
    8220             : 
    8221             : /** Return the maximum number of values for the argument.
    8222             :  *
    8223             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    8224             :  * Only applies to list type of arguments.
    8225             :  *
    8226             :  * @param hArg Handle to an argument. Must NOT be null.
    8227             :  * @since 3.11
    8228             :  */
    8229           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    8230             : {
    8231           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8232           1 :     return hArg->ptr->GetMaxCount();
    8233             : }
    8234             : 
    8235             : /************************************************************************/
    8236             : /*               GDALAlgorithmArgGetPackedValuesAllowed()               */
    8237             : /************************************************************************/
    8238             : 
    8239             : /** Return whether, for list type of arguments, several values, space
    8240             :  * separated, may be specified. That is "--foo=bar,baz".
    8241             :  * The default is true.
    8242             :  *
    8243             :  * @param hArg Handle to an argument. Must NOT be null.
    8244             :  * @since 3.11
    8245             :  */
    8246           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    8247             : {
    8248           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8249           1 :     return hArg->ptr->GetPackedValuesAllowed();
    8250             : }
    8251             : 
    8252             : /************************************************************************/
    8253             : /*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
    8254             : /************************************************************************/
    8255             : 
    8256             : /** Return whether, for list type of arguments, the argument may be
    8257             :  * repeated. That is "--foo=bar --foo=baz".
    8258             :  * The default is true.
    8259             :  *
    8260             :  * @param hArg Handle to an argument. Must NOT be null.
    8261             :  * @since 3.11
    8262             :  */
    8263           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    8264             : {
    8265           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8266           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    8267             : }
    8268             : 
    8269             : /************************************************************************/
    8270             : /*                     GDALAlgorithmArgGetChoices()                     */
    8271             : /************************************************************************/
    8272             : 
    8273             : /** Return the allowed values (as strings) for the argument.
    8274             :  *
    8275             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    8276             :  *
    8277             :  * @param hArg Handle to an argument. Must NOT be null.
    8278             :  * @return a NULL terminated list of names, which must be destroyed with
    8279             :  * CSLDestroy()
    8280             : 
    8281             :  * @since 3.11
    8282             :  */
    8283           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    8284             : {
    8285           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8286           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    8287             : }
    8288             : 
    8289             : /************************************************************************/
    8290             : /*                  GDALAlgorithmArgGetMetadataItem()                   */
    8291             : /************************************************************************/
    8292             : 
    8293             : /** Return the values of the metadata item of an argument.
    8294             :  *
    8295             :  * @param hArg Handle to an argument. Must NOT be null.
    8296             :  * @param pszItem Name of the item. Must NOT be null.
    8297             :  * @return a NULL terminated list of values, which must be destroyed with
    8298             :  * CSLDestroy()
    8299             : 
    8300             :  * @since 3.11
    8301             :  */
    8302          85 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
    8303             :                                        const char *pszItem)
    8304             : {
    8305          85 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8306          85 :     VALIDATE_POINTER1(pszItem, __func__, nullptr);
    8307          85 :     const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
    8308          85 :     return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
    8309             : }
    8310             : 
    8311             : /************************************************************************/
    8312             : /*                  GDALAlgorithmArgIsExplicitlySet()                   */
    8313             : /************************************************************************/
    8314             : 
    8315             : /** Return whether the argument value has been explicitly set with Set()
    8316             :  *
    8317             :  * @param hArg Handle to an argument. Must NOT be null.
    8318             :  * @since 3.11
    8319             :  */
    8320         790 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    8321             : {
    8322         790 :     VALIDATE_POINTER1(hArg, __func__, false);
    8323         790 :     return hArg->ptr->IsExplicitlySet();
    8324             : }
    8325             : 
    8326             : /************************************************************************/
    8327             : /*                  GDALAlgorithmArgHasDefaultValue()                   */
    8328             : /************************************************************************/
    8329             : 
    8330             : /** Return if the argument has a declared default value.
    8331             :  *
    8332             :  * @param hArg Handle to an argument. Must NOT be null.
    8333             :  * @since 3.11
    8334             :  */
    8335           2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    8336             : {
    8337           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8338           2 :     return hArg->ptr->HasDefaultValue();
    8339             : }
    8340             : 
    8341             : /************************************************************************/
    8342             : /*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
    8343             : /************************************************************************/
    8344             : 
    8345             : /** Return the argument default value as a integer.
    8346             :  *
    8347             :  * Must only be called on arguments whose type is GAAT_BOOLEAN
    8348             :  *
    8349             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8350             :  * argument has a default value.
    8351             :  *
    8352             :  * @param hArg Handle to an argument. Must NOT be null.
    8353             :  * @since 3.12
    8354             :  */
    8355           3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
    8356             : {
    8357           3 :     VALIDATE_POINTER1(hArg, __func__, false);
    8358           3 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8359             :     {
    8360           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8361             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8362             :                  __func__);
    8363           1 :         return false;
    8364             :     }
    8365           2 :     return hArg->ptr->GetDefault<bool>();
    8366             : }
    8367             : 
    8368             : /************************************************************************/
    8369             : /*                 GDALAlgorithmArgGetDefaultAsString()                 */
    8370             : /************************************************************************/
    8371             : 
    8372             : /** Return the argument default value as a string.
    8373             :  *
    8374             :  * Must only be called on arguments whose type is GAAT_STRING.
    8375             :  *
    8376             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8377             :  * argument has a default value.
    8378             :  *
    8379             :  * @param hArg Handle to an argument. Must NOT be null.
    8380             :  * @return string whose lifetime is bound to hArg and which must not
    8381             :  * be freed.
    8382             :  * @since 3.11
    8383             :  */
    8384           3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
    8385             : {
    8386           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8387           3 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8388             :     {
    8389           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8390             :                  "%s must only be called on arguments of type GAAT_STRING",
    8391             :                  __func__);
    8392           2 :         return nullptr;
    8393             :     }
    8394           1 :     return hArg->ptr->GetDefault<std::string>().c_str();
    8395             : }
    8396             : 
    8397             : /************************************************************************/
    8398             : /*                GDALAlgorithmArgGetDefaultAsInteger()                 */
    8399             : /************************************************************************/
    8400             : 
    8401             : /** Return the argument default value as a integer.
    8402             :  *
    8403             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8404             :  *
    8405             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8406             :  * argument has a default value.
    8407             :  *
    8408             :  * @param hArg Handle to an argument. Must NOT be null.
    8409             :  * @since 3.12
    8410             :  */
    8411           3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
    8412             : {
    8413           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8414           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8415             :     {
    8416           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8417             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8418             :                  __func__);
    8419           2 :         return 0;
    8420             :     }
    8421           1 :     return hArg->ptr->GetDefault<int>();
    8422             : }
    8423             : 
    8424             : /************************************************************************/
    8425             : /*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
    8426             : /************************************************************************/
    8427             : 
    8428             : /** Return the argument default value as a double.
    8429             :  *
    8430             :  * Must only be called on arguments whose type is GAAT_REAL
    8431             :  *
    8432             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8433             :  * argument has a default value.
    8434             :  *
    8435             :  * @param hArg Handle to an argument. Must NOT be null.
    8436             :  * @since 3.12
    8437             :  */
    8438           3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
    8439             : {
    8440           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8441           3 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8442             :     {
    8443           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8444             :                  "%s must only be called on arguments of type GAAT_REAL",
    8445             :                  __func__);
    8446           2 :         return 0;
    8447             :     }
    8448           1 :     return hArg->ptr->GetDefault<double>();
    8449             : }
    8450             : 
    8451             : /************************************************************************/
    8452             : /*               GDALAlgorithmArgGetDefaultAsStringList()               */
    8453             : /************************************************************************/
    8454             : 
    8455             : /** Return the argument default value as a string list.
    8456             :  *
    8457             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8458             :  *
    8459             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8460             :  * argument has a default value.
    8461             :  *
    8462             :  * @param hArg Handle to an argument. Must NOT be null.
    8463             :  * @return a NULL terminated list of names, which must be destroyed with
    8464             :  * CSLDestroy()
    8465             : 
    8466             :  * @since 3.12
    8467             :  */
    8468           3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
    8469             : {
    8470           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8471           3 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8472             :     {
    8473           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8474             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8475             :                  __func__);
    8476           2 :         return nullptr;
    8477             :     }
    8478           2 :     return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
    8479           1 :         .StealList();
    8480             : }
    8481             : 
    8482             : /************************************************************************/
    8483             : /*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
    8484             : /************************************************************************/
    8485             : 
    8486             : /** Return the argument default value as a integer list.
    8487             :  *
    8488             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8489             :  *
    8490             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8491             :  * argument has a default value.
    8492             :  *
    8493             :  * @param hArg Handle to an argument. Must NOT be null.
    8494             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8495             :  * @since 3.12
    8496             :  */
    8497           3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
    8498             :                                                    size_t *pnCount)
    8499             : {
    8500           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8501           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8502           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8503             :     {
    8504           2 :         CPLError(
    8505             :             CE_Failure, CPLE_AppDefined,
    8506             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8507             :             __func__);
    8508           2 :         *pnCount = 0;
    8509           2 :         return nullptr;
    8510             :     }
    8511           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
    8512           1 :     *pnCount = val.size();
    8513           1 :     return val.data();
    8514             : }
    8515             : 
    8516             : /************************************************************************/
    8517             : /*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
    8518             : /************************************************************************/
    8519             : 
    8520             : /** Return the argument default value as a real list.
    8521             :  *
    8522             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8523             :  *
    8524             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8525             :  * argument has a default value.
    8526             :  *
    8527             :  * @param hArg Handle to an argument. Must NOT be null.
    8528             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8529             :  * @since 3.12
    8530             :  */
    8531           3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
    8532             :                                                      size_t *pnCount)
    8533             : {
    8534           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8535           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8536           3 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8537             :     {
    8538           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8539             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8540             :                  __func__);
    8541           2 :         *pnCount = 0;
    8542           2 :         return nullptr;
    8543             :     }
    8544           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
    8545           1 :     *pnCount = val.size();
    8546           1 :     return val.data();
    8547             : }
    8548             : 
    8549             : /************************************************************************/
    8550             : /*                      GDALAlgorithmArgIsHidden()                      */
    8551             : /************************************************************************/
    8552             : 
    8553             : /** Return whether the argument is hidden (for GDAL internal use)
    8554             :  *
    8555             :  * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
    8556             :  * GDALAlgorithmArgIsHiddenForAPI().
    8557             :  *
    8558             :  * @param hArg Handle to an argument. Must NOT be null.
    8559             :  * @since 3.12
    8560             :  */
    8561           1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
    8562             : {
    8563           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8564           1 :     return hArg->ptr->IsHidden();
    8565             : }
    8566             : 
    8567             : /************************************************************************/
    8568             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    8569             : /************************************************************************/
    8570             : 
    8571             : /** Return whether the argument must not be mentioned in CLI usage.
    8572             :  *
    8573             :  * For example, "output-value" for "gdal raster info", which is only
    8574             :  * meant when the algorithm is used from a non-CLI context.
    8575             :  *
    8576             :  * @param hArg Handle to an argument. Must NOT be null.
    8577             :  * @since 3.11
    8578             :  */
    8579           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    8580             : {
    8581           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8582           1 :     return hArg->ptr->IsHiddenForCLI();
    8583             : }
    8584             : 
    8585             : /************************************************************************/
    8586             : /*                   GDALAlgorithmArgIsHiddenForAPI()                   */
    8587             : /************************************************************************/
    8588             : 
    8589             : /** Return whether the argument must not be mentioned in the context of an
    8590             :  * API use.
    8591             :  * Said otherwise, if it is only for CLI usage.
    8592             :  *
    8593             :  * For example "--help"
    8594             :  *
    8595             :  * @param hArg Handle to an argument. Must NOT be null.
    8596             :  * @since 3.12
    8597             :  */
    8598      229314 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
    8599             : {
    8600      229314 :     VALIDATE_POINTER1(hArg, __func__, false);
    8601      229314 :     return hArg->ptr->IsHiddenForAPI();
    8602             : }
    8603             : 
    8604             : /************************************************************************/
    8605             : /*                    GDALAlgorithmArgIsOnlyForCLI()                    */
    8606             : /************************************************************************/
    8607             : 
    8608             : /** Return whether the argument must not be mentioned in the context of an
    8609             :  * API use.
    8610             :  * Said otherwise, if it is only for CLI usage.
    8611             :  *
    8612             :  * For example "--help"
    8613             :  *
    8614             :  * @param hArg Handle to an argument. Must NOT be null.
    8615             :  * @since 3.11
    8616             :  * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
    8617             :  */
    8618           0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    8619             : {
    8620           0 :     VALIDATE_POINTER1(hArg, __func__, false);
    8621           0 :     return hArg->ptr->IsHiddenForAPI();
    8622             : }
    8623             : 
    8624             : /************************************************************************/
    8625             : /*             GDALAlgorithmArgIsAvailableInPipelineStep()              */
    8626             : /************************************************************************/
    8627             : 
    8628             : /** Return whether the argument is available in a pipeline step.
    8629             :  *
    8630             :  * If false, it is only available in standalone mode.
    8631             :  *
    8632             :  * @param hArg Handle to an argument. Must NOT be null.
    8633             :  * @since 3.13
    8634             :  */
    8635           2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
    8636             : {
    8637           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8638           2 :     return hArg->ptr->IsAvailableInPipelineStep();
    8639             : }
    8640             : 
    8641             : /************************************************************************/
    8642             : /*                      GDALAlgorithmArgIsInput()                       */
    8643             : /************************************************************************/
    8644             : 
    8645             : /** Indicate whether the value of the argument is read-only during the
    8646             :  * execution of the algorithm.
    8647             :  *
    8648             :  * Default is true.
    8649             :  *
    8650             :  * @param hArg Handle to an argument. Must NOT be null.
    8651             :  * @since 3.11
    8652             :  */
    8653      226567 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    8654             : {
    8655      226567 :     VALIDATE_POINTER1(hArg, __func__, false);
    8656      226567 :     return hArg->ptr->IsInput();
    8657             : }
    8658             : 
    8659             : /************************************************************************/
    8660             : /*                      GDALAlgorithmArgIsOutput()                      */
    8661             : /************************************************************************/
    8662             : 
    8663             : /** Return whether (at least part of) the value of the argument is set
    8664             :  * during the execution of the algorithm.
    8665             :  *
    8666             :  * For example, "output-value" for "gdal raster info"
    8667             :  * Default is false.
    8668             :  * An argument may return both IsInput() and IsOutput() as true.
    8669             :  * For example the "gdal raster convert" algorithm consumes the dataset
    8670             :  * name of its "output" argument, and sets the dataset object during its
    8671             :  * execution.
    8672             :  *
    8673             :  * @param hArg Handle to an argument. Must NOT be null.
    8674             :  * @since 3.11
    8675             :  */
    8676      127641 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    8677             : {
    8678      127641 :     VALIDATE_POINTER1(hArg, __func__, false);
    8679      127641 :     return hArg->ptr->IsOutput();
    8680             : }
    8681             : 
    8682             : /************************************************************************/
    8683             : /*                   GDALAlgorithmArgGetDatasetType()                   */
    8684             : /************************************************************************/
    8685             : 
    8686             : /** Get which type of dataset is allowed / generated.
    8687             :  *
    8688             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    8689             :  * GDAL_OF_MULTIDIM_RASTER.
    8690             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8691             :  *
    8692             :  * @param hArg Handle to an argument. Must NOT be null.
    8693             :  * @since 3.11
    8694             :  */
    8695           2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
    8696             : {
    8697           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8698           2 :     return hArg->ptr->GetDatasetType();
    8699             : }
    8700             : 
    8701             : /************************************************************************/
    8702             : /*                GDALAlgorithmArgGetDatasetInputFlags()                */
    8703             : /************************************************************************/
    8704             : 
    8705             : /** Indicates which components among name and dataset are accepted as
    8706             :  * input, when this argument serves as an input.
    8707             :  *
    8708             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    8709             :  * input.
    8710             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8711             :  * accepted as input.
    8712             :  * If both bits are set, the algorithm can accept either a name or a dataset
    8713             :  * object.
    8714             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8715             :  *
    8716             :  * @param hArg Handle to an argument. Must NOT be null.
    8717             :  * @return string whose lifetime is bound to hAlg and which must not
    8718             :  * be freed.
    8719             :  * @since 3.11
    8720             :  */
    8721           2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
    8722             : {
    8723           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8724           2 :     return hArg->ptr->GetDatasetInputFlags();
    8725             : }
    8726             : 
    8727             : /************************************************************************/
    8728             : /*               GDALAlgorithmArgGetDatasetOutputFlags()                */
    8729             : /************************************************************************/
    8730             : 
    8731             : /** Indicates which components among name and dataset are modified,
    8732             :  * when this argument serves as an output.
    8733             :  *
    8734             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    8735             :  * output (that is the algorithm will generate the name. Rarely used).
    8736             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8737             :  * generated as output, and available for use after the algorithm has
    8738             :  * completed.
    8739             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8740             :  *
    8741             :  * @param hArg Handle to an argument. Must NOT be null.
    8742             :  * @return string whose lifetime is bound to hAlg and which must not
    8743             :  * be freed.
    8744             :  * @since 3.11
    8745             :  */
    8746           2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
    8747             : {
    8748           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8749           2 :     return hArg->ptr->GetDatasetOutputFlags();
    8750             : }
    8751             : 
    8752             : /************************************************************************/
    8753             : /*              GDALAlgorithmArgGetMutualExclusionGroup()               */
    8754             : /************************************************************************/
    8755             : 
    8756             : /** Return the name of the mutual exclusion group to which this argument
    8757             :  * belongs to.
    8758             :  *
    8759             :  * Or empty string if it does not belong to any exclusion group.
    8760             :  *
    8761             :  * @param hArg Handle to an argument. Must NOT be null.
    8762             :  * @return string whose lifetime is bound to hArg and which must not
    8763             :  * be freed.
    8764             :  * @since 3.11
    8765             :  */
    8766           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    8767             : {
    8768           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8769           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    8770             : }
    8771             : 
    8772             : /************************************************************************/
    8773             : /*              GDALAlgorithmArgGetMutualDependencyGroup()              */
    8774             : /************************************************************************/
    8775             : 
    8776             : /** Return the name of the mutual dependency group to which this argument
    8777             :  * belongs to.
    8778             :  *
    8779             :  * Or empty string if it does not belong to any dependency group.
    8780             :  *
    8781             :  * @param hArg Handle to an argument. Must NOT be null.
    8782             :  * @return string whose lifetime is bound to hArg and which must not
    8783             :  * be freed.
    8784             :  * @since 3.13
    8785             :  */
    8786           5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
    8787             : {
    8788           5 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8789           5 :     return hArg->ptr->GetMutualDependencyGroup().c_str();
    8790             : }
    8791             : 
    8792             : /************************************************************************/
    8793             : /*               GDALAlgorithmArgGetDirectDependencies()                */
    8794             : /************************************************************************/
    8795             : 
    8796             : /** Return the list of names of arguments that this argument depends on.
    8797             :  *
    8798             :  *  This is not necessarily a symmetric relationship.
    8799             :  *  If argument A depends on argument B, it doesn't mean that B depends on A.
    8800             :  *  Mutual dependency groups are a special case of dependencies,
    8801             :  *  where all arguments of the group depend on each other and are not
    8802             :  *  returned by this method.
    8803             :  *
    8804             :  * @param hArg Handle to an argument. Must NOT be null.
    8805             :  * @return a NULL terminated list of names, which must be destroyed with
    8806             :  * CSLDestroy()
    8807             :  * @since 3.13
    8808             :  */
    8809           7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
    8810             : {
    8811           7 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8812           7 :     return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
    8813             : }
    8814             : 
    8815             : /************************************************************************/
    8816             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    8817             : /************************************************************************/
    8818             : 
    8819             : /** Return the argument value as a boolean.
    8820             :  *
    8821             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    8822             :  *
    8823             :  * @param hArg Handle to an argument. Must NOT be null.
    8824             :  * @since 3.11
    8825             :  */
    8826           8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    8827             : {
    8828           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    8829           8 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8830             :     {
    8831           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8832             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8833             :                  __func__);
    8834           1 :         return false;
    8835             :     }
    8836           7 :     return hArg->ptr->Get<bool>();
    8837             : }
    8838             : 
    8839             : /************************************************************************/
    8840             : /*                    GDALAlgorithmArgGetAsString()                     */
    8841             : /************************************************************************/
    8842             : 
    8843             : /** Return the argument value as a string.
    8844             :  *
    8845             :  * Must only be called on arguments whose type is GAAT_STRING.
    8846             :  *
    8847             :  * @param hArg Handle to an argument. Must NOT be null.
    8848             :  * @return string whose lifetime is bound to hArg and which must not
    8849             :  * be freed.
    8850             :  * @since 3.11
    8851             :  */
    8852         441 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    8853             : {
    8854         441 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8855         441 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8856             :     {
    8857           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8858             :                  "%s must only be called on arguments of type GAAT_STRING",
    8859             :                  __func__);
    8860           1 :         return nullptr;
    8861             :     }
    8862         440 :     return hArg->ptr->Get<std::string>().c_str();
    8863             : }
    8864             : 
    8865             : /************************************************************************/
    8866             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    8867             : /************************************************************************/
    8868             : 
    8869             : /** Return the argument value as a GDALArgDatasetValueH.
    8870             :  *
    8871             :  * Must only be called on arguments whose type is GAAT_DATASET
    8872             :  *
    8873             :  * @param hArg Handle to an argument. Must NOT be null.
    8874             :  * @return handle to a GDALArgDatasetValue that must be released with
    8875             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    8876             :  * the one of hArg.
    8877             :  * @since 3.11
    8878             :  */
    8879        3515 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    8880             : {
    8881        3515 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8882        3515 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    8883             :     {
    8884           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8885             :                  "%s must only be called on arguments of type GAAT_DATASET",
    8886             :                  __func__);
    8887           1 :         return nullptr;
    8888             :     }
    8889        3514 :     return std::make_unique<GDALArgDatasetValueHS>(
    8890        7028 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    8891        3514 :         .release();
    8892             : }
    8893             : 
    8894             : /************************************************************************/
    8895             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    8896             : /************************************************************************/
    8897             : 
    8898             : /** Return the argument value as a integer.
    8899             :  *
    8900             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8901             :  *
    8902             :  * @param hArg Handle to an argument. Must NOT be null.
    8903             :  * @since 3.11
    8904             :  */
    8905          26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    8906             : {
    8907          26 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8908          26 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8909             :     {
    8910           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8911             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8912             :                  __func__);
    8913           1 :         return 0;
    8914             :     }
    8915          25 :     return hArg->ptr->Get<int>();
    8916             : }
    8917             : 
    8918             : /************************************************************************/
    8919             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    8920             : /************************************************************************/
    8921             : 
    8922             : /** Return the argument value as a double.
    8923             :  *
    8924             :  * Must only be called on arguments whose type is GAAT_REAL
    8925             :  *
    8926             :  * @param hArg Handle to an argument. Must NOT be null.
    8927             :  * @since 3.11
    8928             :  */
    8929          16 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    8930             : {
    8931          16 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8932          16 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8933             :     {
    8934           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8935             :                  "%s must only be called on arguments of type GAAT_REAL",
    8936             :                  __func__);
    8937           1 :         return 0;
    8938             :     }
    8939          15 :     return hArg->ptr->Get<double>();
    8940             : }
    8941             : 
    8942             : /************************************************************************/
    8943             : /*                  GDALAlgorithmArgGetAsStringList()                   */
    8944             : /************************************************************************/
    8945             : 
    8946             : /** Return the argument value as a string list.
    8947             :  *
    8948             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8949             :  *
    8950             :  * @param hArg Handle to an argument. Must NOT be null.
    8951             :  * @return a NULL terminated list of names, which must be destroyed with
    8952             :  * CSLDestroy()
    8953             : 
    8954             :  * @since 3.11
    8955             :  */
    8956           4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    8957             : {
    8958           4 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8959           4 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8960             :     {
    8961           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8962             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8963             :                  __func__);
    8964           1 :         return nullptr;
    8965             :     }
    8966           6 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    8967           3 :         .StealList();
    8968             : }
    8969             : 
    8970             : /************************************************************************/
    8971             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    8972             : /************************************************************************/
    8973             : 
    8974             : /** Return the argument value as a integer list.
    8975             :  *
    8976             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8977             :  *
    8978             :  * @param hArg Handle to an argument. Must NOT be null.
    8979             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8980             :  * @since 3.11
    8981             :  */
    8982           8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    8983             :                                             size_t *pnCount)
    8984             : {
    8985           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8986           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8987           8 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8988             :     {
    8989           1 :         CPLError(
    8990             :             CE_Failure, CPLE_AppDefined,
    8991             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8992             :             __func__);
    8993           1 :         *pnCount = 0;
    8994           1 :         return nullptr;
    8995             :     }
    8996           7 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    8997           7 :     *pnCount = val.size();
    8998           7 :     return val.data();
    8999             : }
    9000             : 
    9001             : /************************************************************************/
    9002             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    9003             : /************************************************************************/
    9004             : 
    9005             : /** Return the argument value as a real list.
    9006             :  *
    9007             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    9008             :  *
    9009             :  * @param hArg Handle to an argument. Must NOT be null.
    9010             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    9011             :  * @since 3.11
    9012             :  */
    9013           8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    9014             :                                               size_t *pnCount)
    9015             : {
    9016           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    9017           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    9018           8 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    9019             :     {
    9020           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    9021             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    9022             :                  __func__);
    9023           1 :         *pnCount = 0;
    9024           1 :         return nullptr;
    9025             :     }
    9026           7 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    9027           7 :     *pnCount = val.size();
    9028           7 :     return val.data();
    9029             : }
    9030             : 
    9031             : /************************************************************************/
    9032             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    9033             : /************************************************************************/
    9034             : 
    9035             : /** Set the value for a GAAT_BOOLEAN argument.
    9036             :  *
    9037             :  * It cannot be called several times for a given argument.
    9038             :  * Validation checks and other actions are run.
    9039             :  *
    9040             :  * @param hArg Handle to an argument. Must NOT be null.
    9041             :  * @param value value.
    9042             :  * @return true if success.
    9043             :  * @since 3.11
    9044             :  */
    9045             : 
    9046         778 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    9047             : {
    9048         778 :     VALIDATE_POINTER1(hArg, __func__, false);
    9049         778 :     return hArg->ptr->Set(value);
    9050             : }
    9051             : 
    9052             : /************************************************************************/
    9053             : /*                    GDALAlgorithmArgSetAsString()                     */
    9054             : /************************************************************************/
    9055             : 
    9056             : /** Set the value for a GAAT_STRING argument.
    9057             :  *
    9058             :  * It cannot be called several times for a given argument.
    9059             :  * Validation checks and other actions are run.
    9060             :  *
    9061             :  * @param hArg Handle to an argument. Must NOT be null.
    9062             :  * @param value value (may be null)
    9063             :  * @return true if success.
    9064             :  * @since 3.11
    9065             :  */
    9066             : 
    9067        3481 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    9068             : {
    9069        3481 :     VALIDATE_POINTER1(hArg, __func__, false);
    9070        3481 :     return hArg->ptr->Set(value ? value : "");
    9071             : }
    9072             : 
    9073             : /************************************************************************/
    9074             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    9075             : /************************************************************************/
    9076             : 
    9077             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    9078             :  *
    9079             :  * It cannot be called several times for a given argument.
    9080             :  * Validation checks and other actions are run.
    9081             :  *
    9082             :  * @param hArg Handle to an argument. Must NOT be null.
    9083             :  * @param value value.
    9084             :  * @return true if success.
    9085             :  * @since 3.11
    9086             :  */
    9087             : 
    9088         495 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    9089             : {
    9090         495 :     VALIDATE_POINTER1(hArg, __func__, false);
    9091         495 :     return hArg->ptr->Set(value);
    9092             : }
    9093             : 
    9094             : /************************************************************************/
    9095             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    9096             : /************************************************************************/
    9097             : 
    9098             : /** Set the value for a GAAT_REAL argument.
    9099             :  *
    9100             :  * It cannot be called several times for a given argument.
    9101             :  * Validation checks and other actions are run.
    9102             :  *
    9103             :  * @param hArg Handle to an argument. Must NOT be null.
    9104             :  * @param value value.
    9105             :  * @return true if success.
    9106             :  * @since 3.11
    9107             :  */
    9108             : 
    9109         313 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    9110             : {
    9111         313 :     VALIDATE_POINTER1(hArg, __func__, false);
    9112         313 :     return hArg->ptr->Set(value);
    9113             : }
    9114             : 
    9115             : /************************************************************************/
    9116             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    9117             : /************************************************************************/
    9118             : 
    9119             : /** Set the value for a GAAT_DATASET argument.
    9120             :  *
    9121             :  * It cannot be called several times for a given argument.
    9122             :  * Validation checks and other actions are run.
    9123             :  *
    9124             :  * @param hArg Handle to an argument. Must NOT be null.
    9125             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    9126             :  * @return true if success.
    9127             :  * @since 3.11
    9128             :  */
    9129           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    9130             :                                        GDALArgDatasetValueH value)
    9131             : {
    9132           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    9133           2 :     VALIDATE_POINTER1(value, __func__, false);
    9134           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    9135             : }
    9136             : 
    9137             : /************************************************************************/
    9138             : /*                     GDALAlgorithmArgSetDataset()                     */
    9139             : /************************************************************************/
    9140             : 
    9141             : /** Set dataset object, increasing its reference counter.
    9142             :  *
    9143             :  * @param hArg Handle to an argument. Must NOT be null.
    9144             :  * @param hDS Dataset object. May be null.
    9145             :  * @return true if success.
    9146             :  * @since 3.11
    9147             :  */
    9148             : 
    9149           2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    9150             : {
    9151           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    9152           2 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    9153             : }
    9154             : 
    9155             : /************************************************************************/
    9156             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    9157             : /************************************************************************/
    9158             : 
    9159             : /** Set the value for a GAAT_STRING_LIST argument.
    9160             :  *
    9161             :  * It cannot be called several times for a given argument.
    9162             :  * Validation checks and other actions are run.
    9163             :  *
    9164             :  * @param hArg Handle to an argument. Must NOT be null.
    9165             :  * @param value value as a NULL terminated list (may be null)
    9166             :  * @return true if success.
    9167             :  * @since 3.11
    9168             :  */
    9169             : 
    9170         975 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    9171             : {
    9172         975 :     VALIDATE_POINTER1(hArg, __func__, false);
    9173         975 :     return hArg->ptr->Set(
    9174        1950 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    9175             : }
    9176             : 
    9177             : /************************************************************************/
    9178             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    9179             : /************************************************************************/
    9180             : 
    9181             : /** Set the value for a GAAT_INTEGER_LIST argument.
    9182             :  *
    9183             :  * It cannot be called several times for a given argument.
    9184             :  * Validation checks and other actions are run.
    9185             :  *
    9186             :  * @param hArg Handle to an argument. Must NOT be null.
    9187             :  * @param nCount Number of values in pnValues.
    9188             :  * @param pnValues Pointer to an array of integer values of size nCount.
    9189             :  * @return true if success.
    9190             :  * @since 3.11
    9191             :  */
    9192          65 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    9193             :                                       const int *pnValues)
    9194             : {
    9195          65 :     VALIDATE_POINTER1(hArg, __func__, false);
    9196          65 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    9197             : }
    9198             : 
    9199             : /************************************************************************/
    9200             : /*                  GDALAlgorithmArgSetAsDoubleList()                   */
    9201             : /************************************************************************/
    9202             : 
    9203             : /** Set the value for a GAAT_REAL_LIST argument.
    9204             :  *
    9205             :  * It cannot be called several times for a given argument.
    9206             :  * Validation checks and other actions are run.
    9207             :  *
    9208             :  * @param hArg Handle to an argument. Must NOT be null.
    9209             :  * @param nCount Number of values in pnValues.
    9210             :  * @param pnValues Pointer to an array of double values of size nCount.
    9211             :  * @return true if success.
    9212             :  * @since 3.11
    9213             :  */
    9214         240 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    9215             :                                      const double *pnValues)
    9216             : {
    9217         240 :     VALIDATE_POINTER1(hArg, __func__, false);
    9218         240 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    9219             : }
    9220             : 
    9221             : /************************************************************************/
    9222             : /*                    GDALAlgorithmArgSetDatasets()                     */
    9223             : /************************************************************************/
    9224             : 
    9225             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    9226             :  *
    9227             :  * @param hArg Handle to an argument. Must NOT be null.
    9228             :  * @param nCount Number of values in pnValues.
    9229             :  * @param pahDS Pointer to an array of dataset of size nCount.
    9230             :  * @return true if success.
    9231             :  * @since 3.11
    9232             :  */
    9233             : 
    9234        1495 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    9235             :                                  GDALDatasetH *pahDS)
    9236             : {
    9237        1495 :     VALIDATE_POINTER1(hArg, __func__, false);
    9238        2990 :     std::vector<GDALArgDatasetValue> values;
    9239        3016 :     for (size_t i = 0; i < nCount; ++i)
    9240             :     {
    9241        1521 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    9242             :     }
    9243        1495 :     return hArg->ptr->Set(std::move(values));
    9244             : }
    9245             : 
    9246             : /************************************************************************/
    9247             : /*                  GDALAlgorithmArgSetDatasetNames()                   */
    9248             : /************************************************************************/
    9249             : 
    9250             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    9251             :  *
    9252             :  * @param hArg Handle to an argument. Must NOT be null.
    9253             :  * @param names Dataset names as a NULL terminated list (may be null)
    9254             :  * @return true if success.
    9255             :  * @since 3.11
    9256             :  */
    9257             : 
    9258         850 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    9259             : {
    9260         850 :     VALIDATE_POINTER1(hArg, __func__, false);
    9261        1700 :     std::vector<GDALArgDatasetValue> values;
    9262        1773 :     for (size_t i = 0; names[i]; ++i)
    9263             :     {
    9264         923 :         values.emplace_back(names[i]);
    9265             :     }
    9266         850 :     return hArg->ptr->Set(std::move(values));
    9267             : }
    9268             : 
    9269             : /************************************************************************/
    9270             : /*                     GDALArgDatasetValueCreate()                      */
    9271             : /************************************************************************/
    9272             : 
    9273             : /** Instantiate an empty GDALArgDatasetValue
    9274             :  *
    9275             :  * @return new handle to free with GDALArgDatasetValueRelease()
    9276             :  * @since 3.11
    9277             :  */
    9278           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    9279             : {
    9280           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    9281             : }
    9282             : 
    9283             : /************************************************************************/
    9284             : /*                     GDALArgDatasetValueRelease()                     */
    9285             : /************************************************************************/
    9286             : 
    9287             : /** Release a handle to a GDALArgDatasetValue
    9288             :  *
    9289             :  * @since 3.11
    9290             :  */
    9291        3515 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    9292             : {
    9293        3515 :     delete hValue;
    9294        3515 : }
    9295             : 
    9296             : /************************************************************************/
    9297             : /*                     GDALArgDatasetValueGetName()                     */
    9298             : /************************************************************************/
    9299             : 
    9300             : /** Return the name component of the GDALArgDatasetValue
    9301             :  *
    9302             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9303             :  * @return string whose lifetime is bound to hAlg and which must not
    9304             :  * be freed.
    9305             :  * @since 3.11
    9306             :  */
    9307           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    9308             : {
    9309           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9310           1 :     return hValue->ptr->GetName().c_str();
    9311             : }
    9312             : 
    9313             : /************************************************************************/
    9314             : /*                  GDALArgDatasetValueGetDatasetRef()                  */
    9315             : /************************************************************************/
    9316             : 
    9317             : /** Return the dataset component of the GDALArgDatasetValue.
    9318             :  *
    9319             :  * This does not modify the reference counter, hence the lifetime of the
    9320             :  * returned object is not guaranteed to exceed the one of hValue.
    9321             :  *
    9322             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9323             :  * @since 3.11
    9324             :  */
    9325           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    9326             : {
    9327           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9328           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    9329             : }
    9330             : 
    9331             : /************************************************************************/
    9332             : /*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
    9333             : /************************************************************************/
    9334             : 
    9335             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    9336             :  * reference count if not null. Once done with the dataset, the caller should
    9337             :  * call GDALReleaseDataset().
    9338             :  *
    9339             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9340             :  * @since 3.11
    9341             :  */
    9342             : GDALDatasetH
    9343        1184 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    9344             : {
    9345        1184 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9346        1184 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    9347             : }
    9348             : 
    9349             : /************************************************************************/
    9350             : /*                     GDALArgDatasetValueSetName()                     */
    9351             : /************************************************************************/
    9352             : 
    9353             : /** Set dataset name
    9354             :  *
    9355             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9356             :  * @param pszName Dataset name. May be null.
    9357             :  * @since 3.11
    9358             :  */
    9359             : 
    9360        1510 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    9361             :                                 const char *pszName)
    9362             : {
    9363        1510 :     VALIDATE_POINTER0(hValue, __func__);
    9364        1510 :     hValue->ptr->Set(pszName ? pszName : "");
    9365             : }
    9366             : 
    9367             : /************************************************************************/
    9368             : /*                   GDALArgDatasetValueSetDataset()                    */
    9369             : /************************************************************************/
    9370             : 
    9371             : /** Set dataset object, increasing its reference counter.
    9372             :  *
    9373             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9374             :  * @param hDS Dataset object. May be null.
    9375             :  * @since 3.11
    9376             :  */
    9377             : 
    9378         806 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    9379             :                                    GDALDatasetH hDS)
    9380             : {
    9381         806 :     VALIDATE_POINTER0(hValue, __func__);
    9382         806 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    9383             : }

Generated by: LCOV version 1.14