LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1966 2004 98.1 %
Date: 2025-01-18 12:42:00 Functions: 171 172 99.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_minixml.h"
      18             : 
      19             : #include "gdalalgorithm.h"
      20             : #include "gdal_priv.h"
      21             : 
      22             : #include <algorithm>
      23             : #include <cassert>
      24             : #include <cerrno>
      25             : #include <cstdlib>
      26             : #include <map>
      27             : 
      28             : #ifndef _
      29             : #define _(x) (x)
      30             : #endif
      31             : 
      32             : constexpr const char *GDAL_ARG_NAME_INPUT_FORMAT = "input-format";
      33             : 
      34             : constexpr const char *GDAL_ARG_NAME_OUTPUT_FORMAT = "output-format";
      35             : 
      36             : constexpr const char *GDAL_ARG_NAME_OPEN_OPTION = "open-option";
      37             : 
      38             : //! @cond Doxygen_Suppress
      39             : struct GDALAlgorithmArgHS
      40             : {
      41             :     GDALAlgorithmArg *ptr = nullptr;
      42             : 
      43         152 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      44             :     {
      45         152 :     }
      46             : };
      47             : 
      48             : //! @endcond
      49             : 
      50             : //! @cond Doxygen_Suppress
      51             : struct GDALArgDatasetValueHS
      52             : {
      53             :     GDALArgDatasetValue val{};
      54             :     GDALArgDatasetValue *ptr = nullptr;
      55             : 
      56           1 :     GDALArgDatasetValueHS() : ptr(&val)
      57             :     {
      58           1 :     }
      59             : 
      60          60 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      61             :     {
      62          60 :     }
      63             : 
      64             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      65             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      66             : };
      67             : 
      68             : //! @endcond
      69             : 
      70             : /************************************************************************/
      71             : /*                     GDALAlgorithmArgTypeIsList()                     */
      72             : /************************************************************************/
      73             : 
      74       13142 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      75             : {
      76       13142 :     switch (type)
      77             :     {
      78        9514 :         case GAAT_BOOLEAN:
      79             :         case GAAT_STRING:
      80             :         case GAAT_INTEGER:
      81             :         case GAAT_REAL:
      82             :         case GAAT_DATASET:
      83        9514 :             break;
      84             : 
      85        3628 :         case GAAT_STRING_LIST:
      86             :         case GAAT_INTEGER_LIST:
      87             :         case GAAT_REAL_LIST:
      88             :         case GAAT_DATASET_LIST:
      89        3628 :             return true;
      90             :     }
      91             : 
      92        9514 :     return false;
      93             : }
      94             : 
      95             : /************************************************************************/
      96             : /*                     GDALAlgorithmArgTypeName()                       */
      97             : /************************************************************************/
      98             : 
      99         462 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     100             : {
     101         462 :     switch (type)
     102             :     {
     103         115 :         case GAAT_BOOLEAN:
     104         115 :             break;
     105         100 :         case GAAT_STRING:
     106         100 :             return "string";
     107           9 :         case GAAT_INTEGER:
     108           9 :             return "integer";
     109           4 :         case GAAT_REAL:
     110           4 :             return "real";
     111          68 :         case GAAT_DATASET:
     112          68 :             return "dataset";
     113         118 :         case GAAT_STRING_LIST:
     114         118 :             return "string_list";
     115           7 :         case GAAT_INTEGER_LIST:
     116           7 :             return "integer_list";
     117          37 :         case GAAT_REAL_LIST:
     118          37 :             return "real_list";
     119           4 :         case GAAT_DATASET_LIST:
     120           4 :             return "dataset_list";
     121             :     }
     122             : 
     123         115 :     return "boolean";
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                     GDALArgDatasetValueTypeName()                    */
     128             : /************************************************************************/
     129             : 
     130         739 : std::string GDALArgDatasetValueTypeName(GDALArgDatasetValueType type)
     131             : {
     132         739 :     std::string ret;
     133         739 :     if ((type & GDAL_OF_RASTER) != 0)
     134         441 :         ret = "raster";
     135         739 :     if ((type & GDAL_OF_VECTOR) != 0)
     136             :     {
     137         383 :         if (!ret.empty())
     138             :         {
     139          86 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     140          79 :                 ret += ", ";
     141             :             else
     142           7 :                 ret += " or ";
     143             :         }
     144         383 :         ret += "vector";
     145             :     }
     146         739 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     147             :     {
     148          82 :         if (!ret.empty())
     149             :         {
     150          81 :             ret += " or ";
     151             :         }
     152          82 :         ret += "multidimensional raster";
     153             :     }
     154         739 :     return ret;
     155             : }
     156             : 
     157             : /************************************************************************/
     158             : /*                     GDALAlgorithmArgDecl()                           */
     159             : /************************************************************************/
     160             : 
     161             : // cppcheck-suppress uninitMemberVar
     162       11064 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     163             :                                            char chShortName,
     164             :                                            const std::string &description,
     165       11064 :                                            GDALAlgorithmArgType type)
     166             :     : m_longName(longName),
     167       11064 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     168             :       m_description(description), m_type(type),
     169       22128 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     170       11064 :                     .toupper()),
     171       33192 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     172             : {
     173       11064 :     if (m_type == GAAT_BOOLEAN)
     174             :     {
     175        6260 :         m_defaultValue = false;
     176             :     }
     177       11064 : }
     178             : 
     179             : /************************************************************************/
     180             : /*               GDALAlgorithmArgDecl::SetMinCount()                    */
     181             : /************************************************************************/
     182             : 
     183         223 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     184             : {
     185         223 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     186             :     {
     187           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     188             :                  "SetMinCount() illegal on scalar argument '%s'",
     189           1 :                  GetName().c_str());
     190             :     }
     191             :     else
     192             :     {
     193         222 :         m_minCount = count;
     194             :     }
     195         223 :     return *this;
     196             : }
     197             : 
     198             : /************************************************************************/
     199             : /*               GDALAlgorithmArgDecl::SetMaxCount()                    */
     200             : /************************************************************************/
     201             : 
     202         151 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     203             : {
     204         151 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     205             :     {
     206           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     207             :                  "SetMaxCount() illegal on scalar argument '%s'",
     208           1 :                  GetName().c_str());
     209             :     }
     210             :     else
     211             :     {
     212         150 :         m_maxCount = count;
     213             :     }
     214         151 :     return *this;
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                         GDALAlgorithmArg::Set()                      */
     219             : /************************************************************************/
     220             : 
     221          87 : bool GDALAlgorithmArg::Set(bool value)
     222             : {
     223          87 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     224             :     {
     225          14 :         CPLError(
     226             :             CE_Failure, CPLE_AppDefined,
     227             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     228           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     229           7 :         return false;
     230             :     }
     231          80 :     return SetInternal(value);
     232             : }
     233             : 
     234         136 : bool GDALAlgorithmArg::Set(const std::string &value)
     235             : {
     236         136 :     if (m_decl.GetType() != GAAT_STRING)
     237             :     {
     238           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     239             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     240             :                  "supported",
     241           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     242           1 :         return false;
     243             :     }
     244             : 
     245         270 :     std::string newValue(value);
     246         142 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     247           7 :         value.front() == '@')
     248             :     {
     249           2 :         GByte *pabyData = nullptr;
     250           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     251           2 :                           1024 * 1024))
     252             :         {
     253             :             // Remove UTF-8 BOM
     254           1 :             size_t offset = 0;
     255           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     256           1 :                 pabyData[2] == 0xBF)
     257             :             {
     258           1 :                 offset = 3;
     259             :             }
     260           1 :             newValue = reinterpret_cast<const char *>(pabyData + offset);
     261           1 :             VSIFree(pabyData);
     262             :         }
     263             :         else
     264             :         {
     265           1 :             return false;
     266             :         }
     267             :     }
     268             : 
     269         134 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     270           6 :         newValue = CPLRemoveSQLComments(newValue);
     271             : 
     272         134 :     return SetInternal(newValue);
     273             : }
     274             : 
     275          18 : bool GDALAlgorithmArg::Set(int value)
     276             : {
     277          18 :     if (m_decl.GetType() == GAAT_REAL)
     278             :     {
     279           2 :         return Set(static_cast<double>(value));
     280             :     }
     281          16 :     if (m_decl.GetType() != GAAT_INTEGER)
     282             :     {
     283           2 :         CPLError(
     284             :             CE_Failure, CPLE_AppDefined,
     285             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     286           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     287           1 :         return false;
     288             :     }
     289          15 :     return SetInternal(value);
     290             : }
     291             : 
     292           6 : bool GDALAlgorithmArg::Set(double value)
     293             : {
     294           6 :     if (m_decl.GetType() != GAAT_REAL)
     295             :     {
     296           2 :         CPLError(
     297             :             CE_Failure, CPLE_AppDefined,
     298             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     299           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     300           1 :         return false;
     301             :     }
     302           5 :     return SetInternal(value);
     303             : }
     304             : 
     305          24 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     306             : {
     307          24 :     if (m_decl.GetType() != GAAT_DATASET)
     308             :     {
     309           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     310             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     311             :                  "is not supported",
     312           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     313           1 :         return false;
     314             :     }
     315          23 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     316          23 :     if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
     317             :     {
     318           2 :         CPLError(
     319             :             CE_Failure, CPLE_AppDefined,
     320             :             "Dataset object '%s' is created by algorithm and cannot be set "
     321             :             "as an input.",
     322           2 :             GetName().c_str());
     323           2 :         return false;
     324             :     }
     325          21 :     m_explicitlySet = true;
     326          21 :     val.Set(ds);
     327          21 :     return RunAllActions();
     328             : }
     329             : 
     330           2 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     331             : {
     332           2 :     if (m_decl.GetType() != GAAT_DATASET)
     333             :     {
     334           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     335             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     336             :                  "is not supported",
     337           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     338           1 :         return false;
     339             :     }
     340           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     341           1 :     if (val.GetInputFlags() == GADV_NAME && val.GetOutputFlags() == GADV_OBJECT)
     342             :     {
     343           0 :         CPLError(
     344             :             CE_Failure, CPLE_AppDefined,
     345             :             "Dataset object '%s' is created by algorithm and cannot be set "
     346             :             "as an input.",
     347           0 :             GetName().c_str());
     348           0 :         return false;
     349             :     }
     350           1 :     m_explicitlySet = true;
     351           1 :     val.Set(std::move(ds));
     352           1 :     return RunAllActions();
     353             : }
     354             : 
     355         236 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     356             : {
     357         236 :     if (m_decl.GetType() != GAAT_DATASET)
     358             :     {
     359           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     360             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     361             :                  "not supported",
     362           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     363           1 :         return false;
     364             :     }
     365         235 :     m_explicitlySet = true;
     366         235 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     367         235 :     return RunAllActions();
     368             : }
     369             : 
     370          21 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     371             : {
     372          21 :     if (m_decl.GetType() != GAAT_DATASET)
     373             :     {
     374           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     375             :                  "Calling SetFrom() on argument '%s' of type %s is "
     376             :                  "not supported",
     377           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     378           1 :         return false;
     379             :     }
     380          20 :     m_explicitlySet = true;
     381          20 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     382          20 :     return RunAllActions();
     383             : }
     384             : 
     385          55 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     386             : {
     387          55 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     388             :     {
     389           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     390             :                  "Calling Set(const std::vector<std::string> &) on argument "
     391             :                  "'%s' of type %s is not supported",
     392           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     393           1 :         return false;
     394             :     }
     395          54 :     return SetInternal(value);
     396             : }
     397             : 
     398          12 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     399             : {
     400          12 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     401             :     {
     402           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     403             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     404             :                  "type %s is not supported",
     405           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     406           1 :         return false;
     407             :     }
     408          11 :     return SetInternal(value);
     409             : }
     410             : 
     411          26 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     412             : {
     413          26 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     414             :     {
     415           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     416             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     417             :                  "type %s is not supported",
     418           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     419           1 :         return false;
     420             :     }
     421          25 :     return SetInternal(value);
     422             : }
     423             : 
     424          28 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     425             : {
     426          28 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     427             :     {
     428           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     429             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     430             :                  "argument '%s' of type %s is not supported",
     431           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     432           1 :         return false;
     433             :     }
     434          27 :     m_explicitlySet = true;
     435          27 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     436          27 :     return RunAllActions();
     437             : }
     438             : 
     439          25 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     440             : {
     441          25 :     if (m_decl.GetType() != other.GetType())
     442             :     {
     443           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     444             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     445             :                  "other argument type is %s is not supported",
     446           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     447             :                  GDALAlgorithmArgTypeName(other.GetType()));
     448           1 :         return false;
     449             :     }
     450             : 
     451          24 :     switch (m_decl.GetType())
     452             :     {
     453           1 :         case GAAT_BOOLEAN:
     454           1 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     455           1 :             break;
     456           1 :         case GAAT_STRING:
     457           2 :             *std::get<std::string *>(m_value) =
     458           1 :                 *std::get<std::string *>(other.m_value);
     459           1 :             break;
     460           1 :         case GAAT_INTEGER:
     461           1 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     462           1 :             break;
     463           1 :         case GAAT_REAL:
     464           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     465           1 :             break;
     466          16 :         case GAAT_DATASET:
     467          16 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     468           1 :         case GAAT_STRING_LIST:
     469           2 :             *std::get<std::vector<std::string> *>(m_value) =
     470           1 :                 *std::get<std::vector<std::string> *>(other.m_value);
     471           1 :             break;
     472           1 :         case GAAT_INTEGER_LIST:
     473           2 :             *std::get<std::vector<int> *>(m_value) =
     474           1 :                 *std::get<std::vector<int> *>(other.m_value);
     475           1 :             break;
     476           1 :         case GAAT_REAL_LIST:
     477           2 :             *std::get<std::vector<double> *>(m_value) =
     478           1 :                 *std::get<std::vector<double> *>(other.m_value);
     479           1 :             break;
     480           1 :         case GAAT_DATASET_LIST:
     481             :         {
     482           1 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     483           2 :             for (const auto &val :
     484           5 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     485             :             {
     486           4 :                 GDALArgDatasetValue v;
     487           2 :                 v.SetFrom(val);
     488           2 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     489           2 :                     ->push_back(std::move(v));
     490             :             }
     491           1 :             break;
     492             :         }
     493             :     }
     494           8 :     m_explicitlySet = true;
     495           8 :     return RunAllActions();
     496             : }
     497             : 
     498             : /************************************************************************/
     499             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     500             : /************************************************************************/
     501             : 
     502         636 : bool GDALAlgorithmArg::RunAllActions()
     503             : {
     504         636 :     if (!RunValidationActions())
     505          25 :         return false;
     506         611 :     RunActions();
     507         611 :     return true;
     508             : }
     509             : 
     510             : /************************************************************************/
     511             : /*                      GDALAlgorithmArg::RunActions()                  */
     512             : /************************************************************************/
     513             : 
     514         612 : void GDALAlgorithmArg::RunActions()
     515             : {
     516         623 :     for (const auto &f : m_actions)
     517          11 :         f();
     518         612 : }
     519             : 
     520             : /************************************************************************/
     521             : /*                    GDALAlgorithmArg::RunValidationActions()          */
     522             : /************************************************************************/
     523             : 
     524         636 : bool GDALAlgorithmArg::RunValidationActions()
     525             : {
     526         730 :     for (const auto &f : m_validationActions)
     527             :     {
     528         119 :         if (!f())
     529          25 :             return false;
     530             :     }
     531         611 :     return true;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
     536             : /************************************************************************/
     537             : 
     538             : GDALInConstructionAlgorithmArg &
     539        2195 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
     540             : {
     541        2195 :     m_decl.AddAlias(alias);
     542        2195 :     if (m_owner)
     543        2195 :         m_owner->AddAliasFor(this, alias);
     544        2195 :     return *this;
     545             : }
     546             : 
     547             : /************************************************************************/
     548             : /*            GDALInConstructionAlgorithmArg::AddHiddenAlias()          */
     549             : /************************************************************************/
     550             : 
     551             : GDALInConstructionAlgorithmArg &
     552         352 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
     553             : {
     554         352 :     m_decl.AddHiddenAlias(alias);
     555         352 :     if (m_owner)
     556         352 :         m_owner->AddAliasFor(this, alias);
     557         352 :     return *this;
     558             : }
     559             : 
     560             : /************************************************************************/
     561             : /*             GDALInConstructionAlgorithmArg::SetPositional()          */
     562             : /************************************************************************/
     563             : 
     564         733 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
     565             : {
     566         733 :     m_decl.SetPositional();
     567         733 :     if (m_owner)
     568         733 :         m_owner->SetPositional(this);
     569         733 :     return *this;
     570             : }
     571             : 
     572             : /************************************************************************/
     573             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
     574             : /************************************************************************/
     575             : 
     576          15 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
     577          30 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
     578          15 :       m_nameSet(true)
     579             : {
     580          15 :     if (m_poDS)
     581          15 :         m_poDS->Reference();
     582          15 : }
     583             : 
     584             : /************************************************************************/
     585             : /*              GDALArgDatasetValue::Set()                              */
     586             : /************************************************************************/
     587             : 
     588         259 : void GDALArgDatasetValue::Set(const std::string &name)
     589             : {
     590         259 :     Close();
     591         259 :     m_name = name;
     592         259 :     m_nameSet = true;
     593         259 :     if (m_ownerArg)
     594         255 :         m_ownerArg->NotifyValueSet();
     595         259 : }
     596             : 
     597             : /************************************************************************/
     598             : /*              GDALArgDatasetValue::Set()                              */
     599             : /************************************************************************/
     600             : 
     601         106 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
     602             : {
     603         106 :     Close();
     604         106 :     m_poDS = poDS.release();
     605         106 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
     606         106 :     m_nameSet = true;
     607         106 :     if (m_ownerArg)
     608          89 :         m_ownerArg->NotifyValueSet();
     609         106 : }
     610             : 
     611             : /************************************************************************/
     612             : /*              GDALArgDatasetValue::Set()                              */
     613             : /************************************************************************/
     614             : 
     615         344 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
     616             : {
     617         344 :     Close();
     618         344 :     m_poDS = poDS;
     619         344 :     if (m_poDS)
     620         338 :         m_poDS->Reference();
     621         344 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
     622         344 :     m_nameSet = true;
     623         344 :     if (m_ownerArg)
     624         215 :         m_ownerArg->NotifyValueSet();
     625         344 : }
     626             : 
     627             : /************************************************************************/
     628             : /*                   GDALArgDatasetValue::SetFrom()                     */
     629             : /************************************************************************/
     630             : 
     631          22 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
     632             : {
     633          22 :     Close();
     634          22 :     m_name = other.m_name;
     635          22 :     m_nameSet = other.m_nameSet;
     636          22 :     m_poDS = other.m_poDS;
     637          22 :     if (m_poDS)
     638          10 :         m_poDS->Reference();
     639          22 : }
     640             : 
     641             : /************************************************************************/
     642             : /*              GDALArgDatasetValue::~GDALArgDatasetValue()             */
     643             : /************************************************************************/
     644             : 
     645        1263 : GDALArgDatasetValue::~GDALArgDatasetValue()
     646             : {
     647        1263 :     Close();
     648        1263 : }
     649             : 
     650             : /************************************************************************/
     651             : /*                     GDALArgDatasetValue::Close()                     */
     652             : /************************************************************************/
     653             : 
     654        2273 : bool GDALArgDatasetValue::Close()
     655             : {
     656        2273 :     bool ret = true;
     657        2273 :     if (m_poDS && m_poDS->Dereference() == 0)
     658             :     {
     659         209 :         ret = m_poDS->Close() == CE_None;
     660         209 :         delete m_poDS;
     661             :     }
     662        2273 :     m_poDS = nullptr;
     663        2273 :     return ret;
     664             : }
     665             : 
     666             : /************************************************************************/
     667             : /*                      GDALArgDatasetValue::operator=()                */
     668             : /************************************************************************/
     669             : 
     670           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
     671             : {
     672           2 :     Close();
     673           2 :     m_poDS = other.m_poDS;
     674           2 :     m_name = other.m_name;
     675           2 :     m_nameSet = other.m_nameSet;
     676           2 :     m_type = other.m_type;
     677           2 :     m_inputFlags = other.m_inputFlags;
     678           2 :     m_outputFlags = other.m_outputFlags;
     679           2 :     other.m_poDS = nullptr;
     680           2 :     other.m_name.clear();
     681           2 :     other.m_nameSet = false;
     682           2 :     return *this;
     683             : }
     684             : 
     685             : /************************************************************************/
     686             : /*                   GDALArgDatasetValue::GetDataset()                  */
     687             : /************************************************************************/
     688             : 
     689          24 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
     690             : {
     691          24 :     if (m_poDS)
     692          23 :         m_poDS->Reference();
     693          24 :     return m_poDS;
     694             : }
     695             : 
     696             : /************************************************************************/
     697             : /*               GDALArgDatasetValue(GDALArgDatasetValue &&other)       */
     698             : /************************************************************************/
     699             : 
     700          25 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
     701          50 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet),
     702          25 :       m_type(other.m_type), m_inputFlags(other.m_inputFlags),
     703          25 :       m_outputFlags(other.m_outputFlags)
     704             : {
     705          25 :     other.m_poDS = nullptr;
     706          25 :     other.m_name.clear();
     707          25 : }
     708             : 
     709             : /************************************************************************/
     710             : /*              GDALInConstructionAlgorithmArg::SetIsCRSArg()           */
     711             : /************************************************************************/
     712             : 
     713             : GDALInConstructionAlgorithmArg &
     714         117 : GDALInConstructionAlgorithmArg::SetIsCRSArg(bool noneAllowed)
     715             : {
     716         117 :     if (GetType() != GAAT_STRING)
     717             :     {
     718           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     719             :                  "SetIsCRSArg() can only be called on a String argument");
     720           1 :         return *this;
     721             :     }
     722             :     AddValidationAction(
     723          50 :         [this, noneAllowed]()
     724             :         {
     725             :             const std::string &osVal =
     726             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
     727          23 :                     ->Get<std::string>();
     728          23 :             if (!noneAllowed || (osVal != "none" && osVal != "null"))
     729             :             {
     730          20 :                 OGRSpatialReference oSRS;
     731          20 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
     732             :                 {
     733           4 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
     734             :                                          "Invalid value for '%s' argument",
     735           4 :                                          GetName().c_str());
     736           4 :                     return false;
     737             :                 }
     738             :             }
     739          19 :             return true;
     740         116 :         });
     741             : 
     742             :     SetAutoCompleteFunction(
     743           6 :         [](const std::string &currentValue)
     744             :         {
     745           6 :             std::vector<std::string> oRet;
     746           6 :             if (!currentValue.empty())
     747             :             {
     748             :                 const CPLStringList aosTokens(
     749          10 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
     750           5 :                 int nCount = 0;
     751           5 :                 OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
     752             :                     aosTokens[0], nullptr, &nCount);
     753          10 :                 std::string osCode;
     754       33050 :                 for (int i = 0; i < nCount; ++i)
     755             :                 {
     756       52872 :                     if (aosTokens.size() == 1 ||
     757       19827 :                         STARTS_WITH(pCRSList[i]->pszCode, aosTokens[1]))
     758             :                     {
     759       13409 :                         if (oRet.empty())
     760           5 :                             osCode = pCRSList[i]->pszCode;
     761       26818 :                         oRet.push_back(std::string(pCRSList[i]->pszCode)
     762       13409 :                                            .append(" -- ")
     763       13409 :                                            .append(pCRSList[i]->pszName));
     764             :                     }
     765             :                 }
     766           5 :                 if (oRet.size() == 1)
     767             :                 {
     768             :                     // If there is a single match, remove the name from the suggestion.
     769           1 :                     oRet.clear();
     770           1 :                     oRet.push_back(osCode);
     771             :                 }
     772           5 :                 OSRDestroyCRSInfoList(pCRSList);
     773             :             }
     774           6 :             if (oRet.empty())
     775             :             {
     776             :                 const CPLStringList aosAuthorities(
     777           2 :                     OSRGetAuthorityListFromDatabase());
     778           6 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
     779             :                 {
     780           5 :                     int nCount = 0;
     781           5 :                     OSRCRSInfo **pCRSList = OSRGetCRSInfoListFromDatabase(
     782             :                         pszAuth, nullptr, &nCount);
     783           5 :                     OSRDestroyCRSInfoList(pCRSList);
     784           5 :                     if (nCount)
     785           4 :                         oRet.push_back(std::string(pszAuth).append(":"));
     786             :                 }
     787             :             }
     788           6 :             return oRet;
     789         116 :         });
     790             : 
     791         116 :     return *this;
     792             : }
     793             : 
     794             : /************************************************************************/
     795             : /*                     GDALAlgorithm::GDALAlgorithm()                  */
     796             : /************************************************************************/
     797             : 
     798        1100 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
     799             :                              const std::string &description,
     800        1100 :                              const std::string &helpURL)
     801             :     : m_name(name), m_description(description), m_helpURL(helpURL),
     802        2200 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
     803        1100 :                         ? "https://gdal.org" + m_helpURL
     804        3153 :                         : m_helpURL)
     805             : {
     806        2200 :     AddArg("help", 'h', _("Display help message and exit"), &m_helpRequested)
     807        1100 :         .SetOnlyForCLI()
     808        2200 :         .SetCategory(GAAC_COMMON)
     809        1100 :         .AddAction([this]() { m_specialActionRequested = true; });
     810        2200 :     AddArg("version", 0, _("Display GDAL version and exit"), &m_dummyBoolean)
     811        1100 :         .SetOnlyForCLI()
     812        1100 :         .SetCategory(GAAC_COMMON);
     813             :     AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
     814        2200 :            &m_JSONUsageRequested)
     815        1100 :         .SetOnlyForCLI()
     816        2200 :         .SetCategory(GAAC_COMMON)
     817        1100 :         .AddAction([this]() { m_specialActionRequested = true; });
     818             :     AddArg("drivers", 0, _("Display driver list as JSON document and exit"),
     819        2200 :            &m_dummyBoolean)
     820        1100 :         .SetOnlyForCLI()
     821        1100 :         .SetCategory(GAAC_COMMON);
     822        2200 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
     823        2200 :         .SetMetaVar("<KEY>=<VALUE>")
     824        1100 :         .SetOnlyForCLI()
     825        1100 :         .SetCategory(GAAC_COMMON);
     826        1100 : }
     827             : 
     828             : /************************************************************************/
     829             : /*                     GDALAlgorithm::~GDALAlgorithm()                  */
     830             : /************************************************************************/
     831             : 
     832             : GDALAlgorithm::~GDALAlgorithm() = default;
     833             : 
     834             : /************************************************************************/
     835             : /*                    GDALAlgorithm::ParseArgument()                    */
     836             : /************************************************************************/
     837             : 
     838         537 : bool GDALAlgorithm::ParseArgument(
     839             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
     840             :     std::map<
     841             :         GDALAlgorithmArg *,
     842             :         std::variant<std::vector<std::string>, std::vector<int>,
     843             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
     844             :         &inConstructionValues)
     845             : {
     846         537 :     const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
     847         537 :     if (arg->IsExplicitlySet() && !isListArg)
     848             :     {
     849             :         // Hack for "gdal info" to be able to pass an opened raster dataset
     850             :         // by "gdal raster info" to the "gdal vector info" algorithm.
     851           3 :         if (arg->SkipIfAlreadySet())
     852             :         {
     853           1 :             arg->SetSkipIfAlreadySet(false);
     854           1 :             return true;
     855             :         }
     856             : 
     857           2 :         ReportError(CE_Failure, CPLE_IllegalArg,
     858             :                     "Argument '%s' has already been specified.", name.c_str());
     859           2 :         return false;
     860             :     }
     861             : 
     862         552 :     if (!arg->GetRepeatedArgAllowed() &&
     863          18 :         cpl::contains(inConstructionValues, arg))
     864             :     {
     865           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
     866             :                     "Argument '%s' has already been specified.", name.c_str());
     867           1 :         return false;
     868             :     }
     869             : 
     870         533 :     switch (arg->GetType())
     871             :     {
     872          71 :         case GAAT_BOOLEAN:
     873             :         {
     874          71 :             if (value.empty() || value == "true")
     875          69 :                 return arg->Set(true);
     876           2 :             else if (value == "false")
     877           1 :                 return arg->Set(false);
     878             :             else
     879             :             {
     880           1 :                 ReportError(
     881             :                     CE_Failure, CPLE_IllegalArg,
     882             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
     883             :                     "'true' or 'false'.",
     884             :                     value.c_str(), name.c_str());
     885           1 :                 return false;
     886             :             }
     887             :         }
     888             : 
     889         103 :         case GAAT_STRING:
     890             :         {
     891         103 :             const auto &choices = arg->GetChoices();
     892         103 :             const auto &hiddenChoices = arg->GetHiddenChoices();
     893         130 :             if (!choices.empty() &&
     894          27 :                 std::find(choices.begin(), choices.end(), value) ==
     895         130 :                     choices.end() &&
     896           2 :                 std::find(hiddenChoices.begin(), hiddenChoices.end(), value) ==
     897         105 :                     hiddenChoices.end())
     898             :             {
     899           1 :                 std::string expected;
     900           3 :                 for (const auto &choice : choices)
     901             :                 {
     902           2 :                     if (!expected.empty())
     903           1 :                         expected += ", ";
     904           2 :                     expected += '\'';
     905           2 :                     expected += choice;
     906           2 :                     expected += '\'';
     907             :                 }
     908           1 :                 ReportError(
     909             :                     CE_Failure, CPLE_IllegalArg,
     910             :                     "Invalid value '%s' for string argument '%s'. Should be "
     911             :                     "one among %s.",
     912             :                     value.c_str(), name.c_str(), expected.c_str());
     913           1 :                 return false;
     914             :             }
     915             : 
     916         102 :             return arg->Set(value);
     917             :         }
     918             : 
     919           7 :         case GAAT_INTEGER:
     920             :         {
     921           7 :             errno = 0;
     922           7 :             char *endptr = nullptr;
     923           7 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
     924           6 :             if (errno == 0 && endptr &&
     925          13 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
     926             :                 val <= INT_MAX)
     927             :             {
     928           4 :                 return arg->Set(static_cast<int>(val));
     929             :             }
     930             :             else
     931             :             {
     932           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
     933             :                             "Expected integer value for argument '%s', "
     934             :                             "but got '%s'.",
     935             :                             name.c_str(), value.c_str());
     936           3 :                 return false;
     937             :             }
     938             :         }
     939             : 
     940           2 :         case GAAT_REAL:
     941             :         {
     942           2 :             char *endptr = nullptr;
     943           2 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
     944           2 :             if (endptr != value.c_str() + value.size())
     945             :             {
     946           1 :                 ReportError(
     947             :                     CE_Failure, CPLE_IllegalArg,
     948             :                     "Expected real value for argument '%s', but got '%s'.",
     949             :                     name.c_str(), value.c_str());
     950           1 :                 return false;
     951             :             }
     952           1 :             return arg->Set(dfValue);
     953             :         }
     954             : 
     955         234 :         case GAAT_DATASET:
     956             :         {
     957         234 :             return arg->SetDatasetName(value);
     958             :         }
     959             : 
     960          71 :         case GAAT_STRING_LIST:
     961             :         {
     962             :             const CPLStringList aosTokens(
     963          71 :                 arg->GetPackedValuesAllowed()
     964          68 :                     ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
     965         139 :                     : CSLAddString(nullptr, value.c_str()));
     966          71 :             if (!cpl::contains(inConstructionValues, arg))
     967             :             {
     968          51 :                 inConstructionValues[arg] = std::vector<std::string>();
     969             :             }
     970             :             auto &valueVector =
     971          71 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
     972          71 :             const auto &choices = arg->GetChoices();
     973          71 :             const auto &hiddenChoices = arg->GetHiddenChoices();
     974         148 :             for (const char *v : aosTokens)
     975             :             {
     976          91 :                 if (!choices.empty() &&
     977          10 :                     std::find(choices.begin(), choices.end(), v) ==
     978          91 :                         choices.end() &&
     979           0 :                     std::find(hiddenChoices.begin(), hiddenChoices.end(),
     980          85 :                               value) == hiddenChoices.end())
     981             :                 {
     982           4 :                     std::string expected;
     983          10 :                     for (const auto &choice : choices)
     984             :                     {
     985           6 :                         if (!expected.empty())
     986           2 :                             expected += ", ";
     987           6 :                         expected += '\'';
     988           6 :                         expected += choice;
     989           6 :                         expected += '\'';
     990             :                     }
     991           4 :                     ReportError(CE_Failure, CPLE_IllegalArg,
     992             :                                 "Invalid value '%s' for string argument '%s'. "
     993             :                                 "Should be "
     994             :                                 "one among %s.",
     995             :                                 v, name.c_str(), expected.c_str());
     996           4 :                     return false;
     997             :                 }
     998             : 
     999          77 :                 valueVector.push_back(v);
    1000             :             }
    1001          67 :             break;
    1002             :         }
    1003             : 
    1004          13 :         case GAAT_INTEGER_LIST:
    1005             :         {
    1006             :             const CPLStringList aosTokens(
    1007          13 :                 arg->GetPackedValuesAllowed()
    1008          13 :                     ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
    1009          26 :                     : CSLAddString(nullptr, value.c_str()));
    1010          13 :             if (!cpl::contains(inConstructionValues, arg))
    1011             :             {
    1012           9 :                 inConstructionValues[arg] = std::vector<int>();
    1013             :             }
    1014             :             auto &valueVector =
    1015          13 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    1016          25 :             for (const char *v : aosTokens)
    1017             :             {
    1018          16 :                 errno = 0;
    1019          16 :                 char *endptr = nullptr;
    1020          16 :                 const auto val = std::strtol(v, &endptr, 10);
    1021          16 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    1022          12 :                     val >= INT_MIN && val <= INT_MAX)
    1023             :                 {
    1024          12 :                     valueVector.push_back(static_cast<int>(val));
    1025             :                 }
    1026             :                 else
    1027             :                 {
    1028           4 :                     ReportError(
    1029             :                         CE_Failure, CPLE_IllegalArg,
    1030             :                         "Expected list of integer value for argument '%s', "
    1031             :                         "but got '%s'.",
    1032             :                         name.c_str(), value.c_str());
    1033           4 :                     return false;
    1034             :                 }
    1035             :             }
    1036           9 :             break;
    1037             :         }
    1038             : 
    1039          21 :         case GAAT_REAL_LIST:
    1040             :         {
    1041             :             const CPLStringList aosTokens(
    1042          21 :                 arg->GetPackedValuesAllowed()
    1043          21 :                     ? CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS)
    1044          42 :                     : CSLAddString(nullptr, value.c_str()));
    1045          21 :             if (!cpl::contains(inConstructionValues, arg))
    1046             :             {
    1047          19 :                 inConstructionValues[arg] = std::vector<double>();
    1048             :             }
    1049             :             auto &valueVector =
    1050          21 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    1051          83 :             for (const char *v : aosTokens)
    1052             :             {
    1053          64 :                 char *endptr = nullptr;
    1054          64 :                 double dfValue = CPLStrtod(v, &endptr);
    1055          64 :                 if (endptr != v + strlen(v))
    1056             :                 {
    1057           2 :                     ReportError(
    1058             :                         CE_Failure, CPLE_IllegalArg,
    1059             :                         "Expected list of real value for argument '%s', "
    1060             :                         "but got '%s'.",
    1061             :                         name.c_str(), value.c_str());
    1062           2 :                     return false;
    1063             :                 }
    1064          62 :                 valueVector.push_back(dfValue);
    1065             :             }
    1066          19 :             break;
    1067             :         }
    1068             : 
    1069          11 :         case GAAT_DATASET_LIST:
    1070             :         {
    1071             :             const CPLStringList aosTokens(
    1072          22 :                 CSLTokenizeString2(value.c_str(), ",", CSLT_HONOURSTRINGS));
    1073          11 :             if (!cpl::contains(inConstructionValues, arg))
    1074             :             {
    1075           9 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    1076             :             }
    1077             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    1078          11 :                 inConstructionValues[arg]);
    1079          22 :             for (const char *v : aosTokens)
    1080             :             {
    1081          11 :                 valueVector.push_back(GDALArgDatasetValue(v));
    1082             :             }
    1083          11 :             break;
    1084             :         }
    1085             :     }
    1086             : 
    1087         106 :     return true;
    1088             : }
    1089             : 
    1090             : /************************************************************************/
    1091             : /*               GDALAlgorithm::ParseCommandLineArguments()             */
    1092             : /************************************************************************/
    1093             : 
    1094         429 : bool GDALAlgorithm::ParseCommandLineArguments(
    1095             :     const std::vector<std::string> &args)
    1096             : {
    1097         429 :     if (m_parsedSubStringAlreadyCalled)
    1098             :     {
    1099           1 :         ReportError(CE_Failure, CPLE_AppDefined,
    1100             :                     "ParseCommandLineArguments() can only be called once per "
    1101             :                     "instance.");
    1102           1 :         return false;
    1103             :     }
    1104         428 :     m_parsedSubStringAlreadyCalled = true;
    1105             : 
    1106             :     // AWS like syntax supported too (not advertized)
    1107         428 :     if (args.size() == 1 && args[0] == "help")
    1108             :     {
    1109           1 :         auto arg = GetArg("help");
    1110           1 :         assert(arg);
    1111           1 :         arg->Set(true);
    1112           1 :         arg->RunActions();
    1113           1 :         return true;
    1114             :     }
    1115             : 
    1116         427 :     if (HasSubAlgorithms())
    1117             :     {
    1118          29 :         if (args.empty())
    1119             :         {
    1120           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    1121           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    1122           2 :             return false;
    1123             :         }
    1124          27 :         if (!args[0].empty() && args[0][0] == '-')
    1125             :         {
    1126             :             // go on argument parsing
    1127             :         }
    1128             :         else
    1129             :         {
    1130          24 :             m_shortCutAlg = InstantiateSubAlgorithm(args[0]);
    1131          24 :             if (m_shortCutAlg)
    1132             :             {
    1133          23 :                 m_selectedSubAlg = m_shortCutAlg.get();
    1134          23 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    1135          46 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    1136          23 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    1137          23 :                 return bRet;
    1138             :             }
    1139             :             else
    1140             :             {
    1141           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    1142           1 :                             "Unknown command: '%s'", args[0].c_str());
    1143           1 :                 return false;
    1144             :             }
    1145             :         }
    1146             :     }
    1147             : 
    1148             :     std::map<
    1149             :         GDALAlgorithmArg *,
    1150             :         std::variant<std::vector<std::string>, std::vector<int>,
    1151             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1152         802 :         inConstructionValues;
    1153             : 
    1154         802 :     std::vector<std::string> lArgs(args);
    1155         933 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    1156             :     {
    1157         584 :         const auto &strArg = lArgs[i];
    1158         584 :         GDALAlgorithmArg *arg = nullptr;
    1159         584 :         std::string name;
    1160         584 :         std::string value;
    1161         584 :         bool hasValue = false;
    1162         584 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    1163             :         {
    1164         288 :             const auto equalPos = strArg.find('=');
    1165         576 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    1166         288 :                                                    : strArg;
    1167         288 :             auto iterArg = m_mapLongNameToArg.find(name.substr(2));
    1168         288 :             if (iterArg == m_mapLongNameToArg.end())
    1169             :             {
    1170          16 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    1171             :                             "Long name option '%s' is unknown.", name.c_str());
    1172          16 :                 return false;
    1173             :             }
    1174         272 :             arg = iterArg->second;
    1175         272 :             if (equalPos != std::string::npos)
    1176             :             {
    1177         145 :                 hasValue = true;
    1178         145 :                 value = strArg.substr(equalPos + 1);
    1179             :             }
    1180             :         }
    1181         296 :         else if (strArg.size() >= 2 && strArg[0] == '-')
    1182             :         {
    1183          16 :             if (strArg.size() != 2)
    1184             :             {
    1185           1 :                 ReportError(
    1186             :                     CE_Failure, CPLE_IllegalArg,
    1187             :                     "Option '%s' not recognized. Should be either a long "
    1188             :                     "option or a one-letter short option.",
    1189             :                     strArg.c_str());
    1190           2 :                 return false;
    1191             :             }
    1192          15 :             name = strArg;
    1193          15 :             auto iterArg = m_mapShortNameToArg.find(name.substr(1));
    1194          15 :             if (iterArg == m_mapShortNameToArg.end())
    1195             :             {
    1196           1 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    1197             :                             "Short name option '%s' is unknown.", name.c_str());
    1198           1 :                 return false;
    1199             :             }
    1200          14 :             arg = iterArg->second;
    1201             :         }
    1202             :         else
    1203             :         {
    1204         280 :             ++i;
    1205         280 :             continue;
    1206             :         }
    1207         286 :         assert(arg);
    1208             : 
    1209         286 :         if (arg->GetType() == GAAT_BOOLEAN)
    1210             :         {
    1211          72 :             if (!hasValue)
    1212             :             {
    1213          69 :                 hasValue = true;
    1214          69 :                 value = "true";
    1215             :             }
    1216             :         }
    1217             : 
    1218         286 :         if (!hasValue)
    1219             :         {
    1220          72 :             if (i + 1 == lArgs.size())
    1221             :             {
    1222          13 :                 if (m_parseForAutoCompletion)
    1223             :                 {
    1224          12 :                     lArgs.erase(lArgs.begin() + i);
    1225          12 :                     break;
    1226             :                 }
    1227           1 :                 ReportError(
    1228             :                     CE_Failure, CPLE_IllegalArg,
    1229             :                     "Expected value for argument '%s', but ran short of tokens",
    1230             :                     name.c_str());
    1231           1 :                 return false;
    1232             :             }
    1233          59 :             value = lArgs[i + 1];
    1234          59 :             lArgs.erase(lArgs.begin() + i + 1);
    1235             :         }
    1236             : 
    1237         273 :         if (!ParseArgument(arg, name, value, inConstructionValues))
    1238          21 :             return false;
    1239             : 
    1240         252 :         lArgs.erase(lArgs.begin() + i);
    1241             :     }
    1242             : 
    1243         361 :     if (m_specialActionRequested)
    1244             :     {
    1245           8 :         return true;
    1246             :     }
    1247             : 
    1248         431 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    1249             :     {
    1250         417 :         for (auto &[arg, value] : inConstructionValues)
    1251             :         {
    1252          80 :             if (arg->GetType() == GAAT_STRING_LIST)
    1253             :             {
    1254          48 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    1255          48 :                         inConstructionValues[arg])))
    1256             :                 {
    1257          14 :                     return false;
    1258             :                 }
    1259             :             }
    1260          32 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    1261             :             {
    1262           6 :                 if (!arg->Set(
    1263           6 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    1264             :                 {
    1265           1 :                     return false;
    1266             :                 }
    1267             :             }
    1268          26 :             else if (arg->GetType() == GAAT_REAL_LIST)
    1269             :             {
    1270          17 :                 if (!arg->Set(std::get<std::vector<double>>(
    1271          17 :                         inConstructionValues[arg])))
    1272             :                 {
    1273           6 :                     return false;
    1274             :                 }
    1275             :             }
    1276           9 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    1277             :             {
    1278           9 :                 if (!arg->Set(
    1279             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    1280           9 :                             inConstructionValues[arg]))))
    1281             :                 {
    1282           1 :                     return false;
    1283             :                 }
    1284             :             }
    1285             :         }
    1286         337 :         return true;
    1287         353 :     };
    1288             : 
    1289         353 :     size_t i = 0;
    1290         353 :     size_t iCurPosArg = 0;
    1291         601 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    1292             :     {
    1293         257 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    1294         260 :         while (arg->IsExplicitlySet())
    1295             :         {
    1296           4 :             ++iCurPosArg;
    1297           4 :             if (iCurPosArg == m_positionalArgs.size())
    1298           1 :                 break;
    1299           3 :             arg = m_positionalArgs[iCurPosArg];
    1300             :         }
    1301         257 :         if (iCurPosArg == m_positionalArgs.size())
    1302             :         {
    1303           1 :             break;
    1304             :         }
    1305         277 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    1306          21 :             arg->GetMinCount() != arg->GetMaxCount())
    1307             :         {
    1308          16 :             if (iCurPosArg == 0)
    1309             :             {
    1310          13 :                 size_t nCountAtEnd = 0;
    1311          23 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    1312             :                 {
    1313          12 :                     const auto *otherArg = m_positionalArgs[j];
    1314          12 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    1315             :                     {
    1316           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    1317             :                         {
    1318           2 :                             ReportError(
    1319             :                                 CE_Failure, CPLE_AppDefined,
    1320             :                                 "Ambiguity in definition of positional "
    1321             :                                 "argument "
    1322             :                                 "'%s' given it has a varying number of values, "
    1323             :                                 "but follows argument '%s' which also has a "
    1324             :                                 "varying number of values",
    1325           1 :                                 otherArg->GetName().c_str(),
    1326           1 :                                 arg->GetName().c_str());
    1327           1 :                             ProcessInConstructionValues();
    1328           1 :                             return false;
    1329             :                         }
    1330           3 :                         nCountAtEnd += otherArg->GetMinCount();
    1331             :                     }
    1332             :                     else
    1333             :                     {
    1334           8 :                         if (!otherArg->IsRequired())
    1335             :                         {
    1336           2 :                             ReportError(
    1337             :                                 CE_Failure, CPLE_AppDefined,
    1338             :                                 "Ambiguity in definition of positional "
    1339             :                                 "argument "
    1340             :                                 "'%s', given it is not required but follows "
    1341             :                                 "argument '%s' which has a varying number of "
    1342             :                                 "values",
    1343           1 :                                 otherArg->GetName().c_str(),
    1344           1 :                                 arg->GetName().c_str());
    1345           1 :                             ProcessInConstructionValues();
    1346           1 :                             return false;
    1347             :                         }
    1348           7 :                         nCountAtEnd++;
    1349             :                     }
    1350             :                 }
    1351          11 :                 if (lArgs.size() < nCountAtEnd)
    1352             :                 {
    1353           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    1354             :                                 "Not enough positional values.");
    1355           1 :                     ProcessInConstructionValues();
    1356           1 :                     return false;
    1357             :                 }
    1358          26 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    1359             :                 {
    1360          17 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    1361             :                                        inConstructionValues))
    1362             :                     {
    1363           1 :                         ProcessInConstructionValues();
    1364           1 :                         return false;
    1365             :                     }
    1366             :                 }
    1367             :             }
    1368           3 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    1369             :             {
    1370           5 :                 for (; i < lArgs.size(); ++i)
    1371             :                 {
    1372           4 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    1373             :                                        inConstructionValues))
    1374             :                     {
    1375           1 :                         ProcessInConstructionValues();
    1376           1 :                         return false;
    1377             :                     }
    1378             :                 }
    1379             :             }
    1380             :             else
    1381             :             {
    1382           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    1383             :                             "Ambiguity in definition of positional arguments: "
    1384             :                             "arguments with varying number of values must be "
    1385             :                             "first or last one.");
    1386           1 :                 return false;
    1387             :             }
    1388             :         }
    1389             :         else
    1390             :         {
    1391         240 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    1392             :             {
    1393           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    1394             :                             "Not enough positional values.");
    1395           1 :                 return false;
    1396             :             }
    1397         239 :             const size_t iMax = i + arg->GetMaxCount();
    1398         481 :             for (; i < iMax; ++i)
    1399             :             {
    1400         243 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    1401             :                                    inConstructionValues))
    1402             :                 {
    1403           1 :                     ProcessInConstructionValues();
    1404           1 :                     return false;
    1405             :                 }
    1406             :             }
    1407             :         }
    1408         248 :         ++iCurPosArg;
    1409             :     }
    1410             : 
    1411         345 :     if (i < lArgs.size())
    1412             :     {
    1413           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    1414             :                     "Positional values starting at '%s' are not expected.",
    1415           3 :                     lArgs[i].c_str());
    1416           3 :         ProcessInConstructionValues();
    1417           3 :         return false;
    1418             :     }
    1419         410 :     if (iCurPosArg < m_positionalArgs.size() &&
    1420          68 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    1421           1 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    1422          67 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    1423             :     {
    1424         137 :         while (iCurPosArg < m_positionalArgs.size() &&
    1425          53 :                m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    1426             :         {
    1427          31 :             ++iCurPosArg;
    1428             :         }
    1429          53 :         if (iCurPosArg < m_positionalArgs.size())
    1430             :         {
    1431          22 :             ReportError(CE_Failure, CPLE_AppDefined,
    1432             :                         "Positional arguments starting at '%s' have not been "
    1433             :                         "specified.",
    1434          22 :                         m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    1435          22 :             ProcessInConstructionValues();
    1436          22 :             return false;
    1437             :         }
    1438             :     }
    1439             : 
    1440         626 :     return ProcessInConstructionValues() &&
    1441         626 :            (m_skipValidationInParseCommandLine || ValidateArguments());
    1442             : }
    1443             : 
    1444             : /************************************************************************/
    1445             : /*                     GDALAlgorithm::ReportError()                     */
    1446             : /************************************************************************/
    1447             : 
    1448             : //! @cond Doxygen_Suppress
    1449         141 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1450             :                                 const char *fmt, ...) const
    1451             : {
    1452             :     va_list args;
    1453         141 :     va_start(args, fmt);
    1454         141 :     CPLError(eErrClass, err_no, "%s",
    1455         141 :              std::string(m_name)
    1456         141 :                  .append(": ")
    1457         282 :                  .append(CPLString().vPrintf(fmt, args))
    1458             :                  .c_str());
    1459         141 :     va_end(args);
    1460         141 : }
    1461             : 
    1462             : //! @endcond
    1463             : 
    1464             : /************************************************************************/
    1465             : /*                   GDALAlgorithm::ProcessDatasetArg()                 */
    1466             : /************************************************************************/
    1467             : 
    1468         549 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    1469             :                                       GDALAlgorithm *algForOutput)
    1470             : {
    1471         549 :     bool ret = true;
    1472             : 
    1473         549 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    1474         725 :     const bool update = (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    1475         176 :                          updateArg->Get<bool>());
    1476         549 :     const auto overwriteArg = algForOutput->GetArg("overwrite");
    1477             :     const bool overwrite =
    1478         944 :         (arg->IsOutput() && overwriteArg &&
    1479         944 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    1480         549 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    1481         549 :     auto &val = arg->Get<GDALArgDatasetValue>();
    1482         549 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    1483             :     {
    1484           2 :         ReportError(CE_Failure, CPLE_AppDefined,
    1485             :                     "Argument '%s' has no dataset object or dataset name.",
    1486           2 :                     arg->GetName().c_str());
    1487           2 :         ret = false;
    1488             :     }
    1489         869 :     else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
    1490         322 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite)))
    1491             :     {
    1492         135 :         int flags = val.GetType();
    1493         135 :         bool assignToOutputArg = false;
    1494             : 
    1495             :         // Check if input and output parameters point to the same
    1496             :         // filename (for vector datasets)
    1497         255 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    1498         255 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    1499             :         {
    1500           3 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    1501           6 :             if (!outputVal.GetDatasetRef() &&
    1502           6 :                 outputVal.GetName() == val.GetName() &&
    1503           1 :                 (outputVal.GetInputFlags() & GADV_OBJECT) != 0)
    1504             :             {
    1505           1 :                 assignToOutputArg = true;
    1506           1 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    1507             :             }
    1508             :         }
    1509             : 
    1510         135 :         if (!arg->IsOutput() || val.GetInputFlags() == GADV_NAME)
    1511         133 :             flags |= GDAL_OF_VERBOSE_ERROR;
    1512         135 :         if ((arg == outputArg || !outputArg) && update)
    1513           3 :             flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    1514             : 
    1515         135 :         const auto readOnlyArg = algForOutput->GetArg(GDAL_ARG_NAME_READ_ONLY);
    1516             :         const bool readOnly =
    1517         138 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    1518           3 :              readOnlyArg->Get<bool>());
    1519         135 :         if (readOnly)
    1520           2 :             flags &= ~GDAL_OF_UPDATE;
    1521             : 
    1522         270 :         CPLStringList aosOpenOptions;
    1523         270 :         CPLStringList aosAllowedDrivers;
    1524         135 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    1525             :         {
    1526         120 :             const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    1527         120 :             if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    1528             :                 aosOpenOptions =
    1529         117 :                     CPLStringList(ooArg->Get<std::vector<std::string>>());
    1530             : 
    1531         120 :             const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    1532         120 :             if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    1533             :                 aosAllowedDrivers =
    1534         117 :                     CPLStringList(ifArg->Get<std::vector<std::string>>());
    1535             :         }
    1536             : 
    1537             :         auto poDS =
    1538         135 :             GDALDataset::Open(val.GetName().c_str(), flags,
    1539         135 :                               aosAllowedDrivers.List(), aosOpenOptions.List());
    1540         135 :         if (poDS)
    1541             :         {
    1542         120 :             if (assignToOutputArg)
    1543             :             {
    1544             :                 // Avoid opening twice the same datasource if it is both
    1545             :                 // the input and output.
    1546             :                 // Known to cause problems with at least FGdb, SQLite
    1547             :                 // and GPKG drivers. See #4270
    1548             :                 // Restrict to those 3 drivers. For example it is known
    1549             :                 // to break with the PG driver due to the way it
    1550             :                 // manages transactions.
    1551           1 :                 auto poDriver = poDS->GetDriver();
    1552           2 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    1553           1 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    1554           1 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    1555             :                 {
    1556           1 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    1557             :                 }
    1558             :             }
    1559         120 :             val.Set(poDS);
    1560         120 :             poDS->ReleaseRef();
    1561             :         }
    1562             :         else
    1563             :         {
    1564          15 :             ret = false;
    1565             :         }
    1566             :     }
    1567         549 :     return ret;
    1568             : }
    1569             : 
    1570             : /************************************************************************/
    1571             : /*                   GDALAlgorithm::ValidateArguments()                 */
    1572             : /************************************************************************/
    1573             : 
    1574         600 : bool GDALAlgorithm::ValidateArguments()
    1575             : {
    1576         600 :     if (m_selectedSubAlg)
    1577           3 :         return m_selectedSubAlg->ValidateArguments();
    1578             : 
    1579         597 :     if (m_specialActionRequested)
    1580           1 :         return true;
    1581             : 
    1582         596 :     bool ret = true;
    1583         596 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    1584        8095 :     for (auto &arg : m_args)
    1585             :     {
    1586             :         // Check mutually exclusive arguments
    1587        7499 :         if (arg->IsExplicitlySet())
    1588             :         {
    1589         893 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    1590         893 :             if (!mutualExclusionGroup.empty())
    1591             :             {
    1592             :                 auto oIter =
    1593          30 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    1594          30 :                 if (oIter != mutualExclusionGroupUsed.end())
    1595             :                 {
    1596           1 :                     ret = false;
    1597           2 :                     ReportError(
    1598             :                         CE_Failure, CPLE_AppDefined,
    1599             :                         "Argument '%s' is mutually exclusive with '%s'.",
    1600           2 :                         arg->GetName().c_str(), oIter->second.c_str());
    1601             :                 }
    1602             :                 else
    1603             :                 {
    1604          29 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    1605          58 :                         arg->GetName();
    1606             :                 }
    1607             :             }
    1608             :         }
    1609             : 
    1610        7505 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    1611           6 :             !arg->HasDefaultValue())
    1612             :         {
    1613           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1614             :                         "Required argument '%s' has not been specified.",
    1615           6 :                         arg->GetName().c_str());
    1616           6 :             ret = false;
    1617             :         }
    1618        7493 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    1619             :         {
    1620         495 :             if (!ProcessDatasetArg(arg.get(), this))
    1621          16 :                 ret = false;
    1622             :         }
    1623        7396 :         else if (arg->IsExplicitlySet() &&
    1624         398 :                  GDALAlgorithmArgTypeIsList(arg->GetType()))
    1625             :         {
    1626         123 :             int valueCount = 0;
    1627         123 :             if (arg->GetType() == GAAT_STRING_LIST)
    1628             :             {
    1629          58 :                 valueCount = static_cast<int>(
    1630          58 :                     arg->Get<std::vector<std::string>>().size());
    1631             :             }
    1632          65 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    1633             :             {
    1634           8 :                 valueCount =
    1635           8 :                     static_cast<int>(arg->Get<std::vector<int>>().size());
    1636             :             }
    1637          57 :             else if (arg->GetType() == GAAT_REAL_LIST)
    1638             :             {
    1639          27 :                 valueCount =
    1640          27 :                     static_cast<int>(arg->Get<std::vector<double>>().size());
    1641             :             }
    1642          30 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    1643             :             {
    1644          30 :                 valueCount = static_cast<int>(
    1645          30 :                     arg->Get<std::vector<GDALArgDatasetValue>>().size());
    1646             :             }
    1647             : 
    1648         197 :             if (valueCount != arg->GetMinCount() &&
    1649          74 :                 arg->GetMinCount() == arg->GetMaxCount())
    1650             :             {
    1651           2 :                 ReportError(
    1652             :                     CE_Failure, CPLE_AppDefined,
    1653             :                     "%d value(s) have been specified for argument '%s', "
    1654             :                     "whereas exactly %d were expected.",
    1655           1 :                     valueCount, arg->GetName().c_str(), arg->GetMinCount());
    1656           1 :                 ret = false;
    1657             :             }
    1658         122 :             else if (valueCount < arg->GetMinCount())
    1659             :             {
    1660           4 :                 ReportError(
    1661             :                     CE_Failure, CPLE_AppDefined,
    1662             :                     "Only %d value(s) have been specified for argument '%s', "
    1663             :                     "whereas at least %d were expected.",
    1664           2 :                     valueCount, arg->GetName().c_str(), arg->GetMinCount());
    1665           2 :                 ret = false;
    1666             :             }
    1667         120 :             else if (valueCount > arg->GetMaxCount())
    1668             :             {
    1669           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
    1670             :                             "%d values have been specified for argument '%s', "
    1671             :                             "whereas at most %d were expected.",
    1672           1 :                             valueCount, arg->GetName().c_str(),
    1673             :                             arg->GetMaxCount());
    1674           1 :                 ret = false;
    1675             :             }
    1676             :         }
    1677             : 
    1678        7529 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
    1679          30 :             arg->AutoOpenDataset())
    1680             :         {
    1681           4 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    1682           8 :             for (auto &val : listVal)
    1683             :             {
    1684           4 :                 if (!val.GetDatasetRef() && val.GetName().empty())
    1685             :                 {
    1686           1 :                     ReportError(
    1687             :                         CE_Failure, CPLE_AppDefined,
    1688             :                         "Argument '%s' has no dataset object or dataset name.",
    1689           1 :                         arg->GetName().c_str());
    1690           1 :                     ret = false;
    1691             :                 }
    1692           3 :                 else if (!val.GetDatasetRef())
    1693             :                 {
    1694           3 :                     int flags = val.GetType() | GDAL_OF_VERBOSE_ERROR;
    1695             : 
    1696           6 :                     CPLStringList aosOpenOptions;
    1697           6 :                     CPLStringList aosAllowedDrivers;
    1698           3 :                     if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    1699             :                     {
    1700           1 :                         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    1701           1 :                         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    1702             :                         {
    1703           1 :                             aosOpenOptions = CPLStringList(
    1704           1 :                                 ooArg->Get<std::vector<std::string>>());
    1705             :                         }
    1706             : 
    1707           1 :                         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    1708           1 :                         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    1709             :                         {
    1710           1 :                             aosAllowedDrivers = CPLStringList(
    1711           1 :                                 ifArg->Get<std::vector<std::string>>());
    1712             :                         }
    1713             : 
    1714           1 :                         const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    1715           2 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    1716           1 :                             updateArg->Get<bool>())
    1717             :                         {
    1718           1 :                             flags |= GDAL_OF_UPDATE;
    1719             :                         }
    1720             :                     }
    1721             : 
    1722             :                     auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    1723           3 :                         val.GetName().c_str(), flags, aosAllowedDrivers.List(),
    1724           9 :                         aosOpenOptions.List()));
    1725           3 :                     if (poDS)
    1726             :                     {
    1727           2 :                         val.Set(std::move(poDS));
    1728             :                     }
    1729             :                     else
    1730             :                     {
    1731           1 :                         ret = false;
    1732             :                     }
    1733             :                 }
    1734             :             }
    1735             :         }
    1736             :     }
    1737         596 :     return ret;
    1738             : }
    1739             : 
    1740             : /************************************************************************/
    1741             : /*                      GDALAlgorithm::GetArg()                         */
    1742             : /************************************************************************/
    1743             : 
    1744        2757 : const GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName) const
    1745             : {
    1746        2757 :     const auto nPos = osName.find_first_not_of('-');
    1747        2757 :     if (nPos == std::string::npos)
    1748           8 :         return nullptr;
    1749        5498 :     const std::string osKey = osName.substr(nPos);
    1750             :     {
    1751        2749 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    1752        2749 :         if (oIter != m_mapLongNameToArg.end())
    1753        1765 :             return oIter->second;
    1754             :     }
    1755             :     {
    1756         984 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    1757         984 :         if (oIter != m_mapShortNameToArg.end())
    1758           2 :             return oIter->second;
    1759             :     }
    1760         982 :     return nullptr;
    1761             : }
    1762             : 
    1763             : /************************************************************************/
    1764             : /*                   GDALAlgorithm::AddAliasFor()                       */
    1765             : /************************************************************************/
    1766             : 
    1767             : //! @cond Doxygen_Suppress
    1768        2547 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    1769             :                                 const std::string &alias)
    1770             : {
    1771        2547 :     if (cpl::contains(m_mapLongNameToArg, alias))
    1772             :     {
    1773           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    1774             :                     alias.c_str());
    1775             :     }
    1776             :     else
    1777             :     {
    1778        2546 :         m_mapLongNameToArg[alias] = arg;
    1779             :     }
    1780        2547 : }
    1781             : 
    1782             : //! @endcond
    1783             : 
    1784             : /************************************************************************/
    1785             : /*                   GDALAlgorithm::SetPositional()                     */
    1786             : /************************************************************************/
    1787             : 
    1788             : //! @cond Doxygen_Suppress
    1789         733 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    1790             : {
    1791         733 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    1792             :                         arg) == m_positionalArgs.end());
    1793         733 :     m_positionalArgs.push_back(arg);
    1794         733 : }
    1795             : 
    1796             : //! @endcond
    1797             : 
    1798             : /************************************************************************/
    1799             : /*                     GDALAlgorithm::AddArg()                          */
    1800             : /************************************************************************/
    1801             : 
    1802             : GDALInConstructionAlgorithmArg &
    1803       11036 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    1804             : {
    1805       11036 :     auto argRaw = arg.get();
    1806       11036 :     const auto &longName = argRaw->GetName();
    1807       11036 :     if (!longName.empty())
    1808             :     {
    1809       11033 :         if (longName[0] == '-')
    1810             :         {
    1811           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    1812             :                         "Long name '%s' should not start with '-'",
    1813             :                         longName.c_str());
    1814             :         }
    1815       11033 :         if (longName.find('=') != std::string::npos)
    1816             :         {
    1817           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    1818             :                         "Long name '%s' should not contain a '=' character",
    1819             :                         longName.c_str());
    1820             :         }
    1821       11033 :         if (cpl::contains(m_mapLongNameToArg, longName))
    1822             :         {
    1823           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    1824             :                         "Long name '%s' already declared", longName.c_str());
    1825             :         }
    1826       11033 :         m_mapLongNameToArg[longName] = argRaw;
    1827             :     }
    1828       11036 :     const auto &shortName = argRaw->GetShortName();
    1829       11036 :     if (!shortName.empty())
    1830             :     {
    1831        5368 :         if (shortName.size() != 1 ||
    1832        2684 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    1833           3 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    1834           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    1835             :         {
    1836           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    1837             :                         "Short name '%s' should be a single letter or digit",
    1838             :                         shortName.c_str());
    1839             :         }
    1840        2684 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    1841             :         {
    1842           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    1843             :                         "Short name '%s' already declared", shortName.c_str());
    1844             :         }
    1845        2684 :         m_mapShortNameToArg[shortName] = argRaw;
    1846             :     }
    1847       11036 :     m_args.emplace_back(std::move(arg));
    1848             :     return *(
    1849       11036 :         static_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    1850             : }
    1851             : 
    1852             : GDALInConstructionAlgorithmArg &
    1853        6256 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1854             :                       const std::string &helpMessage, bool *pValue)
    1855             : {
    1856        6256 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1857             :         this,
    1858       12512 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    1859       12512 :         pValue));
    1860             : }
    1861             : 
    1862             : GDALInConstructionAlgorithmArg &
    1863        1107 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1864             :                       const std::string &helpMessage, std::string *pValue)
    1865             : {
    1866        1107 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1867             :         this,
    1868        2214 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    1869        2214 :         pValue));
    1870             : }
    1871             : 
    1872             : GDALInConstructionAlgorithmArg &
    1873          66 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1874             :                       const std::string &helpMessage, int *pValue)
    1875             : {
    1876          66 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1877             :         this,
    1878         132 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    1879         132 :         pValue));
    1880             : }
    1881             : 
    1882             : GDALInConstructionAlgorithmArg &
    1883           4 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1884             :                       const std::string &helpMessage, double *pValue)
    1885             : {
    1886           4 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1887             :         this,
    1888           8 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    1889           8 :         pValue));
    1890             : }
    1891             : 
    1892             : GDALInConstructionAlgorithmArg &
    1893         781 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1894             :                       const std::string &helpMessage,
    1895             :                       GDALArgDatasetValue *pValue, GDALArgDatasetValueType type)
    1896             : {
    1897         781 :     pValue->SetType(type);
    1898         781 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1899             :         this,
    1900        1562 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_DATASET),
    1901         781 :         pValue));
    1902         781 :     pValue->SetOwnerArgument(&arg);
    1903         781 :     return arg;
    1904             : }
    1905             : 
    1906             : GDALInConstructionAlgorithmArg &
    1907        2567 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1908             :                       const std::string &helpMessage,
    1909             :                       std::vector<std::string> *pValue)
    1910             : {
    1911        2567 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1912             :         this,
    1913        5134 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    1914             :                              GAAT_STRING_LIST),
    1915        5134 :         pValue));
    1916             : }
    1917             : 
    1918             : GDALInConstructionAlgorithmArg &
    1919          44 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1920             :                       const std::string &helpMessage, std::vector<int> *pValue)
    1921             : {
    1922          44 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1923             :         this,
    1924          88 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    1925             :                              GAAT_INTEGER_LIST),
    1926          88 :         pValue));
    1927             : }
    1928             : 
    1929             : GDALInConstructionAlgorithmArg &
    1930         182 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1931             :                       const std::string &helpMessage,
    1932             :                       std::vector<double> *pValue)
    1933             : {
    1934         182 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1935             :         this,
    1936         364 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    1937             :                              GAAT_REAL_LIST),
    1938         364 :         pValue));
    1939             : }
    1940             : 
    1941             : GDALInConstructionAlgorithmArg &
    1942          29 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    1943             :                       const std::string &helpMessage,
    1944             :                       std::vector<GDALArgDatasetValue> *pValue,
    1945             :                       GDALArgDatasetValueType)
    1946             : {
    1947             :     // FIXME
    1948             :     // pValue->SetType(type);
    1949          29 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    1950             :         this,
    1951          58 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    1952             :                              GAAT_DATASET_LIST),
    1953          58 :         pValue));
    1954             : }
    1955             : 
    1956             : /************************************************************************/
    1957             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    1958             : /************************************************************************/
    1959             : 
    1960             : GDALInConstructionAlgorithmArg &
    1961         410 : GDALAlgorithm::AddInputDatasetArg(GDALArgDatasetValue *pValue,
    1962             :                                   GDALArgDatasetValueType type,
    1963             :                                   bool positionalAndRequired)
    1964             : {
    1965             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
    1966             :                        CPLSPrintf("Input %s dataset",
    1967         410 :                                   GDALArgDatasetValueTypeName(type).c_str()),
    1968         820 :                        pValue, type);
    1969         410 :     if (positionalAndRequired)
    1970         293 :         arg.SetPositional().SetRequired();
    1971             : 
    1972             :     arg.SetAutoCompleteFunction(
    1973        1176 :         [type](const std::string &currentValue)
    1974             :         {
    1975           3 :             std::vector<std::string> oRet;
    1976             : 
    1977           3 :             auto poDM = GetGDALDriverManager();
    1978           6 :             std::set<std::string> oExtensions;
    1979         720 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    1980             :             {
    1981         717 :                 auto poDriver = poDM->GetDriver(i);
    1982        2151 :                 if (((type & GDAL_OF_RASTER) != 0 &&
    1983         717 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    1984         228 :                     ((type & GDAL_OF_VECTOR) != 0 &&
    1985        1434 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    1986         228 :                     ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    1987           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    1988             :                 {
    1989             :                     const char *pszExtensions =
    1990         489 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    1991         489 :                     if (pszExtensions)
    1992             :                     {
    1993             :                         const CPLStringList aosExts(
    1994         630 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    1995         693 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    1996         378 :                             oExtensions.insert(CPLString(pszExt).tolower());
    1997             :                     }
    1998             :                 }
    1999             :             }
    2000             : 
    2001           6 :             std::string osDir = CPLGetDirnameSafe(currentValue.c_str());
    2002           3 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    2003           6 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    2004           3 :             if (currentValue.empty())
    2005           1 :                 osDir.clear();
    2006             :             const std::string currentFilename =
    2007           6 :                 CPLGetFilename(currentValue.c_str());
    2008           3 :             if (psDir)
    2009             :             {
    2010         115 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    2011             :                 {
    2012         112 :                     if ((currentFilename.empty() ||
    2013           0 :                          STARTS_WITH(psEntry->pszName,
    2014         112 :                                      currentFilename.c_str())) &&
    2015         112 :                         strcmp(psEntry->pszName, ".") != 0 &&
    2016         336 :                         strcmp(psEntry->pszName, "..") != 0 &&
    2017         112 :                         !strstr(psEntry->pszName, ".aux.xml"))
    2018             :                     {
    2019         333 :                         if (cpl::contains(
    2020             :                                 oExtensions,
    2021         222 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    2022         310 :                                     .tolower()) ||
    2023          88 :                             VSI_ISDIR(psEntry->nMode))
    2024             :                         {
    2025          54 :                             std::string osVal;
    2026          27 :                             if (osDir.empty())
    2027           4 :                                 osVal = psEntry->pszName;
    2028             :                             else
    2029          46 :                                 osVal = CPLFormFilenameSafe(
    2030          46 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    2031          27 :                             if (VSI_ISDIR(psEntry->nMode))
    2032           4 :                                 osVal += osSep;
    2033          27 :                             oRet.push_back(osVal);
    2034             :                         }
    2035             :                     }
    2036         112 :                 }
    2037           3 :                 VSICloseDir(psDir);
    2038             :             }
    2039           6 :             return oRet;
    2040         410 :         });
    2041             : 
    2042         410 :     return arg;
    2043             : }
    2044             : 
    2045             : /************************************************************************/
    2046             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    2047             : /************************************************************************/
    2048             : 
    2049             : GDALInConstructionAlgorithmArg &
    2050           1 : GDALAlgorithm::AddInputDatasetArg(std::vector<GDALArgDatasetValue> *pValue,
    2051             :                                   GDALArgDatasetValueType type,
    2052             :                                   bool positionalAndRequired)
    2053             : {
    2054             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT, 'i',
    2055             :                        CPLSPrintf("Input %s datasets",
    2056           1 :                                   GDALArgDatasetValueTypeName(type).c_str()),
    2057           2 :                        pValue, type);
    2058           1 :     if (positionalAndRequired)
    2059           1 :         arg.SetPositional().SetRequired();
    2060           1 :     return arg;
    2061             : }
    2062             : 
    2063             : /************************************************************************/
    2064             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    2065             : /************************************************************************/
    2066             : 
    2067             : GDALInConstructionAlgorithmArg &
    2068         321 : GDALAlgorithm::AddOutputDatasetArg(GDALArgDatasetValue *pValue,
    2069             :                                    GDALArgDatasetValueType type,
    2070             :                                    bool positionalAndRequired)
    2071             : {
    2072         321 :     pValue->SetInputFlags(GADV_NAME);
    2073         321 :     pValue->SetOutputFlags(GADV_OBJECT);
    2074             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    2075             :                        CPLSPrintf("Output %s dataset",
    2076         321 :                                   GDALArgDatasetValueTypeName(type).c_str()),
    2077         963 :                        pValue, type)
    2078         321 :                     .SetIsInput(true)
    2079         321 :                     .SetIsOutput(true);
    2080         321 :     if (positionalAndRequired)
    2081         204 :         arg.SetPositional().SetRequired();
    2082         321 :     return arg;
    2083             : }
    2084             : 
    2085             : /************************************************************************/
    2086             : /*                 GDALAlgorithm::AddOverwriteArg()                     */
    2087             : /************************************************************************/
    2088             : 
    2089         312 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOverwriteArg(bool *pValue)
    2090             : {
    2091             :     return AddArg("overwrite", 0,
    2092         624 :                   _("Whether overwriting existing output is allowed"), pValue)
    2093         624 :         .SetDefault(false);
    2094             : }
    2095             : 
    2096             : /************************************************************************/
    2097             : /*                 GDALAlgorithm::AddUpdateArg()                        */
    2098             : /************************************************************************/
    2099             : 
    2100         132 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddUpdateArg(bool *pValue)
    2101             : {
    2102             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    2103         264 :                   _("Whether to open existing dataset in update mode"), pValue)
    2104         264 :         .SetDefault(false);
    2105             : }
    2106             : 
    2107             : /************************************************************************/
    2108             : /*                          AddOptionsSuggestions()                     */
    2109             : /************************************************************************/
    2110             : 
    2111          19 : static bool AddOptionsSuggestions(const char *pszXML, int datasetType,
    2112             :                                   const std::string &currentValue,
    2113             :                                   std::vector<std::string> &oRet)
    2114             : {
    2115          19 :     if (!pszXML)
    2116           0 :         return false;
    2117          38 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    2118          19 :     if (!poTree)
    2119           0 :         return false;
    2120             :     const CPLXMLNode *psRoot =
    2121          19 :         CPLGetXMLNode(poTree.get(), "=CreationOptionList");
    2122          19 :     if (!psRoot)
    2123           4 :         psRoot = CPLGetXMLNode(poTree.get(), "=LayerCreationOptionList");
    2124          19 :     if (!psRoot)
    2125           2 :         psRoot = CPLGetXMLNode(poTree.get(), "=OpenOptionList");
    2126          19 :     if (!psRoot)
    2127           0 :         return false;
    2128             : 
    2129         307 :     for (const CPLXMLNode *psChild = psRoot->psChild; psChild;
    2130         288 :          psChild = psChild->psNext)
    2131             :     {
    2132         298 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    2133         308 :         if (pszName && currentValue == pszName &&
    2134          10 :             EQUAL(psChild->pszValue, "Option"))
    2135             :         {
    2136          10 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    2137          10 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    2138          10 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    2139          10 :             if (EQUAL(pszType, "string-select"))
    2140             :             {
    2141          72 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    2142          68 :                      psChild2 = psChild2->psNext)
    2143             :                 {
    2144          68 :                     if (EQUAL(psChild2->pszValue, "Value"))
    2145             :                     {
    2146          60 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    2147             :                     }
    2148             :                 }
    2149             :             }
    2150           6 :             else if (EQUAL(pszType, "boolean"))
    2151             :             {
    2152           1 :                 oRet.push_back("NO");
    2153           1 :                 oRet.push_back("YES");
    2154             :             }
    2155           5 :             else if (EQUAL(pszType, "int"))
    2156             :             {
    2157           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    2158           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    2159             :                 {
    2160           1 :                     const int nMax = atoi(pszMax);
    2161          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    2162          12 :                         oRet.push_back(std::to_string(i));
    2163             :                 }
    2164             :             }
    2165             : 
    2166          10 :             if (oRet.empty())
    2167             :             {
    2168           4 :                 if (pszMin && pszMax)
    2169             :                 {
    2170           1 :                     oRet.push_back(std::string("##"));
    2171           2 :                     oRet.push_back(std::string("validity range: [")
    2172           1 :                                        .append(pszMin)
    2173           1 :                                        .append(",")
    2174           1 :                                        .append(pszMax)
    2175           1 :                                        .append("]"));
    2176             :                 }
    2177           3 :                 else if (pszMin)
    2178             :                 {
    2179           1 :                     oRet.push_back(std::string("##"));
    2180           1 :                     oRet.push_back(
    2181           1 :                         std::string("validity range: >= ").append(pszMin));
    2182             :                 }
    2183           2 :                 else if (pszMax)
    2184             :                 {
    2185           1 :                     oRet.push_back(std::string("##"));
    2186           1 :                     oRet.push_back(
    2187           1 :                         std::string("validity range: <= ").append(pszMax));
    2188             :                 }
    2189           1 :                 else if (const char *pszDescription =
    2190           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    2191             :                 {
    2192           1 :                     oRet.push_back(std::string("##"));
    2193           2 :                     oRet.push_back(std::string("type: ")
    2194           1 :                                        .append(pszType)
    2195           1 :                                        .append(", description: ")
    2196           1 :                                        .append(pszDescription));
    2197             :                 }
    2198             :             }
    2199             : 
    2200          10 :             return true;
    2201             :         }
    2202             :     }
    2203             : 
    2204         228 :     for (const CPLXMLNode *psChild = psRoot->psChild; psChild;
    2205         219 :          psChild = psChild->psNext)
    2206             :     {
    2207         219 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    2208         219 :         if (pszName && EQUAL(psChild->pszValue, "Option"))
    2209             :         {
    2210         219 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    2211         219 :             if (!pszScope ||
    2212          40 :                 (EQUAL(pszScope, "raster") &&
    2213          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    2214          20 :                 (EQUAL(pszScope, "vector") &&
    2215           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    2216             :             {
    2217         199 :                 oRet.push_back(std::string(pszName).append("="));
    2218             :             }
    2219             :         }
    2220             :     }
    2221             : 
    2222           9 :     return false;
    2223             : }
    2224             : 
    2225             : /************************************************************************/
    2226             : /*                 GDALAlgorithm::AddOpenOptionsArg()                   */
    2227             : /************************************************************************/
    2228             : 
    2229             : GDALInConstructionAlgorithmArg &
    2230         388 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue)
    2231             : {
    2232         776 :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0, _("Open options"), pValue)
    2233         776 :                     .AddAlias("oo")
    2234         776 :                     .SetMetaVar("KEY=VALUE")
    2235         388 :                     .SetCategory(GAAC_ADVANCED);
    2236             : 
    2237             :     arg.SetAutoCompleteFunction(
    2238           6 :         [this](const std::string &currentValue)
    2239             :         {
    2240           2 :             std::vector<std::string> oRet;
    2241             : 
    2242           2 :             int datasetType =
    2243             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    2244           2 :             auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    2245           2 :             if (inputArg && inputArg->GetType() == GAAT_DATASET)
    2246             :             {
    2247           2 :                 auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    2248           2 :                 datasetType = datasetValue.GetType();
    2249             :             }
    2250             : 
    2251           2 :             auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2252           4 :             if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    2253           2 :                 inputFormat->IsExplicitlySet())
    2254             :             {
    2255             :                 const auto &aosAllowedDrivers =
    2256           1 :                     inputFormat->Get<std::vector<std::string>>();
    2257           1 :                 if (aosAllowedDrivers.size() == 1)
    2258             :                 {
    2259           2 :                     auto poDriver = GetGDALDriverManager()->GetDriverByName(
    2260           1 :                         aosAllowedDrivers[0].c_str());
    2261           1 :                     if (poDriver)
    2262             :                     {
    2263           1 :                         AddOptionsSuggestions(
    2264           1 :                             poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    2265             :                             datasetType, currentValue, oRet);
    2266             :                     }
    2267           1 :                     return oRet;
    2268             :                 }
    2269             :             }
    2270             : 
    2271           1 :             if (inputArg && inputArg->GetType() == GAAT_DATASET)
    2272             :             {
    2273           1 :                 auto poDM = GetGDALDriverManager();
    2274           1 :                 auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    2275           1 :                 const auto &osDSName = datasetValue.GetName();
    2276           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    2277           1 :                 if (!osExt.empty())
    2278             :                 {
    2279           1 :                     std::set<std::string> oVisitedExtensions;
    2280         240 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    2281             :                     {
    2282         239 :                         auto poDriver = poDM->GetDriver(i);
    2283         717 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    2284         239 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    2285          76 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    2286         478 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    2287          76 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    2288           0 :                              poDriver->GetMetadataItem(
    2289           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    2290             :                         {
    2291             :                             const char *pszExtensions =
    2292         163 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    2293         163 :                             if (pszExtensions)
    2294             :                             {
    2295             :                                 const CPLStringList aosExts(
    2296         105 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    2297         229 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    2298             :                                 {
    2299         128 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    2300           3 :                                         !cpl::contains(oVisitedExtensions,
    2301             :                                                        pszExt))
    2302             :                                     {
    2303           1 :                                         oVisitedExtensions.insert(pszExt);
    2304           1 :                                         if (AddOptionsSuggestions(
    2305             :                                                 poDriver->GetMetadataItem(
    2306           1 :                                                     GDAL_DMD_OPENOPTIONLIST),
    2307             :                                                 datasetType, currentValue,
    2308             :                                                 oRet))
    2309             :                                         {
    2310           0 :                                             return oRet;
    2311             :                                         }
    2312           1 :                                         break;
    2313             :                                     }
    2314             :                                 }
    2315             :                             }
    2316             :                         }
    2317             :                     }
    2318             :                 }
    2319             :             }
    2320             : 
    2321           1 :             return oRet;
    2322         388 :         });
    2323             : 
    2324         388 :     return arg;
    2325             : }
    2326             : 
    2327             : /************************************************************************/
    2328             : /*                            ValidateFormat()                          */
    2329             : /************************************************************************/
    2330             : 
    2331          42 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg) const
    2332             : {
    2333          42 :     if (arg.GetChoices().empty())
    2334             :     {
    2335          43 :         const auto Validate = [this, &arg](const std::string &val)
    2336             :         {
    2337          19 :             auto hDriver = GDALGetDriverByName(val.c_str());
    2338          19 :             if (!hDriver)
    2339             :             {
    2340           2 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2341             :                             "Invalid value for argument '%s'. Driver '%s' does "
    2342             :                             "not exist",
    2343           1 :                             arg.GetName().c_str(), val.c_str());
    2344           1 :                 return false;
    2345             :             }
    2346             : 
    2347          18 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    2348          18 :             if (caps)
    2349             :             {
    2350          46 :                 for (const std::string &cap : *caps)
    2351             :                 {
    2352          31 :                     if (!GDALGetMetadataItem(hDriver, cap.c_str(), nullptr))
    2353             :                     {
    2354           8 :                         if (cap == GDAL_DCAP_CREATECOPY &&
    2355           0 :                             std::find(caps->begin(), caps->end(),
    2356           3 :                                       GDAL_DCAP_RASTER) != caps->end() &&
    2357           3 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
    2358           8 :                                                 nullptr) &&
    2359           3 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
    2360             :                                                 nullptr))
    2361             :                         {
    2362             :                             // if it supports Create, it supports CreateCopy
    2363             :                         }
    2364             :                         else
    2365             :                         {
    2366           4 :                             ReportError(
    2367             :                                 CE_Failure, CPLE_AppDefined,
    2368             :                                 "Invalid value for argument '%s'. Driver '%s' "
    2369             :                                 "does "
    2370             :                                 "not expose the required '%s' capability.",
    2371           2 :                                 arg.GetName().c_str(), val.c_str(),
    2372             :                                 cap.c_str());
    2373           2 :                             return false;
    2374             :                         }
    2375             :                     }
    2376             :                 }
    2377             :             }
    2378          16 :             return true;
    2379          19 :         };
    2380             : 
    2381          19 :         if (arg.GetType() == GAAT_STRING)
    2382             :         {
    2383          17 :             return Validate(arg.Get<std::string>());
    2384             :         }
    2385           4 :         else if (arg.GetType() == GAAT_STRING_LIST)
    2386             :         {
    2387           6 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    2388             :             {
    2389           4 :                 if (!Validate(val))
    2390           2 :                     return false;
    2391             :             }
    2392             :         }
    2393             :     }
    2394             : 
    2395          25 :     return true;
    2396             : }
    2397             : 
    2398             : /************************************************************************/
    2399             : /*                    FormatAutoCompleteFunction()                      */
    2400             : /************************************************************************/
    2401             : 
    2402             : static std::vector<std::string>
    2403           1 : FormatAutoCompleteFunction(const GDALAlgorithmArg &arg)
    2404             : {
    2405           1 :     std::vector<std::string> res;
    2406           1 :     auto poDM = GetGDALDriverManager();
    2407         240 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    2408             :     {
    2409         239 :         auto poDriver = poDM->GetDriver(i);
    2410             : 
    2411         239 :         const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    2412         239 :         if (caps)
    2413             :         {
    2414         239 :             bool ok = true;
    2415         491 :             for (const std::string &cap : *caps)
    2416             :             {
    2417         402 :                 if (poDriver->GetMetadataItem(cap.c_str()))
    2418             :                 {
    2419             :                 }
    2420         280 :                 else if (cap == GDAL_DCAP_CREATECOPY &&
    2421           0 :                          std::find(caps->begin(), caps->end(),
    2422         102 :                                    GDAL_DCAP_RASTER) != caps->end() &&
    2423         382 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    2424         102 :                          poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    2425             :                 {
    2426             :                     // if it supports Create, it supports CreateCopy
    2427             :                 }
    2428             :                 else
    2429             :                 {
    2430         150 :                     ok = false;
    2431         150 :                     break;
    2432             :                 }
    2433             :             }
    2434         239 :             if (ok)
    2435             :             {
    2436          89 :                 res.push_back(poDriver->GetDescription());
    2437             :             }
    2438             :         }
    2439             :     }
    2440           1 :     return res;
    2441             : }
    2442             : 
    2443             : /************************************************************************/
    2444             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    2445             : /************************************************************************/
    2446             : 
    2447             : GDALInConstructionAlgorithmArg &
    2448         375 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue)
    2449             : {
    2450             :     auto &arg =
    2451         750 :         AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0, _("Input formats"), pValue)
    2452         750 :             .AddAlias("if")
    2453         375 :             .SetCategory(GAAC_ADVANCED);
    2454         379 :     arg.AddValidationAction([this, &arg]() { return ValidateFormat(arg); });
    2455           0 :     arg.SetAutoCompleteFunction([&arg](const std::string &)
    2456         375 :                                 { return FormatAutoCompleteFunction(arg); });
    2457         375 :     return arg;
    2458             : }
    2459             : 
    2460             : /************************************************************************/
    2461             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    2462             : /************************************************************************/
    2463             : 
    2464             : GDALInConstructionAlgorithmArg &
    2465         404 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue)
    2466             : {
    2467             :     auto &arg =
    2468         808 :         AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f', _("Output format"), pValue)
    2469         808 :             .AddAlias("of")
    2470         404 :             .AddAlias("format");
    2471         442 :     arg.AddValidationAction([this, &arg]() { return ValidateFormat(arg); });
    2472           1 :     arg.SetAutoCompleteFunction([&arg](const std::string &)
    2473         405 :                                 { return FormatAutoCompleteFunction(arg); });
    2474         404 :     return arg;
    2475             : }
    2476             : 
    2477             : /************************************************************************/
    2478             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    2479             : /************************************************************************/
    2480             : 
    2481             : GDALInConstructionAlgorithmArg &
    2482          80 : GDALAlgorithm::AddOutputStringArg(std::string *pValue)
    2483             : {
    2484             :     return AddArg("output-string", 0,
    2485         160 :                   _("Output string, in which the result is placed"), pValue)
    2486          80 :         .SetHiddenForCLI()
    2487          80 :         .SetIsInput(false)
    2488         160 :         .SetIsOutput(true);
    2489             : }
    2490             : 
    2491             : /************************************************************************/
    2492             : /*                    GDALAlgorithm::AddLayerNameArg()                  */
    2493             : /************************************************************************/
    2494             : 
    2495             : GDALInConstructionAlgorithmArg &
    2496           1 : GDALAlgorithm::AddLayerNameArg(std::string *pValue)
    2497             : {
    2498           1 :     return AddArg("layer", 'l', _("Layer name"), pValue);
    2499             : }
    2500             : 
    2501             : /************************************************************************/
    2502             : /*                    GDALAlgorithm::AddLayerNameArg()                  */
    2503             : /************************************************************************/
    2504             : 
    2505             : GDALInConstructionAlgorithmArg &
    2506          36 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue)
    2507             : {
    2508          36 :     return AddArg("layer", 'l', _("Layer name"), pValue);
    2509             : }
    2510             : 
    2511             : /************************************************************************/
    2512             : /*                          ValidateKeyValue()                          */
    2513             : /************************************************************************/
    2514             : 
    2515          15 : bool GDALAlgorithm::ValidateKeyValue(const GDALAlgorithmArg &arg) const
    2516             : {
    2517          22 :     const auto Validate = [this, &arg](const std::string &val)
    2518             :     {
    2519          20 :         if (val.find('=') == std::string::npos)
    2520             :         {
    2521           2 :             ReportError(
    2522             :                 CE_Failure, CPLE_AppDefined,
    2523             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    2524           2 :                 arg.GetName().c_str());
    2525           2 :             return false;
    2526             :         }
    2527             : 
    2528          18 :         return true;
    2529          15 :     };
    2530             : 
    2531          15 :     if (arg.GetType() == GAAT_STRING)
    2532             :     {
    2533           0 :         return Validate(arg.Get<std::string>());
    2534             :     }
    2535          15 :     else if (arg.GetType() == GAAT_STRING_LIST)
    2536             :     {
    2537          33 :         for (const auto &val : arg.Get<std::vector<std::string>>())
    2538             :         {
    2539          20 :             if (!Validate(val))
    2540           2 :                 return false;
    2541             :         }
    2542             :     }
    2543             : 
    2544          13 :     return true;
    2545             : }
    2546             : 
    2547             : /************************************************************************/
    2548             : /*                 GDALAlgorithm::AddCreationOptionsArg()               */
    2549             : /************************************************************************/
    2550             : 
    2551             : GDALInConstructionAlgorithmArg &
    2552         314 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue)
    2553             : {
    2554         628 :     auto &arg = AddArg("creation-option", 0, _("Creation option"), pValue)
    2555         628 :                     .AddAlias("co")
    2556         314 :                     .SetMetaVar("<KEY>=<VALUE>");
    2557         322 :     arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
    2558             : 
    2559             :     arg.SetAutoCompleteFunction(
    2560          45 :         [this](const std::string &currentValue)
    2561             :         {
    2562          15 :             std::vector<std::string> oRet;
    2563             : 
    2564          15 :             int datasetType =
    2565             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    2566          15 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    2567          15 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    2568             :             {
    2569          15 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    2570          15 :                 datasetType = datasetValue.GetType();
    2571             :             }
    2572             : 
    2573          15 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    2574          30 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    2575          15 :                 outputFormat->IsExplicitlySet())
    2576             :             {
    2577          12 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    2578           6 :                     outputFormat->Get<std::string>().c_str());
    2579           6 :                 if (poDriver)
    2580             :                 {
    2581           6 :                     AddOptionsSuggestions(
    2582           6 :                         poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    2583             :                         datasetType, currentValue, oRet);
    2584             :                 }
    2585           6 :                 return oRet;
    2586             :             }
    2587             : 
    2588           9 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    2589             :             {
    2590           9 :                 auto poDM = GetGDALDriverManager();
    2591           9 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    2592           9 :                 const auto &osDSName = datasetValue.GetName();
    2593           9 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    2594           9 :                 if (!osExt.empty())
    2595             :                 {
    2596           9 :                     std::set<std::string> oVisitedExtensions;
    2597         515 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    2598             :                     {
    2599         513 :                         auto poDriver = poDM->GetDriver(i);
    2600        1539 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    2601         513 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    2602         152 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    2603        1026 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    2604         152 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    2605           0 :                              poDriver->GetMetadataItem(
    2606           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    2607             :                         {
    2608             :                             const char *pszExtensions =
    2609         361 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    2610         361 :                             if (pszExtensions)
    2611             :                             {
    2612             :                                 const CPLStringList aosExts(
    2613         231 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    2614         507 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    2615             :                                 {
    2616         298 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    2617          13 :                                         !cpl::contains(oVisitedExtensions,
    2618             :                                                        pszExt))
    2619             :                                     {
    2620           9 :                                         oVisitedExtensions.insert(pszExt);
    2621           9 :                                         if (AddOptionsSuggestions(
    2622             :                                                 poDriver->GetMetadataItem(
    2623           9 :                                                     GDAL_DMD_CREATIONOPTIONLIST),
    2624             :                                                 datasetType, currentValue,
    2625             :                                                 oRet))
    2626             :                                         {
    2627           7 :                                             return oRet;
    2628             :                                         }
    2629           2 :                                         break;
    2630             :                                     }
    2631             :                                 }
    2632             :                             }
    2633             :                         }
    2634             :                     }
    2635             :                 }
    2636             :             }
    2637             : 
    2638           2 :             return oRet;
    2639         314 :         });
    2640             : 
    2641         314 :     return arg;
    2642             : }
    2643             : 
    2644             : /************************************************************************/
    2645             : /*                GDALAlgorithm::AddLayerCreationOptionsArg()           */
    2646             : /************************************************************************/
    2647             : 
    2648             : GDALInConstructionAlgorithmArg &
    2649         131 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue)
    2650             : {
    2651             :     auto &arg =
    2652         262 :         AddArg("layer-creation-option", 0, _("Layer creation option"), pValue)
    2653         262 :             .AddAlias("lco")
    2654         131 :             .SetMetaVar("<KEY>=<VALUE>");
    2655         135 :     arg.AddValidationAction([this, &arg]() { return ValidateKeyValue(arg); });
    2656             : 
    2657             :     arg.SetAutoCompleteFunction(
    2658           5 :         [this](const std::string &currentValue)
    2659             :         {
    2660           2 :             std::vector<std::string> oRet;
    2661             : 
    2662           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    2663           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    2664           2 :                 outputFormat->IsExplicitlySet())
    2665             :             {
    2666           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    2667           1 :                     outputFormat->Get<std::string>().c_str());
    2668           1 :                 if (poDriver)
    2669             :                 {
    2670           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    2671           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    2672             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    2673             :                 }
    2674           1 :                 return oRet;
    2675             :             }
    2676             : 
    2677           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    2678           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    2679             :             {
    2680           1 :                 auto poDM = GetGDALDriverManager();
    2681           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    2682           1 :                 const auto &osDSName = datasetValue.GetName();
    2683           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    2684           1 :                 if (!osExt.empty())
    2685             :                 {
    2686           1 :                     std::set<std::string> oVisitedExtensions;
    2687         240 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    2688             :                     {
    2689         239 :                         auto poDriver = poDM->GetDriver(i);
    2690         239 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    2691             :                         {
    2692             :                             const char *pszExtensions =
    2693          93 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    2694          93 :                             if (pszExtensions)
    2695             :                             {
    2696             :                                 const CPLStringList aosExts(
    2697          62 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    2698         157 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    2699             :                                 {
    2700          97 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    2701           1 :                                         !cpl::contains(oVisitedExtensions,
    2702             :                                                        pszExt))
    2703             :                                     {
    2704           1 :                                         oVisitedExtensions.insert(pszExt);
    2705           1 :                                         if (AddOptionsSuggestions(
    2706             :                                                 poDriver->GetMetadataItem(
    2707           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    2708             :                                                 GDAL_OF_VECTOR, currentValue,
    2709             :                                                 oRet))
    2710             :                                         {
    2711           0 :                                             return oRet;
    2712             :                                         }
    2713           1 :                                         break;
    2714             :                                     }
    2715             :                                 }
    2716             :                             }
    2717             :                         }
    2718             :                     }
    2719             :                 }
    2720             :             }
    2721             : 
    2722           1 :             return oRet;
    2723         131 :         });
    2724             : 
    2725         131 :     return arg;
    2726             : }
    2727             : 
    2728             : /************************************************************************/
    2729             : /*                        GDALAlgorithm::AddBBOXArg()                   */
    2730             : /************************************************************************/
    2731             : 
    2732             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    2733             : GDALInConstructionAlgorithmArg &
    2734         103 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    2735             : {
    2736             :     auto &arg = AddArg("bbox", 0,
    2737             :                        helpMessage ? helpMessage
    2738             :                                    : _("Bounding box as xmin,ymin,xmax,ymax"),
    2739         206 :                        pValue)
    2740         103 :                     .SetRepeatedArgAllowed(false)
    2741         103 :                     .SetMinCount(4)
    2742         103 :                     .SetMaxCount(4)
    2743         103 :                     .SetDisplayHintAboutRepetition(false);
    2744             :     arg.AddValidationAction(
    2745          15 :         [&arg]()
    2746             :         {
    2747          15 :             const auto &val = arg.Get<std::vector<double>>();
    2748          15 :             CPLAssert(val.size() == 4);
    2749          15 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    2750             :             {
    2751           4 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2752             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    2753             :                          "xmin <= xmax and ymin <= ymax");
    2754           4 :                 return false;
    2755             :             }
    2756          11 :             return true;
    2757         103 :         });
    2758         103 :     return arg;
    2759             : }
    2760             : 
    2761             : /************************************************************************/
    2762             : /*                  GDALAlgorithm::AddProgressArg()                     */
    2763             : /************************************************************************/
    2764             : 
    2765         251 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddProgressArg()
    2766             : {
    2767             :     return AddArg("progress", 0, _("Display progress bar"),
    2768         502 :                   &m_progressBarRequested)
    2769         251 :         .SetOnlyForCLI()
    2770         502 :         .SetCategory(GAAC_COMMON);
    2771             : }
    2772             : 
    2773             : /************************************************************************/
    2774             : /*                       GDALAlgorithm::Run()                           */
    2775             : /************************************************************************/
    2776             : 
    2777         331 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    2778             : {
    2779         331 :     if (m_selectedSubAlg)
    2780          31 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    2781             : 
    2782         300 :     if (m_helpRequested)
    2783             :     {
    2784           1 :         printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    2785           1 :         return true;
    2786             :     }
    2787             : 
    2788         299 :     if (m_JSONUsageRequested)
    2789             :     {
    2790           3 :         printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    2791           3 :         return true;
    2792             :     }
    2793             : 
    2794         296 :     return ValidateArguments() && RunImpl(pfnProgress, pProgressData);
    2795             : }
    2796             : 
    2797             : /************************************************************************/
    2798             : /*                     GDALAlgorithm::Finalize()                        */
    2799             : /************************************************************************/
    2800             : 
    2801         241 : bool GDALAlgorithm::Finalize()
    2802             : {
    2803         241 :     bool ret = true;
    2804         241 :     if (m_selectedSubAlg)
    2805          29 :         ret = m_selectedSubAlg->Finalize();
    2806             : 
    2807        3273 :     for (auto &arg : m_args)
    2808             :     {
    2809        3032 :         if (arg->GetType() == GAAT_DATASET)
    2810             :         {
    2811         274 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    2812             :         }
    2813        2758 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    2814             :         {
    2815           6 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    2816             :             {
    2817           3 :                 ret = ds.Close() && ret;
    2818             :             }
    2819             :         }
    2820             :     }
    2821         241 :     return ret;
    2822             : }
    2823             : 
    2824             : /************************************************************************/
    2825             : /*                   GDALAlgorithm::GetArgNamesForCLI()                 */
    2826             : /************************************************************************/
    2827             : 
    2828             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    2829          62 : GDALAlgorithm::GetArgNamesForCLI() const
    2830             : {
    2831         124 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    2832             : 
    2833          62 :     size_t maxOptLen = 0;
    2834         537 :     for (const auto &arg : m_args)
    2835             :     {
    2836         475 :         if (arg->IsHiddenForCLI())
    2837          25 :             continue;
    2838         450 :         std::string opt;
    2839         450 :         bool addComma = false;
    2840         450 :         if (!arg->GetShortName().empty())
    2841             :         {
    2842         113 :             opt += '-';
    2843         113 :             opt += arg->GetShortName();
    2844         113 :             addComma = true;
    2845             :         }
    2846         485 :         for (const std::string &alias : arg->GetAliases())
    2847             :         {
    2848          35 :             if (addComma)
    2849          15 :                 opt += ", ";
    2850          35 :             opt += "--";
    2851          35 :             opt += alias;
    2852          35 :             addComma = true;
    2853             :         }
    2854         450 :         if (!arg->GetName().empty())
    2855             :         {
    2856         450 :             if (addComma)
    2857         133 :                 opt += ", ";
    2858         450 :             opt += "--";
    2859         450 :             opt += arg->GetName();
    2860             :         }
    2861         450 :         const auto &metaVar = arg->GetMetaVar();
    2862         450 :         if (!metaVar.empty())
    2863             :         {
    2864         165 :             opt += ' ';
    2865         165 :             if (metaVar.front() != '<')
    2866          89 :                 opt += '<';
    2867         165 :             opt += metaVar;
    2868         165 :             if (metaVar.back() != '>')
    2869          89 :                 opt += '>';
    2870             :         }
    2871         450 :         maxOptLen = std::max(maxOptLen, opt.size());
    2872         450 :         options.emplace_back(arg.get(), opt);
    2873             :     }
    2874             : 
    2875         124 :     return std::make_pair(std::move(options), maxOptLen);
    2876             : }
    2877             : 
    2878             : /************************************************************************/
    2879             : /*                    GDALAlgorithm::GetUsageForCLI()                   */
    2880             : /************************************************************************/
    2881             : 
    2882             : std::string
    2883          63 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    2884             :                               const UsageOptions &usageOptions) const
    2885             : {
    2886          63 :     if (m_selectedSubAlg)
    2887           4 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    2888             : 
    2889         118 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    2890         118 :     std::string osPath;
    2891          83 :     for (const std::string &s : m_callPath)
    2892             :     {
    2893          24 :         if (!osPath.empty())
    2894           3 :             osPath += ' ';
    2895          24 :         osPath += s;
    2896             :     }
    2897          59 :     osRet += ' ';
    2898          59 :     osRet += osPath;
    2899             : 
    2900          59 :     bool hasNonPositionals = false;
    2901         528 :     for (const auto &arg : m_args)
    2902             :     {
    2903         469 :         if (!arg->IsHiddenForCLI() && !arg->IsPositional())
    2904         393 :             hasNonPositionals = true;
    2905             :     }
    2906             : 
    2907          59 :     if (HasSubAlgorithms())
    2908             :     {
    2909           4 :         if (m_callPath.size() == 1)
    2910             :         {
    2911           3 :             osRet += " <COMMAND>";
    2912           3 :             if (hasNonPositionals)
    2913           3 :                 osRet += " [OPTIONS]";
    2914           3 :             osRet += "\nwhere <COMMAND> is one of:\n";
    2915             :         }
    2916             :         else
    2917             :         {
    2918           1 :             osRet += " <SUBCOMMAND>";
    2919           1 :             if (hasNonPositionals)
    2920           1 :                 osRet += " [OPTIONS]";
    2921           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    2922             :         }
    2923           4 :         size_t maxNameLen = 0;
    2924          20 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    2925             :         {
    2926          16 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    2927             :         }
    2928          20 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    2929             :         {
    2930          32 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    2931          16 :             assert(subAlg);
    2932          16 :             const std::string &name(subAlg->GetName());
    2933          16 :             osRet += "  - ";
    2934          16 :             osRet += name;
    2935          16 :             osRet += ": ";
    2936          16 :             osRet.append(maxNameLen - name.size(), ' ');
    2937          16 :             osRet += subAlg->GetDescription();
    2938          16 :             if (!subAlg->m_aliases.empty())
    2939             :             {
    2940           0 :                 bool first = true;
    2941           0 :                 for (const auto &alias : subAlg->GetAliases())
    2942             :                 {
    2943           0 :                     if (alias == GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    2944           0 :                         break;
    2945           0 :                     if (first)
    2946           0 :                         osRet += " (alias: ";
    2947             :                     else
    2948           0 :                         osRet += ", ";
    2949           0 :                     osRet += alias;
    2950           0 :                     first = false;
    2951             :                 }
    2952           0 :                 if (!first)
    2953             :                 {
    2954           0 :                     osRet += ')';
    2955             :                 }
    2956             :             }
    2957          16 :             osRet += '\n';
    2958             :         }
    2959             : 
    2960           4 :         if (shortUsage && hasNonPositionals)
    2961             :         {
    2962           2 :             osRet += "\nTry '";
    2963           2 :             osRet += osPath;
    2964           2 :             osRet += " --help' for help.\n";
    2965             :         }
    2966             :     }
    2967             :     else
    2968             :     {
    2969          55 :         if (!m_args.empty())
    2970             :         {
    2971          55 :             if (hasNonPositionals)
    2972          55 :                 osRet += " [OPTIONS]";
    2973          86 :             for (const auto *arg : m_positionalArgs)
    2974             :             {
    2975          31 :                 osRet += " <";
    2976          31 :                 osRet += arg->GetMetaVar();
    2977          31 :                 osRet += '>';
    2978             :             }
    2979             :         }
    2980             : 
    2981          55 :         const size_t nLenFirstLine = osRet.size();
    2982          55 :         osRet += '\n';
    2983          55 :         if (usageOptions.isPipelineStep)
    2984             :         {
    2985           9 :             osRet.append(nLenFirstLine, '-');
    2986           9 :             osRet += '\n';
    2987             :         }
    2988             : 
    2989          55 :         if (shortUsage)
    2990             :         {
    2991           4 :             osRet += "Try '";
    2992           4 :             osRet += osPath;
    2993           4 :             osRet += " --help' for help.\n";
    2994           4 :             return osRet;
    2995             :         }
    2996             : 
    2997          51 :         osRet += '\n';
    2998          51 :         osRet += m_description;
    2999          51 :         osRet += '\n';
    3000             :     }
    3001             : 
    3002          55 :     if (!m_args.empty() && !shortUsage)
    3003             :     {
    3004         106 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    3005             :         size_t maxOptLen;
    3006          53 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    3007          53 :         if (usageOptions.maxOptLen)
    3008           9 :             maxOptLen = usageOptions.maxOptLen;
    3009             : 
    3010             :         const auto OutputArg =
    3011         324 :             [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
    3012        2149 :                                       const std::string &opt)
    3013             :         {
    3014         324 :             osRet += "  ";
    3015         324 :             osRet += opt;
    3016         324 :             osRet += "  ";
    3017         324 :             osRet.append(maxOptLen - opt.size(), ' ');
    3018         324 :             osRet += arg->GetDescription();
    3019             : 
    3020         324 :             const auto &choices = arg->GetChoices();
    3021         324 :             if (!choices.empty())
    3022             :             {
    3023           5 :                 osRet += ". ";
    3024           5 :                 osRet += arg->GetMetaVar();
    3025           5 :                 osRet += '=';
    3026           5 :                 bool firstChoice = true;
    3027          27 :                 for (const auto &choice : choices)
    3028             :                 {
    3029          22 :                     if (!firstChoice)
    3030          17 :                         osRet += '|';
    3031          22 :                     osRet += choice;
    3032          22 :                     firstChoice = false;
    3033             :                 }
    3034             :             }
    3035             : 
    3036         324 :             if (arg->GetType() == GAAT_DATASET)
    3037             :             {
    3038           8 :                 auto &val = arg->Get<GDALArgDatasetValue>();
    3039           9 :                 if (val.GetInputFlags() == GADV_NAME &&
    3040           1 :                     val.GetOutputFlags() == GADV_OBJECT)
    3041             :                 {
    3042           1 :                     osRet += " (created by algorithm)";
    3043             :                 }
    3044             :             }
    3045             : 
    3046         324 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    3047             :             {
    3048           3 :                 osRet += " (default: ";
    3049           3 :                 osRet += arg->GetDefault<std::string>();
    3050           3 :                 osRet += ')';
    3051             :             }
    3052         321 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    3053             :             {
    3054           5 :                 if (arg->GetDefault<bool>())
    3055           0 :                     osRet += " (default: true)";
    3056             :             }
    3057         316 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    3058             :             {
    3059           1 :                 osRet += " (default: ";
    3060           1 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    3061           1 :                 osRet += ')';
    3062             :             }
    3063         315 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    3064             :             {
    3065           1 :                 osRet += " (default: ";
    3066           1 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    3067           1 :                 osRet += ')';
    3068             :             }
    3069             : 
    3070         324 :             if (arg->GetDisplayHintAboutRepetition())
    3071             :             {
    3072         324 :                 if (arg->GetMinCount() > 0 &&
    3073           6 :                     arg->GetMinCount() == arg->GetMaxCount())
    3074             :                 {
    3075           2 :                     osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    3076             :                 }
    3077         320 :                 else if (arg->GetMinCount() > 0 &&
    3078           4 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    3079             :                 {
    3080             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    3081           4 :                                         arg->GetMaxCount());
    3082             :                 }
    3083         312 :                 else if (arg->GetMinCount() > 0)
    3084             :                 {
    3085           0 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    3086             :                 }
    3087         312 :                 else if (arg->GetMaxCount() > 1)
    3088             :                 {
    3089          66 :                     osRet += " [may be repeated]";
    3090             :                 }
    3091             :             }
    3092             : 
    3093         324 :             if (arg->IsRequired())
    3094             :             {
    3095          14 :                 osRet += " [required]";
    3096             :             }
    3097             : 
    3098         324 :             osRet += '\n';
    3099             : 
    3100         324 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3101         324 :             if (!mutualExclusionGroup.empty())
    3102             :             {
    3103          14 :                 std::string otherArgs;
    3104          97 :                 for (const auto &otherArg : m_args)
    3105             :                 {
    3106          90 :                     if (otherArg->IsHiddenForCLI() || otherArg.get() == arg)
    3107          11 :                         continue;
    3108          79 :                     if (otherArg->GetMutualExclusionGroup() ==
    3109             :                         mutualExclusionGroup)
    3110             :                     {
    3111          10 :                         if (!otherArgs.empty())
    3112           3 :                             otherArgs += ", ";
    3113          10 :                         otherArgs += "--";
    3114          10 :                         otherArgs += otherArg->GetName();
    3115             :                     }
    3116             :                 }
    3117           7 :                 if (!otherArgs.empty())
    3118             :                 {
    3119           7 :                     osRet += "  ";
    3120           7 :                     osRet += "  ";
    3121           7 :                     osRet.append(maxOptLen, ' ');
    3122           7 :                     osRet += "Mutually exclusive with ";
    3123           7 :                     osRet += otherArgs;
    3124           7 :                     osRet += '\n';
    3125             :                 }
    3126             :             }
    3127         324 :         };
    3128             : 
    3129          53 :         if (!m_positionalArgs.empty())
    3130             :         {
    3131          19 :             osRet += "\nPositional arguments:\n";
    3132         176 :             for (const auto &[arg, opt] : options)
    3133             :             {
    3134         157 :                 if (arg->IsPositional())
    3135          26 :                     OutputArg(arg, opt);
    3136             :             }
    3137             :         }
    3138             : 
    3139          53 :         if (hasNonPositionals)
    3140             :         {
    3141          53 :             bool hasCommon = false;
    3142          53 :             bool hasBase = false;
    3143          53 :             bool hasAdvanced = false;
    3144          53 :             bool hasEsoteric = false;
    3145         106 :             std::vector<std::string> categories;
    3146         422 :             for (const auto &iter : options)
    3147             :             {
    3148         369 :                 const auto &arg = iter.first;
    3149         369 :                 if (!arg->IsPositional())
    3150             :                 {
    3151         343 :                     const auto &category = arg->GetCategory();
    3152         343 :                     if (category == GAAC_COMMON)
    3153             :                     {
    3154         271 :                         hasCommon = true;
    3155             :                     }
    3156          72 :                     else if (category == GAAC_BASE)
    3157             :                     {
    3158          54 :                         hasBase = true;
    3159             :                     }
    3160          18 :                     else if (category == GAAC_ADVANCED)
    3161             :                     {
    3162          14 :                         hasAdvanced = true;
    3163             :                     }
    3164           4 :                     else if (category == GAAC_ESOTERIC)
    3165             :                     {
    3166           3 :                         hasEsoteric = true;
    3167             :                     }
    3168           1 :                     else if (std::find(categories.begin(), categories.end(),
    3169           1 :                                        category) == categories.end())
    3170             :                     {
    3171           1 :                         categories.push_back(category);
    3172             :                     }
    3173             :                 }
    3174             :             }
    3175          53 :             if (hasAdvanced)
    3176           4 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    3177          53 :             if (hasBase)
    3178          28 :                 categories.insert(categories.begin(), GAAC_BASE);
    3179          53 :             if (hasCommon && !usageOptions.isPipelineStep)
    3180          44 :                 categories.insert(categories.begin(), GAAC_COMMON);
    3181          53 :             if (hasEsoteric)
    3182           1 :                 categories.push_back(GAAC_ESOTERIC);
    3183             : 
    3184         131 :             for (const auto &category : categories)
    3185             :             {
    3186          78 :                 osRet += "\n";
    3187          78 :                 if (category != GAAC_BASE)
    3188             :                 {
    3189          50 :                     osRet += category;
    3190          50 :                     osRet += ' ';
    3191             :                 }
    3192          78 :                 osRet += "Options:\n";
    3193         654 :                 for (const auto &[arg, opt] : options)
    3194             :                 {
    3195         576 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    3196         298 :                         OutputArg(arg, opt);
    3197             :                 }
    3198             :             }
    3199             :         }
    3200             :     }
    3201             : 
    3202          55 :     if (!m_longDescription.empty())
    3203             :     {
    3204           5 :         osRet += '\n';
    3205           5 :         osRet += m_longDescription;
    3206           5 :         osRet += '\n';
    3207             :     }
    3208             : 
    3209          55 :     if (!m_helpURL.empty())
    3210             :     {
    3211          55 :         osRet += "\nFor more details, consult ";
    3212          55 :         osRet += GetHelpFullURL();
    3213          55 :         osRet += '\n';
    3214             :     }
    3215             : 
    3216          55 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    3217             :     {
    3218             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    3219             :                  "alternative interface to GDAL and OGR command line "
    3220             :                  "utilities.\nThe project reserves the right to modify, "
    3221             :                  "rename, reorganize, and change the behavior of the utility\n"
    3222             :                  "until it is officially frozen in a future feature release of "
    3223           0 :                  "GDAL.\n";
    3224             :     }
    3225             : 
    3226          55 :     return osRet;
    3227             : }
    3228             : 
    3229             : /************************************************************************/
    3230             : /*                    GDALAlgorithm::GetUsageAsJSON()                   */
    3231             : /************************************************************************/
    3232             : 
    3233          72 : std::string GDALAlgorithm::GetUsageAsJSON() const
    3234             : {
    3235         144 :     CPLJSONDocument oDoc;
    3236         144 :     auto oRoot = oDoc.GetRoot();
    3237             : 
    3238          72 :     if (m_displayInJSONUsage)
    3239             :     {
    3240          70 :         oRoot.Add("name", m_name);
    3241          70 :         CPLJSONArray jFullPath;
    3242         142 :         for (const std::string &s : m_callPath)
    3243             :         {
    3244          72 :             jFullPath.Add(s);
    3245             :         }
    3246          70 :         oRoot.Add("full_path", jFullPath);
    3247             :     }
    3248             : 
    3249          72 :     oRoot.Add("description", m_description);
    3250          72 :     if (!m_helpURL.empty())
    3251             :     {
    3252          72 :         oRoot.Add("short_url", m_helpURL);
    3253          72 :         oRoot.Add("url", GetHelpFullURL());
    3254             :     }
    3255             : 
    3256         144 :     CPLJSONArray jSubAlgorithms;
    3257         102 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    3258             :     {
    3259          60 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    3260          30 :         assert(subAlg);
    3261          30 :         if (subAlg->m_displayInJSONUsage)
    3262             :         {
    3263          27 :             CPLJSONDocument oSubDoc;
    3264          27 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    3265          27 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    3266             :         }
    3267             :     }
    3268          72 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    3269             : 
    3270         433 :     const auto ProcessArg = [](const GDALAlgorithmArg *arg)
    3271             :     {
    3272         433 :         CPLJSONObject jArg;
    3273         433 :         jArg.Add("name", arg->GetName());
    3274         433 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    3275         433 :         jArg.Add("description", arg->GetDescription());
    3276         433 :         const auto &choices = arg->GetChoices();
    3277         433 :         if (!choices.empty())
    3278             :         {
    3279          11 :             CPLJSONArray jChoices;
    3280         121 :             for (const auto &choice : choices)
    3281         110 :                 jChoices.Add(choice);
    3282          11 :             jArg.Add("choices", jChoices);
    3283             :         }
    3284         433 :         if (arg->HasDefaultValue())
    3285             :         {
    3286          63 :             switch (arg->GetType())
    3287             :             {
    3288          57 :                 case GAAT_BOOLEAN:
    3289          57 :                     jArg.Add("default", arg->GetDefault<bool>());
    3290          57 :                     break;
    3291           4 :                 case GAAT_STRING:
    3292           4 :                     jArg.Add("default", arg->GetDefault<std::string>());
    3293           4 :                     break;
    3294           1 :                 case GAAT_INTEGER:
    3295           1 :                     jArg.Add("default", arg->GetDefault<int>());
    3296           1 :                     break;
    3297           1 :                 case GAAT_REAL:
    3298           1 :                     jArg.Add("default", arg->GetDefault<double>());
    3299           1 :                     break;
    3300           0 :                 case GAAT_DATASET:
    3301             :                 case GAAT_STRING_LIST:
    3302             :                 case GAAT_INTEGER_LIST:
    3303             :                 case GAAT_REAL_LIST:
    3304             :                 case GAAT_DATASET_LIST:
    3305           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3306             :                              "Unhandled default value for arg %s",
    3307           0 :                              arg->GetName().c_str());
    3308           0 :                     break;
    3309             :             }
    3310             :         }
    3311         433 :         jArg.Add("required", arg->IsRequired());
    3312         433 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    3313             :         {
    3314         158 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    3315         158 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    3316         158 :             jArg.Add("min_count", arg->GetMinCount());
    3317         158 :             jArg.Add("max_count", arg->GetMaxCount());
    3318             :         }
    3319         433 :         jArg.Add("category", arg->GetCategory());
    3320             : 
    3321         433 :         if (arg->GetType() == GAAT_DATASET)
    3322             :         {
    3323          67 :             const auto &val = arg->Get<GDALArgDatasetValue>();
    3324             :             {
    3325          67 :                 CPLJSONArray jAr;
    3326          67 :                 if (val.GetType() & GDAL_OF_RASTER)
    3327          48 :                     jAr.Add("raster");
    3328          67 :                 if (val.GetType() & GDAL_OF_VECTOR)
    3329          21 :                     jAr.Add("vector");
    3330          67 :                 if (val.GetType() & GDAL_OF_MULTIDIM_RASTER)
    3331           2 :                     jAr.Add("muldim_raster");
    3332          67 :                 jArg.Add("dataset_type", jAr);
    3333             :             }
    3334             : 
    3335          93 :             const auto GetFlags = [](int flags)
    3336             :             {
    3337          93 :                 CPLJSONArray jAr;
    3338          93 :                 if (flags & GADV_NAME)
    3339          67 :                     jAr.Add("name");
    3340          93 :                 if (flags & GADV_OBJECT)
    3341          88 :                     jAr.Add("dataset");
    3342          93 :                 return jAr;
    3343             :             };
    3344             : 
    3345          67 :             if (arg->IsInput())
    3346             :             {
    3347          67 :                 jArg.Add("input_flags", GetFlags(val.GetInputFlags()));
    3348             :             }
    3349          67 :             if (arg->IsOutput())
    3350             :             {
    3351          26 :                 jArg.Add("output_flags", GetFlags(val.GetOutputFlags()));
    3352             :             }
    3353             :         }
    3354             : 
    3355         433 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3356         433 :         if (!mutualExclusionGroup.empty())
    3357             :         {
    3358          24 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    3359             :         }
    3360             : 
    3361         866 :         const auto &metadata = arg->GetMetadata();
    3362         433 :         if (!metadata.empty())
    3363             :         {
    3364          51 :             CPLJSONObject jMetadata;
    3365         102 :             for (const auto &[key, values] : metadata)
    3366             :             {
    3367         102 :                 CPLJSONArray jValue;
    3368         126 :                 for (const auto &value : values)
    3369          75 :                     jValue.Add(value);
    3370          51 :                 jMetadata.Add(key, jValue);
    3371             :             }
    3372          51 :             jArg.Add("metadata", jMetadata);
    3373             :         }
    3374             : 
    3375         866 :         return jArg;
    3376             :     };
    3377             : 
    3378             :     {
    3379          72 :         CPLJSONArray jArgs;
    3380         888 :         for (const auto &arg : m_args)
    3381             :         {
    3382         816 :             if (!arg->IsOnlyForCLI() && arg->IsInput() && !arg->IsOutput())
    3383         404 :                 jArgs.Add(ProcessArg(arg.get()));
    3384             :         }
    3385          72 :         oRoot.Add("input_arguments", jArgs);
    3386             :     }
    3387             : 
    3388             :     {
    3389          72 :         CPLJSONArray jArgs;
    3390         888 :         for (const auto &arg : m_args)
    3391             :         {
    3392         816 :             if (!arg->IsOnlyForCLI() && !arg->IsInput() && arg->IsOutput())
    3393           3 :                 jArgs.Add(ProcessArg(arg.get()));
    3394             :         }
    3395          72 :         oRoot.Add("output_arguments", jArgs);
    3396             :     }
    3397             : 
    3398             :     {
    3399          72 :         CPLJSONArray jArgs;
    3400         888 :         for (const auto &arg : m_args)
    3401             :         {
    3402         816 :             if (!arg->IsOnlyForCLI() && arg->IsInput() && arg->IsOutput())
    3403          26 :                 jArgs.Add(ProcessArg(arg.get()));
    3404             :         }
    3405          72 :         oRoot.Add("input_output_arguments", jArgs);
    3406             :     }
    3407             : 
    3408         144 :     return oDoc.SaveAsString();
    3409             : }
    3410             : 
    3411             : /************************************************************************/
    3412             : /*                    GDALAlgorithm::GetAutoComplete()                  */
    3413             : /************************************************************************/
    3414             : 
    3415             : std::vector<std::string>
    3416          43 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    3417             :                                bool showAllOptions)
    3418             : {
    3419          43 :     std::vector<std::string> ret;
    3420             : 
    3421          86 :     std::string option;
    3422          86 :     std::string value;
    3423          43 :     ExtractLastOptionAndValue(args, option, value);
    3424             : 
    3425          51 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    3426           8 :         args.back()[0] == '-')
    3427             :     {
    3428             :         // List available options
    3429          88 :         for (const auto &arg : GetArgs())
    3430             :         {
    3431         160 :             if (arg->IsHiddenForCLI() ||
    3432         153 :                 (!showAllOptions &&
    3433         204 :                  (arg->GetName() == "help" || arg->GetName() == "drivers" ||
    3434         168 :                   arg->GetName() == "config" || arg->GetName() == "version" ||
    3435          50 :                   arg->GetName() == "json-usage")))
    3436             :             {
    3437          32 :                 continue;
    3438             :             }
    3439          49 :             if (!arg->GetShortName().empty())
    3440             :             {
    3441          14 :                 ret.push_back(std::string("-").append(arg->GetShortName()));
    3442             :             }
    3443          66 :             for (const std::string &alias : arg->GetAliases())
    3444             :             {
    3445          17 :                 ret.push_back(std::string("--").append(alias));
    3446             :             }
    3447          49 :             if (!arg->GetName().empty())
    3448             :             {
    3449          49 :                 ret.push_back(std::string("--").append(arg->GetName()));
    3450             :             }
    3451             :         }
    3452             :     }
    3453          36 :     else if (!option.empty())
    3454             :     {
    3455             :         // List possible choices for current option
    3456          33 :         auto arg = GetArg(option);
    3457          33 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    3458             :         {
    3459          33 :             ret = arg->GetChoices();
    3460          33 :             if (ret.empty())
    3461             :             {
    3462             :                 {
    3463          29 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    3464          29 :                     SetParseForAutoCompletion();
    3465          29 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    3466             :                 }
    3467          29 :                 ret = arg->GetAutoCompleteChoices(value);
    3468             :             }
    3469          33 :             if (ret.empty())
    3470             :             {
    3471           1 :                 ret.push_back("**");
    3472           1 :                 ret.push_back(
    3473           1 :                     std::string("description: ").append(arg->GetDescription()));
    3474             :             }
    3475             :         }
    3476             :     }
    3477           3 :     else if (!args.empty() && STARTS_WITH(args.back().c_str(), "/vsi"))
    3478             :     {
    3479           1 :         auto arg = GetArg(GDAL_ARG_NAME_INPUT);
    3480           1 :         if (arg)
    3481             :         {
    3482           1 :             ret = arg->GetAutoCompleteChoices(args.back());
    3483             :         }
    3484             :     }
    3485             :     else
    3486             :     {
    3487             :         // List possible sub-algorithms
    3488           2 :         ret = GetSubAlgorithmNames();
    3489             :     }
    3490             : 
    3491          86 :     return ret;
    3492             : }
    3493             : 
    3494             : /************************************************************************/
    3495             : /*             GDALAlgorithm::ExtractLastOptionAndValue()               */
    3496             : /************************************************************************/
    3497             : 
    3498          43 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    3499             :                                               std::string &option,
    3500             :                                               std::string &value) const
    3501             : {
    3502          43 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    3503             :     {
    3504          28 :         const auto nPosEqual = args.back().find('=');
    3505          28 :         if (nPosEqual == std::string::npos)
    3506             :         {
    3507             :             // Deal with "gdal ... --option"
    3508          17 :             if (GetArg(args.back()))
    3509             :             {
    3510          10 :                 option = args.back();
    3511          10 :                 args.pop_back();
    3512             :             }
    3513             :         }
    3514             :         else
    3515             :         {
    3516             :             // Deal with "gdal ... --option=<value>"
    3517          11 :             if (GetArg(args.back().substr(0, nPosEqual)))
    3518             :             {
    3519          11 :                 option = args.back().substr(0, nPosEqual);
    3520          11 :                 value = args.back().substr(nPosEqual + 1);
    3521          11 :                 args.pop_back();
    3522             :             }
    3523             :         }
    3524             :     }
    3525          27 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    3526          12 :              args[args.size() - 2][0] == '-')
    3527             :     {
    3528             :         // Deal with "gdal ... --option <value>"
    3529          12 :         auto arg = GetArg(args[args.size() - 2]);
    3530          12 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    3531             :         {
    3532          12 :             option = args[args.size() - 2];
    3533          12 :             value = args.back();
    3534          12 :             args.pop_back();
    3535             :         }
    3536             :     }
    3537             : 
    3538          43 :     const auto IsKeyValueOption = [](const std::string &osStr)
    3539             :     {
    3540         100 :         return osStr == "--co" || osStr == "--creation-option" ||
    3541          81 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    3542          98 :                osStr == "--oo" || osStr == "--open-option";
    3543             :     };
    3544             : 
    3545          43 :     if (IsKeyValueOption(option))
    3546             :     {
    3547          19 :         const auto nPosEqual = value.find('=');
    3548          19 :         if (nPosEqual != std::string::npos)
    3549             :         {
    3550          10 :             value.resize(nPosEqual);
    3551             :         }
    3552             :     }
    3553          43 : }
    3554             : 
    3555             : /************************************************************************/
    3556             : /*                        GDALAlgorithmRelease()                        */
    3557             : /************************************************************************/
    3558             : 
    3559             : /** Release a handle to an algorithm.
    3560             :  *
    3561             :  * @since 3.11
    3562             :  */
    3563         367 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    3564             : {
    3565         367 :     delete hAlg;
    3566         367 : }
    3567             : 
    3568             : /************************************************************************/
    3569             : /*                        GDALAlgorithmGetName()                        */
    3570             : /************************************************************************/
    3571             : 
    3572             : /** Return the algorithm name.
    3573             :  *
    3574             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3575             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    3576             :  * be freed.
    3577             :  * @since 3.11
    3578             :  */
    3579           2 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    3580             : {
    3581           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3582           2 :     return hAlg->ptr->GetName().c_str();
    3583             : }
    3584             : 
    3585             : /************************************************************************/
    3586             : /*                     GDALAlgorithmGetDescription()                    */
    3587             : /************************************************************************/
    3588             : 
    3589             : /** Return the algorithm (short) description.
    3590             :  *
    3591             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3592             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    3593             :  * not be freed.
    3594             :  * @since 3.11
    3595             :  */
    3596           2 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    3597             : {
    3598           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3599           2 :     return hAlg->ptr->GetDescription().c_str();
    3600             : }
    3601             : 
    3602             : /************************************************************************/
    3603             : /*                     GDALAlgorithmGetLongDescription()                */
    3604             : /************************************************************************/
    3605             : 
    3606             : /** Return the algorithm (longer) description.
    3607             :  *
    3608             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3609             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    3610             :  * not be freed.
    3611             :  * @since 3.11
    3612             :  */
    3613           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    3614             : {
    3615           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3616           2 :     return hAlg->ptr->GetLongDescription().c_str();
    3617             : }
    3618             : 
    3619             : /************************************************************************/
    3620             : /*                     GDALAlgorithmGetHelpFullURL()                    */
    3621             : /************************************************************************/
    3622             : 
    3623             : /** Return the algorithm full URL.
    3624             :  *
    3625             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3626             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    3627             :  * not be freed.
    3628             :  * @since 3.11
    3629             :  */
    3630           2 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    3631             : {
    3632           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3633           2 :     return hAlg->ptr->GetHelpFullURL().c_str();
    3634             : }
    3635             : 
    3636             : /************************************************************************/
    3637             : /*                     GDALAlgorithmHasSubAlgorithms()                  */
    3638             : /************************************************************************/
    3639             : 
    3640             : /** Return whether the algorithm has sub-algorithms.
    3641             :  *
    3642             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3643             :  * @since 3.11
    3644             :  */
    3645           2 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    3646             : {
    3647           2 :     VALIDATE_POINTER1(hAlg, __func__, false);
    3648           2 :     return hAlg->ptr->HasSubAlgorithms();
    3649             : }
    3650             : 
    3651             : /************************************************************************/
    3652             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    3653             : /************************************************************************/
    3654             : 
    3655             : /** Get the names of registered algorithms.
    3656             :  *
    3657             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3658             :  * @return a NULL terminated list of names, which must be destroyed with
    3659             :  * CSLDestroy()
    3660             :  * @since 3.11
    3661             :  */
    3662           2 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    3663             : {
    3664           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3665           2 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    3666             : }
    3667             : 
    3668             : /************************************************************************/
    3669             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    3670             : /************************************************************************/
    3671             : 
    3672             : /** Instantiate an algorithm by its name (or its alias).
    3673             :  *
    3674             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3675             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    3676             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    3677             :  * or NULL if the algorithm does not exist or another error occurred.
    3678             :  * @since 3.11
    3679             :  */
    3680         176 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    3681             :                                                     const char *pszSubAlgName)
    3682             : {
    3683         176 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3684         176 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    3685         352 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    3686             :     return subAlg
    3687         352 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    3688         352 :                : nullptr;
    3689             : }
    3690             : 
    3691             : /************************************************************************/
    3692             : /*                GDALAlgorithmParseCommandLineArguments()              */
    3693             : /************************************************************************/
    3694             : 
    3695             : /** Parse a command line argument, which does not include the algorithm
    3696             :  * name, to set the value of corresponding arguments.
    3697             :  *
    3698             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3699             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    3700             :  * @return true if successful, false otherwise
    3701             :  * @since 3.11
    3702             :  */
    3703             : 
    3704         140 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    3705             :                                             CSLConstList papszArgs)
    3706             : {
    3707         140 :     VALIDATE_POINTER1(hAlg, __func__, false);
    3708         140 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    3709             : }
    3710             : 
    3711             : /************************************************************************/
    3712             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    3713             : /************************************************************************/
    3714             : 
    3715             : /** Return the actual algorithm that is going to be invoked, when the
    3716             :  * current algorithm has sub-algorithms.
    3717             :  *
    3718             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    3719             :  *
    3720             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    3721             :  * the hAlg instance that owns it.
    3722             :  *
    3723             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3724             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    3725             :  * @since 3.11
    3726             :  */
    3727           8 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    3728             : {
    3729           8 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3730           8 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    3731             : }
    3732             : 
    3733             : /************************************************************************/
    3734             : /*                          GDALAlgorithmRun()                          */
    3735             : /************************************************************************/
    3736             : 
    3737             : /** Execute the algorithm, starting with ValidateArguments() and then
    3738             :  * calling RunImpl().
    3739             :  *
    3740             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3741             :  * @param pfnProgress Progress callback. May be null.
    3742             :  * @param pProgressData Progress callback user data. May be null.
    3743             :  * @return true if successful, false otherwise
    3744             :  * @since 3.11
    3745             :  */
    3746             : 
    3747         151 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    3748             :                       void *pProgressData)
    3749             : {
    3750         151 :     VALIDATE_POINTER1(hAlg, __func__, false);
    3751         151 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    3752             : }
    3753             : 
    3754             : /************************************************************************/
    3755             : /*                       GDALAlgorithmFinalize()                        */
    3756             : /************************************************************************/
    3757             : 
    3758             : /** Complete any pending actions, and return the final status.
    3759             :  * This is typically useful for algorithm that generate an output dataset.
    3760             :  *
    3761             :  * Note that this function does *NOT* release memory associated with the
    3762             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    3763             :  *
    3764             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3765             :  * @return true if successful, false otherwise
    3766             :  * @since 3.11
    3767             :  */
    3768             : 
    3769          95 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    3770             : {
    3771          95 :     VALIDATE_POINTER1(hAlg, __func__, false);
    3772          95 :     return hAlg->ptr->Finalize();
    3773             : }
    3774             : 
    3775             : /************************************************************************/
    3776             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    3777             : /************************************************************************/
    3778             : 
    3779             : /** Return the usage of the algorithm as a JSON-serialized string.
    3780             :  *
    3781             :  * This can be used to dynamically generate interfaces to algorithms.
    3782             :  *
    3783             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3784             :  * @return a string that must be freed with CPLFree()
    3785             :  * @since 3.11
    3786             :  */
    3787           4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    3788             : {
    3789           4 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3790           4 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    3791             : }
    3792             : 
    3793             : /************************************************************************/
    3794             : /*                      GDALAlgorithmGetArgNames()                      */
    3795             : /************************************************************************/
    3796             : 
    3797             : /** Return the list of available argument names.
    3798             :  *
    3799             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3800             :  * @return a NULL terminated list of names, which must be destroyed with
    3801             :  * CSLDestroy()
    3802             :  * @since 3.11
    3803             :  */
    3804           2 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    3805             : {
    3806           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3807           4 :     CPLStringList list;
    3808          29 :     for (const auto &arg : hAlg->ptr->GetArgs())
    3809          27 :         list.AddString(arg->GetName().c_str());
    3810           2 :     return list.StealList();
    3811             : }
    3812             : 
    3813             : /************************************************************************/
    3814             : /*                        GDALAlgorithmGetArg()                         */
    3815             : /************************************************************************/
    3816             : 
    3817             : /** Return an argument from its name.
    3818             :  *
    3819             :  * The lifetime of the returned object does not exceed the one of hAlg.
    3820             :  *
    3821             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    3822             :  * @param pszArgName Argument name. Must NOT be null.
    3823             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    3824             :  * or nullptr in case of error
    3825             :  * @since 3.11
    3826             :  */
    3827         154 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    3828             :                                       const char *pszArgName)
    3829             : {
    3830         154 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    3831         154 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    3832         154 :     auto arg = hAlg->ptr->GetArg(pszArgName);
    3833         154 :     if (!arg)
    3834           2 :         return nullptr;
    3835         152 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    3836             : }
    3837             : 
    3838             : /************************************************************************/
    3839             : /*                       GDALAlgorithmArgRelease()                      */
    3840             : /************************************************************************/
    3841             : 
    3842             : /** Release a handle to an argument.
    3843             :  *
    3844             :  * @since 3.11
    3845             :  */
    3846         152 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    3847             : {
    3848         152 :     delete hArg;
    3849         152 : }
    3850             : 
    3851             : /************************************************************************/
    3852             : /*                      GDALAlgorithmArgGetName()                       */
    3853             : /************************************************************************/
    3854             : 
    3855             : /** Return the name of an argument.
    3856             :  *
    3857             :  * @param hArg Handle to an argument. Must NOT be null.
    3858             :  * @return argument name whose lifetime is bound to hArg and which must not
    3859             :  * be freed.
    3860             :  * @since 3.11
    3861             :  */
    3862           1 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    3863             : {
    3864           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3865           1 :     return hArg->ptr->GetName().c_str();
    3866             : }
    3867             : 
    3868             : /************************************************************************/
    3869             : /*                       GDALAlgorithmArgGetType()                      */
    3870             : /************************************************************************/
    3871             : 
    3872             : /** Get the type of an argument
    3873             :  *
    3874             :  * @param hArg Handle to an argument. Must NOT be null.
    3875             :  * @since 3.11
    3876             :  */
    3877         136 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    3878             : {
    3879         136 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    3880         136 :     return hArg->ptr->GetType();
    3881             : }
    3882             : 
    3883             : /************************************************************************/
    3884             : /*                   GDALAlgorithmArgGetDescription()                   */
    3885             : /************************************************************************/
    3886             : 
    3887             : /** Return the description of an argument.
    3888             :  *
    3889             :  * @param hArg Handle to an argument. Must NOT be null.
    3890             :  * @return argument descriptioin whose lifetime is bound to hArg and which must not
    3891             :  * be freed.
    3892             :  * @since 3.11
    3893             :  */
    3894           1 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    3895             : {
    3896           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3897           1 :     return hArg->ptr->GetDescription().c_str();
    3898             : }
    3899             : 
    3900             : /************************************************************************/
    3901             : /*                   GDALAlgorithmArgGetShortName()                     */
    3902             : /************************************************************************/
    3903             : 
    3904             : /** Return the short name, or empty string if there is none
    3905             :  *
    3906             :  * @param hArg Handle to an argument. Must NOT be null.
    3907             :  * @return short name whose lifetime is bound to hArg and which must not
    3908             :  * be freed.
    3909             :  * @since 3.11
    3910             :  */
    3911           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    3912             : {
    3913           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3914           1 :     return hArg->ptr->GetShortName().c_str();
    3915             : }
    3916             : 
    3917             : /************************************************************************/
    3918             : /*                    GDALAlgorithmArgGetAliases()                      */
    3919             : /************************************************************************/
    3920             : 
    3921             : /** Return the aliases (potentially none)
    3922             :  *
    3923             :  * @param hArg Handle to an argument. Must NOT be null.
    3924             :  * @return a NULL terminated list of names, which must be destroyed with
    3925             :  * CSLDestroy()
    3926             : 
    3927             :  * @since 3.11
    3928             :  */
    3929           1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    3930             : {
    3931           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3932           1 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    3933             : }
    3934             : 
    3935             : /************************************************************************/
    3936             : /*                    GDALAlgorithmArgGetMetaVar()                      */
    3937             : /************************************************************************/
    3938             : 
    3939             : /** Return the "meta-var" hint.
    3940             :  *
    3941             :  * By default, the meta-var value is the long name of the argument in
    3942             :  * upper case.
    3943             :  *
    3944             :  * @param hArg Handle to an argument. Must NOT be null.
    3945             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    3946             :  * be freed.
    3947             :  * @since 3.11
    3948             :  */
    3949           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    3950             : {
    3951           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3952           1 :     return hArg->ptr->GetMetaVar().c_str();
    3953             : }
    3954             : 
    3955             : /************************************************************************/
    3956             : /*                   GDALAlgorithmArgGetCategory()                      */
    3957             : /************************************************************************/
    3958             : 
    3959             : /** Return the argument category
    3960             :  *
    3961             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    3962             :  *
    3963             :  * @param hArg Handle to an argument. Must NOT be null.
    3964             :  * @return category whose lifetime is bound to hArg and which must not
    3965             :  * be freed.
    3966             :  * @since 3.11
    3967             :  */
    3968           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    3969             : {
    3970           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    3971           1 :     return hArg->ptr->GetCategory().c_str();
    3972             : }
    3973             : 
    3974             : /************************************************************************/
    3975             : /*                   GDALAlgorithmArgIsPositional()                     */
    3976             : /************************************************************************/
    3977             : 
    3978             : /** Return if the argument is a positional one.
    3979             :  *
    3980             :  * @param hArg Handle to an argument. Must NOT be null.
    3981             :  * @since 3.11
    3982             :  */
    3983           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    3984             : {
    3985           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    3986           1 :     return hArg->ptr->IsPositional();
    3987             : }
    3988             : 
    3989             : /************************************************************************/
    3990             : /*                   GDALAlgorithmArgIsRequired()                       */
    3991             : /************************************************************************/
    3992             : 
    3993             : /** Return whether the argument is required. Defaults to false.
    3994             :  *
    3995             :  * @param hArg Handle to an argument. Must NOT be null.
    3996             :  * @since 3.11
    3997             :  */
    3998           1 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    3999             : {
    4000           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4001           1 :     return hArg->ptr->IsRequired();
    4002             : }
    4003             : 
    4004             : /************************************************************************/
    4005             : /*                   GDALAlgorithmArgGetMinCount()                      */
    4006             : /************************************************************************/
    4007             : 
    4008             : /** Return the minimum number of values for the argument.
    4009             :  *
    4010             :  * Defaults to 0.
    4011             :  * Only applies to list type of arguments.
    4012             :  *
    4013             :  * @param hArg Handle to an argument. Must NOT be null.
    4014             :  * @since 3.11
    4015             :  */
    4016           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    4017             : {
    4018           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    4019           1 :     return hArg->ptr->GetMinCount();
    4020             : }
    4021             : 
    4022             : /************************************************************************/
    4023             : /*                   GDALAlgorithmArgGetMaxCount()                      */
    4024             : /************************************************************************/
    4025             : 
    4026             : /** Return the maximum number of values for the argument.
    4027             :  *
    4028             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    4029             :  * Only applies to list type of arguments.
    4030             :  *
    4031             :  * @param hArg Handle to an argument. Must NOT be null.
    4032             :  * @since 3.11
    4033             :  */
    4034           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    4035             : {
    4036           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    4037           1 :     return hArg->ptr->GetMaxCount();
    4038             : }
    4039             : 
    4040             : /************************************************************************/
    4041             : /*                GDALAlgorithmArgGetPackedValuesAllowed()              */
    4042             : /************************************************************************/
    4043             : 
    4044             : /** Return whether, for list type of arguments, several values, space
    4045             :  * separated, may be specified. That is "--foo=bar,baz".
    4046             :  * The default is true.
    4047             :  *
    4048             :  * @param hArg Handle to an argument. Must NOT be null.
    4049             :  * @since 3.11
    4050             :  */
    4051           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    4052             : {
    4053           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4054           1 :     return hArg->ptr->GetPackedValuesAllowed();
    4055             : }
    4056             : 
    4057             : /************************************************************************/
    4058             : /*                GDALAlgorithmArgGetRepeatedArgAllowed()               */
    4059             : /************************************************************************/
    4060             : 
    4061             : /** Return whether, for list type of arguments, the argument may be
    4062             :  * repeated. That is "--foo=bar --foo=baz".
    4063             :  * The default is true.
    4064             :  *
    4065             :  * @param hArg Handle to an argument. Must NOT be null.
    4066             :  * @since 3.11
    4067             :  */
    4068           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    4069             : {
    4070           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4071           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    4072             : }
    4073             : 
    4074             : /************************************************************************/
    4075             : /*                    GDALAlgorithmArgGetChoices()                      */
    4076             : /************************************************************************/
    4077             : 
    4078             : /** Return the allowed values (as strings) for the argument.
    4079             :  *
    4080             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    4081             :  *
    4082             :  * @param hArg Handle to an argument. Must NOT be null.
    4083             :  * @return a NULL terminated list of names, which must be destroyed with
    4084             :  * CSLDestroy()
    4085             : 
    4086             :  * @since 3.11
    4087             :  */
    4088           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    4089             : {
    4090           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4091           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    4092             : }
    4093             : 
    4094             : /************************************************************************/
    4095             : /*                   GDALAlgorithmArgIsExplicitlySet()                  */
    4096             : /************************************************************************/
    4097             : 
    4098             : /** Return whether the argument value has been explicitly set with Set()
    4099             :  *
    4100             :  * @param hArg Handle to an argument. Must NOT be null.
    4101             :  * @since 3.11
    4102             :  */
    4103           1 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    4104             : {
    4105           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4106           1 :     return hArg->ptr->IsExplicitlySet();
    4107             : }
    4108             : 
    4109             : /************************************************************************/
    4110             : /*                   GDALAlgorithmArgHasDefaultValue()                  */
    4111             : /************************************************************************/
    4112             : 
    4113             : /** Return if the argument has a declared default value.
    4114             :  *
    4115             :  * @param hArg Handle to an argument. Must NOT be null.
    4116             :  * @since 3.11
    4117             :  */
    4118           1 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    4119             : {
    4120           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4121           1 :     return hArg->ptr->HasDefaultValue();
    4122             : }
    4123             : 
    4124             : /************************************************************************/
    4125             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    4126             : /************************************************************************/
    4127             : 
    4128             : /** Return whether the argument must not be mentioned in CLI usage.
    4129             :  *
    4130             :  * For example, "output-value" for "gdal raster info", which is only
    4131             :  * meant when the algorithm is used from a non-CLI context.
    4132             :  *
    4133             :  * @param hArg Handle to an argument. Must NOT be null.
    4134             :  * @since 3.11
    4135             :  */
    4136           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    4137             : {
    4138           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4139           1 :     return hArg->ptr->IsHiddenForCLI();
    4140             : }
    4141             : 
    4142             : /************************************************************************/
    4143             : /*                   GDALAlgorithmArgIsOnlyForCLI()                     */
    4144             : /************************************************************************/
    4145             : 
    4146             : /** Return whether the argument is only for CLI usage.
    4147             :  *
    4148             :  * For example "--help"
    4149             :  *
    4150             :  * @param hArg Handle to an argument. Must NOT be null.
    4151             :  * @since 3.11
    4152             :  */
    4153           1 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    4154             : {
    4155           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4156           1 :     return hArg->ptr->IsOnlyForCLI();
    4157             : }
    4158             : 
    4159             : /************************************************************************/
    4160             : /*                     GDALAlgorithmArgIsInput()                        */
    4161             : /************************************************************************/
    4162             : 
    4163             : /** Indicate whether the value of the argument is read-only during the
    4164             :  * execution of the algorithm.
    4165             :  *
    4166             :  * Default is true.
    4167             :  *
    4168             :  * @param hArg Handle to an argument. Must NOT be null.
    4169             :  * @since 3.11
    4170             :  */
    4171           1 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    4172             : {
    4173           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4174           1 :     return hArg->ptr->IsInput();
    4175             : }
    4176             : 
    4177             : /************************************************************************/
    4178             : /*                     GDALAlgorithmArgIsOutput()                       */
    4179             : /************************************************************************/
    4180             : 
    4181             : /** Return whether (at least part of) the value of the argument is set
    4182             :  * during the execution of the algorithm.
    4183             :  *
    4184             :  * For example, "output-value" for "gdal raster info"
    4185             :  * Default is false.
    4186             :  * An argument may return both IsInput() and IsOutput() as true.
    4187             :  * For example the "gdal raster convert" algorithm consumes the dataset
    4188             :  * name of its "output" argument, and sets the dataset object during its
    4189             :  * execution.
    4190             :  *
    4191             :  * @param hArg Handle to an argument. Must NOT be null.
    4192             :  * @since 3.11
    4193             :  */
    4194           1 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    4195             : {
    4196           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4197           1 :     return hArg->ptr->IsOutput();
    4198             : }
    4199             : 
    4200             : /************************************************************************/
    4201             : /*               GDALAlgorithmArgGetMutualExclusionGroup()              */
    4202             : /************************************************************************/
    4203             : 
    4204             : /** Return the name of the mutual exclusion group to which this argument
    4205             :  * belongs to.
    4206             :  *
    4207             :  * Or empty string if it does not belong to any exclusion group.
    4208             :  *
    4209             :  * @param hArg Handle to an argument. Must NOT be null.
    4210             :  * @return string whose lifetime is bound to hArg and which must not
    4211             :  * be freed.
    4212             :  * @since 3.11
    4213             :  */
    4214           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    4215             : {
    4216           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4217           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    4218             : }
    4219             : 
    4220             : /************************************************************************/
    4221             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    4222             : /************************************************************************/
    4223             : 
    4224             : /** Return the argument value as a boolean.
    4225             :  *
    4226             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    4227             :  *
    4228             :  * @param hArg Handle to an argument. Must NOT be null.
    4229             :  * @since 3.11
    4230             :  */
    4231           4 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    4232             : {
    4233           4 :     VALIDATE_POINTER1(hArg, __func__, false);
    4234           4 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    4235             :     {
    4236           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4237             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    4238             :                  __func__);
    4239           1 :         return false;
    4240             :     }
    4241           3 :     return hArg->ptr->Get<bool>();
    4242             : }
    4243             : 
    4244             : /************************************************************************/
    4245             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    4246             : /************************************************************************/
    4247             : 
    4248             : /** Return the argument value as a string.
    4249             :  *
    4250             :  * Must only be called on arguments whose type is GAAT_STRING.
    4251             :  *
    4252             :  * @param hArg Handle to an argument. Must NOT be null.
    4253             :  * @return string whose lifetime is bound to hArg and which must not
    4254             :  * be freed.
    4255             :  * @since 3.11
    4256             :  */
    4257          26 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    4258             : {
    4259          26 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4260          26 :     if (hArg->ptr->GetType() != GAAT_STRING)
    4261             :     {
    4262           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4263             :                  "%s must only be called on arguments of type GAAT_STRING",
    4264             :                  __func__);
    4265           1 :         return nullptr;
    4266             :     }
    4267          25 :     return hArg->ptr->Get<std::string>().c_str();
    4268             : }
    4269             : 
    4270             : /************************************************************************/
    4271             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    4272             : /************************************************************************/
    4273             : 
    4274             : /** Return the argument value as a GDALArgDatasetValueH.
    4275             :  *
    4276             :  * Must only be called on arguments whose type is GAAT_DATASET
    4277             :  *
    4278             :  * @param hArg Handle to an argument. Must NOT be null.
    4279             :  * @return handle to a GDALArgDatasetValue that must be released with
    4280             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    4281             :  * the one of hArg.
    4282             :  * @since 3.11
    4283             :  */
    4284          61 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    4285             : {
    4286          61 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4287          61 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    4288             :     {
    4289           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4290             :                  "%s must only be called on arguments of type GAAT_DATASET",
    4291             :                  __func__);
    4292           1 :         return nullptr;
    4293             :     }
    4294          60 :     return std::make_unique<GDALArgDatasetValueHS>(
    4295         120 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    4296          60 :         .release();
    4297             : }
    4298             : 
    4299             : /************************************************************************/
    4300             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    4301             : /************************************************************************/
    4302             : 
    4303             : /** Return the argument value as a integer.
    4304             :  *
    4305             :  * Must only be called on arguments whose type is GAAT_INTEGER
    4306             :  *
    4307             :  * @param hArg Handle to an argument. Must NOT be null.
    4308             :  * @since 3.11
    4309             :  */
    4310           2 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    4311             : {
    4312           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    4313           2 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    4314             :     {
    4315           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4316             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    4317             :                  __func__);
    4318           1 :         return 0;
    4319             :     }
    4320           1 :     return hArg->ptr->Get<int>();
    4321             : }
    4322             : 
    4323             : /************************************************************************/
    4324             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    4325             : /************************************************************************/
    4326             : 
    4327             : /** Return the argument value as a double.
    4328             :  *
    4329             :  * Must only be called on arguments whose type is GAAT_REAL
    4330             :  *
    4331             :  * @param hArg Handle to an argument. Must NOT be null.
    4332             :  * @since 3.11
    4333             :  */
    4334           2 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    4335             : {
    4336           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    4337           2 :     if (hArg->ptr->GetType() != GAAT_REAL)
    4338             :     {
    4339           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4340             :                  "%s must only be called on arguments of type GAAT_REAL",
    4341             :                  __func__);
    4342           1 :         return 0;
    4343             :     }
    4344           1 :     return hArg->ptr->Get<double>();
    4345             : }
    4346             : 
    4347             : /************************************************************************/
    4348             : /*                   GDALAlgorithmArgGetAsStringList()                  */
    4349             : /************************************************************************/
    4350             : 
    4351             : /** Return the argument value as a double.
    4352             :  *
    4353             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    4354             :  *
    4355             :  * @param hArg Handle to an argument. Must NOT be null.
    4356             :  * @return a NULL terminated list of names, which must be destroyed with
    4357             :  * CSLDestroy()
    4358             : 
    4359             :  * @since 3.11
    4360             :  */
    4361           2 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    4362             : {
    4363           2 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4364           2 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    4365             :     {
    4366           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4367             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    4368             :                  __func__);
    4369           1 :         return nullptr;
    4370             :     }
    4371           2 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    4372           1 :         .StealList();
    4373             : }
    4374             : 
    4375             : /************************************************************************/
    4376             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    4377             : /************************************************************************/
    4378             : 
    4379             : /** Return the argument value as a integer.
    4380             :  *
    4381             :  * Must only be called on arguments whose type is GAAT_INTEGER
    4382             :  *
    4383             :  * @param hArg Handle to an argument. Must NOT be null.
    4384             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    4385             :  * @since 3.11
    4386             :  */
    4387           2 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    4388             :                                             size_t *pnCount)
    4389             : {
    4390           2 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4391           2 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    4392           2 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    4393             :     {
    4394           1 :         CPLError(
    4395             :             CE_Failure, CPLE_AppDefined,
    4396             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    4397             :             __func__);
    4398           1 :         *pnCount = 0;
    4399           1 :         return nullptr;
    4400             :     }
    4401           1 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    4402           1 :     *pnCount = val.size();
    4403           1 :     return val.data();
    4404             : }
    4405             : 
    4406             : /************************************************************************/
    4407             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    4408             : /************************************************************************/
    4409             : 
    4410             : /** Return the argument value as a integer.
    4411             :  *
    4412             :  * Must only be called on arguments whose type is GAAT_INTEGER
    4413             :  *
    4414             :  * @param hArg Handle to an argument. Must NOT be null.
    4415             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    4416             :  * @since 3.11
    4417             :  */
    4418           2 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    4419             :                                               size_t *pnCount)
    4420             : {
    4421           2 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    4422           2 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    4423           2 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    4424             :     {
    4425           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4426             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    4427             :                  __func__);
    4428           1 :         *pnCount = 0;
    4429           1 :         return nullptr;
    4430             :     }
    4431           1 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    4432           1 :     *pnCount = val.size();
    4433           1 :     return val.data();
    4434             : }
    4435             : 
    4436             : /************************************************************************/
    4437             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    4438             : /************************************************************************/
    4439             : 
    4440             : /** Set the value for a GAAT_BOOLEAN argument.
    4441             :  *
    4442             :  * It cannot be called several times for a given argument.
    4443             :  * Validation checks and other actions are run.
    4444             :  *
    4445             :  * @param hArg Handle to an argument. Must NOT be null.
    4446             :  * @param value value.
    4447             :  * @return true if success.
    4448             :  * @since 3.11
    4449             :  */
    4450             : 
    4451           4 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    4452             : {
    4453           4 :     VALIDATE_POINTER1(hArg, __func__, false);
    4454           4 :     return hArg->ptr->Set(value);
    4455             : }
    4456             : 
    4457             : /************************************************************************/
    4458             : /*                    GDALAlgorithmArgSetAsString()                     */
    4459             : /************************************************************************/
    4460             : 
    4461             : /** Set the value for a GAAT_STRING argument.
    4462             :  *
    4463             :  * It cannot be called several times for a given argument.
    4464             :  * Validation checks and other actions are run.
    4465             :  *
    4466             :  * @param hArg Handle to an argument. Must NOT be null.
    4467             :  * @param value value (may be null)
    4468             :  * @return true if success.
    4469             :  * @since 3.11
    4470             :  */
    4471             : 
    4472          22 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    4473             : {
    4474          22 :     VALIDATE_POINTER1(hArg, __func__, false);
    4475          22 :     return hArg->ptr->Set(value ? value : "");
    4476             : }
    4477             : 
    4478             : /************************************************************************/
    4479             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    4480             : /************************************************************************/
    4481             : 
    4482             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    4483             :  *
    4484             :  * It cannot be called several times for a given argument.
    4485             :  * Validation checks and other actions are run.
    4486             :  *
    4487             :  * @param hArg Handle to an argument. Must NOT be null.
    4488             :  * @param value value.
    4489             :  * @return true if success.
    4490             :  * @since 3.11
    4491             :  */
    4492             : 
    4493           5 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    4494             : {
    4495           5 :     VALIDATE_POINTER1(hArg, __func__, false);
    4496           5 :     return hArg->ptr->Set(value);
    4497             : }
    4498             : 
    4499             : /************************************************************************/
    4500             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    4501             : /************************************************************************/
    4502             : 
    4503             : /** Set the value for a GAAT_REAL argument.
    4504             :  *
    4505             :  * It cannot be called several times for a given argument.
    4506             :  * Validation checks and other actions are run.
    4507             :  *
    4508             :  * @param hArg Handle to an argument. Must NOT be null.
    4509             :  * @param value value.
    4510             :  * @return true if success.
    4511             :  * @since 3.11
    4512             :  */
    4513             : 
    4514           1 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    4515             : {
    4516           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    4517           1 :     return hArg->ptr->Set(value);
    4518             : }
    4519             : 
    4520             : /************************************************************************/
    4521             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    4522             : /************************************************************************/
    4523             : 
    4524             : /** Set the value for a GAAT_DATASET argument.
    4525             :  *
    4526             :  * It cannot be called several times for a given argument.
    4527             :  * Validation checks and other actions are run.
    4528             :  *
    4529             :  * @param hArg Handle to an argument. Must NOT be null.
    4530             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    4531             :  * @return true if success.
    4532             :  * @since 3.11
    4533             :  */
    4534           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    4535             :                                        GDALArgDatasetValueH value)
    4536             : {
    4537           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    4538           2 :     VALIDATE_POINTER1(value, __func__, false);
    4539           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    4540             : }
    4541             : 
    4542             : /************************************************************************/
    4543             : /*                     GDALAlgorithmArgSetDataset()                     */
    4544             : /************************************************************************/
    4545             : 
    4546             : /** Set dataset object, increasing its reference counter.
    4547             :  *
    4548             :  * @param hArg Handle to an argument. Must NOT be null.
    4549             :  * @param hDS Dataset object. May be null.
    4550             :  * @return true if success.
    4551             :  * @since 3.11
    4552             :  */
    4553             : 
    4554          11 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    4555             : {
    4556          11 :     VALIDATE_POINTER1(hArg, __func__, false);
    4557          11 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    4558             : }
    4559             : 
    4560             : /************************************************************************/
    4561             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    4562             : /************************************************************************/
    4563             : 
    4564             : /** Set the value for a GAAT_STRING_LIST argument.
    4565             :  *
    4566             :  * It cannot be called several times for a given argument.
    4567             :  * Validation checks and other actions are run.
    4568             :  *
    4569             :  * @param hArg Handle to an argument. Must NOT be null.
    4570             :  * @param value value as a NULL terminated list (may be null)
    4571             :  * @return true if success.
    4572             :  * @since 3.11
    4573             :  */
    4574             : 
    4575           2 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    4576             : {
    4577           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    4578           2 :     return hArg->ptr->Set(
    4579           4 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    4580             : }
    4581             : 
    4582             : /************************************************************************/
    4583             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    4584             : /************************************************************************/
    4585             : 
    4586             : /** Set the value for a GAAT_INTEGER_LIST argument.
    4587             :  *
    4588             :  * It cannot be called several times for a given argument.
    4589             :  * Validation checks and other actions are run.
    4590             :  *
    4591             :  * @param hArg Handle to an argument. Must NOT be null.
    4592             :  * @param nCount Number of values in pnValues.
    4593             :  * @param pnValues Pointer to an array of integer values of size nCount.
    4594             :  * @return true if success.
    4595             :  * @since 3.11
    4596             :  */
    4597           4 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    4598             :                                       const int *pnValues)
    4599             : {
    4600           4 :     VALIDATE_POINTER1(hArg, __func__, false);
    4601           4 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    4602             : }
    4603             : 
    4604             : /************************************************************************/
    4605             : /*                   GDALAlgorithmArgSetAsDoubleList()                  */
    4606             : /************************************************************************/
    4607             : 
    4608             : /** Set the value for a GAAT_REAL_LIST argument.
    4609             :  *
    4610             :  * It cannot be called several times for a given argument.
    4611             :  * Validation checks and other actions are run.
    4612             :  *
    4613             :  * @param hArg Handle to an argument. Must NOT be null.
    4614             :  * @param nCount Number of values in pnValues.
    4615             :  * @param pnValues Pointer to an array of double values of size nCount.
    4616             :  * @return true if success.
    4617             :  * @since 3.11
    4618             :  */
    4619           5 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    4620             :                                      const double *pnValues)
    4621             : {
    4622           5 :     VALIDATE_POINTER1(hArg, __func__, false);
    4623           5 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    4624             : }
    4625             : 
    4626             : /************************************************************************/
    4627             : /*                     GDALAlgorithmArgSetDatasets()                    */
    4628             : /************************************************************************/
    4629             : 
    4630             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    4631             :  *
    4632             :  * @param hArg Handle to an argument. Must NOT be null.
    4633             :  * @param nCount Number of values in pnValues.
    4634             :  * @param pahDS Pointer to an array of dataset of size nCount.
    4635             :  * @return true if success.
    4636             :  * @since 3.11
    4637             :  */
    4638             : 
    4639           8 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    4640             :                                  GDALDatasetH *pahDS)
    4641             : {
    4642           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    4643          16 :     std::vector<GDALArgDatasetValue> values;
    4644          22 :     for (size_t i = 0; i < nCount; ++i)
    4645             :     {
    4646          14 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    4647             :     }
    4648           8 :     return hArg->ptr->Set(std::move(values));
    4649             : }
    4650             : 
    4651             : /************************************************************************/
    4652             : /*                    GDALAlgorithmArgSetDatasetNames()                 */
    4653             : /************************************************************************/
    4654             : 
    4655             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    4656             :  *
    4657             :  * @param hArg Handle to an argument. Must NOT be null.
    4658             :  * @param names Dataset names as a NULL terminated list (may be null)
    4659             :  * @return true if success.
    4660             :  * @since 3.11
    4661             :  */
    4662             : 
    4663           8 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    4664             : {
    4665           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    4666          16 :     std::vector<GDALArgDatasetValue> values;
    4667          16 :     for (size_t i = 0; names[i]; ++i)
    4668             :     {
    4669           8 :         values.emplace_back(names[i]);
    4670             :     }
    4671           8 :     return hArg->ptr->Set(std::move(values));
    4672             : }
    4673             : 
    4674             : /************************************************************************/
    4675             : /*                      GDALArgDatasetValueCreate()                     */
    4676             : /************************************************************************/
    4677             : 
    4678             : /** Instantiate an empty GDALArgDatasetValue
    4679             :  *
    4680             :  * @return new handle to free with GDALArgDatasetValueRelease()
    4681             :  * @since 3.11
    4682             :  */
    4683           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    4684             : {
    4685           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    4686             : }
    4687             : 
    4688             : /************************************************************************/
    4689             : /*                      GDALArgDatasetValueRelease()                    */
    4690             : /************************************************************************/
    4691             : 
    4692             : /** Release a handle to a GDALArgDatasetValue
    4693             :  *
    4694             :  * @since 3.11
    4695             :  */
    4696          61 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    4697             : {
    4698          61 :     delete hValue;
    4699          61 : }
    4700             : 
    4701             : /************************************************************************/
    4702             : /*                    GDALArgDatasetValueGetName()                      */
    4703             : /************************************************************************/
    4704             : 
    4705             : /** Return the name component of the GDALArgDatasetValue
    4706             :  *
    4707             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4708             :  * @return string whose lifetime is bound to hAlg and which must not
    4709             :  * be freed.
    4710             :  * @since 3.11
    4711             :  */
    4712           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    4713             : {
    4714           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    4715           1 :     return hValue->ptr->GetName().c_str();
    4716             : }
    4717             : 
    4718             : /************************************************************************/
    4719             : /*               GDALArgDatasetValueGetDatasetRef()                     */
    4720             : /************************************************************************/
    4721             : 
    4722             : /** Return the dataset component of the GDALArgDatasetValue.
    4723             :  *
    4724             :  * This does not modify the reference counter, hence the lifetime of the
    4725             :  * returned object is not guaranteed to exceed the one of hValue.
    4726             :  *
    4727             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4728             :  * @since 3.11
    4729             :  */
    4730           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    4731             : {
    4732           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    4733           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    4734             : }
    4735             : 
    4736             : /************************************************************************/
    4737             : /*               GDALArgDatasetValueGetDatasetIncreaseRefCount()        */
    4738             : /************************************************************************/
    4739             : 
    4740             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    4741             :  * reference count if not null. Once done with the dataset, the caller should
    4742             :  * call GDALReleaseDataset().
    4743             :  *
    4744             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4745             :  * @since 3.11
    4746             :  */
    4747             : GDALDatasetH
    4748          24 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    4749             : {
    4750          24 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    4751          24 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    4752             : }
    4753             : 
    4754             : /************************************************************************/
    4755             : /*                    GDALArgDatasetValueGetType()                      */
    4756             : /************************************************************************/
    4757             : 
    4758             : /** Get which type of dataset is allowed / generated.
    4759             :  *
    4760             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    4761             :  * GDAL_OF_MULTIDIM_RASTER.
    4762             :  *
    4763             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4764             :  * @since 3.11
    4765             :  */
    4766           1 : GDALArgDatasetValueType GDALArgDatasetValueGetType(GDALArgDatasetValueH hValue)
    4767             : {
    4768           1 :     VALIDATE_POINTER1(hValue, __func__, 0);
    4769           1 :     return hValue->ptr->GetType();
    4770             : }
    4771             : 
    4772             : /************************************************************************/
    4773             : /*                   GDALArgDatasetValueGetInputFlags()                 */
    4774             : /************************************************************************/
    4775             : 
    4776             : /** Indicates which components among name and dataset are accepted as
    4777             :  * input, when this argument serves as an input.
    4778             :  *
    4779             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    4780             :  * input.
    4781             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    4782             :  * accepted as input.
    4783             :  * If both bits are set, the algorithm can accept either a name or a dataset
    4784             :  * object.
    4785             :  *
    4786             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4787             :  * @return string whose lifetime is bound to hAlg and which must not
    4788             :  * be freed.
    4789             :  * @since 3.11
    4790             :  */
    4791           1 : int GDALArgDatasetValueGetInputFlags(GDALArgDatasetValueH hValue)
    4792             : {
    4793           1 :     VALIDATE_POINTER1(hValue, __func__, 0);
    4794           1 :     return hValue->ptr->GetInputFlags();
    4795             : }
    4796             : 
    4797             : /************************************************************************/
    4798             : /*                  GDALArgDatasetValueGetOutputFlags()                 */
    4799             : /************************************************************************/
    4800             : 
    4801             : /** Indicates which components among name and dataset are modified,
    4802             :  * when this argument serves as an output.
    4803             :  *
    4804             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    4805             :  * output (that is the algorithm will generate the name. Rarely used).
    4806             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    4807             :  * generated as output, and available for use after the algorithm has
    4808             :  * completed.
    4809             :  *
    4810             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4811             :  * @return string whose lifetime is bound to hAlg and which must not
    4812             :  * be freed.
    4813             :  * @since 3.11
    4814             :  */
    4815           1 : int GDALArgDatasetValueGetOutputFlags(GDALArgDatasetValueH hValue)
    4816             : {
    4817           1 :     VALIDATE_POINTER1(hValue, __func__, 0);
    4818           1 :     return hValue->ptr->GetOutputFlags();
    4819             : }
    4820             : 
    4821             : /************************************************************************/
    4822             : /*                    GDALArgDatasetValueSetName()                      */
    4823             : /************************************************************************/
    4824             : 
    4825             : /** Set dataset name
    4826             :  *
    4827             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4828             :  * @param pszName Dataset name. May be null.
    4829             :  * @since 3.11
    4830             :  */
    4831             : 
    4832          22 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    4833             :                                 const char *pszName)
    4834             : {
    4835          22 :     VALIDATE_POINTER0(hValue, __func__);
    4836          22 :     hValue->ptr->Set(pszName ? pszName : "");
    4837             : }
    4838             : 
    4839             : /************************************************************************/
    4840             : /*                  GDALArgDatasetValueSetDataset()                     */
    4841             : /************************************************************************/
    4842             : 
    4843             : /** Set dataset object, increasing its reference counter.
    4844             :  *
    4845             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    4846             :  * @param hDS Dataset object. May be null.
    4847             :  * @since 3.11
    4848             :  */
    4849             : 
    4850          17 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    4851             :                                    GDALDatasetH hDS)
    4852             : {
    4853          17 :     VALIDATE_POINTER0(hValue, __func__);
    4854          17 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    4855             : }

Generated by: LCOV version 1.14