LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2908 3073 94.6 %
Date: 2025-05-31 00:00:17 Functions: 227 229 99.1 %

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

Generated by: LCOV version 1.14