LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3501 3731 93.8 %
Date: 2026-03-04 01:45:47 Functions: 271 278 97.5 %

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

Generated by: LCOV version 1.14