LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3452 3668 94.1 %
Date: 2025-11-15 02:16:39 Functions: 267 274 97.4 %

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

Generated by: LCOV version 1.14