LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3448 3665 94.1 %
Date: 2026-02-03 04:34:44 Functions: 267 274 97.4 %

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

Generated by: LCOV version 1.14