LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3771 4016 93.9 %
Date: 2026-05-12 01:09:31 Functions: 284 291 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_error.h"
      16             : #include "cpl_error_internal.h"
      17             : #include "cpl_json.h"
      18             : #include "cpl_levenshtein.h"
      19             : #include "cpl_minixml.h"
      20             : #include "cpl_multiproc.h"
      21             : 
      22             : #include "gdalalgorithm.h"
      23             : #include "gdalalg_abstract_pipeline.h"
      24             : #include "gdal_priv.h"
      25             : #include "gdal_thread_pool.h"
      26             : #include "memdataset.h"
      27             : #include "ogrsf_frmts.h"
      28             : #include "ogr_spatialref.h"
      29             : #include "vrtdataset.h"
      30             : 
      31             : #include <algorithm>
      32             : #include <cassert>
      33             : #include <cerrno>
      34             : #include <cmath>
      35             : #include <cstdlib>
      36             : #include <limits>
      37             : #include <map>
      38             : #include <type_traits>
      39             : #include <string_view>
      40             : #include <regex>
      41             : 
      42             : #ifndef _
      43             : #define _(x) (x)
      44             : #endif
      45             : 
      46             : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
      47             : 
      48             : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
      49             : 
      50             : constexpr const char *GDAL_ARG_NAME_BAND = "band";
      51             : 
      52             : //! @cond Doxygen_Suppress
      53             : struct GDALAlgorithmArgHS
      54             : {
      55             :     GDALAlgorithmArg *ptr = nullptr;
      56             : 
      57      339706 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      58             :     {
      59      339706 :     }
      60             : };
      61             : 
      62             : //! @endcond
      63             : 
      64             : //! @cond Doxygen_Suppress
      65             : struct GDALArgDatasetValueHS
      66             : {
      67             :     GDALArgDatasetValue val{};
      68             :     GDALArgDatasetValue *ptr = nullptr;
      69             : 
      70           1 :     GDALArgDatasetValueHS() : ptr(&val)
      71             :     {
      72           1 :     }
      73             : 
      74        3206 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      75             :     {
      76        3206 :     }
      77             : 
      78             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      79             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      80             : };
      81             : 
      82             : //! @endcond
      83             : 
      84             : /************************************************************************/
      85             : /*                     GDALAlgorithmArgTypeIsList()                     */
      86             : /************************************************************************/
      87             : 
      88      412633 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      89             : {
      90      412633 :     switch (type)
      91             :     {
      92      272064 :         case GAAT_BOOLEAN:
      93             :         case GAAT_STRING:
      94             :         case GAAT_INTEGER:
      95             :         case GAAT_REAL:
      96             :         case GAAT_DATASET:
      97      272064 :             break;
      98             : 
      99      140569 :         case GAAT_STRING_LIST:
     100             :         case GAAT_INTEGER_LIST:
     101             :         case GAAT_REAL_LIST:
     102             :         case GAAT_DATASET_LIST:
     103      140569 :             return true;
     104             :     }
     105             : 
     106      272064 :     return false;
     107             : }
     108             : 
     109             : /************************************************************************/
     110             : /*                      GDALAlgorithmArgTypeName()                      */
     111             : /************************************************************************/
     112             : 
     113        5480 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     114             : {
     115        5480 :     switch (type)
     116             :     {
     117        1362 :         case GAAT_BOOLEAN:
     118        1362 :             break;
     119        1476 :         case GAAT_STRING:
     120        1476 :             return "string";
     121         379 :         case GAAT_INTEGER:
     122         379 :             return "integer";
     123         477 :         case GAAT_REAL:
     124         477 :             return "real";
     125         251 :         case GAAT_DATASET:
     126         251 :             return "dataset";
     127        1016 :         case GAAT_STRING_LIST:
     128        1016 :             return "string_list";
     129          93 :         case GAAT_INTEGER_LIST:
     130          93 :             return "integer_list";
     131         221 :         case GAAT_REAL_LIST:
     132         221 :             return "real_list";
     133         205 :         case GAAT_DATASET_LIST:
     134         205 :             return "dataset_list";
     135             :     }
     136             : 
     137        1362 :     return "boolean";
     138             : }
     139             : 
     140             : /************************************************************************/
     141             : /*                  GDALAlgorithmArgDatasetTypeName()                   */
     142             : /************************************************************************/
     143             : 
     144       22313 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
     145             : {
     146       22313 :     std::string ret;
     147       22313 :     if ((type & GDAL_OF_RASTER) != 0)
     148       12277 :         ret = "raster";
     149       22313 :     if ((type & GDAL_OF_VECTOR) != 0)
     150             :     {
     151       10830 :         if (!ret.empty())
     152             :         {
     153        1187 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     154         257 :                 ret += ", ";
     155             :             else
     156         930 :                 ret += " or ";
     157             :         }
     158       10830 :         ret += "vector";
     159             :     }
     160       22313 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     161             :     {
     162         576 :         if (!ret.empty())
     163             :         {
     164         316 :             ret += " or ";
     165             :         }
     166         576 :         ret += "multidimensional raster";
     167             :     }
     168       22313 :     return ret;
     169             : }
     170             : 
     171             : /************************************************************************/
     172             : /*                        GDALAlgorithmArgDecl()                        */
     173             : /************************************************************************/
     174             : 
     175             : // cppcheck-suppress uninitMemberVar
     176      331756 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     177             :                                            char chShortName,
     178             :                                            const std::string &description,
     179      331756 :                                            GDALAlgorithmArgType type)
     180             :     : m_longName(longName),
     181      331756 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     182             :       m_description(description), m_type(type),
     183      663512 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     184      331756 :                     .toupper()),
     185      995268 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     186             : {
     187      331756 :     if (m_type == GAAT_BOOLEAN)
     188             :     {
     189      138411 :         m_defaultValue = false;
     190             :     }
     191      331756 : }
     192             : 
     193             : /************************************************************************/
     194             : /*                 GDALAlgorithmArgDecl::SetMinCount()                  */
     195             : /************************************************************************/
     196             : 
     197       18203 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     198             : {
     199       18203 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     200             :     {
     201           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     202             :                  "SetMinCount() illegal on scalar argument '%s'",
     203           1 :                  GetName().c_str());
     204             :     }
     205             :     else
     206             :     {
     207       18202 :         m_minCount = count;
     208             :     }
     209       18203 :     return *this;
     210             : }
     211             : 
     212             : /************************************************************************/
     213             : /*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
     214             : /************************************************************************/
     215             : 
     216       17235 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     217             : {
     218       17235 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     219             :     {
     220           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     221             :                  "SetMaxCount() illegal on scalar argument '%s'",
     222           1 :                  GetName().c_str());
     223             :     }
     224             :     else
     225             :     {
     226       17234 :         m_maxCount = count;
     227             :     }
     228       17235 :     return *this;
     229             : }
     230             : 
     231             : /************************************************************************/
     232             : /*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
     233             : /************************************************************************/
     234             : 
     235             : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
     236             : 
     237             : /************************************************************************/
     238             : /*                       GDALAlgorithmArg::Set()                        */
     239             : /************************************************************************/
     240             : 
     241        1224 : bool GDALAlgorithmArg::Set(bool value)
     242             : {
     243        1224 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     244             :     {
     245          14 :         CPLError(
     246             :             CE_Failure, CPLE_AppDefined,
     247             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     248           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     249           7 :         return false;
     250             :     }
     251        1217 :     return SetInternal(value);
     252             : }
     253             : 
     254        4082 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
     255             : {
     256        4127 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     257          45 :         value.front() == '@')
     258             :     {
     259           2 :         GByte *pabyData = nullptr;
     260           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     261           2 :                           10 * 1024 * 1024))
     262             :         {
     263             :             // Remove UTF-8 BOM
     264           1 :             size_t offset = 0;
     265           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     266           1 :                 pabyData[2] == 0xBF)
     267             :             {
     268           1 :                 offset = 3;
     269             :             }
     270           1 :             value = reinterpret_cast<const char *>(pabyData + offset);
     271           1 :             VSIFree(pabyData);
     272             :         }
     273             :         else
     274             :         {
     275           1 :             return false;
     276             :         }
     277             :     }
     278             : 
     279        4081 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     280          44 :         value = CPLRemoveSQLComments(value);
     281             : 
     282        4081 :     return true;
     283             : }
     284             : 
     285        4098 : bool GDALAlgorithmArg::Set(const std::string &value)
     286             : {
     287        4098 :     switch (m_decl.GetType())
     288             :     {
     289           9 :         case GAAT_BOOLEAN:
     290          17 :             if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
     291          17 :                 EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
     292             :             {
     293           4 :                 return Set(true);
     294             :             }
     295           5 :             else if (EQUAL(value.c_str(), "0") ||
     296           4 :                      EQUAL(value.c_str(), "FALSE") ||
     297           9 :                      EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
     298             :             {
     299           4 :                 return Set(false);
     300             :             }
     301           1 :             break;
     302             : 
     303           8 :         case GAAT_INTEGER:
     304             :         case GAAT_INTEGER_LIST:
     305             :         {
     306           8 :             errno = 0;
     307           8 :             char *endptr = nullptr;
     308           8 :             const auto v = std::strtoll(value.c_str(), &endptr, 10);
     309          13 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     310           5 :                 endptr == value.c_str() + value.size())
     311             :             {
     312           3 :                 if (m_decl.GetType() == GAAT_INTEGER)
     313           3 :                     return Set(static_cast<int>(v));
     314             :                 else
     315           1 :                     return Set(std::vector<int>{static_cast<int>(v)});
     316             :             }
     317           5 :             break;
     318             :         }
     319             : 
     320           5 :         case GAAT_REAL:
     321             :         case GAAT_REAL_LIST:
     322             :         {
     323           5 :             char *endptr = nullptr;
     324           5 :             const double v = CPLStrtod(value.c_str(), &endptr);
     325           5 :             if (endptr == value.c_str() + value.size())
     326             :             {
     327           3 :                 if (m_decl.GetType() == GAAT_REAL)
     328           3 :                     return Set(v);
     329             :                 else
     330           1 :                     return Set(std::vector<double>{v});
     331             :             }
     332           2 :             break;
     333             :         }
     334             : 
     335        4060 :         case GAAT_STRING:
     336        4060 :             break;
     337             : 
     338           1 :         case GAAT_STRING_LIST:
     339           2 :             return Set(std::vector<std::string>{value});
     340             : 
     341          15 :         case GAAT_DATASET:
     342          15 :             return SetDatasetName(value);
     343             : 
     344           0 :         case GAAT_DATASET_LIST:
     345             :         {
     346           0 :             std::vector<GDALArgDatasetValue> v;
     347           0 :             v.resize(1);
     348           0 :             v[0].Set(value);
     349           0 :             return Set(std::move(v));
     350             :         }
     351             :     }
     352             : 
     353        4068 :     if (m_decl.GetType() != GAAT_STRING)
     354             :     {
     355          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     356             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     357             :                  "supported",
     358           8 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     359           8 :         return false;
     360             :     }
     361             : 
     362        4060 :     std::string newValue(value);
     363        4060 :     return ProcessString(newValue) && SetInternal(newValue);
     364             : }
     365             : 
     366         872 : bool GDALAlgorithmArg::Set(int value)
     367             : {
     368         872 :     if (m_decl.GetType() == GAAT_BOOLEAN)
     369             :     {
     370           3 :         if (value == 1)
     371           1 :             return Set(true);
     372           2 :         else if (value == 0)
     373           1 :             return Set(false);
     374             :     }
     375         869 :     else if (m_decl.GetType() == GAAT_REAL)
     376             :     {
     377           3 :         return Set(static_cast<double>(value));
     378             :     }
     379         866 :     else if (m_decl.GetType() == GAAT_STRING)
     380             :     {
     381           2 :         return Set(std::to_string(value));
     382             :     }
     383         864 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST)
     384             :     {
     385           1 :         return Set(std::vector<int>{value});
     386             :     }
     387         863 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     388             :     {
     389           1 :         return Set(std::vector<double>{static_cast<double>(value)});
     390             :     }
     391         862 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     392             :     {
     393           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     394             :     }
     395             : 
     396         862 :     if (m_decl.GetType() != GAAT_INTEGER)
     397             :     {
     398           2 :         CPLError(
     399             :             CE_Failure, CPLE_AppDefined,
     400             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     401           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     402           1 :         return false;
     403             :     }
     404         861 :     return SetInternal(value);
     405             : }
     406             : 
     407         291 : bool GDALAlgorithmArg::Set(double value)
     408             : {
     409         294 :     if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
     410         294 :         value <= INT_MAX && static_cast<int>(value) == value)
     411             :     {
     412           2 :         return Set(static_cast<int>(value));
     413             :     }
     414         289 :     else if (m_decl.GetType() == GAAT_STRING)
     415             :     {
     416           2 :         return Set(std::to_string(value));
     417             :     }
     418         289 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
     419         289 :              value <= INT_MAX && static_cast<int>(value) == value)
     420             :     {
     421           1 :         return Set(std::vector<int>{static_cast<int>(value)});
     422             :     }
     423         286 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     424             :     {
     425           0 :         return Set(std::vector<double>{value});
     426             :     }
     427         286 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     428             :     {
     429           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     430             :     }
     431         285 :     else if (m_decl.GetType() != GAAT_REAL)
     432             :     {
     433           6 :         CPLError(
     434             :             CE_Failure, CPLE_AppDefined,
     435             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     436           3 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     437           3 :         return false;
     438             :     }
     439         282 :     return SetInternal(value);
     440             : }
     441             : 
     442        6292 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
     443             : {
     444        6295 :     if (arg->IsOutput() && arg->GetDatasetInputFlags() == GADV_NAME &&
     445           3 :         arg->GetDatasetOutputFlags() == GADV_OBJECT)
     446             :     {
     447           3 :         CPLError(
     448             :             CE_Failure, CPLE_AppDefined,
     449             :             "Dataset object '%s' is created by algorithm and cannot be set "
     450             :             "as an input.",
     451           3 :             arg->GetName().c_str());
     452           3 :         return false;
     453             :     }
     454        6289 :     else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
     455             :     {
     456           8 :         CPLError(CE_Failure, CPLE_AppDefined,
     457             :                  "Dataset%s '%s' must be provided by name, not as object.",
     458           8 :                  arg->GetMaxCount() > 1 ? "s" : "", arg->GetName().c_str());
     459           4 :         return false;
     460             :     }
     461             : 
     462        6285 :     return true;
     463             : }
     464             : 
     465          33 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     466             : {
     467          58 :     if (m_decl.GetType() != GAAT_DATASET &&
     468          25 :         m_decl.GetType() != GAAT_DATASET_LIST)
     469             :     {
     470           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     471             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     472             :                  "is not supported",
     473           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     474           1 :         return false;
     475             :     }
     476          32 :     if (!CheckCanSetDatasetObject(this))
     477           2 :         return false;
     478          30 :     m_explicitlySet = true;
     479          30 :     if (m_decl.GetType() == GAAT_DATASET)
     480             :     {
     481           6 :         auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     482           6 :         val.Set(ds);
     483             :     }
     484             :     else
     485             :     {
     486          24 :         CPLAssert(m_decl.GetType() == GAAT_DATASET_LIST);
     487          24 :         auto &val = *std::get<std::vector<GDALArgDatasetValue> *>(m_value);
     488          24 :         val.resize(1);
     489          24 :         val[0].Set(ds);
     490             :     }
     491          30 :     return RunAllActions();
     492             : }
     493             : 
     494           3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     495             : {
     496           3 :     if (m_decl.GetType() != GAAT_DATASET)
     497             :     {
     498           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     499             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     500             :                  "is not supported",
     501           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     502           1 :         return false;
     503             :     }
     504           2 :     if (!CheckCanSetDatasetObject(this))
     505           1 :         return false;
     506           1 :     m_explicitlySet = true;
     507           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     508           1 :     val.Set(std::move(ds));
     509           1 :     return RunAllActions();
     510             : }
     511             : 
     512         564 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     513             : {
     514         564 :     if (m_decl.GetType() != GAAT_DATASET)
     515             :     {
     516           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     517             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     518             :                  "not supported",
     519           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     520           1 :         return false;
     521             :     }
     522         563 :     m_explicitlySet = true;
     523         563 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     524         563 :     return RunAllActions();
     525             : }
     526             : 
     527         986 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     528             : {
     529         986 :     if (m_decl.GetType() != GAAT_DATASET)
     530             :     {
     531           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     532             :                  "Calling SetFrom() on argument '%s' of type %s is "
     533             :                  "not supported",
     534           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     535           1 :         return false;
     536             :     }
     537         985 :     if (!CheckCanSetDatasetObject(this))
     538           1 :         return false;
     539         984 :     m_explicitlySet = true;
     540         984 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     541         984 :     return RunAllActions();
     542             : }
     543             : 
     544        1092 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     545             : {
     546        1092 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     547             :     {
     548           3 :         std::vector<int> v_i;
     549           4 :         for (const std::string &s : value)
     550             :         {
     551           3 :             errno = 0;
     552           3 :             char *endptr = nullptr;
     553           3 :             const auto v = std::strtoll(s.c_str(), &endptr, 10);
     554           5 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     555           2 :                 endptr == s.c_str() + s.size())
     556             :             {
     557           1 :                 v_i.push_back(static_cast<int>(v));
     558             :             }
     559             :             else
     560             :             {
     561           2 :                 break;
     562             :             }
     563             :         }
     564           3 :         if (v_i.size() == value.size())
     565           1 :             return Set(v_i);
     566             :     }
     567        1089 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     568             :     {
     569           2 :         std::vector<double> v_d;
     570           3 :         for (const std::string &s : value)
     571             :         {
     572           2 :             char *endptr = nullptr;
     573           2 :             const double v = CPLStrtod(s.c_str(), &endptr);
     574           2 :             if (endptr == s.c_str() + s.size())
     575             :             {
     576           1 :                 v_d.push_back(v);
     577             :             }
     578             :             else
     579             :             {
     580           1 :                 break;
     581             :             }
     582             :         }
     583           2 :         if (v_d.size() == value.size())
     584           1 :             return Set(v_d);
     585             :     }
     586        2172 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     587        2169 :               m_decl.GetType() == GAAT_REAL ||
     588        3258 :               m_decl.GetType() == GAAT_STRING) &&
     589           5 :              value.size() == 1)
     590             :     {
     591           4 :         return Set(value[0]);
     592             :     }
     593        1083 :     else if (m_decl.GetType() == GAAT_DATASET_LIST)
     594             :     {
     595          30 :         std::vector<GDALArgDatasetValue> dsVector;
     596          46 :         for (const std::string &s : value)
     597          31 :             dsVector.emplace_back(s);
     598          15 :         return Set(std::move(dsVector));
     599             :     }
     600             : 
     601        1071 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     602             :     {
     603          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     604             :                  "Calling Set(const std::vector<std::string> &) on argument "
     605             :                  "'%s' of type %s is not supported",
     606           5 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     607           5 :         return false;
     608             :     }
     609             : 
     610        2113 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
     611        1047 :         m_decl.IsRemoveSQLCommentsEnabled())
     612             :     {
     613          38 :         std::vector<std::string> newValue(value);
     614          41 :         for (auto &s : newValue)
     615             :         {
     616          22 :             if (!ProcessString(s))
     617           0 :                 return false;
     618             :         }
     619          19 :         return SetInternal(newValue);
     620             :     }
     621             :     else
     622             :     {
     623        1047 :         return SetInternal(value);
     624             :     }
     625             : }
     626             : 
     627         172 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     628             : {
     629         172 :     if (m_decl.GetType() == GAAT_REAL_LIST)
     630             :     {
     631           2 :         std::vector<double> v_d;
     632           2 :         for (int i : value)
     633           1 :             v_d.push_back(i);
     634           1 :         return Set(v_d);
     635             :     }
     636         171 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     637             :     {
     638           2 :         std::vector<std::string> v_s;
     639           3 :         for (int i : value)
     640           2 :             v_s.push_back(std::to_string(i));
     641           1 :         return Set(v_s);
     642             :     }
     643         338 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     644         334 :               m_decl.GetType() == GAAT_REAL ||
     645         506 :               m_decl.GetType() == GAAT_STRING) &&
     646           5 :              value.size() == 1)
     647             :     {
     648           3 :         return Set(value[0]);
     649             :     }
     650             : 
     651         167 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     652             :     {
     653           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     654             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     655             :                  "type %s is not supported",
     656           3 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     657           3 :         return false;
     658             :     }
     659         164 :     return SetInternal(value);
     660             : }
     661             : 
     662         268 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     663             : {
     664         268 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     665             :     {
     666           2 :         std::vector<int> v_i;
     667           3 :         for (double d : value)
     668             :         {
     669           2 :             if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
     670             :             {
     671           1 :                 v_i.push_back(static_cast<int>(d));
     672             :             }
     673             :             else
     674             :             {
     675             :                 break;
     676             :             }
     677             :         }
     678           2 :         if (v_i.size() == value.size())
     679           1 :             return Set(v_i);
     680             :     }
     681         266 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     682             :     {
     683           2 :         std::vector<std::string> v_s;
     684           3 :         for (double d : value)
     685           2 :             v_s.push_back(std::to_string(d));
     686           1 :         return Set(v_s);
     687             :     }
     688         529 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     689         527 :               m_decl.GetType() == GAAT_REAL ||
     690         793 :               m_decl.GetType() == GAAT_STRING) &&
     691           3 :              value.size() == 1)
     692             :     {
     693           3 :         return Set(value[0]);
     694             :     }
     695             : 
     696         263 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     697             :     {
     698           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     699             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     700             :                  "type %s is not supported",
     701           2 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     702           2 :         return false;
     703             :     }
     704         261 :     return SetInternal(value);
     705             : }
     706             : 
     707        3437 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     708             : {
     709        3437 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     710             :     {
     711           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     712             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     713             :                  "argument '%s' of type %s is not supported",
     714           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     715           1 :         return false;
     716             :     }
     717        3436 :     m_explicitlySet = true;
     718        3436 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     719        3436 :     return RunAllActions();
     720             : }
     721             : 
     722             : GDALAlgorithmArg &
     723           0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
     724             : {
     725           0 :     Set(std::move(value));
     726           0 :     return *this;
     727             : }
     728             : 
     729           1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
     730             : {
     731           1 :     const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
     732           1 :     return Set(value.exportToWkt(apszOptions));
     733             : }
     734             : 
     735        4254 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     736             : {
     737        4254 :     if (m_decl.GetType() != other.GetType())
     738             :     {
     739           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     740             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     741             :                  "other argument type is %s is not supported",
     742           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     743             :                  GDALAlgorithmArgTypeName(other.GetType()));
     744           1 :         return false;
     745             :     }
     746             : 
     747        4253 :     switch (m_decl.GetType())
     748             :     {
     749          91 :         case GAAT_BOOLEAN:
     750          91 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     751          91 :             break;
     752         807 :         case GAAT_STRING:
     753        1614 :             *std::get<std::string *>(m_value) =
     754         807 :                 *std::get<std::string *>(other.m_value);
     755         807 :             break;
     756           6 :         case GAAT_INTEGER:
     757           6 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     758           6 :             break;
     759           1 :         case GAAT_REAL:
     760           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     761           1 :             break;
     762         980 :         case GAAT_DATASET:
     763         980 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     764          58 :         case GAAT_STRING_LIST:
     765         116 :             *std::get<std::vector<std::string> *>(m_value) =
     766          58 :                 *std::get<std::vector<std::string> *>(other.m_value);
     767          58 :             break;
     768           1 :         case GAAT_INTEGER_LIST:
     769           2 :             *std::get<std::vector<int> *>(m_value) =
     770           1 :                 *std::get<std::vector<int> *>(other.m_value);
     771           1 :             break;
     772           1 :         case GAAT_REAL_LIST:
     773           2 :             *std::get<std::vector<double> *>(m_value) =
     774           1 :                 *std::get<std::vector<double> *>(other.m_value);
     775           1 :             break;
     776        2308 :         case GAAT_DATASET_LIST:
     777             :         {
     778        2308 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     779        2313 :             for (const auto &val :
     780        6934 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     781             :             {
     782        4626 :                 GDALArgDatasetValue v;
     783        2313 :                 v.SetFrom(val);
     784        2313 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     785        2313 :                     ->push_back(std::move(v));
     786             :             }
     787        2308 :             break;
     788             :         }
     789             :     }
     790        3273 :     m_explicitlySet = true;
     791        3273 :     return RunAllActions();
     792             : }
     793             : 
     794             : /************************************************************************/
     795             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     796             : /************************************************************************/
     797             : 
     798       16197 : bool GDALAlgorithmArg::RunAllActions()
     799             : {
     800       16197 :     if (!RunValidationActions())
     801         147 :         return false;
     802       16050 :     RunActions();
     803       16050 :     return true;
     804             : }
     805             : 
     806             : /************************************************************************/
     807             : /*                    GDALAlgorithmArg::RunActions()                    */
     808             : /************************************************************************/
     809             : 
     810       16051 : void GDALAlgorithmArg::RunActions()
     811             : {
     812       16347 :     for (const auto &f : m_actions)
     813         296 :         f();
     814       16051 : }
     815             : 
     816             : /************************************************************************/
     817             : /*                  GDALAlgorithmArg::ValidateChoice()                  */
     818             : /************************************************************************/
     819             : 
     820             : // Returns the canonical value if matching a valid choice, or empty string
     821             : // otherwise.
     822        2670 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
     823             : {
     824       15464 :     for (const std::string &choice : GetChoices())
     825             :     {
     826       15346 :         if (EQUAL(value.c_str(), choice.c_str()))
     827             :         {
     828        2552 :             return choice;
     829             :         }
     830             :     }
     831             : 
     832         190 :     for (const std::string &choice : GetHiddenChoices())
     833             :     {
     834         172 :         if (EQUAL(value.c_str(), choice.c_str()))
     835             :         {
     836         100 :             return choice;
     837             :         }
     838             :     }
     839             : 
     840          36 :     std::string expected;
     841         220 :     for (const auto &choice : GetChoices())
     842             :     {
     843         202 :         if (!expected.empty())
     844         184 :             expected += ", ";
     845         202 :         expected += '\'';
     846         202 :         expected += choice;
     847         202 :         expected += '\'';
     848             :     }
     849          18 :     if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
     850             :     {
     851           6 :         return "?";
     852             :     }
     853          24 :     CPLError(CE_Failure, CPLE_IllegalArg,
     854             :              "Invalid value '%s' for string argument '%s'. Should be "
     855             :              "one among %s.",
     856          12 :              value.c_str(), GetName().c_str(), expected.c_str());
     857          12 :     return std::string();
     858             : }
     859             : 
     860             : /************************************************************************/
     861             : /*                 GDALAlgorithmArg::ValidateIntRange()                 */
     862             : /************************************************************************/
     863             : 
     864        2728 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
     865             : {
     866        2728 :     bool ret = true;
     867             : 
     868        2728 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     869        2728 :     if (!std::isnan(minVal))
     870             :     {
     871        2078 :         if (minValIsIncluded && val < minVal)
     872             :         {
     873           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
     874             :                      "Value of argument '%s' is %d, but should be >= %d",
     875           3 :                      GetName().c_str(), val, static_cast<int>(minVal));
     876           3 :             ret = false;
     877             :         }
     878        2075 :         else if (!minValIsIncluded && val <= minVal)
     879             :         {
     880           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     881             :                      "Value of argument '%s' is %d, but should be > %d",
     882           1 :                      GetName().c_str(), val, static_cast<int>(minVal));
     883           1 :             ret = false;
     884             :         }
     885             :     }
     886             : 
     887        2728 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     888        2728 :     if (!std::isnan(maxVal))
     889             :     {
     890             : 
     891         430 :         if (maxValIsIncluded && val > maxVal)
     892             :         {
     893           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     894             :                      "Value of argument '%s' is %d, but should be <= %d",
     895           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     896           1 :             ret = false;
     897             :         }
     898         429 :         else if (!maxValIsIncluded && val >= maxVal)
     899             :         {
     900           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     901             :                      "Value of argument '%s' is %d, but should be < %d",
     902           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     903           1 :             ret = false;
     904             :         }
     905             :     }
     906             : 
     907        2728 :     return ret;
     908             : }
     909             : 
     910             : /************************************************************************/
     911             : /*                GDALAlgorithmArg::ValidateRealRange()                 */
     912             : /************************************************************************/
     913             : 
     914        2138 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
     915             : {
     916        2138 :     bool ret = true;
     917             : 
     918        2138 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     919        2138 :     if (!std::isnan(minVal))
     920             :     {
     921         216 :         if (minValIsIncluded && !(val >= minVal))
     922             :         {
     923          11 :             CPLError(CE_Failure, CPLE_IllegalArg,
     924             :                      "Value of argument '%s' is %g, but should be >= %g",
     925          11 :                      GetName().c_str(), val, minVal);
     926          11 :             ret = false;
     927             :         }
     928         205 :         else if (!minValIsIncluded && !(val > minVal))
     929             :         {
     930           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
     931             :                      "Value of argument '%s' is %g, but should be > %g",
     932           4 :                      GetName().c_str(), val, minVal);
     933           4 :             ret = false;
     934             :         }
     935             :     }
     936             : 
     937        2138 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     938        2138 :     if (!std::isnan(maxVal))
     939             :     {
     940             : 
     941          58 :         if (maxValIsIncluded && !(val <= maxVal))
     942             :         {
     943           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     944             :                      "Value of argument '%s' is %g, but should be <= %g",
     945           2 :                      GetName().c_str(), val, maxVal);
     946           2 :             ret = false;
     947             :         }
     948          56 :         else if (!maxValIsIncluded && !(val < maxVal))
     949             :         {
     950           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     951             :                      "Value of argument '%s' is %g, but should be < %g",
     952           1 :                      GetName().c_str(), val, maxVal);
     953           1 :             ret = false;
     954             :         }
     955             :     }
     956             : 
     957        2138 :     return ret;
     958             : }
     959             : 
     960             : /************************************************************************/
     961             : /*                        CheckDuplicateValues()                        */
     962             : /************************************************************************/
     963             : 
     964             : template <class T>
     965          92 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
     966             :                                  const std::vector<T> &values)
     967             : {
     968         184 :     auto tmpValues = values;
     969          92 :     bool bHasDupValues = false;
     970             :     if constexpr (std::is_floating_point_v<T>)
     971             :     {
     972             :         // Avoid undefined behavior with NaN values
     973           4 :         std::sort(tmpValues.begin(), tmpValues.end(),
     974          21 :                   [](T a, T b)
     975             :                   {
     976          21 :                       if (std::isnan(a) && !std::isnan(b))
     977           3 :                           return true;
     978          18 :                       if (std::isnan(b))
     979          10 :                           return false;
     980           8 :                       return a < b;
     981             :                   });
     982             : 
     983             :         bHasDupValues =
     984           4 :             std::adjacent_find(tmpValues.begin(), tmpValues.end(),
     985           6 :                                [](T a, T b)
     986             :                                {
     987           6 :                                    if (std::isnan(a) && std::isnan(b))
     988           1 :                                        return true;
     989           5 :                                    return a == b;
     990           8 :                                }) != tmpValues.end();
     991             :     }
     992             :     else
     993             :     {
     994          88 :         std::sort(tmpValues.begin(), tmpValues.end());
     995          88 :         bHasDupValues = std::adjacent_find(tmpValues.begin(),
     996         176 :                                            tmpValues.end()) != tmpValues.end();
     997             :     }
     998          92 :     if (bHasDupValues)
     999             :     {
    1000          10 :         CPLError(CE_Failure, CPLE_AppDefined,
    1001             :                  "'%s' must be a list of unique values.",
    1002          10 :                  arg->GetName().c_str());
    1003          10 :         return false;
    1004             :     }
    1005          82 :     return true;
    1006             : }
    1007             : 
    1008             : /************************************************************************/
    1009             : /*               GDALAlgorithmArg::RunValidationActions()               */
    1010             : /************************************************************************/
    1011             : 
    1012       35760 : bool GDALAlgorithmArg::RunValidationActions()
    1013             : {
    1014       35760 :     bool ret = true;
    1015             : 
    1016       35760 :     if (GetType() == GAAT_STRING && !GetChoices().empty())
    1017             :     {
    1018        1751 :         auto &val = Get<std::string>();
    1019        3502 :         std::string validVal = ValidateChoice(val);
    1020        1751 :         if (validVal.empty())
    1021           7 :             ret = false;
    1022             :         else
    1023        1744 :             val = std::move(validVal);
    1024             :     }
    1025       34009 :     else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
    1026             :     {
    1027         659 :         auto &values = Get<std::vector<std::string>>();
    1028        1578 :         for (std::string &val : values)
    1029             :         {
    1030        1838 :             std::string validVal = ValidateChoice(val);
    1031         919 :             if (validVal.empty())
    1032           5 :                 ret = false;
    1033             :             else
    1034         914 :                 val = std::move(validVal);
    1035             :         }
    1036             :     }
    1037             : 
    1038             :     const auto CheckMinCharCount =
    1039         994 :         [this, &ret](const std::string &val, int nMinCharCount)
    1040             :     {
    1041         982 :         if (val.size() < static_cast<size_t>(nMinCharCount))
    1042             :         {
    1043          12 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1044             :                      "Value of argument '%s' is '%s', but should have at least "
    1045             :                      "%d character%s",
    1046           6 :                      GetName().c_str(), val.c_str(), nMinCharCount,
    1047             :                      nMinCharCount > 1 ? "s" : "");
    1048           6 :             ret = false;
    1049             :         }
    1050       36742 :     };
    1051             : 
    1052             :     const auto CheckMaxCharCount =
    1053       12877 :         [this, &ret](const std::string &val, int nMaxCharCount)
    1054             :     {
    1055       12875 :         if (val.size() > static_cast<size_t>(nMaxCharCount))
    1056             :         {
    1057           2 :             CPLError(
    1058             :                 CE_Failure, CPLE_IllegalArg,
    1059             :                 "Value of argument '%s' is '%s', but should have no more than "
    1060             :                 "%d character%s",
    1061           1 :                 GetName().c_str(), val.c_str(), nMaxCharCount,
    1062             :                 nMaxCharCount > 1 ? "s" : "");
    1063           1 :             ret = false;
    1064             :         }
    1065       48635 :     };
    1066             : 
    1067       35760 :     switch (GetType())
    1068             :     {
    1069        2758 :         case GAAT_BOOLEAN:
    1070        2758 :             break;
    1071             : 
    1072       10071 :         case GAAT_STRING:
    1073             :         {
    1074       10071 :             const auto &val = Get<std::string>();
    1075       10071 :             const int nMinCharCount = GetMinCharCount();
    1076       10071 :             if (nMinCharCount > 0)
    1077             :             {
    1078         900 :                 CheckMinCharCount(val, nMinCharCount);
    1079             :             }
    1080             : 
    1081       10071 :             const int nMaxCharCount = GetMaxCharCount();
    1082       10071 :             CheckMaxCharCount(val, nMaxCharCount);
    1083       10071 :             break;
    1084             :         }
    1085             : 
    1086        2335 :         case GAAT_STRING_LIST:
    1087             :         {
    1088        2335 :             const int nMinCharCount = GetMinCharCount();
    1089        2335 :             const int nMaxCharCount = GetMaxCharCount();
    1090        2335 :             const auto &values = Get<std::vector<std::string>>();
    1091        5139 :             for (const auto &val : values)
    1092             :             {
    1093        2804 :                 if (nMinCharCount > 0)
    1094          82 :                     CheckMinCharCount(val, nMinCharCount);
    1095        2804 :                 CheckMaxCharCount(val, nMaxCharCount);
    1096             :             }
    1097             : 
    1098        2409 :             if (!GetDuplicateValuesAllowed() &&
    1099          74 :                 !CheckDuplicateValues(this, values))
    1100           2 :                 ret = false;
    1101        2335 :             break;
    1102             :         }
    1103             : 
    1104        2030 :         case GAAT_INTEGER:
    1105             :         {
    1106        2030 :             ret = ValidateIntRange(Get<int>()) && ret;
    1107        2030 :             break;
    1108             :         }
    1109             : 
    1110         344 :         case GAAT_INTEGER_LIST:
    1111             :         {
    1112         344 :             const auto &values = Get<std::vector<int>>();
    1113        1042 :             for (int v : values)
    1114         698 :                 ret = ValidateIntRange(v) && ret;
    1115             : 
    1116         347 :             if (!GetDuplicateValuesAllowed() &&
    1117           3 :                 !CheckDuplicateValues(this, values))
    1118           1 :                 ret = false;
    1119         344 :             break;
    1120             :         }
    1121             : 
    1122         549 :         case GAAT_REAL:
    1123             :         {
    1124         549 :             ret = ValidateRealRange(Get<double>()) && ret;
    1125         549 :             break;
    1126             :         }
    1127             : 
    1128         576 :         case GAAT_REAL_LIST:
    1129             :         {
    1130         576 :             const auto &values = Get<std::vector<double>>();
    1131        2165 :             for (double v : values)
    1132        1589 :                 ret = ValidateRealRange(v) && ret;
    1133             : 
    1134         580 :             if (!GetDuplicateValuesAllowed() &&
    1135           4 :                 !CheckDuplicateValues(this, values))
    1136           2 :                 ret = false;
    1137         576 :             break;
    1138             :         }
    1139             : 
    1140        5807 :         case GAAT_DATASET:
    1141        5807 :             break;
    1142             : 
    1143       11290 :         case GAAT_DATASET_LIST:
    1144             :         {
    1145       11290 :             if (!GetDuplicateValuesAllowed())
    1146             :             {
    1147          11 :                 const auto &values = Get<std::vector<GDALArgDatasetValue>>();
    1148          22 :                 std::vector<std::string> aosValues;
    1149          34 :                 for (const auto &v : values)
    1150             :                 {
    1151          23 :                     const GDALDataset *poDS = v.GetDatasetRef();
    1152          23 :                     if (poDS)
    1153             :                     {
    1154          16 :                         auto poDriver = poDS->GetDriver();
    1155             :                         // The dataset name for a MEM driver is not relevant,
    1156             :                         // so use the pointer address
    1157          32 :                         if ((poDriver &&
    1158          24 :                              EQUAL(poDriver->GetDescription(), "MEM")) ||
    1159           8 :                             poDS->GetDescription()[0] == 0)
    1160             :                         {
    1161           8 :                             aosValues.push_back(CPLSPrintf("%p", poDS));
    1162             :                         }
    1163             :                         else
    1164             :                         {
    1165           8 :                             aosValues.push_back(poDS->GetDescription());
    1166             :                         }
    1167             :                     }
    1168             :                     else
    1169             :                     {
    1170           7 :                         aosValues.push_back(v.GetName());
    1171             :                     }
    1172             :                 }
    1173          11 :                 if (!CheckDuplicateValues(this, aosValues))
    1174           5 :                     ret = false;
    1175             :             }
    1176       11290 :             break;
    1177             :         }
    1178             :     }
    1179             : 
    1180       35760 :     if (GDALAlgorithmArgTypeIsList(GetType()))
    1181             :     {
    1182       14545 :         int valueCount = 0;
    1183       14545 :         if (GetType() == GAAT_STRING_LIST)
    1184             :         {
    1185        2335 :             valueCount =
    1186        2335 :                 static_cast<int>(Get<std::vector<std::string>>().size());
    1187             :         }
    1188       12210 :         else if (GetType() == GAAT_INTEGER_LIST)
    1189             :         {
    1190         344 :             valueCount = static_cast<int>(Get<std::vector<int>>().size());
    1191             :         }
    1192       11866 :         else if (GetType() == GAAT_REAL_LIST)
    1193             :         {
    1194         576 :             valueCount = static_cast<int>(Get<std::vector<double>>().size());
    1195             :         }
    1196       11290 :         else if (GetType() == GAAT_DATASET_LIST)
    1197             :         {
    1198       11290 :             valueCount = static_cast<int>(
    1199       11290 :                 Get<std::vector<GDALArgDatasetValue>>().size());
    1200             :         }
    1201             : 
    1202       14545 :         if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
    1203             :         {
    1204          14 :             ReportError(CE_Failure, CPLE_AppDefined,
    1205             :                         "%d value%s been specified for argument '%s', "
    1206             :                         "whereas exactly %d %s expected.",
    1207             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1208           7 :                         GetName().c_str(), GetMinCount(),
    1209           7 :                         GetMinCount() > 1 ? "were" : "was");
    1210           7 :             ret = false;
    1211             :         }
    1212       14538 :         else if (valueCount < GetMinCount())
    1213             :         {
    1214           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1215             :                         "Only %d value%s been specified for argument '%s', "
    1216             :                         "whereas at least %d %s expected.",
    1217             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1218           3 :                         GetName().c_str(), GetMinCount(),
    1219           3 :                         GetMinCount() > 1 ? "were" : "was");
    1220           3 :             ret = false;
    1221             :         }
    1222       14535 :         else if (valueCount > GetMaxCount())
    1223             :         {
    1224           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    1225             :                         "%d value%s been specified for argument '%s', "
    1226             :                         "whereas at most %d %s expected.",
    1227             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1228           1 :                         GetName().c_str(), GetMaxCount(),
    1229           1 :                         GetMaxCount() > 1 ? "were" : "was");
    1230           1 :             ret = false;
    1231             :         }
    1232             :     }
    1233             : 
    1234       35760 :     if (ret)
    1235             :     {
    1236       42626 :         for (const auto &f : m_validationActions)
    1237             :         {
    1238        6930 :             if (!f())
    1239          92 :                 ret = false;
    1240             :         }
    1241             :     }
    1242             : 
    1243       35760 :     return ret;
    1244             : }
    1245             : 
    1246             : /************************************************************************/
    1247             : /*                   GDALAlgorithmArg::ReportError()                    */
    1248             : /************************************************************************/
    1249             : 
    1250          11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1251             :                                    const char *fmt, ...) const
    1252             : {
    1253             :     va_list args;
    1254          11 :     va_start(args, fmt);
    1255          11 :     if (m_owner)
    1256             :     {
    1257          11 :         m_owner->ReportError(eErrClass, err_no, "%s",
    1258          22 :                              CPLString().vPrintf(fmt, args).c_str());
    1259             :     }
    1260             :     else
    1261             :     {
    1262           0 :         CPLError(eErrClass, err_no, "%s",
    1263           0 :                  CPLString().vPrintf(fmt, args).c_str());
    1264             :     }
    1265          11 :     va_end(args);
    1266          11 : }
    1267             : 
    1268             : /************************************************************************/
    1269             : /*                 GDALAlgorithmArg::GetEscapedString()                 */
    1270             : /************************************************************************/
    1271             : 
    1272             : /* static */
    1273         134 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
    1274             : {
    1275         150 :     if (s.find_first_of("\" \\,") != std::string::npos &&
    1276           8 :         !(s.size() > 4 &&
    1277           8 :           s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
    1278           2 :           s[1] == ' ' && s[s.size() - 2] == ' ' &&
    1279           2 :           s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
    1280             :     {
    1281          12 :         return std::string("\"")
    1282             :             .append(
    1283          12 :                 CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
    1284           6 :             .append("\"");
    1285             :     }
    1286             :     else
    1287             :     {
    1288         128 :         return s;
    1289             :     }
    1290             : }
    1291             : 
    1292             : /************************************************************************/
    1293             : /*                    GDALAlgorithmArg::Serialize()                     */
    1294             : /************************************************************************/
    1295             : 
    1296          39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
    1297             :                                  bool absolutePath) const
    1298             : {
    1299          39 :     serializedArg.clear();
    1300             : 
    1301          39 :     if (!IsExplicitlySet())
    1302             :     {
    1303           0 :         return false;
    1304             :     }
    1305             : 
    1306          78 :     std::string ret = "--";
    1307          39 :     ret += GetName();
    1308          39 :     if (GetType() == GAAT_BOOLEAN)
    1309             :     {
    1310           0 :         serializedArg = std::move(ret);
    1311           0 :         return true;
    1312             :     }
    1313             : 
    1314           5 :     const auto AddListValueSeparator = [this, &ret]()
    1315             :     {
    1316           1 :         if (GetPackedValuesAllowed())
    1317             :         {
    1318           0 :             ret += ',';
    1319             :         }
    1320             :         else
    1321             :         {
    1322           1 :             ret += " --";
    1323           1 :             ret += GetName();
    1324           1 :             ret += ' ';
    1325             :         }
    1326          40 :     };
    1327             : 
    1328           0 :     const auto MakeAbsolutePath = [](const std::string &filename)
    1329             :     {
    1330             :         VSIStatBufL sStat;
    1331           0 :         if (VSIStatL(filename.c_str(), &sStat) != 0 ||
    1332           0 :             !CPLIsFilenameRelative(filename.c_str()))
    1333           0 :             return filename;
    1334           0 :         char *pszCWD = CPLGetCurrentDir();
    1335           0 :         if (!pszCWD)
    1336           0 :             return filename;
    1337             :         const auto absPath =
    1338           0 :             CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
    1339           0 :         CPLFree(pszCWD);
    1340           0 :         return absPath;
    1341             :     };
    1342             : 
    1343          39 :     ret += ' ';
    1344          39 :     switch (GetType())
    1345             :     {
    1346           0 :         case GAAT_BOOLEAN:
    1347           0 :             break;
    1348           8 :         case GAAT_STRING:
    1349             :         {
    1350           8 :             const auto &val = Get<std::string>();
    1351           8 :             ret += GetEscapedString(val);
    1352           8 :             break;
    1353             :         }
    1354           0 :         case GAAT_INTEGER:
    1355             :         {
    1356           0 :             ret += CPLSPrintf("%d", Get<int>());
    1357           0 :             break;
    1358             :         }
    1359           0 :         case GAAT_REAL:
    1360             :         {
    1361           0 :             ret += CPLSPrintf("%.17g", Get<double>());
    1362           0 :             break;
    1363             :         }
    1364           2 :         case GAAT_DATASET:
    1365             :         {
    1366           2 :             const auto &val = Get<GDALArgDatasetValue>();
    1367           2 :             const auto &str = val.GetName();
    1368           2 :             if (str.empty())
    1369             :             {
    1370           0 :                 return false;
    1371             :             }
    1372           2 :             ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
    1373           2 :             break;
    1374             :         }
    1375           4 :         case GAAT_STRING_LIST:
    1376             :         {
    1377           4 :             const auto &vals = Get<std::vector<std::string>>();
    1378           8 :             for (size_t i = 0; i < vals.size(); ++i)
    1379             :             {
    1380           4 :                 if (i > 0)
    1381           1 :                     AddListValueSeparator();
    1382           4 :                 ret += GetEscapedString(vals[i]);
    1383             :             }
    1384           4 :             break;
    1385             :         }
    1386           0 :         case GAAT_INTEGER_LIST:
    1387             :         {
    1388           0 :             const auto &vals = Get<std::vector<int>>();
    1389           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1390             :             {
    1391           0 :                 if (i > 0)
    1392           0 :                     AddListValueSeparator();
    1393           0 :                 ret += CPLSPrintf("%d", vals[i]);
    1394             :             }
    1395           0 :             break;
    1396             :         }
    1397           0 :         case GAAT_REAL_LIST:
    1398             :         {
    1399           0 :             const auto &vals = Get<std::vector<double>>();
    1400           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1401             :             {
    1402           0 :                 if (i > 0)
    1403           0 :                     AddListValueSeparator();
    1404           0 :                 ret += CPLSPrintf("%.17g", vals[i]);
    1405             :             }
    1406           0 :             break;
    1407             :         }
    1408          25 :         case GAAT_DATASET_LIST:
    1409             :         {
    1410          25 :             const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
    1411          49 :             for (size_t i = 0; i < vals.size(); ++i)
    1412             :             {
    1413          25 :                 if (i > 0)
    1414           0 :                     AddListValueSeparator();
    1415          25 :                 const auto &val = vals[i];
    1416          25 :                 const auto &str = val.GetName();
    1417          25 :                 if (str.empty())
    1418             :                 {
    1419           1 :                     return false;
    1420             :                 }
    1421          48 :                 ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
    1422          24 :                                                      : str);
    1423             :             }
    1424          24 :             break;
    1425             :         }
    1426             :     }
    1427             : 
    1428          38 :     serializedArg = std::move(ret);
    1429          38 :     return true;
    1430             : }
    1431             : 
    1432             : /************************************************************************/
    1433             : /*                  ~GDALInConstructionAlgorithmArg()                   */
    1434             : /************************************************************************/
    1435             : 
    1436             : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
    1437             : 
    1438             : /************************************************************************/
    1439             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
    1440             : /************************************************************************/
    1441             : 
    1442             : GDALInConstructionAlgorithmArg &
    1443       63155 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
    1444             : {
    1445       63155 :     m_decl.AddAlias(alias);
    1446       63155 :     if (m_owner)
    1447       63155 :         m_owner->AddAliasFor(this, alias);
    1448       63155 :     return *this;
    1449             : }
    1450             : 
    1451             : /************************************************************************/
    1452             : /*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
    1453             : /************************************************************************/
    1454             : 
    1455             : GDALInConstructionAlgorithmArg &
    1456       17095 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
    1457             : {
    1458       17095 :     m_decl.AddHiddenAlias(alias);
    1459       17095 :     if (m_owner)
    1460       17095 :         m_owner->AddAliasFor(this, alias);
    1461       17095 :     return *this;
    1462             : }
    1463             : 
    1464             : /************************************************************************/
    1465             : /*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
    1466             : /************************************************************************/
    1467             : 
    1468             : GDALInConstructionAlgorithmArg &
    1469          48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
    1470             : {
    1471          48 :     m_decl.AddShortNameAlias(shortNameAlias);
    1472          48 :     if (m_owner)
    1473          48 :         m_owner->AddShortNameAliasFor(this, shortNameAlias);
    1474          48 :     return *this;
    1475             : }
    1476             : 
    1477             : /************************************************************************/
    1478             : /*           GDALInConstructionAlgorithmArg::SetPositional()            */
    1479             : /************************************************************************/
    1480             : 
    1481       21552 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
    1482             : {
    1483       21552 :     m_decl.SetPositional();
    1484       21552 :     if (m_owner)
    1485       21552 :         m_owner->SetPositional(this);
    1486       21552 :     return *this;
    1487             : }
    1488             : 
    1489             : /************************************************************************/
    1490             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
    1491             : /************************************************************************/
    1492             : 
    1493        1319 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
    1494        2638 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
    1495        1319 :       m_nameSet(true)
    1496             : {
    1497        1319 :     if (m_poDS)
    1498        1319 :         m_poDS->Reference();
    1499        1319 : }
    1500             : 
    1501             : /************************************************************************/
    1502             : /*                      GDALArgDatasetValue::Set()                      */
    1503             : /************************************************************************/
    1504             : 
    1505        2283 : void GDALArgDatasetValue::Set(const std::string &name)
    1506             : {
    1507        2283 :     Close();
    1508        2283 :     m_name = name;
    1509        2283 :     m_nameSet = true;
    1510        2283 :     if (m_ownerArg)
    1511        2278 :         m_ownerArg->NotifyValueSet();
    1512        2283 : }
    1513             : 
    1514             : /************************************************************************/
    1515             : /*                      GDALArgDatasetValue::Set()                      */
    1516             : /************************************************************************/
    1517             : 
    1518        1986 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
    1519             : {
    1520        1986 :     Close();
    1521        1986 :     m_poDS = poDS.release();
    1522        1986 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1523        1986 :     m_nameSet = true;
    1524        1986 :     if (m_ownerArg)
    1525        1846 :         m_ownerArg->NotifyValueSet();
    1526        1986 : }
    1527             : 
    1528             : /************************************************************************/
    1529             : /*                      GDALArgDatasetValue::Set()                      */
    1530             : /************************************************************************/
    1531             : 
    1532        7945 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
    1533             : {
    1534        7945 :     Close();
    1535        7945 :     m_poDS = poDS;
    1536        7945 :     if (m_poDS)
    1537        7076 :         m_poDS->Reference();
    1538        7945 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1539        7945 :     m_nameSet = true;
    1540        7945 :     if (m_ownerArg)
    1541        3339 :         m_ownerArg->NotifyValueSet();
    1542        7945 : }
    1543             : 
    1544             : /************************************************************************/
    1545             : /*                    GDALArgDatasetValue::SetFrom()                    */
    1546             : /************************************************************************/
    1547             : 
    1548        3297 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
    1549             : {
    1550        3297 :     Close();
    1551        3297 :     m_name = other.m_name;
    1552        3297 :     m_nameSet = other.m_nameSet;
    1553        3297 :     m_poDS = other.m_poDS;
    1554        3297 :     if (m_poDS)
    1555        2311 :         m_poDS->Reference();
    1556        3297 : }
    1557             : 
    1558             : /************************************************************************/
    1559             : /*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
    1560             : /************************************************************************/
    1561             : 
    1562       31342 : GDALArgDatasetValue::~GDALArgDatasetValue()
    1563             : {
    1564       31342 :     Close();
    1565       31342 : }
    1566             : 
    1567             : /************************************************************************/
    1568             : /*                     GDALArgDatasetValue::Close()                     */
    1569             : /************************************************************************/
    1570             : 
    1571       51979 : bool GDALArgDatasetValue::Close()
    1572             : {
    1573       51979 :     bool ret = true;
    1574       51979 :     if (m_poDS && m_poDS->Dereference() == 0)
    1575             :     {
    1576        3269 :         ret = m_poDS->Close() == CE_None;
    1577        3269 :         delete m_poDS;
    1578             :     }
    1579       51979 :     m_poDS = nullptr;
    1580       51979 :     return ret;
    1581             : }
    1582             : 
    1583             : /************************************************************************/
    1584             : /*                   GDALArgDatasetValue::operator=()                   */
    1585             : /************************************************************************/
    1586             : 
    1587           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
    1588             : {
    1589           2 :     Close();
    1590           2 :     m_poDS = other.m_poDS;
    1591           2 :     m_name = other.m_name;
    1592           2 :     m_nameSet = other.m_nameSet;
    1593           2 :     other.m_poDS = nullptr;
    1594           2 :     other.m_name.clear();
    1595           2 :     other.m_nameSet = false;
    1596           2 :     return *this;
    1597             : }
    1598             : 
    1599             : /************************************************************************/
    1600             : /*                  GDALArgDatasetValue::GetDataset()                   */
    1601             : /************************************************************************/
    1602             : 
    1603        1051 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
    1604             : {
    1605        1051 :     if (m_poDS)
    1606        1051 :         m_poDS->Reference();
    1607        1051 :     return m_poDS;
    1608             : }
    1609             : 
    1610             : /************************************************************************/
    1611             : /*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
    1612             : /************************************************************************/
    1613             : 
    1614        3116 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
    1615        3116 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
    1616             : {
    1617        3116 :     other.m_poDS = nullptr;
    1618        3116 :     other.m_name.clear();
    1619        3116 : }
    1620             : 
    1621             : /************************************************************************/
    1622             : /*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
    1623             : /************************************************************************/
    1624             : 
    1625        3292 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
    1626             :     bool noneAllowed, const std::vector<std::string> &specialValues)
    1627             : {
    1628        3292 :     if (GetType() != GAAT_STRING)
    1629             :     {
    1630           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1631             :                  "SetIsCRSArg() can only be called on a String argument");
    1632           1 :         return *this;
    1633             :     }
    1634             :     AddValidationAction(
    1635         711 :         [this, noneAllowed, specialValues]()
    1636             :         {
    1637             :             const std::string &osVal =
    1638             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
    1639         352 :                     ->Get<std::string>();
    1640         352 :             if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
    1641           0 :                 return true;
    1642             : 
    1643         691 :             if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
    1644         339 :                 std::find(specialValues.begin(), specialValues.end(), osVal) ==
    1645         691 :                     specialValues.end())
    1646             :             {
    1647         331 :                 OGRSpatialReference oSRS;
    1648         331 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
    1649             :                 {
    1650           7 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
    1651             :                                          "Invalid value for '%s' argument",
    1652           7 :                                          GetName().c_str());
    1653           7 :                     return false;
    1654             :                 }
    1655             :             }
    1656         345 :             return true;
    1657        3291 :         });
    1658             : 
    1659             :     SetAutoCompleteFunction(
    1660          40 :         [this, noneAllowed, specialValues](const std::string &currentValue)
    1661             :         {
    1662          10 :             bool bIsRaster = false;
    1663          10 :             OGREnvelope sDatasetLongLatEnv;
    1664          20 :             std::string osCelestialBodyName;
    1665          10 :             if (GetName() == GDAL_ARG_NAME_OUTPUT_CRS)
    1666             :             {
    1667          10 :                 auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
    1668          10 :                 if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    1669             :                 {
    1670             :                     auto &val =
    1671          10 :                         inputArg->Get<std::vector<GDALArgDatasetValue>>();
    1672          10 :                     if (val.size() == 1)
    1673             :                     {
    1674           4 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1675             :                         auto poDS = std::unique_ptr<GDALDataset>(
    1676           4 :                             GDALDataset::Open(val[0].GetName().c_str()));
    1677           2 :                         if (poDS)
    1678             :                         {
    1679           2 :                             bIsRaster = poDS->GetRasterCount() != 0;
    1680           2 :                             if (auto poCRS = poDS->GetSpatialRef())
    1681             :                             {
    1682             :                                 const char *pszCelestialBodyName =
    1683           2 :                                     poCRS->GetCelestialBodyName();
    1684           2 :                                 if (pszCelestialBodyName)
    1685           2 :                                     osCelestialBodyName = pszCelestialBodyName;
    1686             : 
    1687           2 :                                 if (!pszCelestialBodyName ||
    1688           2 :                                     !EQUAL(pszCelestialBodyName, "Earth"))
    1689             :                                 {
    1690           0 :                                     OGRSpatialReference oLongLat;
    1691           0 :                                     oLongLat.CopyGeogCSFrom(poCRS);
    1692           0 :                                     oLongLat.SetAxisMappingStrategy(
    1693             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    1694           0 :                                     poDS->GetExtent(&sDatasetLongLatEnv,
    1695           0 :                                                     &oLongLat);
    1696             :                                 }
    1697             :                                 else
    1698             :                                 {
    1699           2 :                                     poDS->GetExtentWGS84LongLat(
    1700           2 :                                         &sDatasetLongLatEnv);
    1701             :                                 }
    1702             :                             }
    1703             :                         }
    1704             :                     }
    1705             :                 }
    1706             :             }
    1707             : 
    1708             :             const auto IsCRSCompatible =
    1709       42959 :                 [bIsRaster, &sDatasetLongLatEnv,
    1710       73695 :                  &osCelestialBodyName](const OSRCRSInfo *crsInfo)
    1711             :             {
    1712       42959 :                 if (!sDatasetLongLatEnv.IsInit())
    1713       30685 :                     return true;
    1714       24108 :                 return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
    1715       11834 :                        !(bIsRaster &&
    1716        5917 :                          crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
    1717       11652 :                        crsInfo->dfWestLongitudeDeg <
    1718       11652 :                            crsInfo->dfEastLongitudeDeg &&
    1719       11517 :                        sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
    1720        5618 :                        sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
    1721         615 :                        sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
    1722       24437 :                        sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
    1723         329 :                        ((!osCelestialBodyName.empty() &&
    1724         658 :                          crsInfo->pszCelestialBodyName &&
    1725         329 :                          osCelestialBodyName ==
    1726         329 :                              crsInfo->pszCelestialBodyName) ||
    1727           0 :                         (osCelestialBodyName.empty() &&
    1728       12274 :                          !crsInfo->pszCelestialBodyName));
    1729          10 :             };
    1730             : 
    1731          10 :             std::vector<std::string> oRet;
    1732          10 :             if (noneAllowed)
    1733           0 :                 oRet.push_back("none");
    1734          10 :             oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
    1735          10 :             if (!currentValue.empty())
    1736             :             {
    1737             :                 const CPLStringList aosTokens(
    1738          14 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
    1739           7 :                 int nCount = 0;
    1740             :                 std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
    1741             :                     pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
    1742             :                                                            nullptr, &nCount),
    1743          14 :                              OSRDestroyCRSInfoList);
    1744          14 :                 std::string osCode;
    1745             : 
    1746          14 :                 std::vector<const OSRCRSInfo *> candidates;
    1747       46270 :                 for (int i = 0; i < nCount; ++i)
    1748             :                 {
    1749       46263 :                     const auto *entry = (pCRSList.get())[i];
    1750       46263 :                     if (!entry->bDeprecated && IsCRSCompatible(entry))
    1751             :                     {
    1752       49425 :                         if (aosTokens.size() == 1 ||
    1753       18411 :                             STARTS_WITH(entry->pszCode, aosTokens[1]))
    1754             :                         {
    1755       12666 :                             if (candidates.empty())
    1756           7 :                                 osCode = entry->pszCode;
    1757       12666 :                             candidates.push_back(entry);
    1758             :                         }
    1759             :                     }
    1760             :                 }
    1761           7 :                 if (candidates.size() == 1)
    1762             :                 {
    1763           1 :                     oRet.push_back(std::move(osCode));
    1764             :                 }
    1765             :                 else
    1766             :                 {
    1767           6 :                     if (sDatasetLongLatEnv.IsInit())
    1768             :                     {
    1769           2 :                         std::sort(
    1770             :                             candidates.begin(), candidates.end(),
    1771        2999 :                             [](const OSRCRSInfo *a, const OSRCRSInfo *b)
    1772             :                             {
    1773        2999 :                                 const double dfXa =
    1774        2999 :                                     a->dfWestLongitudeDeg >
    1775        2999 :                                             a->dfEastLongitudeDeg
    1776        2999 :                                         ? a->dfWestLongitudeDeg -
    1777           0 :                                               a->dfEastLongitudeDeg
    1778        2999 :                                         : (180 - a->dfWestLongitudeDeg) +
    1779        2999 :                                               (a->dfEastLongitudeDeg - -180);
    1780        2999 :                                 const double dfYa = a->dfNorthLatitudeDeg -
    1781        2999 :                                                     a->dfSouthLatitudeDeg;
    1782        2999 :                                 const double dfXb =
    1783        2999 :                                     b->dfWestLongitudeDeg >
    1784        2999 :                                             b->dfEastLongitudeDeg
    1785        2999 :                                         ? b->dfWestLongitudeDeg -
    1786           0 :                                               b->dfEastLongitudeDeg
    1787        2999 :                                         : (180 - b->dfWestLongitudeDeg) +
    1788        2999 :                                               (b->dfEastLongitudeDeg - -180);
    1789        2999 :                                 const double dfYb = b->dfNorthLatitudeDeg -
    1790        2999 :                                                     b->dfSouthLatitudeDeg;
    1791        2999 :                                 const double diffArea =
    1792        2999 :                                     dfXa * dfYa - dfXb * dfYb;
    1793        2999 :                                 if (diffArea < 0)
    1794         279 :                                     return true;
    1795        2720 :                                 if (diffArea == 0)
    1796             :                                 {
    1797        2506 :                                     if (std::string_view(a->pszName) ==
    1798        2506 :                                         b->pszName)
    1799             :                                     {
    1800          57 :                                         if (a->eType ==
    1801          13 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D &&
    1802          13 :                                             b->eType !=
    1803             :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1804          13 :                                             return true;
    1805          44 :                                         if (a->eType ==
    1806          32 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_3D &&
    1807          32 :                                             b->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1808           9 :                                             return true;
    1809          35 :                                         return false;
    1810             :                                     }
    1811        4898 :                                     return std::string_view(a->pszCode) <
    1812        4898 :                                            b->pszCode;
    1813             :                                 }
    1814         214 :                                 return false;
    1815             :                             });
    1816             :                     }
    1817             : 
    1818       12671 :                     for (const auto *entry : candidates)
    1819             :                     {
    1820       25330 :                         std::string val = std::string(entry->pszCode)
    1821       12665 :                                               .append(" -- ")
    1822       25330 :                                               .append(entry->pszName);
    1823       12665 :                         if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1824        1294 :                             val.append(" (geographic 2D)");
    1825       11371 :                         else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
    1826         446 :                             val.append(" (geographic 3D)");
    1827       10925 :                         else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1828         397 :                             val.append(" (geocentric)");
    1829       12665 :                         oRet.push_back(std::move(val));
    1830             :                     }
    1831             :                 }
    1832             :             }
    1833          10 :             if (currentValue.empty() || oRet.empty())
    1834             :             {
    1835             :                 const CPLStringList aosAuthorities(
    1836           6 :                     OSRGetAuthorityListFromDatabase());
    1837          18 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
    1838             :                 {
    1839          15 :                     int nCount = 0;
    1840          15 :                     OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
    1841             :                         pszAuth, nullptr, &nCount));
    1842          15 :                     if (nCount)
    1843          12 :                         oRet.push_back(std::string(pszAuth).append(":"));
    1844             :                 }
    1845             :             }
    1846          20 :             return oRet;
    1847        3291 :         });
    1848             : 
    1849        3291 :     return *this;
    1850             : }
    1851             : 
    1852             : /************************************************************************/
    1853             : /*                    GDALAlgorithm::GDALAlgorithm()                    */
    1854             : /************************************************************************/
    1855             : 
    1856       21621 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
    1857             :                              const std::string &description,
    1858       21621 :                              const std::string &helpURL)
    1859             :     : m_name(name), m_description(description), m_helpURL(helpURL),
    1860       43082 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
    1861       21621 :                         ? "https://gdal.org" + m_helpURL
    1862       64448 :                         : m_helpURL)
    1863             : {
    1864             :     auto &helpArg =
    1865             :         AddArg("help", 'h', _("Display help message and exit"),
    1866       43242 :                &m_helpRequested)
    1867       21621 :             .SetHiddenForAPI()
    1868       43242 :             .SetCategory(GAAC_COMMON)
    1869          14 :             .AddAction([this]()
    1870       21621 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1871             :     auto &helpDocArg =
    1872             :         AddArg("help-doc", 0,
    1873             :                _("Display help message for use by documentation"),
    1874       43242 :                &m_helpDocRequested)
    1875       21621 :             .SetHidden()
    1876          12 :             .AddAction([this]()
    1877       21621 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1878             :     auto &jsonUsageArg =
    1879             :         AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
    1880       43242 :                &m_JSONUsageRequested)
    1881       21621 :             .SetHiddenForAPI()
    1882       43242 :             .SetCategory(GAAC_COMMON)
    1883           4 :             .AddAction([this]()
    1884       21621 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1885       43242 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
    1886       43242 :         .SetMetaVar("<KEY>=<VALUE>")
    1887       21621 :         .SetHiddenForAPI()
    1888       43242 :         .SetCategory(GAAC_COMMON)
    1889             :         .AddAction(
    1890           2 :             [this]()
    1891             :             {
    1892           2 :                 ReportError(
    1893             :                     CE_Warning, CPLE_AppDefined,
    1894             :                     "Configuration options passed with the 'config' argument "
    1895             :                     "are ignored");
    1896       21621 :             });
    1897             : 
    1898       21621 :     AddValidationAction(
    1899       13155 :         [this, &helpArg, &helpDocArg, &jsonUsageArg]()
    1900             :         {
    1901        6774 :             if (!m_calledFromCommandLine && m_specialActionRequested)
    1902             :             {
    1903           0 :                 for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
    1904             :                 {
    1905           0 :                     if (arg->IsExplicitlySet())
    1906             :                     {
    1907           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    1908             :                                     "'%s' argument only available when called "
    1909             :                                     "from command line",
    1910           0 :                                     arg->GetName().c_str());
    1911           0 :                         return false;
    1912             :                     }
    1913             :                 }
    1914             :             }
    1915        6774 :             return true;
    1916             :         });
    1917       21621 : }
    1918             : 
    1919             : /************************************************************************/
    1920             : /*                   GDALAlgorithm::~GDALAlgorithm()                    */
    1921             : /************************************************************************/
    1922             : 
    1923             : GDALAlgorithm::~GDALAlgorithm() = default;
    1924             : 
    1925             : /************************************************************************/
    1926             : /*                    GDALAlgorithm::ParseArgument()                    */
    1927             : /************************************************************************/
    1928             : 
    1929        3114 : bool GDALAlgorithm::ParseArgument(
    1930             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
    1931             :     std::map<
    1932             :         GDALAlgorithmArg *,
    1933             :         std::variant<std::vector<std::string>, std::vector<int>,
    1934             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1935             :         &inConstructionValues)
    1936             : {
    1937             :     const bool isListArg =
    1938        3114 :         GDALAlgorithmArgTypeIsList(arg->GetType()) && arg->GetMaxCount() > 1;
    1939        3114 :     if (arg->IsExplicitlySet() && !isListArg)
    1940             :     {
    1941             :         // Hack for "gdal info" to be able to pass an opened raster dataset
    1942             :         // by "gdal raster info" to the "gdal vector info" algorithm.
    1943           4 :         if (arg->SkipIfAlreadySet())
    1944             :         {
    1945           1 :             arg->SetSkipIfAlreadySet(false);
    1946           1 :             return true;
    1947             :         }
    1948             : 
    1949           3 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1950             :                     "Argument '%s' has already been specified.", name.c_str());
    1951           3 :         return false;
    1952             :     }
    1953             : 
    1954        3178 :     if (!arg->GetRepeatedArgAllowed() &&
    1955          68 :         cpl::contains(inConstructionValues, arg))
    1956             :     {
    1957           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1958             :                     "Argument '%s' has already been specified.", name.c_str());
    1959           1 :         return false;
    1960             :     }
    1961             : 
    1962        3109 :     switch (arg->GetType())
    1963             :     {
    1964         320 :         case GAAT_BOOLEAN:
    1965             :         {
    1966         320 :             if (value.empty() || value == "true")
    1967         318 :                 return arg->Set(true);
    1968           2 :             else if (value == "false")
    1969           1 :                 return arg->Set(false);
    1970             :             else
    1971             :             {
    1972           1 :                 ReportError(
    1973             :                     CE_Failure, CPLE_IllegalArg,
    1974             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
    1975             :                     "'true' or 'false'.",
    1976             :                     value.c_str(), name.c_str());
    1977           1 :                 return false;
    1978             :             }
    1979             :         }
    1980             : 
    1981         769 :         case GAAT_STRING:
    1982             :         {
    1983         769 :             return arg->Set(value);
    1984             :         }
    1985             : 
    1986         356 :         case GAAT_INTEGER:
    1987             :         {
    1988         356 :             errno = 0;
    1989         356 :             char *endptr = nullptr;
    1990         356 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
    1991         355 :             if (errno == 0 && endptr &&
    1992         711 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
    1993             :                 val <= INT_MAX)
    1994             :             {
    1995         353 :                 return arg->Set(static_cast<int>(val));
    1996             :             }
    1997             :             else
    1998             :             {
    1999           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    2000             :                             "Expected integer value for argument '%s', "
    2001             :                             "but got '%s'.",
    2002             :                             name.c_str(), value.c_str());
    2003           3 :                 return false;
    2004             :             }
    2005             :         }
    2006             : 
    2007          32 :         case GAAT_REAL:
    2008             :         {
    2009          32 :             char *endptr = nullptr;
    2010          32 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
    2011          32 :             if (endptr != value.c_str() + value.size())
    2012             :             {
    2013           1 :                 ReportError(
    2014             :                     CE_Failure, CPLE_IllegalArg,
    2015             :                     "Expected real value for argument '%s', but got '%s'.",
    2016             :                     name.c_str(), value.c_str());
    2017           1 :                 return false;
    2018             :             }
    2019          31 :             return arg->Set(dfValue);
    2020             :         }
    2021             : 
    2022         547 :         case GAAT_DATASET:
    2023             :         {
    2024         547 :             return arg->SetDatasetName(value);
    2025             :         }
    2026             : 
    2027         268 :         case GAAT_STRING_LIST:
    2028             :         {
    2029             :             const CPLStringList aosTokens(
    2030         268 :                 arg->GetPackedValuesAllowed()
    2031         171 :                     ? CSLTokenizeString2(value.c_str(), ",",
    2032             :                                          CSLT_HONOURSTRINGS |
    2033             :                                              CSLT_PRESERVEQUOTES)
    2034         439 :                     : CSLAddString(nullptr, value.c_str()));
    2035         268 :             if (!cpl::contains(inConstructionValues, arg))
    2036             :             {
    2037         244 :                 inConstructionValues[arg] = std::vector<std::string>();
    2038             :             }
    2039             :             auto &valueVector =
    2040         268 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
    2041         568 :             for (const char *v : aosTokens)
    2042             :             {
    2043         300 :                 valueVector.push_back(v);
    2044             :             }
    2045         268 :             if (arg->GetMaxCount() == 1)
    2046             :             {
    2047           3 :                 bool ret = arg->Set(std::move(valueVector));
    2048           3 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2049           3 :                 return ret;
    2050             :             }
    2051             : 
    2052         265 :             break;
    2053             :         }
    2054             : 
    2055          65 :         case GAAT_INTEGER_LIST:
    2056             :         {
    2057             :             const CPLStringList aosTokens(
    2058          65 :                 arg->GetPackedValuesAllowed()
    2059          65 :                     ? CSLTokenizeString2(
    2060             :                           value.c_str(), ",",
    2061             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2062             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2063         130 :                     : CSLAddString(nullptr, value.c_str()));
    2064          65 :             if (!cpl::contains(inConstructionValues, arg))
    2065             :             {
    2066          61 :                 inConstructionValues[arg] = std::vector<int>();
    2067             :             }
    2068             :             auto &valueVector =
    2069          65 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    2070         199 :             for (const char *v : aosTokens)
    2071             :             {
    2072         140 :                 errno = 0;
    2073         140 :                 char *endptr = nullptr;
    2074         140 :                 const auto val = std::strtol(v, &endptr, 10);
    2075         140 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    2076         136 :                     val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
    2077             :                 {
    2078         134 :                     valueVector.push_back(static_cast<int>(val));
    2079             :                 }
    2080             :                 else
    2081             :                 {
    2082           6 :                     ReportError(
    2083             :                         CE_Failure, CPLE_IllegalArg,
    2084             :                         "Expected list of integer value for argument '%s', "
    2085             :                         "but got '%s'.",
    2086             :                         name.c_str(), value.c_str());
    2087           6 :                     return false;
    2088             :                 }
    2089             :             }
    2090          59 :             if (arg->GetMaxCount() == 1)
    2091             :             {
    2092           2 :                 bool ret = arg->Set(std::move(valueVector));
    2093           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2094           2 :                 return ret;
    2095             :             }
    2096             : 
    2097          57 :             break;
    2098             :         }
    2099             : 
    2100         102 :         case GAAT_REAL_LIST:
    2101             :         {
    2102             :             const CPLStringList aosTokens(
    2103         102 :                 arg->GetPackedValuesAllowed()
    2104         102 :                     ? CSLTokenizeString2(
    2105             :                           value.c_str(), ",",
    2106             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2107             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2108         204 :                     : CSLAddString(nullptr, value.c_str()));
    2109         102 :             if (!cpl::contains(inConstructionValues, arg))
    2110             :             {
    2111         100 :                 inConstructionValues[arg] = std::vector<double>();
    2112             :             }
    2113             :             auto &valueVector =
    2114         102 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    2115         410 :             for (const char *v : aosTokens)
    2116             :             {
    2117         312 :                 char *endptr = nullptr;
    2118         312 :                 double dfValue = CPLStrtod(v, &endptr);
    2119         312 :                 if (strlen(v) == 0 || endptr != v + strlen(v))
    2120             :                 {
    2121           4 :                     ReportError(
    2122             :                         CE_Failure, CPLE_IllegalArg,
    2123             :                         "Expected list of real value for argument '%s', "
    2124             :                         "but got '%s'.",
    2125             :                         name.c_str(), value.c_str());
    2126           4 :                     return false;
    2127             :                 }
    2128         308 :                 valueVector.push_back(dfValue);
    2129             :             }
    2130          98 :             if (arg->GetMaxCount() == 1)
    2131             :             {
    2132           2 :                 bool ret = arg->Set(std::move(valueVector));
    2133           2 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2134           2 :                 return ret;
    2135             :             }
    2136             : 
    2137          96 :             break;
    2138             :         }
    2139             : 
    2140         650 :         case GAAT_DATASET_LIST:
    2141             :         {
    2142         650 :             if (!cpl::contains(inConstructionValues, arg))
    2143             :             {
    2144         637 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    2145             :             }
    2146             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    2147         650 :                 inConstructionValues[arg]);
    2148         650 :             if (!value.empty() && value[0] == '{' && value.back() == '}')
    2149             :             {
    2150          12 :                 valueVector.push_back(GDALArgDatasetValue(value));
    2151             :             }
    2152             :             else
    2153             :             {
    2154             :                 const CPLStringList aosTokens(
    2155         638 :                     arg->GetPackedValuesAllowed()
    2156           6 :                         ? CSLTokenizeString2(value.c_str(), ",",
    2157             :                                              CSLT_HONOURSTRINGS |
    2158             :                                                  CSLT_STRIPLEADSPACES)
    2159        1282 :                         : CSLAddString(nullptr, value.c_str()));
    2160        1279 :                 for (const char *v : aosTokens)
    2161             :                 {
    2162         641 :                     valueVector.push_back(GDALArgDatasetValue(v));
    2163             :                 }
    2164             :             }
    2165         650 :             if (arg->GetMaxCount() == 1)
    2166             :             {
    2167         529 :                 bool ret = arg->Set(std::move(valueVector));
    2168         529 :                 inConstructionValues.erase(inConstructionValues.find(arg));
    2169         529 :                 return ret;
    2170             :             }
    2171             : 
    2172         121 :             break;
    2173             :         }
    2174             :     }
    2175             : 
    2176         539 :     return true;
    2177             : }
    2178             : 
    2179             : /************************************************************************/
    2180             : /*              GDALAlgorithm::ParseCommandLineArguments()              */
    2181             : /************************************************************************/
    2182             : 
    2183        2072 : bool GDALAlgorithm::ParseCommandLineArguments(
    2184             :     const std::vector<std::string> &args)
    2185             : {
    2186        2072 :     if (m_parsedSubStringAlreadyCalled)
    2187             :     {
    2188           6 :         ReportError(CE_Failure, CPLE_AppDefined,
    2189             :                     "ParseCommandLineArguments() can only be called once per "
    2190             :                     "instance.");
    2191           6 :         return false;
    2192             :     }
    2193        2066 :     m_parsedSubStringAlreadyCalled = true;
    2194             : 
    2195             :     // AWS like syntax supported too (not advertized)
    2196        2066 :     if (args.size() == 1 && args[0] == "help")
    2197             :     {
    2198           1 :         auto arg = GetArg("help");
    2199           1 :         assert(arg);
    2200           1 :         arg->Set(true);
    2201           1 :         arg->RunActions();
    2202           1 :         return true;
    2203             :     }
    2204             : 
    2205        2065 :     if (HasSubAlgorithms())
    2206             :     {
    2207         445 :         if (args.empty())
    2208             :         {
    2209           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    2210           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    2211           2 :             return false;
    2212             :         }
    2213         443 :         if (!args[0].empty() && args[0][0] == '-')
    2214             :         {
    2215             :             // go on argument parsing
    2216             :         }
    2217             :         else
    2218             :         {
    2219         440 :             const auto nCounter = CPLGetErrorCounter();
    2220         440 :             m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
    2221         440 :             if (m_selectedSubAlgHolder)
    2222             :             {
    2223         437 :                 m_selectedSubAlg = m_selectedSubAlgHolder.get();
    2224         437 :                 m_selectedSubAlg->SetReferencePathForRelativePaths(
    2225         437 :                     m_referencePath);
    2226         437 :                 m_selectedSubAlg->m_executionForStreamOutput =
    2227         437 :                     m_executionForStreamOutput;
    2228         437 :                 m_selectedSubAlg->m_calledFromCommandLine =
    2229         437 :                     m_calledFromCommandLine;
    2230         437 :                 m_selectedSubAlg->m_skipValidationInParseCommandLine =
    2231         437 :                     m_skipValidationInParseCommandLine;
    2232         437 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    2233         874 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    2234         437 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    2235         437 :                 return bRet;
    2236             :             }
    2237             :             else
    2238             :             {
    2239           4 :                 if (!(CPLGetErrorCounter() == nCounter + 1 &&
    2240           1 :                       strstr(CPLGetLastErrorMsg(), "Do you mean")))
    2241             :                 {
    2242           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2243           2 :                                 "Unknown command: '%s'", args[0].c_str());
    2244             :                 }
    2245           3 :                 return false;
    2246             :             }
    2247             :         }
    2248             :     }
    2249             : 
    2250             :     std::map<
    2251             :         GDALAlgorithmArg *,
    2252             :         std::variant<std::vector<std::string>, std::vector<int>,
    2253             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    2254        3246 :         inConstructionValues;
    2255             : 
    2256        3246 :     std::vector<std::string> lArgs(args);
    2257        1623 :     bool helpValueRequested = false;
    2258        4741 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    2259             :     {
    2260        3218 :         const auto &strArg = lArgs[i];
    2261        3218 :         GDALAlgorithmArg *arg = nullptr;
    2262        3218 :         std::string name;
    2263        3218 :         std::string value;
    2264        3218 :         bool hasValue = false;
    2265        3218 :         if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
    2266           5 :             helpValueRequested = true;
    2267        3218 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    2268             :         {
    2269        2046 :             const auto equalPos = strArg.find('=');
    2270        4092 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    2271        2046 :                                                    : strArg;
    2272        2046 :             const std::string nameWithoutDash = name.substr(2);
    2273        2046 :             auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2274        2099 :             if (m_arbitraryLongNameArgsAllowed &&
    2275        2099 :                 iterArg == m_mapLongNameToArg.end())
    2276             :             {
    2277          16 :                 GetArg(nameWithoutDash);
    2278          16 :                 iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2279             :             }
    2280        2046 :             if (iterArg == m_mapLongNameToArg.end())
    2281             :             {
    2282             :                 const std::string bestCandidate =
    2283          26 :                     GetSuggestionForArgumentName(nameWithoutDash);
    2284          26 :                 if (!bestCandidate.empty())
    2285             :                 {
    2286           2 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2287             :                                 "Option '%s' is unknown. Do you mean '--%s'?",
    2288             :                                 name.c_str(), bestCandidate.c_str());
    2289             :                 }
    2290             :                 else
    2291             :                 {
    2292          24 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2293             :                                 "Option '%s' is unknown.", name.c_str());
    2294             :                 }
    2295          26 :                 return false;
    2296             :             }
    2297        2020 :             arg = iterArg->second;
    2298        2020 :             if (equalPos != std::string::npos)
    2299             :             {
    2300         441 :                 hasValue = true;
    2301         441 :                 value = strArg.substr(equalPos + 1);
    2302             :             }
    2303             :         }
    2304        1246 :         else if (strArg.size() >= 2 && strArg[0] == '-' &&
    2305          74 :                  CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
    2306             :         {
    2307         143 :             for (size_t j = 1; j < strArg.size(); ++j)
    2308             :             {
    2309          74 :                 name.clear();
    2310          74 :                 name += strArg[j];
    2311          74 :                 const auto iterArg = m_mapShortNameToArg.find(name);
    2312          74 :                 if (iterArg == m_mapShortNameToArg.end())
    2313             :                 {
    2314           5 :                     const std::string nameWithoutDash = strArg.substr(1);
    2315           5 :                     if (m_mapLongNameToArg.find(nameWithoutDash) !=
    2316          10 :                         m_mapLongNameToArg.end())
    2317             :                     {
    2318           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2319             :                                     "Short name option '%s' is unknown. Do you "
    2320             :                                     "mean '--%s' (with leading double dash) ?",
    2321             :                                     name.c_str(), nameWithoutDash.c_str());
    2322             :                     }
    2323             :                     else
    2324             :                     {
    2325             :                         const std::string bestCandidate =
    2326           8 :                             GetSuggestionForArgumentName(nameWithoutDash);
    2327           4 :                         if (!bestCandidate.empty())
    2328             :                         {
    2329           1 :                             ReportError(
    2330             :                                 CE_Failure, CPLE_IllegalArg,
    2331             :                                 "Short name option '%s' is unknown. Do you "
    2332             :                                 "mean '--%s' (with leading double dash) ?",
    2333             :                                 name.c_str(), bestCandidate.c_str());
    2334             :                         }
    2335             :                         else
    2336             :                         {
    2337           3 :                             ReportError(CE_Failure, CPLE_IllegalArg,
    2338             :                                         "Short name option '%s' is unknown.",
    2339             :                                         name.c_str());
    2340             :                         }
    2341             :                     }
    2342           5 :                     return false;
    2343             :                 }
    2344          69 :                 arg = iterArg->second;
    2345          69 :                 if (strArg.size() > 2)
    2346             :                 {
    2347           0 :                     if (arg->GetType() != GAAT_BOOLEAN)
    2348             :                     {
    2349           0 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2350             :                                     "Invalid argument '%s'. Option '%s' is not "
    2351             :                                     "a boolean option.",
    2352             :                                     strArg.c_str(), name.c_str());
    2353           0 :                         return false;
    2354             :                     }
    2355             : 
    2356           0 :                     if (!ParseArgument(arg, name, "true", inConstructionValues))
    2357           0 :                         return false;
    2358             :                 }
    2359             :             }
    2360          69 :             if (strArg.size() > 2)
    2361             :             {
    2362           0 :                 lArgs.erase(lArgs.begin() + i);
    2363           0 :                 continue;
    2364             :             }
    2365             :         }
    2366             :         else
    2367             :         {
    2368        1098 :             ++i;
    2369        1098 :             continue;
    2370             :         }
    2371        2089 :         CPLAssert(arg);
    2372             : 
    2373        2089 :         if (arg && arg->GetType() == GAAT_BOOLEAN)
    2374             :         {
    2375         321 :             if (!hasValue)
    2376             :             {
    2377         318 :                 hasValue = true;
    2378         318 :                 value = "true";
    2379             :             }
    2380             :         }
    2381             : 
    2382        2089 :         if (!hasValue)
    2383             :         {
    2384        1330 :             if (i + 1 == lArgs.size())
    2385             :             {
    2386          30 :                 if (m_parseForAutoCompletion)
    2387             :                 {
    2388          24 :                     lArgs.erase(lArgs.begin() + i);
    2389          24 :                     break;
    2390             :                 }
    2391           6 :                 ReportError(
    2392             :                     CE_Failure, CPLE_IllegalArg,
    2393             :                     "Expected value for argument '%s', but ran short of tokens",
    2394             :                     name.c_str());
    2395           6 :                 return false;
    2396             :             }
    2397        1300 :             value = lArgs[i + 1];
    2398        1300 :             lArgs.erase(lArgs.begin() + i + 1);
    2399             :         }
    2400             : 
    2401        2059 :         if (arg && !ParseArgument(arg, name, value, inConstructionValues))
    2402          39 :             return false;
    2403             : 
    2404        2020 :         lArgs.erase(lArgs.begin() + i);
    2405             :     }
    2406             : 
    2407        1547 :     if (m_specialActionRequested)
    2408             :     {
    2409          23 :         return true;
    2410             :     }
    2411             : 
    2412        1994 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    2413             :     {
    2414        1960 :         for (auto &[arg, value] : inConstructionValues)
    2415             :         {
    2416         494 :             if (arg->GetType() == GAAT_STRING_LIST)
    2417             :             {
    2418         238 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    2419         238 :                         inConstructionValues[arg])))
    2420             :                 {
    2421          34 :                     return false;
    2422             :                 }
    2423             :             }
    2424         256 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    2425             :             {
    2426          54 :                 if (!arg->Set(
    2427          54 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    2428             :                 {
    2429           4 :                     return false;
    2430             :                 }
    2431             :             }
    2432         202 :             else if (arg->GetType() == GAAT_REAL_LIST)
    2433             :             {
    2434          94 :                 if (!arg->Set(std::get<std::vector<double>>(
    2435          94 :                         inConstructionValues[arg])))
    2436             :                 {
    2437          10 :                     return false;
    2438             :                 }
    2439             :             }
    2440         108 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    2441             :             {
    2442         108 :                 if (!arg->Set(
    2443             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    2444         108 :                             inConstructionValues[arg]))))
    2445             :                 {
    2446           2 :                     return false;
    2447             :                 }
    2448             :             }
    2449             :         }
    2450        1466 :         return true;
    2451        1524 :     };
    2452             : 
    2453             :     // Process positional arguments that have not been set through their
    2454             :     // option name.
    2455        1524 :     size_t i = 0;
    2456        1524 :     size_t iCurPosArg = 0;
    2457             : 
    2458             :     // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
    2459        1547 :     if (m_positionalArgs.size() == 3 &&
    2460          24 :         (m_positionalArgs[0]->IsRequired() ||
    2461          23 :          m_positionalArgs[0]->GetMinCount() == 1) &&
    2462          44 :         m_positionalArgs[0]->GetMaxCount() == 1 &&
    2463          29 :         (m_positionalArgs[1]->IsRequired() ||
    2464          29 :          m_positionalArgs[1]->GetMinCount() == 1) &&
    2465             :         /* Second argument may have several occurrences */
    2466          44 :         m_positionalArgs[1]->GetMaxCount() >= 1 &&
    2467          22 :         (m_positionalArgs[2]->IsRequired() ||
    2468          22 :          m_positionalArgs[2]->GetMinCount() == 1) &&
    2469          22 :         m_positionalArgs[2]->GetMaxCount() == 1 &&
    2470           9 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2471        1556 :         !m_positionalArgs[1]->IsExplicitlySet() &&
    2472           9 :         !m_positionalArgs[2]->IsExplicitlySet())
    2473             :     {
    2474           7 :         if (lArgs.size() - i < 3)
    2475             :         {
    2476           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    2477             :                         "Not enough positional values.");
    2478           1 :             return false;
    2479             :         }
    2480          12 :         bool ok = ParseArgument(m_positionalArgs[0],
    2481           6 :                                 m_positionalArgs[0]->GetName().c_str(),
    2482           6 :                                 lArgs[i], inConstructionValues);
    2483           6 :         if (ok)
    2484             :         {
    2485           5 :             ++i;
    2486          11 :             for (; i + 1 < lArgs.size() && ok; ++i)
    2487             :             {
    2488          12 :                 ok = ParseArgument(m_positionalArgs[1],
    2489           6 :                                    m_positionalArgs[1]->GetName().c_str(),
    2490           6 :                                    lArgs[i], inConstructionValues);
    2491             :             }
    2492             :         }
    2493           6 :         if (ok)
    2494             :         {
    2495          10 :             ok = ParseArgument(m_positionalArgs[2],
    2496          10 :                                m_positionalArgs[2]->GetName().c_str(), lArgs[i],
    2497             :                                inConstructionValues);
    2498           5 :             ++i;
    2499             :         }
    2500           6 :         if (!ok)
    2501             :         {
    2502           3 :             ProcessInConstructionValues();
    2503           3 :             return false;
    2504             :         }
    2505             :     }
    2506             : 
    2507         516 :     if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
    2508         601 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2509        2356 :         m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
    2510         106 :         (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
    2511          53 :          m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
    2512             :     {
    2513          53 :         ++iCurPosArg;
    2514             :     }
    2515             : 
    2516        2533 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    2517             :     {
    2518        1020 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    2519        1028 :         while (arg->IsExplicitlySet())
    2520             :         {
    2521           9 :             ++iCurPosArg;
    2522           9 :             if (iCurPosArg == m_positionalArgs.size())
    2523           1 :                 break;
    2524           8 :             arg = m_positionalArgs[iCurPosArg];
    2525             :         }
    2526        1020 :         if (iCurPosArg == m_positionalArgs.size())
    2527             :         {
    2528           1 :             break;
    2529             :         }
    2530        1569 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2531         550 :             arg->GetMinCount() != arg->GetMaxCount())
    2532             :         {
    2533         109 :             if (iCurPosArg == 0)
    2534             :             {
    2535          73 :                 size_t nCountAtEnd = 0;
    2536         102 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    2537             :                 {
    2538          31 :                     const auto *otherArg = m_positionalArgs[j];
    2539          31 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    2540             :                     {
    2541           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    2542             :                         {
    2543           2 :                             ReportError(
    2544             :                                 CE_Failure, CPLE_AppDefined,
    2545             :                                 "Ambiguity in definition of positional "
    2546             :                                 "argument "
    2547             :                                 "'%s' given it has a varying number of values, "
    2548             :                                 "but follows argument '%s' which also has a "
    2549             :                                 "varying number of values",
    2550           1 :                                 otherArg->GetName().c_str(),
    2551           1 :                                 arg->GetName().c_str());
    2552           1 :                             ProcessInConstructionValues();
    2553           1 :                             return false;
    2554             :                         }
    2555           3 :                         nCountAtEnd += otherArg->GetMinCount();
    2556             :                     }
    2557             :                     else
    2558             :                     {
    2559          27 :                         if (!otherArg->IsRequired())
    2560             :                         {
    2561           2 :                             ReportError(
    2562             :                                 CE_Failure, CPLE_AppDefined,
    2563             :                                 "Ambiguity in definition of positional "
    2564             :                                 "argument "
    2565             :                                 "'%s', given it is not required but follows "
    2566             :                                 "argument '%s' which has a varying number of "
    2567             :                                 "values",
    2568           1 :                                 otherArg->GetName().c_str(),
    2569           1 :                                 arg->GetName().c_str());
    2570           1 :                             ProcessInConstructionValues();
    2571           1 :                             return false;
    2572             :                         }
    2573          26 :                         nCountAtEnd++;
    2574             :                     }
    2575             :                 }
    2576          71 :                 if (lArgs.size() < nCountAtEnd)
    2577             :                 {
    2578           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2579             :                                 "Not enough positional values.");
    2580           1 :                     ProcessInConstructionValues();
    2581           1 :                     return false;
    2582             :                 }
    2583         148 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    2584             :                 {
    2585          78 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2586             :                                        inConstructionValues))
    2587             :                     {
    2588           0 :                         ProcessInConstructionValues();
    2589           0 :                         return false;
    2590             :                     }
    2591             :                 }
    2592             :             }
    2593          36 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    2594             :             {
    2595          82 :                 for (; i < lArgs.size(); ++i)
    2596             :                 {
    2597          47 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2598             :                                        inConstructionValues))
    2599             :                     {
    2600           0 :                         ProcessInConstructionValues();
    2601           0 :                         return false;
    2602             :                     }
    2603             :                 }
    2604             :             }
    2605             :             else
    2606             :             {
    2607           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2608             :                             "Ambiguity in definition of positional arguments: "
    2609             :                             "arguments with varying number of values must be "
    2610             :                             "first or last one.");
    2611           1 :                 return false;
    2612             :             }
    2613             :         }
    2614             :         else
    2615             :         {
    2616         910 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    2617             :             {
    2618           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2619             :                             "Not enough positional values.");
    2620           1 :                 return false;
    2621             :             }
    2622         909 :             const size_t iMax = i + arg->GetMaxCount();
    2623        1821 :             for (; i < iMax; ++i)
    2624             :             {
    2625         913 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2626             :                                    inConstructionValues))
    2627             :                 {
    2628           1 :                     ProcessInConstructionValues();
    2629           1 :                     return false;
    2630             :                 }
    2631             :             }
    2632             :         }
    2633        1013 :         ++iCurPosArg;
    2634             :     }
    2635             : 
    2636        1514 :     if (i < lArgs.size())
    2637             :     {
    2638          21 :         ReportError(CE_Failure, CPLE_AppDefined,
    2639             :                     "Positional values starting at '%s' are not expected.",
    2640          21 :                     lArgs[i].c_str());
    2641          21 :         return false;
    2642             :     }
    2643             : 
    2644        1493 :     if (!ProcessInConstructionValues())
    2645             :     {
    2646          33 :         return false;
    2647             :     }
    2648             : 
    2649             :     // Skip to first unset positional argument.
    2650        2491 :     while (iCurPosArg < m_positionalArgs.size() &&
    2651         556 :            m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    2652             :     {
    2653         475 :         ++iCurPosArg;
    2654             :     }
    2655             :     // Check if this positional argument is required.
    2656        1540 :     if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
    2657          80 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    2658          48 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    2659          32 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    2660             :     {
    2661          69 :         ReportError(CE_Failure, CPLE_AppDefined,
    2662             :                     "Positional arguments starting at '%s' have not been "
    2663             :                     "specified.",
    2664          69 :                     m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    2665          69 :         return false;
    2666             :     }
    2667             : 
    2668        1391 :     if (m_calledFromCommandLine)
    2669             :     {
    2670        4920 :         for (auto &arg : m_args)
    2671             :         {
    2672        6439 :             if (arg->IsExplicitlySet() &&
    2673        1021 :                 ((arg->GetType() == GAAT_STRING &&
    2674        1018 :                   arg->Get<std::string>() == "?") ||
    2675         936 :                  (arg->GetType() == GAAT_STRING_LIST &&
    2676         157 :                   arg->Get<std::vector<std::string>>().size() == 1 &&
    2677          78 :                   arg->Get<std::vector<std::string>>()[0] == "?")))
    2678             :             {
    2679             :                 {
    2680          10 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2681           5 :                     ValidateArguments();
    2682             :                 }
    2683             : 
    2684           5 :                 auto choices = arg->GetChoices();
    2685           5 :                 if (choices.empty())
    2686           2 :                     choices = arg->GetAutoCompleteChoices(std::string());
    2687           5 :                 if (!choices.empty())
    2688             :                 {
    2689           5 :                     if (choices.size() == 1)
    2690             :                     {
    2691           4 :                         ReportError(
    2692             :                             CE_Failure, CPLE_AppDefined,
    2693             :                             "Single potential value for argument '%s' is '%s'",
    2694           4 :                             arg->GetName().c_str(), choices.front().c_str());
    2695             :                     }
    2696             :                     else
    2697             :                     {
    2698           6 :                         std::string msg("Potential values for argument '");
    2699           3 :                         msg += arg->GetName();
    2700           3 :                         msg += "' are:";
    2701          45 :                         for (const auto &v : choices)
    2702             :                         {
    2703          42 :                             msg += "\n- ";
    2704          42 :                             msg += v;
    2705             :                         }
    2706           3 :                         ReportError(CE_Failure, CPLE_AppDefined, "%s",
    2707             :                                     msg.c_str());
    2708             :                     }
    2709           5 :                     return false;
    2710             :                 }
    2711             :             }
    2712             :         }
    2713             :     }
    2714             : 
    2715        1386 :     return m_skipValidationInParseCommandLine || ValidateArguments();
    2716             : }
    2717             : 
    2718             : /************************************************************************/
    2719             : /*                     GDALAlgorithm::ReportError()                     */
    2720             : /************************************************************************/
    2721             : 
    2722             : //! @cond Doxygen_Suppress
    2723         902 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    2724             :                                 const char *fmt, ...) const
    2725             : {
    2726             :     va_list args;
    2727         902 :     va_start(args, fmt);
    2728         902 :     CPLError(eErrClass, err_no, "%s",
    2729         902 :              std::string(m_name)
    2730         902 :                  .append(": ")
    2731        1804 :                  .append(CPLString().vPrintf(fmt, args))
    2732             :                  .c_str());
    2733         902 :     va_end(args);
    2734         902 : }
    2735             : 
    2736             : //! @endcond
    2737             : 
    2738             : /************************************************************************/
    2739             : /*                  GDALAlgorithm::ProcessDatasetArg()                  */
    2740             : /************************************************************************/
    2741             : 
    2742        9908 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    2743             :                                       GDALAlgorithm *algForOutput)
    2744             : {
    2745        9908 :     bool ret = true;
    2746             : 
    2747        9908 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    2748        9908 :     const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
    2749        9908 :     const bool update = hasUpdateArg && updateArg->Get<bool>();
    2750             : 
    2751        9908 :     const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
    2752        9908 :     const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
    2753        9908 :     const bool append = hasAppendArg && appendArg->Get<bool>();
    2754             : 
    2755        9908 :     const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
    2756             :     const bool overwrite =
    2757       16355 :         (arg->IsOutput() && overwriteArg &&
    2758       16355 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    2759             : 
    2760        9908 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    2761       19816 :     auto &val = [arg]() -> GDALArgDatasetValue &
    2762             :     {
    2763        9908 :         if (arg->GetType() == GAAT_DATASET_LIST)
    2764        5655 :             return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
    2765             :         else
    2766        4253 :             return arg->Get<GDALArgDatasetValue>();
    2767        9908 :     }();
    2768             :     const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
    2769       15662 :         arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
    2770       15670 :         !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
    2771           8 :         !overwrite;
    2772             : 
    2773             :     // Used for nested pipelines
    2774             :     const auto oIterDatasetNameToDataset =
    2775       19813 :         val.IsNameSet() ? m_oMapDatasetNameToDataset.find(val.GetName())
    2776        9908 :                         : m_oMapDatasetNameToDataset.end();
    2777             : 
    2778        9908 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    2779             :     {
    2780           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    2781             :                     "Argument '%s' has no dataset object or dataset name.",
    2782           3 :                     arg->GetName().c_str());
    2783           3 :         ret = false;
    2784             :     }
    2785        9905 :     else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
    2786             :     {
    2787           3 :         return false;
    2788             :     }
    2789         220 :     else if (m_inputDatasetCanBeOmitted &&
    2790       10122 :              val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
    2791           8 :              !arg->IsOutput())
    2792             :     {
    2793           8 :         return true;
    2794             :     }
    2795       14638 :     else if (!val.GetDatasetRef() &&
    2796        5044 :              (arg->AutoOpenDataset() ||
    2797       14938 :               oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()) &&
    2798        4444 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
    2799             :               onlyInputSpecifiedInUpdateAndOutputNotRequired))
    2800             :     {
    2801        1345 :         int flags = arg->GetDatasetType();
    2802        1345 :         bool assignToOutputArg = false;
    2803             : 
    2804             :         // Check if input and output parameters point to the same
    2805             :         // filename (for vector datasets)
    2806        2488 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    2807        2488 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    2808             :         {
    2809          62 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    2810         121 :             if (!outputVal.GetDatasetRef() &&
    2811         121 :                 outputVal.GetName() == val.GetName() &&
    2812           2 :                 (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
    2813             :             {
    2814           2 :                 assignToOutputArg = true;
    2815           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2816             :             }
    2817          60 :             else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
    2818             :             {
    2819           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2820             :             }
    2821             :         }
    2822             : 
    2823        1345 :         if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
    2824        1262 :             flags |= GDAL_OF_VERBOSE_ERROR;
    2825        1345 :         if ((arg == outputArg || !outputArg) && update)
    2826             :         {
    2827          85 :             flags |= GDAL_OF_UPDATE;
    2828          85 :             if (!append)
    2829          64 :                 flags |= GDAL_OF_VERBOSE_ERROR;
    2830             :         }
    2831             : 
    2832        1345 :         const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
    2833             :         const bool readOnly =
    2834        1388 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    2835          43 :              readOnlyArg->Get<bool>());
    2836        1345 :         if (readOnly)
    2837          12 :             flags &= ~GDAL_OF_UPDATE;
    2838             : 
    2839        2690 :         CPLStringList aosOpenOptions;
    2840        2690 :         CPLStringList aosAllowedDrivers;
    2841        1345 :         if (arg->IsInput())
    2842             :         {
    2843        1345 :             if (arg == outputArg)
    2844             :             {
    2845          83 :                 if (update && !overwrite)
    2846             :                 {
    2847          83 :                     const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
    2848          83 :                     if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2849          31 :                         aosOpenOptions = CPLStringList(
    2850          31 :                             ooArg->Get<std::vector<std::string>>());
    2851             :                 }
    2852             :             }
    2853             :             else
    2854             :             {
    2855        1262 :                 const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    2856        1262 :                 if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2857             :                     aosOpenOptions =
    2858        1211 :                         CPLStringList(ooArg->Get<std::vector<std::string>>());
    2859             : 
    2860        1262 :                 const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2861        1262 :                 if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    2862             :                     aosAllowedDrivers =
    2863        1168 :                         CPLStringList(ifArg->Get<std::vector<std::string>>());
    2864             :             }
    2865             :         }
    2866             : 
    2867        2690 :         std::string osDatasetName = val.GetName();
    2868        1345 :         if (!m_referencePath.empty())
    2869             :         {
    2870          42 :             osDatasetName = GDALDataset::BuildFilename(
    2871          21 :                 osDatasetName.c_str(), m_referencePath.c_str(), true);
    2872             :         }
    2873        1345 :         if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
    2874           0 :             osDatasetName = "/vsistdin/";
    2875             : 
    2876             :         // Handle special case of overview delete in GTiff which would fail
    2877             :         // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
    2878         142 :         if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
    2879        1489 :             m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
    2880           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
    2881             :         {
    2882           4 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2883             :             GDALDriverH hDrv =
    2884           2 :                 GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
    2885           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
    2886             :             {
    2887             :                 // Cleaning does not break COG layout
    2888           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
    2889             :             }
    2890             :         }
    2891             : 
    2892             :         GDALDataset *poDS;
    2893        2690 :         CPLErrorAccumulator oAccumulator;
    2894             :         {
    2895        2690 :             auto oContext = oAccumulator.InstallForCurrentScope();
    2896             : 
    2897        1345 :             poDS = oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end()
    2898        1345 :                        ? oIterDatasetNameToDataset->second
    2899        1342 :                        : GDALDataset::Open(osDatasetName.c_str(), flags,
    2900        1342 :                                            aosAllowedDrivers.List(),
    2901        1342 :                                            aosOpenOptions.List());
    2902             : 
    2903          65 :             if (!poDS && aosAllowedDrivers.empty() && aosOpenOptions.empty() &&
    2904        1410 :                 !arg->IsOutput() && arg->GetDatasetType() & GDAL_OF_VECTOR)
    2905             :             {
    2906          35 :                 auto [poWktGeom, eErr] = OGRGeometryFactory::createFromWkt(
    2907          70 :                     osDatasetName.c_str(), nullptr);
    2908          35 :                 if (eErr == OGRERR_NONE)
    2909             :                 {
    2910           4 :                     auto poMemDS = std::make_unique<MEMDataset>();
    2911           4 :                     auto *poLayer = poMemDS->CreateLayer(
    2912             :                         "", poWktGeom->getSpatialReference(),
    2913           2 :                         poWktGeom->getGeometryType());
    2914             : 
    2915           2 :                     auto poFeatureDefn = poLayer->GetLayerDefn();
    2916           4 :                     OGRFeature oFeature(poFeatureDefn);
    2917             : 
    2918           2 :                     oFeature.SetGeometry(std::move(poWktGeom));
    2919           2 :                     if (poLayer->CreateFeature(&oFeature) == OGRERR_NONE)
    2920             :                     {
    2921           2 :                         poDS = poMemDS.release();
    2922           2 :                         oAccumulator.ClearErrors();
    2923             :                     }
    2924             :                 }
    2925             :             }
    2926             : 
    2927             :             // Retry with PostGIS vector driver
    2928          63 :             if (!poDS && (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0 &&
    2929          61 :                 cpl::starts_with(osDatasetName, "PG:") &&
    2930           0 :                 GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
    2931        1408 :                 aosAllowedDrivers.empty() && aosOpenOptions.empty())
    2932             :             {
    2933           0 :                 oAccumulator.ClearErrors();
    2934           0 :                 poDS = GDALDataset::Open(
    2935           0 :                     osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
    2936           0 :                     aosAllowedDrivers.List(), aosOpenOptions.List());
    2937             :             }
    2938             :         }
    2939        1345 :         oAccumulator.ReplayErrors();
    2940             : 
    2941        1345 :         if (poDS)
    2942             :         {
    2943        1282 :             if (oIterDatasetNameToDataset != m_oMapDatasetNameToDataset.end())
    2944             :             {
    2945           3 :                 if (arg->GetType() == GAAT_DATASET)
    2946           3 :                     arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
    2947           3 :                 poDS->Reference();
    2948           3 :                 m_oMapDatasetNameToDataset.erase(oIterDatasetNameToDataset);
    2949             :             }
    2950             : 
    2951             :             // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
    2952             :             // where the PG: dataset will be first opened with the PostGISRaster
    2953             :             // driver whereas the PostgreSQL (vector) one is actually wanted.
    2954        1750 :             if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
    2955        1840 :                 (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
    2956          90 :                 aosOpenOptions.empty())
    2957             :             {
    2958          86 :                 auto poDrv = poDS->GetDriver();
    2959          86 :                 if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
    2960             :                 {
    2961             :                     // Retry with PostgreSQL (vector) driver
    2962             :                     std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
    2963           0 :                         osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
    2964           0 :                     if (poTmpDS)
    2965             :                     {
    2966           0 :                         poDS->ReleaseRef();
    2967           0 :                         poDS = poTmpDS.release();
    2968             :                     }
    2969             :                 }
    2970             :             }
    2971             : 
    2972        1282 :             if (assignToOutputArg)
    2973             :             {
    2974             :                 // Avoid opening twice the same datasource if it is both
    2975             :                 // the input and output.
    2976             :                 // Known to cause problems with at least FGdb, SQLite
    2977             :                 // and GPKG drivers. See #4270
    2978             :                 // Restrict to those 3 drivers. For example it is known
    2979             :                 // to break with the PG driver due to the way it
    2980             :                 // manages transactions.
    2981           2 :                 auto poDriver = poDS->GetDriver();
    2982           4 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    2983           2 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    2984           2 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    2985             :                 {
    2986           2 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    2987             :                 }
    2988             :             }
    2989        1282 :             val.SetDatasetOpenedByAlgorithm();
    2990        1282 :             val.Set(poDS);
    2991        1282 :             poDS->ReleaseRef();
    2992             :         }
    2993          63 :         else if (!append)
    2994             :         {
    2995          61 :             ret = false;
    2996             :         }
    2997             :     }
    2998             : 
    2999             :     // Deal with overwriting the output dataset
    3000        9897 :     if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
    3001             :     {
    3002        3101 :         if (!append)
    3003             :         {
    3004             :             // If outputting to MEM, do not try to erase a real file of the same name!
    3005             :             const auto outputFormatArg =
    3006        3089 :                 algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3007        9229 :             if (!(outputFormatArg &&
    3008        3070 :                   outputFormatArg->GetType() == GAAT_STRING &&
    3009        3070 :                   (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3010        2000 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3011        1072 :                          "stream") ||
    3012        1072 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    3013             :                          "Memory"))))
    3014             :             {
    3015        1091 :                 const char *pszType = "";
    3016        1091 :                 GDALDriver *poDriver = nullptr;
    3017        2136 :                 if (!val.GetName().empty() &&
    3018        1045 :                     GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
    3019             :                                                &poDriver))
    3020             :                 {
    3021          79 :                     if (!overwrite)
    3022             :                     {
    3023          68 :                         std::string options;
    3024          34 :                         if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
    3025             :                         {
    3026          11 :                             options += "--";
    3027          11 :                             options += GDAL_ARG_NAME_OVERWRITE_LAYER;
    3028             :                         }
    3029          34 :                         if (hasAppendArg)
    3030             :                         {
    3031          22 :                             if (!options.empty())
    3032           8 :                                 options += '/';
    3033          22 :                             options += "--";
    3034          22 :                             options += GDAL_ARG_NAME_APPEND;
    3035             :                         }
    3036          34 :                         if (hasUpdateArg)
    3037             :                         {
    3038          15 :                             if (!options.empty())
    3039          12 :                                 options += '/';
    3040          15 :                             options += "--";
    3041          15 :                             options += GDAL_ARG_NAME_UPDATE;
    3042             :                         }
    3043             : 
    3044          34 :                         if (poDriver)
    3045             :                         {
    3046          68 :                             const char *pszPrefix = poDriver->GetMetadataItem(
    3047          34 :                                 GDAL_DMD_CONNECTION_PREFIX);
    3048          34 :                             if (pszPrefix &&
    3049           0 :                                 STARTS_WITH_CI(val.GetName().c_str(),
    3050             :                                                pszPrefix))
    3051             :                             {
    3052           0 :                                 bool bExists = false;
    3053             :                                 {
    3054             :                                     CPLErrorStateBackuper oBackuper(
    3055           0 :                                         CPLQuietErrorHandler);
    3056           0 :                                     bExists = std::unique_ptr<GDALDataset>(
    3057             :                                                   GDALDataset::Open(
    3058           0 :                                                       val.GetName().c_str())) !=
    3059             :                                               nullptr;
    3060             :                                 }
    3061           0 :                                 if (bExists)
    3062             :                                 {
    3063           0 :                                     if (!options.empty())
    3064           0 :                                         options = " You may specify the " +
    3065           0 :                                                   options + " option.";
    3066           0 :                                     ReportError(CE_Failure, CPLE_AppDefined,
    3067             :                                                 "%s '%s' already exists.%s",
    3068           0 :                                                 pszType, val.GetName().c_str(),
    3069             :                                                 options.c_str());
    3070           0 :                                     return false;
    3071             :                                 }
    3072             : 
    3073           0 :                                 return true;
    3074             :                             }
    3075             :                         }
    3076             : 
    3077          34 :                         if (!options.empty())
    3078          28 :                             options = '/' + options;
    3079          68 :                         ReportError(
    3080             :                             CE_Failure, CPLE_AppDefined,
    3081             :                             "%s '%s' already exists. You may specify the "
    3082             :                             "--overwrite%s option.",
    3083          34 :                             pszType, val.GetName().c_str(), options.c_str());
    3084          34 :                         return false;
    3085             :                     }
    3086          45 :                     else if (EQUAL(pszType, "File"))
    3087             :                     {
    3088           1 :                         if (VSIUnlink(val.GetName().c_str()) != 0)
    3089             :                         {
    3090           0 :                             ReportError(CE_Failure, CPLE_AppDefined,
    3091             :                                         "Deleting %s failed: %s",
    3092           0 :                                         val.GetName().c_str(),
    3093           0 :                                         VSIStrerror(errno));
    3094           0 :                             return false;
    3095             :                         }
    3096             :                     }
    3097          44 :                     else if (EQUAL(pszType, "Directory"))
    3098             :                     {
    3099             :                         // We don't want the user to accidentally erase a non-GDAL dataset
    3100           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3101             :                                     "Directory '%s' already exists, but is not "
    3102             :                                     "recognized as a valid GDAL dataset. "
    3103             :                                     "Please manually delete it before retrying",
    3104           1 :                                     val.GetName().c_str());
    3105           1 :                         return false;
    3106             :                     }
    3107          43 :                     else if (poDriver)
    3108             :                     {
    3109             :                         bool bDeleteOK;
    3110             :                         {
    3111             :                             CPLErrorStateBackuper oBackuper(
    3112          43 :                                 CPLQuietErrorHandler);
    3113          43 :                             bDeleteOK = (poDriver->Delete(
    3114          43 :                                              val.GetName().c_str()) == CE_None);
    3115             :                         }
    3116             :                         VSIStatBufL sStat;
    3117          46 :                         if (!bDeleteOK &&
    3118           3 :                             VSIStatL(val.GetName().c_str(), &sStat) == 0)
    3119             :                         {
    3120           3 :                             if (VSI_ISDIR(sStat.st_mode))
    3121             :                             {
    3122             :                                 // We don't want the user to accidentally erase a non-GDAL dataset
    3123           0 :                                 ReportError(
    3124             :                                     CE_Failure, CPLE_AppDefined,
    3125             :                                     "Directory '%s' already exists, but is not "
    3126             :                                     "recognized as a valid GDAL dataset. "
    3127             :                                     "Please manually delete it before retrying",
    3128           0 :                                     val.GetName().c_str());
    3129           2 :                                 return false;
    3130             :                             }
    3131           3 :                             else if (VSIUnlink(val.GetName().c_str()) != 0)
    3132             :                             {
    3133           2 :                                 ReportError(CE_Failure, CPLE_AppDefined,
    3134             :                                             "Deleting %s failed: %s",
    3135           2 :                                             val.GetName().c_str(),
    3136           2 :                                             VSIStrerror(errno));
    3137           2 :                                 return false;
    3138             :                             }
    3139             :                         }
    3140             :                     }
    3141             :                 }
    3142             :             }
    3143             :         }
    3144             :     }
    3145             : 
    3146             :     // If outputting to stdout, automatically turn off progress bar
    3147        9860 :     if (arg == outputArg && val.GetName() == "/vsistdout/")
    3148             :     {
    3149           8 :         auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
    3150           8 :         if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
    3151           5 :             quietArg->Set(true);
    3152             :     }
    3153             : 
    3154        9860 :     return ret;
    3155             : }
    3156             : 
    3157             : /************************************************************************/
    3158             : /*                  GDALAlgorithm::ValidateArguments()                  */
    3159             : /************************************************************************/
    3160             : 
    3161        6778 : bool GDALAlgorithm::ValidateArguments()
    3162             : {
    3163        6778 :     if (m_selectedSubAlg)
    3164           3 :         return m_selectedSubAlg->ValidateArguments();
    3165             : 
    3166        6775 :     if (m_specialActionRequested)
    3167           1 :         return true;
    3168             : 
    3169        6774 :     m_arbitraryLongNameArgsAllowed = false;
    3170             : 
    3171             :     // If only --output=format=MEM/stream is specified and not --output,
    3172             :     // then set empty name for --output.
    3173        6774 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    3174        6774 :     auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3175        4030 :     if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
    3176        2679 :         !outputArg->IsExplicitlySet() &&
    3177         360 :         outputFormatArg->GetType() == GAAT_STRING &&
    3178         360 :         (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3179         590 :          EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
    3180       11137 :         outputArg->GetType() == GAAT_DATASET &&
    3181         333 :         (outputArg->GetDatasetInputFlags() & GADV_NAME))
    3182             :     {
    3183         333 :         outputArg->Get<GDALArgDatasetValue>().Set("");
    3184             :     }
    3185             : 
    3186             :     // The method may emit several errors if several constraints are not met.
    3187        6774 :     bool ret = true;
    3188       13548 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    3189       13548 :     std::map<std::string, std::vector<std::string>> mutualDependencyGroupUsed;
    3190      128872 :     for (auto &arg : m_args)
    3191             :     {
    3192             :         // Check mutually exclusive/dependent arguments
    3193      122098 :         if (arg->IsExplicitlySet())
    3194             :         {
    3195             : 
    3196       19563 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3197       19563 :             if (!mutualExclusionGroup.empty())
    3198             :             {
    3199             :                 auto oIter =
    3200         769 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    3201         769 :                 if (oIter != mutualExclusionGroupUsed.end())
    3202             :                 {
    3203          13 :                     ret = false;
    3204          26 :                     ReportError(
    3205             :                         CE_Failure, CPLE_AppDefined,
    3206             :                         "Argument '%s' is mutually exclusive with '%s'.",
    3207          26 :                         arg->GetName().c_str(), oIter->second.c_str());
    3208             :                 }
    3209             :                 else
    3210             :                 {
    3211         756 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    3212        1512 :                         arg->GetName();
    3213             :                 }
    3214             :             }
    3215             : 
    3216       19563 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3217       19563 :             if (!mutualDependencyGroup.empty())
    3218             :             {
    3219          50 :                 if (mutualDependencyGroupUsed.find(mutualDependencyGroup) ==
    3220         100 :                     mutualDependencyGroupUsed.end())
    3221             :                 {
    3222          87 :                     mutualDependencyGroupUsed[mutualDependencyGroup] = {
    3223          87 :                         arg->GetName()};
    3224             :                 }
    3225             :                 else
    3226             :                 {
    3227          42 :                     mutualDependencyGroupUsed[mutualDependencyGroup].push_back(
    3228          21 :                         arg->GetName());
    3229             :                 }
    3230             :             }
    3231             : 
    3232             :             // Check direct dependencies
    3233       19575 :             for (const auto &dependency : arg->GetDirectDependencies())
    3234             :             {
    3235          12 :                 auto depArg = GetArg(dependency);
    3236          12 :                 if (!depArg)
    3237             :                 {
    3238           0 :                     ret = false;
    3239           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3240             :                                 "Argument '%s' depends on argument '%s' that "
    3241             :                                 "is not defined.",
    3242           0 :                                 arg->GetName().c_str(), dependency.c_str());
    3243             :                 }
    3244          12 :                 else if (!depArg->IsExplicitlySet())
    3245             :                 {
    3246           6 :                     ret = false;
    3247          12 :                     ReportError(CE_Failure, CPLE_AppDefined,
    3248             :                                 "Argument '%s' depends on argument '%s' that "
    3249             :                                 "has not been specified.",
    3250           6 :                                 arg->GetName().c_str(),
    3251           6 :                                 depArg->GetName().c_str());
    3252             :                 }
    3253             :             }
    3254             :         }
    3255             : 
    3256      122252 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    3257         154 :             !arg->HasDefaultValue())
    3258             :         {
    3259         154 :             bool emitError = true;
    3260         154 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3261         154 :             if (!mutualExclusionGroup.empty())
    3262             :             {
    3263        1765 :                 for (const auto &otherArg : m_args)
    3264             :                 {
    3265        1751 :                     if (otherArg->GetMutualExclusionGroup() ==
    3266        1856 :                             mutualExclusionGroup &&
    3267         105 :                         otherArg->IsExplicitlySet())
    3268             :                     {
    3269          74 :                         emitError = false;
    3270          74 :                         break;
    3271             :                     }
    3272             :                 }
    3273             :             }
    3274         232 :             if (emitError && !(m_inputDatasetCanBeOmitted &&
    3275          48 :                                arg->GetName() == GDAL_ARG_NAME_INPUT &&
    3276          60 :                                (arg->GetType() == GAAT_DATASET ||
    3277          30 :                                 arg->GetType() == GAAT_DATASET_LIST)))
    3278             :             {
    3279          50 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3280             :                             "Required argument '%s' has not been specified.",
    3281          50 :                             arg->GetName().c_str());
    3282          50 :                 ret = false;
    3283             :             }
    3284             :         }
    3285      121944 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    3286             :         {
    3287        4253 :             if (!ProcessDatasetArg(arg.get(), this))
    3288          51 :                 ret = false;
    3289             :         }
    3290             : 
    3291      122098 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST)
    3292             :         {
    3293        5522 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    3294        5522 :             if (listVal.size() == 1)
    3295             :             {
    3296        5368 :                 if (!ProcessDatasetArg(arg.get(), this))
    3297          39 :                     ret = false;
    3298             :             }
    3299             :             else
    3300             :             {
    3301         473 :                 for (auto &val : listVal)
    3302             :                 {
    3303         319 :                     if (val.GetDatasetRef())
    3304             :                     {
    3305         120 :                         if (!CheckCanSetDatasetObject(arg.get()))
    3306             :                         {
    3307           0 :                             ret = false;
    3308             :                         }
    3309         315 :                         continue;
    3310             :                     }
    3311             : 
    3312         199 :                     if (val.GetName().empty())
    3313             :                     {
    3314           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3315             :                                     "Argument '%s' has no dataset object or "
    3316             :                                     "dataset name.",
    3317           0 :                                     arg->GetName().c_str());
    3318           0 :                         ret = false;
    3319           0 :                         continue;
    3320             :                     }
    3321             : 
    3322         199 :                     auto oIter = m_oMapDatasetNameToDataset.find(val.GetName());
    3323         199 :                     if (oIter != m_oMapDatasetNameToDataset.end())
    3324             :                     {
    3325           2 :                         auto poDS = oIter->second;
    3326           2 :                         val.SetDatasetOpenedByAlgorithm();
    3327           2 :                         val.Set(poDS);
    3328           2 :                         m_oMapDatasetNameToDataset.erase(oIter);
    3329           2 :                         continue;
    3330             :                     }
    3331             : 
    3332         197 :                     if (!arg->AutoOpenDataset())
    3333         193 :                         continue;
    3334             : 
    3335           4 :                     int flags = arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
    3336             : 
    3337           8 :                     CPLStringList aosOpenOptions;
    3338           8 :                     CPLStringList aosAllowedDrivers;
    3339           4 :                     if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    3340             :                     {
    3341           4 :                         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    3342           4 :                         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    3343             :                         {
    3344           4 :                             aosOpenOptions = CPLStringList(
    3345           4 :                                 ooArg->Get<std::vector<std::string>>());
    3346             :                         }
    3347             : 
    3348           4 :                         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    3349           4 :                         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    3350             :                         {
    3351           4 :                             aosAllowedDrivers = CPLStringList(
    3352           4 :                                 ifArg->Get<std::vector<std::string>>());
    3353             :                         }
    3354             : 
    3355           4 :                         const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3356           4 :                         if (updateArg && updateArg->GetType() == GAAT_BOOLEAN &&
    3357           0 :                             updateArg->Get<bool>())
    3358             :                         {
    3359           0 :                             flags |= GDAL_OF_UPDATE;
    3360             :                         }
    3361             :                     }
    3362             : 
    3363             :                     auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    3364           4 :                         val.GetName().c_str(), flags, aosAllowedDrivers.List(),
    3365          12 :                         aosOpenOptions.List()));
    3366           4 :                     if (poDS)
    3367             :                     {
    3368           3 :                         val.Set(std::move(poDS));
    3369             :                     }
    3370             :                     else
    3371             :                     {
    3372           1 :                         ret = false;
    3373             :                     }
    3374             :                 }
    3375             :             }
    3376             :         }
    3377             : 
    3378      122098 :         if (arg->IsExplicitlySet() && !arg->RunValidationActions())
    3379             :         {
    3380           8 :             ret = false;
    3381             :         }
    3382             :     }
    3383             : 
    3384             :     // Check mutual dependency groups
    3385        6774 :     std::vector<std::string> processedGroups;
    3386             :     // Loop through group map and check there are not required args in the group that are not set
    3387        6803 :     for (const auto &[groupName, argNames] : mutualDependencyGroupUsed)
    3388             :     {
    3389          29 :         if (std::find(processedGroups.begin(), processedGroups.end(),
    3390          29 :                       groupName) != processedGroups.end())
    3391           0 :             continue;
    3392          58 :         std::vector<std::string> missingArgs;
    3393         562 :         for (auto &arg : m_args)
    3394             :         {
    3395         533 :             const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    3396         598 :             if (mutualDependencyGroup == groupName &&
    3397          65 :                 std::find(argNames.begin(), argNames.end(), arg->GetName()) ==
    3398         598 :                     argNames.end())
    3399             :             {
    3400          15 :                 missingArgs.push_back(arg->GetName());
    3401             :             }
    3402             :         }
    3403          29 :         if (!missingArgs.empty())
    3404             :         {
    3405          12 :             ret = false;
    3406          24 :             std::string missingArgsStr;
    3407          27 :             for (const auto &missingArg : missingArgs)
    3408             :             {
    3409          15 :                 if (!missingArgsStr.empty())
    3410           3 :                     missingArgsStr += ", ";
    3411          15 :                 missingArgsStr += missingArg;
    3412             :             }
    3413          24 :             std::string givenArgsStr;
    3414          27 :             for (const auto &givenArg : argNames)
    3415             :             {
    3416          15 :                 if (!givenArgsStr.empty())
    3417           3 :                     givenArgsStr += ", ";
    3418          15 :                 givenArgsStr += givenArg;
    3419             :             }
    3420          12 :             ReportError(CE_Failure, CPLE_AppDefined,
    3421             :                         "Argument(s) '%s' require(s) that the following "
    3422             :                         "argument(s) are also specified: %s.",
    3423             :                         givenArgsStr.c_str(), missingArgsStr.c_str());
    3424             :         }
    3425          29 :         processedGroups.push_back(groupName);
    3426             :     }
    3427             : 
    3428       29065 :     for (const auto &f : m_validationActions)
    3429             :     {
    3430       22291 :         if (!f())
    3431          80 :             ret = false;
    3432             :     }
    3433             : 
    3434        6774 :     return ret;
    3435             : }
    3436             : 
    3437             : /************************************************************************/
    3438             : /*                GDALAlgorithm::InstantiateSubAlgorithm                */
    3439             : /************************************************************************/
    3440             : 
    3441             : std::unique_ptr<GDALAlgorithm>
    3442        9975 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
    3443             :                                        bool suggestionAllowed) const
    3444             : {
    3445        9975 :     auto ret = m_subAlgRegistry.Instantiate(name);
    3446       19950 :     auto childCallPath = m_callPath;
    3447        9975 :     childCallPath.push_back(name);
    3448        9975 :     if (!ret)
    3449             :     {
    3450         916 :         ret = GDALGlobalAlgorithmRegistry::GetSingleton()
    3451         916 :                   .InstantiateDeclaredSubAlgorithm(childCallPath);
    3452             :     }
    3453        9975 :     if (ret)
    3454             :     {
    3455        9838 :         ret->SetCallPath(childCallPath);
    3456             :     }
    3457         137 :     else if (suggestionAllowed)
    3458             :     {
    3459          58 :         std::string bestCandidate;
    3460          29 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3461         470 :         for (const std::string &candidate : GetSubAlgorithmNames())
    3462             :         {
    3463             :             const size_t distance =
    3464         441 :                 CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
    3465             :                                        /* transpositionAllowed = */ true);
    3466         441 :             if (distance < bestDistance)
    3467             :             {
    3468          71 :                 bestCandidate = candidate;
    3469          71 :                 bestDistance = distance;
    3470             :             }
    3471         370 :             else if (distance == bestDistance)
    3472             :             {
    3473          45 :                 bestCandidate.clear();
    3474             :             }
    3475             :         }
    3476          29 :         if (!bestCandidate.empty() && bestDistance <= 2)
    3477             :         {
    3478           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3479             :                      "Algorithm '%s' is unknown. Do you mean '%s'?",
    3480             :                      name.c_str(), bestCandidate.c_str());
    3481             :         }
    3482             :     }
    3483       19950 :     return ret;
    3484             : }
    3485             : 
    3486             : /************************************************************************/
    3487             : /*            GDALAlgorithm::GetSuggestionForArgumentName()             */
    3488             : /************************************************************************/
    3489             : 
    3490             : std::string
    3491          37 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
    3492             : {
    3493          37 :     if (osName.size() >= 3)
    3494             :     {
    3495          34 :         std::string bestCandidate;
    3496          34 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3497         722 :         for (const auto &[key, value] : m_mapLongNameToArg)
    3498             :         {
    3499         688 :             CPL_IGNORE_RET_VAL(value);
    3500         688 :             const size_t distance = CPLLevenshteinDistance(
    3501             :                 osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
    3502         688 :             if (distance < bestDistance)
    3503             :             {
    3504          87 :                 bestCandidate = key;
    3505          87 :                 bestDistance = distance;
    3506             :             }
    3507         601 :             else if (distance == bestDistance)
    3508             :             {
    3509          80 :                 bestCandidate.clear();
    3510             :             }
    3511             :         }
    3512          47 :         if (!bestCandidate.empty() &&
    3513          13 :             bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
    3514             :         {
    3515           5 :             return bestCandidate;
    3516             :         }
    3517             :     }
    3518          32 :     return std::string();
    3519             : }
    3520             : 
    3521             : /************************************************************************/
    3522             : /*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
    3523             : /************************************************************************/
    3524             : 
    3525             : /* static */
    3526          22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
    3527             : {
    3528          66 :     return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
    3529          66 :            osName == GDAL_ARG_NAME_OVERWRITE ||
    3530          44 :            osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
    3531             : }
    3532             : 
    3533             : /************************************************************************/
    3534             : /*                   GDALAlgorithm::HasOutputString()                   */
    3535             : /************************************************************************/
    3536             : 
    3537          67 : bool GDALAlgorithm::HasOutputString() const
    3538             : {
    3539          67 :     auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
    3540          67 :     return outputStringArg && outputStringArg->IsOutput();
    3541             : }
    3542             : 
    3543             : /************************************************************************/
    3544             : /*                       GDALAlgorithm::GetArg()                        */
    3545             : /************************************************************************/
    3546             : 
    3547      464319 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
    3548             :                                         bool suggestionAllowed, bool isConst)
    3549             : {
    3550      464319 :     const auto nPos = osName.find_first_not_of('-');
    3551      464319 :     if (nPos == std::string::npos)
    3552          23 :         return nullptr;
    3553      928592 :     std::string osKey = osName.substr(nPos);
    3554             :     {
    3555      464296 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3556      464296 :         if (oIter != m_mapLongNameToArg.end())
    3557      432424 :             return oIter->second;
    3558             :     }
    3559             :     {
    3560       31872 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    3561       31872 :         if (oIter != m_mapShortNameToArg.end())
    3562           6 :             return oIter->second;
    3563             :     }
    3564             : 
    3565       31866 :     if (!isConst && m_arbitraryLongNameArgsAllowed)
    3566             :     {
    3567          22 :         const auto nDotPos = osKey.find('.');
    3568             :         const std::string osKeyEnd =
    3569          22 :             nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
    3570          22 :         if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
    3571             :         {
    3572             :             m_arbitraryLongNameArgsValuesBool.emplace_back(
    3573           0 :                 std::make_unique<bool>());
    3574           0 :             AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3575           0 :                    m_arbitraryLongNameArgsValuesBool.back().get())
    3576           0 :                 .SetUserProvided();
    3577             :         }
    3578             :         else
    3579             :         {
    3580          44 :             const std::string osKeyInit = osKey;
    3581          22 :             if (osKey == "oo")
    3582           0 :                 osKey = GDAL_ARG_NAME_OPEN_OPTION;
    3583          22 :             else if (osKey == "co")
    3584           0 :                 osKey = GDAL_ARG_NAME_CREATION_OPTION;
    3585          22 :             else if (osKey == "of")
    3586           0 :                 osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
    3587          22 :             else if (osKey == "if")
    3588           0 :                 osKey = GDAL_ARG_NAME_INPUT_FORMAT;
    3589             :             m_arbitraryLongNameArgsValuesStr.emplace_back(
    3590          22 :                 std::make_unique<std::string>());
    3591             :             auto &arg =
    3592          44 :                 AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3593          44 :                        m_arbitraryLongNameArgsValuesStr.back().get())
    3594          22 :                     .SetUserProvided();
    3595          22 :             if (osKey != osKeyInit)
    3596           0 :                 arg.AddAlias(osKeyInit);
    3597             :         }
    3598          22 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3599          22 :         CPLAssert(oIter != m_mapLongNameToArg.end());
    3600          22 :         return oIter->second;
    3601             :     }
    3602             : 
    3603       31844 :     if (suggestionAllowed)
    3604             :     {
    3605          14 :         const std::string bestCandidate = GetSuggestionForArgumentName(osName);
    3606           7 :         if (!bestCandidate.empty())
    3607             :         {
    3608           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3609             :                      "Argument '%s' is unknown. Do you mean '%s'?",
    3610             :                      osName.c_str(), bestCandidate.c_str());
    3611             :         }
    3612             :     }
    3613             : 
    3614       31844 :     return nullptr;
    3615             : }
    3616             : 
    3617             : /************************************************************************/
    3618             : /*                     GDALAlgorithm::AddAliasFor()                     */
    3619             : /************************************************************************/
    3620             : 
    3621             : //! @cond Doxygen_Suppress
    3622       80250 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    3623             :                                 const std::string &alias)
    3624             : {
    3625       80250 :     if (cpl::contains(m_mapLongNameToArg, alias))
    3626             :     {
    3627           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    3628             :                     alias.c_str());
    3629             :     }
    3630             :     else
    3631             :     {
    3632       80249 :         m_mapLongNameToArg[alias] = arg;
    3633             :     }
    3634       80250 : }
    3635             : 
    3636             : //! @endcond
    3637             : 
    3638             : /************************************************************************/
    3639             : /*                GDALAlgorithm::AddShortNameAliasFor()                 */
    3640             : /************************************************************************/
    3641             : 
    3642             : //! @cond Doxygen_Suppress
    3643          48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
    3644             :                                          char shortNameAlias)
    3645             : {
    3646          96 :     std::string alias;
    3647          48 :     alias += shortNameAlias;
    3648          48 :     if (cpl::contains(m_mapShortNameToArg, alias))
    3649             :     {
    3650           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    3651             :                     "Short name '%s' already declared.", alias.c_str());
    3652             :     }
    3653             :     else
    3654             :     {
    3655          48 :         m_mapShortNameToArg[alias] = arg;
    3656             :     }
    3657          48 : }
    3658             : 
    3659             : //! @endcond
    3660             : 
    3661             : /************************************************************************/
    3662             : /*                    GDALAlgorithm::SetPositional()                    */
    3663             : /************************************************************************/
    3664             : 
    3665             : //! @cond Doxygen_Suppress
    3666       21552 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    3667             : {
    3668       21552 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    3669             :                         arg) == m_positionalArgs.end());
    3670       21552 :     m_positionalArgs.push_back(arg);
    3671       21552 : }
    3672             : 
    3673             : //! @endcond
    3674             : 
    3675             : /************************************************************************/
    3676             : /*                  GDALAlgorithm::HasSubAlgorithms()                   */
    3677             : /************************************************************************/
    3678             : 
    3679       12640 : bool GDALAlgorithm::HasSubAlgorithms() const
    3680             : {
    3681       12640 :     if (!m_subAlgRegistry.empty())
    3682        3295 :         return true;
    3683        9345 :     return !GDALGlobalAlgorithmRegistry::GetSingleton()
    3684       18690 :                 .GetDeclaredSubAlgorithmNames(m_callPath)
    3685        9345 :                 .empty();
    3686             : }
    3687             : 
    3688             : /************************************************************************/
    3689             : /*                GDALAlgorithm::GetSubAlgorithmNames()                 */
    3690             : /************************************************************************/
    3691             : 
    3692        1330 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
    3693             : {
    3694        1330 :     std::vector<std::string> ret = m_subAlgRegistry.GetNames();
    3695        1330 :     const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
    3696        2660 :                            .GetDeclaredSubAlgorithmNames(m_callPath);
    3697        1330 :     ret.insert(ret.end(), other.begin(), other.end());
    3698        1330 :     if (!other.empty())
    3699         415 :         std::sort(ret.begin(), ret.end());
    3700        2660 :     return ret;
    3701             : }
    3702             : 
    3703             : /************************************************************************/
    3704             : /*                       GDALAlgorithm::AddArg()                        */
    3705             : /************************************************************************/
    3706             : 
    3707             : GDALInConstructionAlgorithmArg &
    3708      310105 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    3709             : {
    3710      310105 :     auto argRaw = arg.get();
    3711      310105 :     const auto &longName = argRaw->GetName();
    3712      310105 :     if (!longName.empty())
    3713             :     {
    3714      310092 :         if (longName[0] == '-')
    3715             :         {
    3716           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3717             :                         "Long name '%s' should not start with '-'",
    3718             :                         longName.c_str());
    3719             :         }
    3720      310092 :         if (longName.find('=') != std::string::npos)
    3721             :         {
    3722           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3723             :                         "Long name '%s' should not contain a '=' character",
    3724             :                         longName.c_str());
    3725             :         }
    3726      310092 :         if (cpl::contains(m_mapLongNameToArg, longName))
    3727             :         {
    3728           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3729             :                         "Long name '%s' already declared", longName.c_str());
    3730             :         }
    3731      310092 :         m_mapLongNameToArg[longName] = argRaw;
    3732             :     }
    3733      310105 :     const auto &shortName = argRaw->GetShortName();
    3734      310105 :     if (!shortName.empty())
    3735             :     {
    3736      149824 :         if (shortName.size() != 1 ||
    3737       74912 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    3738          65 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    3739           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    3740             :         {
    3741           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3742             :                         "Short name '%s' should be a single letter or digit",
    3743             :                         shortName.c_str());
    3744             :         }
    3745       74912 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    3746             :         {
    3747           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3748             :                         "Short name '%s' already declared", shortName.c_str());
    3749             :         }
    3750       74912 :         m_mapShortNameToArg[shortName] = argRaw;
    3751             :     }
    3752      310105 :     m_args.emplace_back(std::move(arg));
    3753             :     return *(
    3754      310105 :         cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    3755             : }
    3756             : 
    3757             : GDALInConstructionAlgorithmArg &
    3758      138407 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3759             :                       const std::string &helpMessage, bool *pValue)
    3760             : {
    3761      138407 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3762             :         this,
    3763      276814 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    3764      276814 :         pValue));
    3765             : }
    3766             : 
    3767             : GDALInConstructionAlgorithmArg &
    3768       50194 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3769             :                       const std::string &helpMessage, std::string *pValue)
    3770             : {
    3771       50194 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3772             :         this,
    3773      100388 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    3774      100388 :         pValue));
    3775             : }
    3776             : 
    3777             : GDALInConstructionAlgorithmArg &
    3778       12313 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3779             :                       const std::string &helpMessage, int *pValue)
    3780             : {
    3781       12313 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3782             :         this,
    3783       24626 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    3784       24626 :         pValue));
    3785             : }
    3786             : 
    3787             : GDALInConstructionAlgorithmArg &
    3788       10140 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3789             :                       const std::string &helpMessage, double *pValue)
    3790             : {
    3791       10140 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3792             :         this,
    3793       20280 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    3794       20280 :         pValue));
    3795             : }
    3796             : 
    3797             : GDALInConstructionAlgorithmArg &
    3798       11681 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3799             :                       const std::string &helpMessage,
    3800             :                       GDALArgDatasetValue *pValue, GDALArgDatasetType type)
    3801             : {
    3802       23362 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3803             :                            this,
    3804       23362 :                            GDALAlgorithmArgDecl(longName, chShortName,
    3805             :                                                 helpMessage, GAAT_DATASET),
    3806       11681 :                            pValue))
    3807       11681 :                     .SetDatasetType(type);
    3808       11681 :     pValue->SetOwnerArgument(&arg);
    3809       11681 :     return arg;
    3810             : }
    3811             : 
    3812             : GDALInConstructionAlgorithmArg &
    3813       66191 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3814             :                       const std::string &helpMessage,
    3815             :                       std::vector<std::string> *pValue)
    3816             : {
    3817       66191 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3818             :         this,
    3819      132382 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3820             :                              GAAT_STRING_LIST),
    3821      132382 :         pValue));
    3822             : }
    3823             : 
    3824             : GDALInConstructionAlgorithmArg &
    3825        2208 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3826             :                       const std::string &helpMessage, std::vector<int> *pValue)
    3827             : {
    3828        2208 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3829             :         this,
    3830        4416 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3831             :                              GAAT_INTEGER_LIST),
    3832        4416 :         pValue));
    3833             : }
    3834             : 
    3835             : GDALInConstructionAlgorithmArg &
    3836        5181 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3837             :                       const std::string &helpMessage,
    3838             :                       std::vector<double> *pValue)
    3839             : {
    3840        5181 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3841             :         this,
    3842       10362 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3843             :                              GAAT_REAL_LIST),
    3844       10362 :         pValue));
    3845             : }
    3846             : 
    3847             : GDALInConstructionAlgorithmArg &
    3848       13790 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3849             :                       const std::string &helpMessage,
    3850             :                       std::vector<GDALArgDatasetValue> *pValue,
    3851             :                       GDALArgDatasetType type)
    3852             : {
    3853       27580 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3854             :                       this,
    3855       27580 :                       GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3856             :                                            GAAT_DATASET_LIST),
    3857       13790 :                       pValue))
    3858       27580 :         .SetDatasetType(type);
    3859             : }
    3860             : 
    3861             : /************************************************************************/
    3862             : /*                            MsgOrDefault()                            */
    3863             : /************************************************************************/
    3864             : 
    3865      103724 : inline const char *MsgOrDefault(const char *helpMessage,
    3866             :                                 const char *defaultMessage)
    3867             : {
    3868      103724 :     return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
    3869             : }
    3870             : 
    3871             : /************************************************************************/
    3872             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
    3873             : /************************************************************************/
    3874             : 
    3875             : /* static */
    3876       16827 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
    3877             :     GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
    3878             : {
    3879             :     arg.SetAutoCompleteFunction(
    3880           7 :         [&arg,
    3881        2467 :          type](const std::string &currentValue) -> std::vector<std::string>
    3882             :         {
    3883          14 :             std::vector<std::string> oRet;
    3884             : 
    3885           7 :             if (arg.IsHidden())
    3886           0 :                 return oRet;
    3887             : 
    3888             :             {
    3889           7 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3890             :                 VSIStatBufL sStat;
    3891          10 :                 if (!currentValue.empty() && currentValue.back() != '/' &&
    3892           3 :                     VSIStatL(currentValue.c_str(), &sStat) == 0)
    3893             :                 {
    3894           0 :                     return oRet;
    3895             :                 }
    3896             :             }
    3897             : 
    3898           7 :             auto poDM = GetGDALDriverManager();
    3899          14 :             std::set<std::string> oExtensions;
    3900           7 :             if (type)
    3901             :             {
    3902        1374 :                 for (int i = 0; i < poDM->GetDriverCount(); ++i)
    3903             :                 {
    3904        1368 :                     auto poDriver = poDM->GetDriver(i);
    3905        3876 :                     if (((type & GDAL_OF_RASTER) != 0 &&
    3906        1140 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    3907         588 :                         ((type & GDAL_OF_VECTOR) != 0 &&
    3908        2873 :                          poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    3909         497 :                         ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    3910           0 :                          poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    3911             :                     {
    3912             :                         const char *pszExtensions =
    3913         871 :                             poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    3914         871 :                         if (pszExtensions)
    3915             :                         {
    3916             :                             const CPLStringList aosExts(
    3917        1154 :                                 CSLTokenizeString2(pszExtensions, " ", 0));
    3918        1303 :                             for (const char *pszExt : cpl::Iterate(aosExts))
    3919         726 :                                 oExtensions.insert(CPLString(pszExt).tolower());
    3920             :                         }
    3921             :                     }
    3922             :                 }
    3923             :             }
    3924             : 
    3925          14 :             std::string osDir;
    3926          14 :             const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
    3927          14 :             std::string osPrefix;
    3928           7 :             if (STARTS_WITH(currentValue.c_str(), "/vsi"))
    3929             :             {
    3930          79 :                 for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
    3931             :                 {
    3932          78 :                     if (STARTS_WITH(currentValue.c_str(), pszPrefix))
    3933             :                     {
    3934           2 :                         osPrefix = pszPrefix;
    3935           2 :                         break;
    3936             :                     }
    3937             :                 }
    3938           3 :                 if (osPrefix.empty())
    3939           1 :                     return aosVSIPrefixes;
    3940           2 :                 if (currentValue == osPrefix)
    3941           1 :                     osDir = osPrefix;
    3942             :             }
    3943           6 :             if (osDir.empty())
    3944             :             {
    3945           5 :                 osDir = CPLGetDirnameSafe(currentValue.c_str());
    3946           5 :                 if (!osPrefix.empty() && osDir.size() < osPrefix.size())
    3947           0 :                     osDir = std::move(osPrefix);
    3948             :             }
    3949             : 
    3950           6 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    3951          12 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    3952           6 :             if (currentValue.empty())
    3953           1 :                 osDir.clear();
    3954             :             const std::string currentFilename =
    3955          12 :                 CPLGetFilename(currentValue.c_str());
    3956           6 :             if (psDir)
    3957             :             {
    3958         444 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    3959             :                 {
    3960         439 :                     if ((currentFilename.empty() ||
    3961         219 :                          STARTS_WITH(psEntry->pszName,
    3962         221 :                                      currentFilename.c_str())) &&
    3963         221 :                         strcmp(psEntry->pszName, ".") != 0 &&
    3964        1319 :                         strcmp(psEntry->pszName, "..") != 0 &&
    3965         221 :                         (oExtensions.empty() ||
    3966         220 :                          !strstr(psEntry->pszName, ".aux.xml")))
    3967             :                     {
    3968         874 :                         if (oExtensions.empty() ||
    3969         218 :                             cpl::contains(
    3970             :                                 oExtensions,
    3971         437 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    3972         655 :                                     .tolower()) ||
    3973         186 :                             VSI_ISDIR(psEntry->nMode))
    3974             :                         {
    3975          74 :                             std::string osVal;
    3976          37 :                             if (osDir.empty() || osDir == ".")
    3977           4 :                                 osVal = psEntry->pszName;
    3978             :                             else
    3979          66 :                                 osVal = CPLFormFilenameSafe(
    3980          66 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    3981          37 :                             if (VSI_ISDIR(psEntry->nMode))
    3982           4 :                                 osVal += osSep;
    3983          37 :                             oRet.push_back(std::move(osVal));
    3984             :                         }
    3985             :                     }
    3986         439 :                 }
    3987           5 :                 VSICloseDir(psDir);
    3988             :             }
    3989           6 :             return oRet;
    3990       16827 :         });
    3991       16827 : }
    3992             : 
    3993             : /************************************************************************/
    3994             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    3995             : /************************************************************************/
    3996             : 
    3997         704 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    3998             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    3999             :     bool positionalAndRequired, const char *helpMessage)
    4000             : {
    4001             :     auto &arg = AddArg(
    4002             :         GDAL_ARG_NAME_INPUT, 'i',
    4003             :         MsgOrDefault(helpMessage,
    4004             :                      CPLSPrintf("Input %s dataset",
    4005         704 :                                 GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4006        1408 :         pValue, type);
    4007         704 :     if (positionalAndRequired)
    4008         697 :         arg.SetPositional().SetRequired();
    4009             : 
    4010         704 :     SetAutoCompleteFunctionForFilename(arg, type);
    4011             : 
    4012         704 :     AddValidationAction(
    4013         155 :         [pValue]()
    4014             :         {
    4015         154 :             if (pValue->GetName() == "-")
    4016           1 :                 pValue->Set("/vsistdin/");
    4017         154 :             return true;
    4018             :         });
    4019             : 
    4020         704 :     return arg;
    4021             : }
    4022             : 
    4023             : /************************************************************************/
    4024             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    4025             : /************************************************************************/
    4026             : 
    4027       13321 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    4028             :     std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
    4029             :     bool positionalAndRequired, const char *helpMessage)
    4030             : {
    4031             :     auto &arg =
    4032             :         AddArg(GDAL_ARG_NAME_INPUT, 'i',
    4033             :                MsgOrDefault(
    4034             :                    helpMessage,
    4035             :                    CPLSPrintf("Input %s datasets",
    4036       13321 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4037       39963 :                pValue, type)
    4038       13321 :             .SetPackedValuesAllowed(false);
    4039       13321 :     if (positionalAndRequired)
    4040        1722 :         arg.SetPositional().SetRequired();
    4041             : 
    4042       13321 :     SetAutoCompleteFunctionForFilename(arg, type);
    4043             : 
    4044       13321 :     AddValidationAction(
    4045        6227 :         [pValue]()
    4046             :         {
    4047       11840 :             for (auto &val : *pValue)
    4048             :             {
    4049        5613 :                 if (val.GetName() == "-")
    4050           1 :                     val.Set("/vsistdin/");
    4051             :             }
    4052        6227 :             return true;
    4053             :         });
    4054       13321 :     return arg;
    4055             : }
    4056             : 
    4057             : /************************************************************************/
    4058             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    4059             : /************************************************************************/
    4060             : 
    4061        8281 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
    4062             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    4063             :     bool positionalAndRequired, const char *helpMessage)
    4064             : {
    4065             :     auto &arg =
    4066             :         AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    4067             :                MsgOrDefault(
    4068             :                    helpMessage,
    4069             :                    CPLSPrintf("Output %s dataset",
    4070        8281 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    4071       24843 :                pValue, type)
    4072        8281 :             .SetIsInput(true)
    4073        8281 :             .SetIsOutput(true)
    4074        8281 :             .SetDatasetInputFlags(GADV_NAME)
    4075        8281 :             .SetDatasetOutputFlags(GADV_OBJECT);
    4076        8281 :     if (positionalAndRequired)
    4077        4332 :         arg.SetPositional().SetRequired();
    4078             : 
    4079        8281 :     AddValidationAction(
    4080       11835 :         [this, &arg, pValue]()
    4081             :         {
    4082        3633 :             if (pValue->GetName() == "-")
    4083           4 :                 pValue->Set("/vsistdout/");
    4084             : 
    4085        3633 :             auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    4086        3581 :             if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    4087        5768 :                 (!outputFormatArg->IsExplicitlySet() ||
    4088        9401 :                  outputFormatArg->Get<std::string>().empty()) &&
    4089        1394 :                 arg.IsExplicitlySet())
    4090             :             {
    4091             :                 const auto vrtCompatible =
    4092        1046 :                     outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4093         182 :                 if (vrtCompatible && !vrtCompatible->empty() &&
    4094        1228 :                     vrtCompatible->front() == "false" &&
    4095        1137 :                     EQUAL(
    4096             :                         CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
    4097             :                         "VRT"))
    4098             :                 {
    4099           6 :                     ReportError(
    4100             :                         CE_Failure, CPLE_NotSupported,
    4101             :                         "VRT output is not supported.%s",
    4102           6 :                         outputFormatArg->GetDescription().find("GDALG") !=
    4103             :                                 std::string::npos
    4104             :                             ? " Consider using the GDALG driver instead (files "
    4105             :                               "with .gdalg.json extension)"
    4106             :                             : "");
    4107           6 :                     return false;
    4108             :                 }
    4109        1040 :                 else if (pValue->GetName().size() > strlen(".gdalg.json") &&
    4110        2057 :                          EQUAL(pValue->GetName()
    4111             :                                    .substr(pValue->GetName().size() -
    4112             :                                            strlen(".gdalg.json"))
    4113             :                                    .c_str(),
    4114        3097 :                                ".gdalg.json") &&
    4115          27 :                          outputFormatArg->GetDescription().find("GDALG") ==
    4116             :                              std::string::npos)
    4117             :                 {
    4118           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4119             :                                 "GDALG output is not supported");
    4120           0 :                     return false;
    4121             :                 }
    4122             :             }
    4123        3627 :             return true;
    4124             :         });
    4125             : 
    4126        8281 :     return arg;
    4127             : }
    4128             : 
    4129             : /************************************************************************/
    4130             : /*                   GDALAlgorithm::AddOverwriteArg()                   */
    4131             : /************************************************************************/
    4132             : 
    4133             : GDALInConstructionAlgorithmArg &
    4134        8191 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
    4135             : {
    4136             :     return AddArg(
    4137             :                GDAL_ARG_NAME_OVERWRITE, 0,
    4138             :                MsgOrDefault(
    4139             :                    helpMessage,
    4140             :                    _("Whether overwriting existing output dataset is allowed")),
    4141       16382 :                pValue)
    4142       16382 :         .SetDefault(false);
    4143             : }
    4144             : 
    4145             : /************************************************************************/
    4146             : /*                GDALAlgorithm::AddOverwriteLayerArg()                 */
    4147             : /************************************************************************/
    4148             : 
    4149             : GDALInConstructionAlgorithmArg &
    4150        3424 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
    4151             : {
    4152        3424 :     AddValidationAction(
    4153        1554 :         [this]
    4154             :         {
    4155        1553 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4156        1553 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4157             :             {
    4158           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4159             :                             "--update argument must exist for "
    4160             :                             "--overwrite-layer, even if hidden");
    4161           1 :                 return false;
    4162             :             }
    4163        1552 :             return true;
    4164             :         });
    4165             :     return AddArg(
    4166             :                GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
    4167             :                MsgOrDefault(
    4168             :                    helpMessage,
    4169             :                    _("Whether overwriting existing output layer is allowed")),
    4170        6848 :                pValue)
    4171        3424 :         .SetDefault(false)
    4172             :         .AddAction(
    4173          19 :             [this]
    4174             :             {
    4175          19 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4176          19 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4177             :                 {
    4178          19 :                     updateArg->Set(true);
    4179             :                 }
    4180        6867 :             });
    4181             : }
    4182             : 
    4183             : /************************************************************************/
    4184             : /*                    GDALAlgorithm::AddUpdateArg()                     */
    4185             : /************************************************************************/
    4186             : 
    4187             : GDALInConstructionAlgorithmArg &
    4188        3976 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
    4189             : {
    4190             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    4191             :                   MsgOrDefault(
    4192             :                       helpMessage,
    4193             :                       _("Whether to open existing dataset in update mode")),
    4194        7952 :                   pValue)
    4195        7952 :         .SetDefault(false);
    4196             : }
    4197             : 
    4198             : /************************************************************************/
    4199             : /*                  GDALAlgorithm::AddAppendLayerArg()                  */
    4200             : /************************************************************************/
    4201             : 
    4202             : GDALInConstructionAlgorithmArg &
    4203        3198 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
    4204             : {
    4205        3198 :     AddValidationAction(
    4206        1509 :         [this]
    4207             :         {
    4208        1508 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4209        1508 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4210             :             {
    4211           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4212             :                             "--update argument must exist for --append, even "
    4213             :                             "if hidden");
    4214           1 :                 return false;
    4215             :             }
    4216        1507 :             return true;
    4217             :         });
    4218             :     return AddArg(GDAL_ARG_NAME_APPEND, 0,
    4219             :                   MsgOrDefault(
    4220             :                       helpMessage,
    4221             :                       _("Whether appending to existing layer is allowed")),
    4222        6396 :                   pValue)
    4223        3198 :         .SetDefault(false)
    4224             :         .AddAction(
    4225          25 :             [this]
    4226             :             {
    4227          25 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4228          25 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4229             :                 {
    4230          25 :                     updateArg->Set(true);
    4231             :                 }
    4232        6421 :             });
    4233             : }
    4234             : 
    4235             : /************************************************************************/
    4236             : /*                GDALAlgorithm::AddOptionsSuggestions()                */
    4237             : /************************************************************************/
    4238             : 
    4239             : /* static */
    4240          29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
    4241             :                                           const std::string &currentValue,
    4242             :                                           std::vector<std::string> &oRet)
    4243             : {
    4244          29 :     if (!pszXML)
    4245           0 :         return false;
    4246          58 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    4247          29 :     if (!poTree)
    4248           0 :         return false;
    4249             : 
    4250          58 :     std::string typedOptionName = currentValue;
    4251          29 :     const auto posEqual = typedOptionName.find('=');
    4252          58 :     std::string typedValue;
    4253          29 :     if (posEqual != 0 && posEqual != std::string::npos)
    4254             :     {
    4255           2 :         typedValue = currentValue.substr(posEqual + 1);
    4256           2 :         typedOptionName.resize(posEqual);
    4257             :     }
    4258             : 
    4259         405 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4260         376 :          psChild = psChild->psNext)
    4261             :     {
    4262         389 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4263         402 :         if (pszName && typedOptionName == pszName &&
    4264          13 :             (strcmp(psChild->pszValue, "Option") == 0 ||
    4265           2 :              strcmp(psChild->pszValue, "Argument") == 0))
    4266             :         {
    4267          13 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    4268          13 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    4269          13 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    4270          13 :             if (EQUAL(pszType, "string-select"))
    4271             :             {
    4272          90 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    4273          85 :                      psChild2 = psChild2->psNext)
    4274             :                 {
    4275          85 :                     if (EQUAL(psChild2->pszValue, "Value"))
    4276             :                     {
    4277          75 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    4278             :                     }
    4279             :                 }
    4280             :             }
    4281           8 :             else if (EQUAL(pszType, "boolean"))
    4282             :             {
    4283           3 :                 if (typedValue == "YES" || typedValue == "NO")
    4284             :                 {
    4285           1 :                     oRet.push_back(currentValue);
    4286           1 :                     return true;
    4287             :                 }
    4288           2 :                 oRet.push_back("NO");
    4289           2 :                 oRet.push_back("YES");
    4290             :             }
    4291           5 :             else if (EQUAL(pszType, "int"))
    4292             :             {
    4293           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    4294           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    4295             :                 {
    4296           1 :                     const int nMax = atoi(pszMax);
    4297          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    4298          12 :                         oRet.push_back(std::to_string(i));
    4299             :                 }
    4300             :             }
    4301             : 
    4302          12 :             if (oRet.empty())
    4303             :             {
    4304           4 :                 if (pszMin && pszMax)
    4305             :                 {
    4306           1 :                     oRet.push_back(std::string("##"));
    4307           2 :                     oRet.push_back(std::string("validity range: [")
    4308           1 :                                        .append(pszMin)
    4309           1 :                                        .append(",")
    4310           1 :                                        .append(pszMax)
    4311           1 :                                        .append("]"));
    4312             :                 }
    4313           3 :                 else if (pszMin)
    4314             :                 {
    4315           1 :                     oRet.push_back(std::string("##"));
    4316           1 :                     oRet.push_back(
    4317           1 :                         std::string("validity range: >= ").append(pszMin));
    4318             :                 }
    4319           2 :                 else if (pszMax)
    4320             :                 {
    4321           1 :                     oRet.push_back(std::string("##"));
    4322           1 :                     oRet.push_back(
    4323           1 :                         std::string("validity range: <= ").append(pszMax));
    4324             :                 }
    4325           1 :                 else if (const char *pszDescription =
    4326           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    4327             :                 {
    4328           1 :                     oRet.push_back(std::string("##"));
    4329           2 :                     oRet.push_back(std::string("type: ")
    4330           1 :                                        .append(pszType)
    4331           1 :                                        .append(", description: ")
    4332           1 :                                        .append(pszDescription));
    4333             :                 }
    4334             :             }
    4335             : 
    4336          12 :             return true;
    4337             :         }
    4338             :     }
    4339             : 
    4340         319 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4341         303 :          psChild = psChild->psNext)
    4342             :     {
    4343         303 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4344         303 :         if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
    4345           5 :                         strcmp(psChild->pszValue, "Argument") == 0))
    4346             :         {
    4347         300 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    4348         300 :             if (!pszScope ||
    4349          40 :                 (EQUAL(pszScope, "raster") &&
    4350          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    4351          20 :                 (EQUAL(pszScope, "vector") &&
    4352           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    4353             :             {
    4354         280 :                 oRet.push_back(std::string(pszName).append("="));
    4355             :             }
    4356             :         }
    4357             :     }
    4358             : 
    4359          16 :     return false;
    4360             : }
    4361             : 
    4362             : /************************************************************************/
    4363             : /*             GDALAlgorithm::OpenOptionCompleteFunction()              */
    4364             : /************************************************************************/
    4365             : 
    4366             : //! @cond Doxygen_Suppress
    4367             : std::vector<std::string>
    4368           2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
    4369             : {
    4370           2 :     std::vector<std::string> oRet;
    4371             : 
    4372           2 :     int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    4373           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4374           4 :     if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
    4375           2 :                      inputArg->GetType() == GAAT_DATASET_LIST))
    4376             :     {
    4377           2 :         datasetType = inputArg->GetDatasetType();
    4378             :     }
    4379             : 
    4380           2 :     auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4381           4 :     if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    4382           2 :         inputFormat->IsExplicitlySet())
    4383             :     {
    4384             :         const auto &aosAllowedDrivers =
    4385           1 :             inputFormat->Get<std::vector<std::string>>();
    4386           1 :         if (aosAllowedDrivers.size() == 1)
    4387             :         {
    4388           2 :             auto poDriver = GetGDALDriverManager()->GetDriverByName(
    4389           1 :                 aosAllowedDrivers[0].c_str());
    4390           1 :             if (poDriver)
    4391             :             {
    4392           1 :                 AddOptionsSuggestions(
    4393           1 :                     poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    4394             :                     datasetType, currentValue, oRet);
    4395             :             }
    4396           1 :             return oRet;
    4397             :         }
    4398             :     }
    4399             : 
    4400           1 :     const auto AddSuggestions = [datasetType, &currentValue,
    4401         373 :                                  &oRet](const GDALArgDatasetValue &datasetValue)
    4402             :     {
    4403           1 :         auto poDM = GetGDALDriverManager();
    4404             : 
    4405           1 :         const auto &osDSName = datasetValue.GetName();
    4406           1 :         const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    4407           1 :         if (!osExt.empty())
    4408             :         {
    4409           1 :             std::set<std::string> oVisitedExtensions;
    4410         229 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4411             :             {
    4412         228 :                 auto poDriver = poDM->GetDriver(i);
    4413         684 :                 if (((datasetType & GDAL_OF_RASTER) != 0 &&
    4414         228 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    4415          72 :                     ((datasetType & GDAL_OF_VECTOR) != 0 &&
    4416         456 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    4417          72 :                     ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    4418           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    4419             :                 {
    4420             :                     const char *pszExtensions =
    4421         156 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    4422         156 :                     if (pszExtensions)
    4423             :                     {
    4424             :                         const CPLStringList aosExts(
    4425         103 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    4426         227 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    4427             :                         {
    4428         128 :                             if (EQUAL(pszExt, osExt.c_str()) &&
    4429           3 :                                 !cpl::contains(oVisitedExtensions, pszExt))
    4430             :                             {
    4431           1 :                                 oVisitedExtensions.insert(pszExt);
    4432           1 :                                 if (AddOptionsSuggestions(
    4433             :                                         poDriver->GetMetadataItem(
    4434           1 :                                             GDAL_DMD_OPENOPTIONLIST),
    4435             :                                         datasetType, currentValue, oRet))
    4436             :                                 {
    4437           0 :                                     return;
    4438             :                                 }
    4439           1 :                                 break;
    4440             :                             }
    4441             :                         }
    4442             :                     }
    4443             :                 }
    4444             :             }
    4445             :         }
    4446           1 :     };
    4447             : 
    4448           1 :     if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4449             :     {
    4450           0 :         auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    4451           0 :         AddSuggestions(datasetValue);
    4452             :     }
    4453           1 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4454             :     {
    4455           1 :         auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4456           1 :         if (datasetValues.size() == 1)
    4457           1 :             AddSuggestions(datasetValues[0]);
    4458             :     }
    4459             : 
    4460           1 :     return oRet;
    4461             : }
    4462             : 
    4463             : //! @endcond
    4464             : 
    4465             : /************************************************************************/
    4466             : /*                  GDALAlgorithm::AddOpenOptionsArg()                  */
    4467             : /************************************************************************/
    4468             : 
    4469             : GDALInConstructionAlgorithmArg &
    4470        9126 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
    4471             :                                  const char *helpMessage)
    4472             : {
    4473             :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
    4474       18252 :                        MsgOrDefault(helpMessage, _("Open options")), pValue)
    4475       18252 :                     .AddAlias("oo")
    4476       18252 :                     .SetMetaVar("<KEY>=<VALUE>")
    4477        9126 :                     .SetPackedValuesAllowed(false)
    4478        9126 :                     .SetCategory(GAAC_ADVANCED);
    4479             : 
    4480          25 :     arg.AddValidationAction([this, &arg]()
    4481        9151 :                             { return ParseAndValidateKeyValue(arg); });
    4482             : 
    4483             :     arg.SetAutoCompleteFunction(
    4484           2 :         [this](const std::string &currentValue)
    4485        9128 :         { return OpenOptionCompleteFunction(currentValue); });
    4486             : 
    4487        9126 :     return arg;
    4488             : }
    4489             : 
    4490             : /************************************************************************/
    4491             : /*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
    4492             : /************************************************************************/
    4493             : 
    4494             : GDALInConstructionAlgorithmArg &
    4495        3195 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
    4496             :                                        const char *helpMessage)
    4497             : {
    4498             :     auto &arg =
    4499             :         AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
    4500        6390 :                MsgOrDefault(helpMessage, _("Output open options")), pValue)
    4501        6390 :             .AddAlias("output-oo")
    4502        6390 :             .SetMetaVar("<KEY>=<VALUE>")
    4503        3195 :             .SetPackedValuesAllowed(false)
    4504        3195 :             .SetCategory(GAAC_ADVANCED);
    4505             : 
    4506           0 :     arg.AddValidationAction([this, &arg]()
    4507        3195 :                             { return ParseAndValidateKeyValue(arg); });
    4508             : 
    4509             :     arg.SetAutoCompleteFunction(
    4510           0 :         [this](const std::string &currentValue)
    4511        3195 :         { return OpenOptionCompleteFunction(currentValue); });
    4512             : 
    4513        3195 :     return arg;
    4514             : }
    4515             : 
    4516             : /************************************************************************/
    4517             : /*                           ValidateFormat()                           */
    4518             : /************************************************************************/
    4519             : 
    4520        4693 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
    4521             :                                    bool bStreamAllowed,
    4522             :                                    bool bGDALGAllowed) const
    4523             : {
    4524        4693 :     if (arg.GetChoices().empty())
    4525             :     {
    4526             :         const auto Validate =
    4527       20205 :             [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
    4528             :         {
    4529        4588 :             if (const auto extraFormats =
    4530        4588 :                     arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4531             :             {
    4532          60 :                 for (const auto &extraFormat : *extraFormats)
    4533             :                 {
    4534          48 :                     if (EQUAL(val.c_str(), extraFormat.c_str()))
    4535          14 :                         return true;
    4536             :                 }
    4537             :             }
    4538             : 
    4539        4574 :             if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
    4540        1808 :                 return true;
    4541             : 
    4542        2772 :             if (EQUAL(val.c_str(), "GDALG") &&
    4543           6 :                 arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
    4544             :             {
    4545           2 :                 if (bGDALGAllowed)
    4546             :                 {
    4547           2 :                     return true;
    4548             :                 }
    4549             :                 else
    4550             :                 {
    4551           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4552             :                                 "GDALG output is not supported.");
    4553           0 :                     return false;
    4554             :                 }
    4555             :             }
    4556             : 
    4557             :             const auto vrtCompatible =
    4558        2764 :                 arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4559         540 :             if (vrtCompatible && !vrtCompatible->empty() &&
    4560        3304 :                 vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
    4561             :             {
    4562           7 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4563             :                             "VRT output is not supported.%s",
    4564             :                             bGDALGAllowed
    4565             :                                 ? " Consider using the GDALG driver instead "
    4566             :                                   "(files with .gdalg.json extension)."
    4567             :                                 : "");
    4568           7 :                 return false;
    4569             :             }
    4570             : 
    4571             :             const auto allowedFormats =
    4572        2757 :                 arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4573        2781 :             if (allowedFormats && !allowedFormats->empty() &&
    4574           0 :                 std::find(allowedFormats->begin(), allowedFormats->end(),
    4575        2781 :                           val) != allowedFormats->end())
    4576             :             {
    4577           9 :                 return true;
    4578             :             }
    4579             : 
    4580             :             const auto excludedFormats =
    4581        2748 :                 arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4582        2763 :             if (excludedFormats && !excludedFormats->empty() &&
    4583           0 :                 std::find(excludedFormats->begin(), excludedFormats->end(),
    4584        2763 :                           val) != excludedFormats->end())
    4585             :             {
    4586           0 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4587             :                             "%s output is not supported.", val.c_str());
    4588           0 :                 return false;
    4589             :             }
    4590             : 
    4591        2748 :             auto hDriver = GDALGetDriverByName(val.c_str());
    4592        2748 :             if (!hDriver)
    4593             :             {
    4594             :                 auto poMissingDriver =
    4595           4 :                     GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
    4596           4 :                 if (poMissingDriver)
    4597             :                 {
    4598             :                     const std::string msg =
    4599           0 :                         GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
    4600           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4601             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4602             :                                 "not found but is known. However plugin %s",
    4603           0 :                                 arg.GetName().c_str(), val.c_str(),
    4604             :                                 msg.c_str());
    4605             :                 }
    4606             :                 else
    4607             :                 {
    4608           8 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4609             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4610             :                                 "does not exist.",
    4611           4 :                                 arg.GetName().c_str(), val.c_str());
    4612             :                 }
    4613           4 :                 return false;
    4614             :             }
    4615             : 
    4616        2744 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4617        2744 :             if (caps)
    4618             :             {
    4619        8292 :                 for (const std::string &cap : *caps)
    4620             :                 {
    4621             :                     const char *pszVal =
    4622        5573 :                         GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
    4623        5573 :                     if (!(pszVal && pszVal[0]))
    4624             :                     {
    4625        1569 :                         if (cap == GDAL_DCAP_CREATECOPY &&
    4626           0 :                             std::find(caps->begin(), caps->end(),
    4627         783 :                                       GDAL_DCAP_RASTER) != caps->end() &&
    4628         783 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
    4629        1569 :                                                 nullptr) &&
    4630         783 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
    4631             :                                                 nullptr))
    4632             :                         {
    4633             :                             // if it supports Create, it supports CreateCopy
    4634             :                         }
    4635           3 :                         else if (cap == GDAL_DMD_EXTENSIONS)
    4636             :                         {
    4637           2 :                             ReportError(
    4638             :                                 CE_Failure, CPLE_AppDefined,
    4639             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4640             :                                 "does "
    4641             :                                 "not advertise any file format extension.",
    4642           1 :                                 arg.GetName().c_str(), val.c_str());
    4643           3 :                             return false;
    4644             :                         }
    4645             :                         else
    4646             :                         {
    4647           2 :                             if (cap == GDAL_DCAP_CREATE)
    4648             :                             {
    4649           1 :                                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4650           1 :                                 if (updateArg &&
    4651           2 :                                     updateArg->GetType() == GAAT_BOOLEAN &&
    4652           1 :                                     updateArg->IsExplicitlySet())
    4653             :                                 {
    4654           0 :                                     continue;
    4655             :                                 }
    4656             : 
    4657           2 :                                 ReportError(
    4658             :                                     CE_Failure, CPLE_AppDefined,
    4659             :                                     "Invalid value for argument '%s'. "
    4660             :                                     "Driver '%s' does not have write support.",
    4661           1 :                                     arg.GetName().c_str(), val.c_str());
    4662           1 :                                 return false;
    4663             :                             }
    4664             :                             else
    4665             :                             {
    4666           2 :                                 ReportError(
    4667             :                                     CE_Failure, CPLE_AppDefined,
    4668             :                                     "Invalid value for argument '%s'. Driver "
    4669             :                                     "'%s' "
    4670             :                                     "does "
    4671             :                                     "not expose the required '%s' capability.",
    4672           1 :                                     arg.GetName().c_str(), val.c_str(),
    4673             :                                     cap.c_str());
    4674           1 :                                 return false;
    4675             :                             }
    4676             :                         }
    4677             :                     }
    4678             :                 }
    4679             :             }
    4680        2741 :             return true;
    4681        4591 :         };
    4682             : 
    4683        4591 :         if (arg.GetType() == GAAT_STRING)
    4684             :         {
    4685        4581 :             return Validate(arg.Get<std::string>());
    4686             :         }
    4687          12 :         else if (arg.GetType() == GAAT_STRING_LIST)
    4688             :         {
    4689          19 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    4690             :             {
    4691           9 :                 if (!Validate(val))
    4692           2 :                     return false;
    4693             :             }
    4694             :         }
    4695             :     }
    4696             : 
    4697         112 :     return true;
    4698             : }
    4699             : 
    4700             : /************************************************************************/
    4701             : /*                     FormatAutoCompleteFunction()                     */
    4702             : /************************************************************************/
    4703             : 
    4704             : /* static */
    4705           7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
    4706             :     const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
    4707             : {
    4708           7 :     std::vector<std::string> res;
    4709           7 :     auto poDM = GetGDALDriverManager();
    4710           7 :     const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4711           7 :     const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4712           7 :     const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4713           7 :     const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4714           7 :     if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4715           0 :         res = std::move(*extraFormats);
    4716        1602 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4717             :     {
    4718        1595 :         auto poDriver = poDM->GetDriver(i);
    4719             : 
    4720           0 :         if (vrtCompatible && !vrtCompatible->empty() &&
    4721        1595 :             vrtCompatible->front() == "false" &&
    4722           0 :             EQUAL(poDriver->GetDescription(), "VRT"))
    4723             :         {
    4724             :             // do nothing
    4725             :         }
    4726        1595 :         else if (allowedFormats && !allowedFormats->empty() &&
    4727           0 :                  std::find(allowedFormats->begin(), allowedFormats->end(),
    4728        1595 :                            poDriver->GetDescription()) != allowedFormats->end())
    4729             :         {
    4730           0 :             res.push_back(poDriver->GetDescription());
    4731             :         }
    4732        1595 :         else if (excludedFormats && !excludedFormats->empty() &&
    4733           0 :                  std::find(excludedFormats->begin(), excludedFormats->end(),
    4734           0 :                            poDriver->GetDescription()) !=
    4735        1595 :                      excludedFormats->end())
    4736             :         {
    4737           0 :             continue;
    4738             :         }
    4739        1595 :         else if (caps)
    4740             :         {
    4741        1595 :             bool ok = true;
    4742        3155 :             for (const std::string &cap : *caps)
    4743             :             {
    4744        2374 :                 if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
    4745             :                 {
    4746           0 :                     if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    4747           0 :                         !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
    4748             :                     {
    4749           0 :                         ok = false;
    4750           0 :                         break;
    4751             :                     }
    4752             :                 }
    4753        2374 :                 else if (const char *pszVal =
    4754        2374 :                              poDriver->GetMetadataItem(cap.c_str());
    4755        1488 :                          pszVal && pszVal[0])
    4756             :                 {
    4757             :                 }
    4758        1274 :                 else if (cap == GDAL_DCAP_CREATECOPY &&
    4759           0 :                          (std::find(caps->begin(), caps->end(),
    4760         388 :                                     GDAL_DCAP_RASTER) != caps->end() &&
    4761        1662 :                           poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
    4762         388 :                          poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    4763             :                 {
    4764             :                     // if it supports Create, it supports CreateCopy
    4765             :                 }
    4766             :                 else
    4767             :                 {
    4768         814 :                     ok = false;
    4769         814 :                     break;
    4770             :                 }
    4771             :             }
    4772        1595 :             if (ok)
    4773             :             {
    4774         781 :                 res.push_back(poDriver->GetDescription());
    4775             :             }
    4776             :         }
    4777             :     }
    4778           7 :     if (bGDALGAllowed)
    4779           4 :         res.push_back("GDALG");
    4780           7 :     return res;
    4781             : }
    4782             : 
    4783             : /************************************************************************/
    4784             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    4785             : /************************************************************************/
    4786             : 
    4787             : GDALInConstructionAlgorithmArg &
    4788        8864 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
    4789             :                                   const char *helpMessage)
    4790             : {
    4791             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
    4792       17728 :                        MsgOrDefault(helpMessage, _("Input formats")), pValue)
    4793       17728 :                     .AddAlias("if")
    4794        8864 :                     .SetCategory(GAAC_ADVANCED);
    4795          12 :     arg.AddValidationAction([this, &arg]()
    4796        8876 :                             { return ValidateFormat(arg, false, false); });
    4797             :     arg.SetAutoCompleteFunction(
    4798           1 :         [&arg](const std::string &)
    4799        8865 :         { return FormatAutoCompleteFunction(arg, false, false); });
    4800        8864 :     return arg;
    4801             : }
    4802             : 
    4803             : /************************************************************************/
    4804             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    4805             : /************************************************************************/
    4806             : 
    4807             : GDALInConstructionAlgorithmArg &
    4808        9284 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
    4809             :                                   bool bGDALGAllowed, const char *helpMessage)
    4810             : {
    4811             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
    4812             :                        MsgOrDefault(helpMessage,
    4813             :                                     bGDALGAllowed
    4814             :                                         ? _("Output format (\"GDALG\" allowed)")
    4815             :                                         : _("Output format")),
    4816       18568 :                        pValue)
    4817       18568 :                     .AddAlias("of")
    4818        9284 :                     .AddAlias("format");
    4819             :     arg.AddValidationAction(
    4820        4677 :         [this, &arg, bStreamAllowed, bGDALGAllowed]()
    4821       13961 :         { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
    4822             :     arg.SetAutoCompleteFunction(
    4823           4 :         [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
    4824             :         {
    4825             :             return FormatAutoCompleteFunction(arg, bStreamAllowed,
    4826           4 :                                               bGDALGAllowed);
    4827        9284 :         });
    4828        9284 :     return arg;
    4829             : }
    4830             : 
    4831             : /************************************************************************/
    4832             : /*                GDALAlgorithm::AddOutputDataTypeArg()                 */
    4833             : /************************************************************************/
    4834             : GDALInConstructionAlgorithmArg &
    4835        1739 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
    4836             :                                     const char *helpMessage)
    4837             : {
    4838             :     auto &arg =
    4839             :         AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
    4840        3478 :                MsgOrDefault(helpMessage, _("Output data type")), pValue)
    4841        3478 :             .AddAlias("ot")
    4842        3478 :             .AddAlias("datatype")
    4843        5217 :             .AddMetadataItem("type", {"GDALDataType"})
    4844             :             .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
    4845             :                         "UInt64", "Int64", "CInt16", "CInt32", "Float16",
    4846        1739 :                         "Float32", "Float64", "CFloat32", "CFloat64")
    4847        1739 :             .SetHiddenChoices("Byte");
    4848        1739 :     return arg;
    4849             : }
    4850             : 
    4851             : /************************************************************************/
    4852             : /*                    GDALAlgorithm::AddNodataArg()                     */
    4853             : /************************************************************************/
    4854             : 
    4855             : GDALInConstructionAlgorithmArg &
    4856         655 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
    4857             :                             const std::string &optionName,
    4858             :                             const char *helpMessage)
    4859             : {
    4860             :     auto &arg = AddArg(
    4861             :         optionName, 0,
    4862             :         MsgOrDefault(helpMessage,
    4863             :                      noneAllowed
    4864             :                          ? _("Assign a specified nodata value to output bands "
    4865             :                              "('none', numeric value, 'nan', 'inf', '-inf')")
    4866             :                          : _("Assign a specified nodata value to output bands "
    4867             :                              "(numeric value, 'nan', 'inf', '-inf')")),
    4868         655 :         pValue);
    4869             :     arg.AddValidationAction(
    4870         356 :         [this, pValue, noneAllowed, optionName]()
    4871             :         {
    4872          77 :             if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
    4873             :             {
    4874          67 :                 char *endptr = nullptr;
    4875          67 :                 CPLStrtod(pValue->c_str(), &endptr);
    4876          67 :                 if (endptr != pValue->c_str() + pValue->size())
    4877             :                 {
    4878           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    4879             :                                 "Value of '%s' should be %sa "
    4880             :                                 "numeric value, 'nan', 'inf' or '-inf'",
    4881             :                                 optionName.c_str(),
    4882             :                                 noneAllowed ? "'none', " : "");
    4883           1 :                     return false;
    4884             :                 }
    4885             :             }
    4886          76 :             return true;
    4887         655 :         });
    4888         655 :     return arg;
    4889             : }
    4890             : 
    4891             : /************************************************************************/
    4892             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    4893             : /************************************************************************/
    4894             : 
    4895             : GDALInConstructionAlgorithmArg &
    4896        5937 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
    4897             : {
    4898             :     return AddArg(
    4899             :                GDAL_ARG_NAME_OUTPUT_STRING, 0,
    4900             :                MsgOrDefault(helpMessage,
    4901             :                             _("Output string, in which the result is placed")),
    4902       11874 :                pValue)
    4903        5937 :         .SetHiddenForCLI()
    4904        5937 :         .SetIsInput(false)
    4905       11874 :         .SetIsOutput(true);
    4906             : }
    4907             : 
    4908             : /************************************************************************/
    4909             : /*                    GDALAlgorithm::AddStdoutArg()                     */
    4910             : /************************************************************************/
    4911             : 
    4912             : GDALInConstructionAlgorithmArg &
    4913        1477 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
    4914             : {
    4915             :     return AddArg(GDAL_ARG_NAME_STDOUT, 0,
    4916             :                   MsgOrDefault(helpMessage,
    4917             :                                _("Directly output on stdout. If enabled, "
    4918             :                                  "output-string will be empty")),
    4919        2954 :                   pValue)
    4920        2954 :         .SetHidden();
    4921             : }
    4922             : 
    4923             : /************************************************************************/
    4924             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    4925             : /************************************************************************/
    4926             : 
    4927             : GDALInConstructionAlgorithmArg &
    4928         208 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
    4929             : {
    4930             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    4931         208 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    4932             : }
    4933             : 
    4934             : /************************************************************************/
    4935             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4936             : /************************************************************************/
    4937             : 
    4938             : GDALInConstructionAlgorithmArg &
    4939          50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
    4940             : {
    4941             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
    4942         100 :                   pValue)
    4943           2 :         .SetAutoCompleteFunction([this](const std::string &)
    4944         102 :                                  { return AutoCompleteArrayName(); });
    4945             : }
    4946             : 
    4947             : /************************************************************************/
    4948             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4949             : /************************************************************************/
    4950             : 
    4951             : GDALInConstructionAlgorithmArg &
    4952          76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
    4953             :                                const char *helpMessage)
    4954             : {
    4955             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
    4956         152 :                   pValue)
    4957           0 :         .SetAutoCompleteFunction([this](const std::string &)
    4958         152 :                                  { return AutoCompleteArrayName(); });
    4959             : }
    4960             : 
    4961             : /************************************************************************/
    4962             : /*                GDALAlgorithm::AutoCompleteArrayName()                */
    4963             : /************************************************************************/
    4964             : 
    4965           2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
    4966             : {
    4967           2 :     std::vector<std::string> ret;
    4968           4 :     std::string osDSName;
    4969           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4970           2 :     if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4971             :     {
    4972           0 :         auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4973           0 :         if (!inputDatasets.empty())
    4974             :         {
    4975           0 :             osDSName = inputDatasets[0].GetName();
    4976             :         }
    4977             :     }
    4978           2 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4979             :     {
    4980           2 :         auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
    4981           2 :         osDSName = inputDataset.GetName();
    4982             :     }
    4983             : 
    4984           2 :     if (!osDSName.empty())
    4985             :     {
    4986           4 :         CPLStringList aosAllowedDrivers;
    4987           2 :         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4988           2 :         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    4989             :             aosAllowedDrivers =
    4990           2 :                 CPLStringList(ifArg->Get<std::vector<std::string>>());
    4991             : 
    4992           4 :         CPLStringList aosOpenOptions;
    4993           2 :         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    4994           2 :         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    4995             :             aosOpenOptions =
    4996           2 :                 CPLStringList(ooArg->Get<std::vector<std::string>>());
    4997             : 
    4998           2 :         if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    4999             :                 osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
    5000           4 :                 aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
    5001             :         {
    5002           2 :             if (auto poRG = poDS->GetRootGroup())
    5003             :             {
    5004           1 :                 ret = poRG->GetMDArrayFullNamesRecursive();
    5005             :             }
    5006             :         }
    5007             :     }
    5008             : 
    5009           4 :     return ret;
    5010             : }
    5011             : 
    5012             : /************************************************************************/
    5013             : /*                  GDALAlgorithm::AddMemorySizeArg()                   */
    5014             : /************************************************************************/
    5015             : 
    5016             : GDALInConstructionAlgorithmArg &
    5017         224 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
    5018             :                                 const std::string &optionName,
    5019             :                                 const char *helpMessage)
    5020             : {
    5021         448 :     return AddArg(optionName, 0, helpMessage, pStrValue)
    5022         224 :         .SetDefault(*pStrValue)
    5023             :         .AddValidationAction(
    5024         139 :             [this, pValue, pStrValue]()
    5025             :             {
    5026          47 :                 CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
    5027             :                 GIntBig nBytes;
    5028             :                 bool bUnitSpecified;
    5029          47 :                 if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
    5030          47 :                                        &bUnitSpecified) != CE_None)
    5031             :                 {
    5032           2 :                     return false;
    5033             :                 }
    5034          45 :                 if (!bUnitSpecified)
    5035             :                 {
    5036           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5037             :                                 "Memory size must have a unit or be a "
    5038             :                                 "percentage of usable RAM (2GB, 5%%, etc.)");
    5039           1 :                     return false;
    5040             :                 }
    5041             :                 if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
    5042             :                 {
    5043             :                     // -1 to please CoverityScan
    5044             :                     if (static_cast<std::uint64_t>(nBytes) >
    5045             :                         std::numeric_limits<size_t>::max() - 1U)
    5046             :                     {
    5047             :                         ReportError(CE_Failure, CPLE_AppDefined,
    5048             :                                     "Memory size %s is too large.",
    5049             :                                     pStrValue->c_str());
    5050             :                         return false;
    5051             :                     }
    5052             :                 }
    5053             : 
    5054          44 :                 *pValue = static_cast<size_t>(nBytes);
    5055          44 :                 return true;
    5056         448 :             });
    5057             : }
    5058             : 
    5059             : /************************************************************************/
    5060             : /*                GDALAlgorithm::AddOutputLayerNameArg()                */
    5061             : /************************************************************************/
    5062             : 
    5063             : GDALInConstructionAlgorithmArg &
    5064         474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
    5065             :                                      const char *helpMessage)
    5066             : {
    5067             :     return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
    5068         474 :                   MsgOrDefault(helpMessage, _("Output layer name")), pValue);
    5069             : }
    5070             : 
    5071             : /************************************************************************/
    5072             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    5073             : /************************************************************************/
    5074             : 
    5075             : GDALInConstructionAlgorithmArg &
    5076         882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
    5077             :                                const char *helpMessage)
    5078             : {
    5079             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    5080         882 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    5081             : }
    5082             : 
    5083             : /************************************************************************/
    5084             : /*                 GDALAlgorithm::AddGeometryTypeArg()                  */
    5085             : /************************************************************************/
    5086             : 
    5087             : GDALInConstructionAlgorithmArg &
    5088         422 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
    5089             : {
    5090             :     return AddArg("geometry-type", 0,
    5091         844 :                   MsgOrDefault(helpMessage, _("Geometry type")), pValue)
    5092             :         .SetAutoCompleteFunction(
    5093           3 :             [](const std::string &currentValue)
    5094             :             {
    5095           3 :                 std::vector<std::string> oRet;
    5096          51 :                 for (const char *type :
    5097             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
    5098             :                       "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
    5099             :                       "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
    5100             :                       "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
    5101          54 :                       "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
    5102             :                 {
    5103          68 :                     if (currentValue.empty() ||
    5104          17 :                         STARTS_WITH(type, currentValue.c_str()))
    5105             :                     {
    5106          35 :                         oRet.push_back(type);
    5107          35 :                         oRet.push_back(std::string(type).append("Z"));
    5108          35 :                         oRet.push_back(std::string(type).append("M"));
    5109          35 :                         oRet.push_back(std::string(type).append("ZM"));
    5110             :                     }
    5111             :                 }
    5112           3 :                 return oRet;
    5113         844 :             })
    5114             :         .AddValidationAction(
    5115         118 :             [this, pValue]()
    5116             :             {
    5117         107 :                 if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
    5118         115 :                         wkbUnknown &&
    5119           8 :                     !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
    5120             :                 {
    5121           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5122             :                                 "Invalid geometry type '%s'", pValue->c_str());
    5123           3 :                     return false;
    5124             :                 }
    5125         104 :                 return true;
    5126         844 :             });
    5127             : }
    5128             : 
    5129             : /************************************************************************/
    5130             : /*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
    5131             : /************************************************************************/
    5132             : 
    5133             : /* static */
    5134        2917 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
    5135             :     GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
    5136             : {
    5137        2917 :     CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
    5138             :               datasetArg.GetType() == GAAT_DATASET_LIST);
    5139             : 
    5140             :     layerArg.SetAutoCompleteFunction(
    5141          18 :         [&datasetArg](const std::string &currentValue)
    5142             :         {
    5143           6 :             std::vector<std::string> ret;
    5144          12 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5145           6 :             GDALArgDatasetValue *dsVal = nullptr;
    5146           6 :             if (datasetArg.GetType() == GAAT_DATASET)
    5147             :             {
    5148           0 :                 dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
    5149             :             }
    5150             :             else
    5151             :             {
    5152           6 :                 auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
    5153           6 :                 if (val.size() == 1)
    5154             :                 {
    5155           6 :                     dsVal = &val[0];
    5156             :                 }
    5157             :             }
    5158           6 :             if (dsVal && !dsVal->GetName().empty())
    5159             :             {
    5160             :                 auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    5161          12 :                     dsVal->GetName().c_str(), GDAL_OF_VECTOR));
    5162           6 :                 if (poDS)
    5163             :                 {
    5164          12 :                     for (auto &&poLayer : poDS->GetLayers())
    5165             :                     {
    5166           6 :                         if (currentValue == poLayer->GetDescription())
    5167             :                         {
    5168           1 :                             ret.clear();
    5169           1 :                             ret.push_back(poLayer->GetDescription());
    5170           1 :                             break;
    5171             :                         }
    5172           5 :                         ret.push_back(poLayer->GetDescription());
    5173             :                     }
    5174             :                 }
    5175             :             }
    5176          12 :             return ret;
    5177        2917 :         });
    5178        2917 : }
    5179             : 
    5180             : /************************************************************************/
    5181             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
    5182             : /************************************************************************/
    5183             : 
    5184         131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
    5185             :     GDALInConstructionAlgorithmArg &fieldArg,
    5186             :     GDALInConstructionAlgorithmArg &layerNameArg,
    5187             :     std::vector<GDALArgDatasetValue> &datasetArg)
    5188             : {
    5189             : 
    5190             :     fieldArg.SetAutoCompleteFunction(
    5191          12 :         [&datasetArg, &layerNameArg](const std::string &currentValue)
    5192             :         {
    5193           8 :             std::set<std::string> ret;
    5194           4 :             if (!datasetArg.empty())
    5195             :             {
    5196           4 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5197             : 
    5198          14 :                 auto getLayerFields = [&ret, &currentValue](OGRLayer *poLayer)
    5199             :                 {
    5200           2 :                     auto poDefn = poLayer->GetLayerDefn();
    5201           2 :                     const int nFieldCount = poDefn->GetFieldCount();
    5202           8 :                     for (int iField = 0; iField < nFieldCount; iField++)
    5203             :                     {
    5204             :                         const char *fieldName =
    5205           6 :                             poDefn->GetFieldDefn(iField)->GetNameRef();
    5206           6 :                         if (currentValue == fieldName)
    5207             :                         {
    5208           0 :                             ret.clear();
    5209           0 :                             ret.insert(fieldName);
    5210           0 :                             break;
    5211             :                         }
    5212           6 :                         ret.insert(fieldName);
    5213             :                     }
    5214           2 :                 };
    5215             : 
    5216           2 :                 GDALArgDatasetValue &dsVal = datasetArg[0];
    5217             : 
    5218           2 :                 if (!dsVal.GetName().empty())
    5219             :                 {
    5220             :                     auto poDS = std::unique_ptr<GDALDataset>(
    5221           2 :                         GDALDataset::Open(dsVal.GetName().c_str(),
    5222           4 :                                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
    5223           2 :                     if (poDS)
    5224             :                     {
    5225           2 :                         const auto &layerName = layerNameArg.Get<std::string>();
    5226           2 :                         if (layerName.empty())
    5227             :                         {
    5228             :                             // Loop through all layers
    5229           4 :                             for (auto &&poLayer : poDS->GetLayers())
    5230             :                             {
    5231           2 :                                 getLayerFields(poLayer);
    5232             :                             }
    5233             :                         }
    5234             :                         else
    5235             :                         {
    5236           0 :                             const auto poLayer = poDS->GetLayerByName(
    5237           0 :                                 layerNameArg.Get<std::string>().c_str());
    5238           0 :                             if (poLayer)
    5239             :                             {
    5240           0 :                                 getLayerFields(poLayer);
    5241             :                             }
    5242             :                         }
    5243             :                     }
    5244             :                 }
    5245             :             }
    5246           4 :             std::vector<std::string> retVector(ret.begin(), ret.end());
    5247           8 :             return retVector;
    5248         131 :         });
    5249         131 : }
    5250             : 
    5251             : /************************************************************************/
    5252             : /*                   GDALAlgorithm::AddFieldNameArg()                   */
    5253             : /************************************************************************/
    5254             : 
    5255             : GDALInConstructionAlgorithmArg &
    5256         131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
    5257             : {
    5258             :     return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
    5259         131 :                   pValue);
    5260             : }
    5261             : 
    5262             : /************************************************************************/
    5263             : /*                GDALAlgorithm::ParseFieldDefinition()                 */
    5264             : /************************************************************************/
    5265          67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
    5266             :                                          OGRFieldDefn *poFieldDefn,
    5267             :                                          std::string *posError)
    5268             : {
    5269             :     static const std::regex re(
    5270          67 :         R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
    5271         134 :     std::smatch match;
    5272          67 :     if (std::regex_match(posStrDef, match, re))
    5273             :     {
    5274         132 :         const std::string name = match[1];
    5275         132 :         const std::string type = match[2];
    5276          66 :         const int width = match[3].matched ? std::stoi(match[3]) : 0;
    5277          66 :         const int precision = match[4].matched ? std::stoi(match[4]) : 0;
    5278          66 :         poFieldDefn->SetName(name.c_str());
    5279             : 
    5280          66 :         const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
    5281          66 :         if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
    5282             :         {
    5283           1 :             if (posError)
    5284           1 :                 *posError = "Unsupported field type: " + type;
    5285             : 
    5286           1 :             return false;
    5287             :         }
    5288          65 :         poFieldDefn->SetType(typeEnum);
    5289          65 :         poFieldDefn->SetWidth(width);
    5290          65 :         poFieldDefn->SetPrecision(precision);
    5291          65 :         return true;
    5292             :     }
    5293             : 
    5294           1 :     if (posError)
    5295             :         *posError = "Invalid field definition format. Expected "
    5296           1 :                     "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
    5297             : 
    5298           1 :     return false;
    5299             : }
    5300             : 
    5301             : /************************************************************************/
    5302             : /*                GDALAlgorithm::AddFieldDefinitionArg()                */
    5303             : /************************************************************************/
    5304             : 
    5305             : GDALInConstructionAlgorithmArg &
    5306         129 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
    5307             :                                      std::vector<OGRFieldDefn> *pFieldDefns,
    5308             :                                      const char *helpMessage)
    5309             : {
    5310             :     auto &arg =
    5311             :         AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
    5312         258 :                pValues)
    5313         258 :             .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
    5314         129 :             .SetPackedValuesAllowed(true)
    5315         129 :             .SetRepeatedArgAllowed(true);
    5316             : 
    5317         132 :     auto validationFunction = [this, pFieldDefns, pValues]()
    5318             :     {
    5319          65 :         pFieldDefns->clear();
    5320         130 :         for (const auto &strValue : *pValues)
    5321             :         {
    5322          67 :             OGRFieldDefn fieldDefn("", OFTString);
    5323          67 :             std::string error;
    5324          67 :             if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
    5325             :                                                      &error))
    5326             :             {
    5327           2 :                 ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
    5328           2 :                 return false;
    5329             :             }
    5330             :             // Check uniqueness of field names
    5331          67 :             for (const auto &existingFieldDefn : *pFieldDefns)
    5332             :             {
    5333           2 :                 if (EQUAL(existingFieldDefn.GetNameRef(),
    5334             :                           fieldDefn.GetNameRef()))
    5335             :                 {
    5336           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5337             :                                 "Duplicate field name: '%s'",
    5338             :                                 fieldDefn.GetNameRef());
    5339           0 :                     return false;
    5340             :                 }
    5341             :             }
    5342          65 :             pFieldDefns->push_back(fieldDefn);
    5343             :         }
    5344          63 :         return true;
    5345         129 :     };
    5346             : 
    5347         129 :     arg.AddValidationAction(std::move(validationFunction));
    5348             : 
    5349         129 :     return arg;
    5350             : }
    5351             : 
    5352             : /************************************************************************/
    5353             : /*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
    5354             : /************************************************************************/
    5355             : 
    5356         262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
    5357             :     OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
    5358             :     std::string *pStrValue, const std::string &argName, const char *helpMessage)
    5359             : {
    5360             :     auto &arg =
    5361         524 :         AddArg(argName.empty() ? std::string("field-type") : argName, 0,
    5362         786 :                MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
    5363             :             .SetAutoCompleteFunction(
    5364           1 :                 [](const std::string &currentValue)
    5365             :                 {
    5366           1 :                     std::vector<std::string> oRet;
    5367           6 :                     for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
    5368             :                     {
    5369             :                         const char *pszSubType =
    5370           5 :                             OGRFieldDefn::GetFieldSubTypeName(
    5371             :                                 static_cast<OGRFieldSubType>(i));
    5372           5 :                         if (pszSubType != nullptr)
    5373             :                         {
    5374           5 :                             if (currentValue.empty() ||
    5375           0 :                                 STARTS_WITH(pszSubType, currentValue.c_str()))
    5376             :                             {
    5377           5 :                                 oRet.push_back(pszSubType);
    5378             :                             }
    5379             :                         }
    5380             :                     }
    5381             : 
    5382          15 :                     for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
    5383             :                     {
    5384             :                         // Skip deprecated
    5385          14 :                         if (static_cast<OGRFieldType>(i) ==
    5386          13 :                                 OGRFieldType::OFTWideString ||
    5387             :                             static_cast<OGRFieldType>(i) ==
    5388             :                                 OGRFieldType::OFTWideStringList)
    5389           2 :                             continue;
    5390          12 :                         const char *pszType = OGRFieldDefn::GetFieldTypeName(
    5391             :                             static_cast<OGRFieldType>(i));
    5392          12 :                         if (pszType != nullptr)
    5393             :                         {
    5394          12 :                             if (currentValue.empty() ||
    5395           0 :                                 STARTS_WITH(pszType, currentValue.c_str()))
    5396             :                             {
    5397          12 :                                 oRet.push_back(pszType);
    5398             :                             }
    5399             :                         }
    5400             :                     }
    5401           1 :                     return oRet;
    5402         262 :                 });
    5403             : 
    5404             :     auto validationFunction =
    5405         845 :         [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
    5406             :     {
    5407         120 :         bool isValid{true};
    5408         120 :         *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
    5409             : 
    5410             :         // String is returned for unknown types
    5411         120 :         if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
    5412             :         {
    5413          16 :             isValid = false;
    5414             :         }
    5415             : 
    5416         120 :         *pSubtypeValue =
    5417         120 :             OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
    5418             : 
    5419         120 :         if (*pSubtypeValue != OFSTNone)
    5420             :         {
    5421          15 :             isValid = true;
    5422          15 :             switch (*pSubtypeValue)
    5423             :             {
    5424           6 :                 case OFSTBoolean:
    5425             :                 case OFSTInt16:
    5426             :                 {
    5427           6 :                     *pTypeValue = OFTInteger;
    5428           6 :                     break;
    5429             :                 }
    5430           3 :                 case OFSTFloat32:
    5431             :                 {
    5432           3 :                     *pTypeValue = OFTReal;
    5433           3 :                     break;
    5434             :                 }
    5435           6 :                 default:
    5436             :                 {
    5437           6 :                     *pTypeValue = OFTString;
    5438           6 :                     break;
    5439             :                 }
    5440             :             }
    5441             :         }
    5442             : 
    5443         120 :         if (!isValid)
    5444             :         {
    5445           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    5446             :                         "Invalid value for argument '%s': '%s'",
    5447           1 :                         arg.GetName().c_str(), pStrValue->c_str());
    5448             :         }
    5449             : 
    5450         120 :         return isValid;
    5451         262 :     };
    5452             : 
    5453         262 :     if (!pStrValue->empty())
    5454             :     {
    5455           0 :         arg.SetDefault(*pStrValue);
    5456           0 :         validationFunction();
    5457             :     }
    5458             : 
    5459         262 :     arg.AddValidationAction(std::move(validationFunction));
    5460             : 
    5461         262 :     return arg;
    5462             : }
    5463             : 
    5464             : /************************************************************************/
    5465             : /*                   GDALAlgorithm::ValidateBandArg()                   */
    5466             : /************************************************************************/
    5467             : 
    5468        4139 : bool GDALAlgorithm::ValidateBandArg() const
    5469             : {
    5470        4139 :     bool ret = true;
    5471        4139 :     const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
    5472        4139 :     const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
    5473        1639 :     if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
    5474         292 :         (inputDatasetArg->GetType() == GAAT_DATASET ||
    5475        5772 :          inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
    5476         149 :         (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
    5477             :     {
    5478         104 :         const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
    5479             :         {
    5480          99 :             if (nBand > poDS->GetRasterCount())
    5481             :             {
    5482           5 :                 ReportError(CE_Failure, CPLE_AppDefined,
    5483             :                             "Value of 'band' should be greater or equal than "
    5484             :                             "1 and less or equal than %d.",
    5485             :                             poDS->GetRasterCount());
    5486           5 :                 return false;
    5487             :             }
    5488          94 :             return true;
    5489          92 :         };
    5490             : 
    5491             :         const auto ValidateForOneDataset =
    5492         304 :             [&bandArg, &CheckBand](const GDALDataset *poDS)
    5493             :         {
    5494          87 :             bool l_ret = true;
    5495          87 :             if (bandArg->GetType() == GAAT_INTEGER)
    5496             :             {
    5497          24 :                 l_ret = CheckBand(poDS, bandArg->Get<int>());
    5498             :             }
    5499          63 :             else if (bandArg->GetType() == GAAT_INTEGER_LIST)
    5500             :             {
    5501         130 :                 for (int nBand : bandArg->Get<std::vector<int>>())
    5502             :                 {
    5503          75 :                     l_ret = l_ret && CheckBand(poDS, nBand);
    5504             :                 }
    5505             :             }
    5506          87 :             return l_ret;
    5507          92 :         };
    5508             : 
    5509          92 :         if (inputDatasetArg->GetType() == GAAT_DATASET)
    5510             :         {
    5511             :             auto poDS =
    5512           6 :                 inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
    5513           6 :             if (poDS && !ValidateForOneDataset(poDS))
    5514           2 :                 ret = false;
    5515             :         }
    5516             :         else
    5517             :         {
    5518          86 :             CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
    5519          85 :             for (auto &datasetValue :
    5520         256 :                  inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
    5521             :             {
    5522          85 :                 auto poDS = datasetValue.GetDatasetRef();
    5523          85 :                 if (poDS && !ValidateForOneDataset(poDS))
    5524           3 :                     ret = false;
    5525             :             }
    5526             :         }
    5527             :     }
    5528        4139 :     return ret;
    5529             : }
    5530             : 
    5531             : /************************************************************************/
    5532             : /*            GDALAlgorithm::RunPreStepPipelineValidations()            */
    5533             : /************************************************************************/
    5534             : 
    5535        3223 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
    5536             : {
    5537        3223 :     return ValidateBandArg();
    5538             : }
    5539             : 
    5540             : /************************************************************************/
    5541             : /*                     GDALAlgorithm::AddBandArg()                      */
    5542             : /************************************************************************/
    5543             : 
    5544             : GDALInConstructionAlgorithmArg &
    5545        1667 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
    5546             : {
    5547        2105 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5548             : 
    5549             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5550             :                   MsgOrDefault(helpMessage, _("Input band (1-based index)")),
    5551        3334 :                   pValue)
    5552             :         .AddValidationAction(
    5553          34 :             [pValue]()
    5554             :             {
    5555          34 :                 if (*pValue <= 0)
    5556             :                 {
    5557           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5558             :                              "Value of 'band' should greater or equal to 1.");
    5559           1 :                     return false;
    5560             :                 }
    5561          33 :                 return true;
    5562        3334 :             });
    5563             : }
    5564             : 
    5565             : /************************************************************************/
    5566             : /*                     GDALAlgorithm::AddBandArg()                      */
    5567             : /************************************************************************/
    5568             : 
    5569             : GDALInConstructionAlgorithmArg &
    5570         853 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
    5571             : {
    5572        1331 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5573             : 
    5574             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5575             :                   MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
    5576        1706 :                   pValue)
    5577             :         .AddValidationAction(
    5578         126 :             [pValue]()
    5579             :             {
    5580         397 :                 for (int val : *pValue)
    5581             :                 {
    5582         272 :                     if (val <= 0)
    5583             :                     {
    5584           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    5585             :                                  "Value of 'band' should greater or equal "
    5586             :                                  "to 1.");
    5587           1 :                         return false;
    5588             :                     }
    5589             :                 }
    5590         125 :                 return true;
    5591        1706 :             });
    5592             : }
    5593             : 
    5594             : /************************************************************************/
    5595             : /*                      ParseAndValidateKeyValue()                      */
    5596             : /************************************************************************/
    5597             : 
    5598         543 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
    5599             : {
    5600         503 :     const auto Validate = [this, &arg](const std::string &val)
    5601             :     {
    5602         498 :         if (val.find('=') == std::string::npos)
    5603             :         {
    5604           5 :             ReportError(
    5605             :                 CE_Failure, CPLE_AppDefined,
    5606             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    5607           5 :                 arg.GetName().c_str());
    5608           5 :             return false;
    5609             :         }
    5610             : 
    5611         493 :         return true;
    5612         543 :     };
    5613             : 
    5614         543 :     if (arg.GetType() == GAAT_STRING)
    5615             :     {
    5616           0 :         return Validate(arg.Get<std::string>());
    5617             :     }
    5618         543 :     else if (arg.GetType() == GAAT_STRING_LIST)
    5619             :     {
    5620         543 :         std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
    5621         543 :         if (vals.size() == 1)
    5622             :         {
    5623             :             // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
    5624         880 :             std::vector<std::string> newVals;
    5625         880 :             std::string curToken;
    5626         440 :             bool canSplitOnComma = true;
    5627         440 :             char lastSep = 0;
    5628         440 :             bool inString = false;
    5629         440 :             bool equalFoundInLastToken = false;
    5630        6501 :             for (char c : vals[0])
    5631             :             {
    5632        6065 :                 if (!inString && c == ',')
    5633             :                 {
    5634          10 :                     if (lastSep != '=' || !equalFoundInLastToken)
    5635             :                     {
    5636           2 :                         canSplitOnComma = false;
    5637           2 :                         break;
    5638             :                     }
    5639           8 :                     lastSep = c;
    5640           8 :                     newVals.push_back(curToken);
    5641           8 :                     curToken.clear();
    5642           8 :                     equalFoundInLastToken = false;
    5643             :                 }
    5644        6055 :                 else if (!inString && c == '=')
    5645             :                 {
    5646         439 :                     if (lastSep == '=')
    5647             :                     {
    5648           2 :                         canSplitOnComma = false;
    5649           2 :                         break;
    5650             :                     }
    5651         437 :                     equalFoundInLastToken = true;
    5652         437 :                     lastSep = c;
    5653         437 :                     curToken += c;
    5654             :                 }
    5655        5616 :                 else if (c == '"')
    5656             :                 {
    5657           4 :                     inString = !inString;
    5658           4 :                     curToken += c;
    5659             :                 }
    5660             :                 else
    5661             :                 {
    5662        5612 :                     curToken += c;
    5663             :                 }
    5664             :             }
    5665         440 :             if (canSplitOnComma && !inString && equalFoundInLastToken)
    5666             :             {
    5667         427 :                 if (!curToken.empty())
    5668         427 :                     newVals.emplace_back(std::move(curToken));
    5669         427 :                 vals = std::move(newVals);
    5670             :             }
    5671             :         }
    5672             : 
    5673        1036 :         for (const auto &val : vals)
    5674             :         {
    5675         498 :             if (!Validate(val))
    5676           5 :                 return false;
    5677             :         }
    5678             :     }
    5679             : 
    5680         538 :     return true;
    5681             : }
    5682             : 
    5683             : /************************************************************************/
    5684             : /*                           IsGDALGOutput()                            */
    5685             : /************************************************************************/
    5686             : 
    5687        2126 : bool GDALAlgorithm::IsGDALGOutput() const
    5688             : {
    5689        2126 :     bool isGDALGOutput = false;
    5690        2126 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5691        2126 :     const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5692        3655 :     if (outputArg && outputArg->GetType() == GAAT_DATASET &&
    5693        1529 :         outputArg->IsExplicitlySet())
    5694             :     {
    5695        3001 :         if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    5696        1488 :             outputFormatArg->IsExplicitlySet())
    5697             :         {
    5698             :             const auto &val =
    5699         955 :                 outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    5700         955 :             isGDALGOutput = EQUAL(val.c_str(), "GDALG");
    5701             :         }
    5702             :         else
    5703             :         {
    5704             :             const auto &filename =
    5705         558 :                 outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
    5706         558 :             isGDALGOutput =
    5707        1089 :                 filename.GetName().size() > strlen(".gdalg.json") &&
    5708         531 :                 EQUAL(filename.GetName().c_str() + filename.GetName().size() -
    5709             :                           strlen(".gdalg.json"),
    5710             :                       ".gdalg.json");
    5711             :         }
    5712             :     }
    5713        2126 :     return isGDALGOutput;
    5714             : }
    5715             : 
    5716             : /************************************************************************/
    5717             : /*                         ProcessGDALGOutput()                         */
    5718             : /************************************************************************/
    5719             : 
    5720        2462 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
    5721             : {
    5722        2462 :     if (!SupportsStreamedOutput())
    5723         798 :         return ProcessGDALGOutputRet::NOT_GDALG;
    5724             : 
    5725        1664 :     if (IsGDALGOutput())
    5726             :     {
    5727          11 :         const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5728             :         const auto &filename =
    5729          11 :             outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
    5730             :         VSIStatBufL sStat;
    5731          11 :         if (VSIStatL(filename.c_str(), &sStat) == 0)
    5732             :         {
    5733           0 :             const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
    5734           0 :             if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
    5735             :             {
    5736           0 :                 if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
    5737             :                 {
    5738           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5739             :                              "File '%s' already exists. Specify the "
    5740             :                              "--overwrite option to overwrite it.",
    5741             :                              filename.c_str());
    5742           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5743             :                 }
    5744             :             }
    5745             :         }
    5746             : 
    5747          22 :         std::string osCommandLine;
    5748             : 
    5749          44 :         for (const auto &path : GDALAlgorithm::m_callPath)
    5750             :         {
    5751          33 :             if (!osCommandLine.empty())
    5752          22 :                 osCommandLine += ' ';
    5753          33 :             osCommandLine += path;
    5754             :         }
    5755             : 
    5756         250 :         for (const auto &arg : GetArgs())
    5757             :         {
    5758         265 :             if (arg->IsExplicitlySet() &&
    5759          41 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
    5760          30 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
    5761         280 :                 arg->GetName() != GDAL_ARG_NAME_UPDATE &&
    5762          15 :                 arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
    5763             :             {
    5764          14 :                 osCommandLine += ' ';
    5765          14 :                 std::string strArg;
    5766          14 :                 if (!arg->Serialize(strArg))
    5767             :                 {
    5768           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5769             :                              "Cannot serialize argument %s",
    5770           0 :                              arg->GetName().c_str());
    5771           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5772             :                 }
    5773          14 :                 osCommandLine += strArg;
    5774             :             }
    5775             :         }
    5776             : 
    5777          11 :         osCommandLine += " --output-format stream --output streamed_dataset";
    5778             : 
    5779          11 :         std::string outStringUnused;
    5780          11 :         return SaveGDALG(filename, outStringUnused, osCommandLine)
    5781          11 :                    ? ProcessGDALGOutputRet::GDALG_OK
    5782          11 :                    : ProcessGDALGOutputRet::GDALG_ERROR;
    5783             :     }
    5784             : 
    5785        1653 :     return ProcessGDALGOutputRet::NOT_GDALG;
    5786             : }
    5787             : 
    5788             : /************************************************************************/
    5789             : /*                      GDALAlgorithm::SaveGDALG()                      */
    5790             : /************************************************************************/
    5791             : 
    5792          22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
    5793             :                                            std::string &outString,
    5794             :                                            const std::string &commandLine)
    5795             : {
    5796          44 :     CPLJSONDocument oDoc;
    5797          22 :     oDoc.GetRoot().Add("type", "gdal_streamed_alg");
    5798          22 :     oDoc.GetRoot().Add("command_line", commandLine);
    5799          22 :     oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
    5800             : 
    5801          22 :     if (!filename.empty())
    5802          21 :         return oDoc.Save(filename);
    5803             : 
    5804           1 :     outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
    5805           1 :     return true;
    5806             : }
    5807             : 
    5808             : /************************************************************************/
    5809             : /*                GDALAlgorithm::AddCreationOptionsArg()                */
    5810             : /************************************************************************/
    5811             : 
    5812             : GDALInConstructionAlgorithmArg &
    5813        8138 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
    5814             :                                      const char *helpMessage)
    5815             : {
    5816             :     auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
    5817       16276 :                        MsgOrDefault(helpMessage, _("Creation option")), pValue)
    5818       16276 :                     .AddAlias("co")
    5819       16276 :                     .SetMetaVar("<KEY>=<VALUE>")
    5820        8138 :                     .SetPackedValuesAllowed(false);
    5821         277 :     arg.AddValidationAction([this, &arg]()
    5822        8415 :                             { return ParseAndValidateKeyValue(arg); });
    5823             : 
    5824             :     arg.SetAutoCompleteFunction(
    5825          48 :         [this](const std::string &currentValue)
    5826             :         {
    5827          16 :             std::vector<std::string> oRet;
    5828             : 
    5829          16 :             int datasetType =
    5830             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    5831          16 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5832          16 :             if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
    5833           0 :                               outputArg->GetType() == GAAT_DATASET_LIST))
    5834             :             {
    5835          16 :                 datasetType = outputArg->GetDatasetType();
    5836             :             }
    5837             : 
    5838          16 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5839          32 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5840          16 :                 outputFormat->IsExplicitlySet())
    5841             :             {
    5842          12 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5843           6 :                     outputFormat->Get<std::string>().c_str());
    5844           6 :                 if (poDriver)
    5845             :                 {
    5846           6 :                     AddOptionsSuggestions(
    5847           6 :                         poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    5848             :                         datasetType, currentValue, oRet);
    5849             :                 }
    5850           6 :                 return oRet;
    5851             :             }
    5852             : 
    5853          10 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5854             :             {
    5855          10 :                 auto poDM = GetGDALDriverManager();
    5856          10 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5857          10 :                 const auto &osDSName = datasetValue.GetName();
    5858          10 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5859          10 :                 if (!osExt.empty())
    5860             :                 {
    5861          10 :                     std::set<std::string> oVisitedExtensions;
    5862         715 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5863             :                     {
    5864         712 :                         auto poDriver = poDM->GetDriver(i);
    5865        2136 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    5866         712 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    5867         216 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    5868        1424 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    5869         216 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    5870           0 :                              poDriver->GetMetadataItem(
    5871           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    5872             :                         {
    5873             :                             const char *pszExtensions =
    5874         496 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5875         496 :                             if (pszExtensions)
    5876             :                             {
    5877             :                                 const CPLStringList aosExts(
    5878         323 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5879         716 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5880             :                                 {
    5881         419 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5882          16 :                                         !cpl::contains(oVisitedExtensions,
    5883             :                                                        pszExt))
    5884             :                                     {
    5885          10 :                                         oVisitedExtensions.insert(pszExt);
    5886          10 :                                         if (AddOptionsSuggestions(
    5887             :                                                 poDriver->GetMetadataItem(
    5888          10 :                                                     GDAL_DMD_CREATIONOPTIONLIST),
    5889             :                                                 datasetType, currentValue,
    5890             :                                                 oRet))
    5891             :                                         {
    5892           7 :                                             return oRet;
    5893             :                                         }
    5894           3 :                                         break;
    5895             :                                     }
    5896             :                                 }
    5897             :                             }
    5898             :                         }
    5899             :                     }
    5900             :                 }
    5901             :             }
    5902             : 
    5903           3 :             return oRet;
    5904        8138 :         });
    5905             : 
    5906        8138 :     return arg;
    5907             : }
    5908             : 
    5909             : /************************************************************************/
    5910             : /*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
    5911             : /************************************************************************/
    5912             : 
    5913             : GDALInConstructionAlgorithmArg &
    5914        3894 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
    5915             :                                           const char *helpMessage)
    5916             : {
    5917             :     auto &arg =
    5918             :         AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
    5919        7788 :                MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
    5920        7788 :             .AddAlias("lco")
    5921        7788 :             .SetMetaVar("<KEY>=<VALUE>")
    5922        3894 :             .SetPackedValuesAllowed(false);
    5923          77 :     arg.AddValidationAction([this, &arg]()
    5924        3971 :                             { return ParseAndValidateKeyValue(arg); });
    5925             : 
    5926             :     arg.SetAutoCompleteFunction(
    5927           5 :         [this](const std::string &currentValue)
    5928             :         {
    5929           2 :             std::vector<std::string> oRet;
    5930             : 
    5931           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5932           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5933           2 :                 outputFormat->IsExplicitlySet())
    5934             :             {
    5935           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5936           1 :                     outputFormat->Get<std::string>().c_str());
    5937           1 :                 if (poDriver)
    5938             :                 {
    5939           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    5940           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5941             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    5942             :                 }
    5943           1 :                 return oRet;
    5944             :             }
    5945             : 
    5946           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5947           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5948             :             {
    5949           1 :                 auto poDM = GetGDALDriverManager();
    5950           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5951           1 :                 const auto &osDSName = datasetValue.GetName();
    5952           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5953           1 :                 if (!osExt.empty())
    5954             :                 {
    5955           1 :                     std::set<std::string> oVisitedExtensions;
    5956         229 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5957             :                     {
    5958         228 :                         auto poDriver = poDM->GetDriver(i);
    5959         228 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    5960             :                         {
    5961             :                             const char *pszExtensions =
    5962          91 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5963          91 :                             if (pszExtensions)
    5964             :                             {
    5965             :                                 const CPLStringList aosExts(
    5966          62 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5967         156 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5968             :                                 {
    5969          96 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5970           1 :                                         !cpl::contains(oVisitedExtensions,
    5971             :                                                        pszExt))
    5972             :                                     {
    5973           1 :                                         oVisitedExtensions.insert(pszExt);
    5974           1 :                                         if (AddOptionsSuggestions(
    5975             :                                                 poDriver->GetMetadataItem(
    5976           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5977             :                                                 GDAL_OF_VECTOR, currentValue,
    5978             :                                                 oRet))
    5979             :                                         {
    5980           0 :                                             return oRet;
    5981             :                                         }
    5982           1 :                                         break;
    5983             :                                     }
    5984             :                                 }
    5985             :                             }
    5986             :                         }
    5987             :                     }
    5988             :                 }
    5989             :             }
    5990             : 
    5991           1 :             return oRet;
    5992        3894 :         });
    5993             : 
    5994        3894 :     return arg;
    5995             : }
    5996             : 
    5997             : /************************************************************************/
    5998             : /*                     GDALAlgorithm::AddBBOXArg()                      */
    5999             : /************************************************************************/
    6000             : 
    6001             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    6002             : GDALInConstructionAlgorithmArg &
    6003        1848 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    6004             : {
    6005             :     auto &arg = AddArg("bbox", 0,
    6006             :                        MsgOrDefault(helpMessage,
    6007             :                                     _("Bounding box as xmin,ymin,xmax,ymax")),
    6008        3696 :                        pValue)
    6009        1848 :                     .SetRepeatedArgAllowed(false)
    6010        1848 :                     .SetMinCount(4)
    6011        1848 :                     .SetMaxCount(4)
    6012        1848 :                     .SetDisplayHintAboutRepetition(false);
    6013             :     arg.AddValidationAction(
    6014         167 :         [&arg]()
    6015             :         {
    6016         167 :             const auto &val = arg.Get<std::vector<double>>();
    6017         167 :             CPLAssert(val.size() == 4);
    6018         167 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    6019             :             {
    6020           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6021             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    6022             :                          "xmin <= xmax and ymin <= ymax");
    6023           5 :                 return false;
    6024             :             }
    6025         162 :             return true;
    6026        1848 :         });
    6027        1848 :     return arg;
    6028             : }
    6029             : 
    6030             : /************************************************************************/
    6031             : /*                  GDALAlgorithm::AddActiveLayerArg()                  */
    6032             : /************************************************************************/
    6033             : 
    6034             : GDALInConstructionAlgorithmArg &
    6035        1723 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
    6036             : {
    6037             :     return AddArg("active-layer", 0,
    6038             :                   MsgOrDefault(helpMessage,
    6039             :                                _("Set active layer (if not specified, all)")),
    6040        1723 :                   pValue);
    6041             : }
    6042             : 
    6043             : /************************************************************************/
    6044             : /*                  GDALAlgorithm::AddNumThreadsArg()                   */
    6045             : /************************************************************************/
    6046             : 
    6047             : GDALInConstructionAlgorithmArg &
    6048         702 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
    6049             :                                 const char *helpMessage)
    6050             : {
    6051             :     auto &arg =
    6052             :         AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
    6053             :                MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
    6054         702 :                pStrValue);
    6055             : 
    6056             :     AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
    6057        1404 :            _("Number of jobs (read-only, hidden argument)"), pValue)
    6058         702 :         .SetHidden();
    6059             : 
    6060        2661 :     auto lambda = [this, &arg, pValue, pStrValue]
    6061             :     {
    6062         887 :         bool bOK = false;
    6063         887 :         const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    6064             :         const int nLimit = std::clamp(
    6065         887 :             pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
    6066        1774 :             CPLGetNumCPUs());
    6067             :         const int nNumThreads =
    6068         887 :             GDALGetNumThreads(pStrValue->c_str(), nLimit,
    6069             :                               /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
    6070         887 :         if (bOK)
    6071             :         {
    6072         887 :             *pValue = nNumThreads;
    6073             :         }
    6074             :         else
    6075             :         {
    6076           0 :             ReportError(CE_Failure, CPLE_IllegalArg,
    6077             :                         "Invalid value for '%s' argument",
    6078           0 :                         arg.GetName().c_str());
    6079             :         }
    6080         887 :         return bOK;
    6081         702 :     };
    6082         702 :     if (!pStrValue->empty())
    6083             :     {
    6084         656 :         arg.SetDefault(*pStrValue);
    6085         656 :         lambda();
    6086             :     }
    6087         702 :     arg.AddValidationAction(std::move(lambda));
    6088         702 :     return arg;
    6089             : }
    6090             : 
    6091             : /************************************************************************/
    6092             : /*                 GDALAlgorithm::AddAbsolutePathArg()                  */
    6093             : /************************************************************************/
    6094             : 
    6095             : GDALInConstructionAlgorithmArg &
    6096         619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
    6097             : {
    6098             :     return AddArg(
    6099             :         "absolute-path", 0,
    6100             :         MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
    6101             :                                     "should be stored as an absolute path")),
    6102         619 :         pValue);
    6103             : }
    6104             : 
    6105             : /************************************************************************/
    6106             : /*               GDALAlgorithm::AddPixelFunctionNameArg()               */
    6107             : /************************************************************************/
    6108             : 
    6109             : GDALInConstructionAlgorithmArg &
    6110         137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
    6111             :                                        const char *helpMessage)
    6112             : {
    6113             : 
    6114             :     const auto pixelFunctionNames =
    6115         137 :         VRTDerivedRasterBand::GetPixelFunctionNames();
    6116             :     return AddArg(
    6117             :                "pixel-function", 0,
    6118             :                MsgOrDefault(
    6119             :                    helpMessage,
    6120             :                    _("Specify a pixel function to calculate output value from "
    6121             :                      "overlapping inputs")),
    6122         274 :                pValue)
    6123         274 :         .SetChoices(pixelFunctionNames);
    6124             : }
    6125             : 
    6126             : /************************************************************************/
    6127             : /*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
    6128             : /************************************************************************/
    6129             : 
    6130             : GDALInConstructionAlgorithmArg &
    6131         137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
    6132             :                                        const char *helpMessage)
    6133             : {
    6134             :     auto &pixelFunctionArgArg =
    6135             :         AddArg("pixel-function-arg", 0,
    6136             :                MsgOrDefault(
    6137             :                    helpMessage,
    6138             :                    _("Specify argument(s) to pass to the pixel function")),
    6139         274 :                pValue)
    6140         274 :             .SetMetaVar("<NAME>=<VALUE>")
    6141         137 :             .SetRepeatedArgAllowed(true);
    6142             :     pixelFunctionArgArg.AddValidationAction(
    6143           7 :         [this, &pixelFunctionArgArg]()
    6144         144 :         { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
    6145             : 
    6146             :     pixelFunctionArgArg.SetAutoCompleteFunction(
    6147          12 :         [this](const std::string &currentValue)
    6148             :         {
    6149          12 :             std::string pixelFunction;
    6150           6 :             const auto pixelFunctionArg = GetArg("pixel-function");
    6151           6 :             if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
    6152             :             {
    6153           6 :                 pixelFunction = pixelFunctionArg->Get<std::string>();
    6154             :             }
    6155             : 
    6156           6 :             std::vector<std::string> ret;
    6157             : 
    6158           6 :             if (!pixelFunction.empty())
    6159             :             {
    6160           5 :                 const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
    6161             :                     pixelFunction.c_str());
    6162           5 :                 if (!pair)
    6163             :                 {
    6164           1 :                     ret.push_back("**");
    6165             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6166           1 :                     ret.push_back(std::string("\xC2\xA0"
    6167             :                                               "Invalid pixel function name"));
    6168             :                 }
    6169           4 :                 else if (pair->second.find("Argument name=") ==
    6170             :                          std::string::npos)
    6171             :                 {
    6172           1 :                     ret.push_back("**");
    6173             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6174           1 :                     ret.push_back(
    6175           2 :                         std::string(
    6176             :                             "\xC2\xA0"
    6177             :                             "No pixel function arguments for pixel function '")
    6178           1 :                             .append(pixelFunction)
    6179           1 :                             .append("'"));
    6180             :                 }
    6181             :                 else
    6182             :                 {
    6183           3 :                     AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
    6184             :                                           ret);
    6185             :                 }
    6186             :             }
    6187             : 
    6188          12 :             return ret;
    6189         137 :         });
    6190             : 
    6191         137 :     return pixelFunctionArgArg;
    6192             : }
    6193             : 
    6194             : /************************************************************************/
    6195             : /*                   GDALAlgorithm::AddProgressArg()                    */
    6196             : /************************************************************************/
    6197             : 
    6198        8231 : void GDALAlgorithm::AddProgressArg()
    6199             : {
    6200             :     AddArg(GDAL_ARG_NAME_QUIET, 'q',
    6201       16462 :            _("Quiet mode (no progress bar or warning message)"), &m_quiet)
    6202        8231 :         .SetAvailableInPipelineStep(false)
    6203       16462 :         .SetCategory(GAAC_COMMON)
    6204        8231 :         .AddAction([this]() { m_progressBarRequested = false; });
    6205             : 
    6206       16462 :     AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
    6207        8231 :         .SetAvailableInPipelineStep(false)
    6208        8231 :         .SetHidden();
    6209        8231 : }
    6210             : 
    6211             : /************************************************************************/
    6212             : /*                         GDALAlgorithm::Run()                         */
    6213             : /************************************************************************/
    6214             : 
    6215        4636 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    6216             : {
    6217        4636 :     WarnIfDeprecated();
    6218             : 
    6219        4636 :     if (m_selectedSubAlg)
    6220             :     {
    6221         411 :         if (m_calledFromCommandLine)
    6222         233 :             m_selectedSubAlg->m_calledFromCommandLine = true;
    6223         411 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    6224             :     }
    6225             : 
    6226        4225 :     if (m_helpRequested || m_helpDocRequested)
    6227             :     {
    6228          16 :         if (m_calledFromCommandLine)
    6229          16 :             printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    6230          16 :         return true;
    6231             :     }
    6232             : 
    6233        4209 :     if (m_JSONUsageRequested)
    6234             :     {
    6235           3 :         if (m_calledFromCommandLine)
    6236           3 :             printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    6237           3 :         return true;
    6238             :     }
    6239             : 
    6240        4206 :     if (!ValidateArguments())
    6241         125 :         return false;
    6242             : 
    6243        4081 :     if (m_alreadyRun)
    6244             :     {
    6245           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    6246             :                     "Run() can be called only once per algorithm instance");
    6247           3 :         return false;
    6248             :     }
    6249        4078 :     m_alreadyRun = true;
    6250             : 
    6251        4078 :     switch (ProcessGDALGOutput())
    6252             :     {
    6253           0 :         case ProcessGDALGOutputRet::GDALG_ERROR:
    6254           0 :             return false;
    6255             : 
    6256          11 :         case ProcessGDALGOutputRet::GDALG_OK:
    6257          11 :             return true;
    6258             : 
    6259        4067 :         case ProcessGDALGOutputRet::NOT_GDALG:
    6260        4067 :             break;
    6261             :     }
    6262             : 
    6263        4067 :     if (m_executionForStreamOutput)
    6264             :     {
    6265          93 :         if (!CheckSafeForStreamOutput())
    6266             :         {
    6267           4 :             return false;
    6268             :         }
    6269             :     }
    6270             : 
    6271        4063 :     return RunImpl(pfnProgress, pProgressData);
    6272             : }
    6273             : 
    6274             : /************************************************************************/
    6275             : /*              GDALAlgorithm::CheckSafeForStreamOutput()               */
    6276             : /************************************************************************/
    6277             : 
    6278          48 : bool GDALAlgorithm::CheckSafeForStreamOutput()
    6279             : {
    6280          48 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6281          48 :     if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
    6282             :     {
    6283          48 :         const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    6284          48 :         if (!EQUAL(val.c_str(), "stream"))
    6285             :         {
    6286             :             // For security reasons, to avoid that reading a .gdalg.json file
    6287             :             // writes a file on the file system.
    6288           4 :             ReportError(
    6289             :                 CE_Failure, CPLE_NotSupported,
    6290             :                 "in streamed execution, --format stream should be used");
    6291           4 :             return false;
    6292             :         }
    6293             :     }
    6294          44 :     return true;
    6295             : }
    6296             : 
    6297             : /************************************************************************/
    6298             : /*                      GDALAlgorithm::Finalize()                       */
    6299             : /************************************************************************/
    6300             : 
    6301        1675 : bool GDALAlgorithm::Finalize()
    6302             : {
    6303        1675 :     bool ret = true;
    6304        1675 :     if (m_selectedSubAlg)
    6305         239 :         ret = m_selectedSubAlg->Finalize();
    6306             : 
    6307       31193 :     for (auto &arg : m_args)
    6308             :     {
    6309       29518 :         if (arg->GetType() == GAAT_DATASET)
    6310             :         {
    6311        1334 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    6312             :         }
    6313       28184 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    6314             :         {
    6315        2580 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    6316             :             {
    6317        1220 :                 ret = ds.Close() && ret;
    6318             :             }
    6319             :         }
    6320             :     }
    6321        1675 :     return ret;
    6322             : }
    6323             : 
    6324             : /************************************************************************/
    6325             : /*                  GDALAlgorithm::GetArgNamesForCLI()                  */
    6326             : /************************************************************************/
    6327             : 
    6328             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    6329         693 : GDALAlgorithm::GetArgNamesForCLI() const
    6330             : {
    6331        1386 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6332             : 
    6333         693 :     size_t maxOptLen = 0;
    6334        8734 :     for (const auto &arg : m_args)
    6335             :     {
    6336        8041 :         if (arg->IsHidden() || arg->IsHiddenForCLI())
    6337        1713 :             continue;
    6338        6328 :         std::string opt;
    6339        6328 :         bool addComma = false;
    6340        6328 :         if (!arg->GetShortName().empty())
    6341             :         {
    6342        1441 :             opt += '-';
    6343        1441 :             opt += arg->GetShortName();
    6344        1441 :             addComma = true;
    6345             :         }
    6346        6328 :         for (char alias : arg->GetShortNameAliases())
    6347             :         {
    6348           0 :             if (addComma)
    6349           0 :                 opt += ", ";
    6350           0 :             opt += "-";
    6351           0 :             opt += alias;
    6352           0 :             addComma = true;
    6353             :         }
    6354        7080 :         for (const std::string &alias : arg->GetAliases())
    6355             :         {
    6356         752 :             if (addComma)
    6357         324 :                 opt += ", ";
    6358         752 :             opt += "--";
    6359         752 :             opt += alias;
    6360         752 :             addComma = true;
    6361             :         }
    6362        6328 :         if (!arg->GetName().empty())
    6363             :         {
    6364        6328 :             if (addComma)
    6365        1869 :                 opt += ", ";
    6366        6328 :             opt += "--";
    6367        6328 :             opt += arg->GetName();
    6368             :         }
    6369        6328 :         const auto &metaVar = arg->GetMetaVar();
    6370        6328 :         if (!metaVar.empty())
    6371             :         {
    6372        3956 :             opt += ' ';
    6373        3956 :             if (metaVar.front() != '<')
    6374        2837 :                 opt += '<';
    6375        3956 :             opt += metaVar;
    6376        3956 :             if (metaVar.back() != '>')
    6377        2831 :                 opt += '>';
    6378             :         }
    6379        6328 :         maxOptLen = std::max(maxOptLen, opt.size());
    6380        6328 :         options.emplace_back(arg.get(), opt);
    6381             :     }
    6382             : 
    6383        1386 :     return std::make_pair(std::move(options), maxOptLen);
    6384             : }
    6385             : 
    6386             : /************************************************************************/
    6387             : /*                   GDALAlgorithm::GetUsageForCLI()                    */
    6388             : /************************************************************************/
    6389             : 
    6390             : std::string
    6391         412 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    6392             :                               const UsageOptions &usageOptions) const
    6393             : {
    6394         412 :     if (m_selectedSubAlg)
    6395           7 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    6396             : 
    6397         810 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    6398         810 :     std::string osPath;
    6399         813 :     for (const std::string &s : m_callPath)
    6400             :     {
    6401         408 :         if (!osPath.empty())
    6402          49 :             osPath += ' ';
    6403         408 :         osPath += s;
    6404             :     }
    6405         405 :     osRet += ' ';
    6406         405 :     osRet += osPath;
    6407             : 
    6408         405 :     bool hasNonPositionals = false;
    6409        5064 :     for (const auto &arg : m_args)
    6410             :     {
    6411        4659 :         if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
    6412        3356 :             hasNonPositionals = true;
    6413             :     }
    6414             : 
    6415         405 :     if (HasSubAlgorithms())
    6416             :     {
    6417           9 :         if (m_callPath.size() == 1)
    6418             :         {
    6419           8 :             osRet += " <COMMAND>";
    6420           8 :             if (hasNonPositionals)
    6421           8 :                 osRet += " [OPTIONS]";
    6422           8 :             if (usageOptions.isPipelineStep)
    6423             :             {
    6424           5 :                 const size_t nLenFirstLine = osRet.size();
    6425           5 :                 osRet += '\n';
    6426           5 :                 osRet.append(nLenFirstLine, '-');
    6427           5 :                 osRet += '\n';
    6428             :             }
    6429           8 :             osRet += "\nwhere <COMMAND> is one of:\n";
    6430             :         }
    6431             :         else
    6432             :         {
    6433           1 :             osRet += " <SUBCOMMAND>";
    6434           1 :             if (hasNonPositionals)
    6435           1 :                 osRet += " [OPTIONS]";
    6436           1 :             if (usageOptions.isPipelineStep)
    6437             :             {
    6438           0 :                 const size_t nLenFirstLine = osRet.size();
    6439           0 :                 osRet += '\n';
    6440           0 :                 osRet.append(nLenFirstLine, '-');
    6441           0 :                 osRet += '\n';
    6442             :             }
    6443           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    6444             :         }
    6445           9 :         size_t maxNameLen = 0;
    6446          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6447             :         {
    6448          43 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    6449             :         }
    6450          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6451             :         {
    6452          86 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6453          43 :             if (subAlg && !subAlg->IsHidden())
    6454             :             {
    6455          43 :                 const std::string &name(subAlg->GetName());
    6456          43 :                 osRet += "  - ";
    6457          43 :                 osRet += name;
    6458          43 :                 osRet += ": ";
    6459          43 :                 osRet.append(maxNameLen - name.size(), ' ');
    6460          43 :                 osRet += subAlg->GetDescription();
    6461          43 :                 if (!subAlg->m_aliases.empty())
    6462             :                 {
    6463           6 :                     bool first = true;
    6464           6 :                     for (const auto &alias : subAlg->GetAliases())
    6465             :                     {
    6466           6 :                         if (alias ==
    6467             :                             GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    6468           6 :                             break;
    6469           0 :                         if (first)
    6470           0 :                             osRet += " (alias: ";
    6471             :                         else
    6472           0 :                             osRet += ", ";
    6473           0 :                         osRet += alias;
    6474           0 :                         first = false;
    6475             :                     }
    6476           6 :                     if (!first)
    6477             :                     {
    6478           0 :                         osRet += ')';
    6479             :                     }
    6480             :                 }
    6481          43 :                 osRet += '\n';
    6482             :             }
    6483             :         }
    6484             : 
    6485           9 :         if (shortUsage && hasNonPositionals)
    6486             :         {
    6487           2 :             osRet += "\nTry '";
    6488           2 :             osRet += osPath;
    6489           2 :             osRet += " --help' for help.\n";
    6490             :         }
    6491             :     }
    6492             :     else
    6493             :     {
    6494         396 :         if (!m_args.empty())
    6495             :         {
    6496         396 :             if (hasNonPositionals)
    6497         396 :                 osRet += " [OPTIONS]";
    6498         584 :             for (const auto *arg : m_positionalArgs)
    6499             :             {
    6500         261 :                 if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
    6501          73 :                     (GetName() == "pipeline" && arg->GetName() == "pipeline"))
    6502             :                 {
    6503             :                     const bool optional =
    6504         199 :                         (!arg->IsRequired() && !(GetName() == "pipeline" &&
    6505          28 :                                                  arg->GetName() == "pipeline"));
    6506         171 :                     osRet += ' ';
    6507         171 :                     if (optional)
    6508          25 :                         osRet += '[';
    6509         171 :                     const std::string &metavar = arg->GetMetaVar();
    6510         171 :                     if (!metavar.empty() && metavar[0] == '<')
    6511             :                     {
    6512           4 :                         osRet += metavar;
    6513             :                     }
    6514             :                     else
    6515             :                     {
    6516         167 :                         osRet += '<';
    6517         167 :                         osRet += metavar;
    6518         167 :                         osRet += '>';
    6519             :                     }
    6520         213 :                     if (arg->GetType() == GAAT_DATASET_LIST &&
    6521          42 :                         arg->GetMaxCount() > 1)
    6522             :                     {
    6523          28 :                         osRet += "...";
    6524             :                     }
    6525         171 :                     if (optional)
    6526          25 :                         osRet += ']';
    6527             :                 }
    6528             :             }
    6529             :         }
    6530             : 
    6531         396 :         const size_t nLenFirstLine = osRet.size();
    6532         396 :         osRet += '\n';
    6533         396 :         if (usageOptions.isPipelineStep)
    6534             :         {
    6535         309 :             osRet.append(nLenFirstLine, '-');
    6536         309 :             osRet += '\n';
    6537             :         }
    6538             : 
    6539         396 :         if (shortUsage)
    6540             :         {
    6541          21 :             osRet += "Try '";
    6542          21 :             osRet += osPath;
    6543          21 :             osRet += " --help' for help.\n";
    6544          21 :             return osRet;
    6545             :         }
    6546             : 
    6547         375 :         osRet += '\n';
    6548         375 :         osRet += m_description;
    6549         375 :         osRet += '\n';
    6550             :     }
    6551             : 
    6552         384 :     if (!m_args.empty() && !shortUsage)
    6553             :     {
    6554         764 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6555             :         size_t maxOptLen;
    6556         382 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    6557         382 :         if (usageOptions.maxOptLen)
    6558         311 :             maxOptLen = usageOptions.maxOptLen;
    6559             : 
    6560         764 :         const std::string userProvidedOpt = "--<user-provided-option>=<value>";
    6561         382 :         if (m_arbitraryLongNameArgsAllowed)
    6562           2 :             maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
    6563             : 
    6564             :         const auto OutputArg =
    6565        2386 :             [this, maxOptLen, &osRet,
    6566       23765 :              &usageOptions](const GDALAlgorithmArg *arg, const std::string &opt)
    6567             :         {
    6568        2386 :             osRet += "  ";
    6569        2386 :             osRet += opt;
    6570        2386 :             osRet += "  ";
    6571        2386 :             osRet.append(maxOptLen - opt.size(), ' ');
    6572        2386 :             osRet += arg->GetDescription();
    6573             : 
    6574        2386 :             const auto &choices = arg->GetChoices();
    6575        2386 :             if (!choices.empty())
    6576             :             {
    6577         224 :                 osRet += ". ";
    6578         224 :                 osRet += arg->GetMetaVar();
    6579         224 :                 osRet += '=';
    6580         224 :                 bool firstChoice = true;
    6581        1725 :                 for (const auto &choice : choices)
    6582             :                 {
    6583        1501 :                     if (!firstChoice)
    6584        1277 :                         osRet += '|';
    6585        1501 :                     osRet += choice;
    6586        1501 :                     firstChoice = false;
    6587             :                 }
    6588             :             }
    6589             : 
    6590        4706 :             if (arg->GetType() == GAAT_DATASET ||
    6591        2320 :                 arg->GetType() == GAAT_DATASET_LIST)
    6592             :             {
    6593         144 :                 if (arg->IsOutput() &&
    6594         144 :                     arg->GetDatasetInputFlags() == GADV_NAME &&
    6595           9 :                     arg->GetDatasetOutputFlags() == GADV_OBJECT)
    6596             :                 {
    6597           9 :                     osRet += " (created by algorithm)";
    6598             :                 }
    6599             :             }
    6600             : 
    6601        2386 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    6602             :             {
    6603         190 :                 osRet += " (default: ";
    6604         190 :                 osRet += arg->GetDefault<std::string>();
    6605         190 :                 osRet += ')';
    6606             :             }
    6607        2196 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    6608             :             {
    6609          66 :                 if (arg->GetDefault<bool>())
    6610           0 :                     osRet += " (default: true)";
    6611             :             }
    6612        2130 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    6613             :             {
    6614          76 :                 osRet += " (default: ";
    6615          76 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    6616          76 :                 osRet += ')';
    6617             :             }
    6618        2054 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    6619             :             {
    6620          49 :                 osRet += " (default: ";
    6621          49 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    6622          49 :                 osRet += ')';
    6623             :             }
    6624        2417 :             else if (arg->GetType() == GAAT_STRING_LIST &&
    6625         412 :                      arg->HasDefaultValue())
    6626             :             {
    6627             :                 const auto &defaultVal =
    6628           9 :                     arg->GetDefault<std::vector<std::string>>();
    6629           9 :                 if (defaultVal.size() == 1)
    6630             :                 {
    6631           9 :                     osRet += " (default: ";
    6632           9 :                     osRet += defaultVal[0];
    6633           9 :                     osRet += ')';
    6634             :                 }
    6635             :             }
    6636        2023 :             else if (arg->GetType() == GAAT_INTEGER_LIST &&
    6637          27 :                      arg->HasDefaultValue())
    6638             :             {
    6639           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<int>>();
    6640           0 :                 if (defaultVal.size() == 1)
    6641             :                 {
    6642           0 :                     osRet += " (default: ";
    6643           0 :                     osRet += CPLSPrintf("%d", defaultVal[0]);
    6644           0 :                     osRet += ')';
    6645             :                 }
    6646             :             }
    6647        1996 :             else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
    6648             :             {
    6649           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<double>>();
    6650           0 :                 if (defaultVal.size() == 1)
    6651             :                 {
    6652           0 :                     osRet += " (default: ";
    6653           0 :                     osRet += CPLSPrintf("%g", defaultVal[0]);
    6654           0 :                     osRet += ')';
    6655             :                 }
    6656             :             }
    6657             : 
    6658        2386 :             if (arg->GetDisplayHintAboutRepetition())
    6659             :             {
    6660        2419 :                 if (arg->GetMinCount() > 0 &&
    6661          92 :                     arg->GetMinCount() == arg->GetMaxCount())
    6662             :                 {
    6663          18 :                     if (arg->GetMinCount() != 1)
    6664           5 :                         osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    6665             :                 }
    6666        2383 :                 else if (arg->GetMinCount() > 0 &&
    6667          74 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    6668             :                 {
    6669             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    6670           8 :                                         arg->GetMaxCount());
    6671             :                 }
    6672        2301 :                 else if (arg->GetMinCount() > 0)
    6673             :                 {
    6674          66 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    6675             :                 }
    6676        2235 :                 else if (arg->GetMaxCount() > 1)
    6677             :                 {
    6678         401 :                     osRet += " [may be repeated]";
    6679             :                 }
    6680             :             }
    6681             : 
    6682        2386 :             if (arg->IsRequired())
    6683             :             {
    6684         169 :                 osRet += " [required]";
    6685             :             }
    6686             : 
    6687        2640 :             if (!arg->IsAvailableInPipelineStep() &&
    6688         254 :                 !usageOptions.isPipelineStep)
    6689             :             {
    6690          28 :                 osRet += " [not available in pipelines]";
    6691             :             }
    6692             : 
    6693        2386 :             osRet += '\n';
    6694             : 
    6695        2386 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6696        2386 :             if (!mutualExclusionGroup.empty())
    6697             :             {
    6698         434 :                 std::string otherArgs;
    6699        4189 :                 for (const auto &otherArg : m_args)
    6700             :                 {
    6701        7256 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6702        3284 :                         otherArg.get() == arg)
    6703         905 :                         continue;
    6704        3067 :                     if (otherArg->GetMutualExclusionGroup() ==
    6705             :                         mutualExclusionGroup)
    6706             :                     {
    6707         290 :                         if (!otherArgs.empty())
    6708          77 :                             otherArgs += ", ";
    6709         290 :                         otherArgs += "--";
    6710         290 :                         otherArgs += otherArg->GetName();
    6711             :                     }
    6712             :                 }
    6713         217 :                 if (!otherArgs.empty())
    6714             :                 {
    6715         213 :                     osRet += "  ";
    6716         213 :                     osRet += "  ";
    6717         213 :                     osRet.append(maxOptLen, ' ');
    6718         213 :                     osRet += "Mutually exclusive with ";
    6719         213 :                     osRet += otherArgs;
    6720         213 :                     osRet += '\n';
    6721             :                 }
    6722             :             }
    6723             : 
    6724             :             // Check dependency
    6725        4772 :             std::string dependencyArgs;
    6726             : 
    6727          32 :             for (const auto &dependencyArgumentName :
    6728        2450 :                  GetArgDependencies(arg->GetName()))
    6729             :             {
    6730          32 :                 const auto otherArg{GetArg(dependencyArgumentName)};
    6731          32 :                 if (otherArg != nullptr)
    6732             :                 {
    6733          32 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6734             :                         otherArg == arg)
    6735             :                     {
    6736           0 :                         continue;
    6737             :                     }
    6738             : 
    6739          32 :                     if (!dependencyArgs.empty())
    6740             :                     {
    6741           3 :                         dependencyArgs += ", ";
    6742             :                     }
    6743             : 
    6744          32 :                     dependencyArgs += "--";
    6745          32 :                     dependencyArgs += otherArg->GetName();
    6746             :                 }
    6747             :                 else
    6748             :                 {
    6749           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6750             :                              "Argument '%s' depends on unknown argument '%s'",
    6751           0 :                              arg->GetName().c_str(),
    6752             :                              dependencyArgumentName.c_str());
    6753             :                 }
    6754             :             }
    6755             : 
    6756        2386 :             if (!dependencyArgs.empty())
    6757             :             {
    6758          29 :                 osRet += "  ";
    6759          29 :                 osRet += "  ";
    6760          29 :                 osRet.append(maxOptLen, ' ');
    6761          29 :                 osRet += "Depends on ";
    6762          29 :                 osRet += dependencyArgs;
    6763          29 :                 osRet += '\n';
    6764             :             }
    6765        2386 :         };
    6766             : 
    6767         382 :         if (!m_positionalArgs.empty())
    6768             :         {
    6769         150 :             osRet += "\nPositional arguments:\n";
    6770        1613 :             for (const auto &[arg, opt] : options)
    6771             :             {
    6772        1463 :                 if (arg->IsPositional())
    6773         141 :                     OutputArg(arg, opt);
    6774             :             }
    6775             :         }
    6776             : 
    6777         382 :         if (hasNonPositionals)
    6778             :         {
    6779         382 :             bool hasCommon = false;
    6780         382 :             bool hasBase = false;
    6781         382 :             bool hasAdvanced = false;
    6782         382 :             bool hasEsoteric = false;
    6783         764 :             std::vector<std::string> categories;
    6784        3739 :             for (const auto &iter : options)
    6785             :             {
    6786        3357 :                 const auto &arg = iter.first;
    6787        3357 :                 if (!arg->IsPositional())
    6788             :                 {
    6789        3216 :                     const auto &category = arg->GetCategory();
    6790        3216 :                     if (category == GAAC_COMMON)
    6791             :                     {
    6792        1189 :                         hasCommon = true;
    6793             :                     }
    6794        2027 :                     else if (category == GAAC_BASE)
    6795             :                     {
    6796        1787 :                         hasBase = true;
    6797             :                     }
    6798         240 :                     else if (category == GAAC_ADVANCED)
    6799             :                     {
    6800         178 :                         hasAdvanced = true;
    6801             :                     }
    6802          62 :                     else if (category == GAAC_ESOTERIC)
    6803             :                     {
    6804          29 :                         hasEsoteric = true;
    6805             :                     }
    6806          33 :                     else if (std::find(categories.begin(), categories.end(),
    6807          33 :                                        category) == categories.end())
    6808             :                     {
    6809           9 :                         categories.push_back(category);
    6810             :                     }
    6811             :                 }
    6812             :             }
    6813         382 :             if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
    6814          67 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    6815         382 :             if (hasBase)
    6816         336 :                 categories.insert(categories.begin(), GAAC_BASE);
    6817         382 :             if (hasCommon && !usageOptions.isPipelineStep)
    6818          68 :                 categories.insert(categories.begin(), GAAC_COMMON);
    6819         382 :             if (hasEsoteric)
    6820          11 :                 categories.push_back(GAAC_ESOTERIC);
    6821             : 
    6822         873 :             for (const auto &category : categories)
    6823             :             {
    6824         491 :                 osRet += "\n";
    6825         491 :                 if (category != GAAC_BASE)
    6826             :                 {
    6827         155 :                     osRet += category;
    6828         155 :                     osRet += ' ';
    6829             :                 }
    6830         491 :                 osRet += "Options:\n";
    6831        5362 :                 for (const auto &[arg, opt] : options)
    6832             :                 {
    6833        4871 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    6834        2245 :                         OutputArg(arg, opt);
    6835             :                 }
    6836         491 :                 if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
    6837             :                 {
    6838           2 :                     osRet += "  ";
    6839           2 :                     osRet += userProvidedOpt;
    6840           2 :                     osRet += "  ";
    6841           2 :                     if (userProvidedOpt.size() < maxOptLen)
    6842           0 :                         osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
    6843           2 :                     osRet += "Argument provided by user";
    6844           2 :                     osRet += '\n';
    6845             :                 }
    6846             :             }
    6847             :         }
    6848             :     }
    6849             : 
    6850         384 :     if (!m_longDescription.empty())
    6851             :     {
    6852           6 :         osRet += '\n';
    6853           6 :         osRet += m_longDescription;
    6854           6 :         osRet += '\n';
    6855             :     }
    6856             : 
    6857         384 :     if (!m_helpDocRequested && !usageOptions.isPipelineMain)
    6858             :     {
    6859         371 :         if (!m_helpURL.empty())
    6860             :         {
    6861         371 :             osRet += "\nFor more details, consult ";
    6862         371 :             osRet += GetHelpFullURL();
    6863         371 :             osRet += '\n';
    6864             :         }
    6865         371 :         osRet += GetUsageForCLIEnd();
    6866             :     }
    6867             : 
    6868         384 :     return osRet;
    6869             : }
    6870             : 
    6871             : /************************************************************************/
    6872             : /*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
    6873             : /************************************************************************/
    6874             : 
    6875             : //! @cond Doxygen_Suppress
    6876         378 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
    6877             : {
    6878         378 :     std::string osRet;
    6879             : 
    6880         378 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    6881             :     {
    6882             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    6883             :                  "alternative interface to GDAL and OGR command line "
    6884             :                  "utilities.\nThe project reserves the right to modify, "
    6885             :                  "rename, reorganize, and change the behavior of the utility\n"
    6886             :                  "until it is officially frozen in a future feature release of "
    6887          13 :                  "GDAL.\n";
    6888             :     }
    6889         378 :     return osRet;
    6890             : }
    6891             : 
    6892             : //! @endcond
    6893             : 
    6894             : /************************************************************************/
    6895             : /*                   GDALAlgorithm::GetUsageAsJSON()                    */
    6896             : /************************************************************************/
    6897             : 
    6898         563 : std::string GDALAlgorithm::GetUsageAsJSON() const
    6899             : {
    6900        1126 :     CPLJSONDocument oDoc;
    6901        1126 :     auto oRoot = oDoc.GetRoot();
    6902             : 
    6903         563 :     if (m_displayInJSONUsage)
    6904             :     {
    6905         561 :         oRoot.Add("name", m_name);
    6906         561 :         CPLJSONArray jFullPath;
    6907        1168 :         for (const std::string &s : m_callPath)
    6908             :         {
    6909         607 :             jFullPath.Add(s);
    6910             :         }
    6911         561 :         oRoot.Add("full_path", jFullPath);
    6912             :     }
    6913             : 
    6914         563 :     oRoot.Add("description", m_description);
    6915         563 :     if (!m_helpURL.empty())
    6916             :     {
    6917         562 :         oRoot.Add("short_url", m_helpURL);
    6918         562 :         oRoot.Add("url", GetHelpFullURL());
    6919             :     }
    6920             : 
    6921        1126 :     CPLJSONArray jSubAlgorithms;
    6922         763 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    6923             :     {
    6924         400 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6925         200 :         if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
    6926             :         {
    6927         198 :             CPLJSONDocument oSubDoc;
    6928         198 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    6929         198 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    6930             :         }
    6931             :     }
    6932         563 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    6933             : 
    6934         563 :     if (m_arbitraryLongNameArgsAllowed)
    6935             :     {
    6936           1 :         oRoot.Add("user_provided_arguments_allowed", true);
    6937             :     }
    6938             : 
    6939       10870 :     const auto ProcessArg = [this](const GDALAlgorithmArg *arg)
    6940             :     {
    6941        5435 :         CPLJSONObject jArg;
    6942        5435 :         jArg.Add("name", arg->GetName());
    6943        5435 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    6944        5435 :         jArg.Add("description", arg->GetDescription());
    6945             : 
    6946        5435 :         const auto &metaVar = arg->GetMetaVar();
    6947        5435 :         if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
    6948             :         {
    6949        1671 :             if (metaVar.front() == '<' && metaVar.back() == '>' &&
    6950        1671 :                 metaVar.substr(1, metaVar.size() - 2).find('>') ==
    6951             :                     std::string::npos)
    6952          32 :                 jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
    6953             :             else
    6954         888 :                 jArg.Add("metavar", metaVar);
    6955             :         }
    6956             : 
    6957        5435 :         if (!arg->IsAvailableInPipelineStep())
    6958             :         {
    6959        1622 :             jArg.Add("available_in_pipeline_step", false);
    6960             :         }
    6961             : 
    6962        5435 :         const auto &choices = arg->GetChoices();
    6963        5435 :         if (!choices.empty())
    6964             :         {
    6965         408 :             CPLJSONArray jChoices;
    6966        3500 :             for (const auto &choice : choices)
    6967        3092 :                 jChoices.Add(choice);
    6968         408 :             jArg.Add("choices", jChoices);
    6969             :         }
    6970        5435 :         if (arg->HasDefaultValue())
    6971             :         {
    6972        1188 :             switch (arg->GetType())
    6973             :             {
    6974         424 :                 case GAAT_BOOLEAN:
    6975         424 :                     jArg.Add("default", arg->GetDefault<bool>());
    6976         424 :                     break;
    6977         366 :                 case GAAT_STRING:
    6978         366 :                     jArg.Add("default", arg->GetDefault<std::string>());
    6979         366 :                     break;
    6980         200 :                 case GAAT_INTEGER:
    6981         200 :                     jArg.Add("default", arg->GetDefault<int>());
    6982         200 :                     break;
    6983         178 :                 case GAAT_REAL:
    6984         178 :                     jArg.Add("default", arg->GetDefault<double>());
    6985         178 :                     break;
    6986          18 :                 case GAAT_STRING_LIST:
    6987             :                 {
    6988             :                     const auto &val =
    6989          18 :                         arg->GetDefault<std::vector<std::string>>();
    6990          18 :                     if (val.size() == 1)
    6991             :                     {
    6992          17 :                         jArg.Add("default", val[0]);
    6993             :                     }
    6994             :                     else
    6995             :                     {
    6996           1 :                         CPLJSONArray jArr;
    6997           3 :                         for (const auto &s : val)
    6998             :                         {
    6999           2 :                             jArr.Add(s);
    7000             :                         }
    7001           1 :                         jArg.Add("default", jArr);
    7002             :                     }
    7003          18 :                     break;
    7004             :                 }
    7005           1 :                 case GAAT_INTEGER_LIST:
    7006             :                 {
    7007           1 :                     const auto &val = arg->GetDefault<std::vector<int>>();
    7008           1 :                     if (val.size() == 1)
    7009             :                     {
    7010           0 :                         jArg.Add("default", val[0]);
    7011             :                     }
    7012             :                     else
    7013             :                     {
    7014           1 :                         CPLJSONArray jArr;
    7015           3 :                         for (int i : val)
    7016             :                         {
    7017           2 :                             jArr.Add(i);
    7018             :                         }
    7019           1 :                         jArg.Add("default", jArr);
    7020             :                     }
    7021           1 :                     break;
    7022             :                 }
    7023           1 :                 case GAAT_REAL_LIST:
    7024             :                 {
    7025           1 :                     const auto &val = arg->GetDefault<std::vector<double>>();
    7026           1 :                     if (val.size() == 1)
    7027             :                     {
    7028           0 :                         jArg.Add("default", val[0]);
    7029             :                     }
    7030             :                     else
    7031             :                     {
    7032           1 :                         CPLJSONArray jArr;
    7033           3 :                         for (double d : val)
    7034             :                         {
    7035           2 :                             jArr.Add(d);
    7036             :                         }
    7037           1 :                         jArg.Add("default", jArr);
    7038             :                     }
    7039           1 :                     break;
    7040             :                 }
    7041           0 :                 case GAAT_DATASET:
    7042             :                 case GAAT_DATASET_LIST:
    7043           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    7044             :                              "Unhandled default value for arg %s",
    7045           0 :                              arg->GetName().c_str());
    7046           0 :                     break;
    7047             :             }
    7048             :         }
    7049             : 
    7050        5435 :         const auto [minVal, minValIsIncluded] = arg->GetMinValue();
    7051        5435 :         if (!std::isnan(minVal))
    7052             :         {
    7053         671 :             if (arg->GetType() == GAAT_INTEGER ||
    7054         261 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7055         173 :                 jArg.Add("min_value", static_cast<int>(minVal));
    7056             :             else
    7057         237 :                 jArg.Add("min_value", minVal);
    7058         410 :             jArg.Add("min_value_is_included", minValIsIncluded);
    7059             :         }
    7060             : 
    7061        5435 :         const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
    7062        5435 :         if (!std::isnan(maxVal))
    7063             :         {
    7064         199 :             if (arg->GetType() == GAAT_INTEGER ||
    7065          82 :                 arg->GetType() == GAAT_INTEGER_LIST)
    7066          35 :                 jArg.Add("max_value", static_cast<int>(maxVal));
    7067             :             else
    7068          82 :                 jArg.Add("max_value", maxVal);
    7069         117 :             jArg.Add("max_value_is_included", maxValIsIncluded);
    7070             :         }
    7071             : 
    7072        5435 :         jArg.Add("required", arg->IsRequired());
    7073        5435 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    7074             :         {
    7075        1519 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    7076        1519 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    7077        1519 :             jArg.Add("min_count", arg->GetMinCount());
    7078        1519 :             jArg.Add("max_count", arg->GetMaxCount());
    7079             :         }
    7080             : 
    7081             :         // Process dependencies
    7082        5435 :         const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7083        5435 :         if (!mutualDependencyGroup.empty())
    7084             :         {
    7085          32 :             jArg.Add("mutual_dependency_group", mutualDependencyGroup);
    7086             :         }
    7087             : 
    7088       10870 :         CPLJSONArray jDependencies;
    7089          50 :         for (const auto &dependencyArgumentName :
    7090        5535 :              GetArgDependencies(arg->GetName()))
    7091             :         {
    7092          50 :             jDependencies.Add(dependencyArgumentName);
    7093             :         }
    7094             : 
    7095        5435 :         if (jDependencies.Size() > 0)
    7096             :         {
    7097          50 :             jArg.Add("depends_on", jDependencies);
    7098             :         }
    7099             : 
    7100        5435 :         jArg.Add("category", arg->GetCategory());
    7101             : 
    7102       10620 :         if (arg->GetType() == GAAT_DATASET ||
    7103        5185 :             arg->GetType() == GAAT_DATASET_LIST)
    7104             :         {
    7105             :             {
    7106         453 :                 CPLJSONArray jAr;
    7107         453 :                 if (arg->GetDatasetType() & GDAL_OF_RASTER)
    7108         311 :                     jAr.Add("raster");
    7109         453 :                 if (arg->GetDatasetType() & GDAL_OF_VECTOR)
    7110         179 :                     jAr.Add("vector");
    7111         453 :                 if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
    7112          28 :                     jAr.Add("multidim_raster");
    7113         453 :                 jArg.Add("dataset_type", jAr);
    7114             :             }
    7115             : 
    7116         620 :             const auto GetFlags = [](int flags)
    7117             :             {
    7118         620 :                 CPLJSONArray jAr;
    7119         620 :                 if (flags & GADV_NAME)
    7120         453 :                     jAr.Add("name");
    7121         620 :                 if (flags & GADV_OBJECT)
    7122         571 :                     jAr.Add("dataset");
    7123         620 :                 return jAr;
    7124             :             };
    7125             : 
    7126         453 :             if (arg->IsInput())
    7127             :             {
    7128         453 :                 jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
    7129             :             }
    7130         453 :             if (arg->IsOutput())
    7131             :             {
    7132         167 :                 jArg.Add("output_flags",
    7133         334 :                          GetFlags(arg->GetDatasetOutputFlags()));
    7134             :             }
    7135             :         }
    7136             : 
    7137        5435 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    7138        5435 :         if (!mutualExclusionGroup.empty())
    7139             :         {
    7140         673 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    7141             :         }
    7142             : 
    7143       10870 :         const auto &metadata = arg->GetMetadata();
    7144        5435 :         if (!metadata.empty())
    7145             :         {
    7146         450 :             CPLJSONObject jMetadata;
    7147         939 :             for (const auto &[key, values] : metadata)
    7148             :             {
    7149         978 :                 CPLJSONArray jValue;
    7150        1183 :                 for (const auto &value : values)
    7151         694 :                     jValue.Add(value);
    7152         489 :                 jMetadata.Add(key, jValue);
    7153             :             }
    7154         450 :             jArg.Add("metadata", jMetadata);
    7155             :         }
    7156             : 
    7157       10870 :         return jArg;
    7158         563 :     };
    7159             : 
    7160             :     {
    7161         563 :         CPLJSONArray jArgs;
    7162        8941 :         for (const auto &arg : m_args)
    7163             :         {
    7164        8378 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
    7165        5211 :                 jArgs.Add(ProcessArg(arg.get()));
    7166             :         }
    7167         563 :         oRoot.Add("input_arguments", jArgs);
    7168             :     }
    7169             : 
    7170             :     {
    7171         563 :         CPLJSONArray jArgs;
    7172        8941 :         for (const auto &arg : m_args)
    7173             :         {
    7174        8378 :             if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
    7175          57 :                 jArgs.Add(ProcessArg(arg.get()));
    7176             :         }
    7177         563 :         oRoot.Add("output_arguments", jArgs);
    7178             :     }
    7179             : 
    7180             :     {
    7181         563 :         CPLJSONArray jArgs;
    7182        8941 :         for (const auto &arg : m_args)
    7183             :         {
    7184        8378 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
    7185         167 :                 jArgs.Add(ProcessArg(arg.get()));
    7186             :         }
    7187         563 :         oRoot.Add("input_output_arguments", jArgs);
    7188             :     }
    7189             : 
    7190         563 :     if (m_supportsStreamedOutput)
    7191             :     {
    7192         117 :         oRoot.Add("supports_streamed_output", true);
    7193             :     }
    7194             : 
    7195        1126 :     return oDoc.SaveAsString();
    7196             : }
    7197             : 
    7198             : /************************************************************************/
    7199             : /*                   GDALAlgorithm::GetAutoComplete()                   */
    7200             : /************************************************************************/
    7201             : 
    7202             : std::vector<std::string>
    7203         246 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    7204             :                                bool lastWordIsComplete, bool showAllOptions)
    7205             : {
    7206         492 :     std::vector<std::string> ret;
    7207             : 
    7208             :     // Get inner-most algorithm
    7209         246 :     std::unique_ptr<GDALAlgorithm> curAlgHolder;
    7210         246 :     GDALAlgorithm *curAlg = this;
    7211         487 :     while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
    7212             :     {
    7213             :         auto subAlg = curAlg->InstantiateSubAlgorithm(
    7214         350 :             args.front(), /* suggestionAllowed = */ false);
    7215         350 :         if (!subAlg)
    7216         108 :             break;
    7217         242 :         if (args.size() == 1 && !lastWordIsComplete)
    7218             :         {
    7219           5 :             int nCount = 0;
    7220         115 :             for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
    7221             :             {
    7222         110 :                 if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
    7223           6 :                     nCount++;
    7224             :             }
    7225           5 :             if (nCount >= 2)
    7226             :             {
    7227          11 :                 for (const std::string &subAlgName :
    7228          23 :                      curAlg->GetSubAlgorithmNames())
    7229             :                 {
    7230          11 :                     subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
    7231          11 :                     if (subAlg && !subAlg->IsHidden())
    7232          11 :                         ret.push_back(subAlg->GetName());
    7233             :                 }
    7234           1 :                 return ret;
    7235             :             }
    7236             :         }
    7237         241 :         showAllOptions = false;
    7238         241 :         args.erase(args.begin());
    7239         241 :         curAlgHolder = std::move(subAlg);
    7240         241 :         curAlg = curAlgHolder.get();
    7241             :     }
    7242         245 :     if (curAlg != this)
    7243             :     {
    7244         131 :         curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
    7245             :         return curAlg->GetAutoComplete(args, lastWordIsComplete,
    7246         131 :                                        /* showAllOptions = */ false);
    7247             :     }
    7248             : 
    7249         228 :     std::string option;
    7250         228 :     std::string value;
    7251         114 :     ExtractLastOptionAndValue(args, option, value);
    7252             : 
    7253         141 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    7254          27 :         args.back()[0] == '-')
    7255             :     {
    7256          24 :         const auto &lastArg = args.back();
    7257             :         // List available options
    7258         355 :         for (const auto &arg : GetArgs())
    7259             :         {
    7260         615 :             if (arg->IsHidden() || arg->IsHiddenForCLI() ||
    7261         563 :                 (!showAllOptions &&
    7262         768 :                  (arg->GetName() == "help" || arg->GetName() == "config" ||
    7263         466 :                   arg->GetName() == "version" ||
    7264         233 :                   arg->GetName() == "json-usage")))
    7265             :             {
    7266         116 :                 continue;
    7267             :             }
    7268         215 :             if (!arg->GetShortName().empty())
    7269             :             {
    7270         132 :                 std::string str = std::string("-").append(arg->GetShortName());
    7271          44 :                 if (lastArg == str)
    7272           0 :                     ret.push_back(std::move(str));
    7273             :             }
    7274         215 :             if (lastArg != "-" && lastArg != "--")
    7275             :             {
    7276          54 :                 for (const std::string &alias : arg->GetAliases())
    7277             :                 {
    7278          48 :                     std::string str = std::string("--").append(alias);
    7279          16 :                     if (cpl::starts_with(str, lastArg))
    7280           3 :                         ret.push_back(std::move(str));
    7281             :                 }
    7282             :             }
    7283         215 :             if (!arg->GetName().empty())
    7284             :             {
    7285         645 :                 std::string str = std::string("--").append(arg->GetName());
    7286         215 :                 if (cpl::starts_with(str, lastArg))
    7287         179 :                     ret.push_back(std::move(str));
    7288             :             }
    7289             :         }
    7290          24 :         std::sort(ret.begin(), ret.end());
    7291             :     }
    7292          90 :     else if (!option.empty())
    7293             :     {
    7294             :         // List possible choices for current option
    7295          84 :         auto arg = GetArg(option);
    7296          84 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7297             :         {
    7298          84 :             ret = arg->GetChoices();
    7299          84 :             if (ret.empty())
    7300             :             {
    7301             :                 {
    7302          79 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7303          79 :                     SetParseForAutoCompletion();
    7304          79 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7305             :                 }
    7306          79 :                 ret = arg->GetAutoCompleteChoices(value);
    7307             :             }
    7308             :             else
    7309             :             {
    7310           5 :                 std::sort(ret.begin(), ret.end());
    7311             :             }
    7312          84 :             if (!ret.empty() && ret.back() == value)
    7313             :             {
    7314           2 :                 ret.clear();
    7315             :             }
    7316          82 :             else if (ret.empty())
    7317             :             {
    7318          10 :                 ret.push_back("**");
    7319             :                 // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    7320          20 :                 ret.push_back(std::string("\xC2\xA0"
    7321             :                                           "description: ")
    7322          10 :                                   .append(arg->GetDescription()));
    7323             :             }
    7324             :         }
    7325             :     }
    7326             :     else
    7327             :     {
    7328             :         // List possible sub-algorithms
    7329          59 :         for (const std::string &subAlgName : GetSubAlgorithmNames())
    7330             :         {
    7331         106 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7332          53 :             if (subAlg && !subAlg->IsHidden())
    7333          53 :                 ret.push_back(subAlg->GetName());
    7334             :         }
    7335           6 :         if (!ret.empty())
    7336             :         {
    7337           2 :             std::sort(ret.begin(), ret.end());
    7338             :         }
    7339             : 
    7340             :         // Try filenames
    7341           6 :         if (ret.empty() && !args.empty())
    7342             :         {
    7343             :             {
    7344           3 :                 CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7345           3 :                 SetParseForAutoCompletion();
    7346           3 :                 CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7347             :             }
    7348             : 
    7349           3 :             const std::string &lastArg = args.back();
    7350           3 :             GDALAlgorithmArg *arg = nullptr;
    7351          18 :             for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
    7352          21 :                                      "like", "source", "destination"})
    7353             :             {
    7354          18 :                 if (!arg)
    7355             :                 {
    7356           3 :                     auto newArg = GetArg(name);
    7357           3 :                     if (newArg)
    7358             :                     {
    7359           3 :                         if (!newArg->IsExplicitlySet())
    7360             :                         {
    7361           0 :                             arg = newArg;
    7362             :                         }
    7363           6 :                         else if (newArg->GetType() == GAAT_STRING ||
    7364           5 :                                  newArg->GetType() == GAAT_STRING_LIST ||
    7365           8 :                                  newArg->GetType() == GAAT_DATASET ||
    7366           2 :                                  newArg->GetType() == GAAT_DATASET_LIST)
    7367             :                         {
    7368             :                             VSIStatBufL sStat;
    7369           5 :                             if ((!lastArg.empty() && lastArg.back() == '/') ||
    7370           2 :                                 VSIStatL(lastArg.c_str(), &sStat) != 0)
    7371             :                             {
    7372           3 :                                 arg = newArg;
    7373             :                             }
    7374             :                         }
    7375             :                     }
    7376             :                 }
    7377             :             }
    7378           3 :             if (arg)
    7379             :             {
    7380           3 :                 ret = arg->GetAutoCompleteChoices(lastArg);
    7381             :             }
    7382             :         }
    7383             :     }
    7384             : 
    7385         114 :     return ret;
    7386             : }
    7387             : 
    7388             : /************************************************************************/
    7389             : /*                   GDALAlgorithm::GetFieldIndices()                   */
    7390             : /************************************************************************/
    7391             : 
    7392          44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
    7393             :                                     OGRLayerH hLayer, std::vector<int> &indices)
    7394             : {
    7395          44 :     VALIDATE_POINTER1(hLayer, __func__, false);
    7396             : 
    7397          44 :     const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
    7398             : 
    7399          44 :     if (names.size() == 1 && names[0] == "ALL")
    7400             :     {
    7401          12 :         const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
    7402          28 :         for (int i = 0; i < nSrcFieldCount; ++i)
    7403             :         {
    7404          16 :             indices.push_back(i);
    7405             :         }
    7406             :     }
    7407          32 :     else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
    7408             :     {
    7409           6 :         std::set<int> fieldsAdded;
    7410          14 :         for (const std::string &osFieldName : names)
    7411             :         {
    7412             : 
    7413             :             const int nIdx =
    7414          10 :                 layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
    7415             : 
    7416          10 :             if (nIdx < 0)
    7417             :             {
    7418           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7419             :                          "Field '%s' does not exist in layer '%s'",
    7420           2 :                          osFieldName.c_str(), layer.GetName());
    7421           2 :                 return false;
    7422             :             }
    7423             : 
    7424           8 :             if (fieldsAdded.insert(nIdx).second)
    7425             :             {
    7426           7 :                 indices.push_back(nIdx);
    7427             :             }
    7428             :         }
    7429             :     }
    7430             : 
    7431          42 :     return true;
    7432             : }
    7433             : 
    7434             : /************************************************************************/
    7435             : /*              GDALAlgorithm::ExtractLastOptionAndValue()              */
    7436             : /************************************************************************/
    7437             : 
    7438         114 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    7439             :                                               std::string &option,
    7440             :                                               std::string &value) const
    7441             : {
    7442         114 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    7443             :     {
    7444          84 :         const auto nPosEqual = args.back().find('=');
    7445          84 :         if (nPosEqual == std::string::npos)
    7446             :         {
    7447             :             // Deal with "gdal ... --option"
    7448          65 :             if (GetArg(args.back()))
    7449             :             {
    7450          41 :                 option = args.back();
    7451          41 :                 args.pop_back();
    7452             :             }
    7453             :         }
    7454             :         else
    7455             :         {
    7456             :             // Deal with "gdal ... --option=<value>"
    7457          19 :             if (GetArg(args.back().substr(0, nPosEqual)))
    7458             :             {
    7459          19 :                 option = args.back().substr(0, nPosEqual);
    7460          19 :                 value = args.back().substr(nPosEqual + 1);
    7461          19 :                 args.pop_back();
    7462             :             }
    7463             :         }
    7464             :     }
    7465          55 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    7466          25 :              args[args.size() - 2][0] == '-')
    7467             :     {
    7468             :         // Deal with "gdal ... --option <value>"
    7469          24 :         auto arg = GetArg(args[args.size() - 2]);
    7470          24 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7471             :         {
    7472          24 :             option = args[args.size() - 2];
    7473          24 :             value = args.back();
    7474          24 :             args.pop_back();
    7475             :         }
    7476             :     }
    7477             : 
    7478         114 :     const auto IsKeyValueOption = [](const std::string &osStr)
    7479             :     {
    7480         308 :         return osStr == "--co" || osStr == "--creation-option" ||
    7481         285 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    7482         306 :                osStr == "--oo" || osStr == "--open-option";
    7483             :     };
    7484             : 
    7485         114 :     if (IsKeyValueOption(option))
    7486             :     {
    7487          22 :         const auto nPosEqual = value.find('=');
    7488          22 :         if (nPosEqual != std::string::npos)
    7489             :         {
    7490          11 :             value.resize(nPosEqual);
    7491             :         }
    7492             :     }
    7493         114 : }
    7494             : 
    7495             : /************************************************************************/
    7496             : /*                 GDALAlgorithm::GetArgDependencies()                  */
    7497             : /************************************************************************/
    7498             : 
    7499             : std::vector<std::string>
    7500        7830 : GDALAlgorithm::GetArgDependencies(const std::string &osName) const
    7501             : {
    7502        7830 :     const auto arg = GetArg(osName, false);
    7503        7830 :     if (!arg)
    7504             :     {
    7505           0 :         ReportError(CE_Failure, CPLE_AppDefined, "Argument '%s' does not exist",
    7506             :                     osName.c_str());
    7507           0 :         return {};
    7508             :     }
    7509       15660 :     std::vector<std::string> dependencies = arg->GetDirectDependencies();
    7510        7830 :     if (const auto &mutualDependencyGroup = arg->GetMutualDependencyGroup();
    7511        7830 :         !mutualDependencyGroup.empty())
    7512             :     {
    7513         896 :         for (const auto &otherArg : m_args)
    7514             :         {
    7515        1627 :             if (otherArg.get() == arg ||
    7516         786 :                 mutualDependencyGroup.compare(
    7517         786 :                     otherArg->GetMutualDependencyGroup()) != 0)
    7518         783 :                 continue;
    7519          58 :             dependencies.push_back(otherArg->GetName());
    7520             :         }
    7521             :     }
    7522        7830 :     return dependencies;
    7523             : }
    7524             : 
    7525             : //! @cond Doxygen_Suppress
    7526             : 
    7527             : /************************************************************************/
    7528             : /*                  GDALContainerAlgorithm::RunImpl()                   */
    7529             : /************************************************************************/
    7530             : 
    7531           0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
    7532             : {
    7533           0 :     return false;
    7534             : }
    7535             : 
    7536             : //! @endcond
    7537             : 
    7538             : /************************************************************************/
    7539             : /*                        GDALAlgorithmRelease()                        */
    7540             : /************************************************************************/
    7541             : 
    7542             : /** Release a handle to an algorithm.
    7543             :  *
    7544             :  * @since 3.11
    7545             :  */
    7546       12421 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    7547             : {
    7548       12421 :     delete hAlg;
    7549       12421 : }
    7550             : 
    7551             : /************************************************************************/
    7552             : /*                        GDALAlgorithmGetName()                        */
    7553             : /************************************************************************/
    7554             : 
    7555             : /** Return the algorithm name.
    7556             :  *
    7557             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7558             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    7559             :  * be freed.
    7560             :  * @since 3.11
    7561             :  */
    7562        5699 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    7563             : {
    7564        5699 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7565        5699 :     return hAlg->ptr->GetName().c_str();
    7566             : }
    7567             : 
    7568             : /************************************************************************/
    7569             : /*                    GDALAlgorithmGetDescription()                     */
    7570             : /************************************************************************/
    7571             : 
    7572             : /** Return the algorithm (short) description.
    7573             :  *
    7574             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7575             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7576             :  * not be freed.
    7577             :  * @since 3.11
    7578             :  */
    7579        5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    7580             : {
    7581        5619 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7582        5619 :     return hAlg->ptr->GetDescription().c_str();
    7583             : }
    7584             : 
    7585             : /************************************************************************/
    7586             : /*                  GDALAlgorithmGetLongDescription()                   */
    7587             : /************************************************************************/
    7588             : 
    7589             : /** Return the algorithm (longer) description.
    7590             :  *
    7591             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7592             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7593             :  * not be freed.
    7594             :  * @since 3.11
    7595             :  */
    7596           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    7597             : {
    7598           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7599           2 :     return hAlg->ptr->GetLongDescription().c_str();
    7600             : }
    7601             : 
    7602             : /************************************************************************/
    7603             : /*                    GDALAlgorithmGetHelpFullURL()                     */
    7604             : /************************************************************************/
    7605             : 
    7606             : /** Return the algorithm full URL.
    7607             :  *
    7608             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7609             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    7610             :  * not be freed.
    7611             :  * @since 3.11
    7612             :  */
    7613        4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    7614             : {
    7615        4963 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7616        4963 :     return hAlg->ptr->GetHelpFullURL().c_str();
    7617             : }
    7618             : 
    7619             : /************************************************************************/
    7620             : /*                   GDALAlgorithmHasSubAlgorithms()                    */
    7621             : /************************************************************************/
    7622             : 
    7623             : /** Return whether the algorithm has sub-algorithms.
    7624             :  *
    7625             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7626             :  * @since 3.11
    7627             :  */
    7628        9147 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    7629             : {
    7630        9147 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7631        9147 :     return hAlg->ptr->HasSubAlgorithms();
    7632             : }
    7633             : 
    7634             : /************************************************************************/
    7635             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    7636             : /************************************************************************/
    7637             : 
    7638             : /** Get the names of registered algorithms.
    7639             :  *
    7640             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7641             :  * @return a NULL terminated list of names, which must be destroyed with
    7642             :  * CSLDestroy()
    7643             :  * @since 3.11
    7644             :  */
    7645         707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    7646             : {
    7647         707 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7648         707 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    7649             : }
    7650             : 
    7651             : /************************************************************************/
    7652             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    7653             : /************************************************************************/
    7654             : 
    7655             : /** Instantiate an algorithm by its name (or its alias).
    7656             :  *
    7657             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7658             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    7659             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    7660             :  * or NULL if the algorithm does not exist or another error occurred.
    7661             :  * @since 3.11
    7662             :  */
    7663        8610 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    7664             :                                                     const char *pszSubAlgName)
    7665             : {
    7666        8610 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7667        8610 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    7668       17220 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    7669             :     return subAlg
    7670       17220 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    7671       17220 :                : nullptr;
    7672             : }
    7673             : 
    7674             : /************************************************************************/
    7675             : /*               GDALAlgorithmParseCommandLineArguments()               */
    7676             : /************************************************************************/
    7677             : 
    7678             : /** Parse a command line argument, which does not include the algorithm
    7679             :  * name, to set the value of corresponding arguments.
    7680             :  *
    7681             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7682             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    7683             :  * @return true if successful, false otherwise
    7684             :  * @since 3.11
    7685             :  */
    7686             : 
    7687         352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    7688             :                                             CSLConstList papszArgs)
    7689             : {
    7690         352 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7691         352 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    7692             : }
    7693             : 
    7694             : /************************************************************************/
    7695             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    7696             : /************************************************************************/
    7697             : 
    7698             : /** Return the actual algorithm that is going to be invoked, when the
    7699             :  * current algorithm has sub-algorithms.
    7700             :  *
    7701             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    7702             :  *
    7703             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    7704             :  * the hAlg instance that owns it.
    7705             :  *
    7706             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7707             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    7708             :  * @since 3.11
    7709             :  */
    7710         914 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    7711             : {
    7712         914 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7713         914 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    7714             : }
    7715             : 
    7716             : /************************************************************************/
    7717             : /*                          GDALAlgorithmRun()                          */
    7718             : /************************************************************************/
    7719             : 
    7720             : /** Execute the algorithm, starting with ValidateArguments() and then
    7721             :  * calling RunImpl().
    7722             :  *
    7723             :  * This function must be called at most once per instance.
    7724             :  *
    7725             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7726             :  * @param pfnProgress Progress callback. May be null.
    7727             :  * @param pProgressData Progress callback user data. May be null.
    7728             :  * @return true if successful, false otherwise
    7729             :  * @since 3.11
    7730             :  */
    7731             : 
    7732        2720 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    7733             :                       void *pProgressData)
    7734             : {
    7735        2720 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7736        2720 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    7737             : }
    7738             : 
    7739             : /************************************************************************/
    7740             : /*                       GDALAlgorithmFinalize()                        */
    7741             : /************************************************************************/
    7742             : 
    7743             : /** Complete any pending actions, and return the final status.
    7744             :  * This is typically useful for algorithm that generate an output dataset.
    7745             :  *
    7746             :  * Note that this function does *NOT* release memory associated with the
    7747             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    7748             :  *
    7749             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7750             :  * @return true if successful, false otherwise
    7751             :  * @since 3.11
    7752             :  */
    7753             : 
    7754         896 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    7755             : {
    7756         896 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7757         896 :     return hAlg->ptr->Finalize();
    7758             : }
    7759             : 
    7760             : /************************************************************************/
    7761             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    7762             : /************************************************************************/
    7763             : 
    7764             : /** Return the usage of the algorithm as a JSON-serialized string.
    7765             :  *
    7766             :  * This can be used to dynamically generate interfaces to algorithms.
    7767             :  *
    7768             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7769             :  * @return a string that must be freed with CPLFree()
    7770             :  * @since 3.11
    7771             :  */
    7772           6 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    7773             : {
    7774           6 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7775           6 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    7776             : }
    7777             : 
    7778             : /************************************************************************/
    7779             : /*                      GDALAlgorithmGetArgNames()                      */
    7780             : /************************************************************************/
    7781             : 
    7782             : /** Return the list of available argument names.
    7783             :  *
    7784             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7785             :  * @return a NULL terminated list of names, which must be destroyed with
    7786             :  * CSLDestroy()
    7787             :  * @since 3.11
    7788             :  */
    7789       15433 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    7790             : {
    7791       15433 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7792       30866 :     CPLStringList list;
    7793      344478 :     for (const auto &arg : hAlg->ptr->GetArgs())
    7794      329045 :         list.AddString(arg->GetName().c_str());
    7795       15433 :     return list.StealList();
    7796             : }
    7797             : 
    7798             : /************************************************************************/
    7799             : /*                        GDALAlgorithmGetArg()                         */
    7800             : /************************************************************************/
    7801             : 
    7802             : /** Return an argument from its name.
    7803             :  *
    7804             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7805             :  *
    7806             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7807             :  * @param pszArgName Argument name. Must NOT be null.
    7808             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7809             :  * or nullptr in case of error
    7810             :  * @since 3.11
    7811             :  */
    7812      329955 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    7813             :                                       const char *pszArgName)
    7814             : {
    7815      329955 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7816      329955 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7817      659910 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7818      329955 :                                  /* isConst = */ true);
    7819      329955 :     if (!arg)
    7820           3 :         return nullptr;
    7821      329952 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7822             : }
    7823             : 
    7824             : /************************************************************************/
    7825             : /*                    GDALAlgorithmGetArgNonConst()                     */
    7826             : /************************************************************************/
    7827             : 
    7828             : /** Return an argument from its name, possibly allowing creation of user-provided
    7829             :  * argument if the algorithm allow it.
    7830             :  *
    7831             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7832             :  *
    7833             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7834             :  * @param pszArgName Argument name. Must NOT be null.
    7835             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7836             :  * or nullptr in case of error
    7837             :  * @since 3.12
    7838             :  */
    7839        9756 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
    7840             :                                               const char *pszArgName)
    7841             : {
    7842        9756 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7843        9756 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7844       19512 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7845        9756 :                                  /* isConst = */ false);
    7846        9756 :     if (!arg)
    7847           2 :         return nullptr;
    7848        9754 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7849             : }
    7850             : 
    7851             : /************************************************************************/
    7852             : /*                  GDALAlgorithmGetArgDependencies()                   */
    7853             : /************************************************************************/
    7854             : 
    7855             : /** Return the list of argument names the specified argument depends on.
    7856             :  *
    7857             :  *  This includes both regular dependencies and mutual dependencies.
    7858             :  *
    7859             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7860             :  * @param pszArgName Argument name. Must NOT be null.
    7861             :  * @return a NULL terminated list of names, which must be destroyed with
    7862             :  * CSLDestroy()
    7863             :  * @since 3.11
    7864             :  */
    7865           7 : char **GDALAlgorithmGetArgDependencies(GDALAlgorithmH hAlg,
    7866             :                                        const char *pszArgName)
    7867             : {
    7868           7 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7869           7 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7870           7 :     return CPLStringList(hAlg->ptr->GetArgDependencies(pszArgName)).StealList();
    7871             : }
    7872             : 
    7873             : /************************************************************************/
    7874             : /*                      GDALAlgorithmArgRelease()                       */
    7875             : /************************************************************************/
    7876             : 
    7877             : /** Release a handle to an argument.
    7878             :  *
    7879             :  * @since 3.11
    7880             :  */
    7881      339706 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    7882             : {
    7883      339706 :     delete hArg;
    7884      339706 : }
    7885             : 
    7886             : /************************************************************************/
    7887             : /*                      GDALAlgorithmArgGetName()                       */
    7888             : /************************************************************************/
    7889             : 
    7890             : /** Return the name of an argument.
    7891             :  *
    7892             :  * @param hArg Handle to an argument. Must NOT be null.
    7893             :  * @return argument name whose lifetime is bound to hArg and which must not
    7894             :  * be freed.
    7895             :  * @since 3.11
    7896             :  */
    7897       18865 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    7898             : {
    7899       18865 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7900       18865 :     return hArg->ptr->GetName().c_str();
    7901             : }
    7902             : 
    7903             : /************************************************************************/
    7904             : /*                      GDALAlgorithmArgGetType()                       */
    7905             : /************************************************************************/
    7906             : 
    7907             : /** Get the type of an argument
    7908             :  *
    7909             :  * @param hArg Handle to an argument. Must NOT be null.
    7910             :  * @since 3.11
    7911             :  */
    7912      419641 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    7913             : {
    7914      419641 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    7915      419641 :     return hArg->ptr->GetType();
    7916             : }
    7917             : 
    7918             : /************************************************************************/
    7919             : /*                   GDALAlgorithmArgGetDescription()                   */
    7920             : /************************************************************************/
    7921             : 
    7922             : /** Return the description of an argument.
    7923             :  *
    7924             :  * @param hArg Handle to an argument. Must NOT be null.
    7925             :  * @return argument description whose lifetime is bound to hArg and which must not
    7926             :  * be freed.
    7927             :  * @since 3.11
    7928             :  */
    7929       83723 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    7930             : {
    7931       83723 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7932       83723 :     return hArg->ptr->GetDescription().c_str();
    7933             : }
    7934             : 
    7935             : /************************************************************************/
    7936             : /*                    GDALAlgorithmArgGetShortName()                    */
    7937             : /************************************************************************/
    7938             : 
    7939             : /** Return the short name, or empty string if there is none
    7940             :  *
    7941             :  * @param hArg Handle to an argument. Must NOT be null.
    7942             :  * @return short name whose lifetime is bound to hArg and which must not
    7943             :  * be freed.
    7944             :  * @since 3.11
    7945             :  */
    7946           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    7947             : {
    7948           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7949           1 :     return hArg->ptr->GetShortName().c_str();
    7950             : }
    7951             : 
    7952             : /************************************************************************/
    7953             : /*                     GDALAlgorithmArgGetAliases()                     */
    7954             : /************************************************************************/
    7955             : 
    7956             : /** Return the aliases (potentially none)
    7957             :  *
    7958             :  * @param hArg Handle to an argument. Must NOT be null.
    7959             :  * @return a NULL terminated list of names, which must be destroyed with
    7960             :  * CSLDestroy()
    7961             : 
    7962             :  * @since 3.11
    7963             :  */
    7964      158425 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    7965             : {
    7966      158425 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7967      158425 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    7968             : }
    7969             : 
    7970             : /************************************************************************/
    7971             : /*                     GDALAlgorithmArgGetMetaVar()                     */
    7972             : /************************************************************************/
    7973             : 
    7974             : /** Return the "meta-var" hint.
    7975             :  *
    7976             :  * By default, the meta-var value is the long name of the argument in
    7977             :  * upper case.
    7978             :  *
    7979             :  * @param hArg Handle to an argument. Must NOT be null.
    7980             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    7981             :  * be freed.
    7982             :  * @since 3.11
    7983             :  */
    7984           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    7985             : {
    7986           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7987           1 :     return hArg->ptr->GetMetaVar().c_str();
    7988             : }
    7989             : 
    7990             : /************************************************************************/
    7991             : /*                    GDALAlgorithmArgGetCategory()                     */
    7992             : /************************************************************************/
    7993             : 
    7994             : /** Return the argument category
    7995             :  *
    7996             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    7997             :  *
    7998             :  * @param hArg Handle to an argument. Must NOT be null.
    7999             :  * @return category whose lifetime is bound to hArg and which must not
    8000             :  * be freed.
    8001             :  * @since 3.11
    8002             :  */
    8003           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    8004             : {
    8005           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8006           1 :     return hArg->ptr->GetCategory().c_str();
    8007             : }
    8008             : 
    8009             : /************************************************************************/
    8010             : /*                    GDALAlgorithmArgIsPositional()                    */
    8011             : /************************************************************************/
    8012             : 
    8013             : /** Return if the argument is a positional one.
    8014             :  *
    8015             :  * @param hArg Handle to an argument. Must NOT be null.
    8016             :  * @since 3.11
    8017             :  */
    8018           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    8019             : {
    8020           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8021           1 :     return hArg->ptr->IsPositional();
    8022             : }
    8023             : 
    8024             : /************************************************************************/
    8025             : /*                     GDALAlgorithmArgIsRequired()                     */
    8026             : /************************************************************************/
    8027             : 
    8028             : /** Return whether the argument is required. Defaults to false.
    8029             :  *
    8030             :  * @param hArg Handle to an argument. Must NOT be null.
    8031             :  * @since 3.11
    8032             :  */
    8033      158425 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    8034             : {
    8035      158425 :     VALIDATE_POINTER1(hArg, __func__, false);
    8036      158425 :     return hArg->ptr->IsRequired();
    8037             : }
    8038             : 
    8039             : /************************************************************************/
    8040             : /*                    GDALAlgorithmArgGetMinCount()                     */
    8041             : /************************************************************************/
    8042             : 
    8043             : /** Return the minimum number of values for the argument.
    8044             :  *
    8045             :  * Defaults to 0.
    8046             :  * Only applies to list type of arguments.
    8047             :  *
    8048             :  * @param hArg Handle to an argument. Must NOT be null.
    8049             :  * @since 3.11
    8050             :  */
    8051           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    8052             : {
    8053           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8054           1 :     return hArg->ptr->GetMinCount();
    8055             : }
    8056             : 
    8057             : /************************************************************************/
    8058             : /*                    GDALAlgorithmArgGetMaxCount()                     */
    8059             : /************************************************************************/
    8060             : 
    8061             : /** Return the maximum number of values for the argument.
    8062             :  *
    8063             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    8064             :  * Only applies to list type of arguments.
    8065             :  *
    8066             :  * @param hArg Handle to an argument. Must NOT be null.
    8067             :  * @since 3.11
    8068             :  */
    8069           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    8070             : {
    8071           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8072           1 :     return hArg->ptr->GetMaxCount();
    8073             : }
    8074             : 
    8075             : /************************************************************************/
    8076             : /*               GDALAlgorithmArgGetPackedValuesAllowed()               */
    8077             : /************************************************************************/
    8078             : 
    8079             : /** Return whether, for list type of arguments, several values, space
    8080             :  * separated, may be specified. That is "--foo=bar,baz".
    8081             :  * The default is true.
    8082             :  *
    8083             :  * @param hArg Handle to an argument. Must NOT be null.
    8084             :  * @since 3.11
    8085             :  */
    8086           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    8087             : {
    8088           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8089           1 :     return hArg->ptr->GetPackedValuesAllowed();
    8090             : }
    8091             : 
    8092             : /************************************************************************/
    8093             : /*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
    8094             : /************************************************************************/
    8095             : 
    8096             : /** Return whether, for list type of arguments, the argument may be
    8097             :  * repeated. That is "--foo=bar --foo=baz".
    8098             :  * The default is true.
    8099             :  *
    8100             :  * @param hArg Handle to an argument. Must NOT be null.
    8101             :  * @since 3.11
    8102             :  */
    8103           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    8104             : {
    8105           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8106           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    8107             : }
    8108             : 
    8109             : /************************************************************************/
    8110             : /*                     GDALAlgorithmArgGetChoices()                     */
    8111             : /************************************************************************/
    8112             : 
    8113             : /** Return the allowed values (as strings) for the argument.
    8114             :  *
    8115             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    8116             :  *
    8117             :  * @param hArg Handle to an argument. Must NOT be null.
    8118             :  * @return a NULL terminated list of names, which must be destroyed with
    8119             :  * CSLDestroy()
    8120             : 
    8121             :  * @since 3.11
    8122             :  */
    8123           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    8124             : {
    8125           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8126           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    8127             : }
    8128             : 
    8129             : /************************************************************************/
    8130             : /*                  GDALAlgorithmArgGetMetadataItem()                   */
    8131             : /************************************************************************/
    8132             : 
    8133             : /** Return the values of the metadata item of an argument.
    8134             :  *
    8135             :  * @param hArg Handle to an argument. Must NOT be null.
    8136             :  * @param pszItem Name of the item. Must NOT be null.
    8137             :  * @return a NULL terminated list of values, which must be destroyed with
    8138             :  * CSLDestroy()
    8139             : 
    8140             :  * @since 3.11
    8141             :  */
    8142          63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
    8143             :                                        const char *pszItem)
    8144             : {
    8145          63 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8146          63 :     VALIDATE_POINTER1(pszItem, __func__, nullptr);
    8147          63 :     const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
    8148          63 :     return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
    8149             : }
    8150             : 
    8151             : /************************************************************************/
    8152             : /*                  GDALAlgorithmArgIsExplicitlySet()                   */
    8153             : /************************************************************************/
    8154             : 
    8155             : /** Return whether the argument value has been explicitly set with Set()
    8156             :  *
    8157             :  * @param hArg Handle to an argument. Must NOT be null.
    8158             :  * @since 3.11
    8159             :  */
    8160         632 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    8161             : {
    8162         632 :     VALIDATE_POINTER1(hArg, __func__, false);
    8163         632 :     return hArg->ptr->IsExplicitlySet();
    8164             : }
    8165             : 
    8166             : /************************************************************************/
    8167             : /*                  GDALAlgorithmArgHasDefaultValue()                   */
    8168             : /************************************************************************/
    8169             : 
    8170             : /** Return if the argument has a declared default value.
    8171             :  *
    8172             :  * @param hArg Handle to an argument. Must NOT be null.
    8173             :  * @since 3.11
    8174             :  */
    8175           2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    8176             : {
    8177           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8178           2 :     return hArg->ptr->HasDefaultValue();
    8179             : }
    8180             : 
    8181             : /************************************************************************/
    8182             : /*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
    8183             : /************************************************************************/
    8184             : 
    8185             : /** Return the argument default value as a integer.
    8186             :  *
    8187             :  * Must only be called on arguments whose type is GAAT_BOOLEAN
    8188             :  *
    8189             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8190             :  * argument has a default value.
    8191             :  *
    8192             :  * @param hArg Handle to an argument. Must NOT be null.
    8193             :  * @since 3.12
    8194             :  */
    8195           3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
    8196             : {
    8197           3 :     VALIDATE_POINTER1(hArg, __func__, false);
    8198           3 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8199             :     {
    8200           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8201             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8202             :                  __func__);
    8203           1 :         return false;
    8204             :     }
    8205           2 :     return hArg->ptr->GetDefault<bool>();
    8206             : }
    8207             : 
    8208             : /************************************************************************/
    8209             : /*                 GDALAlgorithmArgGetDefaultAsString()                 */
    8210             : /************************************************************************/
    8211             : 
    8212             : /** Return the argument default value as a string.
    8213             :  *
    8214             :  * Must only be called on arguments whose type is GAAT_STRING.
    8215             :  *
    8216             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8217             :  * argument has a default value.
    8218             :  *
    8219             :  * @param hArg Handle to an argument. Must NOT be null.
    8220             :  * @return string whose lifetime is bound to hArg and which must not
    8221             :  * be freed.
    8222             :  * @since 3.11
    8223             :  */
    8224           3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
    8225             : {
    8226           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8227           3 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8228             :     {
    8229           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8230             :                  "%s must only be called on arguments of type GAAT_STRING",
    8231             :                  __func__);
    8232           2 :         return nullptr;
    8233             :     }
    8234           1 :     return hArg->ptr->GetDefault<std::string>().c_str();
    8235             : }
    8236             : 
    8237             : /************************************************************************/
    8238             : /*                GDALAlgorithmArgGetDefaultAsInteger()                 */
    8239             : /************************************************************************/
    8240             : 
    8241             : /** Return the argument default value as a integer.
    8242             :  *
    8243             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8244             :  *
    8245             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8246             :  * argument has a default value.
    8247             :  *
    8248             :  * @param hArg Handle to an argument. Must NOT be null.
    8249             :  * @since 3.12
    8250             :  */
    8251           3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
    8252             : {
    8253           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8254           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8255             :     {
    8256           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8257             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8258             :                  __func__);
    8259           2 :         return 0;
    8260             :     }
    8261           1 :     return hArg->ptr->GetDefault<int>();
    8262             : }
    8263             : 
    8264             : /************************************************************************/
    8265             : /*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
    8266             : /************************************************************************/
    8267             : 
    8268             : /** Return the argument default value as a double.
    8269             :  *
    8270             :  * Must only be called on arguments whose type is GAAT_REAL
    8271             :  *
    8272             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8273             :  * argument has a default value.
    8274             :  *
    8275             :  * @param hArg Handle to an argument. Must NOT be null.
    8276             :  * @since 3.12
    8277             :  */
    8278           3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
    8279             : {
    8280           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8281           3 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8282             :     {
    8283           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8284             :                  "%s must only be called on arguments of type GAAT_REAL",
    8285             :                  __func__);
    8286           2 :         return 0;
    8287             :     }
    8288           1 :     return hArg->ptr->GetDefault<double>();
    8289             : }
    8290             : 
    8291             : /************************************************************************/
    8292             : /*               GDALAlgorithmArgGetDefaultAsStringList()               */
    8293             : /************************************************************************/
    8294             : 
    8295             : /** Return the argument default value as a string list.
    8296             :  *
    8297             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8298             :  *
    8299             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8300             :  * argument has a default value.
    8301             :  *
    8302             :  * @param hArg Handle to an argument. Must NOT be null.
    8303             :  * @return a NULL terminated list of names, which must be destroyed with
    8304             :  * CSLDestroy()
    8305             : 
    8306             :  * @since 3.12
    8307             :  */
    8308           3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
    8309             : {
    8310           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8311           3 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8312             :     {
    8313           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8314             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8315             :                  __func__);
    8316           2 :         return nullptr;
    8317             :     }
    8318           2 :     return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
    8319           1 :         .StealList();
    8320             : }
    8321             : 
    8322             : /************************************************************************/
    8323             : /*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
    8324             : /************************************************************************/
    8325             : 
    8326             : /** Return the argument default value as a integer list.
    8327             :  *
    8328             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8329             :  *
    8330             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8331             :  * argument has a default value.
    8332             :  *
    8333             :  * @param hArg Handle to an argument. Must NOT be null.
    8334             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8335             :  * @since 3.12
    8336             :  */
    8337           3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
    8338             :                                                    size_t *pnCount)
    8339             : {
    8340           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8341           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8342           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8343             :     {
    8344           2 :         CPLError(
    8345             :             CE_Failure, CPLE_AppDefined,
    8346             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8347             :             __func__);
    8348           2 :         *pnCount = 0;
    8349           2 :         return nullptr;
    8350             :     }
    8351           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
    8352           1 :     *pnCount = val.size();
    8353           1 :     return val.data();
    8354             : }
    8355             : 
    8356             : /************************************************************************/
    8357             : /*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
    8358             : /************************************************************************/
    8359             : 
    8360             : /** Return the argument default value as a real list.
    8361             :  *
    8362             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8363             :  *
    8364             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8365             :  * argument has a default value.
    8366             :  *
    8367             :  * @param hArg Handle to an argument. Must NOT be null.
    8368             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8369             :  * @since 3.12
    8370             :  */
    8371           3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
    8372             :                                                      size_t *pnCount)
    8373             : {
    8374           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8375           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8376           3 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8377             :     {
    8378           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8379             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8380             :                  __func__);
    8381           2 :         *pnCount = 0;
    8382           2 :         return nullptr;
    8383             :     }
    8384           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
    8385           1 :     *pnCount = val.size();
    8386           1 :     return val.data();
    8387             : }
    8388             : 
    8389             : /************************************************************************/
    8390             : /*                      GDALAlgorithmArgIsHidden()                      */
    8391             : /************************************************************************/
    8392             : 
    8393             : /** Return whether the argument is hidden (for GDAL internal use)
    8394             :  *
    8395             :  * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
    8396             :  * GDALAlgorithmArgIsHiddenForAPI().
    8397             :  *
    8398             :  * @param hArg Handle to an argument. Must NOT be null.
    8399             :  * @since 3.12
    8400             :  */
    8401           1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
    8402             : {
    8403           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8404           1 :     return hArg->ptr->IsHidden();
    8405             : }
    8406             : 
    8407             : /************************************************************************/
    8408             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    8409             : /************************************************************************/
    8410             : 
    8411             : /** Return whether the argument must not be mentioned in CLI usage.
    8412             :  *
    8413             :  * For example, "output-value" for "gdal raster info", which is only
    8414             :  * meant when the algorithm is used from a non-CLI context.
    8415             :  *
    8416             :  * @param hArg Handle to an argument. Must NOT be null.
    8417             :  * @since 3.11
    8418             :  */
    8419           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    8420             : {
    8421           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8422           1 :     return hArg->ptr->IsHiddenForCLI();
    8423             : }
    8424             : 
    8425             : /************************************************************************/
    8426             : /*                   GDALAlgorithmArgIsHiddenForAPI()                   */
    8427             : /************************************************************************/
    8428             : 
    8429             : /** Return whether the argument must not be mentioned in the context of an
    8430             :  * API use.
    8431             :  * Said otherwise, if it is only for CLI usage.
    8432             :  *
    8433             :  * For example "--help"
    8434             :  *
    8435             :  * @param hArg Handle to an argument. Must NOT be null.
    8436             :  * @since 3.12
    8437             :  */
    8438      214103 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
    8439             : {
    8440      214103 :     VALIDATE_POINTER1(hArg, __func__, false);
    8441      214103 :     return hArg->ptr->IsHiddenForAPI();
    8442             : }
    8443             : 
    8444             : /************************************************************************/
    8445             : /*                    GDALAlgorithmArgIsOnlyForCLI()                    */
    8446             : /************************************************************************/
    8447             : 
    8448             : /** Return whether the argument must not be mentioned in the context of an
    8449             :  * API use.
    8450             :  * Said otherwise, if it is only for CLI usage.
    8451             :  *
    8452             :  * For example "--help"
    8453             :  *
    8454             :  * @param hArg Handle to an argument. Must NOT be null.
    8455             :  * @since 3.11
    8456             :  * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
    8457             :  */
    8458           0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    8459             : {
    8460           0 :     VALIDATE_POINTER1(hArg, __func__, false);
    8461           0 :     return hArg->ptr->IsHiddenForAPI();
    8462             : }
    8463             : 
    8464             : /************************************************************************/
    8465             : /*             GDALAlgorithmArgIsAvailableInPipelineStep()              */
    8466             : /************************************************************************/
    8467             : 
    8468             : /** Return whether the argument is available in a pipeline step.
    8469             :  *
    8470             :  * If false, it is only available in standalone mode.
    8471             :  *
    8472             :  * @param hArg Handle to an argument. Must NOT be null.
    8473             :  * @since 3.13
    8474             :  */
    8475           2 : bool GDALAlgorithmArgIsAvailableInPipelineStep(GDALAlgorithmArgH hArg)
    8476             : {
    8477           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8478           2 :     return hArg->ptr->IsAvailableInPipelineStep();
    8479             : }
    8480             : 
    8481             : /************************************************************************/
    8482             : /*                      GDALAlgorithmArgIsInput()                       */
    8483             : /************************************************************************/
    8484             : 
    8485             : /** Indicate whether the value of the argument is read-only during the
    8486             :  * execution of the algorithm.
    8487             :  *
    8488             :  * Default is true.
    8489             :  *
    8490             :  * @param hArg Handle to an argument. Must NOT be null.
    8491             :  * @since 3.11
    8492             :  */
    8493      211233 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    8494             : {
    8495      211233 :     VALIDATE_POINTER1(hArg, __func__, false);
    8496      211233 :     return hArg->ptr->IsInput();
    8497             : }
    8498             : 
    8499             : /************************************************************************/
    8500             : /*                      GDALAlgorithmArgIsOutput()                      */
    8501             : /************************************************************************/
    8502             : 
    8503             : /** Return whether (at least part of) the value of the argument is set
    8504             :  * during the execution of the algorithm.
    8505             :  *
    8506             :  * For example, "output-value" for "gdal raster info"
    8507             :  * Default is false.
    8508             :  * An argument may return both IsInput() and IsOutput() as true.
    8509             :  * For example the "gdal raster convert" algorithm consumes the dataset
    8510             :  * name of its "output" argument, and sets the dataset object during its
    8511             :  * execution.
    8512             :  *
    8513             :  * @param hArg Handle to an argument. Must NOT be null.
    8514             :  * @since 3.11
    8515             :  */
    8516      117788 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    8517             : {
    8518      117788 :     VALIDATE_POINTER1(hArg, __func__, false);
    8519      117788 :     return hArg->ptr->IsOutput();
    8520             : }
    8521             : 
    8522             : /************************************************************************/
    8523             : /*                   GDALAlgorithmArgGetDatasetType()                   */
    8524             : /************************************************************************/
    8525             : 
    8526             : /** Get which type of dataset is allowed / generated.
    8527             :  *
    8528             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    8529             :  * GDAL_OF_MULTIDIM_RASTER.
    8530             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8531             :  *
    8532             :  * @param hArg Handle to an argument. Must NOT be null.
    8533             :  * @since 3.11
    8534             :  */
    8535           2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
    8536             : {
    8537           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8538           2 :     return hArg->ptr->GetDatasetType();
    8539             : }
    8540             : 
    8541             : /************************************************************************/
    8542             : /*                GDALAlgorithmArgGetDatasetInputFlags()                */
    8543             : /************************************************************************/
    8544             : 
    8545             : /** Indicates which components among name and dataset are accepted as
    8546             :  * input, when this argument serves as an input.
    8547             :  *
    8548             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    8549             :  * input.
    8550             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8551             :  * accepted as input.
    8552             :  * If both bits are set, the algorithm can accept either a name or a dataset
    8553             :  * object.
    8554             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8555             :  *
    8556             :  * @param hArg Handle to an argument. Must NOT be null.
    8557             :  * @return string whose lifetime is bound to hAlg and which must not
    8558             :  * be freed.
    8559             :  * @since 3.11
    8560             :  */
    8561           2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
    8562             : {
    8563           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8564           2 :     return hArg->ptr->GetDatasetInputFlags();
    8565             : }
    8566             : 
    8567             : /************************************************************************/
    8568             : /*               GDALAlgorithmArgGetDatasetOutputFlags()                */
    8569             : /************************************************************************/
    8570             : 
    8571             : /** Indicates which components among name and dataset are modified,
    8572             :  * when this argument serves as an output.
    8573             :  *
    8574             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    8575             :  * output (that is the algorithm will generate the name. Rarely used).
    8576             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8577             :  * generated as output, and available for use after the algorithm has
    8578             :  * completed.
    8579             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8580             :  *
    8581             :  * @param hArg Handle to an argument. Must NOT be null.
    8582             :  * @return string whose lifetime is bound to hAlg and which must not
    8583             :  * be freed.
    8584             :  * @since 3.11
    8585             :  */
    8586           2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
    8587             : {
    8588           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8589           2 :     return hArg->ptr->GetDatasetOutputFlags();
    8590             : }
    8591             : 
    8592             : /************************************************************************/
    8593             : /*              GDALAlgorithmArgGetMutualExclusionGroup()               */
    8594             : /************************************************************************/
    8595             : 
    8596             : /** Return the name of the mutual exclusion group to which this argument
    8597             :  * belongs to.
    8598             :  *
    8599             :  * Or empty string if it does not belong to any exclusion group.
    8600             :  *
    8601             :  * @param hArg Handle to an argument. Must NOT be null.
    8602             :  * @return string whose lifetime is bound to hArg and which must not
    8603             :  * be freed.
    8604             :  * @since 3.11
    8605             :  */
    8606           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    8607             : {
    8608           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8609           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    8610             : }
    8611             : 
    8612             : /************************************************************************/
    8613             : /*              GDALAlgorithmArgGetMutualDependencyGroup()              */
    8614             : /************************************************************************/
    8615             : 
    8616             : /** Return the name of the mutual dependency group to which this argument
    8617             :  * belongs to.
    8618             :  *
    8619             :  * Or empty string if it does not belong to any dependency group.
    8620             :  *
    8621             :  * @param hArg Handle to an argument. Must NOT be null.
    8622             :  * @return string whose lifetime is bound to hArg and which must not
    8623             :  * be freed.
    8624             :  * @since 3.13
    8625             :  */
    8626           5 : const char *GDALAlgorithmArgGetMutualDependencyGroup(GDALAlgorithmArgH hArg)
    8627             : {
    8628           5 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8629           5 :     return hArg->ptr->GetMutualDependencyGroup().c_str();
    8630             : }
    8631             : 
    8632             : /************************************************************************/
    8633             : /*               GDALAlgorithmArgGetDirectDependencies()                */
    8634             : /************************************************************************/
    8635             : 
    8636             : /** Return the list of names of arguments that this argument depends on.
    8637             :  *
    8638             :  *  This is not necessarily a symmetric relationship.
    8639             :  *  If argument A depends on argument B, it doesn't mean that B depends on A.
    8640             :  *  Mutual dependency groups are a special case of dependencies,
    8641             :  *  where all arguments of the group depend on each other and are not
    8642             :  *  returned by this method.
    8643             :  *
    8644             :  * @param hArg Handle to an argument. Must NOT be null.
    8645             :  * @return a NULL terminated list of names, which must be destroyed with
    8646             :  * CSLDestroy()
    8647             :  * @since 3.13
    8648             :  */
    8649           7 : char **GDALAlgorithmArgGetDirectDependencies(GDALAlgorithmArgH hArg)
    8650             : {
    8651           7 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8652           7 :     return CPLStringList(hArg->ptr->GetDirectDependencies()).StealList();
    8653             : }
    8654             : 
    8655             : /************************************************************************/
    8656             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    8657             : /************************************************************************/
    8658             : 
    8659             : /** Return the argument value as a boolean.
    8660             :  *
    8661             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    8662             :  *
    8663             :  * @param hArg Handle to an argument. Must NOT be null.
    8664             :  * @since 3.11
    8665             :  */
    8666           8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    8667             : {
    8668           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    8669           8 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8670             :     {
    8671           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8672             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8673             :                  __func__);
    8674           1 :         return false;
    8675             :     }
    8676           7 :     return hArg->ptr->Get<bool>();
    8677             : }
    8678             : 
    8679             : /************************************************************************/
    8680             : /*                    GDALAlgorithmArgGetAsString()                     */
    8681             : /************************************************************************/
    8682             : 
    8683             : /** Return the argument value as a string.
    8684             :  *
    8685             :  * Must only be called on arguments whose type is GAAT_STRING.
    8686             :  *
    8687             :  * @param hArg Handle to an argument. Must NOT be null.
    8688             :  * @return string whose lifetime is bound to hArg and which must not
    8689             :  * be freed.
    8690             :  * @since 3.11
    8691             :  */
    8692         354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    8693             : {
    8694         354 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8695         354 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8696             :     {
    8697           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8698             :                  "%s must only be called on arguments of type GAAT_STRING",
    8699             :                  __func__);
    8700           1 :         return nullptr;
    8701             :     }
    8702         353 :     return hArg->ptr->Get<std::string>().c_str();
    8703             : }
    8704             : 
    8705             : /************************************************************************/
    8706             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    8707             : /************************************************************************/
    8708             : 
    8709             : /** Return the argument value as a GDALArgDatasetValueH.
    8710             :  *
    8711             :  * Must only be called on arguments whose type is GAAT_DATASET
    8712             :  *
    8713             :  * @param hArg Handle to an argument. Must NOT be null.
    8714             :  * @return handle to a GDALArgDatasetValue that must be released with
    8715             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    8716             :  * the one of hArg.
    8717             :  * @since 3.11
    8718             :  */
    8719        3207 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    8720             : {
    8721        3207 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8722        3207 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    8723             :     {
    8724           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8725             :                  "%s must only be called on arguments of type GAAT_DATASET",
    8726             :                  __func__);
    8727           1 :         return nullptr;
    8728             :     }
    8729        3206 :     return std::make_unique<GDALArgDatasetValueHS>(
    8730        6412 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    8731        3206 :         .release();
    8732             : }
    8733             : 
    8734             : /************************************************************************/
    8735             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    8736             : /************************************************************************/
    8737             : 
    8738             : /** Return the argument value as a integer.
    8739             :  *
    8740             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8741             :  *
    8742             :  * @param hArg Handle to an argument. Must NOT be null.
    8743             :  * @since 3.11
    8744             :  */
    8745          26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    8746             : {
    8747          26 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8748          26 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8749             :     {
    8750           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8751             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8752             :                  __func__);
    8753           1 :         return 0;
    8754             :     }
    8755          25 :     return hArg->ptr->Get<int>();
    8756             : }
    8757             : 
    8758             : /************************************************************************/
    8759             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    8760             : /************************************************************************/
    8761             : 
    8762             : /** Return the argument value as a double.
    8763             :  *
    8764             :  * Must only be called on arguments whose type is GAAT_REAL
    8765             :  *
    8766             :  * @param hArg Handle to an argument. Must NOT be null.
    8767             :  * @since 3.11
    8768             :  */
    8769           8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    8770             : {
    8771           8 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8772           8 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8773             :     {
    8774           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8775             :                  "%s must only be called on arguments of type GAAT_REAL",
    8776             :                  __func__);
    8777           1 :         return 0;
    8778             :     }
    8779           7 :     return hArg->ptr->Get<double>();
    8780             : }
    8781             : 
    8782             : /************************************************************************/
    8783             : /*                  GDALAlgorithmArgGetAsStringList()                   */
    8784             : /************************************************************************/
    8785             : 
    8786             : /** Return the argument value as a string list.
    8787             :  *
    8788             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8789             :  *
    8790             :  * @param hArg Handle to an argument. Must NOT be null.
    8791             :  * @return a NULL terminated list of names, which must be destroyed with
    8792             :  * CSLDestroy()
    8793             : 
    8794             :  * @since 3.11
    8795             :  */
    8796           4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    8797             : {
    8798           4 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8799           4 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8800             :     {
    8801           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8802             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8803             :                  __func__);
    8804           1 :         return nullptr;
    8805             :     }
    8806           6 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    8807           3 :         .StealList();
    8808             : }
    8809             : 
    8810             : /************************************************************************/
    8811             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    8812             : /************************************************************************/
    8813             : 
    8814             : /** Return the argument value as a integer list.
    8815             :  *
    8816             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8817             :  *
    8818             :  * @param hArg Handle to an argument. Must NOT be null.
    8819             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8820             :  * @since 3.11
    8821             :  */
    8822           8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    8823             :                                             size_t *pnCount)
    8824             : {
    8825           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8826           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8827           8 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8828             :     {
    8829           1 :         CPLError(
    8830             :             CE_Failure, CPLE_AppDefined,
    8831             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8832             :             __func__);
    8833           1 :         *pnCount = 0;
    8834           1 :         return nullptr;
    8835             :     }
    8836           7 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    8837           7 :     *pnCount = val.size();
    8838           7 :     return val.data();
    8839             : }
    8840             : 
    8841             : /************************************************************************/
    8842             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    8843             : /************************************************************************/
    8844             : 
    8845             : /** Return the argument value as a real list.
    8846             :  *
    8847             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8848             :  *
    8849             :  * @param hArg Handle to an argument. Must NOT be null.
    8850             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8851             :  * @since 3.11
    8852             :  */
    8853           8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    8854             :                                               size_t *pnCount)
    8855             : {
    8856           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8857           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8858           8 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8859             :     {
    8860           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8861             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8862             :                  __func__);
    8863           1 :         *pnCount = 0;
    8864           1 :         return nullptr;
    8865             :     }
    8866           7 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    8867           7 :     *pnCount = val.size();
    8868           7 :     return val.data();
    8869             : }
    8870             : 
    8871             : /************************************************************************/
    8872             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    8873             : /************************************************************************/
    8874             : 
    8875             : /** Set the value for a GAAT_BOOLEAN argument.
    8876             :  *
    8877             :  * It cannot be called several times for a given argument.
    8878             :  * Validation checks and other actions are run.
    8879             :  *
    8880             :  * @param hArg Handle to an argument. Must NOT be null.
    8881             :  * @param value value.
    8882             :  * @return true if success.
    8883             :  * @since 3.11
    8884             :  */
    8885             : 
    8886         698 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    8887             : {
    8888         698 :     VALIDATE_POINTER1(hArg, __func__, false);
    8889         698 :     return hArg->ptr->Set(value);
    8890             : }
    8891             : 
    8892             : /************************************************************************/
    8893             : /*                    GDALAlgorithmArgSetAsString()                     */
    8894             : /************************************************************************/
    8895             : 
    8896             : /** Set the value for a GAAT_STRING argument.
    8897             :  *
    8898             :  * It cannot be called several times for a given argument.
    8899             :  * Validation checks and other actions are run.
    8900             :  *
    8901             :  * @param hArg Handle to an argument. Must NOT be null.
    8902             :  * @param value value (may be null)
    8903             :  * @return true if success.
    8904             :  * @since 3.11
    8905             :  */
    8906             : 
    8907        3121 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    8908             : {
    8909        3121 :     VALIDATE_POINTER1(hArg, __func__, false);
    8910        3121 :     return hArg->ptr->Set(value ? value : "");
    8911             : }
    8912             : 
    8913             : /************************************************************************/
    8914             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    8915             : /************************************************************************/
    8916             : 
    8917             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    8918             :  *
    8919             :  * It cannot be called several times for a given argument.
    8920             :  * Validation checks and other actions are run.
    8921             :  *
    8922             :  * @param hArg Handle to an argument. Must NOT be null.
    8923             :  * @param value value.
    8924             :  * @return true if success.
    8925             :  * @since 3.11
    8926             :  */
    8927             : 
    8928         472 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    8929             : {
    8930         472 :     VALIDATE_POINTER1(hArg, __func__, false);
    8931         472 :     return hArg->ptr->Set(value);
    8932             : }
    8933             : 
    8934             : /************************************************************************/
    8935             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    8936             : /************************************************************************/
    8937             : 
    8938             : /** Set the value for a GAAT_REAL argument.
    8939             :  *
    8940             :  * It cannot be called several times for a given argument.
    8941             :  * Validation checks and other actions are run.
    8942             :  *
    8943             :  * @param hArg Handle to an argument. Must NOT be null.
    8944             :  * @param value value.
    8945             :  * @return true if success.
    8946             :  * @since 3.11
    8947             :  */
    8948             : 
    8949         243 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    8950             : {
    8951         243 :     VALIDATE_POINTER1(hArg, __func__, false);
    8952         243 :     return hArg->ptr->Set(value);
    8953             : }
    8954             : 
    8955             : /************************************************************************/
    8956             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    8957             : /************************************************************************/
    8958             : 
    8959             : /** Set the value for a GAAT_DATASET argument.
    8960             :  *
    8961             :  * It cannot be called several times for a given argument.
    8962             :  * Validation checks and other actions are run.
    8963             :  *
    8964             :  * @param hArg Handle to an argument. Must NOT be null.
    8965             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    8966             :  * @return true if success.
    8967             :  * @since 3.11
    8968             :  */
    8969           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    8970             :                                        GDALArgDatasetValueH value)
    8971             : {
    8972           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8973           2 :     VALIDATE_POINTER1(value, __func__, false);
    8974           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    8975             : }
    8976             : 
    8977             : /************************************************************************/
    8978             : /*                     GDALAlgorithmArgSetDataset()                     */
    8979             : /************************************************************************/
    8980             : 
    8981             : /** Set dataset object, increasing its reference counter.
    8982             :  *
    8983             :  * @param hArg Handle to an argument. Must NOT be null.
    8984             :  * @param hDS Dataset object. May be null.
    8985             :  * @return true if success.
    8986             :  * @since 3.11
    8987             :  */
    8988             : 
    8989           2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    8990             : {
    8991           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8992           2 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    8993             : }
    8994             : 
    8995             : /************************************************************************/
    8996             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    8997             : /************************************************************************/
    8998             : 
    8999             : /** Set the value for a GAAT_STRING_LIST argument.
    9000             :  *
    9001             :  * It cannot be called several times for a given argument.
    9002             :  * Validation checks and other actions are run.
    9003             :  *
    9004             :  * @param hArg Handle to an argument. Must NOT be null.
    9005             :  * @param value value as a NULL terminated list (may be null)
    9006             :  * @return true if success.
    9007             :  * @since 3.11
    9008             :  */
    9009             : 
    9010         785 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    9011             : {
    9012         785 :     VALIDATE_POINTER1(hArg, __func__, false);
    9013         785 :     return hArg->ptr->Set(
    9014        1570 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    9015             : }
    9016             : 
    9017             : /************************************************************************/
    9018             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    9019             : /************************************************************************/
    9020             : 
    9021             : /** Set the value for a GAAT_INTEGER_LIST argument.
    9022             :  *
    9023             :  * It cannot be called several times for a given argument.
    9024             :  * Validation checks and other actions are run.
    9025             :  *
    9026             :  * @param hArg Handle to an argument. Must NOT be null.
    9027             :  * @param nCount Number of values in pnValues.
    9028             :  * @param pnValues Pointer to an array of integer values of size nCount.
    9029             :  * @return true if success.
    9030             :  * @since 3.11
    9031             :  */
    9032         100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    9033             :                                       const int *pnValues)
    9034             : {
    9035         100 :     VALIDATE_POINTER1(hArg, __func__, false);
    9036         100 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    9037             : }
    9038             : 
    9039             : /************************************************************************/
    9040             : /*                  GDALAlgorithmArgSetAsDoubleList()                   */
    9041             : /************************************************************************/
    9042             : 
    9043             : /** Set the value for a GAAT_REAL_LIST argument.
    9044             :  *
    9045             :  * It cannot be called several times for a given argument.
    9046             :  * Validation checks and other actions are run.
    9047             :  *
    9048             :  * @param hArg Handle to an argument. Must NOT be null.
    9049             :  * @param nCount Number of values in pnValues.
    9050             :  * @param pnValues Pointer to an array of double values of size nCount.
    9051             :  * @return true if success.
    9052             :  * @since 3.11
    9053             :  */
    9054         154 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    9055             :                                      const double *pnValues)
    9056             : {
    9057         154 :     VALIDATE_POINTER1(hArg, __func__, false);
    9058         154 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    9059             : }
    9060             : 
    9061             : /************************************************************************/
    9062             : /*                    GDALAlgorithmArgSetDatasets()                     */
    9063             : /************************************************************************/
    9064             : 
    9065             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    9066             :  *
    9067             :  * @param hArg Handle to an argument. Must NOT be null.
    9068             :  * @param nCount Number of values in pnValues.
    9069             :  * @param pahDS Pointer to an array of dataset of size nCount.
    9070             :  * @return true if success.
    9071             :  * @since 3.11
    9072             :  */
    9073             : 
    9074        1284 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    9075             :                                  GDALDatasetH *pahDS)
    9076             : {
    9077        1284 :     VALIDATE_POINTER1(hArg, __func__, false);
    9078        2568 :     std::vector<GDALArgDatasetValue> values;
    9079        2594 :     for (size_t i = 0; i < nCount; ++i)
    9080             :     {
    9081        1310 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    9082             :     }
    9083        1284 :     return hArg->ptr->Set(std::move(values));
    9084             : }
    9085             : 
    9086             : /************************************************************************/
    9087             : /*                  GDALAlgorithmArgSetDatasetNames()                   */
    9088             : /************************************************************************/
    9089             : 
    9090             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    9091             :  *
    9092             :  * @param hArg Handle to an argument. Must NOT be null.
    9093             :  * @param names Dataset names as a NULL terminated list (may be null)
    9094             :  * @return true if success.
    9095             :  * @since 3.11
    9096             :  */
    9097             : 
    9098         739 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    9099             : {
    9100         739 :     VALIDATE_POINTER1(hArg, __func__, false);
    9101        1478 :     std::vector<GDALArgDatasetValue> values;
    9102        1549 :     for (size_t i = 0; names[i]; ++i)
    9103             :     {
    9104         810 :         values.emplace_back(names[i]);
    9105             :     }
    9106         739 :     return hArg->ptr->Set(std::move(values));
    9107             : }
    9108             : 
    9109             : /************************************************************************/
    9110             : /*                     GDALArgDatasetValueCreate()                      */
    9111             : /************************************************************************/
    9112             : 
    9113             : /** Instantiate an empty GDALArgDatasetValue
    9114             :  *
    9115             :  * @return new handle to free with GDALArgDatasetValueRelease()
    9116             :  * @since 3.11
    9117             :  */
    9118           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    9119             : {
    9120           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    9121             : }
    9122             : 
    9123             : /************************************************************************/
    9124             : /*                     GDALArgDatasetValueRelease()                     */
    9125             : /************************************************************************/
    9126             : 
    9127             : /** Release a handle to a GDALArgDatasetValue
    9128             :  *
    9129             :  * @since 3.11
    9130             :  */
    9131        3207 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    9132             : {
    9133        3207 :     delete hValue;
    9134        3207 : }
    9135             : 
    9136             : /************************************************************************/
    9137             : /*                     GDALArgDatasetValueGetName()                     */
    9138             : /************************************************************************/
    9139             : 
    9140             : /** Return the name component of the GDALArgDatasetValue
    9141             :  *
    9142             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9143             :  * @return string whose lifetime is bound to hAlg and which must not
    9144             :  * be freed.
    9145             :  * @since 3.11
    9146             :  */
    9147           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    9148             : {
    9149           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9150           1 :     return hValue->ptr->GetName().c_str();
    9151             : }
    9152             : 
    9153             : /************************************************************************/
    9154             : /*                  GDALArgDatasetValueGetDatasetRef()                  */
    9155             : /************************************************************************/
    9156             : 
    9157             : /** Return the dataset component of the GDALArgDatasetValue.
    9158             :  *
    9159             :  * This does not modify the reference counter, hence the lifetime of the
    9160             :  * returned object is not guaranteed to exceed the one of hValue.
    9161             :  *
    9162             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9163             :  * @since 3.11
    9164             :  */
    9165           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    9166             : {
    9167           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9168           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    9169             : }
    9170             : 
    9171             : /************************************************************************/
    9172             : /*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
    9173             : /************************************************************************/
    9174             : 
    9175             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    9176             :  * reference count if not null. Once done with the dataset, the caller should
    9177             :  * call GDALReleaseDataset().
    9178             :  *
    9179             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9180             :  * @since 3.11
    9181             :  */
    9182             : GDALDatasetH
    9183        1051 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    9184             : {
    9185        1051 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    9186        1051 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    9187             : }
    9188             : 
    9189             : /************************************************************************/
    9190             : /*                     GDALArgDatasetValueSetName()                     */
    9191             : /************************************************************************/
    9192             : 
    9193             : /** Set dataset name
    9194             :  *
    9195             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9196             :  * @param pszName Dataset name. May be null.
    9197             :  * @since 3.11
    9198             :  */
    9199             : 
    9200        1376 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    9201             :                                 const char *pszName)
    9202             : {
    9203        1376 :     VALIDATE_POINTER0(hValue, __func__);
    9204        1376 :     hValue->ptr->Set(pszName ? pszName : "");
    9205             : }
    9206             : 
    9207             : /************************************************************************/
    9208             : /*                   GDALArgDatasetValueSetDataset()                    */
    9209             : /************************************************************************/
    9210             : 
    9211             : /** Set dataset object, increasing its reference counter.
    9212             :  *
    9213             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    9214             :  * @param hDS Dataset object. May be null.
    9215             :  * @since 3.11
    9216             :  */
    9217             : 
    9218         765 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    9219             :                                    GDALDatasetH hDS)
    9220             : {
    9221         765 :     VALIDATE_POINTER0(hValue, __func__);
    9222         765 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    9223             : }

Generated by: LCOV version 1.14