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

Generated by: LCOV version 1.14