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

Generated by: LCOV version 1.14