LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2115 2205 95.9 %
Date: 2025-03-25 20:12:57 Functions: 179 181 98.9 %

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

Generated by: LCOV version 1.14