LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3815 4059 94.0 %
Date: 2026-06-09 05:19:24 Functions: 286 293 97.6 %

          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_enumerate.h"
      16             : #include "cpl_error.h"
      17             : #include "cpl_error_internal.h"
      18             : #include "cpl_json.h"
      19             : #include "cpl_levenshtein.h"
      20             : #include "cpl_minixml.h"
      21             : #include "cpl_multiproc.h"
      22             : 
      23             : #include "gdalalgorithm.h"
      24             : #include "gdalalg_abstract_pipeline.h"
      25             : #include "gdal_priv.h"
      26             : #include "gdal_thread_pool.h"
      27             : #include "memdataset.h"
      28             : #include "ogrsf_frmts.h"
      29             : #include "ogr_spatialref.h"
      30             : #include "vrtdataset.h"
      31             : 
      32             : #include <algorithm>
      33             : #include <cassert>
      34             : #include <cerrno>
      35             : #include <cmath>
      36             : #include <cstdlib>
      37             : #include <limits>
      38             : #include <map>
      39             : #include <type_traits>
      40             : #include <string_view>
      41             : #include <regex>
      42             : 
      43             : #ifndef _
      44             : #define _(x) (x)
      45             : #endif
      46             : 
      47             : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
      48             : 
      49             : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
      50             : 
      51             : constexpr const char *GDAL_ARG_NAME_BAND = "band";
      52             : 
      53             : //! @cond Doxygen_Suppress
      54             : struct GDALAlgorithmArgHS
      55             : {
      56             :     GDALAlgorithmArg *ptr = nullptr;
      57             : 
      58      341078 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      59             :     {
      60      341078 :     }
      61             : };
      62             : 
      63             : //! @endcond
      64             : 
      65             : //! @cond Doxygen_Suppress
      66             : struct GDALArgDatasetValueHS
      67             : {
      68             :     GDALArgDatasetValue val{};
      69             :     GDALArgDatasetValue *ptr = nullptr;
      70             : 
      71           1 :     GDALArgDatasetValueHS() : ptr(&val)
      72             :     {
      73           1 :     }
      74             : 
      75        3240 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      76             :     {
      77        3240 :     }
      78             : 
      79             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      80             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      81             : };
      82             : 
      83             : //! @endcond
      84             : 
      85             : /************************************************************************/
      86             : /*                     GDALAlgorithmArgTypeIsList()                     */
      87             : /************************************************************************/
      88             : 
      89      424571 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      90             : {
      91      424571 :     switch (type)
      92             :     {
      93      279750 :         case GAAT_BOOLEAN:
      94             :         case GAAT_STRING:
      95             :         case GAAT_INTEGER:
      96             :         case GAAT_REAL:
      97             :         case GAAT_DATASET:
      98      279750 :             break;
      99             : 
     100      144821 :         case GAAT_STRING_LIST:
     101             :         case GAAT_INTEGER_LIST:
     102             :         case GAAT_REAL_LIST:
     103             :         case GAAT_DATASET_LIST:
     104      144821 :             return true;
     105             :     }
     106             : 
     107      279750 :     return false;
     108             : }
     109             : 
     110             : /************************************************************************/
     111             : /*                      GDALAlgorithmArgTypeName()                      */
     112             : /************************************************************************/
     113             : 
     114        5448 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     115             : {
     116        5448 :     switch (type)
     117             :     {
     118        1323 :         case GAAT_BOOLEAN:
     119        1323 :             break;
     120        1482 :         case GAAT_STRING:
     121        1482 :             return "string";
     122         379 :         case GAAT_INTEGER:
     123         379 :             return "integer";
     124         477 :         case GAAT_REAL:
     125         477 :             return "real";
     126         252 :         case GAAT_DATASET:
     127         252 :             return "dataset";
     128        1016 :         case GAAT_STRING_LIST:
     129        1016 :             return "string_list";
     130          93 :         case GAAT_INTEGER_LIST:
     131          93 :             return "integer_list";
     132         221 :         case GAAT_REAL_LIST:
     133         221 :             return "real_list";
     134         205 :         case GAAT_DATASET_LIST:
     135         205 :             return "dataset_list";
     136             :     }
     137             : 
     138        1323 :     return "boolean";
     139             : }
     140             : 
     141             : /************************************************************************/
     142             : /*                  GDALAlgorithmArgDatasetTypeName()                   */
     143             : /************************************************************************/
     144             : 
     145       23182 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
     146             : {
     147       23182 :     std::string ret;
     148       23182 :     if ((type & GDAL_OF_RASTER) != 0)
     149       12692 :         ret = "raster";
     150       23182 :     if ((type & GDAL_OF_VECTOR) != 0)
     151             :     {
     152       11296 :         if (!ret.empty())
     153             :         {
     154        1417 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     155         286 :                 ret += ", ";
     156             :             else
     157        1131 :                 ret += " or ";
     158             :         }
     159       11296 :         ret += "vector";
     160             :     }
     161       23182 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     162             :     {
     163         739 :         if (!ret.empty())
     164             :         {
     165         345 :             ret += " or ";
     166             :         }
     167         739 :         ret += "multidimensional raster";
     168             :     }
     169       23182 :     return ret;
     170             : }
     171             : 
     172             : /************************************************************************/
     173             : /*                        GDALAlgorithmArgDecl()                        */
     174             : /************************************************************************/
     175             : 
     176             : // cppcheck-suppress uninitMemberVar
     177      341137 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     178             :                                            char chShortName,
     179             :                                            const std::string &description,
     180      341137 :                                            GDALAlgorithmArgType type)
     181             :     : m_longName(longName),
     182      341137 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     183             :       m_description(description), m_type(type),
     184      682274 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     185      341137 :                     .toupper()),
     186     1023410 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     187             : {
     188      341137 :     if (m_type == GAAT_BOOLEAN)
     189             :     {
     190      142135 :         m_defaultValue = false;
     191             :     }
     192      341137 : }
     193             : 
     194             : /************************************************************************/
     195             : /*                 GDALAlgorithmArgDecl::SetMinCount()                  */
     196             : /************************************************************************/
     197             : 
     198       18793 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     199             : {
     200       18793 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     201             :     {
     202           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     203             :                  "SetMinCount() illegal on scalar argument '%s'",
     204           1 :                  GetName().c_str());
     205             :     }
     206             :     else
     207             :     {
     208       18792 :         m_minCount = count;
     209             :     }
     210       18793 :     return *this;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
     215             : /************************************************************************/
     216             : 
     217       17850 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     218             : {
     219       17850 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     220             :     {
     221           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     222             :                  "SetMaxCount() illegal on scalar argument '%s'",
     223           1 :                  GetName().c_str());
     224             :     }
     225             :     else
     226             :     {
     227       17849 :         m_maxCount = count;
     228             :     }
     229       17850 :     return *this;
     230             : }
     231             : 
     232             : /************************************************************************/
     233             : /*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
     234             : /************************************************************************/
     235             : 
     236             : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
     237             : 
     238             : /************************************************************************/
     239             : /*                       GDALAlgorithmArg::Set()                        */
     240             : /************************************************************************/
     241             : 
     242        1228 : bool GDALAlgorithmArg::Set(bool value)
     243             : {
     244        1228 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     245             :     {
     246          14 :         CPLError(
     247             :             CE_Failure, CPLE_AppDefined,
     248             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     249           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     250           7 :         return false;
     251             :     }
     252        1221 :     return SetInternal(value);
     253             : }
     254             : 
     255        4200 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
     256             : {
     257        4245 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     258          45 :         value.front() == '@')
     259             :     {
     260           2 :         GByte *pabyData = nullptr;
     261           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     262           2 :                           10 * 1024 * 1024))
     263             :         {
     264             :             // Remove UTF-8 BOM
     265           1 :             size_t offset = 0;
     266           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     267           1 :                 pabyData[2] == 0xBF)
     268             :             {
     269           1 :                 offset = 3;
     270             :             }
     271           1 :             value = reinterpret_cast<const char *>(pabyData + offset);
     272           1 :             VSIFree(pabyData);
     273             :         }
     274             :         else
     275             :         {
     276           1 :             return false;
     277             :         }
     278             :     }
     279             : 
     280        4199 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     281          44 :         value = CPLRemoveSQLComments(value);
     282             : 
     283        4199 :     return true;
     284             : }
     285             : 
     286        4236 : bool GDALAlgorithmArg::Set(const std::string &value)
     287             : {
     288        4236 :     switch (m_decl.GetType())
     289             :     {
     290           9 :         case GAAT_BOOLEAN:
     291          17 :             if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
     292          17 :                 EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
     293             :             {
     294           4 :                 return Set(true);
     295             :             }
     296           5 :             else if (EQUAL(value.c_str(), "0") ||
     297           4 :                      EQUAL(value.c_str(), "FALSE") ||
     298           9 :                      EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
     299             :             {
     300           4 :                 return Set(false);
     301             :             }
     302           1 :             break;
     303             : 
     304           8 :         case GAAT_INTEGER:
     305             :         case GAAT_INTEGER_LIST:
     306             :         {
     307           8 :             errno = 0;
     308           8 :             char *endptr = nullptr;
     309           8 :             const auto v = std::strtoll(value.c_str(), &endptr, 10);
     310          13 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     311           5 :                 endptr == value.c_str() + value.size())
     312             :             {
     313           3 :                 if (m_decl.GetType() == GAAT_INTEGER)
     314           3 :                     return Set(static_cast<int>(v));
     315             :                 else
     316           1 :                     return Set(std::vector<int>{static_cast<int>(v)});
     317             :             }
     318           5 :             break;
     319             :         }
     320             : 
     321           5 :         case GAAT_REAL:
     322             :         case GAAT_REAL_LIST:
     323             :         {
     324           5 :             char *endptr = nullptr;
     325           5 :             const double v = CPLStrtod(value.c_str(), &endptr);
     326           5 :             if (endptr == value.c_str() + value.size())
     327             :             {
     328           3 :                 if (m_decl.GetType() == GAAT_REAL)
     329           3 :                     return Set(v);
     330             :                 else
     331           1 :                     return Set(std::vector<double>{v});
     332             :             }
     333           2 :             break;
     334             :         }
     335             : 
     336        4178 :         case GAAT_STRING:
     337        4178 :             break;
     338             : 
     339           8 :         case GAAT_STRING_LIST:
     340          16 :             return Set(std::vector<std::string>{value});
     341             : 
     342          28 :         case GAAT_DATASET:
     343          28 :             return SetDatasetName(value);
     344             : 
     345           0 :         case GAAT_DATASET_LIST:
     346             :         {
     347           0 :             std::vector<GDALArgDatasetValue> v;
     348           0 :             v.resize(1);
     349           0 :             v[0].Set(value);
     350           0 :             return Set(std::move(v));
     351             :         }
     352             :     }
     353             : 
     354        4186 :     if (m_decl.GetType() != GAAT_STRING)
     355             :     {
     356          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     357             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     358             :                  "supported",
     359           8 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     360           8 :         return false;
     361             :     }
     362             : 
     363        4178 :     std::string newValue(value);
     364        4178 :     return ProcessString(newValue) && SetInternal(newValue);
     365             : }
     366             : 
     367         873 : bool GDALAlgorithmArg::Set(int value)
     368             : {
     369         873 :     if (m_decl.GetType() == GAAT_BOOLEAN)
     370             :     {
     371           3 :         if (value == 1)
     372           1 :             return Set(true);
     373           2 :         else if (value == 0)
     374           1 :             return Set(false);
     375             :     }
     376         870 :     else if (m_decl.GetType() == GAAT_REAL)
     377             :     {
     378           3 :         return Set(static_cast<double>(value));
     379             :     }
     380         867 :     else if (m_decl.GetType() == GAAT_STRING)
     381             :     {
     382           2 :         return Set(std::to_string(value));
     383             :     }
     384         865 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST)
     385             :     {
     386           1 :         return Set(std::vector<int>{value});
     387             :     }
     388         864 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     389             :     {
     390           1 :         return Set(std::vector<double>{static_cast<double>(value)});
     391             :     }
     392         863 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     393             :     {
     394           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     395             :     }
     396             : 
     397         863 :     if (m_decl.GetType() != GAAT_INTEGER)
     398             :     {
     399           2 :         CPLError(
     400             :             CE_Failure, CPLE_AppDefined,
     401             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     402           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     403           1 :         return false;
     404             :     }
     405         862 :     return SetInternal(value);
     406             : }
     407             : 
     408         291 : bool GDALAlgorithmArg::Set(double value)
     409             : {
     410         294 :     if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
     411         294 :         value <= INT_MAX && static_cast<int>(value) == value)
     412             :     {
     413           2 :         return Set(static_cast<int>(value));
     414             :     }
     415         289 :     else if (m_decl.GetType() == GAAT_STRING)
     416             :     {
     417           2 :         return Set(std::to_string(value));
     418             :     }
     419         289 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
     420         289 :              value <= INT_MAX && static_cast<int>(value) == value)
     421             :     {
     422           1 :         return Set(std::vector<int>{static_cast<int>(value)});
     423             :     }
     424         286 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     425             :     {
     426           0 :         return Set(std::vector<double>{value});
     427             :     }
     428         286 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     429             :     {
     430           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     431             :     }
     432         285 :     else if (m_decl.GetType() != GAAT_REAL)
     433             :     {
     434           6 :         CPLError(
     435             :             CE_Failure, CPLE_AppDefined,
     436             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     437           3 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     438           3 :         return false;
     439             :     }
     440         282 :     return SetInternal(value);
     441             : }
     442             : 
     443        5597 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
     444             : {
     445        5600 :     if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
     446           3 :         arg->GetDatasetOutputFlags() == GADV_OBJECT)
     447             :     {
     448           3 :         CPLError(
     449             :             CE_Failure, CPLE_AppDefined,
     450             :             "Dataset object '%s' is created by algorithm and cannot be set "
     451             :             "as an input.",
     452           3 :             arg->GetName().c_str());
     453           3 :         return false;
     454             :     }
     455        5594 :     else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
     456             :     {
     457           8 :         CPLError(CE_Failure, CPLE_AppDefined,
     458             :                  "Dataset%s '%s' must be provided by name, not as object.",
     459           8 :                  arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
     460           4 :         return false;
     461             :     }
     462             : 
     463        5590 :     return true;
     464             : }
     465             : 
     466          33 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     467             : {
     468          58 :     if (m_decl.GetType() != GAAT_DATASET &&
     469          25 :         m_decl.GetType() != GAAT_DATASET_LIST)
     470             :     {
     471           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     472             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     473             :                  "is not supported",
     474           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     475           1 :         return false;
     476             :     }
     477          32 :     if (!CheckCanSetDatasetObject(this))
     478           2 :         return false;
     479          30 :     m_explicitlySet = true;
     480          30 :     if (m_decl.GetType() == GAAT_DATASET)
     481             :     {
     482           6 :         auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     483           6 :         val.Set(ds);
     484             :     }
     485             :     else
     486             :     {
     487          24 :         CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
     488          24 :         auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
     489          24 :         val.resize(1);
     490          24 :         val[0].Set(ds);
     491             :     }
     492          30 :     return RunAllActions();
     493             : }
     494             : 
     495           3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     496             : {
     497           3 :     if (m_decl.GetType() != GAAT_DATASET)
     498             :     {
     499           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     500             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     501             :                  "is not supported",
     502           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     503           1 :         return false;
     504             :     }
     505           2 :     if (!CheckCanSetDatasetObject(this))
     506           1 :         return false;
     507           1 :     m_explicitlySet = true;
     508           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     509           1 :     val.Set(std::move(ds));
     510           1 :     return RunAllActions();
     511             : }
     512             : 
     513         614 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     514             : {
     515         614 :     if (m_decl.GetType() != GAAT_DATASET)
     516             :     {
     517           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     518             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     519             :                  "not supported",
     520           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     521           1 :         return false;
     522             :     }
     523         613 :     m_explicitlySet = true;
     524         613 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     525         613 :     return RunAllActions();
     526             : }
     527             : 
     528        1014 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     529             : {
     530        1014 :     if (m_decl.GetType() != GAAT_DATASET)
     531             :     {
     532           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     533             :                  "Calling SetFrom() on argument '%s' of type %s is "
     534             :                  "not supported",
     535           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     536           1 :         return false;
     537             :     }
     538        1013 :     if (other.GetDatasetRef() && !CheckCanSetDatasetObject(this))
     539           1 :         return false;
     540        1012 :     m_explicitlySet = true;
     541        1012 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     542        1012 :     return RunAllActions();
     543             : }
     544             : 
     545        1094 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     546             : {
     547        1094 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     548             :     {
     549           3 :         std::vector<int> v_i;
     550           4 :         for (const std::string &s : value)
     551             :         {
     552           3 :             errno = 0;
     553           3 :             char *endptr = nullptr;
     554           3 :             const auto v = std::strtoll(s.c_str(), &endptr, 10);
     555           5 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     556           2 :                 endptr == s.c_str() + s.size())
     557             :             {
     558           1 :                 v_i.push_back(static_cast<int>(v));
     559             :             }
     560             :             else
     561             :             {
     562           2 :                 break;
     563             :             }
     564             :         }
     565           3 :         if (v_i.size() == value.size())
     566           1 :             return Set(v_i);
     567             :     }
     568        1091 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     569             :     {
     570           2 :         std::vector<double> v_d;
     571           3 :         for (const std::string &s : value)
     572             :         {
     573           2 :             char *endptr = nullptr;
     574           2 :             const double v = CPLStrtod(s.c_str(), &endptr);
     575           2 :             if (endptr == s.c_str() + s.size())
     576             :             {
     577           1 :                 v_d.push_back(v);
     578             :             }
     579             :             else
     580             :             {
     581           1 :                 break;
     582             :             }
     583             :         }
     584           2 :         if (v_d.size() == value.size())
     585           1 :             return Set(v_d);
     586             :     }
     587        2176 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     588        2173 :               m_decl.GetType() == GAAT_REAL ||
     589        3264 :               m_decl.GetType() == GAAT_STRING) &&
     590           5 :              value.size() == 1)
     591             :     {
     592           4 :         return Set(value[0]);
     593             :     }
     594        1085 :     else if (m_decl.GetType() == GAAT_DATASET_LIST)
     595             :     {
     596          30 :         std::vector<GDALArgDatasetValue> dsVector;
     597          46 :         for (const std::string &s : value)
     598          31 :             dsVector.emplace_back(s);
     599          15 :         return Set(std::move(dsVector));
     600             :     }
     601             : 
     602        1073 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     603             :     {
     604          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     605             :                  "Calling Set(const std::vector<std::string> &) on argument "
     606             :                  "'%s' of type %s is not supported",
     607           5 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     608           5 :         return false;
     609             :     }
     610             : 
     611        2117 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
     612        1049 :         m_decl.IsRemoveSQLCommentsEnabled())
     613             :     {
     614          38 :         std::vector<std::string> newValue(value);
     615          41 :         for (auto &s : newValue)
     616             :         {
     617          22 :             if (!ProcessString(s))
     618           0 :                 return false;
     619             :         }
     620          19 :         return SetInternal(newValue);
     621             :     }
     622             :     else
     623             :     {
     624        1049 :         return SetInternal(value);
     625             :     }
     626             : }
     627             : 
     628         177 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     629             : {
     630         177 :     if (m_decl.GetType() == GAAT_REAL_LIST)
     631             :     {
     632           2 :         std::vector<double> v_d;
     633           2 :         for (int i : value)
     634           1 :             v_d.push_back(i);
     635           1 :         return Set(v_d);
     636             :     }
     637         176 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     638             :     {
     639           2 :         std::vector<std::string> v_s;
     640           3 :         for (int i : value)
     641           2 :             v_s.push_back(std::to_string(i));
     642           1 :         return Set(v_s);
     643             :     }
     644         348 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     645         344 :               m_decl.GetType() == GAAT_REAL ||
     646         521 :               m_decl.GetType() == GAAT_STRING) &&
     647           5 :              value.size() == 1)
     648             :     {
     649           3 :         return Set(value[0]);
     650             :     }
     651             : 
     652         172 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     653             :     {
     654           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     655             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     656             :                  "type %s is not supported",
     657           3 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     658           3 :         return false;
     659             :     }
     660         169 :     return SetInternal(value);
     661             : }
     662             : 
     663         273 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     664             : {
     665         273 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     666             :     {
     667           2 :         std::vector<int> v_i;
     668           3 :         for (double d : value)
     669             :         {
     670           2 :             if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
     671             :             {
     672           1 :                 v_i.push_back(static_cast<int>(d));
     673             :             }
     674             :             else
     675             :             {
     676             :                 break;
     677             :             }
     678             :         }
     679           2 :         if (v_i.size() == value.size())
     680           1 :             return Set(v_i);
     681             :     }
     682         271 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     683             :     {
     684           2 :         std::vector<std::string> v_s;
     685           3 :         for (double d : value)
     686           2 :             v_s.push_back(std::to_string(d));
     687           1 :         return Set(v_s);
     688             :     }
     689         539 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     690         537 :               m_decl.GetType() == GAAT_REAL ||
     691         808 :               m_decl.GetType() == GAAT_STRING) &&
     692           3 :              value.size() == 1)
     693             :     {
     694           3 :         return Set(value[0]);
     695             :     }
     696             : 
     697         268 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     698             :     {
     699           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     700             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     701             :                  "type %s is not supported",
     702           2 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     703           2 :         return false;
     704             :     }
     705         266 :     return SetInternal(value);
     706             : }
     707             : 
     708        3547 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     709             : {
     710        3547 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     711             :     {
     712           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     713             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     714             :                  "argument '%s' of type %s is not supported",
     715           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     716           1 :         return false;
     717             :     }
     718        3546 :     m_explicitlySet = true;
     719        3546 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     720        3546 :     return RunAllActions();
     721             : }
     722             : 
     723             : GDALAlgorithmArg &
     724           0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
     725             : {
     726           0 :     Set(std::move(value));
     727           0 :     return *this;
     728             : }
     729             : 
     730           1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
     731             : {
     732           1 :     const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
     733           1 :     return Set(value.exportToWkt(apszOptions));
     734             : }
     735             : 
     736        4336 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     737             : {
     738        4336 :     if (m_decl.GetType() != other.GetType())
     739             :     {
     740           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     741             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     742             :                  "other argument type is %s is not supported",
     743           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     744             :                  GDALAlgorithmArgTypeName(other.GetType()));
     745           1 :         return false;
     746             :     }
     747             : 
     748        4335 :     switch (m_decl.GetType())
     749             :     {
     750          92 :         case GAAT_BOOLEAN:
     751          92 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     752          92 :             break;
     753         809 :         case GAAT_STRING:
     754        1618 :             *std::get<std::string *>(m_value) =
     755         809 :                 *std::get<std::string *>(other.m_value);
     756         809 :             break;
     757           6 :         case GAAT_INTEGER:
     758           6 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     759           6 :             break;
     760           1 :         case GAAT_REAL:
     761           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     762           1 :             break;
     763        1008 :         case GAAT_DATASET:
     764        1008 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     765          60 :         case GAAT_STRING_LIST:
     766         120 :             *std::get<std::vector<std::string> *>(m_value) =
     767          60 :                 *std::get<std::vector<std::string> *>(other.m_value);
     768          60 :             break;
     769           1 :         case GAAT_INTEGER_LIST:
     770           2 :             *std::get<std::vector<int> *>(m_value) =
     771           1 :                 *std::get<std::vector<int> *>(other.m_value);
     772           1 :             break;
     773           1 :         case GAAT_REAL_LIST:
     774           2 :             *std::get<std::vector<double> *>(m_value) =
     775           1 :                 *std::get<std::vector<double> *>(other.m_value);
     776           1 :             break;
     777        2357 :         case GAAT_DATASET_LIST:
     778             :         {
     779        2357 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     780        2363 :             for (const auto &val :
     781        7083 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     782             :             {
     783        4726 :                 GDALArgDatasetValue v;
     784        2363 :                 v.SetFrom(val);
     785        2363 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     786        2363 :                     ->push_back(std::move(v));
     787             :             }
     788        2357 :             break;
     789             :         }
     790             :     }
     791        3327 :     m_explicitlySet = true;
     792        3327 :     return RunAllActions();
     793             : }
     794             : 
     795             : /************************************************************************/
     796             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     797             : /************************************************************************/
     798             : 
     799       16574 : bool GDALAlgorithmArg::RunAllActions()
     800             : {
     801       16574 :     if (!RunValidationActions())
     802         147 :         return false;
     803       16427 :     RunActions();
     804       16427 :     return true;
     805             : }
     806             : 
     807             : /************************************************************************/
     808             : /*                    GDALAlgorithmArg::RunActions()                    */
     809             : /************************************************************************/
     810             : 
     811       16428 : void GDALAlgorithmArg::RunActions()
     812             : {
     813       16728 :     for (const auto &f : m_actions)
     814         300 :         f();
     815       16428 : }
     816             : 
     817             : /************************************************************************/
     818             : /*                  GDALAlgorithmArg::ValidateChoice()                  */
     819             : /************************************************************************/
     820             : 
     821             : // Returns the canonical value if matching a valid choice, or empty string
     822             : // otherwise.
     823        2676 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
     824             : {
     825       15490 :     for (const std::string &choice : GetChoices())
     826             :     {
     827       15372 :         if (EQUAL(value.c_str(), choice.c_str()))
     828             :         {
     829        2558 :             return choice;
     830             :         }
     831             :     }
     832             : 
     833         190 :     for (const std::string &choice : GetHiddenChoices())
     834             :     {
     835         172 :         if (EQUAL(value.c_str(), choice.c_str()))
     836             :         {
     837         100 :             return choice;
     838             :         }
     839             :     }
     840             : 
     841          36 :     std::string expected;
     842         222 :     for (const auto &choice : GetChoices())
     843             :     {
     844         204 :         if (!expected.empty())
     845         186 :             expected += ", ";
     846         204 :         expected += '\'';
     847         204 :         expected += choice;
     848         204 :         expected += '\'';
     849             :     }
     850          18 :     if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
     851             :     {
     852           6 :         return "?";
     853             :     }
     854          24 :     CPLError(CE_Failure, CPLE_IllegalArg,
     855             :              "Invalid value '%s' for string argument '%s'. Should be "
     856             :              "one among %s.",
     857          12 :              value.c_str(), GetName().c_str(), expected.c_str());
     858          12 :     return std::string();
     859             : }
     860             : 
     861             : /************************************************************************/
     862             : /*                 GDALAlgorithmArg::ValidateIntRange()                 */
     863             : /************************************************************************/
     864             : 
     865        2752 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
     866             : {
     867        2752 :     bool ret = true;
     868             : 
     869        2752 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     870        2752 :     if (!std::isnan(minVal))
     871             :     {
     872        2080 :         if (minValIsIncluded && val < minVal)
     873             :         {
     874           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
     875             :                      "Value of argument '%s' is %d, but should be >= %d",
     876           3 :                      GetName().c_str(), val, static_cast<int>(minVal));
     877           3 :             ret = false;
     878             :         }
     879        2077 :         else if (!minValIsIncluded && val <= minVal)
     880             :         {
     881           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     882             :                      "Value of argument '%s' is %d, but should be > %d",
     883           1 :                      GetName().c_str(), val, static_cast<int>(minVal));
     884           1 :             ret = false;
     885             :         }
     886             :     }
     887             : 
     888        2752 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     889        2752 :     if (!std::isnan(maxVal))
     890             :     {
     891             : 
     892         430 :         if (maxValIsIncluded && val > maxVal)
     893             :         {
     894           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     895             :                      "Value of argument '%s' is %d, but should be <= %d",
     896           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     897           1 :             ret = false;
     898             :         }
     899         429 :         else if (!maxValIsIncluded && val >= maxVal)
     900             :         {
     901           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     902             :                      "Value of argument '%s' is %d, but should be < %d",
     903           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     904           1 :             ret = false;
     905             :         }
     906             :     }
     907             : 
     908        2752 :     return ret;
     909             : }
     910             : 
     911             : /************************************************************************/
     912             : /*                GDALAlgorithmArg::ValidateRealRange()                 */
     913             : /************************************************************************/
     914             : 
     915        2147 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
     916             : {
     917        2147 :     bool ret = true;
     918             : 
     919        2147 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     920        2147 :     if (!std::isnan(minVal))
     921             :     {
     922         216 :         if (minValIsIncluded && !(val >= minVal))
     923             :         {
     924          11 :             CPLError(CE_Failure, CPLE_IllegalArg,
     925             :                      "Value of argument '%s' is %g, but should be >= %g",
     926          11 :                      GetName().c_str(), val, minVal);
     927          11 :             ret = false;
     928             :         }
     929         205 :         else if (!minValIsIncluded && !(val > minVal))
     930             :         {
     931           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
     932             :                      "Value of argument '%s' is %g, but should be > %g",
     933           4 :                      GetName().c_str(), val, minVal);
     934           4 :             ret = false;
     935             :         }
     936             :     }
     937             : 
     938        2147 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     939        2147 :     if (!std::isnan(maxVal))
     940             :     {
     941             : 
     942          58 :         if (maxValIsIncluded && !(val <= maxVal))
     943             :         {
     944           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     945             :                      "Value of argument '%s' is %g, but should be <= %g",
     946           2 :                      GetName().c_str(), val, maxVal);
     947           2 :             ret = false;
     948             :         }
     949          56 :         else if (!maxValIsIncluded && !(val < maxVal))
     950             :         {
     951           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     952             :                      "Value of argument '%s' is %g, but should be < %g",
     953           1 :                      GetName().c_str(), val, maxVal);
     954           1 :             ret = false;
     955             :         }
     956             :     }
     957             : 
     958        2147 :     return ret;
     959             : }
     960             : 
     961             : /************************************************************************/
     962             : /*                        CheckDuplicateValues()                        */
     963             : /************************************************************************/
     964             : 
     965             : template <class T>
     966          92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
     967             :                                  const std::vector<T> &values)
     968             : {
     969         184 :     auto tmpValues = values;
     970          92 :     bool bHasDupValues = false;
     971             :     if constexpr (std::is_floating_point_v<T>)
     972             :     {
     973             :         // Avoid undefined behavior with NaN values
     974           4 :         std::sort(tmpValues.begin(), tmpValues.end(),
     975          21 :                   [](T a, T b)
     976             :                   {
     977          21 :                       if (std::isnan(a) && !std::isnan(b))
     978           3 :                           return true;
     979          18 :                       if (std::isnan(b))
     980          10 :                           return false;
     981           8 :                       return a < b;
     982             :                   });
     983             : 
     984             :         bHasDupValues =
     985           4 :             std::adjacent_find(tmpValues.begin(), tmpValues.end(),
     986           6 :                                [](T a, T b)
     987             :                                {
     988           6 :                                    if (std::isnan(a) && std::isnan(b))
     989           1 :                                        return true;
     990           5 :                                    return a == b;
     991           8 :                                }) != tmpValues.end();
     992             :     }
     993             :     else
     994             :     {
     995          88 :         std::sort(tmpValues.begin(), tmpValues.end());
     996          88 :         bHasDupValues = std::adjacent_find(tmpValues.begin(),
     997         176 :                                            tmpValues.end()) != tmpValues.end();
     998             :     }
     999          92 :     if (bHasDupValues)
    1000             :     {
    1001          10 :         CPLError(CE_Failure, CPLE_AppDefined,
    1002             :                  "'%s' must be a list of unique values.",
    1003          10 :                  arg->GetName().c_str());
    1004          10 :         return false;
    1005             :     }
    1006          82 :     return true;
    1007             : }
    1008             : 
    1009             : /************************************************************************/
    1010             : /*               GDALAlgorithmArg::RunValidationActions()               */
    1011             : /************************************************************************/
    1012             : 
    1013       36843 : bool GDALAlgorithmArg::RunValidationActions()
    1014             : {
    1015       36843 :     bool ret = true;
    1016             : 
    1017       36843 :     if (GetType() == GAAT_STRING && !GetChoices().empty())
    1018             :     {
    1019        1755 :         auto &val = Get<std::string>();
    1020        3510 :         std::string validVal = ValidateChoice(val);
    1021        1755 :         if (validVal.empty())
    1022           7 :             ret = false;
    1023             :         else
    1024        1748 :             val = std::move(validVal);
    1025             :     }
    1026       35088 :     else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
    1027             :     {
    1028         661 :         auto &values = Get<std::vector<std::string>>();
    1029        1582 :         for (std::string &val : values)
    1030             :         {
    1031        1842 :             std::string validVal = ValidateChoice(val);
    1032         921 :             if (validVal.empty())
    1033           5 :                 ret = false;
    1034             :             else
    1035         916 :                 val = std::move(validVal);
    1036             :         }
    1037             :     }
    1038             : 
    1039             :     const auto CheckMinCharCount =
    1040        1097 :         [this, &ret](const std::string &val, int nMinCharCount)
    1041             :     {
    1042        1085 :         if (val.size() < static_cast<size_t>(nMinCharCount))
    1043             :         {
    1044          12 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1045             :                      "Value of argument '%s' is '%s', but should have at least "
    1046             :                      "%d character%s",
    1047           6 :                      GetName().c_str(), val.c_str(), nMinCharCount,
    1048             :                      nMinCharCount > 1 ? "s" : "");
    1049           6 :             ret = false;
    1050             :         }
    1051       37928 :     };
    1052             : 
    1053             :     const auto CheckMaxCharCount =
    1054       13214 :         [this, &ret](const std::string &val, int nMaxCharCount)
    1055             :     {
    1056       13212 :         if (val.size() > static_cast<size_t>(nMaxCharCount))
    1057             :         {
    1058           2 :             CPLError(
    1059             :                 CE_Failure, CPLE_IllegalArg,
    1060             :                 "Value of argument '%s' is '%s', but should have no more than "
    1061             :                 "%d character%s",
    1062           1 :                 GetName().c_str(), val.c_str(), nMaxCharCount,
    1063             :                 nMaxCharCount > 1 ? "s" : "");
    1064           1 :             ret = false;
    1065             :         }
    1066       50055 :     };
    1067             : 
    1068       36843 :     switch (GetType())
    1069             :     {
    1070        2770 :         case GAAT_BOOLEAN:
    1071        2770 :             break;
    1072             : 
    1073       10383 :         case GAAT_STRING:
    1074             :         {
    1075       10383 :             const auto &val = Get<std::string>();
    1076       10383 :             const int nMinCharCount = GetMinCharCount();
    1077       10383 :             if (nMinCharCount > 0)
    1078             :             {
    1079        1003 :                 CheckMinCharCount(val, nMinCharCount);
    1080             :             }
    1081             : 
    1082       10383 :             const int nMaxCharCount = GetMaxCharCount();
    1083       10383 :             CheckMaxCharCount(val, nMaxCharCount);
    1084       10383 :             break;
    1085             :         }
    1086             : 
    1087        2357 :         case GAAT_STRING_LIST:
    1088             :         {
    1089        2357 :             const int nMinCharCount = GetMinCharCount();
    1090        2357 :             const int nMaxCharCount = GetMaxCharCount();
    1091        2357 :             const auto &values = Get<std::vector<std::string>>();
    1092        5186 :             for (const auto &val : values)
    1093             :             {
    1094        2829 :                 if (nMinCharCount > 0)
    1095          82 :                     CheckMinCharCount(val, nMinCharCount);
    1096        2829 :                 CheckMaxCharCount(val, nMaxCharCount);
    1097             :             }
    1098             : 
    1099        2431 :             if (!GetDuplicateValuesAllowed() &&
    1100          74 :                 !CheckDuplicateValues(this, values))
    1101           2 :                 ret = false;
    1102        2357 :             break;
    1103             :         }
    1104             : 
    1105        2032 :         case GAAT_INTEGER:
    1106             :         {
    1107        2032 :             ret = ValidateIntRange(Get<int>()) && ret;
    1108        2032 :             break;
    1109             :         }
    1110             : 
    1111         356 :         case GAAT_INTEGER_LIST:
    1112             :         {
    1113         356 :             const auto &values = Get<std::vector<int>>();
    1114        1076 :             for (int v : values)
    1115         720 :                 ret = ValidateIntRange(v) && ret;
    1116             : 
    1117         359 :             if (!GetDuplicateValuesAllowed() &&
    1118           3 :                 !CheckDuplicateValues(this, values))
    1119           1 :                 ret = false;
    1120         356 :             break;
    1121             :         }
    1122             : 
    1123         549 :         case GAAT_REAL:
    1124             :         {
    1125         549 :             ret = ValidateRealRange(Get<double>()) && ret;
    1126         549 :             break;
    1127             :         }
    1128             : 
    1129         588 :         case GAAT_REAL_LIST:
    1130             :         {
    1131         588 :             const auto &values = Get<std::vector<double>>();
    1132        2186 :             for (double v : values)
    1133        1598 :                 ret = ValidateRealRange(v) && ret;
    1134             : 
    1135         592 :             if (!GetDuplicateValuesAllowed() &&
    1136           4 :                 !CheckDuplicateValues(this, values))
    1137           2 :                 ret = false;
    1138         588 :             break;
    1139             :         }
    1140             : 
    1141        6057 :         case GAAT_DATASET:
    1142        6057 :             break;
    1143             : 
    1144       11751 :         case GAAT_DATASET_LIST:
    1145             :         {
    1146       11751 :             if (!GetDuplicateValuesAllowed())
    1147             :             {
    1148          11 :                 const auto &values = Get<std::vector<GDALArgDatasetValue>>();
    1149          22 :                 std::vector<std::string> aosValues;
    1150          34 :                 for (const auto &v : values)
    1151             :                 {
    1152          23 :                     const GDALDataset *poDS = v.GetDatasetRef();
    1153          23 :                     if (poDS)
    1154             :                     {
    1155          16 :                         auto poDriver = poDS->GetDriver();
    1156             :                         // The dataset name for a MEM driver is not relevant,
    1157             :                         // so use the pointer address
    1158          32 :                         if ((poDriver &&
    1159          24 :                              EQUAL(poDriver->GetDescription(), "MEM")) ||
    1160           8 :                             poDS->GetDescription()[0] == 0)
    1161             :                         {
    1162           8 :                             aosValues.push_back(CPLSPrintf("%p", poDS));
    1163             :                         }
    1164             :                         else
    1165             :                         {
    1166           8 :                             aosValues.push_back(poDS->GetDescription());
    1167             :                         }
    1168             :                     }
    1169             :                     else
    1170             :                     {
    1171           7 :                         aosValues.push_back(v.GetName());
    1172             :                     }
    1173             :                 }
    1174          11 :                 if (!CheckDuplicateValues(this, aosValues))
    1175           5 :                     ret = false;
    1176             :             }
    1177       11751 :             break;
    1178             :         }
    1179             :     }
    1180             : 
    1181       36843 :     if (GDALAlgorithmArgTypeIsList(GetType()))
    1182             :     {
    1183       15052 :         int valueCount = 0;
    1184       15052 :         if (GetType() == GAAT_STRING_LIST)
    1185             :         {
    1186        2357 :             valueCount =
    1187        2357 :                 static_cast<int>(Get<std::vector<std::string>>().size());
    1188             :         }
    1189       12695 :         else if (GetType() == GAAT_INTEGER_LIST)
    1190             :         {
    1191         356 :             valueCount = static_cast<int>(Get<std::vector<int>>().size());
    1192             :         }
    1193       12339 :         else if (GetType() == GAAT_REAL_LIST)
    1194             :         {
    1195         588 :             valueCount = static_cast<int>(Get<std::vector<double>>().size());
    1196             :         }
    1197       11751 :         else if (GetType() == GAAT_DATASET_LIST)
    1198             :         {
    1199       11751 :             valueCount = static_cast<int>(
    1200       11751 :                 Get<std::vector<GDALArgDatasetValue>>().size());
    1201             :         }
    1202             : 
    1203       15052 :         if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
    1204             :         {
    1205          14 :             ReportError(CE_Failure, CPLE_AppDefined,
    1206             :                         "%d value%s been specified for argument '%s', "
    1207             :                         "whereas exactly %d %s expected.",
    1208             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1209           7 :                         GetName().c_str(), GetMinCount(),
    1210           7 :                         GetMinCount() > 1 ? "were" : "was");
    1211           7 :             ret = false;
    1212             :         }
    1213       15045 :         else if (valueCount < GetMinCount())
    1214             :         {
    1215           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1216             :                         "Only %d value%s been specified for argument '%s', "
    1217             :                         "whereas at least %d %s expected.",
    1218             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1219           3 :                         GetName().c_str(), GetMinCount(),
    1220           3 :                         GetMinCount() > 1 ? "were" : "was");
    1221           3 :             ret = false;
    1222             :         }
    1223       15042 :         else if (valueCount > GetMaxCount())
    1224             :         {
    1225           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    1226             :                         "%d value%s been specified for argument '%s', "
    1227             :                         "whereas at most %d %s expected.",
    1228             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1229           1 :                         GetName().c_str(), GetMaxCount(),
    1230           1 :                         GetMaxCount() > 1 ? "were" : "was");
    1231           1 :             ret = false;
    1232             :         }
    1233             :     }
    1234             : 
    1235       36843 :     if (ret)
    1236             :     {
    1237       43803 :         for (const auto &f : m_validationActions)
    1238             :         {
    1239        7024 :             if (!f())
    1240          92 :                 ret = false;
    1241             :         }
    1242             :     }
    1243             : 
    1244       36843 :     return ret;
    1245             : }
    1246             : 
    1247             : /************************************************************************/
    1248             : /*                   GDALAlgorithmArg::ReportError()                    */
    1249             : /************************************************************************/
    1250             : 
    1251          11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1252             :                                    const char *fmt, ...) const
    1253             : {
    1254             :     va_list args;
    1255          11 :     va_start(args, fmt);
    1256          11 :     if (m_owner)
    1257             :     {
    1258          11 :         m_owner->ReportError(eErrClass, err_no, "%s",
    1259          22 :                              CPLString().vPrintf(fmt, args).c_str());
    1260             :     }
    1261             :     else
    1262             :     {
    1263           0 :         CPLError(eErrClass, err_no, "%s",
    1264           0 :                  CPLString().vPrintf(fmt, args).c_str());
    1265             :     }
    1266          11 :     va_end(args);
    1267          11 : }
    1268             : 
    1269             : /************************************************************************/
    1270             : /*                 GDALAlgorithmArg::GetEscapedString()                 */
    1271             : /************************************************************************/
    1272             : 
    1273             : /* static */
    1274         181 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
    1275             : {
    1276         199 :     if (s.find_first_of("\" \\,") != std::string::npos &&
    1277           9 :         !(s.size() > 4 &&
    1278           9 :           s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
    1279           2 :           s[1] == ' ' && s[s.size() - 2] == ' ' &&
    1280           2 :           s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
    1281             :     {
    1282          14 :         return std::string("\"")
    1283             :             .append(
    1284          14 :                 CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
    1285           7 :             .append("\"");
    1286             :     }
    1287             :     else
    1288             :     {
    1289         174 :         return s;
    1290             :     }
    1291             : }
    1292             : 
    1293             : /************************************************************************/
    1294             : /*                    GDALAlgorithmArg::Serialize()                     */
    1295             : /************************************************************************/
    1296             : 
    1297          39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
    1298             :                                  bool absolutePath) const
    1299             : {
    1300          39 :     serializedArg.clear();
    1301             : 
    1302          39 :     if (!IsExplicitlySet())
    1303             :     {
    1304           0 :         return false;
    1305             :     }
    1306             : 
    1307          78 :     std::string ret = "--";
    1308          39 :     ret += GetName();
    1309          39 :     if (GetType() == GAAT_BOOLEAN)
    1310             :     {
    1311           0 :         serializedArg = std::move(ret);
    1312           0 :         return true;
    1313             :     }
    1314             : 
    1315           5 :     const auto AddListValueSeparator = [this, &ret]()
    1316             :     {
    1317           1 :         if (GetPackedValuesAllowed())
    1318             :         {
    1319           0 :             ret += ',';
    1320             :         }
    1321             :         else
    1322             :         {
    1323           1 :             ret += " --";
    1324           1 :             ret += GetName();
    1325           1 :             ret += ' ';
    1326             :         }
    1327          40 :     };
    1328             : 
    1329           0 :     const auto MakeAbsolutePath = [](const std::string &filename)
    1330             :     {
    1331             :         VSIStatBufL sStat;
    1332           0 :         if (VSIStatL(filename.c_str(), &sStat) != 0 ||
    1333           0 :             !CPLIsFilenameRelative(filename.c_str()))
    1334           0 :             return filename;
    1335           0 :         char *pszCWD = CPLGetCurrentDir();
    1336           0 :         if (!pszCWD)
    1337           0 :             return filename;
    1338             :         const auto absPath =
    1339           0 :             CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
    1340           0 :         CPLFree(pszCWD);
    1341           0 :         return absPath;
    1342             :     };
    1343             : 
    1344          39 :     ret += ' ';
    1345          39 :     switch (GetType())
    1346             :     {
    1347           0 :         case GAAT_BOOLEAN:
    1348           0 :             break;
    1349           8 :         case GAAT_STRING:
    1350             :         {
    1351           8 :             const auto &val = Get<std::string>();
    1352           8 :             ret += GetEscapedString(val);
    1353           8 :             break;
    1354             :         }
    1355           0 :         case GAAT_INTEGER:
    1356             :         {
    1357           0 :             ret += CPLSPrintf("%d", Get<int>());
    1358           0 :             break;
    1359             :         }
    1360           0 :         case GAAT_REAL:
    1361             :         {
    1362           0 :             ret += CPLSPrintf("%.17g", Get<double>());
    1363           0 :             break;
    1364             :         }
    1365           2 :         case GAAT_DATASET:
    1366             :         {
    1367           2 :             const auto &val = Get<GDALArgDatasetValue>();
    1368           2 :             const auto &str = val.GetName();
    1369           2 :             if (str.empty())
    1370             :             {
    1371           0 :                 return false;
    1372             :             }
    1373           2 :             ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
    1374           2 :             break;
    1375             :         }
    1376           4 :         case GAAT_STRING_LIST:
    1377             :         {
    1378           4 :             const auto &vals = Get<std::vector<std::string>>();
    1379           8 :             for (size_t i = 0; i < vals.size(); ++i)
    1380             :             {
    1381           4 :                 if (i > 0)
    1382           1 :                     AddListValueSeparator();
    1383           4 :                 ret += GetEscapedString(vals[i]);
    1384             :             }
    1385           4 :             break;
    1386             :         }
    1387           0 :         case GAAT_INTEGER_LIST:
    1388             :         {
    1389           0 :             const auto &vals = Get<std::vector<int>>();
    1390           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1391             :             {
    1392           0 :                 if (i > 0)
    1393           0 :                     AddListValueSeparator();
    1394           0 :                 ret += CPLSPrintf("%d", vals[i]);
    1395             :             }
    1396           0 :             break;
    1397             :         }
    1398           0 :         case GAAT_REAL_LIST:
    1399             :         {
    1400           0 :             const auto &vals = Get<std::vector<double>>();
    1401           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1402             :             {
    1403           0 :                 if (i > 0)
    1404           0 :                     AddListValueSeparator();
    1405           0 :                 ret += CPLSPrintf("%.17g", vals[i]);
    1406             :             }
    1407           0 :             break;
    1408             :         }
    1409          25 :         case GAAT_DATASET_LIST:
    1410             :         {
    1411          25 :             const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
    1412          49 :             for (size_t i = 0; i < vals.size(); ++i)
    1413             :             {
    1414          25 :                 if (i > 0)
    1415           0 :                     AddListValueSeparator();
    1416          25 :                 const auto &val = vals[i];
    1417          25 :                 const auto &str = val.GetName();
    1418          25 :                 if (str.empty())
    1419             :                 {
    1420           1 :                     return false;
    1421             :                 }
    1422          48 :                 ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
    1423          24 :                                                      : str);
    1424             :             }
    1425          24 :             break;
    1426             :         }
    1427             :     }
    1428             : 
    1429          38 :     serializedArg = std::move(ret);
    1430          38 :     return true;
    1431             : }
    1432             : 
    1433             : /************************************************************************/
    1434             : /*                  ~GDALInConstructionAlgorithmArg()                   */
    1435             : /************************************************************************/
    1436             : 
    1437             : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
    1438             : 
    1439             : /************************************************************************/
    1440             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
    1441             : /************************************************************************/
    1442             : 
    1443             : GDALInConstructionAlgorithmArg &
    1444       64550 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
    1445             : {
    1446       64550 :     m_decl.AddAlias(alias);
    1447       64550 :     if (m_owner)
    1448       64550 :         m_owner->AddAliasFor(this, alias);
    1449       64550 :     return *this;
    1450             : }
    1451             : 
    1452             : /************************************************************************/
    1453             : /*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
    1454             : /************************************************************************/
    1455             : 
    1456             : GDALInConstructionAlgorithmArg &
    1457       17405 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
    1458             : {
    1459       17405 :     m_decl.AddHiddenAlias(alias);
    1460       17405 :     if (m_owner)
    1461       17405 :         m_owner->AddAliasFor(this, alias);
    1462       17405 :     return *this;
    1463             : }
    1464             : 
    1465             : /************************************************************************/
    1466             : /*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
    1467             : /************************************************************************/
    1468             : 
    1469             : GDALInConstructionAlgorithmArg &
    1470          48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
    1471             : {
    1472          48 :     m_decl.AddShortNameAlias(shortNameAlias);
    1473          48 :     if (m_owner)
    1474          48 :         m_owner->AddShortNameAliasFor(this, shortNameAlias);
    1475          48 :     return *this;
    1476             : }
    1477             : 
    1478             : /************************************************************************/
    1479             : /*           GDALInConstructionAlgorithmArg::SetPositional()            */
    1480             : /************************************************************************/
    1481             : 
    1482       22269 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
    1483             : {
    1484       22269 :     m_decl.SetPositional();
    1485       22269 :     if (m_owner)
    1486       22269 :         m_owner->SetPositional(this);
    1487       22269 :     return *this;
    1488             : }
    1489             : 
    1490             : /************************************************************************/
    1491             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
    1492             : /************************************************************************/
    1493             : 
    1494        1323 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
    1495        2646 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
    1496        1323 :       m_nameSet(true)
    1497             : {
    1498        1323 :     if (m_poDS)
    1499        1323 :         m_poDS->Reference();
    1500        1323 : }
    1501             : 
    1502             : /************************************************************************/
    1503             : /*                      GDALArgDatasetValue::Set()                      */
    1504             : /************************************************************************/
    1505             : 
    1506        2352 : void GDALArgDatasetValue::Set(const std::string &name)
    1507             : {
    1508        2352 :     Close();
    1509        2352 :     m_name = name;
    1510        2352 :     m_nameSet = true;
    1511        2352 :     if (m_ownerArg)
    1512        2347 :         m_ownerArg->NotifyValueSet();
    1513        2352 : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                      GDALArgDatasetValue::Set()                      */
    1517             : /************************************************************************/
    1518             : 
    1519        2092 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
    1520             : {
    1521        2092 :     Close();
    1522        2092 :     m_poDS = poDS.release();
    1523        2092 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1524        2092 :     m_nameSet = true;
    1525        2092 :     if (m_ownerArg)
    1526        1933 :         m_ownerArg->NotifyValueSet();
    1527        2092 : }
    1528             : 
    1529             : /************************************************************************/
    1530             : /*                      GDALArgDatasetValue::Set()                      */
    1531             : /************************************************************************/
    1532             : 
    1533        8336 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
    1534             : {
    1535        8336 :     Close();
    1536        8336 :     m_poDS = poDS;
    1537        8336 :     if (m_poDS)
    1538        7451 :         m_poDS->Reference();
    1539        8336 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1540        8336 :     m_nameSet = true;
    1541        8336 :     if (m_ownerArg)
    1542        3442 :         m_ownerArg->NotifyValueSet();
    1543        8336 : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*                    GDALArgDatasetValue::SetFrom()                    */
    1547             : /************************************************************************/
    1548             : 
    1549        3375 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
    1550             : {
    1551        3375 :     Close();
    1552        3375 :     m_name = other.m_name;
    1553        3375 :     m_nameSet = other.m_nameSet;
    1554        3375 :     m_poDS = other.m_poDS;
    1555        3375 :     if (m_poDS)
    1556        2349 :         m_poDS->Reference();
    1557        3375 : }
    1558             : 
    1559             : /************************************************************************/
    1560             : /*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
    1561             : /************************************************************************/
    1562             : 
    1563       32437 : GDALArgDatasetValue::~GDALArgDatasetValue()
    1564             : {
    1565       32437 :     Close();
    1566       32437 : }
    1567             : 
    1568             : /************************************************************************/
    1569             : /*                     GDALArgDatasetValue::Close()                     */
    1570             : /************************************************************************/
    1571             : 
    1572       54133 : bool GDALArgDatasetValue::Close()
    1573             : {
    1574       54133 :     bool ret = true;
    1575       54133 :     if (m_poDS && m_poDS->Dereference() == 0)
    1576             :     {
    1577        3479 :         ret = m_poDS->Close() == CE_None;
    1578        3479 :         delete m_poDS;
    1579             :     }
    1580       54133 :     m_poDS = nullptr;
    1581       54133 :     return ret;
    1582             : }
    1583             : 
    1584             : /************************************************************************/
    1585             : /*                   GDALArgDatasetValue::operator=()                   */
    1586             : /************************************************************************/
    1587             : 
    1588           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
    1589             : {
    1590           2 :     Close();
    1591           2 :     m_poDS = other.m_poDS;
    1592           2 :     m_name = other.m_name;
    1593           2 :     m_nameSet = other.m_nameSet;
    1594           2 :     other.m_poDS = nullptr;
    1595           2 :     other.m_name.clear();
    1596           2 :     other.m_nameSet = false;
    1597           2 :     return *this;
    1598             : }
    1599             : 
    1600             : /************************************************************************/
    1601             : /*                  GDALArgDatasetValue::GetDataset()                   */
    1602             : /************************************************************************/
    1603             : 
    1604        1069 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
    1605             : {
    1606        1069 :     if (m_poDS)
    1607        1068 :         m_poDS->Reference();
    1608        1069 :     return m_poDS;
    1609             : }
    1610             : 
    1611             : /************************************************************************/
    1612             : /*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
    1613             : /************************************************************************/
    1614             : 
    1615        3240 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
    1616        3240 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
    1617             : {
    1618        3240 :     other.m_poDS = nullptr;
    1619        3240 :     other.m_name.clear();
    1620        3240 : }
    1621             : 
    1622             : /************************************************************************/
    1623             : /*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
    1624             : /************************************************************************/
    1625             : 
    1626        3395 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
    1627             :     bool noneAllowed, const std::vector<std::string> &specialValues)
    1628             : {
    1629        3395 :     if (GetType() != GAAT_STRING)
    1630             :     {
    1631           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1632             :                  "SetIsCRSArg() can only be called on a String argument");
    1633           1 :         return *this;
    1634             :     }
    1635             :     AddValidationAction(
    1636         763 :         [this, noneAllowed, specialValues]()
    1637             :         {
    1638             :             const std::string &osVal =
    1639             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
    1640         378 :                     ->Get<std::string>();
    1641         378 :             if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
    1642           0 :                 return true;
    1643             : 
    1644         743 :             if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
    1645         365 :                 std::find(specialValues.begin(), specialValues.end(), osVal) ==
    1646         743 :                     specialValues.end())
    1647             :             {
    1648         357 :                 OGRSpatialReference oSRS;
    1649         357 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
    1650             :                 {
    1651           7 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
    1652             :                                          "Invalid value for '%s' argument",
    1653           7 :                                          GetName().c_str());
    1654           7 :                     return false;
    1655             :                 }
    1656             :             }
    1657         371 :             return true;
    1658        3394 :         });
    1659             : 
    1660             :     SetAutoCompleteFunction(
    1661          44 :         [this, noneAllowed, specialValues](const std::string &currentValue)
    1662             :         {
    1663          11 :             bool bIsRaster = false;
    1664          11 :             OGREnvelope sDatasetLongLatEnv;
    1665          22 :             std::string osCelestialBodyName;
    1666          11 :             if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
    1667             :             {
    1668          11 :                 auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
    1669          11 :                 if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    1670             :                 {
    1671             :                     auto &val =
    1672          11 :                         inputArg->Get<std::vector<GDALArgDatasetValue>>();
    1673          11 :                     if (val.size() == 1)
    1674             :                     {
    1675           4 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1676             :                         auto poDS = std::unique_ptr<GDALDataset>(
    1677           4 :                             GDALDataset::Open(val[0].GetName().c_str()));
    1678           2 :                         if (poDS)
    1679             :                         {
    1680           2 :                             bIsRaster = poDS->GetRasterCount() != 0;
    1681           2 :                             if (auto poCRS = poDS->GetSpatialRef())
    1682             :                             {
    1683             :                                 const char *pszCelestialBodyName =
    1684           2 :                                     poCRS->GetCelestialBodyName();
    1685           2 :                                 if (pszCelestialBodyName)
    1686           2 :                                     osCelestialBodyName = pszCelestialBodyName;
    1687             : 
    1688           2 :                                 if (!pszCelestialBodyName ||
    1689           2 :                                     !EQUAL(pszCelestialBodyName, "Earth"))
    1690             :                                 {
    1691           0 :                                     OGRSpatialReference oLongLat;
    1692           0 :                                     oLongLat.CopyGeogCSFrom(poCRS);
    1693           0 :                                     oLongLat.SetAxisMappingStrategy(
    1694             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    1695           0 :                                     poDS->GetExtent(&sDatasetLongLatEnv,
    1696           0 :                                                     &oLongLat);
    1697             :                                 }
    1698             :                                 else
    1699             :                                 {
    1700           2 :                                     poDS->GetExtentWGS84LongLat(
    1701           2 :                                         &sDatasetLongLatEnv);
    1702             :                                 }
    1703             :                             }
    1704             :                         }
    1705             :                     }
    1706             :                 }
    1707             :             }
    1708             : 
    1709             :             const auto IsCRSCompatible =
    1710       42959 :                 [bIsRaster, &sDatasetLongLatEnv,
    1711       73695 :                  &osCelestialBodyName](const OSRCRSInfo *crsInfo)
    1712             :             {
    1713       42959 :                 if (!sDatasetLongLatEnv.IsInit())
    1714       30685 :                     return true;
    1715       24108 :                 return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
    1716       11834 :                        !(bIsRaster &&
    1717        5917 :                          crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
    1718       11652 :                        crsInfo->dfWestLongitudeDeg <
    1719       11652 :                            crsInfo->dfEastLongitudeDeg &&
    1720       11517 :                        sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
    1721        5618 :                        sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
    1722         615 :                        sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
    1723       24437 :                        sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
    1724         329 :                        ((!osCelestialBodyName.empty() &&
    1725         658 :                          crsInfo->pszCelestialBodyName &&
    1726         329 :                          osCelestialBodyName ==
    1727         329 :                              crsInfo->pszCelestialBodyName) ||
    1728           0 :                         (osCelestialBodyName.empty() &&
    1729       12274 :                          !crsInfo->pszCelestialBodyName));
    1730          11 :             };
    1731             : 
    1732          11 :             std::vector<std::string> oRet;
    1733          11 :             if (noneAllowed)
    1734           0 :                 oRet.push_back("none");
    1735          11 :             oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
    1736          11 :             if (!currentValue.empty())
    1737             :             {
    1738             :                 const CPLStringList aosTokens(
    1739          14 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
    1740           7 :                 int nCount = 0;
    1741             :                 std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
    1742             :                     pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
    1743             :                                                            nullptr, &nCount),
    1744          14 :                              OSRDestroyCRSInfoList);
    1745          14 :                 std::string osCode;
    1746             : 
    1747          14 :                 std::vector<const OSRCRSInfo *> candidates;
    1748       46270 :                 for (int i = 0; i < nCount; ++i)
    1749             :                 {
    1750       46263 :                     const auto *entry = (pCRSList.get())[i];
    1751       46263 :                     if (!entry->bDeprecated && IsCRSCompatible(entry))
    1752             :                     {
    1753       49425 :                         if (aosTokens.size() == 1 ||
    1754       18411 :                             STARTS_WITH(entry->pszCode, aosTokens[1]))
    1755             :                         {
    1756       12666 :                             if (candidates.empty())
    1757           7 :                                 osCode = entry->pszCode;
    1758       12666 :                             candidates.push_back(entry);
    1759             :                         }
    1760             :                     }
    1761             :                 }
    1762           7 :                 if (candidates.size() == 1)
    1763             :                 {
    1764           1 :                     oRet.push_back(std::move(osCode));
    1765             :                 }
    1766             :                 else
    1767             :                 {
    1768           6 :                     if (sDatasetLongLatEnv.IsInit())
    1769             :                     {
    1770           2 :                         std::sort(
    1771             :                             candidates.begin(), candidates.end(),
    1772        2999 :                             [](const OSRCRSInfo *a, const OSRCRSInfo *b)
    1773             :                             {
    1774        2999 :                                 const double dfXa =
    1775        2999 :                                     a->dfWestLongitudeDeg >
    1776        2999 :                                             a->dfEastLongitudeDeg
    1777        2999 :                                         ? a->dfWestLongitudeDeg -
    1778           0 :                                               a->dfEastLongitudeDeg
    1779        2999 :                                         : (180 - a->dfWestLongitudeDeg) +
    1780        2999 :                                               (a->dfEastLongitudeDeg - -180);
    1781        2999 :                                 const double dfYa = a->dfNorthLatitudeDeg -
    1782        2999 :                                                     a->dfSouthLatitudeDeg;
    1783        2999 :                                 const double dfXb =
    1784        2999 :                                     b->dfWestLongitudeDeg >
    1785        2999 :                                             b->dfEastLongitudeDeg
    1786        2999 :                                         ? b->dfWestLongitudeDeg -
    1787           0 :                                               b->dfEastLongitudeDeg
    1788        2999 :                                         : (180 - b->dfWestLongitudeDeg) +
    1789        2999 :                                               (b->dfEastLongitudeDeg - -180);
    1790        2999 :                                 const double dfYb = b->dfNorthLatitudeDeg -
    1791        2999 :                                                     b->dfSouthLatitudeDeg;
    1792        2999 :                                 const double diffArea =
    1793        2999 :                                     dfXa * dfYa - dfXb * dfYb;
    1794        2999 :                                 if (diffArea < 0)
    1795         279 :                                     return true;
    1796        2720 :                                 if (diffArea == 0)
    1797             :                                 {
    1798        2506 :                                     if (std::string_view(a->pszName) ==
    1799        2506 :                                         b->pszName)
    1800             :                                     {
    1801          57 :                                         if (a->eType ==
    1802          13 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D &&
    1803          13 :                                             b->eType !=
    1804             :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1805          13 :                                             return true;
    1806          44 :                                         if (a->eType ==
    1807          32 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_3D &&
    1808          32 :                                             b->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1809           9 :                                             return true;
    1810          35 :                                         return false;
    1811             :                                     }
    1812        4898 :                                     return std::string_view(a->pszCode) <
    1813        4898 :                                            b->pszCode;
    1814             :                                 }
    1815         214 :                                 return false;
    1816             :                             });
    1817             :                     }
    1818             : 
    1819       12671 :                     for (const auto *entry : candidates)
    1820             :                     {
    1821       25330 :                         std::string val = std::string(entry->pszCode)
    1822       12665 :                                               .append(" -- ")
    1823       25330 :                                               .append(entry->pszName);
    1824       12665 :                         if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1825        1294 :                             val.append(" (geographic 2D)");
    1826       11371 :                         else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
    1827         446 :                             val.append(" (geographic 3D)");
    1828       10925 :                         else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1829         397 :                             val.append(" (geocentric)");
    1830       12665 :                         oRet.push_back(std::move(val));
    1831             :                     }
    1832             :                 }
    1833             :             }
    1834          11 :             if (currentValue.empty() || oRet.empty())
    1835             :             {
    1836             :                 const CPLStringList aosAuthorities(
    1837           8 :                     OSRGetAuthorityListFromDatabase());
    1838          24 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
    1839             :                 {
    1840          20 :                     int nCount = 0;
    1841          20 :                     OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
    1842             :                         pszAuth, nullptr, &nCount));
    1843          20 :                     if (nCount)
    1844          16 :                         oRet.push_back(std::string(pszAuth).append(":"));
    1845             :                 }
    1846             :             }
    1847          22 :             return oRet;
    1848        3394 :         });
    1849             : 
    1850        3394 :     return *this;
    1851             : }
    1852             : 
    1853             : /************************************************************************/
    1854             : /*                    GDALAlgorithm::GDALAlgorithm()                    */
    1855             : /************************************************************************/
    1856             : 
    1857       22577 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
    1858             :                              const std::string &description,
    1859       22577 :                              const std::string &helpURL)
    1860             :     : m_name(name), m_description(description), m_helpURL(helpURL),
    1861       44804 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
    1862       22577 :                         ? "https://gdal.org" + m_helpURL
    1863       67126 :                         : m_helpURL)
    1864             : {
    1865             :     auto &helpArg =
    1866             :         AddArg("help", 'h', _("Display help message and exit"),
    1867       45154 :                &m_helpRequested)
    1868       22577 :             .SetHiddenForAPI()
    1869       45154 :             .SetCategory(GAAC_COMMON)
    1870          14 :             .AddAction([this]()
    1871       22577 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1872             :     auto &helpDocArg =
    1873             :         AddArg("help-doc", 0,
    1874             :                _("Display help message for use by documentation"),
    1875       45154 :                &m_helpDocRequested)
    1876       22577 :             .SetHidden()
    1877          12 :             .AddAction([this]()
    1878       22577 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1879             :     auto &jsonUsageArg =
    1880             :         AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
    1881       45154 :                &m_JSONUsageRequested)
    1882       22577 :             .SetHiddenForAPI()
    1883       45154 :             .SetCategory(GAAC_COMMON)
    1884           4 :             .AddAction([this]()
    1885       22577 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1886       45154 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
    1887       45154 :         .SetMetaVar("<KEY>=<VALUE>")
    1888       22577 :         .SetHiddenForAPI()
    1889       45154 :         .SetCategory(GAAC_COMMON)
    1890             :         .AddAction(
    1891           2 :             [this]()
    1892             :             {
    1893           2 :                 ReportError(
    1894             :                     CE_Warning, CPLE_AppDefined,
    1895             :                     "Configuration options passed with the 'config' argument "
    1896             :                     "are ignored");
    1897       22577 :             });
    1898             : 
    1899       22577 :     AddValidationAction(
    1900       14019 :         [this, &helpArg, &helpDocArg, &jsonUsageArg]()
    1901             :         {
    1902        7231 :             if (!m_calledFromCommandLine && m_specialActionRequested)
    1903             :             {
    1904           0 :                 for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
    1905             :                 {
    1906           0 :                     if (arg->IsExplicitlySet())
    1907             :                     {
    1908           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    1909             :                                     "'%s' argument only available when called "
    1910             :                                     "from command line",
    1911           0 :                                     arg->GetName().c_str());
    1912           0 :                         return false;
    1913             :                     }
    1914             :                 }
    1915             :             }
    1916        7231 :             return true;
    1917             :         });
    1918       22577 : }
    1919             : 
    1920             : /************************************************************************/
    1921             : /*                   GDALAlgorithm::~GDALAlgorithm()                    */
    1922             : /************************************************************************/
    1923             : 
    1924             : GDALAlgorithm::~GDALAlgorithm() = default;
    1925             : 
    1926             : /************************************************************************/
    1927             : /*                    GDALAlgorithm::ParseArgument()                    */
    1928             : /************************************************************************/
    1929             : 
    1930        3272 : bool GDALAlgorithm::ParseArgument(
    1931             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
    1932             :     std::map<
    1933             :         GDALAlgorithmArg *,
    1934             :         std::variant<std::vector<std::string>, std::vector<int>,
    1935             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1936             :         &inConstructionValues)
    1937             : {
    1938             :     const bool isListArg =
    1939        3272 :         GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
    1940        3272 :     if (arg->IsExplicitlySet() && !isListArg)
    1941             :     {
    1942             :         // Hack for "gdal info" to be able to pass an opened raster dataset
    1943             :         // by "gdal raster info" to the "gdal vector info" algorithm.
    1944           4 :         if (arg->SkipIfAlreadySet())
    1945             :         {
    1946           1 :             arg->SetSkipIfAlreadySet(false);
    1947           1 :             return true;
    1948             :         }
    1949             : 
    1950           3 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1951             :                     "Argument '%s' has already been specified.", name.c_str());
    1952           3 :         return false;
    1953             :     }
    1954             : 
    1955        3335 :     if (!arg->GetRepeatedArgAllowed() &&
    1956          67 :         cpl::contains(inConstructionValues, arg))
    1957             :     {
    1958           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1959             :                     "Argument '%s' has already been specified.", name.c_str());
    1960           1 :         return false;
    1961             :     }
    1962             : 
    1963        3267 :     switch (arg->GetType())
    1964             :     {
    1965         321 :         case GAAT_BOOLEAN:
    1966             :         {
    1967         321 :             if (value.empty() || value == "true")
    1968         319 :                 return arg->Set(true);
    1969           2 :             else if (value == "false")
    1970           1 :                 return arg->Set(false);
    1971             :             else
    1972             :             {
    1973           1 :                 ReportError(
    1974             :                     CE_Failure, CPLE_IllegalArg,
    1975             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
    1976             :                     "'true' or 'false'.",
    1977             :                     value.c_str(), name.c_str());
    1978           1 :                 return false;
    1979             :             }
    1980             :         }
    1981             : 
    1982         819 :         case GAAT_STRING:
    1983             :         {
    1984         819 :             return arg->Set(value);
    1985             :         }
    1986             : 
    1987         356 :         case GAAT_INTEGER:
    1988             :         {
    1989         356 :             errno = 0;
    1990         356 :             char *endptr = nullptr;
    1991         356 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
    1992         355 :             if (errno == 0 && endptr &&
    1993         711 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
    1994             :                 val <= INT_MAX)
    1995             :             {
    1996         353 :                 return arg->Set(static_cast<int>(val));
    1997             :             }
    1998             :             else
    1999             :             {
    2000           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    2001             :                             "Expected integer value for argument '%s', "
    2002             :                             "but got '%s'.",
    2003             :                             name.c_str(), value.c_str());
    2004           3 :                 return false;
    2005             :             }
    2006             :         }
    2007             : 
    2008          32 :         case GAAT_REAL:
    2009             :         {
    2010          32 :             char *endptr = nullptr;
    2011          32 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
    2012          32 :             if (endptr != value.c_str() + value.size())
    2013             :             {
    2014           1 :                 ReportError(
    2015             :                     CE_Failure, CPLE_IllegalArg,
    2016             :                     "Expected real value for argument '%s', but got '%s'.",
    2017             :                     name.c_str(), value.c_str());
    2018           1 :                 return false;
    2019             :             }
    2020          31 :             return arg->Set(dfValue);
    2021             :         }
    2022             : 
    2023         584 :         case GAAT_DATASET:
    2024             :         {
    2025         584 :             return arg->SetDatasetName(value);
    2026             :         }
    2027             : 
    2028         260 :         case GAAT_STRING_LIST:
    2029             :         {
    2030             :             const CPLStringList aosTokens(
    2031         260 :                 arg->GetPackedValuesAllowed()
    2032         173 :                     ? CSLTokenizeString2(value.c_str(), ",",
    2033             :                                          CSLT_HONOURSTRINGS |
    2034             :                                              CSLT_PRESERVEQUOTES)
    2035         433 :                     : CSLAddString(nullptr, value.c_str()));
    2036         260 :             if (!cpl::contains(inConstructionValues, arg))
    2037             :             {
    2038         236 :                 inConstructionValues[arg] = std::vector<std::string>();
    2039             :             }
    2040             :             auto &valueVector =
    2041         260 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
    2042         552 :             for (const char *v : aosTokens)
    2043             :             {
    2044         292 :                 valueVector.push_back(v);
    2045             :             }
    2046         260 :             if (arg->GetMaxCount() == 1)
    2047             :             {
    2048           4 :                 bool ret = arg->Set(std::move(valueVector));
    2049           4 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2050           4 :                 return ret;
    2051             :             }
    2052             : 
    2053         256 :             break;
    2054             :         }
    2055             : 
    2056          65 :         case GAAT_INTEGER_LIST:
    2057             :         {
    2058             :             const CPLStringList aosTokens(
    2059          65 :                 arg->GetPackedValuesAllowed()
    2060          65 :                     ? CSLTokenizeString2(
    2061             :                           value.c_str(), ",",
    2062             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2063             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2064         130 :                     : CSLAddString(nullptr, value.c_str()));
    2065          65 :             if (!cpl::contains(inConstructionValues, arg))
    2066             :             {
    2067          61 :                 inConstructionValues[arg] = std::vector<int>();
    2068             :             }
    2069             :             auto &valueVector =
    2070          65 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    2071         197 :             for (const char *v : aosTokens)
    2072             :             {
    2073         138 :                 errno = 0;
    2074         138 :                 char *endptr = nullptr;
    2075         138 :                 const auto val = std::strtol(v, &endptr, 10);
    2076         138 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    2077         134 :                     val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
    2078             :                 {
    2079         132 :                     valueVector.push_back(static_cast<int>(val));
    2080             :                 }
    2081             :                 else
    2082             :                 {
    2083           6 :                     ReportError(
    2084             :                         CE_Failure, CPLE_IllegalArg,
    2085             :                         "Expected list of integer value for argument '%s', "
    2086             :                         "but got '%s'.",
    2087             :                         name.c_str(), value.c_str());
    2088           6 :                     return false;
    2089             :                 }
    2090             :             }
    2091          59 :             if (arg->GetMaxCount() == 1)
    2092             :             {
    2093           2 :                 bool ret = arg->Set(std::move(valueVector));
    2094           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2095           2 :                 return ret;
    2096             :             }
    2097             : 
    2098          57 :             break;
    2099             :         }
    2100             : 
    2101         102 :         case GAAT_REAL_LIST:
    2102             :         {
    2103             :             const CPLStringList aosTokens(
    2104         102 :                 arg->GetPackedValuesAllowed()
    2105         102 :                     ? CSLTokenizeString2(
    2106             :                           value.c_str(), ",",
    2107             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2108             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2109         204 :                     : CSLAddString(nullptr, value.c_str()));
    2110         102 :             if (!cpl::contains(inConstructionValues, arg))
    2111             :             {
    2112         100 :                 inConstructionValues[arg] = std::vector<double>();
    2113             :             }
    2114             :             auto &valueVector =
    2115         102 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    2116         407 :             for (const char *v : aosTokens)
    2117             :             {
    2118         309 :                 char *endptr = nullptr;
    2119         309 :                 double dfValue = CPLStrtod(v, &endptr);
    2120         309 :                 if (strlen(v) == 0 || endptr != v + strlen(v))
    2121             :                 {
    2122           4 :                     ReportError(
    2123             :                         CE_Failure, CPLE_IllegalArg,
    2124             :                         "Expected list of real value for argument '%s', "
    2125             :                         "but got '%s'.",
    2126             :                         name.c_str(), value.c_str());
    2127           4 :                     return false;
    2128             :                 }
    2129         305 :                 valueVector.push_back(dfValue);
    2130             :             }
    2131          98 :             if (arg->GetMaxCount() == 1)
    2132             :             {
    2133           2 :                 bool ret = arg->Set(std::move(valueVector));
    2134           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2135           2 :                 return ret;
    2136             :             }
    2137             : 
    2138          96 :             break;
    2139             :         }
    2140             : 
    2141         728 :         case GAAT_DATASET_LIST:
    2142             :         {
    2143         728 :             if (!cpl::contains(inConstructionValues, arg))
    2144             :             {
    2145         720 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    2146             :             }
    2147             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    2148         728 :                 inConstructionValues[arg]);
    2149         728 :             if (!value.empty() && value[0] == '{' && value.back() == '}')
    2150             :             {
    2151          12 :                 valueVector.push_back(GDALArgDatasetValue(value));
    2152             :             }
    2153             :             else
    2154             :             {
    2155             :                 const CPLStringList aosTokens(
    2156         716 :                     arg->GetPackedValuesAllowed()
    2157           6 :                         ? CSLTokenizeString2(value.c_str(), ",",
    2158             :                                              CSLT_HONOURSTRINGS |
    2159             :                                                  CSLT_STRIPLEADSPACES)
    2160        1438 :                         : CSLAddString(nullptr, value.c_str()));
    2161        1435 :                 for (const char *v : aosTokens)
    2162             :                 {
    2163         719 :                     valueVector.push_back(GDALArgDatasetValue(v));
    2164             :                 }
    2165             :             }
    2166         728 :             if (arg->GetMaxCount() == 1)
    2167             :             {
    2168         625 :                 bool ret = arg->Set(std::move(valueVector));
    2169         625 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2170         625 :                 return ret;
    2171             :             }
    2172             : 
    2173         103 :             break;
    2174             :         }
    2175             :     }
    2176             : 
    2177         512 :     return true;
    2178             : }
    2179             : 
    2180             : /************************************************************************/
    2181             : /*                     FormatSuggestionsAsString()                      */
    2182             : /************************************************************************/
    2183             : 
    2184             : static std::string
    2185           6 : FormatSuggestionsAsString(const std::vector<std::string> &suggestions,
    2186             :                           bool addDashDashPrefix)
    2187             : {
    2188           6 :     std::string ret;
    2189          14 :     for (auto [i, suggestion] : cpl::enumerate(suggestions))
    2190             :     {
    2191           8 :         if (i > 0)
    2192             :         {
    2193           2 :             ret += (i + 1 < suggestions.size()) ? ", " : " or ";
    2194             :         }
    2195           8 :         ret += '\'';
    2196           8 :         if (addDashDashPrefix)
    2197           6 :             ret += "--";
    2198           8 :         ret += suggestion;
    2199           8 :         ret += '\'';
    2200             :     }
    2201           6 :     return ret;
    2202             : }
    2203             : 
    2204             : /************************************************************************/
    2205             : /*              GDALAlgorithm::ParseCommandLineArguments()              */
    2206             : /************************************************************************/
    2207             : 
    2208        2212 : bool GDALAlgorithm::ParseCommandLineArguments(
    2209             :     const std::vector<std::string> &args)
    2210             : {
    2211        2212 :     if (m_parsedSubStringAlreadyCalled)
    2212             :     {
    2213           6 :         ReportError(CE_Failure, CPLE_AppDefined,
    2214             :                     "ParseCommandLineArguments() can only be called once per "
    2215             :                     "instance.");
    2216           6 :         return false;
    2217             :     }
    2218        2206 :     m_parsedSubStringAlreadyCalled = true;
    2219             : 
    2220             :     // AWS like syntax supported too (not advertized)
    2221        2206 :     if (args.size() == 1 && args[0] == "help")
    2222             :     {
    2223           1 :         auto arg = GetArg("help");
    2224           1 :         assert(arg);
    2225           1 :         arg->Set(true);
    2226           1 :         arg->RunActions();
    2227           1 :         return true;
    2228             :     }
    2229             : 
    2230        2205 :     if (HasSubAlgorithms())
    2231             :     {
    2232         475 :         if (args.empty())
    2233             :         {
    2234           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    2235           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    2236           2 :             return false;
    2237             :         }
    2238         473 :         if (!args[0].empty() && args[0][0] == '-')
    2239             :         {
    2240             :             // go on argument parsing
    2241             :         }
    2242             :         else
    2243             :         {
    2244         470 :             const auto nCounter = CPLGetErrorCounter();
    2245         470 :             m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
    2246         470 :             if (m_selectedSubAlgHolder)
    2247             :             {
    2248         467 :                 m_selectedSubAlg = m_selectedSubAlgHolder.get();
    2249         467 :                 m_selectedSubAlg->SetReferencePathForRelativePaths(
    2250         467 :                     m_referencePath);
    2251         467 :                 m_selectedSubAlg->m_executionForStreamOutput =
    2252         467 :                     m_executionForStreamOutput;
    2253         467 :                 m_selectedSubAlg->m_calledFromCommandLine =
    2254         467 :                     m_calledFromCommandLine;
    2255         467 :                 m_selectedSubAlg->m_skipValidationInParseCommandLine =
    2256         467 :                     m_skipValidationInParseCommandLine;
    2257         467 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    2258         934 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    2259         467 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    2260         467 :                 return bRet;
    2261             :             }
    2262             :             else
    2263             :             {
    2264           4 :                 if (!(CPLGetErrorCounter() == nCounter + 1 &&
    2265           1 :                       strstr(CPLGetLastErrorMsg(), "Do you mean")))
    2266             :                 {
    2267           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2268           2 :                                 "Unknown command: '%s'", args[0].c_str());
    2269             :                 }
    2270           3 :                 return false;
    2271             :             }
    2272             :         }
    2273             :     }
    2274             : 
    2275             :     std::map<
    2276             :         GDALAlgorithmArg *,
    2277             :         std::variant<std::vector<std::string>, std::vector<int>,
    2278             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    2279        3466 :         inConstructionValues;
    2280             : 
    2281        3466 :     std::vector<std::string> lArgs(args);
    2282        1733 :     bool helpValueRequested = false;
    2283        5009 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    2284             :     {
    2285        3389 :         const auto &strArg = lArgs[i];
    2286        3389 :         GDALAlgorithmArg *arg = nullptr;
    2287        3389 :         std::string name;
    2288        3389 :         std::string value;
    2289        3389 :         bool hasValue = false;
    2290        3389 :         if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
    2291           5 :             helpValueRequested = true;
    2292        3389 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    2293             :         {
    2294        2094 :             const auto equalPos = strArg.find('=');
    2295        4188 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    2296        2094 :                                                    : strArg;
    2297        2094 :             const std::string nameWithoutDash = name.substr(2);
    2298        2094 :             auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2299        2150 :             if (m_arbitraryLongNameArgsAllowed &&
    2300        2150 :                 iterArg == m_mapLongNameToArg.end())
    2301             :             {
    2302          17 :                 GetArg(nameWithoutDash);
    2303          17 :                 iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2304             :             }
    2305        2094 :             if (iterArg == m_mapLongNameToArg.end())
    2306             :             {
    2307             :                 const auto suggestions =
    2308          28 :                     GetSuggestionsForArgumentName(nameWithoutDash);
    2309          28 :                 if (!suggestions.empty())
    2310             :                 {
    2311           3 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2312             :                                 "Option '%s' is unknown. Do you mean %s?",
    2313             :                                 name.c_str(),
    2314           6 :                                 FormatSuggestionsAsString(
    2315             :                                     suggestions, /* addDashDashPrefix = */ true)
    2316             :                                     .c_str());
    2317             :                 }
    2318             :                 else
    2319             :                 {
    2320          25 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2321             :                                 "Option '%s' is unknown.", name.c_str());
    2322             :                 }
    2323          28 :                 return false;
    2324             :             }
    2325        2066 :             arg = iterArg->second;
    2326        2066 :             if (equalPos != std::string::npos)
    2327             :             {
    2328         444 :                 hasValue = true;
    2329         444 :                 value = strArg.substr(equalPos + 1);
    2330             :             }
    2331             :         }
    2332        1371 :         else if (strArg.size() >= 2 && strArg[0] == '-' &&
    2333          76 :                  CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
    2334             :         {
    2335         147 :             for (size_t j = 1; j < strArg.size(); ++j)
    2336             :             {
    2337          76 :                 name.clear();
    2338          76 :                 name += strArg[j];
    2339          76 :                 const auto iterArg = m_mapShortNameToArg.find(name);
    2340          76 :                 if (iterArg == m_mapShortNameToArg.end())
    2341             :                 {
    2342           5 :                     const std::string nameWithoutDash = strArg.substr(1);
    2343           5 :                     if (m_mapLongNameToArg.find(nameWithoutDash) !=
    2344          10 :                         m_mapLongNameToArg.end())
    2345             :                     {
    2346           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2347             :                                     "Short name option '%s' is unknown. Do you "
    2348             :                                     "mean '--%s' (with leading double dash) ?",
    2349             :                                     name.c_str(), nameWithoutDash.c_str());
    2350             :                     }
    2351             :                     else
    2352             :                     {
    2353             :                         const auto suggestions =
    2354           8 :                             GetSuggestionsForArgumentName(nameWithoutDash);
    2355           4 :                         if (!suggestions.empty())
    2356             :                         {
    2357           1 :                             ReportError(
    2358             :                                 CE_Failure, CPLE_IllegalArg,
    2359             :                                 "Short name option '%s' is unknown. Do you "
    2360             :                                 "mean %s (with leading double dash) ?",
    2361             :                                 name.c_str(),
    2362           2 :                                 FormatSuggestionsAsString(
    2363             :                                     suggestions, /* addDashDashPrefix = */ true)
    2364             :                                     .c_str());
    2365             :                         }
    2366             :                         else
    2367             :                         {
    2368           3 :                             ReportError(CE_Failure, CPLE_IllegalArg,
    2369             :                                         "Short name option '%s' is unknown.",
    2370             :                                         name.c_str());
    2371             :                         }
    2372             :                     }
    2373           5 :                     return false;
    2374             :                 }
    2375          71 :                 arg = iterArg->second;
    2376          71 :                 if (strArg.size() > 2)
    2377             :                 {
    2378           0 :                     if (arg->GetType() != GAAT_BOOLEAN)
    2379             :                     {
    2380           0 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2381             :                                     "Invalid argument '%s'. Option '%s' is not "
    2382             :                                     "a boolean option.",
    2383             :                                     strArg.c_str(), name.c_str());
    2384           0 :                         return false;
    2385             :                     }
    2386             : 
    2387           0 :                     if (!ParseArgument(arg, name, "true", inConstructionValues))
    2388           0 :                         return false;
    2389             :                 }
    2390             :             }
    2391          71 :             if (strArg.size() > 2)
    2392             :             {
    2393           0 :                 lArgs.erase(lArgs.begin() + i);
    2394           0 :                 continue;
    2395             :             }
    2396             :         }
    2397             :         else
    2398             :         {
    2399        1219 :             ++i;
    2400        1219 :             continue;
    2401             :         }
    2402        2137 :         CPLAssert(arg);
    2403             : 
    2404        2137 :         if (arg && arg->GetType() == GAAT_BOOLEAN)
    2405             :         {
    2406         322 :             if (!hasValue)
    2407             :             {
    2408         319 :                 hasValue = true;
    2409         319 :                 value = "true";
    2410             :             }
    2411             :         }
    2412             : 
    2413        2137 :         if (!hasValue)
    2414             :         {
    2415        1374 :             if (i + 1 == lArgs.size())
    2416             :             {
    2417          41 :                 if (m_parseForAutoCompletion)
    2418             :                 {
    2419          35 :                     lArgs.erase(lArgs.begin() + i);
    2420          35 :                     break;
    2421             :                 }
    2422           6 :                 ReportError(
    2423             :                     CE_Failure, CPLE_IllegalArg,
    2424             :                     "Expected value for argument '%s', but ran short of tokens",
    2425             :                     name.c_str());
    2426           6 :                 return false;
    2427             :             }
    2428        1333 :             value = lArgs[i + 1];
    2429        1333 :             lArgs.erase(lArgs.begin() + i + 1);
    2430             :         }
    2431             : 
    2432        2096 :         if (arg && !ParseArgument(arg, name, value, inConstructionValues))
    2433          39 :             return false;
    2434             : 
    2435        2057 :         lArgs.erase(lArgs.begin() + i);
    2436             :     }
    2437             : 
    2438        1655 :     if (m_specialActionRequested)
    2439             :     {
    2440          23 :         return true;
    2441             :     }
    2442             : 
    2443        2078 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    2444             :     {
    2445        2044 :         for (auto &[arg, value] : inConstructionValues)
    2446             :         {
    2447         470 :             if (arg->GetType() == GAAT_STRING_LIST)
    2448             :             {
    2449         227 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    2450         227 :                         inConstructionValues[arg])))
    2451             :                 {
    2452          34 :                     return false;
    2453             :                 }
    2454             :             }
    2455         243 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    2456             :             {
    2457          54 :                 if (!arg->Set(
    2458          54 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    2459             :                 {
    2460           4 :                     return false;
    2461             :                 }
    2462             :             }
    2463         189 :             else if (arg->GetType() == GAAT_REAL_LIST)
    2464             :             {
    2465          94 :                 if (!arg->Set(std::get<std::vector<double>>(
    2466          94 :                         inConstructionValues[arg])))
    2467             :                 {
    2468          10 :                     return false;
    2469             :                 }
    2470             :             }
    2471          95 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    2472             :             {
    2473          95 :                 if (!arg->Set(
    2474             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    2475          95 :                             inConstructionValues[arg]))))
    2476             :                 {
    2477           2 :                     return false;
    2478             :                 }
    2479             :             }
    2480             :         }
    2481        1574 :         return true;
    2482        1632 :     };
    2483             : 
    2484             :     // Process positional arguments that have not been set through their
    2485             :     // option name.
    2486        1632 :     size_t i = 0;
    2487        1632 :     size_t iCurPosArg = 0;
    2488             : 
    2489             :     // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
    2490        1655 :     if (m_positionalArgs.size() == 3 &&
    2491          24 :         (m_positionalArgs[0]->IsRequired() ||
    2492          23 :          m_positionalArgs[0]->GetMinCount() == 1) &&
    2493          44 :         m_positionalArgs[0]->GetMaxCount() == 1 &&
    2494          29 :         (m_positionalArgs[1]->IsRequired() ||
    2495          29 :          m_positionalArgs[1]->GetMinCount() == 1) &&
    2496             :         /* Second argument may have several occurrences */
    2497          44 :         m_positionalArgs[1]->GetMaxCount() >= 1 &&
    2498          22 :         (m_positionalArgs[2]->IsRequired() ||
    2499          22 :          m_positionalArgs[2]->GetMinCount() == 1) &&
    2500          22 :         m_positionalArgs[2]->GetMaxCount() == 1 &&
    2501           9 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2502        1664 :         !m_positionalArgs[1]->IsExplicitlySet() &&
    2503           9 :         !m_positionalArgs[2]->IsExplicitlySet())
    2504             :     {
    2505           7 :         if (lArgs.size() - i < 3)
    2506             :         {
    2507           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    2508             :                         "Not enough positional values.");
    2509           1 :             return false;
    2510             :         }
    2511          12 :         bool ok = ParseArgument(m_positionalArgs[0],
    2512           6 :                                 m_positionalArgs[0]->GetName().c_str(),
    2513           6 :                                 lArgs[i], inConstructionValues);
    2514           6 :         if (ok)
    2515             :         {
    2516           5 :             ++i;
    2517          11 :             for (; i + 1 < lArgs.size() && ok; ++i)
    2518             :             {
    2519          12 :                 ok = ParseArgument(m_positionalArgs[1],
    2520           6 :                                    m_positionalArgs[1]->GetName().c_str(),
    2521           6 :                                    lArgs[i], inConstructionValues);
    2522             :             }
    2523             :         }
    2524           6 :         if (ok)
    2525             :         {
    2526          10 :             ok = ParseArgument(m_positionalArgs[2],
    2527          10 :                                m_positionalArgs[2]->GetName().c_str(), lArgs[i],
    2528             :                                inConstructionValues);
    2529           5 :             ++i;
    2530             :         }
    2531           6 :         if (!ok)
    2532             :         {
    2533           3 :             ProcessInConstructionValues();
    2534           3 :             return false;
    2535             :         }
    2536             :     }
    2537             : 
    2538         519 :     if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
    2539         601 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2540        2457 :         m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
    2541          72 :         (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
    2542          36 :          m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
    2543             :     {
    2544          36 :         ++iCurPosArg;
    2545             :     }
    2546             : 
    2547        2767 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    2548             :     {
    2549        1146 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    2550        1157 :         while (arg->IsExplicitlySet())
    2551             :         {
    2552          12 :             ++iCurPosArg;
    2553          12 :             if (iCurPosArg == m_positionalArgs.size())
    2554           1 :                 break;
    2555          11 :             arg = m_positionalArgs[iCurPosArg];
    2556             :         }
    2557        1146 :         if (iCurPosArg == m_positionalArgs.size())
    2558             :         {
    2559           1 :             break;
    2560             :         }
    2561        1769 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2562         624 :             arg->GetMinCount() != arg->GetMaxCount())
    2563             :         {
    2564          98 :             if (iCurPosArg == 0)
    2565             :             {
    2566          76 :                 size_t nCountAtEnd = 0;
    2567         105 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    2568             :                 {
    2569          31 :                     const auto *otherArg = m_positionalArgs[j];
    2570          31 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    2571             :                     {
    2572           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    2573             :                         {
    2574           2 :                             ReportError(
    2575             :                                 CE_Failure, CPLE_AppDefined,
    2576             :                                 "Ambiguity in definition of positional "
    2577             :                                 "argument "
    2578             :                                 "'%s' given it has a varying number of values, "
    2579             :                                 "but follows argument '%s' which also has a "
    2580             :                                 "varying number of values",
    2581           1 :                                 otherArg->GetName().c_str(),
    2582           1 :                                 arg->GetName().c_str());
    2583           1 :                             ProcessInConstructionValues();
    2584           1 :                             return false;
    2585             :                         }
    2586           3 :                         nCountAtEnd += otherArg->GetMinCount();
    2587             :                     }
    2588             :                     else
    2589             :                     {
    2590          27 :                         if (!otherArg->IsRequired())
    2591             :                         {
    2592           2 :                             ReportError(
    2593             :                                 CE_Failure, CPLE_AppDefined,
    2594             :                                 "Ambiguity in definition of positional "
    2595             :                                 "argument "
    2596             :                                 "'%s', given it is not required but follows "
    2597             :                                 "argument '%s' which has a varying number of "
    2598             :                                 "values",
    2599           1 :                                 otherArg->GetName().c_str(),
    2600           1 :                                 arg->GetName().c_str());
    2601           1 :                             ProcessInConstructionValues();
    2602           1 :                             return false;
    2603             :                         }
    2604          26 :                         nCountAtEnd++;
    2605             :                     }
    2606             :                 }
    2607          74 :                 if (lArgs.size() < nCountAtEnd)
    2608             :                 {
    2609           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2610             :                                 "Not enough positional values.");
    2611           1 :                     ProcessInConstructionValues();
    2612           1 :                     return false;
    2613             :                 }
    2614         154 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    2615             :                 {
    2616          81 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2617             :                                        inConstructionValues))
    2618             :                     {
    2619           0 :                         ProcessInConstructionValues();
    2620           0 :                         return false;
    2621             :                     }
    2622             :                 }
    2623             :             }
    2624          22 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    2625             :             {
    2626          49 :                 for (; i < lArgs.size(); ++i)
    2627             :                 {
    2628          28 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2629             :                                        inConstructionValues))
    2630             :                     {
    2631           0 :                         ProcessInConstructionValues();
    2632           0 :                         return false;
    2633             :                     }
    2634             :                 }
    2635             :             }
    2636             :             else
    2637             :             {
    2638           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2639             :                             "Ambiguity in definition of positional arguments: "
    2640             :                             "arguments with varying number of values must be "
    2641             :                             "first or last one.");
    2642           1 :                 return false;
    2643             :             }
    2644             :         }
    2645             :         else
    2646             :         {
    2647        1047 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    2648             :             {
    2649           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2650             :                             "Not enough positional values.");
    2651           1 :                 return false;
    2652             :             }
    2653        1046 :             const size_t iMax = i + arg->GetMaxCount();
    2654        2095 :             for (; i < iMax; ++i)
    2655             :             {
    2656        1050 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2657             :                                    inConstructionValues))
    2658             :                 {
    2659           1 :                     ProcessInConstructionValues();
    2660           1 :                     return false;
    2661             :                 }
    2662             :             }
    2663             :         }
    2664        1139 :         ++iCurPosArg;
    2665             :     }
    2666             : 
    2667        1622 :     if (i < lArgs.size())
    2668             :     {
    2669          21 :         ReportError(CE_Failure, CPLE_AppDefined,
    2670             :                     "Positional values starting at '%s' are not expected.",
    2671          21 :                     lArgs[i].c_str());
    2672          21 :         return false;
    2673             :     }
    2674             : 
    2675        1601 :     if (!ProcessInConstructionValues())
    2676             :     {
    2677          33 :         return false;
    2678             :     }
    2679             : 
    2680             :     // Skip to first unset positional argument.
    2681        2584 :     while (iCurPosArg < m_positionalArgs.size() &&
    2682         557 :            m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    2683             :     {
    2684         459 :         ++iCurPosArg;
    2685             :     }
    2686             :     // Check if this positional argument is required.
    2687        1665 :     if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
    2688          97 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    2689          50 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    2690          47 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    2691             :     {
    2692          86 :         ReportError(CE_Failure, CPLE_AppDefined,
    2693             :                     "Positional arguments starting at '%s' have not been "
    2694             :                     "specified.",
    2695          86 :                     m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    2696          86 :         return false;
    2697             :     }
    2698             : 
    2699        1482 :     if (m_calledFromCommandLine)
    2700             :     {
    2701        5426 :         for (auto &arg : m_args)
    2702             :         {
    2703        7034 :             if (arg->IsExplicitlySet() &&
    2704        1098 :                 ((arg->GetType() == GAAT_STRING &&
    2705        1095 :                   arg->Get<std::string>() == "?") ||
    2706         994 :                  (arg->GetType() == GAAT_STRING_LIST &&
    2707         157 :                   arg->Get<std::vector<std::string>>().size() == 1 &&
    2708          78 :                   arg->Get<std::vector<std::string>>()[0] == "?")))
    2709             :             {
    2710             :                 {
    2711          10 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2712           5 :                     ValidateArguments();
    2713             :                 }
    2714             : 
    2715           5 :                 auto choices = arg->GetChoices();
    2716           5 :                 if (choices.empty())
    2717           2 :                     choices = arg->GetAutoCompleteChoices(std::string());
    2718           5 :                 if (!choices.empty())
    2719             :                 {
    2720           5 :                     if (choices.size() == 1)
    2721             :                     {
    2722           4 :                         ReportError(
    2723             :                             CE_Failure, CPLE_AppDefined,
    2724             :                             "Single potential value for argument '%s' is '%s'",
    2725           4 :                             arg->GetName().c_str(), choices.front().c_str());
    2726             :                     }
    2727             :                     else
    2728             :                     {
    2729           6 :                         std::string msg("Potential values for argument '");
    2730           3 :                         msg += arg->GetName();
    2731           3 :                         msg += "' are:";
    2732          45 :                         for (const auto &v : choices)
    2733             :                         {
    2734          42 :                             msg += "\n- ";
    2735          42 :                             msg += v;
    2736             :                         }
    2737           3 :                         ReportError(CE_Failure, CPLE_AppDefined, "%s",
    2738             :                                     msg.c_str());
    2739             :                     }
    2740           5 :                     return false;
    2741             :                 }
    2742             :             }
    2743             :         }
    2744             :     }
    2745             : 
    2746        1477 :     return m_skipValidationInParseCommandLine || ValidateArguments();
    2747             : }
    2748             : 
    2749             : /************************************************************************/
    2750             : /*                     GDALAlgorithm::ReportError()                     */
    2751             : /************************************************************************/
    2752             : 
    2753             : //! @cond Doxygen_Suppress
    2754         954 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    2755             :                                 const char *fmt, ...) const
    2756             : {
    2757             :     va_list args;
    2758         954 :     va_start(args, fmt);
    2759         954 :     CPLError(eErrClass, err_no, "%s",
    2760         954 :              std::string(m_name)
    2761         954 :                  .append(": ")
    2762        1908 :                  .append(CPLString().vPrintf(fmt, args))
    2763             :                  .c_str());
    2764         954 :     va_end(args);
    2765         954 : }
    2766             : 
    2767             : //! @endcond
    2768             : 
    2769             : /************************************************************************/
    2770             : /*                  GDALAlgorithm::ProcessDatasetArg()                  */
    2771             : /************************************************************************/
    2772             : 
    2773       10438 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    2774             :                                       GDALAlgorithm *algForOutput)
    2775             : {
    2776       10438 :     bool ret = true;
    2777             : 
    2778       10438 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    2779       10438 :     const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
    2780       10438 :     const bool update = hasUpdateArg && updateArg->Get<bool>();
    2781             : 
    2782       10438 :     const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
    2783       10438 :     const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
    2784       10438 :     const bool append = hasAppendArg && appendArg->Get<bool>();
    2785             : 
    2786       10438 :     const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
    2787             :     const bool overwrite =
    2788       17161 :         (arg->IsOutput() && overwriteArg &&
    2789       17161 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    2790             : 
    2791       10438 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    2792       20876 :     auto &val = [arg]() -> GDALArgDatasetValue &
    2793             :     {
    2794       10438 :         if (arg->GetType() == GAAT_DATASET_LIST)
    2795        6013 :             return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
    2796             :         else
    2797        4425 :             return arg->Get<GDALArgDatasetValue>();
    2798       10438 :     }();
    2799             :     const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
    2800       16548 :         arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
    2801       16556 :         !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
    2802           8 :         !overwrite;
    2803             : 
    2804             :     // Used for nested pipelines
    2805             :     const auto oIterDatasetNameToDataset =
    2806       20873 :         val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
    2807       10438 :                         : m_oMapDatasetNameToDataset.end();
    2808             : 
    2809       10438 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    2810             :     {
    2811           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    2812             :                     "Argument '%s' has no dataset object or dataset name.",
    2813           3 :                     arg->GetName().c_str());
    2814           3 :         ret = false;
    2815             :     }
    2816       10435 :     else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
    2817             :     {
    2818           3 :         return false;
    2819             :     }
    2820         284 :     else if (m_inputDatasetCanBeOmitted &&
    2821       10716 :              val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
    2822          17 :              !arg->IsOutput())
    2823             :     {
    2824          17 :         return true;
    2825             :     }
    2826       15404 :     else if (!val.GetDatasetRef() &&
    2827        5293 :              (arg->AutoOpenDataset() ||
    2828       15708 :               oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
    2829        4685 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
    2830             :               onlyInputSpecifiedInUpdateAndOutputNotRequired))
    2831             :     {
    2832        1447 :         int flags = arg->GetDatasetType();
    2833        1447 :         bool assignToOutputArg = false;
    2834             : 
    2835             :         // Check if input and output parameters point to the same
    2836             :         // filename (for vector datasets)
    2837        2686 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    2838        2686 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    2839             :         {
    2840          62 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    2841         121 :             if (!outputVal.GetDatasetRef() &&
    2842         121 :                 outputVal.GetName() == val.GetName() &&
    2843           2 :                 (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
    2844             :             {
    2845           2 :                 assignToOutputArg = true;
    2846           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2847             :             }
    2848          60 :             else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
    2849             :             {
    2850           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2851             :             }
    2852             :         }
    2853             : 
    2854        1447 :         if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
    2855        1364 :             flags |= GDAL_OF_VERBOSE_ERROR;
    2856        1447 :         if ((arg == outputArg || !outputArg) && update)
    2857             :         {
    2858          85 :             flags |= GDAL_OF_UPDATE;
    2859          85 :             if (!append)
    2860          64 :                 flags |= GDAL_OF_VERBOSE_ERROR;
    2861             :         }
    2862             : 
    2863        1447 :         const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
    2864             :         const bool readOnly =
    2865        1491 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    2866          44 :              readOnlyArg->Get<bool>());
    2867        1447 :         if (readOnly)
    2868          12 :             flags &= ~GDAL_OF_UPDATE;
    2869             : 
    2870        2894 :         CPLStringList aosOpenOptions;
    2871        2894 :         CPLStringList aosAllowedDrivers;
    2872        1447 :         if (arg->IsInput())
    2873             :         {
    2874        1447 :             if (arg == outputArg)
    2875             :             {
    2876          83 :                 if (update && !overwrite)
    2877             :                 {
    2878          83 :                     const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
    2879          83 :                     if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2880          31 :                         aosOpenOptions = CPLStringList(
    2881          31 :                             ooArg->Get<std::vector<std::string>>());
    2882             :                 }
    2883             :             }
    2884             :             else
    2885             :             {
    2886        1364 :                 const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    2887        1364 :                 if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2888             :                     aosOpenOptions =
    2889        1297 :                         CPLStringList(ooArg->Get<std::vector<std::string>>());
    2890             : 
    2891        1364 :                 const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2892        1364 :                 if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    2893             :                     aosAllowedDrivers =
    2894        1253 :                         CPLStringList(ifArg->Get<std::vector<std::string>>());
    2895             :             }
    2896             :         }
    2897             : 
    2898        2894 :         std::string osDatasetName = val.GetName();
    2899        1447 :         if (!m_referencePath.empty())
    2900             :         {
    2901          42 :             osDatasetName = GDALDataset::BuildFilename(
    2902          21 :                 osDatasetName.c_str(), m_referencePath.c_str(), true);
    2903             :         }
    2904        1447 :         if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
    2905           0 :             osDatasetName = "/vsistdin/";
    2906             : 
    2907             :         // Handle special case of overview delete in GTiff which would fail
    2908             :         // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
    2909         145 :         if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
    2910        1594 :             m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
    2911           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
    2912             :         {
    2913           4 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2914             :             GDALDriverH hDrv =
    2915           2 :                 GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
    2916           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
    2917             :             {
    2918             :                 // Cleaning does not break COG layout
    2919           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
    2920             :             }
    2921             :         }
    2922             : 
    2923             :         GDALDataset *poDS;
    2924        2894 :         CPLErrorAccumulator oAccumulator;
    2925             :         {
    2926        2894 :             auto oContext = oAccumulator.InstallForCurrentScope();
    2927             : 
    2928        1447 :             poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
    2929        1447 :                        ? oIterDatasetNameToDataset->second
    2930        1434 :                        : GDALDataset::Open(osDatasetName.c_str(), flags,
    2931        1434 :                                            aosAllowedDrivers.List(),
    2932        1434 :                                            aosOpenOptions.List());
    2933             : 
    2934          66 :             if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
    2935        1513 :                 !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
    2936             :             {
    2937          36 :                 auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
    2938          72 :                     osDatasetName.c_str(), nullptr);
    2939          36 :                 if (eErr == OGRERR_NONE)
    2940             :                 {
    2941           4 :                     auto poMemDS = std::make_unique<MEMDataset>();
    2942           4 :                     auto *poLayer = poMemDS->CreateLayer(
    2943             :                         "", poWktGeom->getSpatialReference(),
    2944           2 :                         poWktGeom->getGeometryType());
    2945             : 
    2946           2 :                     auto poFeatureDefn = poLayer->GetLayerDefn();
    2947           4 :                     OGRFeature oFeature(poFeatureDefn);
    2948             : 
    2949           2 :                     oFeature.SetGeometry(std::move(poWktGeom));
    2950           2 :                     if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
    2951             :                     {
    2952           2 :                         poDS = poMemDS.release();
    2953           2 :                         oAccumulator.ClearErrors();
    2954             :                     }
    2955             :                 }
    2956             :             }
    2957             : 
    2958             :             // Retry with PostGIS vector driver
    2959          64 :             if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
    2960          62 :                 cpl::starts_with(osDatasetName, "PG:") &&
    2961           0 :                 GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
    2962        1511 :                 aosAllowedDrivers.empty() && aosOpenOptions.empty())
    2963             :             {
    2964           0 :                 oAccumulator.ClearErrors();
    2965           0 :                 poDS = GDALDataset::Open(
    2966           0 :                     osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
    2967           0 :                     aosAllowedDrivers.List(), aosOpenOptions.List());
    2968             :             }
    2969             :         }
    2970        1447 :         oAccumulator.ReplayErrors();
    2971             : 
    2972        1447 :         if (poDS)
    2973             :         {
    2974        1383 :             if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
    2975             :             {
    2976          13 :                 if (arg->GetType() == GAAT_DATASET)
    2977           7 :                     arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
    2978          13 :                 poDS->Reference();
    2979          13 :                 m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
    2980             :             }
    2981             : 
    2982             :             // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
    2983             :             // where the PG: dataset will be first opened with the PostGISRaster
    2984             :             // driver whereas the PostgreSQL (vector) one is actually wanted.
    2985        1894 :             if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
    2986        2005 :                 (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
    2987         111 :                 aosOpenOptions.empty())
    2988             :             {
    2989         107 :                 auto poDrv = poDS->GetDriver();
    2990         107 :                 if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
    2991             :                 {
    2992             :                     // Retry with PostgreSQL (vector) driver
    2993             :                     std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
    2994           0 :                         osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
    2995           0 :                     if (poTmpDS)
    2996             :                     {
    2997           0 :                         poDS->ReleaseRef();
    2998           0 :                         poDS = poTmpDS.release();
    2999             :                     }
    3000             :                 }
    3001             :             }
    3002             : 
    3003        1383 :             if (assignToOutputArg)
    3004             :             {
    3005             :                 // Avoid opening twice the same datasource if it is both
    3006             :                 // the input and output.
    3007             :                 // Known to cause problems with at least FGdb, SQLite
    3008             :                 // and GPKG drivers. See #4270
    3009             :                 // Restrict to those 3 drivers. For example it is known
    3010             :                 // to break with the PG driver due to the way it
    3011             :                 // manages transactions.
    3012           2 :                 auto poDriver = poDS->GetDriver();
    3013           4 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    3014           2 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    3015           2 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    3016             :                 {
    3017           2 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    3018             :                 }
    3019             :             }
    3020        1383 :             val.SetDatasetOpenedByAlgorithm();
    3021        1383 :             val.Set(poDS);
    3022        1383 :             poDS->ReleaseRef();
    3023             :         }
    3024          64 :         else if (!append)
    3025             :         {
    3026          62 :             ret = false;
    3027             :         }
    3028             :     }
    3029             : 
    3030             :     // Deal with overwriting the output dataset
    3031       10418 :     if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
    3032             :     {
    3033        3240 :         if (!append)
    3034             :         {
    3035             :             // If outputting to MEM, do not try to erase a real file of the same name!
    3036             :             const auto outputFormatArg =
    3037        3228 :                 algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3038        9646 :             if (!(outputFormatArg &&
    3039        3209 :                   outputFormatArg->GetType() == GAAT_STRING &&
    3040        3209 :                   (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3041        2129 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3042        1191 :                          "stream") ||
    3043        1191 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3044             :                          "Memory"))))
    3045             :             {
    3046        1210 :                 const char *pszType = "";
    3047        1210 :                 GDALDriver *poDriver = nullptr;
    3048        2374 :                 if (!val.GetName().empty() &&
    3049        1164 :                     GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
    3050             :                                                &poDriver))
    3051             :                 {
    3052          79 :                     if (!overwrite)
    3053             :                     {
    3054          68 :                         std::string options;
    3055          34 :                         if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
    3056             :                         {
    3057          11 :                             options += "--";
    3058          11 :                             options += GDAL_ARG_NAME_OVERWRITE_LAYER;
    3059             :                         }
    3060          34 :                         if (hasAppendArg)
    3061             :                         {
    3062          22 :                             if (!options.empty())
    3063           8 :                                 options += '/';
    3064          22 :                             options += "--";
    3065          22 :                             options += GDAL_ARG_NAME_APPEND;
    3066             :                         }
    3067          34 :                         if (hasUpdateArg)
    3068             :                         {
    3069          15 :                             if (!options.empty())
    3070          12 :                                 options += '/';
    3071          15 :                             options += "--";
    3072          15 :                             options += GDAL_ARG_NAME_UPDATE;
    3073             :                         }
    3074             : 
    3075          34 :                         if (poDriver)
    3076             :                         {
    3077          68 :                             const char *pszPrefix = poDriver->GetMetadataItem(
    3078          34 :                                 GDAL_DMD_CONNECTION_PREFIX);
    3079          34 :                             if (pszPrefix &&
    3080           0 :                                 STARTS_WITH_CI(val.GetName().c_str(),
    3081             :                                                pszPrefix))
    3082             :                             {
    3083           0 :                                 bool bExists = false;
    3084             :                                 {
    3085             :                                     CPLErrorStateBackuper oBackuper(
    3086           0 :                                         CPLQuietErrorHandler);
    3087           0 :                                     bExists = std::unique_ptr<GDALDataset>(
    3088             :                                                   GDALDataset::Open(
    3089           0 :                                                       val.GetName().c_str())) !=
    3090             :                                               nullptr;
    3091             :                                 }
    3092           0 :                                 if (bExists)
    3093             :                                 {
    3094           0 :                                     if (!options.empty())
    3095           0 :                                         options = " You may specify the " +
    3096           0 :                                                   options + " option.";
    3097           0 :                                     ReportError(CE_Failure, CPLE_AppDefined,
    3098             :                                                 "%s '%s' already exists.%s",
    3099           0 :                                                 pszType, val.GetName().c_str(),
    3100             :                                                 options.c_str());
    3101           0 :                                     return false;
    3102             :                                 }
    3103             : 
    3104           0 :                                 return true;
    3105             :                             }
    3106             :                         }
    3107             : 
    3108          34 :                         if (!options.empty())
    3109          28 :                             options = '/' + options;
    3110          68 :                         ReportError(
    3111             :                             CE_Failure, CPLE_AppDefined,
    3112             :                             "%s '%s' already exists. You may specify the "
    3113             :                             "--overwrite%s option.",
    3114          34 :                             pszType, val.GetName().c_str(), options.c_str());
    3115          34 :                         return false;
    3116             :                     }
    3117          45 :                     else if (EQUAL(pszType, "File"))
    3118             :                     {
    3119           1 :                         if (VSIUnlink(val.GetName().c_str()) != 0)
    3120             :                         {
    3121           0 :                             ReportError(CE_Failure, CPLE_AppDefined,
    3122             :                                         "Deleting %s failed: %s",
    3123           0 :                                         val.GetName().c_str(),
    3124           0 :                                         VSIStrerror(errno));
    3125           0 :                             return false;
    3126             :                         }
    3127             :                     }
    3128          44 :                     else if (EQUAL(pszType, "Directory"))
    3129             :                     {
    3130             :                         // We don't want the user to accidentally erase a non-GDAL dataset
    3131           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3132             :                                     "Directory '%s' already exists, but is not "
    3133             :                                     "recognized as a valid GDAL dataset. "
    3134             :                                     "Please manually delete it before retrying",
    3135           1 :                                     val.GetName().c_str());
    3136           1 :                         return false;
    3137             :                     }
    3138          43 :                     else if (poDriver)
    3139             :                     {
    3140             :                         bool bDeleteOK;
    3141             :                         {
    3142             :                             CPLErrorStateBackuper oBackuper(
    3143          43 :                                 CPLQuietErrorHandler);
    3144          43 :                             bDeleteOK = (poDriver->Delete(
    3145          43 :                                              val.GetName().c_str()) == CE_None);
    3146             :                         }
    3147             :                         VSIStatBufL sStat;
    3148          46 :                         if (!bDeleteOK &&
    3149           3 :                             VSIStatL(val.GetName().c_str(), &sStat) == 0)
    3150             :                         {
    3151           3 :                             if (VSI_ISDIR(sStat.st_mode))
    3152             :                             {
    3153             :                                 // We don't want the user to accidentally erase a non-GDAL dataset
    3154           0 :                                 ReportError(
    3155             :                                     CE_Failure, CPLE_AppDefined,
    3156             :                                     "Directory '%s' already exists, but is not "
    3157             :                                     "recognized as a valid GDAL dataset. "
    3158             :                                     "Please manually delete it before retrying",
    3159           0 :                                     val.GetName().c_str());
    3160           2 :                                 return false;
    3161             :                             }
    3162           3 :                             else if (VSIUnlink(val.GetName().c_str()) != 0)
    3163             :                             {
    3164           2 :                                 ReportError(CE_Failure, CPLE_AppDefined,
    3165             :                                             "Deleting %s failed: %s",
    3166           2 :                                             val.GetName().c_str(),
    3167           2 :                                             VSIStrerror(errno));
    3168           2 :                                 return false;
    3169             :                             }
    3170             :                         }
    3171             :                     }
    3172             :                 }
    3173             :             }
    3174             :         }
    3175             :     }
    3176             : 
    3177             :     // If outputting to stdout, automatically turn off progress bar
    3178       10381 :     if (arg == outputArg && val.GetName() == "/vsistdout/")
    3179             :     {
    3180           8 :         auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
    3181           8 :         if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
    3182           5 :             quietArg->Set(true);
    3183             :     }
    3184             : 
    3185       10381 :     return ret;
    3186             : }
    3187             : 
    3188             : /************************************************************************/
    3189             : /*                  GDALAlgorithm::ValidateArguments()                  */
    3190             : /************************************************************************/
    3191             : 
    3192        7235 : bool GDALAlgorithm::ValidateArguments()
    3193             : {
    3194        7235 :     if (m_selectedSubAlg)
    3195           3 :         return m_selectedSubAlg->ValidateArguments();
    3196             : 
    3197        7232 :     if (m_specialActionRequested)
    3198           1 :         return true;
    3199             : 
    3200        7231 :     m_arbitraryLongNameArgsAllowed = false;
    3201             : 
    3202             :     // If only --output=format=MEM/stream is specified and not --output,
    3203             :     // then set empty name for --output.
    3204        7231 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    3205        7231 :     auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3206        4300 :     if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
    3207        2721 :         !outputArg->IsExplicitlySet() &&
    3208         363 :         outputFormatArg->GetType() == GAAT_STRING &&
    3209         363 :         (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3210         593 :          EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
    3211       11865 :         outputArg->GetType() == GAAT_DATASET &&
    3212         334 :         (outputArg->GetDatasetInputFlags() & GADV_NAME))
    3213             :     {
    3214         334 :         outputArg->Get<GDALArgDatasetValue>().Set("");
    3215             :     }
    3216             : 
    3217             :     // The method may emit several errors if several constraints are not met.
    3218        7231 :     bool ret = true;
    3219       14462 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    3220       14462 :     std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
    3221      134962 :     for (auto &arg : m_args)
    3222             :     {
    3223             :         // Check mutually exclusive/dependent arguments
    3224      127731 :         if (arg->IsExplicitlySet())
    3225             :         {
    3226             : 
    3227       20269 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3228       20269 :             if (!mutualExclusionGroup.empty())
    3229             :             {
    3230             :                 auto oIter =
    3231         804 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    3232         804 :                 if (oIter != mutualExclusionGroupUsed.end())
    3233             :                 {
    3234          13 :                     ret = false;
    3235          26 :                     ReportError(
    3236             :                         CE_Failure, CPLE_AppDefined,
    3237             :                         "Argument '%s' is mutually exclusive with '%s'.",
    3238          26 :                         arg->GetName().c_str(), oIter->second.c_str());
    3239             :                 }
    3240             :                 else
    3241             :                 {
    3242         791 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    3243        1582 :                         arg->GetName();
    3244             :                 }
    3245             :             }
    3246             : 
    3247       20269 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3248       20269 :             if (!mutualDependencyGroup.empty())
    3249             :             {
    3250          50 :                 if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
    3251         100 :                     mutualDependencyGroupUsed.end())
    3252             :                 {
    3253          87 :                     mutualDependencyGroupUsed[mutualDependencyGroup] = {
    3254          87 :                         arg->GetName()};
    3255             :                 }
    3256             :                 else
    3257             :                 {
    3258          42 :                     mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
    3259          21 :                         arg->GetName());
    3260             :                 }
    3261             :             }
    3262             : 
    3263             :             // Check direct dependencies
    3264       20281 :             for (const auto &dependency : arg->GetDirectDependencies())
    3265             :             {
    3266          12 :                 auto depArg = GetArg(dependency);
    3267          12 :                 if (!depArg)
    3268             :                 {
    3269           0 :                     ret = false;
    3270           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3271             :                                 "Argument '%s' depends on argument '%s' that "
    3272             :                                 "is not defined.",
    3273           0 :                                 arg->GetName().c_str(), dependency.c_str());
    3274             :                 }
    3275          12 :                 else if (!depArg->IsExplicitlySet())
    3276             :                 {
    3277           6 :                     ret = false;
    3278          12 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3279             :                                 "Argument '%s' depends on argument '%s' that "
    3280             :                                 "has not been specified.",
    3281           6 :                                 arg->GetName().c_str(),
    3282           6 :                                 depArg->GetName().c_str());
    3283             :                 }
    3284             :             }
    3285             :         }
    3286             : 
    3287      127907 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    3288         176 :             !arg->HasDefaultValue())
    3289             :         {
    3290         176 :             bool emitError = true;
    3291         176 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3292         176 :             if (!mutualExclusionGroup.empty())
    3293             :             {
    3294        1885 :                 for (const auto &otherArg : m_args)
    3295             :                 {
    3296        1859 :                     if (otherArg->GetMutualExclusionGroup() ==
    3297        1988 :                             mutualExclusionGroup &&
    3298         129 :                         otherArg->IsExplicitlySet())
    3299             :                     {
    3300          74 :                         emitError = false;
    3301          74 :                         break;
    3302             :                     }
    3303             :                 }
    3304             :             }
    3305         266 :             if (emitError && !(m_inputDatasetCanBeOmitted &&
    3306          57 :                                arg->GetName() == GDAL_ARG_NAME_INPUT &&
    3307          66 :                                (arg->GetType() == GAAT_DATASET ||
    3308          33 :                                 arg->GetType() == GAAT_DATASET_LIST)))
    3309             :             {
    3310          69 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3311             :                             "Required argument '%s' has not been specified.",
    3312          69 :                             arg->GetName().c_str());
    3313          69 :                 ret = false;
    3314             :             }
    3315             :         }
    3316      127555 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    3317             :         {
    3318        4425 :             if (!ProcessDatasetArg(arg.get(), this))
    3319          51 :                 ret = false;
    3320             :         }
    3321             : 
    3322      127731 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
    3323             :         {
    3324        5824 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    3325        5824 :             if (listVal.size() == 1)
    3326             :             {
    3327        5670 :                 if (!ProcessDatasetArg(arg.get(), this))
    3328          40 :                     ret = false;
    3329             :             }
    3330             :             else
    3331             :             {
    3332         473 :                 for (auto &val : listVal)
    3333             :                 {
    3334         319 :                     if (val.GetDatasetRef())
    3335             :                     {
    3336         120 :                         if (!CheckCanSetDatasetObject(arg.get()))
    3337             :                         {
    3338           0 :                             ret = false;
    3339             :                         }
    3340         315 :                         continue;
    3341             :                     }
    3342             : 
    3343         199 :                     if (val.GetName().empty())
    3344             :                     {
    3345           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3346             :                                     "Argument '%s' has no dataset object or "
    3347             :                                     "dataset name.",
    3348           0 :                                     arg->GetName().c_str());
    3349           0 :                         ret = false;
    3350           0 :                         continue;
    3351             :                     }
    3352             : 
    3353         199 :                     auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
    3354         199 :                     if (oIter != m_oMapDatasetNameToDataset.end())
    3355             :                     {
    3356           2 :                         auto poDS = oIter->second;
    3357           2 :                         val.SetDatasetOpenedByAlgorithm();
    3358           2 :                         val.Set(poDS);
    3359           2 :                         m_oMapDatasetNameToDataset.erase(oIter);
    3360           2 :                         continue;
    3361             :                     }
    3362             : 
    3363         197 :                     if (!arg->AutoOpenDataset())
    3364         193 :                         continue;
    3365             : 
    3366           4 :                     int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
    3367             : 
    3368           8 :                     CPLStringList aosOpenOptions;
    3369           8 :                     CPLStringList aosAllowedDrivers;
    3370           4 :                     if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    3371             :                     {
    3372           4 :                         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    3373           4 :                         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    3374             :                         {
    3375           4 :                             aosOpenOptions = CPLStringList(
    3376           4 :                                 ooArg->Get<std::vector<std::string>>());
    3377             :                         }
    3378             : 
    3379           4 :                         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    3380           4 :                         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    3381             :                         {
    3382           4 :                             aosAllowedDrivers = CPLStringList(
    3383           4 :                                 ifArg->Get<std::vector<std::string>>());
    3384             :                         }
    3385             : 
    3386           4 :                         const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3387           4 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    3388           0 :                             updateArg->Get<bool>())
    3389             :                         {
    3390           0 :                             flags |= GDAL_OF_UPDATE;
    3391             :                         }
    3392             :                     }
    3393             : 
    3394             :                     auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    3395           4 :                         val.GetName().c_str(), flags, aosAllowedDrivers.List(),
    3396          12 :                         aosOpenOptions.List()));
    3397           4 :                     if (poDS)
    3398             :                     {
    3399           3 :                         val.Set(std::move(poDS));
    3400             :                     }
    3401             :                     else
    3402             :                     {
    3403           1 :                         ret = false;
    3404             :                     }
    3405             :                 }
    3406             :             }
    3407             :         }
    3408             : 
    3409      127731 :         if (arg->IsExplicitlySet() && !arg->RunValidationActions())
    3410             :         {
    3411           8 :             ret = false;
    3412             :         }
    3413             :     }
    3414             : 
    3415             :     // Check mutual dependency groups
    3416        7231 :     std::vector<std::string> processedGroups;
    3417             :     // Loop through group map and check there are not required args in the group that are not set
    3418        7260 :     for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
    3419             :     {
    3420          29 :         if (std::find(processedGroups.begin(), processedGroups.end(),
    3421          29 :                       groupName) != processedGroups.end())
    3422           0 :             continue;
    3423          58 :         std::vector<std::string> missingArgs;
    3424         562 :         for (auto &arg : m_args)
    3425             :         {
    3426         533 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3427         598 :             if (mutualDependencyGroup == groupName &&
    3428          65 :                 std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
    3429         598 :                     argNames.end())
    3430             :             {
    3431          15 :                 missingArgs.push_back(arg->GetName());
    3432             :             }
    3433             :         }
    3434          29 :         if (!missingArgs.empty())
    3435             :         {
    3436          12 :             ret = false;
    3437          24 :             std::string missingArgsStr;
    3438          27 :             for (const auto &missingArg : missingArgs)
    3439             :             {
    3440          15 :                 if (!missingArgsStr.empty())
    3441           3 :                     missingArgsStr += ", ";
    3442          15 :                 missingArgsStr += missingArg;
    3443             :             }
    3444          24 :             std::string givenArgsStr;
    3445          27 :             for (const auto &givenArg : argNames)
    3446             :             {
    3447          15 :                 if (!givenArgsStr.empty())
    3448           3 :                     givenArgsStr += ", ";
    3449          15 :                 givenArgsStr += givenArg;
    3450             :             }
    3451          12 :             ReportError(CE_Failure, CPLE_AppDefined,
    3452             :                         "Argument(s) '%s' require(s) that the following "
    3453             :                         "argument(s) are also specified: %s.",
    3454             :                         givenArgsStr.c_str(), missingArgsStr.c_str());
    3455             :         }
    3456          29 :         processedGroups.push_back(groupName);
    3457             :     }
    3458             : 
    3459       30807 :     for (const auto &f : m_validationActions)
    3460             :     {
    3461       23576 :         if (!f())
    3462          81 :             ret = false;
    3463             :     }
    3464             : 
    3465        7231 :     return ret;
    3466             : }
    3467             : 
    3468             : /************************************************************************/
    3469             : /*                GDALAlgorithm::InstantiateSubAlgorithm                */
    3470             : /************************************************************************/
    3471             : 
    3472             : std::unique_ptr<GDALAlgorithm>
    3473       10228 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
    3474             :                                        bool suggestionAllowed) const
    3475             : {
    3476       10228 :     auto ret = m_subAlgRegistry.Instantiate(name);
    3477       20456 :     auto childCallPath = m_callPath;
    3478       10228 :     childCallPath.push_back(name);
    3479       10228 :     if (!ret)
    3480             :     {
    3481        1045 :         ret = GDALGlobalAlgorithmRegistry::GetSingleton()
    3482        1045 :                   .InstantiateDeclaredSubAlgorithm(childCallPath);
    3483             :     }
    3484       10228 :     if (ret)
    3485             :     {
    3486       10053 :         ret->SetCallPath(childCallPath);
    3487             :     }
    3488         175 :     else if (suggestionAllowed)
    3489             :     {
    3490          64 :         std::string bestCandidate;
    3491          32 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3492         490 :         for (const std::string &candidate : GetSubAlgorithmNames())
    3493             :         {
    3494             :             const size_t distance =
    3495         458 :                 CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
    3496             :                                        /* transpositionAllowed = */ true);
    3497         458 :             if (distance < bestDistance)
    3498             :             {
    3499          75 :                 bestCandidate = candidate;
    3500          75 :                 bestDistance = distance;
    3501             :             }
    3502         383 :             else if (distance == bestDistance)
    3503             :             {
    3504          47 :                 bestCandidate.clear();
    3505             :             }
    3506             :         }
    3507          32 :         if (!bestCandidate.empty() && bestDistance <= 2)
    3508             :         {
    3509           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3510             :                      "Algorithm '%s' is unknown. Do you mean '%s'?",
    3511             :                      name.c_str(), bestCandidate.c_str());
    3512             :         }
    3513             :     }
    3514       20456 :     return ret;
    3515             : }
    3516             : 
    3517             : /************************************************************************/
    3518             : /*            GDALAlgorithm::GetSuggestionForArgumentName()             */
    3519             : /************************************************************************/
    3520             : 
    3521             : std::string
    3522          39 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
    3523             : {
    3524          39 :     if (osName.size() >= 3)
    3525             :     {
    3526          34 :         std::string bestCandidate;
    3527          34 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3528         752 :         for (const auto &[key, value] : m_mapLongNameToArg)
    3529             :         {
    3530         718 :             CPL_IGNORE_RET_VAL(value);
    3531         718 :             const size_t distance = CPLLevenshteinDistance(
    3532             :                 osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
    3533         718 :             if (distance < bestDistance)
    3534             :             {
    3535          89 :                 bestCandidate = key;
    3536          89 :                 bestDistance = distance;
    3537             :             }
    3538         629 :             else if (distance == bestDistance)
    3539             :             {
    3540          77 :                 bestCandidate.clear();
    3541             :             }
    3542             :         }
    3543          48 :         if (!bestCandidate.empty() &&
    3544          14 :             bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
    3545             :         {
    3546           5 :             return bestCandidate;
    3547             :         }
    3548             :     }
    3549          34 :     return std::string();
    3550             : }
    3551             : 
    3552             : /************************************************************************/
    3553             : /*            GDALAlgorithm::GetSuggestionsForArgumentName()            */
    3554             : /************************************************************************/
    3555             : 
    3556             : std::vector<std::string>
    3557          39 : GDALAlgorithm::GetSuggestionsForArgumentName(const std::string &osName) const
    3558             : {
    3559          39 :     std::vector<std::string> ret;
    3560          78 :     std::string suggestion = GetSuggestionForArgumentName(osName);
    3561          39 :     if (!suggestion.empty())
    3562             :     {
    3563           5 :         ret.push_back(std::move(suggestion));
    3564             :     }
    3565          34 :     else if (osName.size() >= 3)
    3566             :     {
    3567             :         // e.g "crs" for reproject will match "input-crs" and "target-crs"
    3568          87 :         const std::string dashName = std::string("-").append(osName);
    3569         532 :         for (const auto &arg : m_args)
    3570             :         {
    3571         503 :             if (cpl::ends_with(arg->GetName(), dashName))
    3572             :             {
    3573           3 :                 ret.push_back(arg->GetName());
    3574             :             }
    3575             :         }
    3576             :     }
    3577          78 :     return ret;
    3578             : }
    3579             : 
    3580             : /************************************************************************/
    3581             : /*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
    3582             : /************************************************************************/
    3583             : 
    3584             : /* static */
    3585          23 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
    3586             : {
    3587          69 :     return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
    3588          69 :            osName == GDAL_ARG_NAME_OVERWRITE ||
    3589          46 :            osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
    3590             : }
    3591             : 
    3592             : /************************************************************************/
    3593             : /*                   GDALAlgorithm::HasOutputString()                   */
    3594             : /************************************************************************/
    3595             : 
    3596          74 : bool GDALAlgorithm::HasOutputString() const
    3597             : {
    3598          74 :     auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
    3599          74 :     return outputStringArg && outputStringArg->IsOutput();
    3600             : }
    3601             : 
    3602             : /************************************************************************/
    3603             : /*                       GDALAlgorithm::GetArg()                        */
    3604             : /************************************************************************/
    3605             : 
    3606      471468 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
    3607             :                                         bool suggestionAllowed, bool isConst)
    3608             : {
    3609      471468 :     const auto nPos = osName.find_first_not_of('-');
    3610      471468 :     if (nPos == std::string::npos)
    3611          27 :         return nullptr;
    3612      942882 :     std::string osKey = osName.substr(nPos);
    3613             :     {
    3614      471441 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3615      471441 :         if (oIter != m_mapLongNameToArg.end())
    3616      437449 :             return oIter->second;
    3617             :     }
    3618             :     {
    3619       33992 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    3620       33992 :         if (oIter != m_mapShortNameToArg.end())
    3621           8 :             return oIter->second;
    3622             :     }
    3623             : 
    3624       33984 :     if (!isConst && m_arbitraryLongNameArgsAllowed)
    3625             :     {
    3626          23 :         const auto nDotPos = osKey.find('.');
    3627             :         const std::string osKeyEnd =
    3628          23 :             nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
    3629          23 :         if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
    3630             :         {
    3631             :             m_arbitraryLongNameArgsValuesBool.emplace_back(
    3632           0 :                 std::make_unique<bool>());
    3633           0 :             AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3634           0 :                    m_arbitraryLongNameArgsValuesBool.back().get())
    3635           0 :                 .SetUserProvided();
    3636             :         }
    3637             :         else
    3638             :         {
    3639          46 :             const std::string osKeyInit = osKey;
    3640          23 :             if (osKey == "oo")
    3641           0 :                 osKey = GDAL_ARG_NAME_OPEN_OPTION;
    3642          23 :             else if (osKey == "co")
    3643           0 :                 osKey = GDAL_ARG_NAME_CREATION_OPTION;
    3644          23 :             else if (osKey == "of")
    3645           0 :                 osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
    3646          23 :             else if (osKey == "if")
    3647           0 :                 osKey = GDAL_ARG_NAME_INPUT_FORMAT;
    3648             :             m_arbitraryLongNameArgsValuesStr.emplace_back(
    3649          23 :                 std::make_unique<std::string>());
    3650             :             auto &arg =
    3651          46 :                 AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3652          46 :                        m_arbitraryLongNameArgsValuesStr.back().get())
    3653          23 :                     .SetUserProvided();
    3654          23 :             if (osKey != osKeyInit)
    3655           0 :                 arg.AddAlias(osKeyInit);
    3656             :         }
    3657          23 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3658          23 :         CPLAssert(oIter != m_mapLongNameToArg.end());
    3659          23 :         return oIter->second;
    3660             :     }
    3661             : 
    3662       33961 :     if (suggestionAllowed)
    3663             :     {
    3664          14 :         const auto suggestions = GetSuggestionsForArgumentName(osName);
    3665           7 :         if (!suggestions.empty())
    3666             :         {
    3667           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3668             :                      "Argument '%s' is unknown. Do you mean %s?",
    3669             :                      osName.c_str(),
    3670           4 :                      FormatSuggestionsAsString(suggestions,
    3671             :                                                /* addDashDashPrefix = */ false)
    3672             :                          .c_str());
    3673             :         }
    3674             :     }
    3675             : 
    3676       33961 :     return nullptr;
    3677             : }
    3678             : 
    3679             : /************************************************************************/
    3680             : /*                     GDALAlgorithm::AddAliasFor()                     */
    3681             : /************************************************************************/
    3682             : 
    3683             : //! @cond Doxygen_Suppress
    3684       81955 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    3685             :                                 const std::string &alias)
    3686             : {
    3687       81955 :     if (cpl::contains(m_mapLongNameToArg, alias))
    3688             :     {
    3689           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    3690             :                     alias.c_str());
    3691             :     }
    3692             :     else
    3693             :     {
    3694       81954 :         m_mapLongNameToArg[alias] = arg;
    3695             :     }
    3696       81955 : }
    3697             : 
    3698             : //! @endcond
    3699             : 
    3700             : /************************************************************************/
    3701             : /*                GDALAlgorithm::AddShortNameAliasFor()                 */
    3702             : /************************************************************************/
    3703             : 
    3704             : //! @cond Doxygen_Suppress
    3705          48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
    3706             :                                          char shortNameAlias)
    3707             : {
    3708          96 :     std::string alias;
    3709          48 :     alias += shortNameAlias;
    3710          48 :     if (cpl::contains(m_mapShortNameToArg, alias))
    3711             :     {
    3712           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    3713             :                     "Short name '%s' already declared.", alias.c_str());
    3714             :     }
    3715             :     else
    3716             :     {
    3717          48 :         m_mapShortNameToArg[alias] = arg;
    3718             :     }
    3719          48 : }
    3720             : 
    3721             : //! @endcond
    3722             : 
    3723             : /************************************************************************/
    3724             : /*                    GDALAlgorithm::SetPositional()                    */
    3725             : /************************************************************************/
    3726             : 
    3727             : //! @cond Doxygen_Suppress
    3728       22269 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    3729             : {
    3730       22269 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    3731             :                         arg) == m_positionalArgs.end());
    3732       22269 :     m_positionalArgs.push_back(arg);
    3733       22269 : }
    3734             : 
    3735             : //! @endcond
    3736             : 
    3737             : /************************************************************************/
    3738             : /*                  GDALAlgorithm::HasSubAlgorithms()                   */
    3739             : /************************************************************************/
    3740             : 
    3741       13135 : bool GDALAlgorithm::HasSubAlgorithms() const
    3742             : {
    3743       13135 :     if (!m_subAlgRegistry.empty())
    3744        3350 :         return true;
    3745        9785 :     return !GDALGlobalAlgorithmRegistry::GetSingleton()
    3746       19570 :                 .GetDeclaredSubAlgorithmNames(m_callPath)
    3747        9785 :                 .empty();
    3748             : }
    3749             : 
    3750             : /************************************************************************/
    3751             : /*                GDALAlgorithm::GetSubAlgorithmNames()                 */
    3752             : /************************************************************************/
    3753             : 
    3754        1378 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
    3755             : {
    3756        1378 :     std::vector<std::string> ret = m_subAlgRegistry.GetNames();
    3757        1378 :     const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
    3758        2756 :                            .GetDeclaredSubAlgorithmNames(m_callPath);
    3759        1378 :     ret.insert(ret.end(), other.begin(), other.end());
    3760        1378 :     if (!other.empty())
    3761         461 :         std::sort(ret.begin(), ret.end());
    3762        2756 :     return ret;
    3763             : }
    3764             : 
    3765             : /************************************************************************/
    3766             : /*                       GDALAlgorithm::AddArg()                        */
    3767             : /************************************************************************/
    3768             : 
    3769             : GDALInConstructionAlgorithmArg &
    3770      318530 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    3771             : {
    3772      318530 :     auto argRaw = arg.get();
    3773      318530 :     const auto &longName = argRaw->GetName();
    3774      318530 :     if (!longName.empty())
    3775             :     {
    3776      318517 :         if (longName[0] == '-')
    3777             :         {
    3778           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3779             :                         "Long name '%s' should not start with '-'",
    3780             :                         longName.c_str());
    3781             :         }
    3782      318517 :         if (longName.find('=') != std::string::npos)
    3783             :         {
    3784           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3785             :                         "Long name '%s' should not contain a '=' character",
    3786             :                         longName.c_str());
    3787             :         }
    3788      318517 :         if (cpl::contains(m_mapLongNameToArg, longName))
    3789             :         {
    3790           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3791             :                         "Long name '%s' already declared", longName.c_str());
    3792             :         }
    3793      318517 :         m_mapLongNameToArg[longName] = argRaw;
    3794             :     }
    3795      318530 :     const auto &shortName = argRaw->GetShortName();
    3796      318530 :     if (!shortName.empty())
    3797             :     {
    3798      154352 :         if (shortName.size() != 1 ||
    3799       77176 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    3800          65 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    3801           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    3802             :         {
    3803           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3804             :                         "Short name '%s' should be a single letter or digit",
    3805             :                         shortName.c_str());
    3806             :         }
    3807       77176 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    3808             :         {
    3809           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3810             :                         "Short name '%s' already declared", shortName.c_str());
    3811             :         }
    3812       77176 :         m_mapShortNameToArg[shortName] = argRaw;
    3813             :     }
    3814      318530 :     m_args.emplace_back(std::move(arg));
    3815             :     return *(
    3816      318530 :         cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    3817             : }
    3818             : 
    3819             : GDALInConstructionAlgorithmArg &
    3820      142131 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3821             :                       const std::string &helpMessage, bool *pValue)
    3822             : {
    3823      142131 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3824             :         this,
    3825      284262 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    3826      284262 :         pValue));
    3827             : }
    3828             : 
    3829             : GDALInConstructionAlgorithmArg &
    3830       51732 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3831             :                       const std::string &helpMessage, std::string *pValue)
    3832             : {
    3833       51732 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3834             :         this,
    3835      103464 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    3836      103464 :         pValue));
    3837             : }
    3838             : 
    3839             : GDALInConstructionAlgorithmArg &
    3840       12531 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3841             :                       const std::string &helpMessage, int *pValue)
    3842             : {
    3843       12531 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3844             :         this,
    3845       25062 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    3846       25062 :         pValue));
    3847             : }
    3848             : 
    3849             : GDALInConstructionAlgorithmArg &
    3850       10242 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3851             :                       const std::string &helpMessage, double *pValue)
    3852             : {
    3853       10242 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3854             :         this,
    3855       20484 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    3856       20484 :         pValue));
    3857             : }
    3858             : 
    3859             : GDALInConstructionAlgorithmArg &
    3860       12130 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3861             :                       const std::string &helpMessage,
    3862             :                       GDALArgDatasetValue *pValue, GDALArgDatasetType type)
    3863             : {
    3864       24260 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3865             :                            this,
    3866       24260 :                            GDALAlgorithmArgDecl(longName, chShortName,
    3867             :                                                 helpMessage, GAAT_DATASET),
    3868       12130 :                            pValue))
    3869       12130 :                     .SetDatasetType(type);
    3870       12130 :     pValue->SetOwnerArgument(&arg);
    3871       12130 :     return arg;
    3872             : }
    3873             : 
    3874             : GDALInConstructionAlgorithmArg &
    3875       67974 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3876             :                       const std::string &helpMessage,
    3877             :                       std::vector<std::string> *pValue)
    3878             : {
    3879       67974 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3880             :         this,
    3881      135948 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3882             :                              GAAT_STRING_LIST),
    3883      135948 :         pValue));
    3884             : }
    3885             : 
    3886             : GDALInConstructionAlgorithmArg &
    3887        2259 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3888             :                       const std::string &helpMessage, std::vector<int> *pValue)
    3889             : {
    3890        2259 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3891             :         this,
    3892        4518 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3893             :                              GAAT_INTEGER_LIST),
    3894        4518 :         pValue));
    3895             : }
    3896             : 
    3897             : GDALInConstructionAlgorithmArg &
    3898        5280 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3899             :                       const std::string &helpMessage,
    3900             :                       std::vector<double> *pValue)
    3901             : {
    3902        5280 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3903             :         this,
    3904       10560 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3905             :                              GAAT_REAL_LIST),
    3906       10560 :         pValue));
    3907             : }
    3908             : 
    3909             : GDALInConstructionAlgorithmArg &
    3910       14251 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3911             :                       const std::string &helpMessage,
    3912             :                       std::vector<GDALArgDatasetValue> *pValue,
    3913             :                       GDALArgDatasetType type)
    3914             : {
    3915       28502 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3916             :                       this,
    3917       28502 :                       GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3918             :                                            GAAT_DATASET_LIST),
    3919       14251 :                       pValue))
    3920       28502 :         .SetDatasetType(type);
    3921             : }
    3922             : 
    3923             : /************************************************************************/
    3924             : /*                            MsgOrDefault()                            */
    3925             : /************************************************************************/
    3926             : 
    3927      106432 : inline const char *MsgOrDefault(const char *helpMessage,
    3928             :                                 const char *defaultMessage)
    3929             : {
    3930      106432 :     return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
    3931             : }
    3932             : 
    3933             : /************************************************************************/
    3934             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
    3935             : /************************************************************************/
    3936             : 
    3937             : /* static */
    3938       17456 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
    3939             :     GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
    3940             : {
    3941             :     arg.SetAutoCompleteFunction(
    3942           7 :         [&arg,
    3943        2467 :          type](const std::string &currentValue) -> std::vector<std::string>
    3944             :         {
    3945          14 :             std::vector<std::string> oRet;
    3946             : 
    3947           7 :             if (arg.IsHidden())
    3948           0 :                 return oRet;
    3949             : 
    3950             :             {
    3951           7 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3952             :                 VSIStatBufL sStat;
    3953          10 :                 if (!currentValue.empty() && currentValue.back() != '/' &&
    3954           3 :                     VSIStatL(currentValue.c_str(), &sStat) == 0)
    3955             :                 {
    3956           0 :                     return oRet;
    3957             :                 }
    3958             :             }
    3959             : 
    3960           7 :             auto poDM = GetGDALDriverManager();
    3961          14 :             std::set<std::string> oExtensions;
    3962           7 :             if (type)
    3963             :             {
    3964        1374 :                 for (int i = 0; i < poDM->GetDriverCount(); ++i)
    3965             :                 {
    3966        1368 :                     auto poDriver = poDM->GetDriver(i);
    3967        3876 :                     if (((type & GDAL_OF_RASTER) != 0 &&
    3968        1140 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    3969         588 :                         ((type & GDAL_OF_VECTOR) != 0 &&
    3970        2873 :                          poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    3971         497 :                         ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    3972           0 :                          poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    3973             :                     {
    3974             :                         const char *pszExtensions =
    3975         871 :                             poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    3976         871 :                         if (pszExtensions)
    3977             :                         {
    3978             :                             const CPLStringList aosExts(
    3979        1154 :                                 CSLTokenizeString2(pszExtensions, " ", 0));
    3980        1303 :                             for (const char *pszExt : cpl::Iterate(aosExts))
    3981         726 :                                 oExtensions.insert(CPLString(pszExt).tolower());
    3982             :                         }
    3983             :                     }
    3984             :                 }
    3985             :             }
    3986             : 
    3987          14 :             std::string osDir;
    3988          14 :             const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
    3989          14 :             std::string osPrefix;
    3990           7 :             if (STARTS_WITH(currentValue.c_str(), "/vsi"))
    3991             :             {
    3992          79 :                 for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
    3993             :                 {
    3994          78 :                     if (STARTS_WITH(currentValue.c_str(), pszPrefix))
    3995             :                     {
    3996           2 :                         osPrefix = pszPrefix;
    3997           2 :                         break;
    3998             :                     }
    3999             :                 }
    4000           3 :                 if (osPrefix.empty())
    4001           1 :                     return aosVSIPrefixes;
    4002           2 :                 if (currentValue == osPrefix)
    4003           1 :                     osDir = osPrefix;
    4004             :             }
    4005           6 :             if (osDir.empty())
    4006             :             {
    4007           5 :                 osDir = CPLGetDirnameSafe(currentValue.c_str());
    4008           5 :                 if (!osPrefix.empty() && osDir.size() < osPrefix.size())
    4009           0 :                     osDir = std::move(osPrefix);
    4010             :             }
    4011             : 
    4012           6 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    4013          12 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    4014           6 :             if (currentValue.empty())
    4015           1 :                 osDir.clear();
    4016             :             const std::string currentFilename =
    4017          12 :                 CPLGetFilename(currentValue.c_str());
    4018           6 :             if (psDir)
    4019             :             {
    4020         448 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    4021             :                 {
    4022         443 :                     if ((currentFilename.empty() ||
    4023         221 :                          STARTS_WITH(psEntry->pszName,
    4024         223 :                                      currentFilename.c_str())) &&
    4025         223 :                         strcmp(psEntry->pszName, ".") != 0 &&
    4026        1331 :                         strcmp(psEntry->pszName, "..") != 0 &&
    4027         223 :                         (oExtensions.empty() ||
    4028         222 :                          !strstr(psEntry->pszName, ".aux.xml")))
    4029             :                     {
    4030         882 :                         if (oExtensions.empty() ||
    4031         220 :                             cpl::contains(
    4032             :                                 oExtensions,
    4033         441 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    4034         661 :                                     .tolower()) ||
    4035         188 :                             VSI_ISDIR(psEntry->nMode))
    4036             :                         {
    4037          74 :                             std::string osVal;
    4038          37 :                             if (osDir.empty() || osDir == ".")
    4039           4 :                                 osVal = psEntry->pszName;
    4040             :                             else
    4041          66 :                                 osVal = CPLFormFilenameSafe(
    4042          66 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    4043          37 :                             if (VSI_ISDIR(psEntry->nMode))
    4044           4 :                                 osVal += osSep;
    4045          37 :                             oRet.push_back(std::move(osVal));
    4046             :                         }
    4047             :                     }
    4048         443 :                 }
    4049           5 :                 VSICloseDir(psDir);
    4050             :             }
    4051           6 :             return oRet;
    4052       17456 :         });
    4053       17456 : }
    4054             : 
    4055             : /************************************************************************/
    4056             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    4057             : /************************************************************************/
    4058             : 
    4059         854 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    4060             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4061             :     bool positionalAndRequired, const char *helpMessage)
    4062             : {
    4063             :     auto &arg = AddArg(
    4064             :         GDAL_ARG_NAME_INPUT, 'i',
    4065             :         MsgOrDefault(helpMessage,
    4066             :                      CPLSPrintf("Input %s dataset",
    4067         854 :                                 GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4068        1708 :         pValue, type);
    4069         854 :     if (positionalAndRequired)
    4070         847 :         arg.SetPositional().SetRequired();
    4071             : 
    4072         854 :     SetAutoCompleteFunctionForFilename(arg, type);
    4073             : 
    4074         854 :     AddValidationAction(
    4075         157 :         [pValue]()
    4076             :         {
    4077         156 :             if (pValue->GetName() == "-")
    4078           1 :                 pValue->Set("/vsistdin/");
    4079         156 :             return true;
    4080             :         });
    4081             : 
    4082         854 :     return arg;
    4083             : }
    4084             : 
    4085             : /************************************************************************/
    4086             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    4087             : /************************************************************************/
    4088             : 
    4089       13787 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    4090             :     std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
    4091             :     bool positionalAndRequired, const char *helpMessage)
    4092             : {
    4093             :     auto &arg =
    4094             :         AddArg(GDAL_ARG_NAME_INPUT, 'i',
    4095             :                MsgOrDefault(
    4096             :                    helpMessage,
    4097             :                    CPLSPrintf("Input %s datasets",
    4098       13787 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4099       41361 :                pValue, type)
    4100       13787 :             .SetPackedValuesAllowed(false);
    4101       13787 :     if (positionalAndRequired)
    4102        1734 :         arg.SetPositional().SetRequired();
    4103             : 
    4104       13787 :     SetAutoCompleteFunctionForFilename(arg, type);
    4105             : 
    4106       13787 :     AddValidationAction(
    4107        6674 :         [pValue]()
    4108             :         {
    4109       12587 :             for (auto &val : *pValue)
    4110             :             {
    4111        5913 :                 if (val.GetName() == "-")
    4112           1 :                     val.Set("/vsistdin/");
    4113             :             }
    4114        6674 :             return true;
    4115             :         });
    4116       13787 :     return arg;
    4117             : }
    4118             : 
    4119             : /************************************************************************/
    4120             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    4121             : /************************************************************************/
    4122             : 
    4123        8534 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
    4124             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4125             :     bool positionalAndRequired, const char *helpMessage)
    4126             : {
    4127             :     auto &arg =
    4128             :         AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    4129             :                MsgOrDefault(
    4130             :                    helpMessage,
    4131             :                    CPLSPrintf("Output %s dataset",
    4132        8534 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4133       25602 :                pValue, type)
    4134        8534 :             .SetIsInput(true)
    4135        8534 :             .SetIsOutput(true)
    4136        8534 :             .SetDatasetInputFlags(GADV_NAME)
    4137        8534 :             .SetDatasetOutputFlags(GADV_OBJECT);
    4138        8534 :     if (positionalAndRequired)
    4139        4392 :         arg.SetPositional().SetRequired();
    4140             : 
    4141        8534 :     AddValidationAction(
    4142       12854 :         [this, &arg, pValue]()
    4143             :         {
    4144        3882 :             if (pValue->GetName() == "-")
    4145           4 :                 pValue->Set("/vsistdout/");
    4146             : 
    4147        3882 :             auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    4148        3830 :             if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    4149        6056 :                 (!outputFormatArg->IsExplicitlySet() ||
    4150        9938 :                  outputFormatArg->Get<std::string>().empty()) &&
    4151        1604 :                 arg.IsExplicitlySet())
    4152             :             {
    4153             :                 const auto vrtCompatible =
    4154        1148 :                     outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4155         192 :                 if (vrtCompatible && !vrtCompatible->empty() &&
    4156        1340 :                     vrtCompatible->front() == "false" &&
    4157        1244 :                     EQUAL(
    4158             :                         CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
    4159             :                         "VRT"))
    4160             :                 {
    4161           6 :                     ReportError(
    4162             :                         CE_Failure, CPLE_NotSupported,
    4163             :                         "VRT output is not supported.%s",
    4164           6 :                         outputFormatArg->GetDescription().find("GDALG") !=
    4165             :                                 std::string::npos
    4166             :                             ? " Consider using the GDALG driver instead (files "
    4167             :                               "with .gdalg.json extension)"
    4168             :                             : "");
    4169           6 :                     return false;
    4170             :                 }
    4171        1142 :                 else if (pValue->GetName().size() > strlen(".gdalg.json") &&
    4172        2261 :                          EQUAL(pValue->GetName()
    4173             :                                    .substr(pValue->GetName().size() -
    4174             :                                            strlen(".gdalg.json"))
    4175             :                                    .c_str(),
    4176        3403 :                                ".gdalg.json") &&
    4177          27 :                          outputFormatArg->GetDescription().find("GDALG") ==
    4178             :                              std::string::npos)
    4179             :                 {
    4180           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4181             :                                 "GDALG output is not supported");
    4182           0 :                     return false;
    4183             :                 }
    4184             :             }
    4185        3876 :             return true;
    4186             :         });
    4187             : 
    4188        8534 :     return arg;
    4189             : }
    4190             : 
    4191             : /************************************************************************/
    4192             : /*                   GDALAlgorithm::AddOverwriteArg()                   */
    4193             : /************************************************************************/
    4194             : 
    4195             : GDALInConstructionAlgorithmArg &
    4196        8405 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
    4197             : {
    4198             :     return AddArg(
    4199             :                GDAL_ARG_NAME_OVERWRITE, 0,
    4200             :                MsgOrDefault(
    4201             :                    helpMessage,
    4202             :                    _("Whether overwriting existing output dataset is allowed")),
    4203       16810 :                pValue)
    4204       16810 :         .SetDefault(false);
    4205             : }
    4206             : 
    4207             : /************************************************************************/
    4208             : /*                GDALAlgorithm::AddOverwriteLayerArg()                 */
    4209             : /************************************************************************/
    4210             : 
    4211             : GDALInConstructionAlgorithmArg &
    4212        3476 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
    4213             : {
    4214        3476 :     AddValidationAction(
    4215        1602 :         [this]
    4216             :         {
    4217        1601 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4218        1601 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4219             :             {
    4220           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4221             :                             "--update argument must exist for "
    4222             :                             "--overwrite-layer, even if hidden");
    4223           1 :                 return false;
    4224             :             }
    4225        1600 :             return true;
    4226             :         });
    4227             :     return AddArg(
    4228             :                GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
    4229             :                MsgOrDefault(
    4230             :                    helpMessage,
    4231             :                    _("Whether overwriting existing output layer is allowed")),
    4232        6952 :                pValue)
    4233        3476 :         .SetDefault(false)
    4234             :         .AddAction(
    4235          19 :             [this]
    4236             :             {
    4237          19 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4238          19 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4239             :                 {
    4240          19 :                     updateArg->Set(true);
    4241             :                 }
    4242        6971 :             });
    4243             : }
    4244             : 
    4245             : /************************************************************************/
    4246             : /*                    GDALAlgorithm::AddUpdateArg()                     */
    4247             : /************************************************************************/
    4248             : 
    4249             : GDALInConstructionAlgorithmArg &
    4250        4039 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
    4251             : {
    4252             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    4253             :                   MsgOrDefault(
    4254             :                       helpMessage,
    4255             :                       _("Whether to open existing dataset in update mode")),
    4256        8078 :                   pValue)
    4257        8078 :         .SetDefault(false);
    4258             : }
    4259             : 
    4260             : /************************************************************************/
    4261             : /*                  GDALAlgorithm::AddAppendLayerArg()                  */
    4262             : /************************************************************************/
    4263             : 
    4264             : GDALInConstructionAlgorithmArg &
    4265        3251 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
    4266             : {
    4267        3251 :     AddValidationAction(
    4268        1557 :         [this]
    4269             :         {
    4270        1556 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4271        1556 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4272             :             {
    4273           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4274             :                             "--update argument must exist for --append, even "
    4275             :                             "if hidden");
    4276           1 :                 return false;
    4277             :             }
    4278        1555 :             return true;
    4279             :         });
    4280             :     return AddArg(GDAL_ARG_NAME_APPEND, 0,
    4281             :                   MsgOrDefault(
    4282             :                       helpMessage,
    4283             :                       _("Whether appending to existing layer is allowed")),
    4284        6502 :                   pValue)
    4285        3251 :         .SetDefault(false)
    4286             :         .AddAction(
    4287          25 :             [this]
    4288             :             {
    4289          25 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4290          25 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4291             :                 {
    4292          25 :                     updateArg->Set(true);
    4293             :                 }
    4294        6527 :             });
    4295             : }
    4296             : 
    4297             : /************************************************************************/
    4298             : /*                GDALAlgorithm::AddOptionsSuggestions()                */
    4299             : /************************************************************************/
    4300             : 
    4301             : /* static */
    4302          30 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
    4303             :                                           const std::string &currentValue,
    4304             :                                           std::vector<std::string> &oRet)
    4305             : {
    4306          30 :     if (!pszXML)
    4307           0 :         return false;
    4308          60 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    4309          30 :     if (!poTree)
    4310           0 :         return false;
    4311             : 
    4312          60 :     std::string typedOptionName = currentValue;
    4313          30 :     const auto posEqual = typedOptionName.find('=');
    4314          60 :     std::string typedValue;
    4315          30 :     if (posEqual != 0 && posEqual != std::string::npos)
    4316             :     {
    4317           2 :         typedValue = currentValue.substr(posEqual + 1);
    4318           2 :         typedOptionName.resize(posEqual);
    4319             :     }
    4320             : 
    4321         453 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4322         423 :          psChild = psChild->psNext)
    4323             :     {
    4324         436 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4325         449 :         if (pszName && typedOptionName == pszName &&
    4326          13 :             (strcmp(psChild->pszValue, "Option") == 0 ||
    4327           2 :              strcmp(psChild->pszValue, "Argument") == 0))
    4328             :         {
    4329          13 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    4330          13 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    4331          13 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    4332          13 :             if (EQUAL(pszType, "string-select"))
    4333             :             {
    4334          90 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    4335          85 :                      psChild2 = psChild2->psNext)
    4336             :                 {
    4337          85 :                     if (EQUAL(psChild2->pszValue, "Value"))
    4338             :                     {
    4339          75 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    4340             :                     }
    4341             :                 }
    4342             :             }
    4343           8 :             else if (EQUAL(pszType, "boolean"))
    4344             :             {
    4345           3 :                 if (typedValue == "YES" || typedValue == "NO")
    4346             :                 {
    4347           1 :                     oRet.push_back(currentValue);
    4348           1 :                     return true;
    4349             :                 }
    4350           2 :                 oRet.push_back("NO");
    4351           2 :                 oRet.push_back("YES");
    4352             :             }
    4353           5 :             else if (EQUAL(pszType, "int"))
    4354             :             {
    4355           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    4356           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    4357             :                 {
    4358           1 :                     const int nMax = atoi(pszMax);
    4359          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    4360          12 :                         oRet.push_back(std::to_string(i));
    4361             :                 }
    4362             :             }
    4363             : 
    4364          12 :             if (oRet.empty())
    4365             :             {
    4366           4 :                 if (pszMin && pszMax)
    4367             :                 {
    4368           1 :                     oRet.push_back(std::string("##"));
    4369           2 :                     oRet.push_back(std::string("validity range: [")
    4370           1 :                                        .append(pszMin)
    4371           1 :                                        .append(",")
    4372           1 :                                        .append(pszMax)
    4373           1 :                                        .append("]"));
    4374             :                 }
    4375           3 :                 else if (pszMin)
    4376             :                 {
    4377           1 :                     oRet.push_back(std::string("##"));
    4378           1 :                     oRet.push_back(
    4379           1 :                         std::string("validity range: >= ").append(pszMin));
    4380             :                 }
    4381           2 :                 else if (pszMax)
    4382             :                 {
    4383           1 :                     oRet.push_back(std::string("##"));
    4384           1 :                     oRet.push_back(
    4385           1 :                         std::string("validity range: <= ").append(pszMax));
    4386             :                 }
    4387           1 :                 else if (const char *pszDescription =
    4388           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    4389             :                 {
    4390           1 :                     oRet.push_back(std::string("##"));
    4391           2 :                     oRet.push_back(std::string("type: ")
    4392           1 :                                        .append(pszType)
    4393           1 :                                        .append(", description: ")
    4394           1 :                                        .append(pszDescription));
    4395             :                 }
    4396             :             }
    4397             : 
    4398          12 :             return true;
    4399             :         }
    4400             :     }
    4401             : 
    4402         367 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4403         350 :          psChild = psChild->psNext)
    4404             :     {
    4405         350 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4406         350 :         if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
    4407           5 :                         strcmp(psChild->pszValue, "Argument") == 0))
    4408             :         {
    4409         347 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    4410         347 :             if (!pszScope ||
    4411          40 :                 (EQUAL(pszScope, "raster") &&
    4412          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    4413          20 :                 (EQUAL(pszScope, "vector") &&
    4414           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    4415             :             {
    4416         327 :                 oRet.push_back(std::string(pszName).append("="));
    4417             :             }
    4418             :         }
    4419             :     }
    4420             : 
    4421          17 :     return false;
    4422             : }
    4423             : 
    4424             : /************************************************************************/
    4425             : /*             GDALAlgorithm::OpenOptionCompleteFunction()              */
    4426             : /************************************************************************/
    4427             : 
    4428             : //! @cond Doxygen_Suppress
    4429             : std::vector<std::string>
    4430           2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
    4431             : {
    4432           2 :     std::vector<std::string> oRet;
    4433             : 
    4434           2 :     int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    4435           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4436           4 :     if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
    4437           2 :                      inputArg->GetType() == GAAT_DATASET_LIST))
    4438             :     {
    4439           2 :         datasetType = inputArg->GetDatasetType();
    4440             :     }
    4441             : 
    4442           2 :     auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4443           4 :     if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    4444           2 :         inputFormat->IsExplicitlySet())
    4445             :     {
    4446             :         const auto &aosAllowedDrivers =
    4447           1 :             inputFormat->Get<std::vector<std::string>>();
    4448           1 :         if (aosAllowedDrivers.size() == 1)
    4449             :         {
    4450           2 :             auto poDriver = GetGDALDriverManager()->GetDriverByName(
    4451           1 :                 aosAllowedDrivers[0].c_str());
    4452           1 :             if (poDriver)
    4453             :             {
    4454           1 :                 AddOptionsSuggestions(
    4455           1 :                     poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    4456             :                     datasetType, currentValue, oRet);
    4457             :             }
    4458           1 :             return oRet;
    4459             :         }
    4460             :     }
    4461             : 
    4462           1 :     const auto AddSuggestions = [datasetType, &currentValue,
    4463         373 :                                  &oRet](const GDALArgDatasetValue &datasetValue)
    4464             :     {
    4465           1 :         auto poDM = GetGDALDriverManager();
    4466             : 
    4467           1 :         const auto &osDSName = datasetValue.GetName();
    4468           1 :         const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    4469           1 :         if (!osExt.empty())
    4470             :         {
    4471           1 :             std::set<std::string> oVisitedExtensions;
    4472         229 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4473             :             {
    4474         228 :                 auto poDriver = poDM->GetDriver(i);
    4475         684 :                 if (((datasetType & GDAL_OF_RASTER) != 0 &&
    4476         228 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    4477          72 :                     ((datasetType & GDAL_OF_VECTOR) != 0 &&
    4478         456 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    4479          72 :                     ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    4480           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    4481             :                 {
    4482             :                     const char *pszExtensions =
    4483         156 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    4484         156 :                     if (pszExtensions)
    4485             :                     {
    4486             :                         const CPLStringList aosExts(
    4487         103 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    4488         227 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    4489             :                         {
    4490         128 :                             if (EQUAL(pszExt, osExt.c_str()) &&
    4491           3 :                                 !cpl::contains(oVisitedExtensions, pszExt))
    4492             :                             {
    4493           1 :                                 oVisitedExtensions.insert(pszExt);
    4494           1 :                                 if (AddOptionsSuggestions(
    4495             :                                         poDriver->GetMetadataItem(
    4496           1 :                                             GDAL_DMD_OPENOPTIONLIST),
    4497             :                                         datasetType, currentValue, oRet))
    4498             :                                 {
    4499           0 :                                     return;
    4500             :                                 }
    4501           1 :                                 break;
    4502             :                             }
    4503             :                         }
    4504             :                     }
    4505             :                 }
    4506             :             }
    4507             :         }
    4508           1 :     };
    4509             : 
    4510           1 :     if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4511             :     {
    4512           0 :         auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    4513           0 :         AddSuggestions(datasetValue);
    4514             :     }
    4515           1 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4516             :     {
    4517           1 :         auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4518           1 :         if (datasetValues.size() == 1)
    4519           1 :             AddSuggestions(datasetValues[0]);
    4520             :     }
    4521             : 
    4522           1 :     return oRet;
    4523             : }
    4524             : 
    4525             : //! @endcond
    4526             : 
    4527             : /************************************************************************/
    4528             : /*                  GDALAlgorithm::AddOpenOptionsArg()                  */
    4529             : /************************************************************************/
    4530             : 
    4531             : GDALInConstructionAlgorithmArg &
    4532        9317 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
    4533             :                                  const char *helpMessage)
    4534             : {
    4535             :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
    4536       18634 :                        MsgOrDefault(helpMessage, _("Open options")), pValue)
    4537       18634 :                     .AddAlias("oo")
    4538       18634 :                     .SetMetaVar("<KEY>=<VALUE>")
    4539        9317 :                     .SetPackedValuesAllowed(false)
    4540        9317 :                     .SetCategory(GAAC_ADVANCED);
    4541             : 
    4542          31 :     arg.AddValidationAction([this, &arg]()
    4543        9348 :                             { return ParseAndValidateKeyValue(arg); });
    4544             : 
    4545             :     arg.SetAutoCompleteFunction(
    4546           2 :         [this](const std::string &currentValue)
    4547        9319 :         { return OpenOptionCompleteFunction(currentValue); });
    4548             : 
    4549        9317 :     return arg;
    4550             : }
    4551             : 
    4552             : /************************************************************************/
    4553             : /*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
    4554             : /************************************************************************/
    4555             : 
    4556             : GDALInConstructionAlgorithmArg &
    4557        3249 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
    4558             :                                        const char *helpMessage)
    4559             : {
    4560             :     auto &arg =
    4561             :         AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
    4562        6498 :                MsgOrDefault(helpMessage, _("Output open options")), pValue)
    4563        6498 :             .AddAlias("output-oo")
    4564        6498 :             .SetMetaVar("<KEY>=<VALUE>")
    4565        3249 :             .SetPackedValuesAllowed(false)
    4566        3249 :             .SetCategory(GAAC_ADVANCED);
    4567             : 
    4568           0 :     arg.AddValidationAction([this, &arg]()
    4569        3249 :                             { return ParseAndValidateKeyValue(arg); });
    4570             : 
    4571             :     arg.SetAutoCompleteFunction(
    4572           0 :         [this](const std::string &currentValue)
    4573        3249 :         { return OpenOptionCompleteFunction(currentValue); });
    4574             : 
    4575        3249 :     return arg;
    4576             : }
    4577             : 
    4578             : /************************************************************************/
    4579             : /*                           ValidateFormat()                           */
    4580             : /************************************************************************/
    4581             : 
    4582        4755 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
    4583             :                                    bool bStreamAllowed,
    4584             :                                    bool bGDALGAllowed) const
    4585             : {
    4586        4755 :     if (arg.GetChoices().empty())
    4587             :     {
    4588             :         const auto Validate =
    4589       20519 :             [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
    4590             :         {
    4591        4650 :             if (const auto extraFormats =
    4592        4650 :                     arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4593             :             {
    4594          60 :                 for (const auto &extraFormat : *extraFormats)
    4595             :                 {
    4596          48 :                     if (EQUAL(val.c_str(), extraFormat.c_str()))
    4597          14 :                         return true;
    4598             :                 }
    4599             :             }
    4600             : 
    4601        4636 :             if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
    4602        1821 :                 return true;
    4603             : 
    4604        2821 :             if (EQUAL(val.c_str(), "GDALG") &&
    4605           6 :                 arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
    4606             :             {
    4607           2 :                 if (bGDALGAllowed)
    4608             :                 {
    4609           2 :                     return true;
    4610             :                 }
    4611             :                 else
    4612             :                 {
    4613           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4614             :                                 "GDALG output is not supported.");
    4615           0 :                     return false;
    4616             :                 }
    4617             :             }
    4618             : 
    4619             :             const auto vrtCompatible =
    4620        2813 :                 arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4621         540 :             if (vrtCompatible && !vrtCompatible->empty() &&
    4622        3353 :                 vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
    4623             :             {
    4624           7 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4625             :                             "VRT output is not supported.%s",
    4626             :                             bGDALGAllowed
    4627             :                                 ? " Consider using the GDALG driver instead "
    4628             :                                   "(files with .gdalg.json extension)."
    4629             :                                 : "");
    4630           7 :                 return false;
    4631             :             }
    4632             : 
    4633             :             const auto allowedFormats =
    4634        2806 :                 arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4635        2859 :             if (allowedFormats && !allowedFormats->empty() &&
    4636           0 :                 std::find(allowedFormats->begin(), allowedFormats->end(),
    4637        2859 :                           val) != allowedFormats->end())
    4638             :             {
    4639          12 :                 return true;
    4640             :             }
    4641             : 
    4642             :             const auto excludedFormats =
    4643        2794 :                 arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4644        2841 :             if (excludedFormats && !excludedFormats->empty() &&
    4645           0 :                 std::find(excludedFormats->begin(), excludedFormats->end(),
    4646        2841 :                           val) != excludedFormats->end())
    4647             :             {
    4648           0 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4649             :                             "%s output is not supported.", val.c_str());
    4650           0 :                 return false;
    4651             :             }
    4652             : 
    4653        2794 :             auto hDriver = GDALGetDriverByName(val.c_str());
    4654        2794 :             if (!hDriver)
    4655             :             {
    4656             :                 auto poMissingDriver =
    4657           4 :                     GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
    4658           4 :                 if (poMissingDriver)
    4659             :                 {
    4660             :                     const std::string msg =
    4661           0 :                         GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
    4662           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4663             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4664             :                                 "not found but is known. However plugin %s",
    4665           0 :                                 arg.GetName().c_str(), val.c_str(),
    4666             :                                 msg.c_str());
    4667             :                 }
    4668             :                 else
    4669             :                 {
    4670           8 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4671             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4672             :                                 "does not exist.",
    4673           4 :                                 arg.GetName().c_str(), val.c_str());
    4674             :                 }
    4675           4 :                 return false;
    4676             :             }
    4677             : 
    4678        2790 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4679        2790 :             if (caps)
    4680             :             {
    4681        8464 :                 for (const std::string &cap : *caps)
    4682             :                 {
    4683             :                     const char *pszVal =
    4684        5705 :                         GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
    4685        5705 :                     if (!(pszVal && pszVal[0]))
    4686             :                     {
    4687        1587 :                         if (cap == GDAL_DCAP_CREATECOPY &&
    4688           0 :                             std::find(caps->begin(), caps->end(),
    4689         792 :                                       GDAL_DCAP_RASTER) != caps->end() &&
    4690         792 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
    4691        1587 :                                                 nullptr) &&
    4692         792 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
    4693             :                                                 nullptr))
    4694             :                         {
    4695             :                             // if it supports Create, it supports CreateCopy
    4696             :                         }
    4697           3 :                         else if (cap == GDAL_DMD_EXTENSIONS)
    4698             :                         {
    4699           2 :                             ReportError(
    4700             :                                 CE_Failure, CPLE_AppDefined,
    4701             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4702             :                                 "does "
    4703             :                                 "not advertise any file format extension.",
    4704           1 :                                 arg.GetName().c_str(), val.c_str());
    4705           3 :                             return false;
    4706             :                         }
    4707             :                         else
    4708             :                         {
    4709           2 :                             if (cap == GDAL_DCAP_CREATE)
    4710             :                             {
    4711           1 :                                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4712           1 :                                 if (updateArg &&
    4713           2 :                                     updateArg->GetType() == GAAT_BOOLEAN &&
    4714           1 :                                     updateArg->IsExplicitlySet())
    4715             :                                 {
    4716           0 :                                     continue;
    4717             :                                 }
    4718             : 
    4719           2 :                                 ReportError(
    4720             :                                     CE_Failure, CPLE_AppDefined,
    4721             :                                     "Invalid value for argument '%s'. "
    4722             :                                     "Driver '%s' does not have write support.",
    4723           1 :                                     arg.GetName().c_str(), val.c_str());
    4724           1 :                                 return false;
    4725             :                             }
    4726             :                             else
    4727             :                             {
    4728           2 :                                 ReportError(
    4729             :                                     CE_Failure, CPLE_AppDefined,
    4730             :                                     "Invalid value for argument '%s'. Driver "
    4731             :                                     "'%s' "
    4732             :                                     "does "
    4733             :                                     "not expose the required '%s' capability.",
    4734           1 :                                     arg.GetName().c_str(), val.c_str(),
    4735             :                                     cap.c_str());
    4736           1 :                                 return false;
    4737             :                             }
    4738             :                         }
    4739             :                     }
    4740             :                 }
    4741             :             }
    4742        2787 :             return true;
    4743        4653 :         };
    4744             : 
    4745        4653 :         if (arg.GetType() == GAAT_STRING)
    4746             :         {
    4747        4640 :             return Validate(arg.Get<std::string>());
    4748             :         }
    4749          15 :         else if (arg.GetType() == GAAT_STRING_LIST)
    4750             :         {
    4751          25 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    4752             :             {
    4753          12 :                 if (!Validate(val))
    4754           2 :                     return false;
    4755             :             }
    4756             :         }
    4757             :     }
    4758             : 
    4759         115 :     return true;
    4760             : }
    4761             : 
    4762             : /************************************************************************/
    4763             : /*                     FormatAutoCompleteFunction()                     */
    4764             : /************************************************************************/
    4765             : 
    4766             : /* static */
    4767           7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
    4768             :     const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
    4769             : {
    4770           7 :     std::vector<std::string> res;
    4771           7 :     auto poDM = GetGDALDriverManager();
    4772           7 :     const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4773           7 :     const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4774           7 :     const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4775           7 :     const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4776           7 :     if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4777           0 :         res = std::move(*extraFormats);
    4778        1602 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4779             :     {
    4780        1595 :         auto poDriver = poDM->GetDriver(i);
    4781             : 
    4782           0 :         if (vrtCompatible && !vrtCompatible->empty() &&
    4783        1595 :             vrtCompatible->front() == "false" &&
    4784           0 :             EQUAL(poDriver->GetDescription(), "VRT"))
    4785             :         {
    4786             :             // do nothing
    4787             :         }
    4788        1595 :         else if (allowedFormats && !allowedFormats->empty() &&
    4789           0 :                  std::find(allowedFormats->begin(), allowedFormats->end(),
    4790        1595 :                            poDriver->GetDescription()) != allowedFormats->end())
    4791             :         {
    4792           0 :             res.push_back(poDriver->GetDescription());
    4793             :         }
    4794        1595 :         else if (excludedFormats && !excludedFormats->empty() &&
    4795           0 :                  std::find(excludedFormats->begin(), excludedFormats->end(),
    4796           0 :                            poDriver->GetDescription()) !=
    4797        1595 :                      excludedFormats->end())
    4798             :         {
    4799           0 :             continue;
    4800             :         }
    4801        1595 :         else if (caps)
    4802             :         {
    4803        1595 :             bool ok = true;
    4804        3155 :             for (const std::string &cap : *caps)
    4805             :             {
    4806        2374 :                 if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
    4807             :                 {
    4808           0 :                     if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    4809           0 :                         !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
    4810             :                     {
    4811           0 :                         ok = false;
    4812           0 :                         break;
    4813             :                     }
    4814             :                 }
    4815        2374 :                 else if (const char *pszVal =
    4816        2374 :                              poDriver->GetMetadataItem(cap.c_str());
    4817        1488 :                          pszVal && pszVal[0])
    4818             :                 {
    4819             :                 }
    4820        1274 :                 else if (cap == GDAL_DCAP_CREATECOPY &&
    4821           0 :                          (std::find(caps->begin(), caps->end(),
    4822         388 :                                     GDAL_DCAP_RASTER) != caps->end() &&
    4823        1662 :                           poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
    4824         388 :                          poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    4825             :                 {
    4826             :                     // if it supports Create, it supports CreateCopy
    4827             :                 }
    4828             :                 else
    4829             :                 {
    4830         814 :                     ok = false;
    4831         814 :                     break;
    4832             :                 }
    4833             :             }
    4834        1595 :             if (ok)
    4835             :             {
    4836         781 :                 res.push_back(poDriver->GetDescription());
    4837             :             }
    4838             :         }
    4839             :     }
    4840           7 :     if (bGDALGAllowed)
    4841           4 :         res.push_back("GDALG");
    4842           7 :     return res;
    4843             : }
    4844             : 
    4845             : /************************************************************************/
    4846             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    4847             : /************************************************************************/
    4848             : 
    4849             : GDALInConstructionAlgorithmArg &
    4850        9102 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
    4851             :                                   const char *helpMessage)
    4852             : {
    4853             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
    4854       18204 :                        MsgOrDefault(helpMessage, _("Input formats")), pValue)
    4855       18204 :                     .AddAlias("if")
    4856        9102 :                     .SetCategory(GAAC_ADVANCED);
    4857          15 :     arg.AddValidationAction([this, &arg]()
    4858        9117 :                             { return ValidateFormat(arg, false, false); });
    4859             :     arg.SetAutoCompleteFunction(
    4860           1 :         [&arg](const std::string &)
    4861        9103 :         { return FormatAutoCompleteFunction(arg, false, false); });
    4862        9102 :     return arg;
    4863             : }
    4864             : 
    4865             : /************************************************************************/
    4866             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    4867             : /************************************************************************/
    4868             : 
    4869             : GDALInConstructionAlgorithmArg &
    4870        9553 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
    4871             :                                   bool bGDALGAllowed, const char *helpMessage)
    4872             : {
    4873             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
    4874             :                        MsgOrDefault(helpMessage,
    4875             :                                     bGDALGAllowed
    4876             :                                         ? _("Output format (\"GDALG\" allowed)")
    4877             :                                         : _("Output format")),
    4878       19106 :                        pValue)
    4879       19106 :                     .AddAlias("of")
    4880        9553 :                     .AddAlias("format");
    4881             :     arg.AddValidationAction(
    4882        4736 :         [this, &arg, bStreamAllowed, bGDALGAllowed]()
    4883       14289 :         { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
    4884             :     arg.SetAutoCompleteFunction(
    4885           4 :         [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
    4886             :         {
    4887             :             return FormatAutoCompleteFunction(arg, bStreamAllowed,
    4888           4 :                                               bGDALGAllowed);
    4889        9553 :         });
    4890        9553 :     return arg;
    4891             : }
    4892             : 
    4893             : /************************************************************************/
    4894             : /*                GDALAlgorithm::AddOutputDataTypeArg()                 */
    4895             : /************************************************************************/
    4896             : GDALInConstructionAlgorithmArg &
    4897        1755 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
    4898             :                                     const char *helpMessage)
    4899             : {
    4900             :     auto &arg =
    4901             :         AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
    4902        3510 :                MsgOrDefault(helpMessage, _("Output data type")), pValue)
    4903        3510 :             .AddAlias("ot")
    4904        3510 :             .AddAlias("datatype")
    4905        5265 :             .AddMetadataItem("type", {"GDALDataType"})
    4906             :             .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
    4907             :                         "UInt64", "Int64", "CInt16", "CInt32", "Float16",
    4908        1755 :                         "Float32", "Float64", "CFloat32", "CFloat64")
    4909        1755 :             .SetHiddenChoices("Byte");
    4910        1755 :     return arg;
    4911             : }
    4912             : 
    4913             : /************************************************************************/
    4914             : /*                    GDALAlgorithm::AddNodataArg()                     */
    4915             : /************************************************************************/
    4916             : 
    4917             : GDALInConstructionAlgorithmArg &
    4918         653 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
    4919             :                             const std::string &optionName,
    4920             :                             const char *helpMessage)
    4921             : {
    4922             :     auto &arg = AddArg(
    4923             :         optionName, 0,
    4924             :         MsgOrDefault(helpMessage,
    4925             :                      noneAllowed
    4926             :                          ? _("Assign a specified nodata value to output bands "
    4927             :                              "('none', numeric value, 'nan', 'inf', '-inf')")
    4928             :                          : _("Assign a specified nodata value to output bands "
    4929             :                              "(numeric value, 'nan', 'inf', '-inf')")),
    4930         653 :         pValue);
    4931             :     arg.AddValidationAction(
    4932         336 :         [this, pValue, noneAllowed, optionName]()
    4933             :         {
    4934          73 :             if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
    4935             :             {
    4936          63 :                 char *endptr = nullptr;
    4937          63 :                 CPLStrtod(pValue->c_str(), &endptr);
    4938          63 :                 if (endptr != pValue->c_str() + pValue->size())
    4939             :                 {
    4940           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    4941             :                                 "Value of '%s' should be %sa "
    4942             :                                 "numeric value, 'nan', 'inf' or '-inf'",
    4943             :                                 optionName.c_str(),
    4944             :                                 noneAllowed ? "'none', " : "");
    4945           1 :                     return false;
    4946             :                 }
    4947             :             }
    4948          72 :             return true;
    4949         653 :         });
    4950         653 :     return arg;
    4951             : }
    4952             : 
    4953             : /************************************************************************/
    4954             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    4955             : /************************************************************************/
    4956             : 
    4957             : GDALInConstructionAlgorithmArg &
    4958        6128 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
    4959             : {
    4960             :     return AddArg(
    4961             :                GDAL_ARG_NAME_OUTPUT_STRING, 0,
    4962             :                MsgOrDefault(helpMessage,
    4963             :                             _("Output string, in which the result is placed")),
    4964       12256 :                pValue)
    4965        6128 :         .SetHiddenForCLI()
    4966        6128 :         .SetIsInput(false)
    4967       12256 :         .SetIsOutput(true);
    4968             : }
    4969             : 
    4970             : /************************************************************************/
    4971             : /*                    GDALAlgorithm::AddStdoutArg()                     */
    4972             : /************************************************************************/
    4973             : 
    4974             : GDALInConstructionAlgorithmArg &
    4975        1560 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
    4976             : {
    4977             :     return AddArg(GDAL_ARG_NAME_STDOUT, 0,
    4978             :                   MsgOrDefault(helpMessage,
    4979             :                                _("Directly output on stdout. If enabled, "
    4980             :                                  "output-string will be empty")),
    4981        3120 :                   pValue)
    4982        3120 :         .SetHidden();
    4983             : }
    4984             : 
    4985             : /************************************************************************/
    4986             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    4987             : /************************************************************************/
    4988             : 
    4989             : GDALInConstructionAlgorithmArg &
    4990         218 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
    4991             : {
    4992             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    4993         218 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    4994             : }
    4995             : 
    4996             : /************************************************************************/
    4997             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4998             : /************************************************************************/
    4999             : 
    5000             : GDALInConstructionAlgorithmArg &
    5001          50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
    5002             : {
    5003             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
    5004         100 :                   pValue)
    5005           2 :         .SetAutoCompleteFunction([this](const std::string &)
    5006         102 :                                  { return AutoCompleteArrayName(); });
    5007             : }
    5008             : 
    5009             : /************************************************************************/
    5010             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    5011             : /************************************************************************/
    5012             : 
    5013             : GDALInConstructionAlgorithmArg &
    5014          76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
    5015             :                                const char *helpMessage)
    5016             : {
    5017             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
    5018         152 :                   pValue)
    5019           0 :         .SetAutoCompleteFunction([this](const std::string &)
    5020         152 :                                  { return AutoCompleteArrayName(); });
    5021             : }
    5022             : 
    5023             : /************************************************************************/
    5024             : /*                GDALAlgorithm::AutoCompleteArrayName()                */
    5025             : /************************************************************************/
    5026             : 
    5027           2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
    5028             : {
    5029           2 :     std::vector<std::string> ret;
    5030           4 :     std::string osDSName;
    5031           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    5032           2 :     if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    5033             :     {
    5034           0 :         auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    5035           0 :         if (!inputDatasets.empty())
    5036             :         {
    5037           0 :             osDSName = inputDatasets[0].GetName();
    5038             :         }
    5039             :     }
    5040           2 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET)
    5041             :     {
    5042           2 :         auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
    5043           2 :         osDSName = inputDataset.GetName();
    5044             :     }
    5045             : 
    5046           2 :     if (!osDSName.empty())
    5047             :     {
    5048           4 :         CPLStringList aosAllowedDrivers;
    5049           2 :         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    5050           2 :         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    5051             :             aosAllowedDrivers =
    5052           2 :                 CPLStringList(ifArg->Get<std::vector<std::string>>());
    5053             : 
    5054           4 :         CPLStringList aosOpenOptions;
    5055           2 :         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    5056           2 :         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    5057             :             aosOpenOptions =
    5058           2 :                 CPLStringList(ooArg->Get<std::vector<std::string>>());
    5059             : 
    5060           2 :         if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5061             :                 osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
    5062           4 :                 aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
    5063             :         {
    5064           2 :             if (auto poRG = poDS->GetRootGroup())
    5065             :             {
    5066           1 :                 ret = poRG->GetMDArrayFullNamesRecursive();
    5067             :             }
    5068             :         }
    5069             :     }
    5070             : 
    5071           4 :     return ret;
    5072             : }
    5073             : 
    5074             : /************************************************************************/
    5075             : /*                  GDALAlgorithm::AddMemorySizeArg()                   */
    5076             : /************************************************************************/
    5077             : 
    5078             : GDALInConstructionAlgorithmArg &
    5079         226 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
    5080             :                                 const std::string &optionName,
    5081             :                                 const char *helpMessage)
    5082             : {
    5083         452 :     return AddArg(optionName, 0, helpMessage, pStrValue)
    5084         226 :         .SetDefault(*pStrValue)
    5085             :         .AddValidationAction(
    5086         139 :             [this, pValue, pStrValue]()
    5087             :             {
    5088          47 :                 CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
    5089             :                 GIntBig nBytes;
    5090             :                 bool bUnitSpecified;
    5091          47 :                 if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
    5092          47 :                                        &bUnitSpecified) != CE_None)
    5093             :                 {
    5094           2 :                     return false;
    5095             :                 }
    5096          45 :                 if (!bUnitSpecified)
    5097             :                 {
    5098           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5099             :                                 "Memory size must have a unit or be a "
    5100             :                                 "percentage of usable RAM (2GB, 5%%, etc.)");
    5101           1 :                     return false;
    5102             :                 }
    5103             :                 if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
    5104             :                 {
    5105             :                     // -1 to please CoverityScan
    5106             :                     if (static_cast<std::uint64_t>(nBytes) >
    5107             :                         std::numeric_limits<size_t>::max() - 1U)
    5108             :                     {
    5109             :                         ReportError(CE_Failure, CPLE_AppDefined,
    5110             :                                     "Memory size %s is too large.",
    5111             :                                     pStrValue->c_str());
    5112             :                         return false;
    5113             :                     }
    5114             :                 }
    5115             : 
    5116          44 :                 *pValue = static_cast<size_t>(nBytes);
    5117          44 :                 return true;
    5118         452 :             });
    5119             : }
    5120             : 
    5121             : /************************************************************************/
    5122             : /*                GDALAlgorithm::AddOutputLayerNameArg()                */
    5123             : /************************************************************************/
    5124             : 
    5125             : GDALInConstructionAlgorithmArg &
    5126         476 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
    5127             :                                      const char *helpMessage)
    5128             : {
    5129             :     return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
    5130         476 :                   MsgOrDefault(helpMessage, _("Output layer name")), pValue);
    5131             : }
    5132             : 
    5133             : /************************************************************************/
    5134             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    5135             : /************************************************************************/
    5136             : 
    5137             : GDALInConstructionAlgorithmArg &
    5138         894 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
    5139             :                                const char *helpMessage)
    5140             : {
    5141             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    5142         894 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    5143             : }
    5144             : 
    5145             : /************************************************************************/
    5146             : /*                 GDALAlgorithm::AddGeometryTypeArg()                  */
    5147             : /************************************************************************/
    5148             : 
    5149             : GDALInConstructionAlgorithmArg &
    5150         427 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
    5151             : {
    5152             :     return AddArg("geometry-type", 0,
    5153         854 :                   MsgOrDefault(helpMessage, _("Geometry type")), pValue)
    5154             :         .SetAutoCompleteFunction(
    5155           3 :             [](const std::string &currentValue)
    5156             :             {
    5157           3 :                 std::vector<std::string> oRet;
    5158          51 :                 for (const char *type :
    5159             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
    5160             :                       "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
    5161             :                       "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
    5162             :                       "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
    5163          54 :                       "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
    5164             :                 {
    5165          68 :                     if (currentValue.empty() ||
    5166          17 :                         STARTS_WITH(type, currentValue.c_str()))
    5167             :                     {
    5168          35 :                         oRet.push_back(type);
    5169          35 :                         oRet.push_back(std::string(type).append("Z"));
    5170          35 :                         oRet.push_back(std::string(type).append("M"));
    5171          35 :                         oRet.push_back(std::string(type).append("ZM"));
    5172             :                     }
    5173             :                 }
    5174           3 :                 return oRet;
    5175         854 :             })
    5176             :         .AddValidationAction(
    5177         118 :             [this, pValue]()
    5178             :             {
    5179         107 :                 if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
    5180         115 :                         wkbUnknown &&
    5181           8 :                     !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
    5182             :                 {
    5183           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5184             :                                 "Invalid geometry type '%s'", pValue->c_str());
    5185           3 :                     return false;
    5186             :                 }
    5187         104 :                 return true;
    5188         854 :             });
    5189             : }
    5190             : 
    5191             : /************************************************************************/
    5192             : /*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
    5193             : /************************************************************************/
    5194             : 
    5195             : /* static */
    5196        2991 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
    5197             :     GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
    5198             : {
    5199        2991 :     CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
    5200             :               datasetArg.GetType() == GAAT_DATASET_LIST);
    5201             : 
    5202             :     layerArg.SetAutoCompleteFunction(
    5203          18 :         [&datasetArg](const std::string &currentValue)
    5204             :         {
    5205           6 :             std::vector<std::string> ret;
    5206          12 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5207           6 :             GDALArgDatasetValue *dsVal = nullptr;
    5208           6 :             if (datasetArg.GetType() == GAAT_DATASET)
    5209             :             {
    5210           0 :                 dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
    5211             :             }
    5212             :             else
    5213             :             {
    5214           6 :                 auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
    5215           6 :                 if (val.size() == 1)
    5216             :                 {
    5217           6 :                     dsVal = &val[0];
    5218             :                 }
    5219             :             }
    5220           6 :             if (dsVal && !dsVal->GetName().empty())
    5221             :             {
    5222             :                 auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5223          12 :                     dsVal->GetName().c_str(), GDAL_OF_VECTOR));
    5224           6 :                 if (poDS)
    5225             :                 {
    5226          12 :                     for (auto &&poLayer : poDS->GetLayers())
    5227             :                     {
    5228           6 :                         if (currentValue == poLayer->GetDescription())
    5229             :                         {
    5230           1 :                             ret.clear();
    5231           1 :                             ret.push_back(poLayer->GetDescription());
    5232           1 :                             break;
    5233             :                         }
    5234           5 :                         ret.push_back(poLayer->GetDescription());
    5235             :                     }
    5236             :                 }
    5237             :             }
    5238          12 :             return ret;
    5239        2991 :         });
    5240        2991 : }
    5241             : 
    5242             : /************************************************************************/
    5243             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
    5244             : /************************************************************************/
    5245             : 
    5246         372 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
    5247             :     GDALInConstructionAlgorithmArg &fieldArg,
    5248             :     const GDALAlgorithmArg *layerNameArg, bool attributeFields,
    5249             :     bool geometryFields, std::vector<GDALArgDatasetValue> &datasetArg)
    5250             : {
    5251             : 
    5252             :     fieldArg.SetAutoCompleteFunction(
    5253          10 :         [&datasetArg, layerNameArg, attributeFields,
    5254          43 :          geometryFields](const std::string &currentValue)
    5255             :         {
    5256          20 :             std::set<std::string> ret;
    5257          10 :             if (!datasetArg.empty())
    5258             :             {
    5259          16 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5260             : 
    5261             :                 const auto getLayerFields =
    5262           6 :                     [&ret, &currentValue, attributeFields,
    5263          50 :                      geometryFields](const OGRLayer *poLayer)
    5264             :                 {
    5265           6 :                     const auto poDefn = poLayer->GetLayerDefn();
    5266           6 :                     if (attributeFields)
    5267             :                     {
    5268          24 :                         for (const auto poFieldDefn : poDefn->GetFields())
    5269             :                         {
    5270          18 :                             const char *fieldName = poFieldDefn->GetNameRef();
    5271          18 :                             if (currentValue == fieldName)
    5272             :                             {
    5273           0 :                                 ret.clear();
    5274           0 :                                 ret.insert(fieldName);
    5275           0 :                                 break;
    5276             :                             }
    5277          18 :                             ret.insert(fieldName);
    5278             :                         }
    5279             :                     }
    5280           6 :                     if (geometryFields)
    5281             :                     {
    5282           2 :                         for (const auto poFieldDefn : poDefn->GetGeomFields())
    5283             :                         {
    5284           1 :                             const char *fieldName = poFieldDefn->GetNameRef();
    5285           1 :                             if (fieldName[0] == 0)
    5286           1 :                                 fieldName = "OGR_GEOMETRY";
    5287           1 :                             if (currentValue == fieldName)
    5288             :                             {
    5289           0 :                                 ret.clear();
    5290           0 :                                 ret.insert(fieldName);
    5291           0 :                                 break;
    5292             :                             }
    5293           1 :                             ret.insert(fieldName);
    5294             :                         }
    5295             :                     }
    5296           6 :                 };
    5297             : 
    5298           8 :                 const GDALArgDatasetValue &dsVal = datasetArg[0];
    5299             : 
    5300           8 :                 if (!dsVal.GetName().empty())
    5301             :                 {
    5302             :                     auto poDS = std::unique_ptr<GDALDataset>(
    5303           8 :                         GDALDataset::Open(dsVal.GetName().c_str(),
    5304          16 :                                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
    5305           8 :                     if (poDS)
    5306             :                     {
    5307          16 :                         std::vector<std::string> layerNames;
    5308           8 :                         if (layerNameArg && layerNameArg->IsExplicitlySet())
    5309             :                         {
    5310           4 :                             if (layerNameArg->GetType() == GAAT_STRING_LIST)
    5311             :                             {
    5312             :                                 layerNames =
    5313             :                                     layerNameArg
    5314           2 :                                         ->Get<std::vector<std::string>>();
    5315             :                             }
    5316           2 :                             else if (layerNameArg->GetType() == GAAT_STRING)
    5317             :                             {
    5318           2 :                                 layerNames.push_back(
    5319           2 :                                     layerNameArg->Get<std::string>());
    5320             :                             }
    5321             :                         }
    5322           8 :                         if (layerNames.empty())
    5323             :                         {
    5324             :                             // Loop through all layers
    5325           8 :                             for (const auto *poLayer : poDS->GetLayers())
    5326             :                             {
    5327           4 :                                 getLayerFields(poLayer);
    5328             :                             }
    5329             :                         }
    5330             :                         else
    5331             :                         {
    5332           8 :                             for (const std::string &layerName : layerNames)
    5333             :                             {
    5334             :                                 const auto poLayer =
    5335           4 :                                     poDS->GetLayerByName(layerName.c_str());
    5336           4 :                                 if (poLayer)
    5337             :                                 {
    5338           2 :                                     getLayerFields(poLayer);
    5339             :                                 }
    5340             :                             }
    5341             :                         }
    5342             :                     }
    5343             :                 }
    5344             :             }
    5345          10 :             std::vector<std::string> retVector(ret.begin(), ret.end());
    5346          20 :             return retVector;
    5347         372 :         });
    5348         372 : }
    5349             : 
    5350             : /************************************************************************/
    5351             : /*                   GDALAlgorithm::AddFieldNameArg()                   */
    5352             : /************************************************************************/
    5353             : 
    5354             : GDALInConstructionAlgorithmArg &
    5355         137 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
    5356             : {
    5357             :     return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
    5358         137 :                   pValue);
    5359             : }
    5360             : 
    5361             : /************************************************************************/
    5362             : /*                GDALAlgorithm::ParseFieldDefinition()                 */
    5363             : /************************************************************************/
    5364          67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
    5365             :                                          OGRFieldDefn *poFieldDefn,
    5366             :                                          std::string *posError)
    5367             : {
    5368             :     static const std::regex re(
    5369          67 :         R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
    5370         134 :     std::smatch match;
    5371          67 :     if (std::regex_match(posStrDef, match, re))
    5372             :     {
    5373         132 :         const std::string name = match[1];
    5374         132 :         const std::string type = match[2];
    5375          66 :         const int width = match[3].matched ? std::stoi(match[3]) : 0;
    5376          66 :         const int precision = match[4].matched ? std::stoi(match[4]) : 0;
    5377          66 :         poFieldDefn->SetName(name.c_str());
    5378             : 
    5379          66 :         const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
    5380          66 :         if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
    5381             :         {
    5382           1 :             if (posError)
    5383           1 :                 *posError = "Unsupported field type: " + type;
    5384             : 
    5385           1 :             return false;
    5386             :         }
    5387          65 :         poFieldDefn->SetType(typeEnum);
    5388          65 :         poFieldDefn->SetWidth(width);
    5389          65 :         poFieldDefn->SetPrecision(precision);
    5390          65 :         return true;
    5391             :     }
    5392             : 
    5393           1 :     if (posError)
    5394             :         *posError = "Invalid field definition format. Expected "
    5395           1 :                     "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
    5396             : 
    5397           1 :     return false;
    5398             : }
    5399             : 
    5400             : /************************************************************************/
    5401             : /*                GDALAlgorithm::AddFieldDefinitionArg()                */
    5402             : /************************************************************************/
    5403             : 
    5404             : GDALInConstructionAlgorithmArg &
    5405         131 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
    5406             :                                      std::vector<OGRFieldDefn> *pFieldDefns,
    5407             :                                      const char *helpMessage)
    5408             : {
    5409             :     auto &arg =
    5410             :         AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
    5411         262 :                pValues)
    5412         262 :             .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
    5413         131 :             .SetPackedValuesAllowed(true)
    5414         131 :             .SetRepeatedArgAllowed(true);
    5415             : 
    5416         132 :     auto validationFunction = [this, pFieldDefns, pValues]()
    5417             :     {
    5418          65 :         pFieldDefns->clear();
    5419         130 :         for (const auto &strValue : *pValues)
    5420             :         {
    5421          67 :             OGRFieldDefn fieldDefn("", OFTString);
    5422          67 :             std::string error;
    5423          67 :             if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
    5424             :                                                      &error))
    5425             :             {
    5426           2 :                 ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
    5427           2 :                 return false;
    5428             :             }
    5429             :             // Check uniqueness of field names
    5430          67 :             for (const auto &existingFieldDefn : *pFieldDefns)
    5431             :             {
    5432           2 :                 if (EQUAL(existingFieldDefn.GetNameRef(),
    5433             :                           fieldDefn.GetNameRef()))
    5434             :                 {
    5435           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5436             :                                 "Duplicate field name: '%s'",
    5437             :                                 fieldDefn.GetNameRef());
    5438           0 :                     return false;
    5439             :                 }
    5440             :             }
    5441          65 :             pFieldDefns->push_back(fieldDefn);
    5442             :         }
    5443          63 :         return true;
    5444         131 :     };
    5445             : 
    5446         131 :     arg.AddValidationAction(std::move(validationFunction));
    5447             : 
    5448         131 :     return arg;
    5449             : }
    5450             : 
    5451             : /************************************************************************/
    5452             : /*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
    5453             : /************************************************************************/
    5454             : 
    5455         274 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
    5456             :     OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
    5457             :     std::string *pStrValue, const std::string &argName, const char *helpMessage)
    5458             : {
    5459             :     auto &arg =
    5460         548 :         AddArg(argName.empty() ? std::string("field-type") : argName, 0,
    5461         822 :                MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
    5462             :             .SetAutoCompleteFunction(
    5463           1 :                 [](const std::string &currentValue)
    5464             :                 {
    5465           1 :                     std::vector<std::string> oRet;
    5466           6 :                     for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
    5467             :                     {
    5468             :                         const char *pszSubType =
    5469           5 :                             OGRFieldDefn::GetFieldSubTypeName(
    5470             :                                 static_cast<OGRFieldSubType>(i));
    5471           5 :                         if (pszSubType != nullptr)
    5472             :                         {
    5473           5 :                             if (currentValue.empty() ||
    5474           0 :                                 STARTS_WITH(pszSubType, currentValue.c_str()))
    5475             :                             {
    5476           5 :                                 oRet.push_back(pszSubType);
    5477             :                             }
    5478             :                         }
    5479             :                     }
    5480             : 
    5481          15 :                     for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
    5482             :                     {
    5483             :                         // Skip deprecated
    5484          14 :                         if (static_cast<OGRFieldType>(i) ==
    5485          13 :                                 OGRFieldType::OFTWideString ||
    5486             :                             static_cast<OGRFieldType>(i) ==
    5487             :                                 OGRFieldType::OFTWideStringList)
    5488           2 :                             continue;
    5489          12 :                         const char *pszType = OGRFieldDefn::GetFieldTypeName(
    5490             :                             static_cast<OGRFieldType>(i));
    5491          12 :                         if (pszType != nullptr)
    5492             :                         {
    5493          12 :                             if (currentValue.empty() ||
    5494           0 :                                 STARTS_WITH(pszType, currentValue.c_str()))
    5495             :                             {
    5496          12 :                                 oRet.push_back(pszType);
    5497             :                             }
    5498             :                         }
    5499             :                     }
    5500           1 :                     return oRet;
    5501         274 :                 });
    5502             : 
    5503             :     auto validationFunction =
    5504         845 :         [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
    5505             :     {
    5506         120 :         bool isValid{true};
    5507         120 :         *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
    5508             : 
    5509             :         // String is returned for unknown types
    5510         120 :         if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
    5511             :         {
    5512          16 :             isValid = false;
    5513             :         }
    5514             : 
    5515         120 :         *pSubtypeValue =
    5516         120 :             OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
    5517             : 
    5518         120 :         if (*pSubtypeValue != OFSTNone)
    5519             :         {
    5520          15 :             isValid = true;
    5521          15 :             switch (*pSubtypeValue)
    5522             :             {
    5523           6 :                 case OFSTBoolean:
    5524             :                 case OFSTInt16:
    5525             :                 {
    5526           6 :                     *pTypeValue = OFTInteger;
    5527           6 :                     break;
    5528             :                 }
    5529           3 :                 case OFSTFloat32:
    5530             :                 {
    5531           3 :                     *pTypeValue = OFTReal;
    5532           3 :                     break;
    5533             :                 }
    5534           6 :                 default:
    5535             :                 {
    5536           6 :                     *pTypeValue = OFTString;
    5537           6 :                     break;
    5538             :                 }
    5539             :             }
    5540             :         }
    5541             : 
    5542         120 :         if (!isValid)
    5543             :         {
    5544           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    5545             :                         "Invalid value for argument '%s': '%s'",
    5546           1 :                         arg.GetName().c_str(), pStrValue->c_str());
    5547             :         }
    5548             : 
    5549         120 :         return isValid;
    5550         274 :     };
    5551             : 
    5552         274 :     if (!pStrValue->empty())
    5553             :     {
    5554           0 :         arg.SetDefault(*pStrValue);
    5555           0 :         validationFunction();
    5556             :     }
    5557             : 
    5558         274 :     arg.AddValidationAction(std::move(validationFunction));
    5559             : 
    5560         274 :     return arg;
    5561             : }
    5562             : 
    5563             : /************************************************************************/
    5564             : /*                   GDALAlgorithm::ValidateBandArg()                   */
    5565             : /************************************************************************/
    5566             : 
    5567        4281 : bool GDALAlgorithm::ValidateBandArg() const
    5568             : {
    5569        4281 :     bool ret = true;
    5570        4281 :     const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
    5571        4281 :     const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
    5572        1669 :     if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
    5573         292 :         (inputDatasetArg->GetType() == GAAT_DATASET ||
    5574        5944 :          inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
    5575         149 :         (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
    5576             :     {
    5577         104 :         const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
    5578             :         {
    5579          99 :             if (nBand > poDS->GetRasterCount())
    5580             :             {
    5581           5 :                 ReportError(CE_Failure, CPLE_AppDefined,
    5582             :                             "Value of 'band' should be greater or equal than "
    5583             :                             "1 and less or equal than %d.",
    5584             :                             poDS->GetRasterCount());
    5585           5 :                 return false;
    5586             :             }
    5587          94 :             return true;
    5588          92 :         };
    5589             : 
    5590             :         const auto ValidateForOneDataset =
    5591         304 :             [&bandArg, &CheckBand](const GDALDataset *poDS)
    5592             :         {
    5593          87 :             bool l_ret = true;
    5594          87 :             if (bandArg->GetType() == GAAT_INTEGER)
    5595             :             {
    5596          24 :                 l_ret = CheckBand(poDS, bandArg->Get<int>());
    5597             :             }
    5598          63 :             else if (bandArg->GetType() == GAAT_INTEGER_LIST)
    5599             :             {
    5600         130 :                 for (int nBand : bandArg->Get<std::vector<int>>())
    5601             :                 {
    5602          75 :                     l_ret = l_ret && CheckBand(poDS, nBand);
    5603             :                 }
    5604             :             }
    5605          87 :             return l_ret;
    5606          92 :         };
    5607             : 
    5608          92 :         if (inputDatasetArg->GetType() == GAAT_DATASET)
    5609             :         {
    5610             :             auto poDS =
    5611           6 :                 inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
    5612           6 :             if (poDS && !ValidateForOneDataset(poDS))
    5613           2 :                 ret = false;
    5614             :         }
    5615             :         else
    5616             :         {
    5617          86 :             CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
    5618          85 :             for (auto &datasetValue :
    5619         256 :                  inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
    5620             :             {
    5621          85 :                 auto poDS = datasetValue.GetDatasetRef();
    5622          85 :                 if (poDS && !ValidateForOneDataset(poDS))
    5623           3 :                     ret = false;
    5624             :             }
    5625             :         }
    5626             :     }
    5627        4281 :     return ret;
    5628             : }
    5629             : 
    5630             : /************************************************************************/
    5631             : /*            GDALAlgorithm::RunPreStepPipelineValidations()            */
    5632             : /************************************************************************/
    5633             : 
    5634        3346 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
    5635             : {
    5636        3346 :     return ValidateBandArg();
    5637             : }
    5638             : 
    5639             : /************************************************************************/
    5640             : /*                     GDALAlgorithm::AddBandArg()                      */
    5641             : /************************************************************************/
    5642             : 
    5643             : GDALInConstructionAlgorithmArg &
    5644        1685 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
    5645             : {
    5646        2134 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5647             : 
    5648             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5649             :                   MsgOrDefault(helpMessage, _("Input band (1-based index)")),
    5650        3370 :                   pValue)
    5651             :         .AddValidationAction(
    5652          34 :             [pValue]()
    5653             :             {
    5654          34 :                 if (*pValue <= 0)
    5655             :                 {
    5656           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5657             :                              "Value of 'band' should greater or equal to 1.");
    5658           1 :                     return false;
    5659             :                 }
    5660          33 :                 return true;
    5661        3370 :             });
    5662             : }
    5663             : 
    5664             : /************************************************************************/
    5665             : /*                     GDALAlgorithm::AddBandArg()                      */
    5666             : /************************************************************************/
    5667             : 
    5668             : GDALInConstructionAlgorithmArg &
    5669         868 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
    5670             : {
    5671        1354 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5672             : 
    5673             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5674             :                   MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
    5675        1736 :                   pValue)
    5676             :         .AddValidationAction(
    5677         126 :             [pValue]()
    5678             :             {
    5679         397 :                 for (int val : *pValue)
    5680             :                 {
    5681         272 :                     if (val <= 0)
    5682             :                     {
    5683           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    5684             :                                  "Value of 'band' should greater or equal "
    5685             :                                  "to 1.");
    5686           1 :                         return false;
    5687             :                     }
    5688             :                 }
    5689         125 :                 return true;
    5690        1736 :             });
    5691             : }
    5692             : 
    5693             : /************************************************************************/
    5694             : /*                      ParseAndValidateKeyValue()                      */
    5695             : /************************************************************************/
    5696             : 
    5697         557 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
    5698             : {
    5699         521 :     const auto Validate = [this, &arg](const std::string &val)
    5700             :     {
    5701         516 :         if (val.find('=') == std::string::npos)
    5702             :         {
    5703           5 :             ReportError(
    5704             :                 CE_Failure, CPLE_AppDefined,
    5705             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    5706           5 :                 arg.GetName().c_str());
    5707           5 :             return false;
    5708             :         }
    5709             : 
    5710         511 :         return true;
    5711         557 :     };
    5712             : 
    5713         557 :     if (arg.GetType() == GAAT_STRING)
    5714             :     {
    5715           0 :         return Validate(arg.Get<std::string>());
    5716             :     }
    5717         557 :     else if (arg.GetType() == GAAT_STRING_LIST)
    5718             :     {
    5719         557 :         std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
    5720         557 :         if (vals.size() == 1)
    5721             :         {
    5722             :             // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
    5723         900 :             std::vector<std::string> newVals;
    5724         900 :             std::string curToken;
    5725         450 :             bool canSplitOnComma = true;
    5726         450 :             char lastSep = 0;
    5727         450 :             bool inString = false;
    5728         450 :             bool equalFoundInLastToken = false;
    5729        6948 :             for (char c : vals[0])
    5730             :             {
    5731        6502 :                 if (!inString && c == ',')
    5732             :                 {
    5733          10 :                     if (lastSep != '=' || !equalFoundInLastToken)
    5734             :                     {
    5735           2 :                         canSplitOnComma = false;
    5736           2 :                         break;
    5737             :                     }
    5738           8 :                     lastSep = c;
    5739           8 :                     newVals.push_back(curToken);
    5740           8 :                     curToken.clear();
    5741           8 :                     equalFoundInLastToken = false;
    5742             :                 }
    5743        6492 :                 else if (!inString && c == '=')
    5744             :                 {
    5745         449 :                     if (lastSep == '=')
    5746             :                     {
    5747           2 :                         canSplitOnComma = false;
    5748           2 :                         break;
    5749             :                     }
    5750         447 :                     equalFoundInLastToken = true;
    5751         447 :                     lastSep = c;
    5752         447 :                     curToken += c;
    5753             :                 }
    5754        6043 :                 else if (c == '"')
    5755             :                 {
    5756           4 :                     inString = !inString;
    5757           4 :                     curToken += c;
    5758             :                 }
    5759             :                 else
    5760             :                 {
    5761        6039 :                     curToken += c;
    5762             :                 }
    5763             :             }
    5764         450 :             if (canSplitOnComma && !inString && equalFoundInLastToken)
    5765             :             {
    5766         437 :                 if (!curToken.empty())
    5767         437 :                     newVals.emplace_back(std::move(curToken));
    5768         437 :                 vals = std::move(newVals);
    5769             :             }
    5770             :         }
    5771             : 
    5772        1068 :         for (const auto &val : vals)
    5773             :         {
    5774         516 :             if (!Validate(val))
    5775           5 :                 return false;
    5776             :         }
    5777             :     }
    5778             : 
    5779         552 :     return true;
    5780             : }
    5781             : 
    5782             : /************************************************************************/
    5783             : /*                           IsGDALGOutput()                            */
    5784             : /************************************************************************/
    5785             : 
    5786        2228 : bool GDALAlgorithm::IsGDALGOutput() const
    5787             : {
    5788        2228 :     bool isGDALGOutput = false;
    5789        2228 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5790        2228 :     const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5791        3841 :     if (outputArg && outputArg->GetType() == GAAT_DATASET &&
    5792        1613 :         outputArg->IsExplicitlySet())
    5793             :     {
    5794        3167 :         if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    5795        1571 :             outputFormatArg->IsExplicitlySet())
    5796             :         {
    5797             :             const auto &val =
    5798         975 :                 outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    5799         975 :             isGDALGOutput = EQUAL(val.c_str(), "GDALG");
    5800             :         }
    5801             :         else
    5802             :         {
    5803             :             const auto &filename =
    5804         621 :                 outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
    5805         621 :             isGDALGOutput =
    5806        1215 :                 filename.GetName().size() > strlen(".gdalg.json") &&
    5807         594 :                 EQUAL(filename.GetName().c_str() + filename.GetName().size() -
    5808             :                           strlen(".gdalg.json"),
    5809             :                       ".gdalg.json");
    5810             :         }
    5811             :     }
    5812        2228 :     return isGDALGOutput;
    5813             : }
    5814             : 
    5815             : /************************************************************************/
    5816             : /*                         ProcessGDALGOutput()                         */
    5817             : /************************************************************************/
    5818             : 
    5819        2497 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
    5820             : {
    5821        2497 :     if (!SupportsStreamedOutput())
    5822         813 :         return ProcessGDALGOutputRet::NOT_GDALG;
    5823             : 
    5824        1684 :     if (IsGDALGOutput())
    5825             :     {
    5826          11 :         const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5827             :         const auto &filename =
    5828          11 :             outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
    5829             :         VSIStatBufL sStat;
    5830          11 :         if (VSIStatL(filename.c_str(), &sStat) == 0)
    5831             :         {
    5832           0 :             const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
    5833           0 :             if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
    5834             :             {
    5835           0 :                 if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
    5836             :                 {
    5837           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5838             :                              "File '%s' already exists. Specify the "
    5839             :                              "--overwrite option to overwrite it.",
    5840             :                              filename.c_str());
    5841           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5842             :                 }
    5843             :             }
    5844             :         }
    5845             : 
    5846          22 :         std::string osCommandLine;
    5847             : 
    5848          44 :         for (const auto &path : GDALAlgorithm::m_callPath)
    5849             :         {
    5850          33 :             if (!osCommandLine.empty())
    5851          22 :                 osCommandLine += ' ';
    5852          33 :             osCommandLine += path;
    5853             :         }
    5854             : 
    5855         250 :         for (const auto &arg : GetArgs())
    5856             :         {
    5857         265 :             if (arg->IsExplicitlySet() &&
    5858          41 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
    5859          30 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
    5860         280 :                 arg->GetName() != GDAL_ARG_NAME_UPDATE &&
    5861          15 :                 arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
    5862             :             {
    5863          14 :                 osCommandLine += ' ';
    5864          14 :                 std::string strArg;
    5865          14 :                 if (!arg->Serialize(strArg))
    5866             :                 {
    5867           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5868             :                              "Cannot serialize argument %s",
    5869           0 :                              arg->GetName().c_str());
    5870           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5871             :                 }
    5872          14 :                 osCommandLine += strArg;
    5873             :             }
    5874             :         }
    5875             : 
    5876          11 :         osCommandLine += " --output-format stream --output streamed_dataset";
    5877             : 
    5878          11 :         std::string outStringUnused;
    5879          11 :         return SaveGDALG(filename, outStringUnused, osCommandLine)
    5880          11 :                    ? ProcessGDALGOutputRet::GDALG_OK
    5881          11 :                    : ProcessGDALGOutputRet::GDALG_ERROR;
    5882             :     }
    5883             : 
    5884        1673 :     return ProcessGDALGOutputRet::NOT_GDALG;
    5885             : }
    5886             : 
    5887             : /************************************************************************/
    5888             : /*                      GDALAlgorithm::SaveGDALG()                      */
    5889             : /************************************************************************/
    5890             : 
    5891          22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
    5892             :                                            std::string &outString,
    5893             :                                            const std::string &commandLine)
    5894             : {
    5895          44 :     CPLJSONDocument oDoc;
    5896          22 :     oDoc.GetRoot().Add("type", "gdal_streamed_alg");
    5897          22 :     oDoc.GetRoot().Add("command_line", commandLine);
    5898          22 :     oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
    5899             : 
    5900          22 :     if (!filename.empty())
    5901          21 :         return oDoc.Save(filename);
    5902             : 
    5903           1 :     outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
    5904           1 :     return true;
    5905             : }
    5906             : 
    5907             : /************************************************************************/
    5908             : /*                GDALAlgorithm::AddCreationOptionsArg()                */
    5909             : /************************************************************************/
    5910             : 
    5911             : GDALInConstructionAlgorithmArg &
    5912        8275 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
    5913             :                                      const char *helpMessage)
    5914             : {
    5915             :     auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
    5916       16550 :                        MsgOrDefault(helpMessage, _("Creation option")), pValue)
    5917       16550 :                     .AddAlias("co")
    5918       16550 :                     .SetMetaVar("<KEY>=<VALUE>")
    5919        8275 :                     .SetPackedValuesAllowed(false);
    5920         293 :     arg.AddValidationAction([this, &arg]()
    5921        8568 :                             { return ParseAndValidateKeyValue(arg); });
    5922             : 
    5923             :     arg.SetAutoCompleteFunction(
    5924          51 :         [this](const std::string &currentValue)
    5925             :         {
    5926          17 :             std::vector<std::string> oRet;
    5927             : 
    5928          17 :             int datasetType =
    5929             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    5930          17 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5931          17 :             if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
    5932           0 :                               outputArg->GetType() == GAAT_DATASET_LIST))
    5933             :             {
    5934          17 :                 datasetType = outputArg->GetDatasetType();
    5935             :             }
    5936             : 
    5937          17 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5938          34 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5939          17 :                 outputFormat->IsExplicitlySet())
    5940             :             {
    5941          14 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5942           7 :                     outputFormat->Get<std::string>().c_str());
    5943           7 :                 if (poDriver)
    5944             :                 {
    5945           7 :                     AddOptionsSuggestions(
    5946           7 :                         poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    5947             :                         datasetType, currentValue, oRet);
    5948             :                 }
    5949           7 :                 return oRet;
    5950             :             }
    5951             : 
    5952          10 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5953             :             {
    5954          10 :                 auto poDM = GetGDALDriverManager();
    5955          10 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5956          10 :                 const auto &osDSName = datasetValue.GetName();
    5957          10 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5958          10 :                 if (!osExt.empty())
    5959             :                 {
    5960          10 :                     std::set<std::string> oVisitedExtensions;
    5961         715 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5962             :                     {
    5963         712 :                         auto poDriver = poDM->GetDriver(i);
    5964        2136 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    5965         712 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    5966         216 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    5967        1424 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    5968         216 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    5969           0 :                              poDriver->GetMetadataItem(
    5970           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    5971             :                         {
    5972             :                             const char *pszExtensions =
    5973         496 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5974         496 :                             if (pszExtensions)
    5975             :                             {
    5976             :                                 const CPLStringList aosExts(
    5977         323 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5978         716 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5979             :                                 {
    5980         419 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5981          16 :                                         !cpl::contains(oVisitedExtensions,
    5982             :                                                        pszExt))
    5983             :                                     {
    5984          10 :                                         oVisitedExtensions.insert(pszExt);
    5985          10 :                                         if (AddOptionsSuggestions(
    5986             :                                                 poDriver->GetMetadataItem(
    5987          10 :                                                     GDAL_DMD_CREATIONOPTIONLIST),
    5988             :                                                 datasetType, currentValue,
    5989             :                                                 oRet))
    5990             :                                         {
    5991           7 :                                             return oRet;
    5992             :                                         }
    5993           3 :                                         break;
    5994             :                                     }
    5995             :                                 }
    5996             :                             }
    5997             :                         }
    5998             :                     }
    5999             :                 }
    6000             :             }
    6001             : 
    6002           3 :             return oRet;
    6003        8275 :         });
    6004             : 
    6005        8275 :     return arg;
    6006             : }
    6007             : 
    6008             : /************************************************************************/
    6009             : /*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
    6010             : /************************************************************************/
    6011             : 
    6012             : GDALInConstructionAlgorithmArg &
    6013        3960 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
    6014             :                                           const char *helpMessage)
    6015             : {
    6016             :     auto &arg =
    6017             :         AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
    6018        7920 :                MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
    6019        7920 :             .AddAlias("lco")
    6020        7920 :             .SetMetaVar("<KEY>=<VALUE>")
    6021        3960 :             .SetPackedValuesAllowed(false);
    6022          76 :     arg.AddValidationAction([this, &arg]()
    6023        4036 :                             { return ParseAndValidateKeyValue(arg); });
    6024             : 
    6025             :     arg.SetAutoCompleteFunction(
    6026           5 :         [this](const std::string &currentValue)
    6027             :         {
    6028           2 :             std::vector<std::string> oRet;
    6029             : 
    6030           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6031           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    6032           2 :                 outputFormat->IsExplicitlySet())
    6033             :             {
    6034           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    6035           1 :                     outputFormat->Get<std::string>().c_str());
    6036           1 :                 if (poDriver)
    6037             :                 {
    6038           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    6039           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    6040             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    6041             :                 }
    6042           1 :                 return oRet;
    6043             :             }
    6044             : 
    6045           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    6046           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    6047             :             {
    6048           1 :                 auto poDM = GetGDALDriverManager();
    6049           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    6050           1 :                 const auto &osDSName = datasetValue.GetName();
    6051           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    6052           1 :                 if (!osExt.empty())
    6053             :                 {
    6054           1 :                     std::set<std::string> oVisitedExtensions;
    6055         229 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    6056             :                     {
    6057         228 :                         auto poDriver = poDM->GetDriver(i);
    6058         228 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    6059             :                         {
    6060             :                             const char *pszExtensions =
    6061          91 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    6062          91 :                             if (pszExtensions)
    6063             :                             {
    6064             :                                 const CPLStringList aosExts(
    6065          62 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    6066         156 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    6067             :                                 {
    6068          96 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    6069           1 :                                         !cpl::contains(oVisitedExtensions,
    6070             :                                                        pszExt))
    6071             :                                     {
    6072           1 :                                         oVisitedExtensions.insert(pszExt);
    6073           1 :                                         if (AddOptionsSuggestions(
    6074             :                                                 poDriver->GetMetadataItem(
    6075           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    6076             :                                                 GDAL_OF_VECTOR, currentValue,
    6077             :                                                 oRet))
    6078             :                                         {
    6079           0 :                                             return oRet;
    6080             :                                         }
    6081           1 :                                         break;
    6082             :                                     }
    6083             :                                 }
    6084             :                             }
    6085             :                         }
    6086             :                     }
    6087             :                 }
    6088             :             }
    6089             : 
    6090           1 :             return oRet;
    6091        3960 :         });
    6092             : 
    6093        3960 :     return arg;
    6094             : }
    6095             : 
    6096             : /************************************************************************/
    6097             : /*                     GDALAlgorithm::AddBBOXArg()                      */
    6098             : /************************************************************************/
    6099             : 
    6100             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    6101             : GDALInConstructionAlgorithmArg &
    6102        1894 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    6103             : {
    6104             :     auto &arg = AddArg("bbox", 0,
    6105             :                        MsgOrDefault(helpMessage,
    6106             :                                     _("Bounding box as xmin,ymin,xmax,ymax")),
    6107        3788 :                        pValue)
    6108        1894 :                     .SetRepeatedArgAllowed(false)
    6109        1894 :                     .SetMinCount(4)
    6110        1894 :                     .SetMaxCount(4)
    6111        1894 :                     .SetDisplayHintAboutRepetition(false);
    6112             :     arg.AddValidationAction(
    6113         166 :         [&arg]()
    6114             :         {
    6115         166 :             const auto &val = arg.Get<std::vector<double>>();
    6116         166 :             CPLAssert(val.size() == 4);
    6117         166 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    6118             :             {
    6119           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6120             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    6121             :                          "xmin <= xmax and ymin <= ymax");
    6122           5 :                 return false;
    6123             :             }
    6124         161 :             return true;
    6125        1894 :         });
    6126        1894 :     return arg;
    6127             : }
    6128             : 
    6129             : /************************************************************************/
    6130             : /*                  GDALAlgorithm::AddActiveLayerArg()                  */
    6131             : /************************************************************************/
    6132             : 
    6133             : GDALInConstructionAlgorithmArg &
    6134        1783 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
    6135             : {
    6136             :     return AddArg("active-layer", 0,
    6137             :                   MsgOrDefault(helpMessage,
    6138             :                                _("Set active layer (if not specified, all)")),
    6139        1783 :                   pValue);
    6140             : }
    6141             : 
    6142             : /************************************************************************/
    6143             : /*                  GDALAlgorithm::AddNumThreadsArg()                   */
    6144             : /************************************************************************/
    6145             : 
    6146             : GDALInConstructionAlgorithmArg &
    6147         723 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
    6148             :                                 const char *helpMessage)
    6149             : {
    6150             :     auto &arg =
    6151             :         AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
    6152             :                MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
    6153         723 :                pStrValue);
    6154             : 
    6155             :     AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
    6156        1446 :            _("Number of jobs (read-only, hidden argument)"), pValue)
    6157         723 :         .SetHidden();
    6158             : 
    6159        2724 :     auto lambda = [this, &arg, pValue, pStrValue]
    6160             :     {
    6161         908 :         bool bOK = false;
    6162         908 :         const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    6163             :         const int nLimit = std::clamp(
    6164         908 :             pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
    6165        1816 :             CPLGetNumCPUs());
    6166             :         const int nNumThreads =
    6167         908 :             GDALGetNumThreads(pStrValue->c_str(), nLimit,
    6168             :                               /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
    6169         908 :         if (bOK)
    6170             :         {
    6171         908 :             *pValue = nNumThreads;
    6172             :         }
    6173             :         else
    6174             :         {
    6175           0 :             ReportError(CE_Failure, CPLE_IllegalArg,
    6176             :                         "Invalid value for '%s' argument",
    6177           0 :                         arg.GetName().c_str());
    6178             :         }
    6179         908 :         return bOK;
    6180         723 :     };
    6181         723 :     if (!pStrValue->empty())
    6182             :     {
    6183         677 :         arg.SetDefault(*pStrValue);
    6184         677 :         lambda();
    6185             :     }
    6186         723 :     arg.AddValidationAction(std::move(lambda));
    6187         723 :     return arg;
    6188             : }
    6189             : 
    6190             : /************************************************************************/
    6191             : /*                 GDALAlgorithm::AddAbsolutePathArg()                  */
    6192             : /************************************************************************/
    6193             : 
    6194             : GDALInConstructionAlgorithmArg &
    6195         622 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
    6196             : {
    6197             :     return AddArg(
    6198             :         "absolute-path", 0,
    6199             :         MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
    6200             :                                     "should be stored as an absolute path")),
    6201         622 :         pValue);
    6202             : }
    6203             : 
    6204             : /************************************************************************/
    6205             : /*               GDALAlgorithm::AddPixelFunctionNameArg()               */
    6206             : /************************************************************************/
    6207             : 
    6208             : GDALInConstructionAlgorithmArg &
    6209         138 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
    6210             :                                        const char *helpMessage)
    6211             : {
    6212             : 
    6213             :     const auto pixelFunctionNames =
    6214         138 :         VRTDerivedRasterBand::GetPixelFunctionNames();
    6215             :     return AddArg(
    6216             :                "pixel-function", 0,
    6217             :                MsgOrDefault(
    6218             :                    helpMessage,
    6219             :                    _("Specify a pixel function to calculate output value from "
    6220             :                      "overlapping inputs")),
    6221         276 :                pValue)
    6222         276 :         .SetChoices(pixelFunctionNames);
    6223             : }
    6224             : 
    6225             : /************************************************************************/
    6226             : /*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
    6227             : /************************************************************************/
    6228             : 
    6229             : GDALInConstructionAlgorithmArg &
    6230         138 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
    6231             :                                        const char *helpMessage)
    6232             : {
    6233             :     auto &pixelFunctionArgArg =
    6234             :         AddArg("pixel-function-arg", 0,
    6235             :                MsgOrDefault(
    6236             :                    helpMessage,
    6237             :                    _("Specify argument(s) to pass to the pixel function")),
    6238         276 :                pValue)
    6239         276 :             .SetMetaVar("<NAME>=<VALUE>")
    6240         138 :             .SetRepeatedArgAllowed(true);
    6241             :     pixelFunctionArgArg.AddValidationAction(
    6242           7 :         [this, &pixelFunctionArgArg]()
    6243         145 :         { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
    6244             : 
    6245             :     pixelFunctionArgArg.SetAutoCompleteFunction(
    6246          12 :         [this](const std::string &currentValue)
    6247             :         {
    6248          12 :             std::string pixelFunction;
    6249           6 :             const auto pixelFunctionArg = GetArg("pixel-function");
    6250           6 :             if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
    6251             :             {
    6252           6 :                 pixelFunction = pixelFunctionArg->Get<std::string>();
    6253             :             }
    6254             : 
    6255           6 :             std::vector<std::string> ret;
    6256             : 
    6257           6 :             if (!pixelFunction.empty())
    6258             :             {
    6259           5 :                 const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
    6260             :                     pixelFunction.c_str());
    6261           5 :                 if (!pair)
    6262             :                 {
    6263           1 :                     ret.push_back("**");
    6264             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6265           1 :                     ret.push_back(std::string("\xC2\xA0"
    6266             :                                               "Invalid pixel function name"));
    6267             :                 }
    6268           4 :                 else if (pair->second.find("Argument name=") ==
    6269             :                          std::string::npos)
    6270             :                 {
    6271           1 :                     ret.push_back("**");
    6272             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6273           1 :                     ret.push_back(
    6274           2 :                         std::string(
    6275             :                             "\xC2\xA0"
    6276             :                             "No pixel function arguments for pixel function '")
    6277           1 :                             .append(pixelFunction)
    6278           1 :                             .append("'"));
    6279             :                 }
    6280             :                 else
    6281             :                 {
    6282           3 :                     AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
    6283             :                                           ret);
    6284             :                 }
    6285             :             }
    6286             : 
    6287          12 :             return ret;
    6288         138 :         });
    6289             : 
    6290         138 :     return pixelFunctionArgArg;
    6291             : }
    6292             : 
    6293             : /************************************************************************/
    6294             : /*                   GDALAlgorithm::AddProgressArg()                    */
    6295             : /************************************************************************/
    6296             : 
    6297        8059 : void GDALAlgorithm::AddProgressArg()
    6298             : {
    6299             :     AddArg(GDAL_ARG_NAME_QUIET, 'q',
    6300       16118 :            _("Quiet mode (no progress bar or warning message)"), &m_quiet)
    6301        8059 :         .SetAvailableInPipelineStep(false)
    6302       16118 :         .SetCategory(GAAC_COMMON)
    6303        8059 :         .AddAction([this]() { m_progressBarRequested = false; });
    6304             : 
    6305       16118 :     AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
    6306        8059 :         .SetAvailableInPipelineStep(false)
    6307        8059 :         .SetHidden();
    6308        8059 : }
    6309             : 
    6310             : /************************************************************************/
    6311             : /*                         GDALAlgorithm::Run()                         */
    6312             : /************************************************************************/
    6313             : 
    6314        4797 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    6315             : {
    6316        4797 :     WarnIfDeprecated();
    6317             : 
    6318        4797 :     if (m_selectedSubAlg)
    6319             :     {
    6320         443 :         if (m_calledFromCommandLine)
    6321         265 :             m_selectedSubAlg->m_calledFromCommandLine = true;
    6322         443 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    6323             :     }
    6324             : 
    6325        4354 :     if (m_helpRequested || m_helpDocRequested)
    6326             :     {
    6327          16 :         if (m_calledFromCommandLine)
    6328          16 :             printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    6329          16 :         return true;
    6330             :     }
    6331             : 
    6332        4338 :     if (m_JSONUsageRequested)
    6333             :     {
    6334           3 :         if (m_calledFromCommandLine)
    6335           3 :             printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    6336           3 :         return true;
    6337             :     }
    6338             : 
    6339        4335 :     if (!ValidateArguments())
    6340         125 :         return false;
    6341             : 
    6342        4210 :     if (m_alreadyRun)
    6343             :     {
    6344           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    6345             :                     "Run() can be called only once per algorithm instance");
    6346           3 :         return false;
    6347             :     }
    6348        4207 :     m_alreadyRun = true;
    6349             : 
    6350        4207 :     switch (ProcessGDALGOutput())
    6351             :     {
    6352           0 :         case ProcessGDALGOutputRet::GDALG_ERROR:
    6353           0 :             return false;
    6354             : 
    6355          11 :         case ProcessGDALGOutputRet::GDALG_OK:
    6356          11 :             return true;
    6357             : 
    6358        4196 :         case ProcessGDALGOutputRet::NOT_GDALG:
    6359        4196 :             break;
    6360             :     }
    6361             : 
    6362        4196 :     if (m_executionForStreamOutput)
    6363             :     {
    6364          93 :         if (!CheckSafeForStreamOutput())
    6365             :         {
    6366           4 :             return false;
    6367             :         }
    6368             :     }
    6369             : 
    6370        4192 :     return RunImpl(pfnProgress, pProgressData);
    6371             : }
    6372             : 
    6373             : /************************************************************************/
    6374             : /*              GDALAlgorithm::CheckSafeForStreamOutput()               */
    6375             : /************************************************************************/
    6376             : 
    6377          48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
    6378             : {
    6379          48 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6380          48 :     if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
    6381             :     {
    6382          48 :         const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    6383          48 :         if (!EQUAL(val.c_str(), "stream"))
    6384             :         {
    6385             :             // For security reasons, to avoid that reading a .gdalg.json file
    6386             :             // writes a file on the file system.
    6387           4 :             ReportError(
    6388             :                 CE_Failure, CPLE_NotSupported,
    6389             :                 "in streamed execution, --format stream should be used");
    6390           4 :             return false;
    6391             :         }
    6392             :     }
    6393          44 :     return true;
    6394             : }
    6395             : 
    6396             : /************************************************************************/
    6397             : /*                      GDALAlgorithm::Finalize()                       */
    6398             : /************************************************************************/
    6399             : 
    6400        1829 : bool GDALAlgorithm::Finalize()
    6401             : {
    6402        1829 :     bool ret = true;
    6403        1829 :     if (m_selectedSubAlg)
    6404         271 :         ret = m_selectedSubAlg->Finalize();
    6405             : 
    6406       33083 :     for (auto &arg : m_args)
    6407             :     {
    6408       31254 :         if (arg->GetType() == GAAT_DATASET)
    6409             :         {
    6410        1444 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    6411             :         }
    6412       29810 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    6413             :         {
    6414        2794 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    6415             :             {
    6416        1314 :                 ret = ds.Close() && ret;
    6417             :             }
    6418             :         }
    6419             :     }
    6420        1829 :     return ret;
    6421             : }
    6422             : 
    6423             : /************************************************************************/
    6424             : /*                  GDALAlgorithm::GetArgNamesForCLI()                  */
    6425             : /************************************************************************/
    6426             : 
    6427             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    6428         693 : GDALAlgorithm::GetArgNamesForCLI() const
    6429             : {
    6430        1386 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6431             : 
    6432         693 :     size_t maxOptLen = 0;
    6433        8658 :     for (const auto &arg : m_args)
    6434             :     {
    6435        7965 :         if (arg->IsHidden() || arg->IsHiddenForCLI())
    6436        1671 :             continue;
    6437        6294 :         std::string opt;
    6438        6294 :         bool addComma = false;
    6439        6294 :         if (!arg->GetShortName().empty())
    6440             :         {
    6441        1399 :             opt += '-';
    6442        1399 :             opt += arg->GetShortName();
    6443        1399 :             addComma = true;
    6444             :         }
    6445        6294 :         for (char alias : arg->GetShortNameAliases())
    6446             :         {
    6447           0 :             if (addComma)
    6448           0 :                 opt += ", ";
    6449           0 :             opt += "-";
    6450           0 :             opt += alias;
    6451           0 :             addComma = true;
    6452             :         }
    6453        7046 :         for (const std::string &alias : arg->GetAliases())
    6454             :         {
    6455         752 :             if (addComma)
    6456         324 :                 opt += ", ";
    6457         752 :             opt += "--";
    6458         752 :             opt += alias;
    6459         752 :             addComma = true;
    6460             :         }
    6461        6294 :         if (!arg->GetName().empty())
    6462             :         {
    6463        6294 :             if (addComma)
    6464        1827 :                 opt += ", ";
    6465        6294 :             opt += "--";
    6466        6294 :             opt += arg->GetName();
    6467             :         }
    6468        6294 :         const auto &metaVar = arg->GetMetaVar();
    6469        6294 :         if (!metaVar.empty())
    6470             :         {
    6471        3964 :             opt += ' ';
    6472        3964 :             if (metaVar.front() != '<')
    6473        2845 :                 opt += '<';
    6474        3964 :             opt += metaVar;
    6475        3964 :             if (metaVar.back() != '>')
    6476        2839 :                 opt += '>';
    6477             :         }
    6478        6294 :         maxOptLen = std::max(maxOptLen, opt.size());
    6479        6294 :         options.emplace_back(arg.get(), opt);
    6480             :     }
    6481             : 
    6482        1386 :     return std::make_pair(std::move(options), maxOptLen);
    6483             : }
    6484             : 
    6485             : /************************************************************************/
    6486             : /*                   GDALAlgorithm::GetUsageForCLI()                    */
    6487             : /************************************************************************/
    6488             : 
    6489             : std::string
    6490         414 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    6491             :                               const UsageOptions &usageOptions) const
    6492             : {
    6493         414 :     if (m_selectedSubAlg)
    6494           7 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    6495             : 
    6496         814 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    6497         814 :     std::string osPath;
    6498         819 :     for (const std::string &s : m_callPath)
    6499             :     {
    6500         412 :         if (!osPath.empty())
    6501          51 :             osPath += ' ';
    6502         412 :         osPath += s;
    6503             :     }
    6504         407 :     osRet += ' ';
    6505         407 :     osRet += osPath;
    6506             : 
    6507         407 :     bool hasNonPositionals = false;
    6508        5046 :     for (const auto &arg : m_args)
    6509             :     {
    6510        4639 :         if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
    6511        3347 :             hasNonPositionals = true;
    6512             :     }
    6513             : 
    6514         407 :     if (HasSubAlgorithms())
    6515             :     {
    6516           9 :         if (m_callPath.size() == 1)
    6517             :         {
    6518           8 :             osRet += " <COMMAND>";
    6519           8 :             if (hasNonPositionals)
    6520           8 :                 osRet += " [OPTIONS]";
    6521           8 :             if (usageOptions.isPipelineStep)
    6522             :             {
    6523           5 :                 const size_t nLenFirstLine = osRet.size();
    6524           5 :                 osRet += '\n';
    6525           5 :                 osRet.append(nLenFirstLine, '-');
    6526           5 :                 osRet += '\n';
    6527             :             }
    6528           8 :             osRet += "\nwhere <COMMAND> is one of:\n";
    6529             :         }
    6530             :         else
    6531             :         {
    6532           1 :             osRet += " <SUBCOMMAND>";
    6533           1 :             if (hasNonPositionals)
    6534           1 :                 osRet += " [OPTIONS]";
    6535           1 :             if (usageOptions.isPipelineStep)
    6536             :             {
    6537           0 :                 const size_t nLenFirstLine = osRet.size();
    6538           0 :                 osRet += '\n';
    6539           0 :                 osRet.append(nLenFirstLine, '-');
    6540           0 :                 osRet += '\n';
    6541             :             }
    6542           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    6543             :         }
    6544           9 :         size_t maxNameLen = 0;
    6545          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6546             :         {
    6547          43 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    6548             :         }
    6549          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6550             :         {
    6551          86 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6552          43 :             if (subAlg && !subAlg->IsHidden())
    6553             :             {
    6554          43 :                 const std::string &name(subAlg->GetName());
    6555          43 :                 osRet += "  - ";
    6556          43 :                 osRet += name;
    6557          43 :                 osRet += ": ";
    6558          43 :                 osRet.append(maxNameLen - name.size(), ' ');
    6559          43 :                 osRet += subAlg->GetDescription();
    6560          43 :                 if (!subAlg->m_aliases.empty())
    6561             :                 {
    6562           6 :                     bool first = true;
    6563           6 :                     for (const auto &alias : subAlg->GetAliases())
    6564             :                     {
    6565           6 :                         if (alias ==
    6566             :                             GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    6567           6 :                             break;
    6568           0 :                         if (first)
    6569           0 :                             osRet += " (alias: ";
    6570             :                         else
    6571           0 :                             osRet += ", ";
    6572           0 :                         osRet += alias;
    6573           0 :                         first = false;
    6574             :                     }
    6575           6 :                     if (!first)
    6576             :                     {
    6577           0 :                         osRet += ')';
    6578             :                     }
    6579             :                 }
    6580          43 :                 osRet += '\n';
    6581             :             }
    6582             :         }
    6583             : 
    6584           9 :         if (shortUsage && hasNonPositionals)
    6585             :         {
    6586           2 :             osRet += "\nTry '";
    6587           2 :             osRet += osPath;
    6588           2 :             osRet += " --help' for help.\n";
    6589             :         }
    6590             :     }
    6591             :     else
    6592             :     {
    6593         398 :         if (!m_args.empty())
    6594             :         {
    6595         398 :             if (hasNonPositionals)
    6596         398 :                 osRet += " [OPTIONS]";
    6597         588 :             for (const auto *arg : m_positionalArgs)
    6598             :             {
    6599         265 :                 if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
    6600          75 :                     (GetName() == "pipeline" && arg->GetName() == "pipeline"))
    6601             :                 {
    6602             :                     const bool optional =
    6603         202 :                         (!arg->IsRequired() && !(GetName() == "pipeline" &&
    6604          29 :                                                  arg->GetName() == "pipeline"));
    6605         173 :                     osRet += ' ';
    6606         173 :                     if (optional)
    6607          25 :                         osRet += '[';
    6608         173 :                     const std::string &metavar = arg->GetMetaVar();
    6609         173 :                     if (!metavar.empty() && metavar[0] == '<')
    6610             :                     {
    6611           4 :                         osRet += metavar;
    6612             :                     }
    6613             :                     else
    6614             :                     {
    6615         169 :                         osRet += '<';
    6616         169 :                         osRet += metavar;
    6617         169 :                         osRet += '>';
    6618             :                     }
    6619         215 :                     if (arg->GetType() == GAAT_DATASET_LIST &&
    6620          42 :                         arg->GetMaxCount() > 1)
    6621             :                     {
    6622          28 :                         osRet += "...";
    6623             :                     }
    6624         173 :                     if (optional)
    6625          25 :                         osRet += ']';
    6626             :                 }
    6627             :             }
    6628             :         }
    6629             : 
    6630         398 :         const size_t nLenFirstLine = osRet.size();
    6631         398 :         osRet += '\n';
    6632         398 :         if (usageOptions.isPipelineStep)
    6633             :         {
    6634         309 :             osRet.append(nLenFirstLine, '-');
    6635         309 :             osRet += '\n';
    6636             :         }
    6637             : 
    6638         398 :         if (shortUsage)
    6639             :         {
    6640          23 :             osRet += "Try '";
    6641          23 :             osRet += osPath;
    6642          23 :             osRet += " --help' for help.\n";
    6643          23 :             return osRet;
    6644             :         }
    6645             : 
    6646         375 :         osRet += '\n';
    6647         375 :         osRet += m_description;
    6648         375 :         osRet += '\n';
    6649             :     }
    6650             : 
    6651         384 :     if (!m_args.empty() && !shortUsage)
    6652             :     {
    6653         764 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6654             :         size_t maxOptLen;
    6655         382 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    6656         382 :         if (usageOptions.maxOptLen)
    6657         311 :             maxOptLen = usageOptions.maxOptLen;
    6658             : 
    6659         764 :         const std::string userProvidedOpt = "--<user-provided-option>=<value>";
    6660         382 :         if (m_arbitraryLongNameArgsAllowed)
    6661           2 :             maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
    6662             : 
    6663             :         const auto OutputArg =
    6664        2394 :             [this, maxOptLen, &osRet,
    6665       23823 :              &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
    6666             :         {
    6667        2394 :             osRet += "  ";
    6668        2394 :             osRet += opt;
    6669        2394 :             osRet += "  ";
    6670        2394 :             osRet.append(maxOptLen - opt.size(), ' ');
    6671        2394 :             osRet += arg->GetDescription();
    6672             : 
    6673        2394 :             const auto &choices = arg->GetChoices();
    6674        2394 :             if (!choices.empty())
    6675             :             {
    6676         224 :                 osRet += ". ";
    6677         224 :                 osRet += arg->GetMetaVar();
    6678         224 :                 osRet += '=';
    6679         224 :                 bool firstChoice = true;
    6680        1729 :                 for (const auto &choice : choices)
    6681             :                 {
    6682        1505 :                     if (!firstChoice)
    6683        1281 :                         osRet += '|';
    6684        1505 :                     osRet += choice;
    6685        1505 :                     firstChoice = false;
    6686             :                 }
    6687             :             }
    6688             : 
    6689        4722 :             if (arg->GetType() == GAAT_DATASET ||
    6690        2328 :                 arg->GetType() == GAAT_DATASET_LIST)
    6691             :             {
    6692         144 :                 if (arg->IsOutput() &&
    6693         144 :                     arg->GetDatasetInputFlags() == GADV_NAME &&
    6694           9 :                     arg->GetDatasetOutputFlags() == GADV_OBJECT)
    6695             :                 {
    6696           9 :                     osRet += " (created by algorithm)";
    6697             :                 }
    6698             :             }
    6699             : 
    6700        2394 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    6701             :             {
    6702         190 :                 osRet += " (default: ";
    6703         190 :                 osRet += arg->GetDefault<std::string>();
    6704         190 :                 osRet += ')';
    6705             :             }
    6706        2204 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    6707             :             {
    6708          70 :                 if (arg->GetDefault<bool>())
    6709           0 :                     osRet += " (default: true)";
    6710             :             }
    6711        2134 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    6712             :             {
    6713          76 :                 osRet += " (default: ";
    6714          76 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    6715          76 :                 osRet += ')';
    6716             :             }
    6717        2058 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    6718             :             {
    6719          49 :                 osRet += " (default: ";
    6720          49 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    6721          49 :                 osRet += ')';
    6722             :             }
    6723        2421 :             else if (arg->GetType() == GAAT_STRING_LIST &&
    6724         412 :                      arg->HasDefaultValue())
    6725             :             {
    6726             :                 const auto &defaultVal =
    6727           9 :                     arg->GetDefault<std::vector<std::string>>();
    6728           9 :                 if (defaultVal.size() == 1)
    6729             :                 {
    6730           9 :                     osRet += " (default: ";
    6731           9 :                     osRet += defaultVal[0];
    6732           9 :                     osRet += ')';
    6733             :                 }
    6734             :             }
    6735        2027 :             else if (arg->GetType() == GAAT_INTEGER_LIST &&
    6736          27 :                      arg->HasDefaultValue())
    6737             :             {
    6738           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<int>>();
    6739           0 :                 if (defaultVal.size() == 1)
    6740             :                 {
    6741           0 :                     osRet += " (default: ";
    6742           0 :                     osRet += CPLSPrintf("%d", defaultVal[0]);
    6743           0 :                     osRet += ')';
    6744             :                 }
    6745             :             }
    6746        2000 :             else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
    6747             :             {
    6748           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<double>>();
    6749           0 :                 if (defaultVal.size() == 1)
    6750             :                 {
    6751           0 :                     osRet += " (default: ";
    6752           0 :                     osRet += CPLSPrintf("%g", defaultVal[0]);
    6753           0 :                     osRet += ')';
    6754             :                 }
    6755             :             }
    6756             : 
    6757        2394 :             if (arg->GetDisplayHintAboutRepetition())
    6758             :             {
    6759        2427 :                 if (arg->GetMinCount() > 0 &&
    6760          92 :                     arg->GetMinCount() == arg->GetMaxCount())
    6761             :                 {
    6762          18 :                     if (arg->GetMinCount() != 1)
    6763           5 :                         osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    6764             :                 }
    6765        2391 :                 else if (arg->GetMinCount() > 0 &&
    6766          74 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    6767             :                 {
    6768             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    6769           8 :                                         arg->GetMaxCount());
    6770             :                 }
    6771        2309 :                 else if (arg->GetMinCount() > 0)
    6772             :                 {
    6773          66 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    6774             :                 }
    6775        2243 :                 else if (arg->GetMaxCount() > 1)
    6776             :                 {
    6777         401 :                     osRet += " [may be repeated]";
    6778             :                 }
    6779             :             }
    6780             : 
    6781        2394 :             if (arg->IsRequired())
    6782             :             {
    6783         169 :                 osRet += " [required]";
    6784             :             }
    6785             : 
    6786        2642 :             if (!arg->IsAvailableInPipelineStep() &&
    6787         248 :                 !usageOptions.isPipelineStep)
    6788             :             {
    6789          28 :                 osRet += " [not available in pipelines]";
    6790             :             }
    6791             : 
    6792        2394 :             osRet += '\n';
    6793             : 
    6794        2394 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6795        2394 :             if (!mutualExclusionGroup.empty())
    6796             :             {
    6797         434 :                 std::string otherArgs;
    6798        4123 :                 for (const auto &otherArg : m_args)
    6799             :                 {
    6800        7157 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6801        3251 :                         otherArg.get() == arg)
    6802         872 :                         continue;
    6803        3034 :                     if (otherArg->GetMutualExclusionGroup() ==
    6804             :                         mutualExclusionGroup)
    6805             :                     {
    6806         290 :                         if (!otherArgs.empty())
    6807          77 :                             otherArgs += ", ";
    6808         290 :                         otherArgs += "--";
    6809         290 :                         otherArgs += otherArg->GetName();
    6810             :                     }
    6811             :                 }
    6812         217 :                 if (!otherArgs.empty())
    6813             :                 {
    6814         213 :                     osRet += "  ";
    6815         213 :                     osRet += "  ";
    6816         213 :                     osRet.append(maxOptLen, ' ');
    6817         213 :                     osRet += "Mutually exclusive with ";
    6818         213 :                     osRet += otherArgs;
    6819         213 :                     osRet += '\n';
    6820             :                 }
    6821             :             }
    6822             : 
    6823             :             // Check dependency
    6824        4788 :             std::string dependencyArgs;
    6825             : 
    6826          32 :             for (const auto &dependencyArgumentName :
    6827        2458 :                  GetArgDependencies(arg->GetName()))
    6828             :             {
    6829          32 :                 const auto otherArg{GetArg(dependencyArgumentName)};
    6830          32 :                 if (otherArg != nullptr)
    6831             :                 {
    6832          32 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6833             :                         otherArg == arg)
    6834             :                     {
    6835           0 :                         continue;
    6836             :                     }
    6837             : 
    6838          32 :                     if (!dependencyArgs.empty())
    6839             :                     {
    6840           3 :                         dependencyArgs += ", ";
    6841             :                     }
    6842             : 
    6843          32 :                     dependencyArgs += "--";
    6844          32 :                     dependencyArgs += otherArg->GetName();
    6845             :                 }
    6846             :                 else
    6847             :                 {
    6848           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6849             :                              "Argument '%s' depends on unknown argument '%s'",
    6850           0 :                              arg->GetName().c_str(),
    6851             :                              dependencyArgumentName.c_str());
    6852             :                 }
    6853             :             }
    6854             : 
    6855        2394 :             if (!dependencyArgs.empty())
    6856             :             {
    6857          29 :                 osRet += "  ";
    6858          29 :                 osRet += "  ";
    6859          29 :                 osRet.append(maxOptLen, ' ');
    6860          29 :                 osRet += "Depends on ";
    6861          29 :                 osRet += dependencyArgs;
    6862          29 :                 osRet += '\n';
    6863             :             }
    6864        2394 :         };
    6865             : 
    6866         382 :         if (!m_positionalArgs.empty())
    6867             :         {
    6868         150 :             osRet += "\nPositional arguments:\n";
    6869        1600 :             for (const auto &[arg, opt] : options)
    6870             :             {
    6871        1450 :                 if (arg->IsPositional())
    6872         141 :                     OutputArg(arg, opt);
    6873             :             }
    6874             :         }
    6875             : 
    6876         382 :         if (hasNonPositionals)
    6877             :         {
    6878         382 :             bool hasCommon = false;
    6879         382 :             bool hasBase = false;
    6880         382 :             bool hasAdvanced = false;
    6881         382 :             bool hasEsoteric = false;
    6882         764 :             std::vector<std::string> categories;
    6883        3722 :             for (const auto &iter : options)
    6884             :             {
    6885        3340 :                 const auto &arg = iter.first;
    6886        3340 :                 if (!arg->IsPositional())
    6887             :                 {
    6888        3199 :                     const auto &category = arg->GetCategory();
    6889        3199 :                     if (category == GAAC_COMMON)
    6890             :                     {
    6891        1164 :                         hasCommon = true;
    6892             :                     }
    6893        2035 :                     else if (category == GAAC_BASE)
    6894             :                     {
    6895        1795 :                         hasBase = true;
    6896             :                     }
    6897         240 :                     else if (category == GAAC_ADVANCED)
    6898             :                     {
    6899         178 :                         hasAdvanced = true;
    6900             :                     }
    6901          62 :                     else if (category == GAAC_ESOTERIC)
    6902             :                     {
    6903          29 :                         hasEsoteric = true;
    6904             :                     }
    6905          33 :                     else if (std::find(categories.begin(), categories.end(),
    6906          33 :                                        category) == categories.end())
    6907             :                     {
    6908           9 :                         categories.push_back(category);
    6909             :                     }
    6910             :                 }
    6911             :             }
    6912         382 :             if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
    6913          67 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    6914         382 :             if (hasBase)
    6915         336 :                 categories.insert(categories.begin(), GAAC_BASE);
    6916         382 :             if (hasCommon && !usageOptions.isPipelineStep)
    6917          68 :                 categories.insert(categories.begin(), GAAC_COMMON);
    6918         382 :             if (hasEsoteric)
    6919          11 :                 categories.push_back(GAAC_ESOTERIC);
    6920             : 
    6921         873 :             for (const auto &category : categories)
    6922             :             {
    6923         491 :                 osRet += "\n";
    6924         491 :                 if (category != GAAC_BASE)
    6925             :                 {
    6926         155 :                     osRet += category;
    6927         155 :                     osRet += ' ';
    6928             :                 }
    6929         491 :                 osRet += "Options:\n";
    6930        5328 :                 for (const auto &[arg, opt] : options)
    6931             :                 {
    6932        4837 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    6933        2253 :                         OutputArg(arg, opt);
    6934             :                 }
    6935         491 :                 if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
    6936             :                 {
    6937           2 :                     osRet += "  ";
    6938           2 :                     osRet += userProvidedOpt;
    6939           2 :                     osRet += "  ";
    6940           2 :                     if (userProvidedOpt.size() < maxOptLen)
    6941           0 :                         osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
    6942           2 :                     osRet += "Argument provided by user";
    6943           2 :                     osRet += '\n';
    6944             :                 }
    6945             :             }
    6946             :         }
    6947             :     }
    6948             : 
    6949         384 :     if (!m_longDescription.empty())
    6950             :     {
    6951           6 :         osRet += '\n';
    6952           6 :         osRet += m_longDescription;
    6953           6 :         osRet += '\n';
    6954             :     }
    6955             : 
    6956         384 :     if (!m_helpDocRequested && !usageOptions.isPipelineMain)
    6957             :     {
    6958         371 :         if (!m_helpURL.empty())
    6959             :         {
    6960         371 :             osRet += "\nFor more details, consult ";
    6961         371 :             osRet += GetHelpFullURL();
    6962         371 :             osRet += '\n';
    6963             :         }
    6964         371 :         osRet += GetUsageForCLIEnd();
    6965             :     }
    6966             : 
    6967         384 :     return osRet;
    6968             : }
    6969             : 
    6970             : /************************************************************************/
    6971             : /*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
    6972             : /************************************************************************/
    6973             : 
    6974             : //! @cond Doxygen_Suppress
    6975         378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
    6976             : {
    6977         378 :     std::string osRet;
    6978             : 
    6979         378 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    6980             :     {
    6981             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    6982             :                  "alternative interface to GDAL and OGR command line "
    6983             :                  "utilities.\nThe project reserves the right to modify, "
    6984             :                  "rename, reorganize, and change the behavior of the utility\n"
    6985             :                  "until it is officially frozen in a future feature release of "
    6986          13 :                  "GDAL.\n";
    6987             :     }
    6988         378 :     return osRet;
    6989             : }
    6990             : 
    6991             : //! @endcond
    6992             : 
    6993             : /************************************************************************/
    6994             : /*                   GDALAlgorithm::GetUsageAsJSON()                    */
    6995             : /************************************************************************/
    6996             : 
    6997         565 : std::string GDALAlgorithm::GetUsageAsJSON() const
    6998             : {
    6999        1130 :     CPLJSONDocument oDoc;
    7000        1130 :     auto oRoot = oDoc.GetRoot();
    7001             : 
    7002         565 :     if (m_displayInJSONUsage)
    7003             :     {
    7004         563 :         oRoot.Add("name", m_name);
    7005         563 :         CPLJSONArray jFullPath;
    7006        1177 :         for (const std::string &s : m_callPath)
    7007             :         {
    7008         614 :             jFullPath.Add(s);
    7009             :         }
    7010         563 :         oRoot.Add("full_path", jFullPath);
    7011             :     }
    7012             : 
    7013         565 :     oRoot.Add("description", m_description);
    7014         565 :     if (!m_helpURL.empty())
    7015             :     {
    7016         563 :         oRoot.Add("short_url", m_helpURL);
    7017         563 :         oRoot.Add("url", GetHelpFullURL());
    7018             :     }
    7019             : 
    7020        1130 :     CPLJSONArray jSubAlgorithms;
    7021         767 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    7022             :     {
    7023         404 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7024         202 :         if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
    7025             :         {
    7026         200 :             CPLJSONDocument oSubDoc;
    7027         200 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    7028         200 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    7029             :         }
    7030             :     }
    7031         565 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    7032             : 
    7033         565 :     if (m_arbitraryLongNameArgsAllowed)
    7034             :     {
    7035           1 :         oRoot.Add("user_provided_arguments_allowed", true);
    7036             :     }
    7037             : 
    7038       10806 :     const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
    7039             :     {
    7040        5403 :         CPLJSONObject jArg;
    7041        5403 :         jArg.Add("name", arg->GetName());
    7042        5403 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    7043        5403 :         jArg.Add("description", arg->GetDescription());
    7044             : 
    7045        5403 :         const auto &metaVar = arg->GetMetaVar();
    7046        5403 :         if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
    7047             :         {
    7048        1671 :             if (metaVar.front() == '<' && metaVar.back() == '>' &&
    7049        1671 :                 metaVar.substr(1, metaVar.size() - 2).find('>') ==
    7050             :                     std::string::npos)
    7051          32 :                 jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
    7052             :             else
    7053         888 :                 jArg.Add("metavar", metaVar);
    7054             :         }
    7055             : 
    7056        5403 :         if (!arg->IsAvailableInPipelineStep())
    7057             :         {
    7058        1567 :             jArg.Add("available_in_pipeline_step", false);
    7059             :         }
    7060             : 
    7061        5403 :         const auto &choices = arg->GetChoices();
    7062        5403 :         if (!choices.empty())
    7063             :         {
    7064         409 :             CPLJSONArray jChoices;
    7065        3510 :             for (const auto &choice : choices)
    7066        3101 :                 jChoices.Add(choice);
    7067         409 :             jArg.Add("choices", jChoices);
    7068             :         }
    7069        5403 :         if (arg->HasDefaultValue())
    7070             :         {
    7071        1193 :             switch (arg->GetType())
    7072             :             {
    7073         429 :                 case GAAT_BOOLEAN:
    7074         429 :                     jArg.Add("default", arg->GetDefault<bool>());
    7075         429 :                     break;
    7076         366 :                 case GAAT_STRING:
    7077         366 :                     jArg.Add("default", arg->GetDefault<std::string>());
    7078         366 :                     break;
    7079         200 :                 case GAAT_INTEGER:
    7080         200 :                     jArg.Add("default", arg->GetDefault<int>());
    7081         200 :                     break;
    7082         178 :                 case GAAT_REAL:
    7083         178 :                     jArg.Add("default", arg->GetDefault<double>());
    7084         178 :                     break;
    7085          18 :                 case GAAT_STRING_LIST:
    7086             :                 {
    7087             :                     const auto &val =
    7088          18 :                         arg->GetDefault<std::vector<std::string>>();
    7089          18 :                     if (val.size() == 1)
    7090             :                     {
    7091          17 :                         jArg.Add("default", val[0]);
    7092             :                     }
    7093             :                     else
    7094             :                     {
    7095           1 :                         CPLJSONArray jArr;
    7096           3 :                         for (const auto &s : val)
    7097             :                         {
    7098           2 :                             jArr.Add(s);
    7099             :                         }
    7100           1 :                         jArg.Add("default", jArr);
    7101             :                     }
    7102          18 :                     break;
    7103             :                 }
    7104           1 :                 case GAAT_INTEGER_LIST:
    7105             :                 {
    7106           1 :                     const auto &val = arg->GetDefault<std::vector<int>>();
    7107           1 :                     if (val.size() == 1)
    7108             :                     {
    7109           0 :                         jArg.Add("default", val[0]);
    7110             :                     }
    7111             :                     else
    7112             :                     {
    7113           1 :                         CPLJSONArray jArr;
    7114           3 :                         for (int i : val)
    7115             :                         {
    7116           2 :                             jArr.Add(i);
    7117             :                         }
    7118           1 :                         jArg.Add("default", jArr);
    7119             :                     }
    7120           1 :                     break;
    7121             :                 }
    7122           1 :                 case GAAT_REAL_LIST:
    7123             :                 {
    7124           1 :                     const auto &val = arg->GetDefault<std::vector<double>>();
    7125           1 :                     if (val.size() == 1)
    7126             :                     {
    7127           0 :                         jArg.Add("default", val[0]);
    7128             :                     }
    7129             :                     else
    7130             :                     {
    7131           1 :                         CPLJSONArray jArr;
    7132           3 :                         for (double d : val)
    7133             :                         {
    7134           2 :                             jArr.Add(d);
    7135             :                         }
    7136           1 :                         jArg.Add("default", jArr);
    7137             :                     }
    7138           1 :                     break;
    7139             :                 }
    7140           0 :                 case GAAT_DATASET:
    7141             :                 case GAAT_DATASET_LIST:
    7142           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    7143             :                              "Unhandled default value for arg %s",
    7144           0 :                              arg->GetName().c_str());
    7145           0 :                     break;
    7146             :             }
    7147             :         }
    7148             : 
    7149        5403 :         const auto [minVal, minValIsIncluded] = arg->GetMinValue();
    7150        5403 :         if (!std::isnan(minVal))
    7151             :         {
    7152         671 :             if (arg->GetType() == GAAT_INTEGER ||
    7153         261 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7154         173 :                 jArg.Add("min_value", static_cast<int>(minVal));
    7155             :             else
    7156         237 :                 jArg.Add("min_value", minVal);
    7157         410 :             jArg.Add("min_value_is_included", minValIsIncluded);
    7158             :         }
    7159             : 
    7160        5403 :         const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
    7161        5403 :         if (!std::isnan(maxVal))
    7162             :         {
    7163         199 :             if (arg->GetType() == GAAT_INTEGER ||
    7164          82 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7165          35 :                 jArg.Add("max_value", static_cast<int>(maxVal));
    7166             :             else
    7167          82 :                 jArg.Add("max_value", maxVal);
    7168         117 :             jArg.Add("max_value_is_included", maxValIsIncluded);
    7169             :         }
    7170             : 
    7171        5403 :         jArg.Add("required", arg->IsRequired());
    7172        5403 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    7173             :         {
    7174        1519 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    7175        1519 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    7176        1519 :             jArg.Add("min_count", arg->GetMinCount());
    7177        1519 :             jArg.Add("max_count", arg->GetMaxCount());
    7178             :         }
    7179             : 
    7180             :         // Process dependencies
    7181        5403 :         const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7182        5403 :         if (!mutualDependencyGroup.empty())
    7183             :         {
    7184          32 :             jArg.Add("mutual_dependency_group", mutualDependencyGroup);
    7185             :         }
    7186             : 
    7187       10806 :         CPLJSONArray jDependencies;
    7188          50 :         for (const auto &dependencyArgumentName :
    7189        5503 :              GetArgDependencies(arg->GetName()))
    7190             :         {
    7191          50 :             jDependencies.Add(dependencyArgumentName);
    7192             :         }
    7193             : 
    7194        5403 :         if (jDependencies.Size() > 0)
    7195             :         {
    7196          50 :             jArg.Add("depends_on", jDependencies);
    7197             :         }
    7198             : 
    7199        5403 :         jArg.Add("category", arg->GetCategory());
    7200             : 
    7201       10555 :         if (arg->GetType() == GAAT_DATASET ||
    7202        5152 :             arg->GetType() == GAAT_DATASET_LIST)
    7203             :         {
    7204             :             {
    7205         454 :                 CPLJSONArray jAr;
    7206         454 :                 if (arg->GetDatasetType() & GDAL_OF_RASTER)
    7207         311 :                     jAr.Add("raster");
    7208         454 :                 if (arg->GetDatasetType() & GDAL_OF_VECTOR)
    7209         179 :                     jAr.Add("vector");
    7210         454 :                 if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
    7211          29 :                     jAr.Add("multidim_raster");
    7212         454 :                 jArg.Add("dataset_type", jAr);
    7213             :             }
    7214             : 
    7215         621 :             const auto GetFlags = [](int flags)
    7216             :             {
    7217         621 :                 CPLJSONArray jAr;
    7218         621 :                 if (flags & GADV_NAME)
    7219         454 :                     jAr.Add("name");
    7220         621 :                 if (flags & GADV_OBJECT)
    7221         572 :                     jAr.Add("dataset");
    7222         621 :                 return jAr;
    7223             :             };
    7224             : 
    7225         454 :             if (arg->IsInput())
    7226             :             {
    7227         454 :                 jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
    7228             :             }
    7229         454 :             if (arg->IsOutput())
    7230             :             {
    7231         167 :                 jArg.Add("output_flags",
    7232         334 :                          GetFlags(arg->GetDatasetOutputFlags()));
    7233             :             }
    7234             :         }
    7235             : 
    7236        5403 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    7237        5403 :         if (!mutualExclusionGroup.empty())
    7238             :         {
    7239         673 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    7240             :         }
    7241             : 
    7242       10806 :         const auto &metadata = arg->GetMetadata();
    7243        5403 :         if (!metadata.empty())
    7244             :         {
    7245         450 :             CPLJSONObject jMetadata;
    7246         939 :             for (const auto &[key, values] : metadata)
    7247             :             {
    7248         978 :                 CPLJSONArray jValue;
    7249        1183 :                 for (const auto &value : values)
    7250         694 :                     jValue.Add(value);
    7251         489 :                 jMetadata.Add(key, jValue);
    7252             :             }
    7253         450 :             jArg.Add("metadata", jMetadata);
    7254             :         }
    7255             : 
    7256       10806 :         return jArg;
    7257         565 :     };
    7258             : 
    7259             :     {
    7260         565 :         CPLJSONArray jArgs;
    7261        8880 :         for (const auto &arg : m_args)
    7262             :         {
    7263        8315 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
    7264        5179 :                 jArgs.Add(ProcessArg(arg.get()));
    7265             :         }
    7266         565 :         oRoot.Add("input_arguments", jArgs);
    7267             :     }
    7268             : 
    7269             :     {
    7270         565 :         CPLJSONArray jArgs;
    7271        8880 :         for (const auto &arg : m_args)
    7272             :         {
    7273        8315 :             if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
    7274          57 :                 jArgs.Add(ProcessArg(arg.get()));
    7275             :         }
    7276         565 :         oRoot.Add("output_arguments", jArgs);
    7277             :     }
    7278             : 
    7279             :     {
    7280         565 :         CPLJSONArray jArgs;
    7281        8880 :         for (const auto &arg : m_args)
    7282             :         {
    7283        8315 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
    7284         167 :                 jArgs.Add(ProcessArg(arg.get()));
    7285             :         }
    7286         565 :         oRoot.Add("input_output_arguments", jArgs);
    7287             :     }
    7288             : 
    7289         565 :     if (m_supportsStreamedOutput)
    7290             :     {
    7291         117 :         oRoot.Add("supports_streamed_output", true);
    7292             :     }
    7293             : 
    7294        1130 :     return oDoc.SaveAsString();
    7295             : }
    7296             : 
    7297             : /************************************************************************/
    7298             : /*                   GDALAlgorithm::GetAutoComplete()                   */
    7299             : /************************************************************************/
    7300             : 
    7301             : std::vector<std::string>
    7302         293 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    7303             :                                bool lastWordIsComplete, bool showAllOptions)
    7304             : {
    7305         586 :     std::vector<std::string> ret;
    7306             : 
    7307             :     // Get inner-most algorithm
    7308         293 :     std::unique_ptr<GDALAlgorithm> curAlgHolder;
    7309         293 :     GDALAlgorithm *curAlg = this;
    7310         576 :     while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
    7311             :     {
    7312             :         auto subAlg = curAlg->InstantiateSubAlgorithm(
    7313         427 :             args.front(), /* suggestionAllowed = */ false);
    7314         427 :         if (!subAlg)
    7315         143 :             break;
    7316         284 :         if (args.size() == 1 && !lastWordIsComplete)
    7317             :         {
    7318           5 :             int nCount = 0;
    7319         115 :             for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
    7320             :             {
    7321         110 :                 if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
    7322           6 :                     nCount++;
    7323             :             }
    7324           5 :             if (nCount >= 2)
    7325             :             {
    7326          11 :                 for (const std::string &subAlgName :
    7327          23 :                      curAlg->GetSubAlgorithmNames())
    7328             :                 {
    7329          11 :                     subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
    7330          11 :                     if (subAlg && !subAlg->IsHidden())
    7331          11 :                         ret.push_back(subAlg->GetName());
    7332             :                 }
    7333           1 :                 return ret;
    7334             :             }
    7335             :         }
    7336         283 :         showAllOptions = false;
    7337         283 :         args.erase(args.begin());
    7338         283 :         curAlgHolder = std::move(subAlg);
    7339         283 :         curAlg = curAlgHolder.get();
    7340             :     }
    7341         292 :     if (curAlg != this)
    7342             :     {
    7343         154 :         curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
    7344             :         return curAlg->GetAutoComplete(args, lastWordIsComplete,
    7345         154 :                                        /* showAllOptions = */ false);
    7346             :     }
    7347             : 
    7348         276 :     std::string option;
    7349         276 :     std::string value;
    7350         138 :     ExtractLastOptionAndValue(args, option, value);
    7351             : 
    7352         169 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    7353          31 :         args.back()[0] == '-')
    7354             :     {
    7355          28 :         const auto &lastArg = args.back();
    7356             :         // List available options
    7357         405 :         for (const auto &arg : GetArgs())
    7358             :         {
    7359         701 :             if (arg->IsHidden() || arg->IsHiddenForCLI() ||
    7360         643 :                 (!showAllOptions &&
    7361         876 :                  (arg->GetName() == "help" || arg->GetName() == "config" ||
    7362         530 :                   arg->GetName() == "version" ||
    7363         265 :                   arg->GetName() == "json-usage")))
    7364             :             {
    7365         134 :                 continue;
    7366             :             }
    7367         243 :             if (!arg->GetShortName().empty())
    7368             :             {
    7369         153 :                 std::string str = std::string("-").append(arg->GetShortName());
    7370          51 :                 if (lastArg == str)
    7371           0 :                     ret.push_back(std::move(str));
    7372             :             }
    7373         243 :             if (lastArg != "-" && lastArg != "--")
    7374             :             {
    7375          54 :                 for (const std::string &alias : arg->GetAliases())
    7376             :                 {
    7377          48 :                     std::string str = std::string("--").append(alias);
    7378          16 :                     if (cpl::starts_with(str, lastArg))
    7379           3 :                         ret.push_back(std::move(str));
    7380             :                 }
    7381             :             }
    7382         243 :             if (!arg->GetName().empty())
    7383             :             {
    7384         729 :                 std::string str = std::string("--").append(arg->GetName());
    7385         243 :                 if (cpl::starts_with(str, lastArg))
    7386         207 :                     ret.push_back(std::move(str));
    7387             :             }
    7388             :         }
    7389          28 :         std::sort(ret.begin(), ret.end());
    7390             :     }
    7391         110 :     else if (!option.empty())
    7392             :     {
    7393             :         // List possible choices for current option
    7394         103 :         auto arg = GetArg(option);
    7395         103 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7396             :         {
    7397         103 :             ret = arg->GetChoices();
    7398         103 :             if (ret.empty())
    7399             :             {
    7400             :                 {
    7401          98 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7402          98 :                     SetParseForAutoCompletion();
    7403          98 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7404             :                 }
    7405          98 :                 ret = arg->GetAutoCompleteChoices(value);
    7406             :             }
    7407             :             else
    7408             :             {
    7409           5 :                 std::sort(ret.begin(), ret.end());
    7410             :             }
    7411         103 :             if (!ret.empty() && ret.back() == value)
    7412             :             {
    7413           2 :                 ret.clear();
    7414             :             }
    7415         101 :             else if (ret.empty())
    7416             :             {
    7417          13 :                 ret.push_back("**");
    7418             :                 // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    7419          26 :                 ret.push_back(std::string("\xC2\xA0"
    7420             :                                           "description: ")
    7421          13 :                                   .append(arg->GetDescription()));
    7422             :             }
    7423             :         }
    7424             :     }
    7425             :     else
    7426             :     {
    7427             :         // List possible sub-algorithms
    7428          69 :         for (const std::string &subAlgName : GetSubAlgorithmNames())
    7429             :         {
    7430         124 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7431          62 :             if (subAlg && !subAlg->IsHidden())
    7432          62 :                 ret.push_back(subAlg->GetName());
    7433             :         }
    7434           7 :         if (!ret.empty())
    7435             :         {
    7436           3 :             std::sort(ret.begin(), ret.end());
    7437             :         }
    7438             : 
    7439             :         // Try filenames
    7440           7 :         if (ret.empty() && !args.empty())
    7441             :         {
    7442             :             {
    7443           3 :                 CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7444           3 :                 SetParseForAutoCompletion();
    7445           3 :                 CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7446             :             }
    7447             : 
    7448           3 :             const std::string &lastArg = args.back();
    7449           3 :             GDALAlgorithmArg *arg = nullptr;
    7450          18 :             for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
    7451          21 :                                      "like", "source", "destination"})
    7452             :             {
    7453          18 :                 if (!arg)
    7454             :                 {
    7455           3 :                     auto newArg = GetArg(name);
    7456           3 :                     if (newArg)
    7457             :                     {
    7458           3 :                         if (!newArg->IsExplicitlySet())
    7459             :                         {
    7460           0 :                             arg = newArg;
    7461             :                         }
    7462           6 :                         else if (newArg->GetType() == GAAT_STRING ||
    7463           5 :                                  newArg->GetType() == GAAT_STRING_LIST ||
    7464           8 :                                  newArg->GetType() == GAAT_DATASET ||
    7465           2 :                                  newArg->GetType() == GAAT_DATASET_LIST)
    7466             :                         {
    7467             :                             VSIStatBufL sStat;
    7468           5 :                             if ((!lastArg.empty() && lastArg.back() == '/') ||
    7469           2 :                                 VSIStatL(lastArg.c_str(), &sStat) != 0)
    7470             :                             {
    7471           3 :                                 arg = newArg;
    7472             :                             }
    7473             :                         }
    7474             :                     }
    7475             :                 }
    7476             :             }
    7477           3 :             if (arg)
    7478             :             {
    7479           3 :                 ret = arg->GetAutoCompleteChoices(lastArg);
    7480             :             }
    7481             :         }
    7482             :     }
    7483             : 
    7484         138 :     return ret;
    7485             : }
    7486             : 
    7487             : /************************************************************************/
    7488             : /*                   GDALAlgorithm::GetFieldIndices()                   */
    7489             : /************************************************************************/
    7490             : 
    7491          44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
    7492             :                                     OGRLayerH hLayer, std::vector<int> &indices)
    7493             : {
    7494          44 :     VALIDATE_POINTER1(hLayer, __func__, false);
    7495             : 
    7496          44 :     const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
    7497             : 
    7498          44 :     if (names.size() == 1 && names[0] == "ALL")
    7499             :     {
    7500          12 :         const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
    7501          28 :         for (int i = 0; i < nSrcFieldCount; ++i)
    7502             :         {
    7503          16 :             indices.push_back(i);
    7504             :         }
    7505             :     }
    7506          32 :     else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
    7507             :     {
    7508           6 :         std::set<int> fieldsAdded;
    7509          14 :         for (const std::string &osFieldName : names)
    7510             :         {
    7511             : 
    7512             :             const int nIdx =
    7513          10 :                 layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
    7514             : 
    7515          10 :             if (nIdx < 0)
    7516             :             {
    7517           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7518             :                          "Field '%s' does not exist in layer '%s'",
    7519           2 :                          osFieldName.c_str(), layer.GetName());
    7520           2 :                 return false;
    7521             :             }
    7522             : 
    7523           8 :             if (fieldsAdded.insert(nIdx).second)
    7524             :             {
    7525           7 :                 indices.push_back(nIdx);
    7526             :             }
    7527             :         }
    7528             :     }
    7529             : 
    7530          42 :     return true;
    7531             : }
    7532             : 
    7533             : /************************************************************************/
    7534             : /*              GDALAlgorithm::ExtractLastOptionAndValue()              */
    7535             : /************************************************************************/
    7536             : 
    7537         138 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    7538             :                                               std::string &option,
    7539             :                                               std::string &value) const
    7540             : {
    7541         138 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    7542             :     {
    7543          96 :         const auto nPosEqual = args.back().find('=');
    7544          96 :         if (nPosEqual == std::string::npos)
    7545             :         {
    7546             :             // Deal with "gdal ... --option"
    7547          77 :             if (GetArg(args.back()))
    7548             :             {
    7549          49 :                 option = args.back();
    7550          49 :                 args.pop_back();
    7551             :             }
    7552             :         }
    7553             :         else
    7554             :         {
    7555             :             // Deal with "gdal ... --option=<value>"
    7556          19 :             if (GetArg(args.back().substr(0, nPosEqual)))
    7557             :             {
    7558          19 :                 option = args.back().substr(0, nPosEqual);
    7559          19 :                 value = args.back().substr(nPosEqual + 1);
    7560          19 :                 args.pop_back();
    7561             :             }
    7562             :         }
    7563             :     }
    7564          78 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    7565          36 :              args[args.size() - 2][0] == '-')
    7566             :     {
    7567             :         // Deal with "gdal ... --option <value>"
    7568          35 :         auto arg = GetArg(args[args.size() - 2]);
    7569          35 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7570             :         {
    7571          35 :             option = args[args.size() - 2];
    7572          35 :             value = args.back();
    7573          35 :             args.pop_back();
    7574             :         }
    7575             :     }
    7576             : 
    7577         138 :     const auto IsKeyValueOption = [](const std::string &osStr)
    7578             :     {
    7579         379 :         return osStr == "--co" || osStr == "--creation-option" ||
    7580         354 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    7581         377 :                osStr == "--oo" || osStr == "--open-option";
    7582             :     };
    7583             : 
    7584         138 :     if (IsKeyValueOption(option))
    7585             :     {
    7586          23 :         const auto nPosEqual = value.find('=');
    7587          23 :         if (nPosEqual != std::string::npos)
    7588             :         {
    7589          11 :             value.resize(nPosEqual);
    7590             :         }
    7591             :     }
    7592         138 : }
    7593             : 
    7594             : /************************************************************************/
    7595             : /*                 GDALAlgorithm::GetArgDependencies()                  */
    7596             : /************************************************************************/
    7597             : 
    7598             : std::vector<std::string>
    7599        7806 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
    7600             : {
    7601        7806 :     const auto arg = GetArg(osName, false);
    7602        7806 :     if (!arg)
    7603             :     {
    7604           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
    7605             :                     osName.c_str());
    7606           0 :         return {};
    7607             :     }
    7608       15612 :     std::vector<std::string> dependencies = arg->GetDirectDependencies();
    7609        7806 :     if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7610        7806 :         !mutualDependencyGroup.empty())
    7611             :     {
    7612         896 :         for (const auto &otherArg : m_args)
    7613             :         {
    7614        1627 :             if (otherArg.get() == arg ||
    7615         786 :                 mutualDependencyGroup.compare(
    7616         786 :                     otherArg->GetMutualDependencyGroup()) != 0)
    7617         783 :                 continue;
    7618          58 :             dependencies.push_back(otherArg->GetName());
    7619             :         }
    7620             :     }
    7621        7806 :     return dependencies;
    7622             : }
    7623             : 
    7624             : //! @cond Doxygen_Suppress
    7625             : 
    7626             : /************************************************************************/
    7627             : /*                  GDALContainerAlgorithm::RunImpl()                   */
    7628             : /************************************************************************/
    7629             : 
    7630           0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
    7631             : {
    7632           0 :     return false;
    7633             : }
    7634             : 
    7635             : //! @endcond
    7636             : 
    7637             : /************************************************************************/
    7638             : /*                        GDALAlgorithmRelease()                        */
    7639             : /************************************************************************/
    7640             : 
    7641             : /** Release a handle to an algorithm.
    7642             :  *
    7643             :  * @since 3.11
    7644             :  */
    7645       12603 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    7646             : {
    7647       12603 :     delete hAlg;
    7648       12603 : }
    7649             : 
    7650             : /************************************************************************/
    7651             : /*                        GDALAlgorithmGetName()                        */
    7652             : /************************************************************************/
    7653             : 
    7654             : /** Return the algorithm name.
    7655             :  *
    7656             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7657             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    7658             :  * be freed.
    7659             :  * @since 3.11
    7660             :  */
    7661        5783 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    7662             : {
    7663        5783 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7664        5783 :     return hAlg->ptr->GetName().c_str();
    7665             : }
    7666             : 
    7667             : /************************************************************************/
    7668             : /*                    GDALAlgorithmGetDescription()                     */
    7669             : /************************************************************************/
    7670             : 
    7671             : /** Return the algorithm (short) description.
    7672             :  *
    7673             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7674             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7675             :  * not be freed.
    7676             :  * @since 3.11
    7677             :  */
    7678        5701 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    7679             : {
    7680        5701 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7681        5701 :     return hAlg->ptr->GetDescription().c_str();
    7682             : }
    7683             : 
    7684             : /************************************************************************/
    7685             : /*                  GDALAlgorithmGetLongDescription()                   */
    7686             : /************************************************************************/
    7687             : 
    7688             : /** Return the algorithm (longer) description.
    7689             :  *
    7690             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7691             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7692             :  * not be freed.
    7693             :  * @since 3.11
    7694             :  */
    7695           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    7696             : {
    7697           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7698           2 :     return hAlg->ptr->GetLongDescription().c_str();
    7699             : }
    7700             : 
    7701             : /************************************************************************/
    7702             : /*                    GDALAlgorithmGetHelpFullURL()                     */
    7703             : /************************************************************************/
    7704             : 
    7705             : /** Return the algorithm full URL.
    7706             :  *
    7707             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7708             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    7709             :  * not be freed.
    7710             :  * @since 3.11
    7711             :  */
    7712        5004 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    7713             : {
    7714        5004 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7715        5004 :     return hAlg->ptr->GetHelpFullURL().c_str();
    7716             : }
    7717             : 
    7718             : /************************************************************************/
    7719             : /*                   GDALAlgorithmHasSubAlgorithms()                    */
    7720             : /************************************************************************/
    7721             : 
    7722             : /** Return whether the algorithm has sub-algorithms.
    7723             :  *
    7724             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7725             :  * @since 3.11
    7726             :  */
    7727        9261 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    7728             : {
    7729        9261 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7730        9261 :     return hAlg->ptr->HasSubAlgorithms();
    7731             : }
    7732             : 
    7733             : /************************************************************************/
    7734             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    7735             : /************************************************************************/
    7736             : 
    7737             : /** Get the names of registered algorithms.
    7738             :  *
    7739             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7740             :  * @return a NULL terminated list of names, which must be destroyed with
    7741             :  * CSLDestroy()
    7742             :  * @since 3.11
    7743             :  */
    7744         749 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    7745             : {
    7746         749 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7747         749 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    7748             : }
    7749             : 
    7750             : /************************************************************************/
    7751             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    7752             : /************************************************************************/
    7753             : 
    7754             : /** Instantiate an algorithm by its name (or its alias).
    7755             :  *
    7756             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7757             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    7758             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    7759             :  * or NULL if the algorithm does not exist or another error occurred.
    7760             :  * @since 3.11
    7761             :  */
    7762        8725 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    7763             :                                                     const char *pszSubAlgName)
    7764             : {
    7765        8725 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7766        8725 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    7767       17450 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    7768             :     return subAlg
    7769       17450 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    7770       17450 :                : nullptr;
    7771             : }
    7772             : 
    7773             : /************************************************************************/
    7774             : /*               GDALAlgorithmParseCommandLineArguments()               */
    7775             : /************************************************************************/
    7776             : 
    7777             : /** Parse a command line argument, which does not include the algorithm
    7778             :  * name, to set the value of corresponding arguments.
    7779             :  *
    7780             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7781             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    7782             :  * @return true if successful, false otherwise
    7783             :  * @since 3.11
    7784             :  */
    7785             : 
    7786         356 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    7787             :                                             CSLConstList papszArgs)
    7788             : {
    7789         356 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7790         356 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    7791             : }
    7792             : 
    7793             : /************************************************************************/
    7794             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    7795             : /************************************************************************/
    7796             : 
    7797             : /** Return the actual algorithm that is going to be invoked, when the
    7798             :  * current algorithm has sub-algorithms.
    7799             :  *
    7800             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    7801             :  *
    7802             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    7803             :  * the hAlg instance that owns it.
    7804             :  *
    7805             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7806             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    7807             :  * @since 3.11
    7808             :  */
    7809         914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    7810             : {
    7811         914 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7812         914 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    7813             : }
    7814             : 
    7815             : /************************************************************************/
    7816             : /*                          GDALAlgorithmRun()                          */
    7817             : /************************************************************************/
    7818             : 
    7819             : /** Execute the algorithm, starting with ValidateArguments() and then
    7820             :  * calling RunImpl().
    7821             :  *
    7822             :  * This function must be called at most once per instance.
    7823             :  *
    7824             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7825             :  * @param pfnProgress Progress callback. May be null.
    7826             :  * @param pProgressData Progress callback user data. May be null.
    7827             :  * @return true if successful, false otherwise
    7828             :  * @since 3.11
    7829             :  */
    7830             : 
    7831        2789 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    7832             :                       void *pProgressData)
    7833             : {
    7834        2789 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7835        2789 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    7836             : }
    7837             : 
    7838             : /************************************************************************/
    7839             : /*                       GDALAlgorithmFinalize()                        */
    7840             : /************************************************************************/
    7841             : 
    7842             : /** Complete any pending actions, and return the final status.
    7843             :  * This is typically useful for algorithm that generate an output dataset.
    7844             :  *
    7845             :  * Note that this function does *NOT* release memory associated with the
    7846             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    7847             :  *
    7848             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7849             :  * @return true if successful, false otherwise
    7850             :  * @since 3.11
    7851             :  */
    7852             : 
    7853         922 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    7854             : {
    7855         922 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7856         922 :     return hAlg->ptr->Finalize();
    7857             : }
    7858             : 
    7859             : /************************************************************************/
    7860             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    7861             : /************************************************************************/
    7862             : 
    7863             : /** Return the usage of the algorithm as a JSON-serialized string.
    7864             :  *
    7865             :  * This can be used to dynamically generate interfaces to algorithms.
    7866             :  *
    7867             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7868             :  * @return a string that must be freed with CPLFree()
    7869             :  * @since 3.11
    7870             :  */
    7871           6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    7872             : {
    7873           6 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7874           6 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    7875             : }
    7876             : 
    7877             : /************************************************************************/
    7878             : /*                      GDALAlgorithmGetArgNames()                      */
    7879             : /************************************************************************/
    7880             : 
    7881             : /** Return the list of available argument names.
    7882             :  *
    7883             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7884             :  * @return a NULL terminated list of names, which must be destroyed with
    7885             :  * CSLDestroy()
    7886             :  * @since 3.11
    7887             :  */
    7888       15574 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    7889             : {
    7890       15574 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7891       31148 :     CPLStringList list;
    7892      345890 :     for (const auto &arg : hAlg->ptr->GetArgs())
    7893      330316 :         list.AddString(arg->GetName().c_str());
    7894       15574 :     return list.StealList();
    7895             : }
    7896             : 
    7897             : /************************************************************************/
    7898             : /*                        GDALAlgorithmGetArg()                         */
    7899             : /************************************************************************/
    7900             : 
    7901             : /** Return an argument from its name.
    7902             :  *
    7903             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7904             :  *
    7905             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7906             :  * @param pszArgName Argument name. Must NOT be null.
    7907             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7908             :  * or nullptr in case of error
    7909             :  * @since 3.11
    7910             :  */
    7911      331226 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    7912             :                                       const char *pszArgName)
    7913             : {
    7914      331226 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7915      331226 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7916      662452 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7917      331226 :                                  /* isConst = */ true);
    7918      331226 :     if (!arg)
    7919           3 :         return nullptr;
    7920      331223 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7921             : }
    7922             : 
    7923             : /************************************************************************/
    7924             : /*                    GDALAlgorithmGetArgNonConst()                     */
    7925             : /************************************************************************/
    7926             : 
    7927             : /** Return an argument from its name, possibly allowing creation of user-provided
    7928             :  * argument if the algorithm allow it.
    7929             :  *
    7930             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7931             :  *
    7932             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7933             :  * @param pszArgName Argument name. Must NOT be null.
    7934             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7935             :  * or nullptr in case of error
    7936             :  * @since 3.12
    7937             :  */
    7938        9857 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
    7939             :                                               const char *pszArgName)
    7940             : {
    7941        9857 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7942        9857 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7943       19714 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7944        9857 :                                  /* isConst = */ false);
    7945        9857 :     if (!arg)
    7946           2 :         return nullptr;
    7947        9855 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7948             : }
    7949             : 
    7950             : /************************************************************************/
    7951             : /*                  GDALAlgorithmGetArgDependencies()                   */
    7952             : /************************************************************************/
    7953             : 
    7954             : /** Return the list of argument names the specified argument depends on.
    7955             :  *
    7956             :  *  This includes both regular dependencies and mutual dependencies.
    7957             :  *
    7958             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7959             :  * @param pszArgName Argument name. Must NOT be null.
    7960             :  * @return a NULL terminated list of names, which must be destroyed with
    7961             :  * CSLDestroy()
    7962             :  * @since 3.11
    7963             :  */
    7964           7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
    7965             :                                        const char *pszArgName)
    7966             : {
    7967           7 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7968           7 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7969           7 :     return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
    7970             : }
    7971             : 
    7972             : /************************************************************************/
    7973             : /*                      GDALAlgorithmArgRelease()                       */
    7974             : /************************************************************************/
    7975             : 
    7976             : /** Release a handle to an argument.
    7977             :  *
    7978             :  * @since 3.11
    7979             :  */
    7980      341078 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    7981             : {
    7982      341078 :     delete hArg;
    7983      341078 : }
    7984             : 
    7985             : /************************************************************************/
    7986             : /*                      GDALAlgorithmArgGetName()                       */
    7987             : /************************************************************************/
    7988             : 
    7989             : /** Return the name of an argument.
    7990             :  *
    7991             :  * @param hArg Handle to an argument. Must NOT be null.
    7992             :  * @return argument name whose lifetime is bound to hArg and which must not
    7993             :  * be freed.
    7994             :  * @since 3.11
    7995             :  */
    7996       18947 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    7997             : {
    7998       18947 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7999       18947 :     return hArg->ptr->GetName().c_str();
    8000             : }
    8001             : 
    8002             : /************************************************************************/
    8003             : /*                      GDALAlgorithmArgGetType()                       */
    8004             : /************************************************************************/
    8005             : 
    8006             : /** Get the type of an argument
    8007             :  *
    8008             :  * @param hArg Handle to an argument. Must NOT be null.
    8009             :  * @since 3.11
    8010             :  */
    8011      420660 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    8012             : {
    8013      420660 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    8014      420660 :     return hArg->ptr->GetType();
    8015             : }
    8016             : 
    8017             : /************************************************************************/
    8018             : /*                   GDALAlgorithmArgGetDescription()                   */
    8019             : /************************************************************************/
    8020             : 
    8021             : /** Return the description of an argument.
    8022             :  *
    8023             :  * @param hArg Handle to an argument. Must NOT be null.
    8024             :  * @return argument description whose lifetime is bound to hArg and which must not
    8025             :  * be freed.
    8026             :  * @since 3.11
    8027             :  */
    8028       83887 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    8029             : {
    8030       83887 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8031       83887 :     return hArg->ptr->GetDescription().c_str();
    8032             : }
    8033             : 
    8034             : /************************************************************************/
    8035             : /*                    GDALAlgorithmArgGetShortName()                    */
    8036             : /************************************************************************/
    8037             : 
    8038             : /** Return the short name, or empty string if there is none
    8039             :  *
    8040             :  * @param hArg Handle to an argument. Must NOT be null.
    8041             :  * @return short name whose lifetime is bound to hArg and which must not
    8042             :  * be freed.
    8043             :  * @since 3.11
    8044             :  */
    8045           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    8046             : {
    8047           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8048           1 :     return hArg->ptr->GetShortName().c_str();
    8049             : }
    8050             : 
    8051             : /************************************************************************/
    8052             : /*                     GDALAlgorithmArgGetAliases()                     */
    8053             : /************************************************************************/
    8054             : 
    8055             : /** Return the aliases (potentially none)
    8056             :  *
    8057             :  * @param hArg Handle to an argument. Must NOT be null.
    8058             :  * @return a NULL terminated list of names, which must be destroyed with
    8059             :  * CSLDestroy()
    8060             : 
    8061             :  * @since 3.11
    8062             :  */
    8063      158753 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    8064             : {
    8065      158753 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8066      158753 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    8067             : }
    8068             : 
    8069             : /************************************************************************/
    8070             : /*                     GDALAlgorithmArgGetMetaVar()                     */
    8071             : /************************************************************************/
    8072             : 
    8073             : /** Return the "meta-var" hint.
    8074             :  *
    8075             :  * By default, the meta-var value is the long name of the argument in
    8076             :  * upper case.
    8077             :  *
    8078             :  * @param hArg Handle to an argument. Must NOT be null.
    8079             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    8080             :  * be freed.
    8081             :  * @since 3.11
    8082             :  */
    8083           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    8084             : {
    8085           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8086           1 :     return hArg->ptr->GetMetaVar().c_str();
    8087             : }
    8088             : 
    8089             : /************************************************************************/
    8090             : /*                    GDALAlgorithmArgGetCategory()                     */
    8091             : /************************************************************************/
    8092             : 
    8093             : /** Return the argument category
    8094             :  *
    8095             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    8096             :  *
    8097             :  * @param hArg Handle to an argument. Must NOT be null.
    8098             :  * @return category whose lifetime is bound to hArg and which must not
    8099             :  * be freed.
    8100             :  * @since 3.11
    8101             :  */
    8102           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    8103             : {
    8104           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8105           1 :     return hArg->ptr->GetCategory().c_str();
    8106             : }
    8107             : 
    8108             : /************************************************************************/
    8109             : /*                    GDALAlgorithmArgIsPositional()                    */
    8110             : /************************************************************************/
    8111             : 
    8112             : /** Return if the argument is a positional one.
    8113             :  *
    8114             :  * @param hArg Handle to an argument. Must NOT be null.
    8115             :  * @since 3.11
    8116             :  */
    8117           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    8118             : {
    8119           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8120           1 :     return hArg->ptr->IsPositional();
    8121             : }
    8122             : 
    8123             : /************************************************************************/
    8124             : /*                     GDALAlgorithmArgIsRequired()                     */
    8125             : /************************************************************************/
    8126             : 
    8127             : /** Return whether the argument is required. Defaults to false.
    8128             :  *
    8129             :  * @param hArg Handle to an argument. Must NOT be null.
    8130             :  * @since 3.11
    8131             :  */
    8132      158753 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    8133             : {
    8134      158753 :     VALIDATE_POINTER1(hArg, __func__, false);
    8135      158753 :     return hArg->ptr->IsRequired();
    8136             : }
    8137             : 
    8138             : /************************************************************************/
    8139             : /*                    GDALAlgorithmArgGetMinCount()                     */
    8140             : /************************************************************************/
    8141             : 
    8142             : /** Return the minimum number of values for the argument.
    8143             :  *
    8144             :  * Defaults to 0.
    8145             :  * Only applies to list type of arguments.
    8146             :  *
    8147             :  * @param hArg Handle to an argument. Must NOT be null.
    8148             :  * @since 3.11
    8149             :  */
    8150           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    8151             : {
    8152           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8153           1 :     return hArg->ptr->GetMinCount();
    8154             : }
    8155             : 
    8156             : /************************************************************************/
    8157             : /*                    GDALAlgorithmArgGetMaxCount()                     */
    8158             : /************************************************************************/
    8159             : 
    8160             : /** Return the maximum number of values for the argument.
    8161             :  *
    8162             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    8163             :  * Only applies to list type of arguments.
    8164             :  *
    8165             :  * @param hArg Handle to an argument. Must NOT be null.
    8166             :  * @since 3.11
    8167             :  */
    8168           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    8169             : {
    8170           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8171           1 :     return hArg->ptr->GetMaxCount();
    8172             : }
    8173             : 
    8174             : /************************************************************************/
    8175             : /*               GDALAlgorithmArgGetPackedValuesAllowed()               */
    8176             : /************************************************************************/
    8177             : 
    8178             : /** Return whether, for list type of arguments, several values, space
    8179             :  * separated, may be specified. That is "--foo=bar,baz".
    8180             :  * The default is true.
    8181             :  *
    8182             :  * @param hArg Handle to an argument. Must NOT be null.
    8183             :  * @since 3.11
    8184             :  */
    8185           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    8186             : {
    8187           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8188           1 :     return hArg->ptr->GetPackedValuesAllowed();
    8189             : }
    8190             : 
    8191             : /************************************************************************/
    8192             : /*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
    8193             : /************************************************************************/
    8194             : 
    8195             : /** Return whether, for list type of arguments, the argument may be
    8196             :  * repeated. That is "--foo=bar --foo=baz".
    8197             :  * The default is true.
    8198             :  *
    8199             :  * @param hArg Handle to an argument. Must NOT be null.
    8200             :  * @since 3.11
    8201             :  */
    8202           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    8203             : {
    8204           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8205           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    8206             : }
    8207             : 
    8208             : /************************************************************************/
    8209             : /*                     GDALAlgorithmArgGetChoices()                     */
    8210             : /************************************************************************/
    8211             : 
    8212             : /** Return the allowed values (as strings) for the argument.
    8213             :  *
    8214             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    8215             :  *
    8216             :  * @param hArg Handle to an argument. Must NOT be null.
    8217             :  * @return a NULL terminated list of names, which must be destroyed with
    8218             :  * CSLDestroy()
    8219             : 
    8220             :  * @since 3.11
    8221             :  */
    8222           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    8223             : {
    8224           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8225           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    8226             : }
    8227             : 
    8228             : /************************************************************************/
    8229             : /*                  GDALAlgorithmArgGetMetadataItem()                   */
    8230             : /************************************************************************/
    8231             : 
    8232             : /** Return the values of the metadata item of an argument.
    8233             :  *
    8234             :  * @param hArg Handle to an argument. Must NOT be null.
    8235             :  * @param pszItem Name of the item. Must NOT be null.
    8236             :  * @return a NULL terminated list of values, which must be destroyed with
    8237             :  * CSLDestroy()
    8238             : 
    8239             :  * @since 3.11
    8240             :  */
    8241          63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
    8242             :                                        const char *pszItem)
    8243             : {
    8244          63 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8245          63 :     VALIDATE_POINTER1(pszItem, __func__, nullptr);
    8246          63 :     const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
    8247          63 :     return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
    8248             : }
    8249             : 
    8250             : /************************************************************************/
    8251             : /*                  GDALAlgorithmArgIsExplicitlySet()                   */
    8252             : /************************************************************************/
    8253             : 
    8254             : /** Return whether the argument value has been explicitly set with Set()
    8255             :  *
    8256             :  * @param hArg Handle to an argument. Must NOT be null.
    8257             :  * @since 3.11
    8258             :  */
    8259         663 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    8260             : {
    8261         663 :     VALIDATE_POINTER1(hArg, __func__, false);
    8262         663 :     return hArg->ptr->IsExplicitlySet();
    8263             : }
    8264             : 
    8265             : /************************************************************************/
    8266             : /*                  GDALAlgorithmArgHasDefaultValue()                   */
    8267             : /************************************************************************/
    8268             : 
    8269             : /** Return if the argument has a declared default value.
    8270             :  *
    8271             :  * @param hArg Handle to an argument. Must NOT be null.
    8272             :  * @since 3.11
    8273             :  */
    8274           2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    8275             : {
    8276           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8277           2 :     return hArg->ptr->HasDefaultValue();
    8278             : }
    8279             : 
    8280             : /************************************************************************/
    8281             : /*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
    8282             : /************************************************************************/
    8283             : 
    8284             : /** Return the argument default value as a integer.
    8285             :  *
    8286             :  * Must only be called on arguments whose type is GAAT_BOOLEAN
    8287             :  *
    8288             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8289             :  * argument has a default value.
    8290             :  *
    8291             :  * @param hArg Handle to an argument. Must NOT be null.
    8292             :  * @since 3.12
    8293             :  */
    8294           3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
    8295             : {
    8296           3 :     VALIDATE_POINTER1(hArg, __func__, false);
    8297           3 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8298             :     {
    8299           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8300             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8301             :                  __func__);
    8302           1 :         return false;
    8303             :     }
    8304           2 :     return hArg->ptr->GetDefault<bool>();
    8305             : }
    8306             : 
    8307             : /************************************************************************/
    8308             : /*                 GDALAlgorithmArgGetDefaultAsString()                 */
    8309             : /************************************************************************/
    8310             : 
    8311             : /** Return the argument default value as a string.
    8312             :  *
    8313             :  * Must only be called on arguments whose type is GAAT_STRING.
    8314             :  *
    8315             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8316             :  * argument has a default value.
    8317             :  *
    8318             :  * @param hArg Handle to an argument. Must NOT be null.
    8319             :  * @return string whose lifetime is bound to hArg and which must not
    8320             :  * be freed.
    8321             :  * @since 3.11
    8322             :  */
    8323           3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
    8324             : {
    8325           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8326           3 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8327             :     {
    8328           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8329             :                  "%s must only be called on arguments of type GAAT_STRING",
    8330             :                  __func__);
    8331           2 :         return nullptr;
    8332             :     }
    8333           1 :     return hArg->ptr->GetDefault<std::string>().c_str();
    8334             : }
    8335             : 
    8336             : /************************************************************************/
    8337             : /*                GDALAlgorithmArgGetDefaultAsInteger()                 */
    8338             : /************************************************************************/
    8339             : 
    8340             : /** Return the argument default value as a integer.
    8341             :  *
    8342             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8343             :  *
    8344             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8345             :  * argument has a default value.
    8346             :  *
    8347             :  * @param hArg Handle to an argument. Must NOT be null.
    8348             :  * @since 3.12
    8349             :  */
    8350           3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
    8351             : {
    8352           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8353           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8354             :     {
    8355           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8356             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8357             :                  __func__);
    8358           2 :         return 0;
    8359             :     }
    8360           1 :     return hArg->ptr->GetDefault<int>();
    8361             : }
    8362             : 
    8363             : /************************************************************************/
    8364             : /*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
    8365             : /************************************************************************/
    8366             : 
    8367             : /** Return the argument default value as a double.
    8368             :  *
    8369             :  * Must only be called on arguments whose type is GAAT_REAL
    8370             :  *
    8371             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8372             :  * argument has a default value.
    8373             :  *
    8374             :  * @param hArg Handle to an argument. Must NOT be null.
    8375             :  * @since 3.12
    8376             :  */
    8377           3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
    8378             : {
    8379           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8380           3 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8381             :     {
    8382           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8383             :                  "%s must only be called on arguments of type GAAT_REAL",
    8384             :                  __func__);
    8385           2 :         return 0;
    8386             :     }
    8387           1 :     return hArg->ptr->GetDefault<double>();
    8388             : }
    8389             : 
    8390             : /************************************************************************/
    8391             : /*               GDALAlgorithmArgGetDefaultAsStringList()               */
    8392             : /************************************************************************/
    8393             : 
    8394             : /** Return the argument default value as a string list.
    8395             :  *
    8396             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8397             :  *
    8398             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8399             :  * argument has a default value.
    8400             :  *
    8401             :  * @param hArg Handle to an argument. Must NOT be null.
    8402             :  * @return a NULL terminated list of names, which must be destroyed with
    8403             :  * CSLDestroy()
    8404             : 
    8405             :  * @since 3.12
    8406             :  */
    8407           3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
    8408             : {
    8409           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8410           3 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8411             :     {
    8412           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8413             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8414             :                  __func__);
    8415           2 :         return nullptr;
    8416             :     }
    8417           2 :     return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
    8418           1 :         .StealList();
    8419             : }
    8420             : 
    8421             : /************************************************************************/
    8422             : /*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
    8423             : /************************************************************************/
    8424             : 
    8425             : /** Return the argument default value as a integer list.
    8426             :  *
    8427             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8428             :  *
    8429             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8430             :  * argument has a default value.
    8431             :  *
    8432             :  * @param hArg Handle to an argument. Must NOT be null.
    8433             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8434             :  * @since 3.12
    8435             :  */
    8436           3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
    8437             :                                                    size_t *pnCount)
    8438             : {
    8439           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8440           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8441           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8442             :     {
    8443           2 :         CPLError(
    8444             :             CE_Failure, CPLE_AppDefined,
    8445             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8446             :             __func__);
    8447           2 :         *pnCount = 0;
    8448           2 :         return nullptr;
    8449             :     }
    8450           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
    8451           1 :     *pnCount = val.size();
    8452           1 :     return val.data();
    8453             : }
    8454             : 
    8455             : /************************************************************************/
    8456             : /*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
    8457             : /************************************************************************/
    8458             : 
    8459             : /** Return the argument default value as a real list.
    8460             :  *
    8461             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8462             :  *
    8463             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8464             :  * argument has a default value.
    8465             :  *
    8466             :  * @param hArg Handle to an argument. Must NOT be null.
    8467             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8468             :  * @since 3.12
    8469             :  */
    8470           3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
    8471             :                                                      size_t *pnCount)
    8472             : {
    8473           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8474           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8475           3 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8476             :     {
    8477           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8478             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8479             :                  __func__);
    8480           2 :         *pnCount = 0;
    8481           2 :         return nullptr;
    8482             :     }
    8483           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
    8484           1 :     *pnCount = val.size();
    8485           1 :     return val.data();
    8486             : }
    8487             : 
    8488             : /************************************************************************/
    8489             : /*                      GDALAlgorithmArgIsHidden()                      */
    8490             : /************************************************************************/
    8491             : 
    8492             : /** Return whether the argument is hidden (for GDAL internal use)
    8493             :  *
    8494             :  * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
    8495             :  * GDALAlgorithmArgIsHiddenForAPI().
    8496             :  *
    8497             :  * @param hArg Handle to an argument. Must NOT be null.
    8498             :  * @since 3.12
    8499             :  */
    8500           1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
    8501             : {
    8502           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8503           1 :     return hArg->ptr->IsHidden();
    8504             : }
    8505             : 
    8506             : /************************************************************************/
    8507             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    8508             : /************************************************************************/
    8509             : 
    8510             : /** Return whether the argument must not be mentioned in CLI usage.
    8511             :  *
    8512             :  * For example, "output-value" for "gdal raster info", which is only
    8513             :  * meant when the algorithm is used from a non-CLI context.
    8514             :  *
    8515             :  * @param hArg Handle to an argument. Must NOT be null.
    8516             :  * @since 3.11
    8517             :  */
    8518           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    8519             : {
    8520           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8521           1 :     return hArg->ptr->IsHiddenForCLI();
    8522             : }
    8523             : 
    8524             : /************************************************************************/
    8525             : /*                   GDALAlgorithmArgIsHiddenForAPI()                   */
    8526             : /************************************************************************/
    8527             : 
    8528             : /** Return whether the argument must not be mentioned in the context of an
    8529             :  * API use.
    8530             :  * Said otherwise, if it is only for CLI usage.
    8531             :  *
    8532             :  * For example "--help"
    8533             :  *
    8534             :  * @param hArg Handle to an argument. Must NOT be null.
    8535             :  * @since 3.12
    8536             :  */
    8537      214759 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
    8538             : {
    8539      214759 :     VALIDATE_POINTER1(hArg, __func__, false);
    8540      214759 :     return hArg->ptr->IsHiddenForAPI();
    8541             : }
    8542             : 
    8543             : /************************************************************************/
    8544             : /*                    GDALAlgorithmArgIsOnlyForCLI()                    */
    8545             : /************************************************************************/
    8546             : 
    8547             : /** Return whether the argument must not be mentioned in the context of an
    8548             :  * API use.
    8549             :  * Said otherwise, if it is only for CLI usage.
    8550             :  *
    8551             :  * For example "--help"
    8552             :  *
    8553             :  * @param hArg Handle to an argument. Must NOT be null.
    8554             :  * @since 3.11
    8555             :  * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
    8556             :  */
    8557           0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    8558             : {
    8559           0 :     VALIDATE_POINTER1(hArg, __func__, false);
    8560           0 :     return hArg->ptr->IsHiddenForAPI();
    8561             : }
    8562             : 
    8563             : /************************************************************************/
    8564             : /*             GDALAlgorithmArgIsAvailableInPipelineStep()              */
    8565             : /************************************************************************/
    8566             : 
    8567             : /** Return whether the argument is available in a pipeline step.
    8568             :  *
    8569             :  * If false, it is only available in standalone mode.
    8570             :  *
    8571             :  * @param hArg Handle to an argument. Must NOT be null.
    8572             :  * @since 3.13
    8573             :  */
    8574           2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
    8575             : {
    8576           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8577           2 :     return hArg->ptr->IsAvailableInPipelineStep();
    8578             : }
    8579             : 
    8580             : /************************************************************************/
    8581             : /*                      GDALAlgorithmArgIsInput()                       */
    8582             : /************************************************************************/
    8583             : 
    8584             : /** Indicate whether the value of the argument is read-only during the
    8585             :  * execution of the algorithm.
    8586             :  *
    8587             :  * Default is true.
    8588             :  *
    8589             :  * @param hArg Handle to an argument. Must NOT be null.
    8590             :  * @since 3.11
    8591             :  */
    8592      211889 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    8593             : {
    8594      211889 :     VALIDATE_POINTER1(hArg, __func__, false);
    8595      211889 :     return hArg->ptr->IsInput();
    8596             : }
    8597             : 
    8598             : /************************************************************************/
    8599             : /*                      GDALAlgorithmArgIsOutput()                      */
    8600             : /************************************************************************/
    8601             : 
    8602             : /** Return whether (at least part of) the value of the argument is set
    8603             :  * during the execution of the algorithm.
    8604             :  *
    8605             :  * For example, "output-value" for "gdal raster info"
    8606             :  * Default is false.
    8607             :  * An argument may return both IsInput() and IsOutput() as true.
    8608             :  * For example the "gdal raster convert" algorithm consumes the dataset
    8609             :  * name of its "output" argument, and sets the dataset object during its
    8610             :  * execution.
    8611             :  *
    8612             :  * @param hArg Handle to an argument. Must NOT be null.
    8613             :  * @since 3.11
    8614             :  */
    8615      118403 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    8616             : {
    8617      118403 :     VALIDATE_POINTER1(hArg, __func__, false);
    8618      118403 :     return hArg->ptr->IsOutput();
    8619             : }
    8620             : 
    8621             : /************************************************************************/
    8622             : /*                   GDALAlgorithmArgGetDatasetType()                   */
    8623             : /************************************************************************/
    8624             : 
    8625             : /** Get which type of dataset is allowed / generated.
    8626             :  *
    8627             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    8628             :  * GDAL_OF_MULTIDIM_RASTER.
    8629             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8630             :  *
    8631             :  * @param hArg Handle to an argument. Must NOT be null.
    8632             :  * @since 3.11
    8633             :  */
    8634           2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
    8635             : {
    8636           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8637           2 :     return hArg->ptr->GetDatasetType();
    8638             : }
    8639             : 
    8640             : /************************************************************************/
    8641             : /*                GDALAlgorithmArgGetDatasetInputFlags()                */
    8642             : /************************************************************************/
    8643             : 
    8644             : /** Indicates which components among name and dataset are accepted as
    8645             :  * input, when this argument serves as an input.
    8646             :  *
    8647             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    8648             :  * input.
    8649             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8650             :  * accepted as input.
    8651             :  * If both bits are set, the algorithm can accept either a name or a dataset
    8652             :  * object.
    8653             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8654             :  *
    8655             :  * @param hArg Handle to an argument. Must NOT be null.
    8656             :  * @return string whose lifetime is bound to hAlg and which must not
    8657             :  * be freed.
    8658             :  * @since 3.11
    8659             :  */
    8660           2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
    8661             : {
    8662           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8663           2 :     return hArg->ptr->GetDatasetInputFlags();
    8664             : }
    8665             : 
    8666             : /************************************************************************/
    8667             : /*               GDALAlgorithmArgGetDatasetOutputFlags()                */
    8668             : /************************************************************************/
    8669             : 
    8670             : /** Indicates which components among name and dataset are modified,
    8671             :  * when this argument serves as an output.
    8672             :  *
    8673             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    8674             :  * output (that is the algorithm will generate the name. Rarely used).
    8675             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8676             :  * generated as output, and available for use after the algorithm has
    8677             :  * completed.
    8678             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8679             :  *
    8680             :  * @param hArg Handle to an argument. Must NOT be null.
    8681             :  * @return string whose lifetime is bound to hAlg and which must not
    8682             :  * be freed.
    8683             :  * @since 3.11
    8684             :  */
    8685           2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
    8686             : {
    8687           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8688           2 :     return hArg->ptr->GetDatasetOutputFlags();
    8689             : }
    8690             : 
    8691             : /************************************************************************/
    8692             : /*              GDALAlgorithmArgGetMutualExclusionGroup()               */
    8693             : /************************************************************************/
    8694             : 
    8695             : /** Return the name of the mutual exclusion group to which this argument
    8696             :  * belongs to.
    8697             :  *
    8698             :  * Or empty string if it does not belong to any exclusion group.
    8699             :  *
    8700             :  * @param hArg Handle to an argument. Must NOT be null.
    8701             :  * @return string whose lifetime is bound to hArg and which must not
    8702             :  * be freed.
    8703             :  * @since 3.11
    8704             :  */
    8705           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    8706             : {
    8707           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8708           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    8709             : }
    8710             : 
    8711             : /************************************************************************/
    8712             : /*              GDALAlgorithmArgGetMutualDependencyGroup()              */
    8713             : /************************************************************************/
    8714             : 
    8715             : /** Return the name of the mutual dependency group to which this argument
    8716             :  * belongs to.
    8717             :  *
    8718             :  * Or empty string if it does not belong to any dependency group.
    8719             :  *
    8720             :  * @param hArg Handle to an argument. Must NOT be null.
    8721             :  * @return string whose lifetime is bound to hArg and which must not
    8722             :  * be freed.
    8723             :  * @since 3.13
    8724             :  */
    8725           5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
    8726             : {
    8727           5 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8728           5 :     return hArg->ptr->GetMutualDependencyGroup().c_str();
    8729             : }
    8730             : 
    8731             : /************************************************************************/
    8732             : /*               GDALAlgorithmArgGetDirectDependencies()                */
    8733             : /************************************************************************/
    8734             : 
    8735             : /** Return the list of names of arguments that this argument depends on.
    8736             :  *
    8737             :  *  This is not necessarily a symmetric relationship.
    8738             :  *  If argument A depends on argument B, it doesn't mean that B depends on A.
    8739             :  *  Mutual dependency groups are a special case of dependencies,
    8740             :  *  where all arguments of the group depend on each other and are not
    8741             :  *  returned by this method.
    8742             :  *
    8743             :  * @param hArg Handle to an argument. Must NOT be null.
    8744             :  * @return a NULL terminated list of names, which must be destroyed with
    8745             :  * CSLDestroy()
    8746             :  * @since 3.13
    8747             :  */
    8748           7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
    8749             : {
    8750           7 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8751           7 :     return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
    8752             : }
    8753             : 
    8754             : /************************************************************************/
    8755             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    8756             : /************************************************************************/
    8757             : 
    8758             : /** Return the argument value as a boolean.
    8759             :  *
    8760             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    8761             :  *
    8762             :  * @param hArg Handle to an argument. Must NOT be null.
    8763             :  * @since 3.11
    8764             :  */
    8765           8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    8766             : {
    8767           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    8768           8 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8769             :     {
    8770           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8771             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8772             :                  __func__);
    8773           1 :         return false;
    8774             :     }
    8775           7 :     return hArg->ptr->Get<bool>();
    8776             : }
    8777             : 
    8778             : /************************************************************************/
    8779             : /*                    GDALAlgorithmArgGetAsString()                     */
    8780             : /************************************************************************/
    8781             : 
    8782             : /** Return the argument value as a string.
    8783             :  *
    8784             :  * Must only be called on arguments whose type is GAAT_STRING.
    8785             :  *
    8786             :  * @param hArg Handle to an argument. Must NOT be null.
    8787             :  * @return string whose lifetime is bound to hArg and which must not
    8788             :  * be freed.
    8789             :  * @since 3.11
    8790             :  */
    8791         355 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    8792             : {
    8793         355 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8794         355 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8795             :     {
    8796           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8797             :                  "%s must only be called on arguments of type GAAT_STRING",
    8798             :                  __func__);
    8799           1 :         return nullptr;
    8800             :     }
    8801         354 :     return hArg->ptr->Get<std::string>().c_str();
    8802             : }
    8803             : 
    8804             : /************************************************************************/
    8805             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    8806             : /************************************************************************/
    8807             : 
    8808             : /** Return the argument value as a GDALArgDatasetValueH.
    8809             :  *
    8810             :  * Must only be called on arguments whose type is GAAT_DATASET
    8811             :  *
    8812             :  * @param hArg Handle to an argument. Must NOT be null.
    8813             :  * @return handle to a GDALArgDatasetValue that must be released with
    8814             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    8815             :  * the one of hArg.
    8816             :  * @since 3.11
    8817             :  */
    8818        3241 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    8819             : {
    8820        3241 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8821        3241 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    8822             :     {
    8823           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8824             :                  "%s must only be called on arguments of type GAAT_DATASET",
    8825             :                  __func__);
    8826           1 :         return nullptr;
    8827             :     }
    8828        3240 :     return std::make_unique<GDALArgDatasetValueHS>(
    8829        6480 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    8830        3240 :         .release();
    8831             : }
    8832             : 
    8833             : /************************************************************************/
    8834             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    8835             : /************************************************************************/
    8836             : 
    8837             : /** Return the argument value as a integer.
    8838             :  *
    8839             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8840             :  *
    8841             :  * @param hArg Handle to an argument. Must NOT be null.
    8842             :  * @since 3.11
    8843             :  */
    8844          26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    8845             : {
    8846          26 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8847          26 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8848             :     {
    8849           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8850             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8851             :                  __func__);
    8852           1 :         return 0;
    8853             :     }
    8854          25 :     return hArg->ptr->Get<int>();
    8855             : }
    8856             : 
    8857             : /************************************************************************/
    8858             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    8859             : /************************************************************************/
    8860             : 
    8861             : /** Return the argument value as a double.
    8862             :  *
    8863             :  * Must only be called on arguments whose type is GAAT_REAL
    8864             :  *
    8865             :  * @param hArg Handle to an argument. Must NOT be null.
    8866             :  * @since 3.11
    8867             :  */
    8868           8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    8869             : {
    8870           8 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8871           8 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8872             :     {
    8873           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8874             :                  "%s must only be called on arguments of type GAAT_REAL",
    8875             :                  __func__);
    8876           1 :         return 0;
    8877             :     }
    8878           7 :     return hArg->ptr->Get<double>();
    8879             : }
    8880             : 
    8881             : /************************************************************************/
    8882             : /*                  GDALAlgorithmArgGetAsStringList()                   */
    8883             : /************************************************************************/
    8884             : 
    8885             : /** Return the argument value as a string list.
    8886             :  *
    8887             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8888             :  *
    8889             :  * @param hArg Handle to an argument. Must NOT be null.
    8890             :  * @return a NULL terminated list of names, which must be destroyed with
    8891             :  * CSLDestroy()
    8892             : 
    8893             :  * @since 3.11
    8894             :  */
    8895           4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    8896             : {
    8897           4 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8898           4 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8899             :     {
    8900           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8901             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8902             :                  __func__);
    8903           1 :         return nullptr;
    8904             :     }
    8905           6 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    8906           3 :         .StealList();
    8907             : }
    8908             : 
    8909             : /************************************************************************/
    8910             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    8911             : /************************************************************************/
    8912             : 
    8913             : /** Return the argument value as a integer list.
    8914             :  *
    8915             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8916             :  *
    8917             :  * @param hArg Handle to an argument. Must NOT be null.
    8918             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8919             :  * @since 3.11
    8920             :  */
    8921           8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    8922             :                                             size_t *pnCount)
    8923             : {
    8924           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8925           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8926           8 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8927             :     {
    8928           1 :         CPLError(
    8929             :             CE_Failure, CPLE_AppDefined,
    8930             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8931             :             __func__);
    8932           1 :         *pnCount = 0;
    8933           1 :         return nullptr;
    8934             :     }
    8935           7 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    8936           7 :     *pnCount = val.size();
    8937           7 :     return val.data();
    8938             : }
    8939             : 
    8940             : /************************************************************************/
    8941             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    8942             : /************************************************************************/
    8943             : 
    8944             : /** Return the argument value as a real list.
    8945             :  *
    8946             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8947             :  *
    8948             :  * @param hArg Handle to an argument. Must NOT be null.
    8949             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8950             :  * @since 3.11
    8951             :  */
    8952           8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    8953             :                                               size_t *pnCount)
    8954             : {
    8955           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8956           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8957           8 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8958             :     {
    8959           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8960             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8961             :                  __func__);
    8962           1 :         *pnCount = 0;
    8963           1 :         return nullptr;
    8964             :     }
    8965           7 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    8966           7 :     *pnCount = val.size();
    8967           7 :     return val.data();
    8968             : }
    8969             : 
    8970             : /************************************************************************/
    8971             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    8972             : /************************************************************************/
    8973             : 
    8974             : /** Set the value for a GAAT_BOOLEAN argument.
    8975             :  *
    8976             :  * It cannot be called several times for a given argument.
    8977             :  * Validation checks and other actions are run.
    8978             :  *
    8979             :  * @param hArg Handle to an argument. Must NOT be null.
    8980             :  * @param value value.
    8981             :  * @return true if success.
    8982             :  * @since 3.11
    8983             :  */
    8984             : 
    8985         698 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    8986             : {
    8987         698 :     VALIDATE_POINTER1(hArg, __func__, false);
    8988         698 :     return hArg->ptr->Set(value);
    8989             : }
    8990             : 
    8991             : /************************************************************************/
    8992             : /*                    GDALAlgorithmArgSetAsString()                     */
    8993             : /************************************************************************/
    8994             : 
    8995             : /** Set the value for a GAAT_STRING argument.
    8996             :  *
    8997             :  * It cannot be called several times for a given argument.
    8998             :  * Validation checks and other actions are run.
    8999             :  *
    9000             :  * @param hArg Handle to an argument. Must NOT be null.
    9001             :  * @param value value (may be null)
    9002             :  * @return true if success.
    9003             :  * @since 3.11
    9004             :  */
    9005             : 
    9006        3177 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    9007             : {
    9008        3177 :     VALIDATE_POINTER1(hArg, __func__, false);
    9009        3177 :     return hArg->ptr->Set(value ? value : "");
    9010             : }
    9011             : 
    9012             : /************************************************************************/
    9013             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    9014             : /************************************************************************/
    9015             : 
    9016             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    9017             :  *
    9018             :  * It cannot be called several times for a given argument.
    9019             :  * Validation checks and other actions are run.
    9020             :  *
    9021             :  * @param hArg Handle to an argument. Must NOT be null.
    9022             :  * @param value value.
    9023             :  * @return true if success.
    9024             :  * @since 3.11
    9025             :  */
    9026             : 
    9027         473 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    9028             : {
    9029         473 :     VALIDATE_POINTER1(hArg, __func__, false);
    9030         473 :     return hArg->ptr->Set(value);
    9031             : }
    9032             : 
    9033             : /************************************************************************/
    9034             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    9035             : /************************************************************************/
    9036             : 
    9037             : /** Set the value for a GAAT_REAL argument.
    9038             :  *
    9039             :  * It cannot be called several times for a given argument.
    9040             :  * Validation checks and other actions are run.
    9041             :  *
    9042             :  * @param hArg Handle to an argument. Must NOT be null.
    9043             :  * @param value value.
    9044             :  * @return true if success.
    9045             :  * @since 3.11
    9046             :  */
    9047             : 
    9048         243 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    9049             : {
    9050         243 :     VALIDATE_POINTER1(hArg, __func__, false);
    9051         243 :     return hArg->ptr->Set(value);
    9052             : }
    9053             : 
    9054             : /************************************************************************/
    9055             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    9056             : /************************************************************************/
    9057             : 
    9058             : /** Set the value for a GAAT_DATASET argument.
    9059             :  *
    9060             :  * It cannot be called several times for a given argument.
    9061             :  * Validation checks and other actions are run.
    9062             :  *
    9063             :  * @param hArg Handle to an argument. Must NOT be null.
    9064             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    9065             :  * @return true if success.
    9066             :  * @since 3.11
    9067             :  */
    9068           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    9069             :                                        GDALArgDatasetValueH value)
    9070             : {
    9071           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    9072           2 :     VALIDATE_POINTER1(value, __func__, false);
    9073           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    9074             : }
    9075             : 
    9076             : /************************************************************************/
    9077             : /*                     GDALAlgorithmArgSetDataset()                     */
    9078             : /************************************************************************/
    9079             : 
    9080             : /** Set dataset object, increasing its reference counter.
    9081             :  *
    9082             :  * @param hArg Handle to an argument. Must NOT be null.
    9083             :  * @param hDS Dataset object. May be null.
    9084             :  * @return true if success.
    9085             :  * @since 3.11
    9086             :  */
    9087             : 
    9088           2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    9089             : {
    9090           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    9091           2 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    9092             : }
    9093             : 
    9094             : /************************************************************************/
    9095             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    9096             : /************************************************************************/
    9097             : 
    9098             : /** Set the value for a GAAT_STRING_LIST argument.
    9099             :  *
    9100             :  * It cannot be called several times for a given argument.
    9101             :  * Validation checks and other actions are run.
    9102             :  *
    9103             :  * @param hArg Handle to an argument. Must NOT be null.
    9104             :  * @param value value as a NULL terminated list (may be null)
    9105             :  * @return true if success.
    9106             :  * @since 3.11
    9107             :  */
    9108             : 
    9109         788 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    9110             : {
    9111         788 :     VALIDATE_POINTER1(hArg, __func__, false);
    9112         788 :     return hArg->ptr->Set(
    9113        1576 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    9114             : }
    9115             : 
    9116             : /************************************************************************/
    9117             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    9118             : /************************************************************************/
    9119             : 
    9120             : /** Set the value for a GAAT_INTEGER_LIST argument.
    9121             :  *
    9122             :  * It cannot be called several times for a given argument.
    9123             :  * Validation checks and other actions are run.
    9124             :  *
    9125             :  * @param hArg Handle to an argument. Must NOT be null.
    9126             :  * @param nCount Number of values in pnValues.
    9127             :  * @param pnValues Pointer to an array of integer values of size nCount.
    9128             :  * @return true if success.
    9129             :  * @since 3.11
    9130             :  */
    9131         105 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    9132             :                                       const int *pnValues)
    9133             : {
    9134         105 :     VALIDATE_POINTER1(hArg, __func__, false);
    9135         105 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    9136             : }
    9137             : 
    9138             : /************************************************************************/
    9139             : /*                  GDALAlgorithmArgSetAsDoubleList()                   */
    9140             : /************************************************************************/
    9141             : 
    9142             : /** Set the value for a GAAT_REAL_LIST argument.
    9143             :  *
    9144             :  * It cannot be called several times for a given argument.
    9145             :  * Validation checks and other actions are run.
    9146             :  *
    9147             :  * @param hArg Handle to an argument. Must NOT be null.
    9148             :  * @param nCount Number of values in pnValues.
    9149             :  * @param pnValues Pointer to an array of double values of size nCount.
    9150             :  * @return true if success.
    9151             :  * @since 3.11
    9152             :  */
    9153         159 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    9154             :                                      const double *pnValues)
    9155             : {
    9156         159 :     VALIDATE_POINTER1(hArg, __func__, false);
    9157         159 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    9158             : }
    9159             : 
    9160             : /************************************************************************/
    9161             : /*                    GDALAlgorithmArgSetDatasets()                     */
    9162             : /************************************************************************/
    9163             : 
    9164             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    9165             :  *
    9166             :  * @param hArg Handle to an argument. Must NOT be null.
    9167             :  * @param nCount Number of values in pnValues.
    9168             :  * @param pahDS Pointer to an array of dataset of size nCount.
    9169             :  * @return true if success.
    9170             :  * @since 3.11
    9171             :  */
    9172             : 
    9173        1288 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    9174             :                                  GDALDatasetH *pahDS)
    9175             : {
    9176        1288 :     VALIDATE_POINTER1(hArg, __func__, false);
    9177        2576 :     std::vector<GDALArgDatasetValue> values;
    9178        2602 :     for (size_t i = 0; i < nCount; ++i)
    9179             :     {
    9180        1314 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    9181             :     }
    9182        1288 :     return hArg->ptr->Set(std::move(values));
    9183             : }
    9184             : 
    9185             : /************************************************************************/
    9186             : /*                  GDALAlgorithmArgSetDatasetNames()                   */
    9187             : /************************************************************************/
    9188             : 
    9189             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    9190             :  *
    9191             :  * @param hArg Handle to an argument. Must NOT be null.
    9192             :  * @param names Dataset names as a NULL terminated list (may be null)
    9193             :  * @return true if success.
    9194             :  * @since 3.11
    9195             :  */
    9196             : 
    9197         750 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    9198             : {
    9199         750 :     VALIDATE_POINTER1(hArg, __func__, false);
    9200        1500 :     std::vector<GDALArgDatasetValue> values;
    9201        1571 :     for (size_t i = 0; names[i]; ++i)
    9202             :     {
    9203         821 :         values.emplace_back(names[i]);
    9204             :     }
    9205         750 :     return hArg->ptr->Set(std::move(values));
    9206             : }
    9207             : 
    9208             : /************************************************************************/
    9209             : /*                     GDALArgDatasetValueCreate()                      */
    9210             : /************************************************************************/
    9211             : 
    9212             : /** Instantiate an empty GDALArgDatasetValue
    9213             :  *
    9214             :  * @return new handle to free with GDALArgDatasetValueRelease()
    9215             :  * @since 3.11
    9216             :  */
    9217           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    9218             : {
    9219           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    9220             : }
    9221             : 
    9222             : /************************************************************************/
    9223             : /*                     GDALArgDatasetValueRelease()                     */
    9224             : /************************************************************************/
    9225             : 
    9226             : /** Release a handle to a GDALArgDatasetValue
    9227             :  *
    9228             :  * @since 3.11
    9229             :  */
    9230        3241 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    9231             : {
    9232        3241 :     delete hValue;
    9233        3241 : }
    9234             : 
    9235             : /************************************************************************/
    9236             : /*                     GDALArgDatasetValueGetName()                     */
    9237             : /************************************************************************/
    9238             : 
    9239             : /** Return the name component of the GDALArgDatasetValue
    9240             :  *
    9241             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9242             :  * @return string whose lifetime is bound to hAlg and which must not
    9243             :  * be freed.
    9244             :  * @since 3.11
    9245             :  */
    9246           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    9247             : {
    9248           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9249           1 :     return hValue->ptr->GetName().c_str();
    9250             : }
    9251             : 
    9252             : /************************************************************************/
    9253             : /*                  GDALArgDatasetValueGetDatasetRef()                  */
    9254             : /************************************************************************/
    9255             : 
    9256             : /** Return the dataset component of the GDALArgDatasetValue.
    9257             :  *
    9258             :  * This does not modify the reference counter, hence the lifetime of the
    9259             :  * returned object is not guaranteed to exceed the one of hValue.
    9260             :  *
    9261             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9262             :  * @since 3.11
    9263             :  */
    9264           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    9265             : {
    9266           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9267           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    9268             : }
    9269             : 
    9270             : /************************************************************************/
    9271             : /*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
    9272             : /************************************************************************/
    9273             : 
    9274             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    9275             :  * reference count if not null. Once done with the dataset, the caller should
    9276             :  * call GDALReleaseDataset().
    9277             :  *
    9278             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9279             :  * @since 3.11
    9280             :  */
    9281             : GDALDatasetH
    9282        1069 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    9283             : {
    9284        1069 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9285        1069 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    9286             : }
    9287             : 
    9288             : /************************************************************************/
    9289             : /*                     GDALArgDatasetValueSetName()                     */
    9290             : /************************************************************************/
    9291             : 
    9292             : /** Set dataset name
    9293             :  *
    9294             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9295             :  * @param pszName Dataset name. May be null.
    9296             :  * @since 3.11
    9297             :  */
    9298             : 
    9299        1390 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    9300             :                                 const char *pszName)
    9301             : {
    9302        1390 :     VALIDATE_POINTER0(hValue, __func__);
    9303        1390 :     hValue->ptr->Set(pszName ? pszName : "");
    9304             : }
    9305             : 
    9306             : /************************************************************************/
    9307             : /*                   GDALArgDatasetValueSetDataset()                    */
    9308             : /************************************************************************/
    9309             : 
    9310             : /** Set dataset object, increasing its reference counter.
    9311             :  *
    9312             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9313             :  * @param hDS Dataset object. May be null.
    9314             :  * @since 3.11
    9315             :  */
    9316             : 
    9317         767 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    9318             :                                    GDALDatasetH hDS)
    9319             : {
    9320         767 :     VALIDATE_POINTER0(hValue, __func__);
    9321         767 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    9322             : }

Generated by: LCOV version 1.14