LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3615 3853 93.8 %
Date: 2026-04-03 14:38:35 Functions: 279 286 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_json.h"
      17             : #include "cpl_levenshtein.h"
      18             : #include "cpl_minixml.h"
      19             : #include "cpl_multiproc.h"
      20             : 
      21             : #include "gdalalgorithm.h"
      22             : #include "gdalalg_abstract_pipeline.h"
      23             : #include "gdal_priv.h"
      24             : #include "gdal_thread_pool.h"
      25             : #include "ogrsf_frmts.h"
      26             : #include "ogr_spatialref.h"
      27             : #include "vrtdataset.h"
      28             : 
      29             : #include <algorithm>
      30             : #include <cassert>
      31             : #include <cerrno>
      32             : #include <cmath>
      33             : #include <cstdlib>
      34             : #include <limits>
      35             : #include <map>
      36             : #include <type_traits>
      37             : #include <string_view>
      38             : #include <regex>
      39             : 
      40             : #ifndef _
      41             : #define _(x) (x)
      42             : #endif
      43             : 
      44             : constexpr const char *GDAL_ARG_NAME_OUTPUT_DATA_TYPE = "output-data-type";
      45             : 
      46             : constexpr const char *GDAL_ARG_NAME_OUTPUT_OPEN_OPTION = "output-open-option";
      47             : 
      48             : constexpr const char *GDAL_ARG_NAME_BAND = "band";
      49             : 
      50             : //! @cond Doxygen_Suppress
      51             : struct GDALAlgorithmArgHS
      52             : {
      53             :     GDALAlgorithmArg *ptr = nullptr;
      54             : 
      55      337167 :     explicit GDALAlgorithmArgHS(GDALAlgorithmArg *arg) : ptr(arg)
      56             :     {
      57      337167 :     }
      58             : };
      59             : 
      60             : //! @endcond
      61             : 
      62             : //! @cond Doxygen_Suppress
      63             : struct GDALArgDatasetValueHS
      64             : {
      65             :     GDALArgDatasetValue val{};
      66             :     GDALArgDatasetValue *ptr = nullptr;
      67             : 
      68           1 :     GDALArgDatasetValueHS() : ptr(&val)
      69             :     {
      70           1 :     }
      71             : 
      72        3061 :     explicit GDALArgDatasetValueHS(GDALArgDatasetValue *arg) : ptr(arg)
      73             :     {
      74        3061 :     }
      75             : 
      76             :     GDALArgDatasetValueHS(const GDALArgDatasetValueHS &) = delete;
      77             :     GDALArgDatasetValueHS &operator=(const GDALArgDatasetValueHS &) = delete;
      78             : };
      79             : 
      80             : //! @endcond
      81             : 
      82             : /************************************************************************/
      83             : /*                     GDALAlgorithmArgTypeIsList()                     */
      84             : /************************************************************************/
      85             : 
      86      399510 : bool GDALAlgorithmArgTypeIsList(GDALAlgorithmArgType type)
      87             : {
      88      399510 :     switch (type)
      89             :     {
      90      263183 :         case GAAT_BOOLEAN:
      91             :         case GAAT_STRING:
      92             :         case GAAT_INTEGER:
      93             :         case GAAT_REAL:
      94             :         case GAAT_DATASET:
      95      263183 :             break;
      96             : 
      97      136327 :         case GAAT_STRING_LIST:
      98             :         case GAAT_INTEGER_LIST:
      99             :         case GAAT_REAL_LIST:
     100             :         case GAAT_DATASET_LIST:
     101      136327 :             return true;
     102             :     }
     103             : 
     104      263183 :     return false;
     105             : }
     106             : 
     107             : /************************************************************************/
     108             : /*                      GDALAlgorithmArgTypeName()                      */
     109             : /************************************************************************/
     110             : 
     111        5378 : const char *GDALAlgorithmArgTypeName(GDALAlgorithmArgType type)
     112             : {
     113        5378 :     switch (type)
     114             :     {
     115        1336 :         case GAAT_BOOLEAN:
     116        1336 :             break;
     117        1442 :         case GAAT_STRING:
     118        1442 :             return "string";
     119         370 :         case GAAT_INTEGER:
     120         370 :             return "integer";
     121         472 :         case GAAT_REAL:
     122         472 :             return "real";
     123         249 :         case GAAT_DATASET:
     124         249 :             return "dataset";
     125         995 :         case GAAT_STRING_LIST:
     126         995 :             return "string_list";
     127          92 :         case GAAT_INTEGER_LIST:
     128          92 :             return "integer_list";
     129         219 :         case GAAT_REAL_LIST:
     130         219 :             return "real_list";
     131         203 :         case GAAT_DATASET_LIST:
     132         203 :             return "dataset_list";
     133             :     }
     134             : 
     135        1336 :     return "boolean";
     136             : }
     137             : 
     138             : /************************************************************************/
     139             : /*                  GDALAlgorithmArgDatasetTypeName()                   */
     140             : /************************************************************************/
     141             : 
     142       21728 : std::string GDALAlgorithmArgDatasetTypeName(GDALArgDatasetType type)
     143             : {
     144       21728 :     std::string ret;
     145       21728 :     if ((type & GDAL_OF_RASTER) != 0)
     146       11934 :         ret = "raster";
     147       21728 :     if ((type & GDAL_OF_VECTOR) != 0)
     148             :     {
     149       10588 :         if (!ret.empty())
     150             :         {
     151        1187 :             if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     152         257 :                 ret += ", ";
     153             :             else
     154         930 :                 ret += " or ";
     155             :         }
     156       10588 :         ret += "vector";
     157             :     }
     158       21728 :     if ((type & GDAL_OF_MULTIDIM_RASTER) != 0)
     159             :     {
     160         576 :         if (!ret.empty())
     161             :         {
     162         316 :             ret += " or ";
     163             :         }
     164         576 :         ret += "multidimensional raster";
     165             :     }
     166       21728 :     return ret;
     167             : }
     168             : 
     169             : /************************************************************************/
     170             : /*                        GDALAlgorithmArgDecl()                        */
     171             : /************************************************************************/
     172             : 
     173             : // cppcheck-suppress uninitMemberVar
     174      321458 : GDALAlgorithmArgDecl::GDALAlgorithmArgDecl(const std::string &longName,
     175             :                                            char chShortName,
     176             :                                            const std::string &description,
     177      321458 :                                            GDALAlgorithmArgType type)
     178             :     : m_longName(longName),
     179      321458 :       m_shortName(chShortName ? std::string(&chShortName, 1) : std::string()),
     180             :       m_description(description), m_type(type),
     181      642916 :       m_metaVar(CPLString(m_type == GAAT_BOOLEAN ? std::string() : longName)
     182      321458 :                     .toupper()),
     183      964374 :       m_maxCount(GDALAlgorithmArgTypeIsList(type) ? UNBOUNDED : 1)
     184             : {
     185      321458 :     if (m_type == GAAT_BOOLEAN)
     186             :     {
     187      134118 :         m_defaultValue = false;
     188             :     }
     189      321458 : }
     190             : 
     191             : /************************************************************************/
     192             : /*                 GDALAlgorithmArgDecl::SetMinCount()                  */
     193             : /************************************************************************/
     194             : 
     195       17832 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMinCount(int count)
     196             : {
     197       17832 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     198             :     {
     199           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     200             :                  "SetMinCount() illegal on scalar argument '%s'",
     201           1 :                  GetName().c_str());
     202             :     }
     203             :     else
     204             :     {
     205       17831 :         m_minCount = count;
     206             :     }
     207       17832 :     return *this;
     208             : }
     209             : 
     210             : /************************************************************************/
     211             : /*                 GDALAlgorithmArgDecl::SetMaxCount()                  */
     212             : /************************************************************************/
     213             : 
     214       16871 : GDALAlgorithmArgDecl &GDALAlgorithmArgDecl::SetMaxCount(int count)
     215             : {
     216       16871 :     if (!GDALAlgorithmArgTypeIsList(m_type))
     217             :     {
     218           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     219             :                  "SetMaxCount() illegal on scalar argument '%s'",
     220           1 :                  GetName().c_str());
     221             :     }
     222             :     else
     223             :     {
     224       16870 :         m_maxCount = count;
     225             :     }
     226       16871 :     return *this;
     227             : }
     228             : 
     229             : /************************************************************************/
     230             : /*                GDALAlgorithmArg::~GDALAlgorithmArg()                 */
     231             : /************************************************************************/
     232             : 
     233             : GDALAlgorithmArg::~GDALAlgorithmArg() = default;
     234             : 
     235             : /************************************************************************/
     236             : /*                       GDALAlgorithmArg::Set()                        */
     237             : /************************************************************************/
     238             : 
     239        1168 : bool GDALAlgorithmArg::Set(bool value)
     240             : {
     241        1168 :     if (m_decl.GetType() != GAAT_BOOLEAN)
     242             :     {
     243          14 :         CPLError(
     244             :             CE_Failure, CPLE_AppDefined,
     245             :             "Calling Set(bool) on argument '%s' of type %s is not supported",
     246           7 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     247           7 :         return false;
     248             :     }
     249        1161 :     return SetInternal(value);
     250             : }
     251             : 
     252        3849 : bool GDALAlgorithmArg::ProcessString(std::string &value) const
     253             : {
     254        3885 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() && !value.empty() &&
     255          36 :         value.front() == '@')
     256             :     {
     257           2 :         GByte *pabyData = nullptr;
     258           2 :         if (VSIIngestFile(nullptr, value.c_str() + 1, &pabyData, nullptr,
     259           2 :                           10 * 1024 * 1024))
     260             :         {
     261             :             // Remove UTF-8 BOM
     262           1 :             size_t offset = 0;
     263           1 :             if (pabyData[0] == 0xEF && pabyData[1] == 0xBB &&
     264           1 :                 pabyData[2] == 0xBF)
     265             :             {
     266           1 :                 offset = 3;
     267             :             }
     268           1 :             value = reinterpret_cast<const char *>(pabyData + offset);
     269           1 :             VSIFree(pabyData);
     270             :         }
     271             :         else
     272             :         {
     273           1 :             return false;
     274             :         }
     275             :     }
     276             : 
     277        3848 :     if (m_decl.IsRemoveSQLCommentsEnabled())
     278          35 :         value = CPLRemoveSQLComments(value);
     279             : 
     280        3848 :     return true;
     281             : }
     282             : 
     283        3870 : bool GDALAlgorithmArg::Set(const std::string &value)
     284             : {
     285        3870 :     switch (m_decl.GetType())
     286             :     {
     287           9 :         case GAAT_BOOLEAN:
     288          17 :             if (EQUAL(value.c_str(), "1") || EQUAL(value.c_str(), "TRUE") ||
     289          17 :                 EQUAL(value.c_str(), "YES") || EQUAL(value.c_str(), "ON"))
     290             :             {
     291           4 :                 return Set(true);
     292             :             }
     293           5 :             else if (EQUAL(value.c_str(), "0") ||
     294           4 :                      EQUAL(value.c_str(), "FALSE") ||
     295           9 :                      EQUAL(value.c_str(), "NO") || EQUAL(value.c_str(), "OFF"))
     296             :             {
     297           4 :                 return Set(false);
     298             :             }
     299           1 :             break;
     300             : 
     301           8 :         case GAAT_INTEGER:
     302             :         case GAAT_INTEGER_LIST:
     303             :         {
     304           8 :             errno = 0;
     305           8 :             char *endptr = nullptr;
     306           8 :             const auto v = std::strtoll(value.c_str(), &endptr, 10);
     307          13 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     308           5 :                 endptr == value.c_str() + value.size())
     309             :             {
     310           3 :                 if (m_decl.GetType() == GAAT_INTEGER)
     311           3 :                     return Set(static_cast<int>(v));
     312             :                 else
     313           1 :                     return Set(std::vector<int>{static_cast<int>(v)});
     314             :             }
     315           5 :             break;
     316             :         }
     317             : 
     318           5 :         case GAAT_REAL:
     319             :         case GAAT_REAL_LIST:
     320             :         {
     321           5 :             char *endptr = nullptr;
     322           5 :             const double v = CPLStrtod(value.c_str(), &endptr);
     323           5 :             if (endptr == value.c_str() + value.size())
     324             :             {
     325           3 :                 if (m_decl.GetType() == GAAT_REAL)
     326           3 :                     return Set(v);
     327             :                 else
     328           1 :                     return Set(std::vector<double>{v});
     329             :             }
     330           2 :             break;
     331             :         }
     332             : 
     333        3832 :         case GAAT_STRING:
     334        3832 :             break;
     335             : 
     336           1 :         case GAAT_STRING_LIST:
     337           2 :             return Set(std::vector<std::string>{value});
     338             : 
     339          15 :         case GAAT_DATASET:
     340          15 :             return SetDatasetName(value);
     341             : 
     342           0 :         case GAAT_DATASET_LIST:
     343             :         {
     344           0 :             std::vector<GDALArgDatasetValue> v;
     345           0 :             v.resize(1);
     346           0 :             v[0].Set(value);
     347           0 :             return Set(std::move(v));
     348             :         }
     349             :     }
     350             : 
     351        3840 :     if (m_decl.GetType() != GAAT_STRING)
     352             :     {
     353          16 :         CPLError(CE_Failure, CPLE_AppDefined,
     354             :                  "Calling Set(std::string) on argument '%s' of type %s is not "
     355             :                  "supported",
     356           8 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     357           8 :         return false;
     358             :     }
     359             : 
     360        3832 :     std::string newValue(value);
     361        3832 :     return ProcessString(newValue) && SetInternal(newValue);
     362             : }
     363             : 
     364         841 : bool GDALAlgorithmArg::Set(int value)
     365             : {
     366         841 :     if (m_decl.GetType() == GAAT_BOOLEAN)
     367             :     {
     368           3 :         if (value == 1)
     369           1 :             return Set(true);
     370           2 :         else if (value == 0)
     371           1 :             return Set(false);
     372             :     }
     373         838 :     else if (m_decl.GetType() == GAAT_REAL)
     374             :     {
     375           3 :         return Set(static_cast<double>(value));
     376             :     }
     377         835 :     else if (m_decl.GetType() == GAAT_STRING)
     378             :     {
     379           2 :         return Set(std::to_string(value));
     380             :     }
     381         833 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST)
     382             :     {
     383           1 :         return Set(std::vector<int>{value});
     384             :     }
     385         832 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     386             :     {
     387           1 :         return Set(std::vector<double>{static_cast<double>(value)});
     388             :     }
     389         831 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     390             :     {
     391           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     392             :     }
     393             : 
     394         831 :     if (m_decl.GetType() != GAAT_INTEGER)
     395             :     {
     396           2 :         CPLError(
     397             :             CE_Failure, CPLE_AppDefined,
     398             :             "Calling Set(int) on argument '%s' of type %s is not supported",
     399           1 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     400           1 :         return false;
     401             :     }
     402         830 :     return SetInternal(value);
     403             : }
     404             : 
     405         287 : bool GDALAlgorithmArg::Set(double value)
     406             : {
     407         290 :     if (m_decl.GetType() == GAAT_INTEGER && value >= INT_MIN &&
     408         290 :         value <= INT_MAX && static_cast<int>(value) == value)
     409             :     {
     410           2 :         return Set(static_cast<int>(value));
     411             :     }
     412         285 :     else if (m_decl.GetType() == GAAT_STRING)
     413             :     {
     414           2 :         return Set(std::to_string(value));
     415             :     }
     416         285 :     else if (m_decl.GetType() == GAAT_INTEGER_LIST && value >= INT_MIN &&
     417         285 :              value <= INT_MAX && static_cast<int>(value) == value)
     418             :     {
     419           1 :         return Set(std::vector<int>{static_cast<int>(value)});
     420             :     }
     421         282 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     422             :     {
     423           0 :         return Set(std::vector<double>{value});
     424             :     }
     425         282 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     426             :     {
     427           2 :         return Set(std::vector<std::string>{std::to_string(value)});
     428             :     }
     429         281 :     else if (m_decl.GetType() != GAAT_REAL)
     430             :     {
     431           6 :         CPLError(
     432             :             CE_Failure, CPLE_AppDefined,
     433             :             "Calling Set(double) on argument '%s' of type %s is not supported",
     434           3 :             GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     435           3 :         return false;
     436             :     }
     437         278 :     return SetInternal(value);
     438             : }
     439             : 
     440        5813 : static bool CheckCanSetDatasetObject(const GDALAlgorithmArg *arg)
     441             : {
     442        5816 :     if (arg->GetDatasetInputFlags() == GADV_NAME &&
     443           3 :         arg->GetDatasetOutputFlags() == GADV_OBJECT)
     444             :     {
     445           3 :         CPLError(
     446             :             CE_Failure, CPLE_AppDefined,
     447             :             "Dataset object '%s' is created by algorithm and cannot be set "
     448             :             "as an input.",
     449           3 :             arg->GetName().c_str());
     450           3 :         return false;
     451             :     }
     452        5810 :     else if ((arg->GetDatasetInputFlags() & GADV_OBJECT) == 0)
     453             :     {
     454           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     455             :                  "A dataset cannot be set as an input argument of '%s'.",
     456           2 :                  arg->GetName().c_str());
     457           2 :         return false;
     458             :     }
     459             : 
     460        5808 :     return true;
     461             : }
     462             : 
     463           9 : bool GDALAlgorithmArg::Set(GDALDataset *ds)
     464             : {
     465           9 :     if (m_decl.GetType() != GAAT_DATASET)
     466             :     {
     467           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     468             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     469             :                  "is not supported",
     470           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     471           1 :         return false;
     472             :     }
     473           8 :     if (!CheckCanSetDatasetObject(this))
     474           2 :         return false;
     475           6 :     m_explicitlySet = true;
     476           6 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     477           6 :     val.Set(ds);
     478           6 :     return RunAllActions();
     479             : }
     480             : 
     481           3 : bool GDALAlgorithmArg::Set(std::unique_ptr<GDALDataset> ds)
     482             : {
     483           3 :     if (m_decl.GetType() != GAAT_DATASET)
     484             :     {
     485           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     486             :                  "Calling Set(GDALDataset*, bool) on argument '%s' of type %s "
     487             :                  "is not supported",
     488           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     489           1 :         return false;
     490             :     }
     491           2 :     if (!CheckCanSetDatasetObject(this))
     492           1 :         return false;
     493           1 :     m_explicitlySet = true;
     494           1 :     auto &val = *std::get<GDALArgDatasetValue *>(m_value);
     495           1 :     val.Set(std::move(ds));
     496           1 :     return RunAllActions();
     497             : }
     498             : 
     499         539 : bool GDALAlgorithmArg::SetDatasetName(const std::string &name)
     500             : {
     501         539 :     if (m_decl.GetType() != GAAT_DATASET)
     502             :     {
     503           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     504             :                  "Calling SetDatasetName() on argument '%s' of type %s is "
     505             :                  "not supported",
     506           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     507           1 :         return false;
     508             :     }
     509         538 :     m_explicitlySet = true;
     510         538 :     std::get<GDALArgDatasetValue *>(m_value)->Set(name);
     511         538 :     return RunAllActions();
     512             : }
     513             : 
     514         940 : bool GDALAlgorithmArg::SetFrom(const GDALArgDatasetValue &other)
     515             : {
     516         940 :     if (m_decl.GetType() != GAAT_DATASET)
     517             :     {
     518           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     519             :                  "Calling SetFrom() on argument '%s' of type %s is "
     520             :                  "not supported",
     521           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     522           1 :         return false;
     523             :     }
     524         939 :     if (!CheckCanSetDatasetObject(this))
     525           1 :         return false;
     526         938 :     m_explicitlySet = true;
     527         938 :     std::get<GDALArgDatasetValue *>(m_value)->SetFrom(other);
     528         938 :     return RunAllActions();
     529             : }
     530             : 
     531         970 : bool GDALAlgorithmArg::Set(const std::vector<std::string> &value)
     532             : {
     533         970 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     534             :     {
     535           3 :         std::vector<int> v_i;
     536           4 :         for (const std::string &s : value)
     537             :         {
     538           3 :             errno = 0;
     539           3 :             char *endptr = nullptr;
     540           3 :             const auto v = std::strtoll(s.c_str(), &endptr, 10);
     541           5 :             if (errno == 0 && v >= INT_MIN && v <= INT_MAX &&
     542           2 :                 endptr == s.c_str() + s.size())
     543             :             {
     544           1 :                 v_i.push_back(static_cast<int>(v));
     545             :             }
     546             :             else
     547             :             {
     548           2 :                 break;
     549             :             }
     550             :         }
     551           3 :         if (v_i.size() == value.size())
     552           1 :             return Set(v_i);
     553             :     }
     554         967 :     else if (m_decl.GetType() == GAAT_REAL_LIST)
     555             :     {
     556           2 :         std::vector<double> v_d;
     557           3 :         for (const std::string &s : value)
     558             :         {
     559           2 :             char *endptr = nullptr;
     560           2 :             const double v = CPLStrtod(s.c_str(), &endptr);
     561           2 :             if (endptr == s.c_str() + s.size())
     562             :             {
     563           1 :                 v_d.push_back(v);
     564             :             }
     565             :             else
     566             :             {
     567           1 :                 break;
     568             :             }
     569             :         }
     570           2 :         if (v_d.size() == value.size())
     571           1 :             return Set(v_d);
     572             :     }
     573        1928 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     574        1925 :               m_decl.GetType() == GAAT_REAL ||
     575        2892 :               m_decl.GetType() == GAAT_STRING) &&
     576           5 :              value.size() == 1)
     577             :     {
     578           4 :         return Set(value[0]);
     579             :     }
     580         961 :     else if (m_decl.GetType() == GAAT_DATASET_LIST)
     581             :     {
     582          30 :         std::vector<GDALArgDatasetValue> dsVector;
     583          46 :         for (const std::string &s : value)
     584          31 :             dsVector.emplace_back(s);
     585          15 :         return Set(std::move(dsVector));
     586             :     }
     587             : 
     588         949 :     if (m_decl.GetType() != GAAT_STRING_LIST)
     589             :     {
     590          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     591             :                  "Calling Set(const std::vector<std::string> &) on argument "
     592             :                  "'%s' of type %s is not supported",
     593           5 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     594           5 :         return false;
     595             :     }
     596             : 
     597        1874 :     if (m_decl.IsReadFromFileAtSyntaxAllowed() ||
     598         930 :         m_decl.IsRemoveSQLCommentsEnabled())
     599             :     {
     600          28 :         std::vector<std::string> newValue(value);
     601          31 :         for (auto &s : newValue)
     602             :         {
     603          17 :             if (!ProcessString(s))
     604           0 :                 return false;
     605             :         }
     606          14 :         return SetInternal(newValue);
     607             :     }
     608             :     else
     609             :     {
     610         930 :         return SetInternal(value);
     611             :     }
     612             : }
     613             : 
     614         169 : bool GDALAlgorithmArg::Set(const std::vector<int> &value)
     615             : {
     616         169 :     if (m_decl.GetType() == GAAT_REAL_LIST)
     617             :     {
     618           2 :         std::vector<double> v_d;
     619           2 :         for (int i : value)
     620           1 :             v_d.push_back(i);
     621           1 :         return Set(v_d);
     622             :     }
     623         168 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     624             :     {
     625           2 :         std::vector<std::string> v_s;
     626           3 :         for (int i : value)
     627           2 :             v_s.push_back(std::to_string(i));
     628           1 :         return Set(v_s);
     629             :     }
     630         332 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     631         328 :               m_decl.GetType() == GAAT_REAL ||
     632         497 :               m_decl.GetType() == GAAT_STRING) &&
     633           5 :              value.size() == 1)
     634             :     {
     635           3 :         return Set(value[0]);
     636             :     }
     637             : 
     638         164 :     if (m_decl.GetType() != GAAT_INTEGER_LIST)
     639             :     {
     640           6 :         CPLError(CE_Failure, CPLE_AppDefined,
     641             :                  "Calling Set(const std::vector<int> &) on argument '%s' of "
     642             :                  "type %s is not supported",
     643           3 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     644           3 :         return false;
     645             :     }
     646         161 :     return SetInternal(value);
     647             : }
     648             : 
     649         263 : bool GDALAlgorithmArg::Set(const std::vector<double> &value)
     650             : {
     651         263 :     if (m_decl.GetType() == GAAT_INTEGER_LIST)
     652             :     {
     653           2 :         std::vector<int> v_i;
     654           3 :         for (double d : value)
     655             :         {
     656           2 :             if (d >= INT_MIN && d <= INT_MAX && static_cast<int>(d) == d)
     657             :             {
     658           1 :                 v_i.push_back(static_cast<int>(d));
     659             :             }
     660             :             else
     661             :             {
     662             :                 break;
     663             :             }
     664             :         }
     665           2 :         if (v_i.size() == value.size())
     666           1 :             return Set(v_i);
     667             :     }
     668         261 :     else if (m_decl.GetType() == GAAT_STRING_LIST)
     669             :     {
     670           2 :         std::vector<std::string> v_s;
     671           3 :         for (double d : value)
     672           2 :             v_s.push_back(std::to_string(d));
     673           1 :         return Set(v_s);
     674             :     }
     675         519 :     else if ((m_decl.GetType() == GAAT_INTEGER ||
     676         517 :               m_decl.GetType() == GAAT_REAL ||
     677         778 :               m_decl.GetType() == GAAT_STRING) &&
     678           3 :              value.size() == 1)
     679             :     {
     680           3 :         return Set(value[0]);
     681             :     }
     682             : 
     683         258 :     if (m_decl.GetType() != GAAT_REAL_LIST)
     684             :     {
     685           4 :         CPLError(CE_Failure, CPLE_AppDefined,
     686             :                  "Calling Set(const std::vector<double> &) on argument '%s' of "
     687             :                  "type %s is not supported",
     688           2 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     689           2 :         return false;
     690             :     }
     691         256 :     return SetInternal(value);
     692             : }
     693             : 
     694        3277 : bool GDALAlgorithmArg::Set(std::vector<GDALArgDatasetValue> &&value)
     695             : {
     696        3277 :     if (m_decl.GetType() != GAAT_DATASET_LIST)
     697             :     {
     698           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     699             :                  "Calling Set(const std::vector<GDALArgDatasetValue> &&) on "
     700             :                  "argument '%s' of type %s is not supported",
     701           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()));
     702           1 :         return false;
     703             :     }
     704        3276 :     m_explicitlySet = true;
     705        3276 :     *std::get<std::vector<GDALArgDatasetValue> *>(m_value) = std::move(value);
     706        3276 :     return RunAllActions();
     707             : }
     708             : 
     709             : GDALAlgorithmArg &
     710           0 : GDALAlgorithmArg::operator=(std::unique_ptr<GDALDataset> value)
     711             : {
     712           0 :     Set(std::move(value));
     713           0 :     return *this;
     714             : }
     715             : 
     716           1 : bool GDALAlgorithmArg::Set(const OGRSpatialReference &value)
     717             : {
     718           1 :     const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
     719           1 :     return Set(value.exportToWkt(apszOptions));
     720             : }
     721             : 
     722        4067 : bool GDALAlgorithmArg::SetFrom(const GDALAlgorithmArg &other)
     723             : {
     724        4067 :     if (m_decl.GetType() != other.GetType())
     725             :     {
     726           2 :         CPLError(CE_Failure, CPLE_AppDefined,
     727             :                  "Calling SetFrom() on argument '%s' of type %s whereas "
     728             :                  "other argument type is %s is not supported",
     729           1 :                  GetName().c_str(), GDALAlgorithmArgTypeName(m_decl.GetType()),
     730             :                  GDALAlgorithmArgTypeName(other.GetType()));
     731           1 :         return false;
     732             :     }
     733             : 
     734        4066 :     switch (m_decl.GetType())
     735             :     {
     736          90 :         case GAAT_BOOLEAN:
     737          90 :             *std::get<bool *>(m_value) = *std::get<bool *>(other.m_value);
     738          90 :             break;
     739         783 :         case GAAT_STRING:
     740        1566 :             *std::get<std::string *>(m_value) =
     741         783 :                 *std::get<std::string *>(other.m_value);
     742         783 :             break;
     743           6 :         case GAAT_INTEGER:
     744           6 :             *std::get<int *>(m_value) = *std::get<int *>(other.m_value);
     745           6 :             break;
     746           1 :         case GAAT_REAL:
     747           1 :             *std::get<double *>(m_value) = *std::get<double *>(other.m_value);
     748           1 :             break;
     749         934 :         case GAAT_DATASET:
     750         934 :             return SetFrom(other.Get<GDALArgDatasetValue>());
     751          39 :         case GAAT_STRING_LIST:
     752          78 :             *std::get<std::vector<std::string> *>(m_value) =
     753          39 :                 *std::get<std::vector<std::string> *>(other.m_value);
     754          39 :             break;
     755           1 :         case GAAT_INTEGER_LIST:
     756           2 :             *std::get<std::vector<int> *>(m_value) =
     757           1 :                 *std::get<std::vector<int> *>(other.m_value);
     758           1 :             break;
     759           1 :         case GAAT_REAL_LIST:
     760           2 :             *std::get<std::vector<double> *>(m_value) =
     761           1 :                 *std::get<std::vector<double> *>(other.m_value);
     762           1 :             break;
     763        2211 :         case GAAT_DATASET_LIST:
     764             :         {
     765        2211 :             std::get<std::vector<GDALArgDatasetValue> *>(m_value)->clear();
     766        2216 :             for (const auto &val :
     767        6643 :                  *std::get<std::vector<GDALArgDatasetValue> *>(other.m_value))
     768             :             {
     769        4432 :                 GDALArgDatasetValue v;
     770        2216 :                 v.SetFrom(val);
     771        2216 :                 std::get<std::vector<GDALArgDatasetValue> *>(m_value)
     772        2216 :                     ->push_back(std::move(v));
     773             :             }
     774        2211 :             break;
     775             :         }
     776             :     }
     777        3132 :     m_explicitlySet = true;
     778        3132 :     return RunAllActions();
     779             : }
     780             : 
     781             : /************************************************************************/
     782             : /*                  GDALAlgorithmArg::RunAllActions()                   */
     783             : /************************************************************************/
     784             : 
     785       15352 : bool GDALAlgorithmArg::RunAllActions()
     786             : {
     787       15352 :     if (!RunValidationActions())
     788         139 :         return false;
     789       15213 :     RunActions();
     790       15213 :     return true;
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*                    GDALAlgorithmArg::RunActions()                    */
     795             : /************************************************************************/
     796             : 
     797       15214 : void GDALAlgorithmArg::RunActions()
     798             : {
     799       15509 :     for (const auto &f : m_actions)
     800         295 :         f();
     801       15214 : }
     802             : 
     803             : /************************************************************************/
     804             : /*                  GDALAlgorithmArg::ValidateChoice()                  */
     805             : /************************************************************************/
     806             : 
     807             : // Returns the canonical value if matching a valid choice, or empty string
     808             : // otherwise.
     809        2498 : std::string GDALAlgorithmArg::ValidateChoice(const std::string &value) const
     810             : {
     811       14887 :     for (const std::string &choice : GetChoices())
     812             :     {
     813       14769 :         if (EQUAL(value.c_str(), choice.c_str()))
     814             :         {
     815        2380 :             return choice;
     816             :         }
     817             :     }
     818             : 
     819         190 :     for (const std::string &choice : GetHiddenChoices())
     820             :     {
     821         172 :         if (EQUAL(value.c_str(), choice.c_str()))
     822             :         {
     823         100 :             return choice;
     824             :         }
     825             :     }
     826             : 
     827          36 :     std::string expected;
     828         220 :     for (const auto &choice : GetChoices())
     829             :     {
     830         202 :         if (!expected.empty())
     831         184 :             expected += ", ";
     832         202 :         expected += '\'';
     833         202 :         expected += choice;
     834         202 :         expected += '\'';
     835             :     }
     836          18 :     if (m_owner && m_owner->IsCalledFromCommandLine() && value == "?")
     837             :     {
     838           6 :         return "?";
     839             :     }
     840          24 :     CPLError(CE_Failure, CPLE_IllegalArg,
     841             :              "Invalid value '%s' for string argument '%s'. Should be "
     842             :              "one among %s.",
     843          12 :              value.c_str(), GetName().c_str(), expected.c_str());
     844          12 :     return std::string();
     845             : }
     846             : 
     847             : /************************************************************************/
     848             : /*                 GDALAlgorithmArg::ValidateIntRange()                 */
     849             : /************************************************************************/
     850             : 
     851        2657 : bool GDALAlgorithmArg::ValidateIntRange(int val) const
     852             : {
     853        2657 :     bool ret = true;
     854             : 
     855        2657 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     856        2657 :     if (!std::isnan(minVal))
     857             :     {
     858        2024 :         if (minValIsIncluded && val < minVal)
     859             :         {
     860           3 :             CPLError(CE_Failure, CPLE_IllegalArg,
     861             :                      "Value of argument '%s' is %d, but should be >= %d",
     862           3 :                      GetName().c_str(), val, static_cast<int>(minVal));
     863           3 :             ret = false;
     864             :         }
     865        2021 :         else if (!minValIsIncluded && val <= minVal)
     866             :         {
     867           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     868             :                      "Value of argument '%s' is %d, but should be > %d",
     869           1 :                      GetName().c_str(), val, static_cast<int>(minVal));
     870           1 :             ret = false;
     871             :         }
     872             :     }
     873             : 
     874        2657 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     875        2657 :     if (!std::isnan(maxVal))
     876             :     {
     877             : 
     878         382 :         if (maxValIsIncluded && val > maxVal)
     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>(maxVal));
     883           1 :             ret = false;
     884             :         }
     885         381 :         else if (!maxValIsIncluded && val >= maxVal)
     886             :         {
     887           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     888             :                      "Value of argument '%s' is %d, but should be < %d",
     889           1 :                      GetName().c_str(), val, static_cast<int>(maxVal));
     890           1 :             ret = false;
     891             :         }
     892             :     }
     893             : 
     894        2657 :     return ret;
     895             : }
     896             : 
     897             : /************************************************************************/
     898             : /*                GDALAlgorithmArg::ValidateRealRange()                 */
     899             : /************************************************************************/
     900             : 
     901        2105 : bool GDALAlgorithmArg::ValidateRealRange(double val) const
     902             : {
     903        2105 :     bool ret = true;
     904             : 
     905        2105 :     const auto [minVal, minValIsIncluded] = GetMinValue();
     906        2105 :     if (!std::isnan(minVal))
     907             :     {
     908         213 :         if (minValIsIncluded && !(val >= minVal))
     909             :         {
     910          11 :             CPLError(CE_Failure, CPLE_IllegalArg,
     911             :                      "Value of argument '%s' is %g, but should be >= %g",
     912          11 :                      GetName().c_str(), val, minVal);
     913          11 :             ret = false;
     914             :         }
     915         202 :         else if (!minValIsIncluded && !(val > minVal))
     916             :         {
     917           4 :             CPLError(CE_Failure, CPLE_IllegalArg,
     918             :                      "Value of argument '%s' is %g, but should be > %g",
     919           4 :                      GetName().c_str(), val, minVal);
     920           4 :             ret = false;
     921             :         }
     922             :     }
     923             : 
     924        2105 :     const auto [maxVal, maxValIsIncluded] = GetMaxValue();
     925        2105 :     if (!std::isnan(maxVal))
     926             :     {
     927             : 
     928          58 :         if (maxValIsIncluded && !(val <= maxVal))
     929             :         {
     930           2 :             CPLError(CE_Failure, CPLE_IllegalArg,
     931             :                      "Value of argument '%s' is %g, but should be <= %g",
     932           2 :                      GetName().c_str(), val, maxVal);
     933           2 :             ret = false;
     934             :         }
     935          56 :         else if (!maxValIsIncluded && !(val < maxVal))
     936             :         {
     937           1 :             CPLError(CE_Failure, CPLE_IllegalArg,
     938             :                      "Value of argument '%s' is %g, but should be < %g",
     939           1 :                      GetName().c_str(), val, maxVal);
     940           1 :             ret = false;
     941             :         }
     942             :     }
     943             : 
     944        2105 :     return ret;
     945             : }
     946             : 
     947             : /************************************************************************/
     948             : /*                        CheckDuplicateValues()                        */
     949             : /************************************************************************/
     950             : 
     951             : template <class T>
     952          82 : static bool CheckDuplicateValues(const GDALAlgorithmArg *arg,
     953             :                                  const std::vector<T> &values)
     954             : {
     955         164 :     auto tmpValues = values;
     956          82 :     bool bHasDupValues = false;
     957             :     if constexpr (std::is_floating_point_v<T>)
     958             :     {
     959             :         // Avoid undefined behavior with NaN values
     960           4 :         std::sort(tmpValues.begin(), tmpValues.end(),
     961          21 :                   [](T a, T b)
     962             :                   {
     963          21 :                       if (std::isnan(a) && !std::isnan(b))
     964           3 :                           return true;
     965          18 :                       if (std::isnan(b))
     966          10 :                           return false;
     967           8 :                       return a < b;
     968             :                   });
     969             : 
     970             :         bHasDupValues =
     971           4 :             std::adjacent_find(tmpValues.begin(), tmpValues.end(),
     972           6 :                                [](T a, T b)
     973             :                                {
     974           6 :                                    if (std::isnan(a) && std::isnan(b))
     975           1 :                                        return true;
     976           5 :                                    return a == b;
     977           8 :                                }) != tmpValues.end();
     978             :     }
     979             :     else
     980             :     {
     981          78 :         std::sort(tmpValues.begin(), tmpValues.end());
     982          78 :         bHasDupValues = std::adjacent_find(tmpValues.begin(),
     983         156 :                                            tmpValues.end()) != tmpValues.end();
     984             :     }
     985          82 :     if (bHasDupValues)
     986             :     {
     987          10 :         CPLError(CE_Failure, CPLE_AppDefined,
     988             :                  "'%s' must be a list of unique values.",
     989          10 :                  arg->GetName().c_str());
     990          10 :         return false;
     991             :     }
     992          72 :     return true;
     993             : }
     994             : 
     995             : /************************************************************************/
     996             : /*               GDALAlgorithmArg::RunValidationActions()               */
     997             : /************************************************************************/
     998             : 
     999       33972 : bool GDALAlgorithmArg::RunValidationActions()
    1000             : {
    1001       33972 :     bool ret = true;
    1002             : 
    1003       33972 :     if (GetType() == GAAT_STRING && !GetChoices().empty())
    1004             :     {
    1005        1621 :         auto &val = Get<std::string>();
    1006        3242 :         std::string validVal = ValidateChoice(val);
    1007        1621 :         if (validVal.empty())
    1008           7 :             ret = false;
    1009             :         else
    1010        1614 :             val = std::move(validVal);
    1011             :     }
    1012       32351 :     else if (GetType() == GAAT_STRING_LIST && !GetChoices().empty())
    1013             :     {
    1014         633 :         auto &values = Get<std::vector<std::string>>();
    1015        1510 :         for (std::string &val : values)
    1016             :         {
    1017        1754 :             std::string validVal = ValidateChoice(val);
    1018         877 :             if (validVal.empty())
    1019           5 :                 ret = false;
    1020             :             else
    1021         872 :                 val = std::move(validVal);
    1022             :         }
    1023             :     }
    1024             : 
    1025             :     const auto CheckMinCharCount =
    1026         942 :         [this, &ret](const std::string &val, int nMinCharCount)
    1027             :     {
    1028         930 :         if (val.size() < static_cast<size_t>(nMinCharCount))
    1029             :         {
    1030          12 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1031             :                      "Value of argument '%s' is '%s', but should have at least "
    1032             :                      "%d character%s",
    1033           6 :                      GetName().c_str(), val.c_str(), nMinCharCount,
    1034             :                      nMinCharCount > 1 ? "s" : "");
    1035           6 :             ret = false;
    1036             :         }
    1037       34902 :     };
    1038             : 
    1039             :     const auto CheckMaxCharCount =
    1040       12065 :         [this, &ret](const std::string &val, int nMaxCharCount)
    1041             :     {
    1042       12063 :         if (val.size() > static_cast<size_t>(nMaxCharCount))
    1043             :         {
    1044           2 :             CPLError(
    1045             :                 CE_Failure, CPLE_IllegalArg,
    1046             :                 "Value of argument '%s' is '%s', but should have no more than "
    1047             :                 "%d character%s",
    1048           1 :                 GetName().c_str(), val.c_str(), nMaxCharCount,
    1049             :                 nMaxCharCount > 1 ? "s" : "");
    1050           1 :             ret = false;
    1051             :         }
    1052       46035 :     };
    1053             : 
    1054       33972 :     switch (GetType())
    1055             :     {
    1056        2644 :         case GAAT_BOOLEAN:
    1057        2644 :             break;
    1058             : 
    1059        9547 :         case GAAT_STRING:
    1060             :         {
    1061        9547 :             const auto &val = Get<std::string>();
    1062        9547 :             const int nMinCharCount = GetMinCharCount();
    1063        9547 :             if (nMinCharCount > 0)
    1064             :             {
    1065         848 :                 CheckMinCharCount(val, nMinCharCount);
    1066             :             }
    1067             : 
    1068        9547 :             const int nMaxCharCount = GetMaxCharCount();
    1069        9547 :             CheckMaxCharCount(val, nMaxCharCount);
    1070        9547 :             break;
    1071             :         }
    1072             : 
    1073        2061 :         case GAAT_STRING_LIST:
    1074             :         {
    1075        2061 :             const int nMinCharCount = GetMinCharCount();
    1076        2061 :             const int nMaxCharCount = GetMaxCharCount();
    1077        2061 :             const auto &values = Get<std::vector<std::string>>();
    1078        4577 :             for (const auto &val : values)
    1079             :             {
    1080        2516 :                 if (nMinCharCount > 0)
    1081          82 :                     CheckMinCharCount(val, nMinCharCount);
    1082        2516 :                 CheckMaxCharCount(val, nMaxCharCount);
    1083             :             }
    1084             : 
    1085        2125 :             if (!GetDuplicateValuesAllowed() &&
    1086          64 :                 !CheckDuplicateValues(this, values))
    1087           2 :                 ret = false;
    1088        2061 :             break;
    1089             :         }
    1090             : 
    1091        1969 :         case GAAT_INTEGER:
    1092             :         {
    1093        1969 :             ret = ValidateIntRange(Get<int>()) && ret;
    1094        1969 :             break;
    1095             :         }
    1096             : 
    1097         337 :         case GAAT_INTEGER_LIST:
    1098             :         {
    1099         337 :             const auto &values = Get<std::vector<int>>();
    1100        1025 :             for (int v : values)
    1101         688 :                 ret = ValidateIntRange(v) && ret;
    1102             : 
    1103         340 :             if (!GetDuplicateValuesAllowed() &&
    1104           3 :                 !CheckDuplicateValues(this, values))
    1105           1 :                 ret = false;
    1106         337 :             break;
    1107             :         }
    1108             : 
    1109         542 :         case GAAT_REAL:
    1110             :         {
    1111         542 :             ret = ValidateRealRange(Get<double>()) && ret;
    1112         542 :             break;
    1113             :         }
    1114             : 
    1115         565 :         case GAAT_REAL_LIST:
    1116             :         {
    1117         565 :             const auto &values = Get<std::vector<double>>();
    1118        2128 :             for (double v : values)
    1119        1563 :                 ret = ValidateRealRange(v) && ret;
    1120             : 
    1121         569 :             if (!GetDuplicateValuesAllowed() &&
    1122           4 :                 !CheckDuplicateValues(this, values))
    1123           2 :                 ret = false;
    1124         565 :             break;
    1125             :         }
    1126             : 
    1127        5553 :         case GAAT_DATASET:
    1128        5553 :             break;
    1129             : 
    1130       10754 :         case GAAT_DATASET_LIST:
    1131             :         {
    1132       10754 :             if (!GetDuplicateValuesAllowed())
    1133             :             {
    1134          11 :                 const auto &values = Get<std::vector<GDALArgDatasetValue>>();
    1135          22 :                 std::vector<std::string> aosValues;
    1136          34 :                 for (const auto &v : values)
    1137             :                 {
    1138          23 :                     const GDALDataset *poDS = v.GetDatasetRef();
    1139          23 :                     if (poDS)
    1140             :                     {
    1141          16 :                         auto poDriver = poDS->GetDriver();
    1142             :                         // The dataset name for a MEM driver is not relevant,
    1143             :                         // so use the pointer address
    1144          32 :                         if ((poDriver &&
    1145          24 :                              EQUAL(poDriver->GetDescription(), "MEM")) ||
    1146           8 :                             poDS->GetDescription()[0] == 0)
    1147             :                         {
    1148           8 :                             aosValues.push_back(CPLSPrintf("%p", poDS));
    1149             :                         }
    1150             :                         else
    1151             :                         {
    1152           8 :                             aosValues.push_back(poDS->GetDescription());
    1153             :                         }
    1154             :                     }
    1155             :                     else
    1156             :                     {
    1157           7 :                         aosValues.push_back(v.GetName());
    1158             :                     }
    1159             :                 }
    1160          11 :                 if (!CheckDuplicateValues(this, aosValues))
    1161           5 :                     ret = false;
    1162             :             }
    1163       10754 :             break;
    1164             :         }
    1165             :     }
    1166             : 
    1167       33972 :     if (GDALAlgorithmArgTypeIsList(GetType()))
    1168             :     {
    1169       13717 :         int valueCount = 0;
    1170       13717 :         if (GetType() == GAAT_STRING_LIST)
    1171             :         {
    1172        2061 :             valueCount =
    1173        2061 :                 static_cast<int>(Get<std::vector<std::string>>().size());
    1174             :         }
    1175       11656 :         else if (GetType() == GAAT_INTEGER_LIST)
    1176             :         {
    1177         337 :             valueCount = static_cast<int>(Get<std::vector<int>>().size());
    1178             :         }
    1179       11319 :         else if (GetType() == GAAT_REAL_LIST)
    1180             :         {
    1181         565 :             valueCount = static_cast<int>(Get<std::vector<double>>().size());
    1182             :         }
    1183       10754 :         else if (GetType() == GAAT_DATASET_LIST)
    1184             :         {
    1185       10754 :             valueCount = static_cast<int>(
    1186       10754 :                 Get<std::vector<GDALArgDatasetValue>>().size());
    1187             :         }
    1188             : 
    1189       13717 :         if (valueCount != GetMinCount() && GetMinCount() == GetMaxCount())
    1190             :         {
    1191          14 :             ReportError(CE_Failure, CPLE_AppDefined,
    1192             :                         "%d value%s been specified for argument '%s', "
    1193             :                         "whereas exactly %d %s expected.",
    1194             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1195           7 :                         GetName().c_str(), GetMinCount(),
    1196           7 :                         GetMinCount() > 1 ? "were" : "was");
    1197           7 :             ret = false;
    1198             :         }
    1199       13710 :         else if (valueCount < GetMinCount())
    1200             :         {
    1201           6 :             ReportError(CE_Failure, CPLE_AppDefined,
    1202             :                         "Only %d value%s been specified for argument '%s', "
    1203             :                         "whereas at least %d %s expected.",
    1204             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1205           3 :                         GetName().c_str(), GetMinCount(),
    1206           3 :                         GetMinCount() > 1 ? "were" : "was");
    1207           3 :             ret = false;
    1208             :         }
    1209       13707 :         else if (valueCount > GetMaxCount())
    1210             :         {
    1211           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    1212             :                         "%d value%s been specified for argument '%s', "
    1213             :                         "whereas at most %d %s expected.",
    1214             :                         valueCount, valueCount > 1 ? "s have" : " has",
    1215           1 :                         GetName().c_str(), GetMaxCount(),
    1216           1 :                         GetMaxCount() > 1 ? "were" : "was");
    1217           1 :             ret = false;
    1218             :         }
    1219             :     }
    1220             : 
    1221       33972 :     if (ret)
    1222             :     {
    1223       40400 :         for (const auto &f : m_validationActions)
    1224             :         {
    1225        6492 :             if (!f())
    1226          84 :                 ret = false;
    1227             :         }
    1228             :     }
    1229             : 
    1230       33972 :     return ret;
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                   GDALAlgorithmArg::ReportError()                    */
    1235             : /************************************************************************/
    1236             : 
    1237          11 : void GDALAlgorithmArg::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    1238             :                                    const char *fmt, ...) const
    1239             : {
    1240             :     va_list args;
    1241          11 :     va_start(args, fmt);
    1242          11 :     if (m_owner)
    1243             :     {
    1244          11 :         m_owner->ReportError(eErrClass, err_no, "%s",
    1245          22 :                              CPLString().vPrintf(fmt, args).c_str());
    1246             :     }
    1247             :     else
    1248             :     {
    1249           0 :         CPLError(eErrClass, err_no, "%s",
    1250           0 :                  CPLString().vPrintf(fmt, args).c_str());
    1251             :     }
    1252          11 :     va_end(args);
    1253          11 : }
    1254             : 
    1255             : /************************************************************************/
    1256             : /*                 GDALAlgorithmArg::GetEscapedString()                 */
    1257             : /************************************************************************/
    1258             : 
    1259             : /* static */
    1260         130 : std::string GDALAlgorithmArg::GetEscapedString(const std::string &s)
    1261             : {
    1262         142 :     if (s.find_first_of("\" \\,") != std::string::npos &&
    1263           6 :         !(s.size() > 4 &&
    1264           6 :           s[0] == GDALAbstractPipelineAlgorithm::OPEN_NESTED_PIPELINE[0] &&
    1265           2 :           s[1] == ' ' && s[s.size() - 2] == ' ' &&
    1266           2 :           s.back() == GDALAbstractPipelineAlgorithm::CLOSE_NESTED_PIPELINE[0]))
    1267             :     {
    1268           8 :         return std::string("\"")
    1269             :             .append(
    1270           8 :                 CPLString(s).replaceAll('\\', "\\\\").replaceAll('"', "\\\""))
    1271           4 :             .append("\"");
    1272             :     }
    1273             :     else
    1274             :     {
    1275         126 :         return s;
    1276             :     }
    1277             : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                    GDALAlgorithmArg::Serialize()                     */
    1281             : /************************************************************************/
    1282             : 
    1283          39 : bool GDALAlgorithmArg::Serialize(std::string &serializedArg,
    1284             :                                  bool absolutePath) const
    1285             : {
    1286          39 :     serializedArg.clear();
    1287             : 
    1288          39 :     if (!IsExplicitlySet())
    1289             :     {
    1290           0 :         return false;
    1291             :     }
    1292             : 
    1293          78 :     std::string ret = "--";
    1294          39 :     ret += GetName();
    1295          39 :     if (GetType() == GAAT_BOOLEAN)
    1296             :     {
    1297           0 :         serializedArg = std::move(ret);
    1298           0 :         return true;
    1299             :     }
    1300             : 
    1301           5 :     const auto AddListValueSeparator = [this, &ret]()
    1302             :     {
    1303           1 :         if (GetPackedValuesAllowed())
    1304             :         {
    1305           0 :             ret += ',';
    1306             :         }
    1307             :         else
    1308             :         {
    1309           1 :             ret += " --";
    1310           1 :             ret += GetName();
    1311           1 :             ret += ' ';
    1312             :         }
    1313          40 :     };
    1314             : 
    1315           0 :     const auto MakeAbsolutePath = [](const std::string &filename)
    1316             :     {
    1317             :         VSIStatBufL sStat;
    1318           0 :         if (VSIStatL(filename.c_str(), &sStat) != 0 ||
    1319           0 :             !CPLIsFilenameRelative(filename.c_str()))
    1320           0 :             return filename;
    1321           0 :         char *pszCWD = CPLGetCurrentDir();
    1322           0 :         if (!pszCWD)
    1323           0 :             return filename;
    1324             :         const auto absPath =
    1325           0 :             CPLFormFilenameSafe(pszCWD, filename.c_str(), nullptr);
    1326           0 :         CPLFree(pszCWD);
    1327           0 :         return absPath;
    1328             :     };
    1329             : 
    1330          39 :     ret += ' ';
    1331          39 :     switch (GetType())
    1332             :     {
    1333           0 :         case GAAT_BOOLEAN:
    1334           0 :             break;
    1335           8 :         case GAAT_STRING:
    1336             :         {
    1337           8 :             const auto &val = Get<std::string>();
    1338           8 :             ret += GetEscapedString(val);
    1339           8 :             break;
    1340             :         }
    1341           0 :         case GAAT_INTEGER:
    1342             :         {
    1343           0 :             ret += CPLSPrintf("%d", Get<int>());
    1344           0 :             break;
    1345             :         }
    1346           0 :         case GAAT_REAL:
    1347             :         {
    1348           0 :             ret += CPLSPrintf("%.17g", Get<double>());
    1349           0 :             break;
    1350             :         }
    1351           2 :         case GAAT_DATASET:
    1352             :         {
    1353           2 :             const auto &val = Get<GDALArgDatasetValue>();
    1354           2 :             const auto &str = val.GetName();
    1355           2 :             if (str.empty())
    1356             :             {
    1357           0 :                 return false;
    1358             :             }
    1359           2 :             ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str) : str);
    1360           2 :             break;
    1361             :         }
    1362           4 :         case GAAT_STRING_LIST:
    1363             :         {
    1364           4 :             const auto &vals = Get<std::vector<std::string>>();
    1365           8 :             for (size_t i = 0; i < vals.size(); ++i)
    1366             :             {
    1367           4 :                 if (i > 0)
    1368           1 :                     AddListValueSeparator();
    1369           4 :                 ret += GetEscapedString(vals[i]);
    1370             :             }
    1371           4 :             break;
    1372             :         }
    1373           0 :         case GAAT_INTEGER_LIST:
    1374             :         {
    1375           0 :             const auto &vals = Get<std::vector<int>>();
    1376           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1377             :             {
    1378           0 :                 if (i > 0)
    1379           0 :                     AddListValueSeparator();
    1380           0 :                 ret += CPLSPrintf("%d", vals[i]);
    1381             :             }
    1382           0 :             break;
    1383             :         }
    1384           0 :         case GAAT_REAL_LIST:
    1385             :         {
    1386           0 :             const auto &vals = Get<std::vector<double>>();
    1387           0 :             for (size_t i = 0; i < vals.size(); ++i)
    1388             :             {
    1389           0 :                 if (i > 0)
    1390           0 :                     AddListValueSeparator();
    1391           0 :                 ret += CPLSPrintf("%.17g", vals[i]);
    1392             :             }
    1393           0 :             break;
    1394             :         }
    1395          25 :         case GAAT_DATASET_LIST:
    1396             :         {
    1397          25 :             const auto &vals = Get<std::vector<GDALArgDatasetValue>>();
    1398          49 :             for (size_t i = 0; i < vals.size(); ++i)
    1399             :             {
    1400          25 :                 if (i > 0)
    1401           0 :                     AddListValueSeparator();
    1402          25 :                 const auto &val = vals[i];
    1403          25 :                 const auto &str = val.GetName();
    1404          25 :                 if (str.empty())
    1405             :                 {
    1406           1 :                     return false;
    1407             :                 }
    1408          48 :                 ret += GetEscapedString(absolutePath ? MakeAbsolutePath(str)
    1409          24 :                                                      : str);
    1410             :             }
    1411          24 :             break;
    1412             :         }
    1413             :     }
    1414             : 
    1415          38 :     serializedArg = std::move(ret);
    1416          38 :     return true;
    1417             : }
    1418             : 
    1419             : /************************************************************************/
    1420             : /*                  ~GDALInConstructionAlgorithmArg()                   */
    1421             : /************************************************************************/
    1422             : 
    1423             : GDALInConstructionAlgorithmArg::~GDALInConstructionAlgorithmArg() = default;
    1424             : 
    1425             : /************************************************************************/
    1426             : /*              GDALInConstructionAlgorithmArg::AddAlias()              */
    1427             : /************************************************************************/
    1428             : 
    1429             : GDALInConstructionAlgorithmArg &
    1430       61571 : GDALInConstructionAlgorithmArg::AddAlias(const std::string &alias)
    1431             : {
    1432       61571 :     m_decl.AddAlias(alias);
    1433       61571 :     if (m_owner)
    1434       61571 :         m_owner->AddAliasFor(this, alias);
    1435       61571 :     return *this;
    1436             : }
    1437             : 
    1438             : /************************************************************************/
    1439             : /*           GDALInConstructionAlgorithmArg::AddHiddenAlias()           */
    1440             : /************************************************************************/
    1441             : 
    1442             : GDALInConstructionAlgorithmArg &
    1443       13007 : GDALInConstructionAlgorithmArg::AddHiddenAlias(const std::string &alias)
    1444             : {
    1445       13007 :     m_decl.AddHiddenAlias(alias);
    1446       13007 :     if (m_owner)
    1447       13007 :         m_owner->AddAliasFor(this, alias);
    1448       13007 :     return *this;
    1449             : }
    1450             : 
    1451             : /************************************************************************/
    1452             : /*         GDALInConstructionAlgorithmArg::AddShortNameAlias()          */
    1453             : /************************************************************************/
    1454             : 
    1455             : GDALInConstructionAlgorithmArg &
    1456          48 : GDALInConstructionAlgorithmArg::AddShortNameAlias(char shortNameAlias)
    1457             : {
    1458          48 :     m_decl.AddShortNameAlias(shortNameAlias);
    1459          48 :     if (m_owner)
    1460          48 :         m_owner->AddShortNameAliasFor(this, shortNameAlias);
    1461          48 :     return *this;
    1462             : }
    1463             : 
    1464             : /************************************************************************/
    1465             : /*           GDALInConstructionAlgorithmArg::SetPositional()            */
    1466             : /************************************************************************/
    1467             : 
    1468       21123 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetPositional()
    1469             : {
    1470       21123 :     m_decl.SetPositional();
    1471       21123 :     if (m_owner)
    1472       21123 :         m_owner->SetPositional(this);
    1473       21123 :     return *this;
    1474             : }
    1475             : 
    1476             : /************************************************************************/
    1477             : /*              GDALArgDatasetValue::GDALArgDatasetValue()              */
    1478             : /************************************************************************/
    1479             : 
    1480        1274 : GDALArgDatasetValue::GDALArgDatasetValue(GDALDataset *poDS)
    1481        2548 :     : m_poDS(poDS), m_name(m_poDS ? m_poDS->GetDescription() : std::string()),
    1482        1274 :       m_nameSet(true)
    1483             : {
    1484        1274 :     if (m_poDS)
    1485        1274 :         m_poDS->Reference();
    1486        1274 : }
    1487             : 
    1488             : /************************************************************************/
    1489             : /*                      GDALArgDatasetValue::Set()                      */
    1490             : /************************************************************************/
    1491             : 
    1492        2170 : void GDALArgDatasetValue::Set(const std::string &name)
    1493             : {
    1494        2170 :     Close();
    1495        2170 :     m_name = name;
    1496        2170 :     m_nameSet = true;
    1497        2170 :     if (m_ownerArg)
    1498        2165 :         m_ownerArg->NotifyValueSet();
    1499        2170 : }
    1500             : 
    1501             : /************************************************************************/
    1502             : /*                      GDALArgDatasetValue::Set()                      */
    1503             : /************************************************************************/
    1504             : 
    1505        1880 : void GDALArgDatasetValue::Set(std::unique_ptr<GDALDataset> poDS)
    1506             : {
    1507        1880 :     Close();
    1508        1880 :     m_poDS = poDS.release();
    1509        1880 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1510        1880 :     m_nameSet = true;
    1511        1880 :     if (m_ownerArg)
    1512        1758 :         m_ownerArg->NotifyValueSet();
    1513        1880 : }
    1514             : 
    1515             : /************************************************************************/
    1516             : /*                      GDALArgDatasetValue::Set()                      */
    1517             : /************************************************************************/
    1518             : 
    1519        7529 : void GDALArgDatasetValue::Set(GDALDataset *poDS)
    1520             : {
    1521        7529 :     Close();
    1522        7529 :     m_poDS = poDS;
    1523        7529 :     if (m_poDS)
    1524        6706 :         m_poDS->Reference();
    1525        7529 :     m_name = m_poDS ? m_poDS->GetDescription() : std::string();
    1526        7529 :     m_nameSet = true;
    1527        7529 :     if (m_ownerArg)
    1528        3160 :         m_ownerArg->NotifyValueSet();
    1529        7529 : }
    1530             : 
    1531             : /************************************************************************/
    1532             : /*                    GDALArgDatasetValue::SetFrom()                    */
    1533             : /************************************************************************/
    1534             : 
    1535        3154 : void GDALArgDatasetValue::SetFrom(const GDALArgDatasetValue &other)
    1536             : {
    1537        3154 :     Close();
    1538        3154 :     m_name = other.m_name;
    1539        3154 :     m_nameSet = other.m_nameSet;
    1540        3154 :     m_poDS = other.m_poDS;
    1541        3154 :     if (m_poDS)
    1542        2214 :         m_poDS->Reference();
    1543        3154 : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*             GDALArgDatasetValue::~GDALArgDatasetValue()              */
    1547             : /************************************************************************/
    1548             : 
    1549       30321 : GDALArgDatasetValue::~GDALArgDatasetValue()
    1550             : {
    1551       30321 :     Close();
    1552       30321 : }
    1553             : 
    1554             : /************************************************************************/
    1555             : /*                     GDALArgDatasetValue::Close()                     */
    1556             : /************************************************************************/
    1557             : 
    1558       50034 : bool GDALArgDatasetValue::Close()
    1559             : {
    1560       50034 :     bool ret = true;
    1561       50034 :     if (m_poDS && m_poDS->Dereference() == 0)
    1562             :     {
    1563        3192 :         ret = m_poDS->Close() == CE_None;
    1564        3192 :         delete m_poDS;
    1565             :     }
    1566       50034 :     m_poDS = nullptr;
    1567       50034 :     return ret;
    1568             : }
    1569             : 
    1570             : /************************************************************************/
    1571             : /*                   GDALArgDatasetValue::operator=()                   */
    1572             : /************************************************************************/
    1573             : 
    1574           2 : GDALArgDatasetValue &GDALArgDatasetValue::operator=(GDALArgDatasetValue &&other)
    1575             : {
    1576           2 :     Close();
    1577           2 :     m_poDS = other.m_poDS;
    1578           2 :     m_name = other.m_name;
    1579           2 :     m_nameSet = other.m_nameSet;
    1580           2 :     other.m_poDS = nullptr;
    1581           2 :     other.m_name.clear();
    1582           2 :     other.m_nameSet = false;
    1583           2 :     return *this;
    1584             : }
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                  GDALArgDatasetValue::GetDataset()                   */
    1588             : /************************************************************************/
    1589             : 
    1590        1025 : GDALDataset *GDALArgDatasetValue::GetDatasetIncreaseRefCount()
    1591             : {
    1592        1025 :     if (m_poDS)
    1593        1025 :         m_poDS->Reference();
    1594        1025 :     return m_poDS;
    1595             : }
    1596             : 
    1597             : /************************************************************************/
    1598             : /*           GDALArgDatasetValue(GDALArgDatasetValue &&other)           */
    1599             : /************************************************************************/
    1600             : 
    1601        2978 : GDALArgDatasetValue::GDALArgDatasetValue(GDALArgDatasetValue &&other)
    1602        2978 :     : m_poDS(other.m_poDS), m_name(other.m_name), m_nameSet(other.m_nameSet)
    1603             : {
    1604        2978 :     other.m_poDS = nullptr;
    1605        2978 :     other.m_name.clear();
    1606        2978 : }
    1607             : 
    1608             : /************************************************************************/
    1609             : /*            GDALInConstructionAlgorithmArg::SetIsCRSArg()             */
    1610             : /************************************************************************/
    1611             : 
    1612        3212 : GDALInConstructionAlgorithmArg &GDALInConstructionAlgorithmArg::SetIsCRSArg(
    1613             :     bool noneAllowed, const std::vector<std::string> &specialValues)
    1614             : {
    1615        3212 :     if (GetType() != GAAT_STRING)
    1616             :     {
    1617           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1618             :                  "SetIsCRSArg() can only be called on a String argument");
    1619           1 :         return *this;
    1620             :     }
    1621             :     AddValidationAction(
    1622         665 :         [this, noneAllowed, specialValues]()
    1623             :         {
    1624             :             const std::string &osVal =
    1625             :                 static_cast<const GDALInConstructionAlgorithmArg *>(this)
    1626         329 :                     ->Get<std::string>();
    1627         329 :             if (osVal == "?" && m_owner && m_owner->IsCalledFromCommandLine())
    1628           0 :                 return true;
    1629             : 
    1630         645 :             if ((!noneAllowed || (osVal != "none" && osVal != "null")) &&
    1631         316 :                 std::find(specialValues.begin(), specialValues.end(), osVal) ==
    1632         645 :                     specialValues.end())
    1633             :             {
    1634         308 :                 OGRSpatialReference oSRS;
    1635         308 :                 if (oSRS.SetFromUserInput(osVal.c_str()) != OGRERR_NONE)
    1636             :                 {
    1637           7 :                     m_owner->ReportError(CE_Failure, CPLE_AppDefined,
    1638             :                                          "Invalid value for '%s' argument",
    1639           7 :                                          GetName().c_str());
    1640           7 :                     return false;
    1641             :                 }
    1642             :             }
    1643         322 :             return true;
    1644        3211 :         });
    1645             : 
    1646             :     SetAutoCompleteFunction(
    1647          40 :         [this, noneAllowed, specialValues](const std::string &currentValue)
    1648             :         {
    1649          10 :             bool bIsRaster = false;
    1650          10 :             OGREnvelope sDatasetLongLatEnv;
    1651          20 :             std::string osCelestialBodyName;
    1652          10 :             if (GetName() == "dst-crs")
    1653             :             {
    1654          10 :                 auto inputArg = m_owner->GetArg(GDAL_ARG_NAME_INPUT);
    1655          10 :                 if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    1656             :                 {
    1657             :                     auto &val =
    1658          10 :                         inputArg->Get<std::vector<GDALArgDatasetValue>>();
    1659          10 :                     if (val.size() == 1)
    1660             :                     {
    1661           4 :                         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1662             :                         auto poDS = std::unique_ptr<GDALDataset>(
    1663           4 :                             GDALDataset::Open(val[0].GetName().c_str()));
    1664           2 :                         if (poDS)
    1665             :                         {
    1666           2 :                             bIsRaster = poDS->GetRasterCount() != 0;
    1667           2 :                             if (auto poCRS = poDS->GetSpatialRef())
    1668             :                             {
    1669             :                                 const char *pszCelestialBodyName =
    1670           2 :                                     poCRS->GetCelestialBodyName();
    1671           2 :                                 if (pszCelestialBodyName)
    1672           2 :                                     osCelestialBodyName = pszCelestialBodyName;
    1673             : 
    1674           2 :                                 if (!pszCelestialBodyName ||
    1675           2 :                                     !EQUAL(pszCelestialBodyName, "Earth"))
    1676             :                                 {
    1677           0 :                                     OGRSpatialReference oLongLat;
    1678           0 :                                     oLongLat.CopyGeogCSFrom(poCRS);
    1679           0 :                                     oLongLat.SetAxisMappingStrategy(
    1680             :                                         OAMS_TRADITIONAL_GIS_ORDER);
    1681           0 :                                     poDS->GetExtent(&sDatasetLongLatEnv,
    1682           0 :                                                     &oLongLat);
    1683             :                                 }
    1684             :                                 else
    1685             :                                 {
    1686           2 :                                     poDS->GetExtentWGS84LongLat(
    1687           2 :                                         &sDatasetLongLatEnv);
    1688             :                                 }
    1689             :                             }
    1690             :                         }
    1691             :                     }
    1692             :                 }
    1693             :             }
    1694             : 
    1695             :             const auto IsCRSCompatible =
    1696       42959 :                 [bIsRaster, &sDatasetLongLatEnv,
    1697       73695 :                  &osCelestialBodyName](const OSRCRSInfo *crsInfo)
    1698             :             {
    1699       42959 :                 if (!sDatasetLongLatEnv.IsInit())
    1700       30685 :                     return true;
    1701       24108 :                 return crsInfo->eType != OSR_CRS_TYPE_VERTICAL &&
    1702       11834 :                        !(bIsRaster &&
    1703        5917 :                          crsInfo->eType == OSR_CRS_TYPE_GEOCENTRIC) &&
    1704       11652 :                        crsInfo->dfWestLongitudeDeg <
    1705       11652 :                            crsInfo->dfEastLongitudeDeg &&
    1706       11517 :                        sDatasetLongLatEnv.MinX < crsInfo->dfEastLongitudeDeg &&
    1707        5618 :                        sDatasetLongLatEnv.MaxX > crsInfo->dfWestLongitudeDeg &&
    1708         615 :                        sDatasetLongLatEnv.MinY < crsInfo->dfNorthLatitudeDeg &&
    1709       24437 :                        sDatasetLongLatEnv.MaxY > crsInfo->dfSouthLatitudeDeg &&
    1710         329 :                        ((!osCelestialBodyName.empty() &&
    1711         658 :                          crsInfo->pszCelestialBodyName &&
    1712         329 :                          osCelestialBodyName ==
    1713         329 :                              crsInfo->pszCelestialBodyName) ||
    1714           0 :                         (osCelestialBodyName.empty() &&
    1715       12274 :                          !crsInfo->pszCelestialBodyName));
    1716          10 :             };
    1717             : 
    1718          10 :             std::vector<std::string> oRet;
    1719          10 :             if (noneAllowed)
    1720           0 :                 oRet.push_back("none");
    1721          10 :             oRet.insert(oRet.end(), specialValues.begin(), specialValues.end());
    1722          10 :             if (!currentValue.empty())
    1723             :             {
    1724             :                 const CPLStringList aosTokens(
    1725          14 :                     CSLTokenizeString2(currentValue.c_str(), ":", 0));
    1726           7 :                 int nCount = 0;
    1727             :                 std::unique_ptr<OSRCRSInfo *, decltype(&OSRDestroyCRSInfoList)>
    1728             :                     pCRSList(OSRGetCRSInfoListFromDatabase(aosTokens[0],
    1729             :                                                            nullptr, &nCount),
    1730          14 :                              OSRDestroyCRSInfoList);
    1731          14 :                 std::string osCode;
    1732             : 
    1733          14 :                 std::vector<const OSRCRSInfo *> candidates;
    1734       46270 :                 for (int i = 0; i < nCount; ++i)
    1735             :                 {
    1736       46263 :                     const auto *entry = (pCRSList.get())[i];
    1737       46263 :                     if (!entry->bDeprecated && IsCRSCompatible(entry))
    1738             :                     {
    1739       49425 :                         if (aosTokens.size() == 1 ||
    1740       18411 :                             STARTS_WITH(entry->pszCode, aosTokens[1]))
    1741             :                         {
    1742       12666 :                             if (candidates.empty())
    1743           7 :                                 osCode = entry->pszCode;
    1744       12666 :                             candidates.push_back(entry);
    1745             :                         }
    1746             :                     }
    1747             :                 }
    1748           7 :                 if (candidates.size() == 1)
    1749             :                 {
    1750           1 :                     oRet.push_back(std::move(osCode));
    1751             :                 }
    1752             :                 else
    1753             :                 {
    1754           6 :                     if (sDatasetLongLatEnv.IsInit())
    1755             :                     {
    1756           2 :                         std::sort(
    1757             :                             candidates.begin(), candidates.end(),
    1758        2999 :                             [](const OSRCRSInfo *a, const OSRCRSInfo *b)
    1759             :                             {
    1760        2999 :                                 const double dfXa =
    1761        2999 :                                     a->dfWestLongitudeDeg >
    1762        2999 :                                             a->dfEastLongitudeDeg
    1763        2999 :                                         ? a->dfWestLongitudeDeg -
    1764           0 :                                               a->dfEastLongitudeDeg
    1765        2999 :                                         : (180 - a->dfWestLongitudeDeg) +
    1766        2999 :                                               (a->dfEastLongitudeDeg - -180);
    1767        2999 :                                 const double dfYa = a->dfNorthLatitudeDeg -
    1768        2999 :                                                     a->dfSouthLatitudeDeg;
    1769        2999 :                                 const double dfXb =
    1770        2999 :                                     b->dfWestLongitudeDeg >
    1771        2999 :                                             b->dfEastLongitudeDeg
    1772        2999 :                                         ? b->dfWestLongitudeDeg -
    1773           0 :                                               b->dfEastLongitudeDeg
    1774        2999 :                                         : (180 - b->dfWestLongitudeDeg) +
    1775        2999 :                                               (b->dfEastLongitudeDeg - -180);
    1776        2999 :                                 const double dfYb = b->dfNorthLatitudeDeg -
    1777        2999 :                                                     b->dfSouthLatitudeDeg;
    1778        2999 :                                 const double diffArea =
    1779        2999 :                                     dfXa * dfYa - dfXb * dfYb;
    1780        2999 :                                 if (diffArea < 0)
    1781         279 :                                     return true;
    1782        2720 :                                 if (diffArea == 0)
    1783             :                                 {
    1784        2506 :                                     if (std::string_view(a->pszName) ==
    1785        2506 :                                         b->pszName)
    1786             :                                     {
    1787          57 :                                         if (a->eType ==
    1788          13 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D &&
    1789          13 :                                             b->eType !=
    1790             :                                                 OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1791          13 :                                             return true;
    1792          44 :                                         if (a->eType ==
    1793          32 :                                                 OSR_CRS_TYPE_GEOGRAPHIC_3D &&
    1794          32 :                                             b->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1795           9 :                                             return true;
    1796          35 :                                         return false;
    1797             :                                     }
    1798        4898 :                                     return std::string_view(a->pszCode) <
    1799        4898 :                                            b->pszCode;
    1800             :                                 }
    1801         214 :                                 return false;
    1802             :                             });
    1803             :                     }
    1804             : 
    1805       12671 :                     for (const auto *entry : candidates)
    1806             :                     {
    1807       25330 :                         std::string val = std::string(entry->pszCode)
    1808       12665 :                                               .append(" -- ")
    1809       25330 :                                               .append(entry->pszName);
    1810       12665 :                         if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_2D)
    1811        1294 :                             val.append(" (geographic 2D)");
    1812       11371 :                         else if (entry->eType == OSR_CRS_TYPE_GEOGRAPHIC_3D)
    1813         446 :                             val.append(" (geographic 3D)");
    1814       10925 :                         else if (entry->eType == OSR_CRS_TYPE_GEOCENTRIC)
    1815         397 :                             val.append(" (geocentric)");
    1816       12665 :                         oRet.push_back(std::move(val));
    1817             :                     }
    1818             :                 }
    1819             :             }
    1820          10 :             if (currentValue.empty() || oRet.empty())
    1821             :             {
    1822             :                 const CPLStringList aosAuthorities(
    1823           6 :                     OSRGetAuthorityListFromDatabase());
    1824          18 :                 for (const char *pszAuth : cpl::Iterate(aosAuthorities))
    1825             :                 {
    1826          15 :                     int nCount = 0;
    1827          15 :                     OSRDestroyCRSInfoList(OSRGetCRSInfoListFromDatabase(
    1828             :                         pszAuth, nullptr, &nCount));
    1829          15 :                     if (nCount)
    1830          12 :                         oRet.push_back(std::string(pszAuth).append(":"));
    1831             :                 }
    1832             :             }
    1833          20 :             return oRet;
    1834        3211 :         });
    1835             : 
    1836        3211 :     return *this;
    1837             : }
    1838             : 
    1839             : /************************************************************************/
    1840             : /*                    GDALAlgorithm::GDALAlgorithm()                    */
    1841             : /************************************************************************/
    1842             : 
    1843       21101 : GDALAlgorithm::GDALAlgorithm(const std::string &name,
    1844             :                              const std::string &description,
    1845       21101 :                              const std::string &helpURL)
    1846             :     : m_name(name), m_description(description), m_helpURL(helpURL),
    1847       42042 :       m_helpFullURL(!m_helpURL.empty() && m_helpURL[0] == '/'
    1848       21101 :                         ? "https://gdal.org" + m_helpURL
    1849       62913 :                         : m_helpURL)
    1850             : {
    1851             :     auto &helpArg =
    1852             :         AddArg("help", 'h', _("Display help message and exit"),
    1853       42202 :                &m_helpRequested)
    1854       21101 :             .SetHiddenForAPI()
    1855       42202 :             .SetCategory(GAAC_COMMON)
    1856          14 :             .AddAction([this]()
    1857       21101 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1858             :     auto &helpDocArg =
    1859             :         AddArg("help-doc", 0,
    1860             :                _("Display help message for use by documentation"),
    1861       42202 :                &m_helpDocRequested)
    1862       21101 :             .SetHidden()
    1863          12 :             .AddAction([this]()
    1864       21101 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1865             :     auto &jsonUsageArg =
    1866             :         AddArg("json-usage", 0, _("Display usage as JSON document and exit"),
    1867       42202 :                &m_JSONUsageRequested)
    1868       21101 :             .SetHiddenForAPI()
    1869       42202 :             .SetCategory(GAAC_COMMON)
    1870           4 :             .AddAction([this]()
    1871       21101 :                        { m_specialActionRequested = m_calledFromCommandLine; });
    1872       42202 :     AddArg("config", 0, _("Configuration option"), &m_dummyConfigOptions)
    1873       42202 :         .SetMetaVar("<KEY>=<VALUE>")
    1874       21101 :         .SetHiddenForAPI()
    1875       42202 :         .SetCategory(GAAC_COMMON)
    1876             :         .AddAction(
    1877           2 :             [this]()
    1878             :             {
    1879           2 :                 ReportError(
    1880             :                     CE_Warning, CPLE_AppDefined,
    1881             :                     "Configuration options passed with the 'config' argument "
    1882             :                     "are ignored");
    1883       21101 :             });
    1884             : 
    1885       21101 :     AddValidationAction(
    1886       12477 :         [this, &helpArg, &helpDocArg, &jsonUsageArg]()
    1887             :         {
    1888        6435 :             if (!m_calledFromCommandLine && m_specialActionRequested)
    1889             :             {
    1890           0 :                 for (auto &arg : {&helpArg, &helpDocArg, &jsonUsageArg})
    1891             :                 {
    1892           0 :                     if (arg->IsExplicitlySet())
    1893             :                     {
    1894           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    1895             :                                     "'%s' argument only available when called "
    1896             :                                     "from command line",
    1897           0 :                                     arg->GetName().c_str());
    1898           0 :                         return false;
    1899             :                     }
    1900             :                 }
    1901             :             }
    1902        6435 :             return true;
    1903             :         });
    1904       21101 : }
    1905             : 
    1906             : /************************************************************************/
    1907             : /*                   GDALAlgorithm::~GDALAlgorithm()                    */
    1908             : /************************************************************************/
    1909             : 
    1910             : GDALAlgorithm::~GDALAlgorithm() = default;
    1911             : 
    1912             : /************************************************************************/
    1913             : /*                    GDALAlgorithm::ParseArgument()                    */
    1914             : /************************************************************************/
    1915             : 
    1916        2981 : bool GDALAlgorithm::ParseArgument(
    1917             :     GDALAlgorithmArg *arg, const std::string &name, const std::string &value,
    1918             :     std::map<
    1919             :         GDALAlgorithmArg *,
    1920             :         std::variant<std::vector<std::string>, std::vector<int>,
    1921             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    1922             :         &inConstructionValues)
    1923             : {
    1924        2981 :     const bool isListArg = GDALAlgorithmArgTypeIsList(arg->GetType());
    1925        2981 :     if (arg->IsExplicitlySet() && !isListArg)
    1926             :     {
    1927             :         // Hack for "gdal info" to be able to pass an opened raster dataset
    1928             :         // by "gdal raster info" to the "gdal vector info" algorithm.
    1929           3 :         if (arg->SkipIfAlreadySet())
    1930             :         {
    1931           1 :             arg->SetSkipIfAlreadySet(false);
    1932           1 :             return true;
    1933             :         }
    1934             : 
    1935           2 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1936             :                     "Argument '%s' has already been specified.", name.c_str());
    1937           2 :         return false;
    1938             :     }
    1939             : 
    1940        3044 :     if (!arg->GetRepeatedArgAllowed() &&
    1941          66 :         cpl::contains(inConstructionValues, arg))
    1942             :     {
    1943           1 :         ReportError(CE_Failure, CPLE_IllegalArg,
    1944             :                     "Argument '%s' has already been specified.", name.c_str());
    1945           1 :         return false;
    1946             :     }
    1947             : 
    1948        2977 :     switch (arg->GetType())
    1949             :     {
    1950         304 :         case GAAT_BOOLEAN:
    1951             :         {
    1952         304 :             if (value.empty() || value == "true")
    1953         302 :                 return arg->Set(true);
    1954           2 :             else if (value == "false")
    1955           1 :                 return arg->Set(false);
    1956             :             else
    1957             :             {
    1958           1 :                 ReportError(
    1959             :                     CE_Failure, CPLE_IllegalArg,
    1960             :                     "Invalid value '%s' for boolean argument '%s'. Should be "
    1961             :                     "'true' or 'false'.",
    1962             :                     value.c_str(), name.c_str());
    1963           1 :                 return false;
    1964             :             }
    1965             :         }
    1966             : 
    1967         736 :         case GAAT_STRING:
    1968             :         {
    1969         736 :             return arg->Set(value);
    1970             :         }
    1971             : 
    1972         350 :         case GAAT_INTEGER:
    1973             :         {
    1974         350 :             errno = 0;
    1975         350 :             char *endptr = nullptr;
    1976         350 :             const auto val = std::strtol(value.c_str(), &endptr, 10);
    1977         349 :             if (errno == 0 && endptr &&
    1978         699 :                 endptr == value.c_str() + value.size() && val >= INT_MIN &&
    1979             :                 val <= INT_MAX)
    1980             :             {
    1981         347 :                 return arg->Set(static_cast<int>(val));
    1982             :             }
    1983             :             else
    1984             :             {
    1985           3 :                 ReportError(CE_Failure, CPLE_IllegalArg,
    1986             :                             "Expected integer value for argument '%s', "
    1987             :                             "but got '%s'.",
    1988             :                             name.c_str(), value.c_str());
    1989           3 :                 return false;
    1990             :             }
    1991             :         }
    1992             : 
    1993          32 :         case GAAT_REAL:
    1994             :         {
    1995          32 :             char *endptr = nullptr;
    1996          32 :             double dfValue = CPLStrtod(value.c_str(), &endptr);
    1997          32 :             if (endptr != value.c_str() + value.size())
    1998             :             {
    1999           1 :                 ReportError(
    2000             :                     CE_Failure, CPLE_IllegalArg,
    2001             :                     "Expected real value for argument '%s', but got '%s'.",
    2002             :                     name.c_str(), value.c_str());
    2003           1 :                 return false;
    2004             :             }
    2005          31 :             return arg->Set(dfValue);
    2006             :         }
    2007             : 
    2008         522 :         case GAAT_DATASET:
    2009             :         {
    2010         522 :             return arg->SetDatasetName(value);
    2011             :         }
    2012             : 
    2013         255 :         case GAAT_STRING_LIST:
    2014             :         {
    2015             :             const CPLStringList aosTokens(
    2016         255 :                 arg->GetPackedValuesAllowed()
    2017         164 :                     ? CSLTokenizeString2(value.c_str(), ",",
    2018             :                                          CSLT_HONOURSTRINGS |
    2019             :                                              CSLT_PRESERVEQUOTES)
    2020         674 :                     : CSLAddString(nullptr, value.c_str()));
    2021         255 :             if (!cpl::contains(inConstructionValues, arg))
    2022             :             {
    2023         231 :                 inConstructionValues[arg] = std::vector<std::string>();
    2024             :             }
    2025             :             auto &valueVector =
    2026         255 :                 std::get<std::vector<std::string>>(inConstructionValues[arg]);
    2027         542 :             for (const char *v : aosTokens)
    2028             :             {
    2029         287 :                 valueVector.push_back(v);
    2030             :             }
    2031         255 :             break;
    2032             :         }
    2033             : 
    2034          62 :         case GAAT_INTEGER_LIST:
    2035             :         {
    2036             :             const CPLStringList aosTokens(
    2037          62 :                 arg->GetPackedValuesAllowed()
    2038          62 :                     ? CSLTokenizeString2(
    2039             :                           value.c_str(), ",",
    2040             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2041             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2042         124 :                     : CSLAddString(nullptr, value.c_str()));
    2043          62 :             if (!cpl::contains(inConstructionValues, arg))
    2044             :             {
    2045          58 :                 inConstructionValues[arg] = std::vector<int>();
    2046             :             }
    2047             :             auto &valueVector =
    2048          62 :                 std::get<std::vector<int>>(inConstructionValues[arg]);
    2049         192 :             for (const char *v : aosTokens)
    2050             :             {
    2051         136 :                 errno = 0;
    2052         136 :                 char *endptr = nullptr;
    2053         136 :                 const auto val = std::strtol(v, &endptr, 10);
    2054         136 :                 if (errno == 0 && endptr && endptr == v + strlen(v) &&
    2055         132 :                     val >= INT_MIN && val <= INT_MAX && strlen(v) > 0)
    2056             :                 {
    2057         130 :                     valueVector.push_back(static_cast<int>(val));
    2058             :                 }
    2059             :                 else
    2060             :                 {
    2061           6 :                     ReportError(
    2062             :                         CE_Failure, CPLE_IllegalArg,
    2063             :                         "Expected list of integer value for argument '%s', "
    2064             :                         "but got '%s'.",
    2065             :                         name.c_str(), value.c_str());
    2066           6 :                     return false;
    2067             :                 }
    2068             :             }
    2069          56 :             break;
    2070             :         }
    2071             : 
    2072          99 :         case GAAT_REAL_LIST:
    2073             :         {
    2074             :             const CPLStringList aosTokens(
    2075          99 :                 arg->GetPackedValuesAllowed()
    2076          99 :                     ? CSLTokenizeString2(
    2077             :                           value.c_str(), ",",
    2078             :                           CSLT_HONOURSTRINGS | CSLT_STRIPLEADSPACES |
    2079             :                               CSLT_STRIPENDSPACES | CSLT_ALLOWEMPTYTOKENS)
    2080         198 :                     : CSLAddString(nullptr, value.c_str()));
    2081          99 :             if (!cpl::contains(inConstructionValues, arg))
    2082             :             {
    2083          97 :                 inConstructionValues[arg] = std::vector<double>();
    2084             :             }
    2085             :             auto &valueVector =
    2086          99 :                 std::get<std::vector<double>>(inConstructionValues[arg]);
    2087         401 :             for (const char *v : aosTokens)
    2088             :             {
    2089         306 :                 char *endptr = nullptr;
    2090         306 :                 double dfValue = CPLStrtod(v, &endptr);
    2091         306 :                 if (strlen(v) == 0 || endptr != v + strlen(v))
    2092             :                 {
    2093           4 :                     ReportError(
    2094             :                         CE_Failure, CPLE_IllegalArg,
    2095             :                         "Expected list of real value for argument '%s', "
    2096             :                         "but got '%s'.",
    2097             :                         name.c_str(), value.c_str());
    2098           4 :                     return false;
    2099             :                 }
    2100         302 :                 valueVector.push_back(dfValue);
    2101             :             }
    2102          95 :             break;
    2103             :         }
    2104             : 
    2105         617 :         case GAAT_DATASET_LIST:
    2106             :         {
    2107         617 :             if (!cpl::contains(inConstructionValues, arg))
    2108             :             {
    2109         605 :                 inConstructionValues[arg] = std::vector<GDALArgDatasetValue>();
    2110             :             }
    2111             :             auto &valueVector = std::get<std::vector<GDALArgDatasetValue>>(
    2112         617 :                 inConstructionValues[arg]);
    2113         617 :             if (!value.empty() && value[0] == '{' && value.back() == '}')
    2114             :             {
    2115          12 :                 valueVector.push_back(GDALArgDatasetValue(value));
    2116             :             }
    2117             :             else
    2118             :             {
    2119             :                 const CPLStringList aosTokens(
    2120         605 :                     arg->GetPackedValuesAllowed()
    2121           6 :                         ? CSLTokenizeString2(value.c_str(), ",",
    2122             :                                              CSLT_HONOURSTRINGS |
    2123             :                                                  CSLT_STRIPLEADSPACES)
    2124        1216 :                         : CSLAddString(nullptr, value.c_str()));
    2125        1213 :                 for (const char *v : aosTokens)
    2126             :                 {
    2127         608 :                     valueVector.push_back(GDALArgDatasetValue(v));
    2128             :                 }
    2129             :             }
    2130         617 :             break;
    2131             :         }
    2132             :     }
    2133             : 
    2134        1023 :     return true;
    2135             : }
    2136             : 
    2137             : /************************************************************************/
    2138             : /*              GDALAlgorithm::ParseCommandLineArguments()              */
    2139             : /************************************************************************/
    2140             : 
    2141        1970 : bool GDALAlgorithm::ParseCommandLineArguments(
    2142             :     const std::vector<std::string> &args)
    2143             : {
    2144        1970 :     if (m_parsedSubStringAlreadyCalled)
    2145             :     {
    2146           6 :         ReportError(CE_Failure, CPLE_AppDefined,
    2147             :                     "ParseCommandLineArguments() can only be called once per "
    2148             :                     "instance.");
    2149           6 :         return false;
    2150             :     }
    2151        1964 :     m_parsedSubStringAlreadyCalled = true;
    2152             : 
    2153             :     // AWS like syntax supported too (not advertized)
    2154        1964 :     if (args.size() == 1 && args[0] == "help")
    2155             :     {
    2156           1 :         auto arg = GetArg("help");
    2157           1 :         assert(arg);
    2158           1 :         arg->Set(true);
    2159           1 :         arg->RunActions();
    2160           1 :         return true;
    2161             :     }
    2162             : 
    2163        1963 :     if (HasSubAlgorithms())
    2164             :     {
    2165         423 :         if (args.empty())
    2166             :         {
    2167           2 :             ReportError(CE_Failure, CPLE_AppDefined, "Missing %s name.",
    2168           2 :                         m_callPath.size() == 1 ? "command" : "subcommand");
    2169           2 :             return false;
    2170             :         }
    2171         421 :         if (!args[0].empty() && args[0][0] == '-')
    2172             :         {
    2173             :             // go on argument parsing
    2174             :         }
    2175             :         else
    2176             :         {
    2177         418 :             const auto nCounter = CPLGetErrorCounter();
    2178         418 :             m_selectedSubAlgHolder = InstantiateSubAlgorithm(args[0]);
    2179         418 :             if (m_selectedSubAlgHolder)
    2180             :             {
    2181         415 :                 m_selectedSubAlg = m_selectedSubAlgHolder.get();
    2182         415 :                 m_selectedSubAlg->SetReferencePathForRelativePaths(
    2183         415 :                     m_referencePath);
    2184         415 :                 m_selectedSubAlg->m_executionForStreamOutput =
    2185         415 :                     m_executionForStreamOutput;
    2186         415 :                 m_selectedSubAlg->m_calledFromCommandLine =
    2187         415 :                     m_calledFromCommandLine;
    2188         415 :                 m_selectedSubAlg->m_skipValidationInParseCommandLine =
    2189         415 :                     m_skipValidationInParseCommandLine;
    2190         415 :                 bool bRet = m_selectedSubAlg->ParseCommandLineArguments(
    2191         830 :                     std::vector<std::string>(args.begin() + 1, args.end()));
    2192         415 :                 m_selectedSubAlg->PropagateSpecialActionTo(this);
    2193         415 :                 return bRet;
    2194             :             }
    2195             :             else
    2196             :             {
    2197           4 :                 if (!(CPLGetErrorCounter() == nCounter + 1 &&
    2198           1 :                       strstr(CPLGetLastErrorMsg(), "Do you mean")))
    2199             :                 {
    2200           2 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2201           2 :                                 "Unknown command: '%s'", args[0].c_str());
    2202             :                 }
    2203           3 :                 return false;
    2204             :             }
    2205             :         }
    2206             :     }
    2207             : 
    2208             :     std::map<
    2209             :         GDALAlgorithmArg *,
    2210             :         std::variant<std::vector<std::string>, std::vector<int>,
    2211             :                      std::vector<double>, std::vector<GDALArgDatasetValue>>>
    2212        3086 :         inConstructionValues;
    2213             : 
    2214        3086 :     std::vector<std::string> lArgs(args);
    2215        1543 :     bool helpValueRequested = false;
    2216        4529 :     for (size_t i = 0; i < lArgs.size(); /* incremented in loop */)
    2217             :     {
    2218        3085 :         const auto &strArg = lArgs[i];
    2219        3085 :         GDALAlgorithmArg *arg = nullptr;
    2220        3085 :         std::string name;
    2221        3085 :         std::string value;
    2222        3085 :         bool hasValue = false;
    2223        3085 :         if (m_calledFromCommandLine && cpl::ends_with(strArg, "=?"))
    2224           5 :             helpValueRequested = true;
    2225        3085 :         if (strArg.size() >= 2 && strArg[0] == '-' && strArg[1] == '-')
    2226             :         {
    2227        1974 :             const auto equalPos = strArg.find('=');
    2228        3948 :             name = (equalPos != std::string::npos) ? strArg.substr(0, equalPos)
    2229        1974 :                                                    : strArg;
    2230        1974 :             const std::string nameWithoutDash = name.substr(2);
    2231        1974 :             auto iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2232        2027 :             if (m_arbitraryLongNameArgsAllowed &&
    2233        2027 :                 iterArg == m_mapLongNameToArg.end())
    2234             :             {
    2235          16 :                 GetArg(nameWithoutDash);
    2236          16 :                 iterArg = m_mapLongNameToArg.find(nameWithoutDash);
    2237             :             }
    2238        1974 :             if (iterArg == m_mapLongNameToArg.end())
    2239             :             {
    2240             :                 const std::string bestCandidate =
    2241          26 :                     GetSuggestionForArgumentName(nameWithoutDash);
    2242          26 :                 if (!bestCandidate.empty())
    2243             :                 {
    2244           2 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2245             :                                 "Option '%s' is unknown. Do you mean '--%s'?",
    2246             :                                 name.c_str(), bestCandidate.c_str());
    2247             :                 }
    2248             :                 else
    2249             :                 {
    2250          24 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    2251             :                                 "Option '%s' is unknown.", name.c_str());
    2252             :                 }
    2253          26 :                 return false;
    2254             :             }
    2255        1948 :             arg = iterArg->second;
    2256        1948 :             if (equalPos != std::string::npos)
    2257             :             {
    2258         427 :                 hasValue = true;
    2259         427 :                 value = strArg.substr(equalPos + 1);
    2260             :             }
    2261             :         }
    2262        1185 :         else if (strArg.size() >= 2 && strArg[0] == '-' &&
    2263          74 :                  CPLGetValueType(strArg.c_str()) == CPL_VALUE_STRING)
    2264             :         {
    2265         143 :             for (size_t j = 1; j < strArg.size(); ++j)
    2266             :             {
    2267          74 :                 name.clear();
    2268          74 :                 name += strArg[j];
    2269          74 :                 const auto iterArg = m_mapShortNameToArg.find(name);
    2270          74 :                 if (iterArg == m_mapShortNameToArg.end())
    2271             :                 {
    2272           5 :                     const std::string nameWithoutDash = strArg.substr(1);
    2273           5 :                     if (m_mapLongNameToArg.find(nameWithoutDash) !=
    2274          10 :                         m_mapLongNameToArg.end())
    2275             :                     {
    2276           1 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2277             :                                     "Short name option '%s' is unknown. Do you "
    2278             :                                     "mean '--%s' (with leading double dash) ?",
    2279             :                                     name.c_str(), nameWithoutDash.c_str());
    2280             :                     }
    2281             :                     else
    2282             :                     {
    2283             :                         const std::string bestCandidate =
    2284           8 :                             GetSuggestionForArgumentName(nameWithoutDash);
    2285           4 :                         if (!bestCandidate.empty())
    2286             :                         {
    2287           1 :                             ReportError(
    2288             :                                 CE_Failure, CPLE_IllegalArg,
    2289             :                                 "Short name option '%s' is unknown. Do you "
    2290             :                                 "mean '--%s' (with leading double dash) ?",
    2291             :                                 name.c_str(), bestCandidate.c_str());
    2292             :                         }
    2293             :                         else
    2294             :                         {
    2295           3 :                             ReportError(CE_Failure, CPLE_IllegalArg,
    2296             :                                         "Short name option '%s' is unknown.",
    2297             :                                         name.c_str());
    2298             :                         }
    2299             :                     }
    2300           5 :                     return false;
    2301             :                 }
    2302          69 :                 arg = iterArg->second;
    2303          69 :                 if (strArg.size() > 2)
    2304             :                 {
    2305           0 :                     if (arg->GetType() != GAAT_BOOLEAN)
    2306             :                     {
    2307           0 :                         ReportError(CE_Failure, CPLE_IllegalArg,
    2308             :                                     "Invalid argument '%s'. Option '%s' is not "
    2309             :                                     "a boolean option.",
    2310             :                                     strArg.c_str(), name.c_str());
    2311           0 :                         return false;
    2312             :                     }
    2313             : 
    2314           0 :                     if (!ParseArgument(arg, name, "true", inConstructionValues))
    2315           0 :                         return false;
    2316             :                 }
    2317             :             }
    2318          69 :             if (strArg.size() > 2)
    2319             :             {
    2320           0 :                 lArgs.erase(lArgs.begin() + i);
    2321           0 :                 continue;
    2322             :             }
    2323             :         }
    2324             :         else
    2325             :         {
    2326        1037 :             ++i;
    2327        1037 :             continue;
    2328             :         }
    2329        2017 :         CPLAssert(arg);
    2330             : 
    2331        2017 :         if (arg && arg->GetType() == GAAT_BOOLEAN)
    2332             :         {
    2333         305 :             if (!hasValue)
    2334             :             {
    2335         302 :                 hasValue = true;
    2336         302 :                 value = "true";
    2337             :             }
    2338             :         }
    2339             : 
    2340        2017 :         if (!hasValue)
    2341             :         {
    2342        1288 :             if (i + 1 == lArgs.size())
    2343             :             {
    2344          30 :                 if (m_parseForAutoCompletion)
    2345             :                 {
    2346          24 :                     lArgs.erase(lArgs.begin() + i);
    2347          24 :                     break;
    2348             :                 }
    2349           6 :                 ReportError(
    2350             :                     CE_Failure, CPLE_IllegalArg,
    2351             :                     "Expected value for argument '%s', but ran short of tokens",
    2352             :                     name.c_str());
    2353           6 :                 return false;
    2354             :             }
    2355        1258 :             value = lArgs[i + 1];
    2356        1258 :             lArgs.erase(lArgs.begin() + i + 1);
    2357             :         }
    2358             : 
    2359        1987 :         if (arg && !ParseArgument(arg, name, value, inConstructionValues))
    2360          38 :             return false;
    2361             : 
    2362        1949 :         lArgs.erase(lArgs.begin() + i);
    2363             :     }
    2364             : 
    2365        1468 :     if (m_specialActionRequested)
    2366             :     {
    2367          23 :         return true;
    2368             :     }
    2369             : 
    2370        2396 :     const auto ProcessInConstructionValues = [&inConstructionValues]()
    2371             :     {
    2372        2362 :         for (auto &[arg, value] : inConstructionValues)
    2373             :         {
    2374         975 :             if (arg->GetType() == GAAT_STRING_LIST)
    2375             :             {
    2376         228 :                 if (!arg->Set(std::get<std::vector<std::string>>(
    2377         228 :                         inConstructionValues[arg])))
    2378             :                 {
    2379          34 :                     return false;
    2380             :                 }
    2381             :             }
    2382         747 :             else if (arg->GetType() == GAAT_INTEGER_LIST)
    2383             :             {
    2384          53 :                 if (!arg->Set(
    2385          53 :                         std::get<std::vector<int>>(inConstructionValues[arg])))
    2386             :                 {
    2387           4 :                     return false;
    2388             :                 }
    2389             :             }
    2390         694 :             else if (arg->GetType() == GAAT_REAL_LIST)
    2391             :             {
    2392          93 :                 if (!arg->Set(std::get<std::vector<double>>(
    2393          93 :                         inConstructionValues[arg])))
    2394             :                 {
    2395          10 :                     return false;
    2396             :                 }
    2397             :             }
    2398         601 :             else if (arg->GetType() == GAAT_DATASET_LIST)
    2399             :             {
    2400         601 :                 if (!arg->Set(
    2401             :                         std::move(std::get<std::vector<GDALArgDatasetValue>>(
    2402         601 :                             inConstructionValues[arg]))))
    2403             :                 {
    2404           2 :                     return false;
    2405             :                 }
    2406             :             }
    2407             :         }
    2408        1387 :         return true;
    2409        1445 :     };
    2410             : 
    2411             :     // Process positional arguments that have not been set through their
    2412             :     // option name.
    2413        1445 :     size_t i = 0;
    2414        1445 :     size_t iCurPosArg = 0;
    2415             : 
    2416             :     // Special case for <INPUT> <AUXILIARY>... <OUTPUT>
    2417        1466 :     if (m_positionalArgs.size() == 3 &&
    2418          22 :         (m_positionalArgs[0]->IsRequired() ||
    2419          21 :          m_positionalArgs[0]->GetMinCount() == 1) &&
    2420          40 :         m_positionalArgs[0]->GetMaxCount() == 1 &&
    2421          27 :         (m_positionalArgs[1]->IsRequired() ||
    2422          27 :          m_positionalArgs[1]->GetMinCount() == 1) &&
    2423             :         /* Second argument may have several occurrences */
    2424          40 :         m_positionalArgs[1]->GetMaxCount() >= 1 &&
    2425          20 :         (m_positionalArgs[2]->IsRequired() ||
    2426          20 :          m_positionalArgs[2]->GetMinCount() == 1) &&
    2427          20 :         m_positionalArgs[2]->GetMaxCount() == 1 &&
    2428           9 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2429        1475 :         !m_positionalArgs[1]->IsExplicitlySet() &&
    2430           9 :         !m_positionalArgs[2]->IsExplicitlySet())
    2431             :     {
    2432           7 :         if (lArgs.size() - i < 3)
    2433             :         {
    2434           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    2435             :                         "Not enough positional values.");
    2436           1 :             return false;
    2437             :         }
    2438          12 :         bool ok = ParseArgument(m_positionalArgs[0],
    2439           6 :                                 m_positionalArgs[0]->GetName().c_str(),
    2440           6 :                                 lArgs[i], inConstructionValues);
    2441           6 :         if (ok)
    2442             :         {
    2443           5 :             ++i;
    2444          11 :             for (; i + 1 < lArgs.size() && ok; ++i)
    2445             :             {
    2446          12 :                 ok = ParseArgument(m_positionalArgs[1],
    2447           6 :                                    m_positionalArgs[1]->GetName().c_str(),
    2448           6 :                                    lArgs[i], inConstructionValues);
    2449             :             }
    2450             :         }
    2451           6 :         if (ok)
    2452             :         {
    2453          10 :             ok = ParseArgument(m_positionalArgs[2],
    2454          10 :                                m_positionalArgs[2]->GetName().c_str(), lArgs[i],
    2455             :                                inConstructionValues);
    2456           5 :             ++i;
    2457             :         }
    2458           6 :         if (!ok)
    2459             :         {
    2460           3 :             ProcessInConstructionValues();
    2461           3 :             return false;
    2462             :         }
    2463             :     }
    2464             : 
    2465         493 :     if (m_inputDatasetCanBeOmitted && m_positionalArgs.size() >= 1 &&
    2466         575 :         !m_positionalArgs[0]->IsExplicitlySet() &&
    2467        2247 :         m_positionalArgs[0]->GetName() == GDAL_ARG_NAME_INPUT &&
    2468         114 :         (m_positionalArgs[0]->GetType() == GAAT_DATASET ||
    2469          57 :          m_positionalArgs[0]->GetType() == GAAT_DATASET_LIST))
    2470             :     {
    2471          57 :         ++iCurPosArg;
    2472             :     }
    2473             : 
    2474        2394 :     while (i < lArgs.size() && iCurPosArg < m_positionalArgs.size())
    2475             :     {
    2476         960 :         GDALAlgorithmArg *arg = m_positionalArgs[iCurPosArg];
    2477         963 :         while (arg->IsExplicitlySet())
    2478             :         {
    2479           4 :             ++iCurPosArg;
    2480           4 :             if (iCurPosArg == m_positionalArgs.size())
    2481           1 :                 break;
    2482           3 :             arg = m_positionalArgs[iCurPosArg];
    2483             :         }
    2484         960 :         if (iCurPosArg == m_positionalArgs.size())
    2485             :         {
    2486           1 :             break;
    2487             :         }
    2488        1479 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()) &&
    2489         520 :             arg->GetMinCount() != arg->GetMaxCount())
    2490             :         {
    2491         104 :             if (iCurPosArg == 0)
    2492             :             {
    2493          68 :                 size_t nCountAtEnd = 0;
    2494          93 :                 for (size_t j = 1; j < m_positionalArgs.size(); j++)
    2495             :                 {
    2496          27 :                     const auto *otherArg = m_positionalArgs[j];
    2497          27 :                     if (GDALAlgorithmArgTypeIsList(otherArg->GetType()))
    2498             :                     {
    2499           4 :                         if (otherArg->GetMinCount() != otherArg->GetMaxCount())
    2500             :                         {
    2501           2 :                             ReportError(
    2502             :                                 CE_Failure, CPLE_AppDefined,
    2503             :                                 "Ambiguity in definition of positional "
    2504             :                                 "argument "
    2505             :                                 "'%s' given it has a varying number of values, "
    2506             :                                 "but follows argument '%s' which also has a "
    2507             :                                 "varying number of values",
    2508           1 :                                 otherArg->GetName().c_str(),
    2509           1 :                                 arg->GetName().c_str());
    2510           1 :                             ProcessInConstructionValues();
    2511           1 :                             return false;
    2512             :                         }
    2513           3 :                         nCountAtEnd += otherArg->GetMinCount();
    2514             :                     }
    2515             :                     else
    2516             :                     {
    2517          23 :                         if (!otherArg->IsRequired())
    2518             :                         {
    2519           2 :                             ReportError(
    2520             :                                 CE_Failure, CPLE_AppDefined,
    2521             :                                 "Ambiguity in definition of positional "
    2522             :                                 "argument "
    2523             :                                 "'%s', given it is not required but follows "
    2524             :                                 "argument '%s' which has a varying number of "
    2525             :                                 "values",
    2526           1 :                                 otherArg->GetName().c_str(),
    2527           1 :                                 arg->GetName().c_str());
    2528           1 :                             ProcessInConstructionValues();
    2529           1 :                             return false;
    2530             :                         }
    2531          22 :                         nCountAtEnd++;
    2532             :                     }
    2533             :                 }
    2534          66 :                 if (lArgs.size() < nCountAtEnd)
    2535             :                 {
    2536           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    2537             :                                 "Not enough positional values.");
    2538           1 :                     ProcessInConstructionValues();
    2539           1 :                     return false;
    2540             :                 }
    2541         137 :                 for (; i < lArgs.size() - nCountAtEnd; ++i)
    2542             :                 {
    2543          72 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2544             :                                        inConstructionValues))
    2545             :                     {
    2546           0 :                         ProcessInConstructionValues();
    2547           0 :                         return false;
    2548             :                     }
    2549             :                 }
    2550             :             }
    2551          36 :             else if (iCurPosArg == m_positionalArgs.size() - 1)
    2552             :             {
    2553          82 :                 for (; i < lArgs.size(); ++i)
    2554             :                 {
    2555          47 :                     if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2556             :                                        inConstructionValues))
    2557             :                     {
    2558           0 :                         ProcessInConstructionValues();
    2559           0 :                         return false;
    2560             :                     }
    2561             :                 }
    2562             :             }
    2563             :             else
    2564             :             {
    2565           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2566             :                             "Ambiguity in definition of positional arguments: "
    2567             :                             "arguments with varying number of values must be "
    2568             :                             "first or last one.");
    2569           1 :                 return false;
    2570             :             }
    2571             :         }
    2572             :         else
    2573             :         {
    2574         855 :             if (lArgs.size() - i < static_cast<size_t>(arg->GetMaxCount()))
    2575             :             {
    2576           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    2577             :                             "Not enough positional values.");
    2578           1 :                 return false;
    2579             :             }
    2580         854 :             const size_t iMax = i + arg->GetMaxCount();
    2581        1711 :             for (; i < iMax; ++i)
    2582             :             {
    2583         858 :                 if (!ParseArgument(arg, arg->GetName().c_str(), lArgs[i],
    2584             :                                    inConstructionValues))
    2585             :                 {
    2586           1 :                     ProcessInConstructionValues();
    2587           1 :                     return false;
    2588             :                 }
    2589             :             }
    2590             :         }
    2591         953 :         ++iCurPosArg;
    2592             :     }
    2593             : 
    2594        1435 :     if (i < lArgs.size())
    2595             :     {
    2596          21 :         ReportError(CE_Failure, CPLE_AppDefined,
    2597             :                     "Positional values starting at '%s' are not expected.",
    2598          21 :                     lArgs[i].c_str());
    2599          21 :         return false;
    2600             :     }
    2601             : 
    2602        1414 :     if (!ProcessInConstructionValues())
    2603             :     {
    2604          33 :         return false;
    2605             :     }
    2606             : 
    2607             :     // Skip to first unset positional argument.
    2608        2375 :     while (iCurPosArg < m_positionalArgs.size() &&
    2609         536 :            m_positionalArgs[iCurPosArg]->IsExplicitlySet())
    2610             :     {
    2611         458 :         ++iCurPosArg;
    2612             :     }
    2613             :     // Check if this positional argument is required.
    2614        1458 :     if (iCurPosArg < m_positionalArgs.size() && !helpValueRequested &&
    2615          77 :         (GDALAlgorithmArgTypeIsList(m_positionalArgs[iCurPosArg]->GetType())
    2616          47 :              ? m_positionalArgs[iCurPosArg]->GetMinCount() > 0
    2617          30 :              : m_positionalArgs[iCurPosArg]->IsRequired()))
    2618             :     {
    2619          66 :         ReportError(CE_Failure, CPLE_AppDefined,
    2620             :                     "Positional arguments starting at '%s' have not been "
    2621             :                     "specified.",
    2622          66 :                     m_positionalArgs[iCurPosArg]->GetMetaVar().c_str());
    2623          66 :         return false;
    2624             :     }
    2625             : 
    2626        1315 :     if (m_calledFromCommandLine)
    2627             :     {
    2628        4793 :         for (auto &arg : m_args)
    2629             :         {
    2630        6312 :             if (arg->IsExplicitlySet() &&
    2631        1021 :                 ((arg->GetType() == GAAT_STRING &&
    2632        1018 :                   arg->Get<std::string>() == "?") ||
    2633         936 :                  (arg->GetType() == GAAT_STRING_LIST &&
    2634         157 :                   arg->Get<std::vector<std::string>>().size() == 1 &&
    2635          78 :                   arg->Get<std::vector<std::string>>()[0] == "?")))
    2636             :             {
    2637             :                 {
    2638          10 :                     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2639           5 :                     ValidateArguments();
    2640             :                 }
    2641             : 
    2642           5 :                 auto choices = arg->GetChoices();
    2643           5 :                 if (choices.empty())
    2644           2 :                     choices = arg->GetAutoCompleteChoices(std::string());
    2645           5 :                 if (!choices.empty())
    2646             :                 {
    2647           5 :                     if (choices.size() == 1)
    2648             :                     {
    2649           4 :                         ReportError(
    2650             :                             CE_Failure, CPLE_AppDefined,
    2651             :                             "Single potential value for argument '%s' is '%s'",
    2652           4 :                             arg->GetName().c_str(), choices.front().c_str());
    2653             :                     }
    2654             :                     else
    2655             :                     {
    2656           6 :                         std::string msg("Potential values for argument '");
    2657           3 :                         msg += arg->GetName();
    2658           3 :                         msg += "' are:";
    2659          45 :                         for (const auto &v : choices)
    2660             :                         {
    2661          42 :                             msg += "\n- ";
    2662          42 :                             msg += v;
    2663             :                         }
    2664           3 :                         ReportError(CE_Failure, CPLE_AppDefined, "%s",
    2665             :                                     msg.c_str());
    2666             :                     }
    2667           5 :                     return false;
    2668             :                 }
    2669             :             }
    2670             :         }
    2671             :     }
    2672             : 
    2673        1310 :     return m_skipValidationInParseCommandLine || ValidateArguments();
    2674             : }
    2675             : 
    2676             : /************************************************************************/
    2677             : /*                     GDALAlgorithm::ReportError()                     */
    2678             : /************************************************************************/
    2679             : 
    2680             : //! @cond Doxygen_Suppress
    2681         846 : void GDALAlgorithm::ReportError(CPLErr eErrClass, CPLErrorNum err_no,
    2682             :                                 const char *fmt, ...) const
    2683             : {
    2684             :     va_list args;
    2685         846 :     va_start(args, fmt);
    2686         846 :     CPLError(eErrClass, err_no, "%s",
    2687         846 :              std::string(m_name)
    2688         846 :                  .append(": ")
    2689        1692 :                  .append(CPLString().vPrintf(fmt, args))
    2690             :                  .c_str());
    2691         846 :     va_end(args);
    2692         846 : }
    2693             : 
    2694             : //! @endcond
    2695             : 
    2696             : /************************************************************************/
    2697             : /*                  GDALAlgorithm::ProcessDatasetArg()                  */
    2698             : /************************************************************************/
    2699             : 
    2700        9127 : bool GDALAlgorithm::ProcessDatasetArg(GDALAlgorithmArg *arg,
    2701             :                                       GDALAlgorithm *algForOutput)
    2702             : {
    2703        9127 :     bool ret = true;
    2704             : 
    2705        9127 :     const auto updateArg = algForOutput->GetArg(GDAL_ARG_NAME_UPDATE);
    2706        9127 :     const bool hasUpdateArg = updateArg && updateArg->GetType() == GAAT_BOOLEAN;
    2707        9127 :     const bool update = hasUpdateArg && updateArg->Get<bool>();
    2708             : 
    2709        9127 :     const auto appendArg = algForOutput->GetArg(GDAL_ARG_NAME_APPEND);
    2710        9127 :     const bool hasAppendArg = appendArg && appendArg->GetType() == GAAT_BOOLEAN;
    2711        9127 :     const bool append = hasAppendArg && appendArg->Get<bool>();
    2712             : 
    2713        9127 :     const auto overwriteArg = algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE);
    2714             :     const bool overwrite =
    2715       15286 :         (arg->IsOutput() && overwriteArg &&
    2716       15286 :          overwriteArg->GetType() == GAAT_BOOLEAN && overwriteArg->Get<bool>());
    2717             : 
    2718        9127 :     auto outputArg = algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT);
    2719       18254 :     auto &val = [arg]() -> GDALArgDatasetValue &
    2720             :     {
    2721        9127 :         if (arg->GetType() == GAAT_DATASET_LIST)
    2722        5057 :             return arg->Get<std::vector<GDALArgDatasetValue>>()[0];
    2723             :         else
    2724        4070 :             return arg->Get<GDALArgDatasetValue>();
    2725        9127 :     }();
    2726             :     const bool onlyInputSpecifiedInUpdateAndOutputNotRequired =
    2727       14296 :         arg->GetName() == GDAL_ARG_NAME_INPUT && outputArg &&
    2728       14304 :         !outputArg->IsExplicitlySet() && !outputArg->IsRequired() && update &&
    2729           8 :         !overwrite;
    2730        9127 :     if (!val.GetDatasetRef() && !val.IsNameSet())
    2731             :     {
    2732           3 :         ReportError(CE_Failure, CPLE_AppDefined,
    2733             :                     "Argument '%s' has no dataset object or dataset name.",
    2734           3 :                     arg->GetName().c_str());
    2735           3 :         ret = false;
    2736             :     }
    2737        9124 :     else if (val.GetDatasetRef() && !CheckCanSetDatasetObject(arg))
    2738             :     {
    2739           1 :         return false;
    2740             :     }
    2741         199 :     else if (m_inputDatasetCanBeOmitted &&
    2742        9322 :              val.GetName() == GDAL_DATASET_PIPELINE_PLACEHOLDER_VALUE &&
    2743           8 :              !arg->IsOutput())
    2744             :     {
    2745           8 :         return true;
    2746             :     }
    2747       13350 :     else if (!val.GetDatasetRef() && arg->AutoOpenDataset() &&
    2748        4235 :              (!arg->IsOutput() || (arg == outputArg && update && !overwrite) ||
    2749             :               onlyInputSpecifiedInUpdateAndOutputNotRequired))
    2750             :     {
    2751        1284 :         int flags = arg->GetDatasetType();
    2752        1284 :         bool assignToOutputArg = false;
    2753             : 
    2754             :         // Check if input and output parameters point to the same
    2755             :         // filename (for vector datasets)
    2756        2368 :         if (arg->GetName() == GDAL_ARG_NAME_INPUT && update && !overwrite &&
    2757        2368 :             outputArg && outputArg->GetType() == GAAT_DATASET)
    2758             :         {
    2759          62 :             auto &outputVal = outputArg->Get<GDALArgDatasetValue>();
    2760         121 :             if (!outputVal.GetDatasetRef() &&
    2761         121 :                 outputVal.GetName() == val.GetName() &&
    2762           2 :                 (outputArg->GetDatasetInputFlags() & GADV_OBJECT) != 0)
    2763             :             {
    2764           2 :                 assignToOutputArg = true;
    2765           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2766             :             }
    2767          60 :             else if (onlyInputSpecifiedInUpdateAndOutputNotRequired)
    2768             :             {
    2769           2 :                 flags |= GDAL_OF_UPDATE | GDAL_OF_VERBOSE_ERROR;
    2770             :             }
    2771             :         }
    2772             : 
    2773        1284 :         if (!arg->IsOutput() || arg->GetDatasetInputFlags() == GADV_NAME)
    2774        1201 :             flags |= GDAL_OF_VERBOSE_ERROR;
    2775        1284 :         if ((arg == outputArg || !outputArg) && update)
    2776             :         {
    2777          85 :             flags |= GDAL_OF_UPDATE;
    2778          85 :             if (!append)
    2779          64 :                 flags |= GDAL_OF_VERBOSE_ERROR;
    2780             :         }
    2781             : 
    2782        1284 :         const auto readOnlyArg = GetArg(GDAL_ARG_NAME_READ_ONLY);
    2783             :         const bool readOnly =
    2784        1327 :             (readOnlyArg && readOnlyArg->GetType() == GAAT_BOOLEAN &&
    2785          43 :              readOnlyArg->Get<bool>());
    2786        1284 :         if (readOnly)
    2787          12 :             flags &= ~GDAL_OF_UPDATE;
    2788             : 
    2789        2568 :         CPLStringList aosOpenOptions;
    2790        2568 :         CPLStringList aosAllowedDrivers;
    2791        1284 :         if (arg->IsInput())
    2792             :         {
    2793        1284 :             if (arg == outputArg)
    2794             :             {
    2795          83 :                 if (update && !overwrite)
    2796             :                 {
    2797          83 :                     const auto ooArg = GetArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION);
    2798          83 :                     if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2799          31 :                         aosOpenOptions = CPLStringList(
    2800          31 :                             ooArg->Get<std::vector<std::string>>());
    2801             :                 }
    2802             :             }
    2803             :             else
    2804             :             {
    2805        1201 :                 const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    2806        1201 :                 if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    2807             :                     aosOpenOptions =
    2808        1150 :                         CPLStringList(ooArg->Get<std::vector<std::string>>());
    2809             : 
    2810        1201 :                 const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    2811        1201 :                 if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    2812             :                     aosAllowedDrivers =
    2813        1107 :                         CPLStringList(ifArg->Get<std::vector<std::string>>());
    2814             :             }
    2815             :         }
    2816             : 
    2817        2568 :         std::string osDatasetName = val.GetName();
    2818        1284 :         if (!m_referencePath.empty())
    2819             :         {
    2820          42 :             osDatasetName = GDALDataset::BuildFilename(
    2821          21 :                 osDatasetName.c_str(), m_referencePath.c_str(), true);
    2822             :         }
    2823        1284 :         if (osDatasetName == "-" && (flags & GDAL_OF_UPDATE) == 0)
    2824           0 :             osDatasetName = "/vsistdin/";
    2825             : 
    2826             :         // Handle special case of overview delete in GTiff which would fail
    2827             :         // if it is COG without IGNORE_COG_LAYOUT_BREAK=YES open option.
    2828         142 :         if ((flags & GDAL_OF_UPDATE) != 0 && m_callPath.size() == 4 &&
    2829        1428 :             m_callPath[2] == "overview" && m_callPath[3] == "delete" &&
    2830           2 :             aosOpenOptions.FetchNameValue("IGNORE_COG_LAYOUT_BREAK") == nullptr)
    2831             :         {
    2832           4 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2833             :             GDALDriverH hDrv =
    2834           2 :                 GDALIdentifyDriver(osDatasetName.c_str(), nullptr);
    2835           2 :             if (hDrv && EQUAL(GDALGetDescription(hDrv), "GTiff"))
    2836             :             {
    2837             :                 // Cleaning does not break COG layout
    2838           2 :                 aosOpenOptions.SetNameValue("IGNORE_COG_LAYOUT_BREAK", "YES");
    2839             :             }
    2840             :         }
    2841             : 
    2842        1284 :         auto oIter = m_oMapDatasetNameToDataset.find(osDatasetName.c_str());
    2843             :         GDALDataset *poDS;
    2844             :         {
    2845             :             // The PostGISRaster may emit an error message, that is not
    2846             :             // relevant, if it is the vector driver that was intended
    2847        1284 :             std::unique_ptr<CPLErrorStateBackuper> poBackuper;
    2848        1284 :             if (cpl::starts_with(osDatasetName, "PG:") &&
    2849           0 :                 (flags & (GDAL_OF_RASTER | GDAL_OF_VECTOR)) != 0)
    2850             :             {
    2851           0 :                 poBackuper = std::make_unique<CPLErrorStateBackuper>(
    2852           0 :                     CPLQuietErrorHandler);
    2853             :             }
    2854             : 
    2855        1284 :             CPL_IGNORE_RET_VAL(poBackuper);
    2856        1284 :             poDS = oIter != m_oMapDatasetNameToDataset.end()
    2857        1284 :                        ? oIter->second
    2858        1281 :                        : GDALDataset::Open(osDatasetName.c_str(), flags,
    2859        1281 :                                            aosAllowedDrivers.List(),
    2860        1281 :                                            aosOpenOptions.List());
    2861             : 
    2862             :             // Retry with PostGIS vector driver
    2863          59 :             if (!poDS && poBackuper &&
    2864           0 :                 GetGDALDriverManager()->GetDriverByName("PostGISRaster") &&
    2865        1343 :                 aosAllowedDrivers.empty() && aosOpenOptions.empty())
    2866             :             {
    2867           0 :                 poBackuper.reset();
    2868           0 :                 poDS = GDALDataset::Open(
    2869           0 :                     osDatasetName.c_str(), flags & ~GDAL_OF_RASTER,
    2870           0 :                     aosAllowedDrivers.List(), aosOpenOptions.List());
    2871             :             }
    2872             :         }
    2873             : 
    2874        1284 :         if (poDS)
    2875             :         {
    2876        1225 :             if (oIter != m_oMapDatasetNameToDataset.end())
    2877             :             {
    2878           3 :                 if (arg->GetType() == GAAT_DATASET)
    2879           3 :                     arg->Get<GDALArgDatasetValue>().Set(poDS->GetDescription());
    2880           3 :                 poDS->Reference();
    2881           3 :                 m_oMapDatasetNameToDataset.erase(oIter);
    2882             :             }
    2883             : 
    2884             :             // A bit of a hack for situations like 'gdal raster clip --like "PG:..."'
    2885             :             // where the PG: dataset will be first opened with the PostGISRaster
    2886             :             // driver whereas the PostgreSQL (vector) one is actually wanted.
    2887        1660 :             if (poDS->GetRasterCount() == 0 && (flags & GDAL_OF_RASTER) != 0 &&
    2888        1742 :                 (flags & GDAL_OF_VECTOR) != 0 && aosAllowedDrivers.empty() &&
    2889          82 :                 aosOpenOptions.empty())
    2890             :             {
    2891          78 :                 auto poDrv = poDS->GetDriver();
    2892          78 :                 if (poDrv && EQUAL(poDrv->GetDescription(), "PostGISRaster"))
    2893             :                 {
    2894             :                     // Retry with PostgreSQL (vector) driver
    2895             :                     std::unique_ptr<GDALDataset> poTmpDS(GDALDataset::Open(
    2896           0 :                         osDatasetName.c_str(), flags & ~GDAL_OF_RASTER));
    2897           0 :                     if (poTmpDS)
    2898             :                     {
    2899           0 :                         poDS->ReleaseRef();
    2900           0 :                         poDS = poTmpDS.release();
    2901             :                     }
    2902             :                 }
    2903             :             }
    2904             : 
    2905        1225 :             if (assignToOutputArg)
    2906             :             {
    2907             :                 // Avoid opening twice the same datasource if it is both
    2908             :                 // the input and output.
    2909             :                 // Known to cause problems with at least FGdb, SQLite
    2910             :                 // and GPKG drivers. See #4270
    2911             :                 // Restrict to those 3 drivers. For example it is known
    2912             :                 // to break with the PG driver due to the way it
    2913             :                 // manages transactions.
    2914           2 :                 auto poDriver = poDS->GetDriver();
    2915           4 :                 if (poDriver && (EQUAL(poDriver->GetDescription(), "FileGDB") ||
    2916           2 :                                  EQUAL(poDriver->GetDescription(), "SQLite") ||
    2917           2 :                                  EQUAL(poDriver->GetDescription(), "GPKG")))
    2918             :                 {
    2919           2 :                     outputArg->Get<GDALArgDatasetValue>().Set(poDS);
    2920             :                 }
    2921             :             }
    2922        1225 :             val.SetDatasetOpenedByAlgorithm();
    2923        1225 :             val.Set(poDS);
    2924        1225 :             poDS->ReleaseRef();
    2925             :         }
    2926          59 :         else if (!append)
    2927             :         {
    2928          57 :             ret = false;
    2929             :         }
    2930             :     }
    2931             : 
    2932             :     // Deal with overwriting the output dataset
    2933        9118 :     if (ret && arg == outputArg && val.GetDatasetRef() == nullptr)
    2934             :     {
    2935        2953 :         if (!append)
    2936             :         {
    2937             :             // If outputting to MEM, do not try to erase a real file of the same name!
    2938             :             const auto outputFormatArg =
    2939        2941 :                 algForOutput->GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    2940        8793 :             if (!(outputFormatArg &&
    2941        2926 :                   outputFormatArg->GetType() == GAAT_STRING &&
    2942        2926 :                   (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    2943        1898 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    2944        1004 :                          "stream") ||
    2945        1004 :                    EQUAL(outputFormatArg->Get<std::string>().c_str(),
    2946             :                          "Memory"))))
    2947             :             {
    2948        1019 :                 const char *pszType = "";
    2949        1019 :                 GDALDriver *poDriver = nullptr;
    2950        1992 :                 if (!val.GetName().empty() &&
    2951         973 :                     GDALDoesFileOrDatasetExist(val.GetName().c_str(), &pszType,
    2952             :                                                &poDriver))
    2953             :                 {
    2954          78 :                     if (!overwrite)
    2955             :                     {
    2956          68 :                         std::string options;
    2957          34 :                         if (algForOutput->GetArg(GDAL_ARG_NAME_OVERWRITE_LAYER))
    2958             :                         {
    2959          11 :                             options += "--";
    2960          11 :                             options += GDAL_ARG_NAME_OVERWRITE_LAYER;
    2961             :                         }
    2962          34 :                         if (hasAppendArg)
    2963             :                         {
    2964          22 :                             if (!options.empty())
    2965           8 :                                 options += '/';
    2966          22 :                             options += "--";
    2967          22 :                             options += GDAL_ARG_NAME_APPEND;
    2968             :                         }
    2969          34 :                         if (hasUpdateArg)
    2970             :                         {
    2971          15 :                             if (!options.empty())
    2972          12 :                                 options += '/';
    2973          15 :                             options += "--";
    2974          15 :                             options += GDAL_ARG_NAME_UPDATE;
    2975             :                         }
    2976             : 
    2977          34 :                         if (poDriver)
    2978             :                         {
    2979          68 :                             const char *pszPrefix = poDriver->GetMetadataItem(
    2980          34 :                                 GDAL_DMD_CONNECTION_PREFIX);
    2981          34 :                             if (pszPrefix &&
    2982           0 :                                 STARTS_WITH_CI(val.GetName().c_str(),
    2983             :                                                pszPrefix))
    2984             :                             {
    2985           0 :                                 bool bExists = false;
    2986             :                                 {
    2987             :                                     CPLErrorStateBackuper oBackuper(
    2988           0 :                                         CPLQuietErrorHandler);
    2989           0 :                                     bExists = std::unique_ptr<GDALDataset>(
    2990             :                                                   GDALDataset::Open(
    2991           0 :                                                       val.GetName().c_str())) !=
    2992             :                                               nullptr;
    2993             :                                 }
    2994           0 :                                 if (bExists)
    2995             :                                 {
    2996           0 :                                     if (!options.empty())
    2997           0 :                                         options = " You may specify the " +
    2998           0 :                                                   options + " option.";
    2999           0 :                                     ReportError(CE_Failure, CPLE_AppDefined,
    3000             :                                                 "%s '%s' already exists.%s",
    3001           0 :                                                 pszType, val.GetName().c_str(),
    3002             :                                                 options.c_str());
    3003           0 :                                     return false;
    3004             :                                 }
    3005             : 
    3006           0 :                                 return true;
    3007             :                             }
    3008             :                         }
    3009             : 
    3010          34 :                         if (!options.empty())
    3011          28 :                             options = '/' + options;
    3012          68 :                         ReportError(
    3013             :                             CE_Failure, CPLE_AppDefined,
    3014             :                             "%s '%s' already exists. You may specify the "
    3015             :                             "--overwrite%s option.",
    3016          34 :                             pszType, val.GetName().c_str(), options.c_str());
    3017          34 :                         return false;
    3018             :                     }
    3019          44 :                     else if (EQUAL(pszType, "File"))
    3020             :                     {
    3021           1 :                         if (VSIUnlink(val.GetName().c_str()) != 0)
    3022             :                         {
    3023           0 :                             ReportError(CE_Failure, CPLE_AppDefined,
    3024             :                                         "Deleting %s failed: %s",
    3025           0 :                                         val.GetName().c_str(),
    3026           0 :                                         VSIStrerror(errno));
    3027           0 :                             return false;
    3028             :                         }
    3029             :                     }
    3030          43 :                     else if (EQUAL(pszType, "Directory"))
    3031             :                     {
    3032             :                         // We don't want the user to accidentally erase a non-GDAL dataset
    3033           1 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3034             :                                     "Directory '%s' already exists, but is not "
    3035             :                                     "recognized as a valid GDAL dataset. "
    3036             :                                     "Please manually delete it before retrying",
    3037           1 :                                     val.GetName().c_str());
    3038           1 :                         return false;
    3039             :                     }
    3040          42 :                     else if (poDriver)
    3041             :                     {
    3042             :                         bool bDeleteOK;
    3043             :                         {
    3044             :                             CPLErrorStateBackuper oBackuper(
    3045          42 :                                 CPLQuietErrorHandler);
    3046          42 :                             bDeleteOK = (poDriver->Delete(
    3047          42 :                                              val.GetName().c_str()) == CE_None);
    3048             :                         }
    3049             :                         VSIStatBufL sStat;
    3050          45 :                         if (!bDeleteOK &&
    3051           3 :                             VSIStatL(val.GetName().c_str(), &sStat) == 0)
    3052             :                         {
    3053           3 :                             if (VSI_ISDIR(sStat.st_mode))
    3054             :                             {
    3055             :                                 // We don't want the user to accidentally erase a non-GDAL dataset
    3056           0 :                                 ReportError(
    3057             :                                     CE_Failure, CPLE_AppDefined,
    3058             :                                     "Directory '%s' already exists, but is not "
    3059             :                                     "recognized as a valid GDAL dataset. "
    3060             :                                     "Please manually delete it before retrying",
    3061           0 :                                     val.GetName().c_str());
    3062           2 :                                 return false;
    3063             :                             }
    3064           3 :                             else if (VSIUnlink(val.GetName().c_str()) != 0)
    3065             :                             {
    3066           2 :                                 ReportError(CE_Failure, CPLE_AppDefined,
    3067             :                                             "Deleting %s failed: %s",
    3068           2 :                                             val.GetName().c_str(),
    3069           2 :                                             VSIStrerror(errno));
    3070           2 :                                 return false;
    3071             :                             }
    3072             :                         }
    3073             :                     }
    3074             :                 }
    3075             :             }
    3076             :         }
    3077             :     }
    3078             : 
    3079             :     // If outputting to stdout, automatically turn off progress bar
    3080        9081 :     if (arg == outputArg && val.GetName() == "/vsistdout/")
    3081             :     {
    3082           8 :         auto quietArg = GetArg(GDAL_ARG_NAME_QUIET);
    3083           8 :         if (quietArg && quietArg->GetType() == GAAT_BOOLEAN)
    3084           5 :             quietArg->Set(true);
    3085             :     }
    3086             : 
    3087        9081 :     return ret;
    3088             : }
    3089             : 
    3090             : /************************************************************************/
    3091             : /*                  GDALAlgorithm::ValidateArguments()                  */
    3092             : /************************************************************************/
    3093             : 
    3094        6439 : bool GDALAlgorithm::ValidateArguments()
    3095             : {
    3096        6439 :     if (m_selectedSubAlg)
    3097           3 :         return m_selectedSubAlg->ValidateArguments();
    3098             : 
    3099        6436 :     if (m_specialActionRequested)
    3100           1 :         return true;
    3101             : 
    3102        6435 :     m_arbitraryLongNameArgsAllowed = false;
    3103             : 
    3104             :     // If only --output=format=MEM/stream is specified and not --output,
    3105             :     // then set empty name for --output.
    3106        6435 :     auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    3107        6435 :     auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3108        3838 :     if (outputArg && outputFormatArg && outputFormatArg->IsExplicitlySet() &&
    3109        2563 :         !outputArg->IsExplicitlySet() &&
    3110         353 :         outputFormatArg->GetType() == GAAT_STRING &&
    3111         353 :         (EQUAL(outputFormatArg->Get<std::string>().c_str(), "MEM") ||
    3112         583 :          EQUAL(outputFormatArg->Get<std::string>().c_str(), "stream")) &&
    3113       10601 :         outputArg->GetType() == GAAT_DATASET &&
    3114         328 :         (outputArg->GetDatasetInputFlags() & GADV_NAME))
    3115             :     {
    3116         328 :         outputArg->Get<GDALArgDatasetValue>().Set("");
    3117             :     }
    3118             : 
    3119             :     // The method may emit several errors if several constraints are not met.
    3120        6435 :     bool ret = true;
    3121        6435 :     std::map<std::string, std::string> mutualExclusionGroupUsed;
    3122      121170 :     for (auto &arg : m_args)
    3123             :     {
    3124             :         // Check mutually exclusive arguments
    3125      114735 :         if (arg->IsExplicitlySet())
    3126             :         {
    3127       18620 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3128       18620 :             if (!mutualExclusionGroup.empty())
    3129             :             {
    3130             :                 auto oIter =
    3131         755 :                     mutualExclusionGroupUsed.find(mutualExclusionGroup);
    3132         755 :                 if (oIter != mutualExclusionGroupUsed.end())
    3133             :                 {
    3134          13 :                     ret = false;
    3135          26 :                     ReportError(
    3136             :                         CE_Failure, CPLE_AppDefined,
    3137             :                         "Argument '%s' is mutually exclusive with '%s'.",
    3138          26 :                         arg->GetName().c_str(), oIter->second.c_str());
    3139             :                 }
    3140             :                 else
    3141             :                 {
    3142         742 :                     mutualExclusionGroupUsed[mutualExclusionGroup] =
    3143        1484 :                         arg->GetName();
    3144             :                 }
    3145             :             }
    3146             :         }
    3147             : 
    3148      114889 :         if (arg->IsRequired() && !arg->IsExplicitlySet() &&
    3149         154 :             !arg->HasDefaultValue())
    3150             :         {
    3151         154 :             bool emitError = true;
    3152         154 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    3153         154 :             if (!mutualExclusionGroup.empty())
    3154             :             {
    3155        1765 :                 for (const auto &otherArg : m_args)
    3156             :                 {
    3157        1751 :                     if (otherArg->GetMutualExclusionGroup() ==
    3158        1856 :                             mutualExclusionGroup &&
    3159         105 :                         otherArg->IsExplicitlySet())
    3160             :                     {
    3161          74 :                         emitError = false;
    3162          74 :                         break;
    3163             :                     }
    3164             :                 }
    3165             :             }
    3166         232 :             if (emitError && !(m_inputDatasetCanBeOmitted &&
    3167          48 :                                arg->GetName() == GDAL_ARG_NAME_INPUT &&
    3168          60 :                                (arg->GetType() == GAAT_DATASET ||
    3169          30 :                                 arg->GetType() == GAAT_DATASET_LIST)))
    3170             :             {
    3171          50 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3172             :                             "Required argument '%s' has not been specified.",
    3173          50 :                             arg->GetName().c_str());
    3174          50 :                 ret = false;
    3175             :             }
    3176             :         }
    3177      114581 :         else if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET)
    3178             :         {
    3179        4070 :             if (!ProcessDatasetArg(arg.get(), this))
    3180          51 :                 ret = false;
    3181             :         }
    3182             : 
    3183      120002 :         if (arg->IsExplicitlySet() && arg->GetType() == GAAT_DATASET_LIST &&
    3184        5267 :             arg->AutoOpenDataset())
    3185             :         {
    3186        4794 :             auto &listVal = arg->Get<std::vector<GDALArgDatasetValue>>();
    3187        4794 :             if (listVal.size() == 1)
    3188             :             {
    3189        4788 :                 if (!ProcessDatasetArg(arg.get(), this))
    3190          37 :                     ret = false;
    3191             :             }
    3192             :             else
    3193             :             {
    3194          18 :                 for (auto &val : listVal)
    3195             :                 {
    3196          12 :                     if (!val.GetDatasetRef() && val.GetName().empty())
    3197             :                     {
    3198           0 :                         ReportError(CE_Failure, CPLE_AppDefined,
    3199             :                                     "Argument '%s' has no dataset object or "
    3200             :                                     "dataset name.",
    3201           0 :                                     arg->GetName().c_str());
    3202           0 :                         ret = false;
    3203             :                     }
    3204          12 :                     else if (!val.GetDatasetRef())
    3205             :                     {
    3206             :                         int flags =
    3207           4 :                             arg->GetDatasetType() | GDAL_OF_VERBOSE_ERROR;
    3208             : 
    3209           8 :                         CPLStringList aosOpenOptions;
    3210           8 :                         CPLStringList aosAllowedDrivers;
    3211           4 :                         if (arg->GetName() == GDAL_ARG_NAME_INPUT)
    3212             :                         {
    3213             :                             const auto ooArg =
    3214           4 :                                 GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    3215           4 :                             if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    3216             :                             {
    3217           4 :                                 aosOpenOptions = CPLStringList(
    3218           4 :                                     ooArg->Get<std::vector<std::string>>());
    3219             :                             }
    3220             : 
    3221             :                             const auto ifArg =
    3222           4 :                                 GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    3223           4 :                             if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    3224             :                             {
    3225           4 :                                 aosAllowedDrivers = CPLStringList(
    3226           4 :                                     ifArg->Get<std::vector<std::string>>());
    3227             :                             }
    3228             : 
    3229           4 :                             const auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3230           0 :                             if (updateArg &&
    3231           4 :                                 updateArg->GetType() == GAAT_BOOLEAN &&
    3232           0 :                                 updateArg->Get<bool>())
    3233             :                             {
    3234           0 :                                 flags |= GDAL_OF_UPDATE;
    3235             :                             }
    3236             :                         }
    3237             : 
    3238             :                         auto poDS = std::unique_ptr<GDALDataset>(
    3239           4 :                             GDALDataset::Open(val.GetName().c_str(), flags,
    3240           4 :                                               aosAllowedDrivers.List(),
    3241          12 :                                               aosOpenOptions.List()));
    3242           4 :                         if (poDS)
    3243             :                         {
    3244           3 :                             val.Set(std::move(poDS));
    3245             :                         }
    3246             :                         else
    3247             :                         {
    3248           1 :                             ret = false;
    3249             :                         }
    3250             :                     }
    3251             :                 }
    3252             :             }
    3253             :         }
    3254             : 
    3255      114735 :         if (arg->IsExplicitlySet() && !arg->RunValidationActions())
    3256             :         {
    3257           8 :             ret = false;
    3258             :         }
    3259             :     }
    3260             : 
    3261       27402 :     for (const auto &f : m_validationActions)
    3262             :     {
    3263       20967 :         if (!f())
    3264          78 :             ret = false;
    3265             :     }
    3266             : 
    3267        6435 :     return ret;
    3268             : }
    3269             : 
    3270             : /************************************************************************/
    3271             : /*                GDALAlgorithm::InstantiateSubAlgorithm                */
    3272             : /************************************************************************/
    3273             : 
    3274             : std::unique_ptr<GDALAlgorithm>
    3275        9794 : GDALAlgorithm::InstantiateSubAlgorithm(const std::string &name,
    3276             :                                        bool suggestionAllowed) const
    3277             : {
    3278        9794 :     auto ret = m_subAlgRegistry.Instantiate(name);
    3279       19588 :     auto childCallPath = m_callPath;
    3280        9794 :     childCallPath.push_back(name);
    3281        9794 :     if (!ret)
    3282             :     {
    3283         914 :         ret = GDALGlobalAlgorithmRegistry::GetSingleton()
    3284         914 :                   .InstantiateDeclaredSubAlgorithm(childCallPath);
    3285             :     }
    3286        9794 :     if (ret)
    3287             :     {
    3288        9659 :         ret->SetCallPath(childCallPath);
    3289             :     }
    3290         135 :     else if (suggestionAllowed)
    3291             :     {
    3292          58 :         std::string bestCandidate;
    3293          29 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3294         470 :         for (const std::string &candidate : GetSubAlgorithmNames())
    3295             :         {
    3296             :             const size_t distance =
    3297         441 :                 CPLLevenshteinDistance(name.c_str(), candidate.c_str(),
    3298             :                                        /* transpositionAllowed = */ true);
    3299         441 :             if (distance < bestDistance)
    3300             :             {
    3301          71 :                 bestCandidate = candidate;
    3302          71 :                 bestDistance = distance;
    3303             :             }
    3304         370 :             else if (distance == bestDistance)
    3305             :             {
    3306          45 :                 bestCandidate.clear();
    3307             :             }
    3308             :         }
    3309          29 :         if (!bestCandidate.empty() && bestDistance <= 2)
    3310             :         {
    3311           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3312             :                      "Algorithm '%s' is unknown. Do you mean '%s'?",
    3313             :                      name.c_str(), bestCandidate.c_str());
    3314             :         }
    3315             :     }
    3316       19588 :     return ret;
    3317             : }
    3318             : 
    3319             : /************************************************************************/
    3320             : /*            GDALAlgorithm::GetSuggestionForArgumentName()             */
    3321             : /************************************************************************/
    3322             : 
    3323             : std::string
    3324          36 : GDALAlgorithm::GetSuggestionForArgumentName(const std::string &osName) const
    3325             : {
    3326          36 :     if (osName.size() >= 3)
    3327             :     {
    3328          33 :         std::string bestCandidate;
    3329          33 :         size_t bestDistance = std::numeric_limits<size_t>::max();
    3330         677 :         for (const auto &[key, value] : m_mapLongNameToArg)
    3331             :         {
    3332         644 :             CPL_IGNORE_RET_VAL(value);
    3333         644 :             const size_t distance = CPLLevenshteinDistance(
    3334             :                 osName.c_str(), key.c_str(), /* transpositionAllowed = */ true);
    3335         644 :             if (distance < bestDistance)
    3336             :             {
    3337          85 :                 bestCandidate = key;
    3338          85 :                 bestDistance = distance;
    3339             :             }
    3340         559 :             else if (distance == bestDistance)
    3341             :             {
    3342          77 :                 bestCandidate.clear();
    3343             :             }
    3344             :         }
    3345          47 :         if (!bestCandidate.empty() &&
    3346          14 :             bestDistance <= (bestCandidate.size() >= 4U ? 2U : 1U))
    3347             :         {
    3348           5 :             return bestCandidate;
    3349             :         }
    3350             :     }
    3351          31 :     return std::string();
    3352             : }
    3353             : 
    3354             : /************************************************************************/
    3355             : /*         GDALAlgorithm::IsKnownOutputRelatedBooleanArgName()          */
    3356             : /************************************************************************/
    3357             : 
    3358             : /* static */
    3359          22 : bool GDALAlgorithm::IsKnownOutputRelatedBooleanArgName(std::string_view osName)
    3360             : {
    3361          66 :     return osName == GDAL_ARG_NAME_APPEND || osName == GDAL_ARG_NAME_UPDATE ||
    3362          66 :            osName == GDAL_ARG_NAME_OVERWRITE ||
    3363          44 :            osName == GDAL_ARG_NAME_OVERWRITE_LAYER;
    3364             : }
    3365             : 
    3366             : /************************************************************************/
    3367             : /*                   GDALAlgorithm::HasOutputString()                   */
    3368             : /************************************************************************/
    3369             : 
    3370          67 : bool GDALAlgorithm::HasOutputString() const
    3371             : {
    3372          67 :     auto outputStringArg = GetArg(GDAL_ARG_NAME_OUTPUT_STRING);
    3373          67 :     return outputStringArg && outputStringArg->IsOutput();
    3374             : }
    3375             : 
    3376             : /************************************************************************/
    3377             : /*                       GDALAlgorithm::GetArg()                        */
    3378             : /************************************************************************/
    3379             : 
    3380      446768 : GDALAlgorithmArg *GDALAlgorithm::GetArg(const std::string &osName,
    3381             :                                         bool suggestionAllowed, bool isConst)
    3382             : {
    3383      446768 :     const auto nPos = osName.find_first_not_of('-');
    3384      446768 :     if (nPos == std::string::npos)
    3385          23 :         return nullptr;
    3386      893490 :     std::string osKey = osName.substr(nPos);
    3387             :     {
    3388      446745 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3389      446745 :         if (oIter != m_mapLongNameToArg.end())
    3390      416605 :             return oIter->second;
    3391             :     }
    3392             :     {
    3393       30140 :         const auto oIter = m_mapShortNameToArg.find(osKey);
    3394       30140 :         if (oIter != m_mapShortNameToArg.end())
    3395           6 :             return oIter->second;
    3396             :     }
    3397             : 
    3398       30134 :     if (!isConst && m_arbitraryLongNameArgsAllowed)
    3399             :     {
    3400          22 :         const auto nDotPos = osKey.find('.');
    3401             :         const std::string osKeyEnd =
    3402          22 :             nDotPos == std::string::npos ? osKey : osKey.substr(nDotPos + 1);
    3403          22 :         if (IsKnownOutputRelatedBooleanArgName(osKeyEnd))
    3404             :         {
    3405             :             m_arbitraryLongNameArgsValuesBool.emplace_back(
    3406           0 :                 std::make_unique<bool>());
    3407           0 :             AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3408           0 :                    m_arbitraryLongNameArgsValuesBool.back().get())
    3409           0 :                 .SetUserProvided();
    3410             :         }
    3411             :         else
    3412             :         {
    3413          44 :             const std::string osKeyInit = osKey;
    3414          22 :             if (osKey == "oo")
    3415           0 :                 osKey = GDAL_ARG_NAME_OPEN_OPTION;
    3416          22 :             else if (osKey == "co")
    3417           0 :                 osKey = GDAL_ARG_NAME_CREATION_OPTION;
    3418          22 :             else if (osKey == "of")
    3419           0 :                 osKey = GDAL_ARG_NAME_OUTPUT_FORMAT;
    3420          22 :             else if (osKey == "if")
    3421           0 :                 osKey = GDAL_ARG_NAME_INPUT_FORMAT;
    3422             :             m_arbitraryLongNameArgsValuesStr.emplace_back(
    3423          22 :                 std::make_unique<std::string>());
    3424             :             auto &arg =
    3425          44 :                 AddArg(osKey, 0, std::string("User-provided argument ") + osKey,
    3426          44 :                        m_arbitraryLongNameArgsValuesStr.back().get())
    3427          22 :                     .SetUserProvided();
    3428          22 :             if (osKey != osKeyInit)
    3429           0 :                 arg.AddAlias(osKeyInit);
    3430             :         }
    3431          22 :         const auto oIter = m_mapLongNameToArg.find(osKey);
    3432          22 :         CPLAssert(oIter != m_mapLongNameToArg.end());
    3433          22 :         return oIter->second;
    3434             :     }
    3435             : 
    3436       30112 :     if (suggestionAllowed)
    3437             :     {
    3438          12 :         const std::string bestCandidate = GetSuggestionForArgumentName(osName);
    3439           6 :         if (!bestCandidate.empty())
    3440             :         {
    3441           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    3442             :                      "Argument '%s' is unknown. Do you mean '%s'?",
    3443             :                      osName.c_str(), bestCandidate.c_str());
    3444             :         }
    3445             :     }
    3446             : 
    3447       30112 :     return nullptr;
    3448             : }
    3449             : 
    3450             : /************************************************************************/
    3451             : /*                     GDALAlgorithm::AddAliasFor()                     */
    3452             : /************************************************************************/
    3453             : 
    3454             : //! @cond Doxygen_Suppress
    3455       74578 : void GDALAlgorithm::AddAliasFor(GDALInConstructionAlgorithmArg *arg,
    3456             :                                 const std::string &alias)
    3457             : {
    3458       74578 :     if (cpl::contains(m_mapLongNameToArg, alias))
    3459             :     {
    3460           1 :         ReportError(CE_Failure, CPLE_AppDefined, "Name '%s' already declared.",
    3461             :                     alias.c_str());
    3462             :     }
    3463             :     else
    3464             :     {
    3465       74577 :         m_mapLongNameToArg[alias] = arg;
    3466             :     }
    3467       74578 : }
    3468             : 
    3469             : //! @endcond
    3470             : 
    3471             : /************************************************************************/
    3472             : /*                GDALAlgorithm::AddShortNameAliasFor()                 */
    3473             : /************************************************************************/
    3474             : 
    3475             : //! @cond Doxygen_Suppress
    3476          48 : void GDALAlgorithm::AddShortNameAliasFor(GDALInConstructionAlgorithmArg *arg,
    3477             :                                          char shortNameAlias)
    3478             : {
    3479          96 :     std::string alias;
    3480          48 :     alias += shortNameAlias;
    3481          48 :     if (cpl::contains(m_mapShortNameToArg, alias))
    3482             :     {
    3483           0 :         ReportError(CE_Failure, CPLE_AppDefined,
    3484             :                     "Short name '%s' already declared.", alias.c_str());
    3485             :     }
    3486             :     else
    3487             :     {
    3488          48 :         m_mapShortNameToArg[alias] = arg;
    3489             :     }
    3490          48 : }
    3491             : 
    3492             : //! @endcond
    3493             : 
    3494             : /************************************************************************/
    3495             : /*                    GDALAlgorithm::SetPositional()                    */
    3496             : /************************************************************************/
    3497             : 
    3498             : //! @cond Doxygen_Suppress
    3499       21123 : void GDALAlgorithm::SetPositional(GDALInConstructionAlgorithmArg *arg)
    3500             : {
    3501       21123 :     CPLAssert(std::find(m_positionalArgs.begin(), m_positionalArgs.end(),
    3502             :                         arg) == m_positionalArgs.end());
    3503       21123 :     m_positionalArgs.push_back(arg);
    3504       21123 : }
    3505             : 
    3506             : //! @endcond
    3507             : 
    3508             : /************************************************************************/
    3509             : /*                  GDALAlgorithm::HasSubAlgorithms()                   */
    3510             : /************************************************************************/
    3511             : 
    3512       12344 : bool GDALAlgorithm::HasSubAlgorithms() const
    3513             : {
    3514       12344 :     if (!m_subAlgRegistry.empty())
    3515        3151 :         return true;
    3516        9193 :     return !GDALGlobalAlgorithmRegistry::GetSingleton()
    3517       18386 :                 .GetDeclaredSubAlgorithmNames(m_callPath)
    3518        9193 :                 .empty();
    3519             : }
    3520             : 
    3521             : /************************************************************************/
    3522             : /*                GDALAlgorithm::GetSubAlgorithmNames()                 */
    3523             : /************************************************************************/
    3524             : 
    3525        1328 : std::vector<std::string> GDALAlgorithm::GetSubAlgorithmNames() const
    3526             : {
    3527        1328 :     std::vector<std::string> ret = m_subAlgRegistry.GetNames();
    3528        1328 :     const auto other = GDALGlobalAlgorithmRegistry::GetSingleton()
    3529        2656 :                            .GetDeclaredSubAlgorithmNames(m_callPath);
    3530        1328 :     ret.insert(ret.end(), other.begin(), other.end());
    3531        1328 :     if (!other.empty())
    3532         415 :         std::sort(ret.begin(), ret.end());
    3533        2656 :     return ret;
    3534             : }
    3535             : 
    3536             : /************************************************************************/
    3537             : /*                       GDALAlgorithm::AddArg()                        */
    3538             : /************************************************************************/
    3539             : 
    3540             : GDALInConstructionAlgorithmArg &
    3541      300327 : GDALAlgorithm::AddArg(std::unique_ptr<GDALInConstructionAlgorithmArg> arg)
    3542             : {
    3543      300327 :     auto argRaw = arg.get();
    3544      300327 :     const auto &longName = argRaw->GetName();
    3545      300327 :     if (!longName.empty())
    3546             :     {
    3547      300314 :         if (longName[0] == '-')
    3548             :         {
    3549           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3550             :                         "Long name '%s' should not start with '-'",
    3551             :                         longName.c_str());
    3552             :         }
    3553      300314 :         if (longName.find('=') != std::string::npos)
    3554             :         {
    3555           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3556             :                         "Long name '%s' should not contain a '=' character",
    3557             :                         longName.c_str());
    3558             :         }
    3559      300314 :         if (cpl::contains(m_mapLongNameToArg, longName))
    3560             :         {
    3561           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3562             :                         "Long name '%s' already declared", longName.c_str());
    3563             :         }
    3564      300314 :         m_mapLongNameToArg[longName] = argRaw;
    3565             :     }
    3566      300327 :     const auto &shortName = argRaw->GetShortName();
    3567      300327 :     if (!shortName.empty())
    3568             :     {
    3569      145844 :         if (shortName.size() != 1 ||
    3570       72922 :             !((shortName[0] >= 'a' && shortName[0] <= 'z') ||
    3571          64 :               (shortName[0] >= 'A' && shortName[0] <= 'Z') ||
    3572           2 :               (shortName[0] >= '0' && shortName[0] <= '9')))
    3573             :         {
    3574           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3575             :                         "Short name '%s' should be a single letter or digit",
    3576             :                         shortName.c_str());
    3577             :         }
    3578       72922 :         if (cpl::contains(m_mapShortNameToArg, shortName))
    3579             :         {
    3580           1 :             ReportError(CE_Failure, CPLE_AppDefined,
    3581             :                         "Short name '%s' already declared", shortName.c_str());
    3582             :         }
    3583       72922 :         m_mapShortNameToArg[shortName] = argRaw;
    3584             :     }
    3585      300327 :     m_args.emplace_back(std::move(arg));
    3586             :     return *(
    3587      300327 :         cpl::down_cast<GDALInConstructionAlgorithmArg *>(m_args.back().get()));
    3588             : }
    3589             : 
    3590             : GDALInConstructionAlgorithmArg &
    3591      134114 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3592             :                       const std::string &helpMessage, bool *pValue)
    3593             : {
    3594      134114 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3595             :         this,
    3596      268228 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_BOOLEAN),
    3597      268228 :         pValue));
    3598             : }
    3599             : 
    3600             : GDALInConstructionAlgorithmArg &
    3601       48212 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3602             :                       const std::string &helpMessage, std::string *pValue)
    3603             : {
    3604       48212 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3605             :         this,
    3606       96424 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_STRING),
    3607       96424 :         pValue));
    3608             : }
    3609             : 
    3610             : GDALInConstructionAlgorithmArg &
    3611       11725 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3612             :                       const std::string &helpMessage, int *pValue)
    3613             : {
    3614       11725 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3615             :         this,
    3616       23450 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_INTEGER),
    3617       23450 :         pValue));
    3618             : }
    3619             : 
    3620             : GDALInConstructionAlgorithmArg &
    3621       10048 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3622             :                       const std::string &helpMessage, double *pValue)
    3623             : {
    3624       10048 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3625             :         this,
    3626       20096 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage, GAAT_REAL),
    3627       20096 :         pValue));
    3628             : }
    3629             : 
    3630             : GDALInConstructionAlgorithmArg &
    3631       11427 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3632             :                       const std::string &helpMessage,
    3633             :                       GDALArgDatasetValue *pValue, GDALArgDatasetType type)
    3634             : {
    3635       22854 :     auto &arg = AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3636             :                            this,
    3637       22854 :                            GDALAlgorithmArgDecl(longName, chShortName,
    3638             :                                                 helpMessage, GAAT_DATASET),
    3639       11427 :                            pValue))
    3640       11427 :                     .SetDatasetType(type);
    3641       11427 :     pValue->SetOwnerArgument(&arg);
    3642       11427 :     return arg;
    3643             : }
    3644             : 
    3645             : GDALInConstructionAlgorithmArg &
    3646       64012 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3647             :                       const std::string &helpMessage,
    3648             :                       std::vector<std::string> *pValue)
    3649             : {
    3650       64012 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3651             :         this,
    3652      128024 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3653             :                              GAAT_STRING_LIST),
    3654      128024 :         pValue));
    3655             : }
    3656             : 
    3657             : GDALInConstructionAlgorithmArg &
    3658        2177 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3659             :                       const std::string &helpMessage, std::vector<int> *pValue)
    3660             : {
    3661        2177 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3662             :         this,
    3663        4354 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3664             :                              GAAT_INTEGER_LIST),
    3665        4354 :         pValue));
    3666             : }
    3667             : 
    3668             : GDALInConstructionAlgorithmArg &
    3669        5111 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3670             :                       const std::string &helpMessage,
    3671             :                       std::vector<double> *pValue)
    3672             : {
    3673        5111 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3674             :         this,
    3675       10222 :         GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3676             :                              GAAT_REAL_LIST),
    3677       10222 :         pValue));
    3678             : }
    3679             : 
    3680             : GDALInConstructionAlgorithmArg &
    3681       13501 : GDALAlgorithm::AddArg(const std::string &longName, char chShortName,
    3682             :                       const std::string &helpMessage,
    3683             :                       std::vector<GDALArgDatasetValue> *pValue,
    3684             :                       GDALArgDatasetType type)
    3685             : {
    3686       27002 :     return AddArg(std::make_unique<GDALInConstructionAlgorithmArg>(
    3687             :                       this,
    3688       27002 :                       GDALAlgorithmArgDecl(longName, chShortName, helpMessage,
    3689             :                                            GAAT_DATASET_LIST),
    3690       13501 :                       pValue))
    3691       27002 :         .SetDatasetType(type);
    3692             : }
    3693             : 
    3694             : /************************************************************************/
    3695             : /*                            MsgOrDefault()                            */
    3696             : /************************************************************************/
    3697             : 
    3698      100987 : inline const char *MsgOrDefault(const char *helpMessage,
    3699             :                                 const char *defaultMessage)
    3700             : {
    3701      100987 :     return helpMessage && helpMessage[0] ? helpMessage : defaultMessage;
    3702             : }
    3703             : 
    3704             : /************************************************************************/
    3705             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFilename()          */
    3706             : /************************************************************************/
    3707             : 
    3708             : /* static */
    3709       16422 : void GDALAlgorithm::SetAutoCompleteFunctionForFilename(
    3710             :     GDALInConstructionAlgorithmArg &arg, GDALArgDatasetType type)
    3711             : {
    3712             :     arg.SetAutoCompleteFunction(
    3713           7 :         [&arg,
    3714        2442 :          type](const std::string &currentValue) -> std::vector<std::string>
    3715             :         {
    3716          14 :             std::vector<std::string> oRet;
    3717             : 
    3718           7 :             if (arg.IsHidden())
    3719           0 :                 return oRet;
    3720             : 
    3721             :             {
    3722           7 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3723             :                 VSIStatBufL sStat;
    3724          10 :                 if (!currentValue.empty() && currentValue.back() != '/' &&
    3725           3 :                     VSIStatL(currentValue.c_str(), &sStat) == 0)
    3726             :                 {
    3727           0 :                     return oRet;
    3728             :                 }
    3729             :             }
    3730             : 
    3731           7 :             auto poDM = GetGDALDriverManager();
    3732          14 :             std::set<std::string> oExtensions;
    3733           7 :             if (type)
    3734             :             {
    3735        1362 :                 for (int i = 0; i < poDM->GetDriverCount(); ++i)
    3736             :                 {
    3737        1356 :                     auto poDriver = poDM->GetDriver(i);
    3738        3842 :                     if (((type & GDAL_OF_RASTER) != 0 &&
    3739        1130 :                          poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    3740         581 :                         ((type & GDAL_OF_VECTOR) != 0 &&
    3741        2848 :                          poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    3742         491 :                         ((type & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    3743           0 :                          poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    3744             :                     {
    3745             :                         const char *pszExtensions =
    3746         865 :                             poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    3747         865 :                         if (pszExtensions)
    3748             :                         {
    3749             :                             const CPLStringList aosExts(
    3750        1142 :                                 CSLTokenizeString2(pszExtensions, " ", 0));
    3751        1291 :                             for (const char *pszExt : cpl::Iterate(aosExts))
    3752         720 :                                 oExtensions.insert(CPLString(pszExt).tolower());
    3753             :                         }
    3754             :                     }
    3755             :                 }
    3756             :             }
    3757             : 
    3758          14 :             std::string osDir;
    3759          14 :             const CPLStringList aosVSIPrefixes(VSIGetFileSystemsPrefixes());
    3760          14 :             std::string osPrefix;
    3761           7 :             if (STARTS_WITH(currentValue.c_str(), "/vsi"))
    3762             :             {
    3763          79 :                 for (const char *pszPrefix : cpl::Iterate(aosVSIPrefixes))
    3764             :                 {
    3765          78 :                     if (STARTS_WITH(currentValue.c_str(), pszPrefix))
    3766             :                     {
    3767           2 :                         osPrefix = pszPrefix;
    3768           2 :                         break;
    3769             :                     }
    3770             :                 }
    3771           3 :                 if (osPrefix.empty())
    3772           1 :                     return aosVSIPrefixes;
    3773           2 :                 if (currentValue == osPrefix)
    3774           1 :                     osDir = osPrefix;
    3775             :             }
    3776           6 :             if (osDir.empty())
    3777             :             {
    3778           5 :                 osDir = CPLGetDirnameSafe(currentValue.c_str());
    3779           5 :                 if (!osPrefix.empty() && osDir.size() < osPrefix.size())
    3780           0 :                     osDir = std::move(osPrefix);
    3781             :             }
    3782             : 
    3783           6 :             auto psDir = VSIOpenDir(osDir.c_str(), 0, nullptr);
    3784          12 :             const std::string osSep = VSIGetDirectorySeparator(osDir.c_str());
    3785           6 :             if (currentValue.empty())
    3786           1 :                 osDir.clear();
    3787             :             const std::string currentFilename =
    3788          12 :                 CPLGetFilename(currentValue.c_str());
    3789           6 :             if (psDir)
    3790             :             {
    3791         442 :                 while (const VSIDIREntry *psEntry = VSIGetNextDirEntry(psDir))
    3792             :                 {
    3793         437 :                     if ((currentFilename.empty() ||
    3794         218 :                          STARTS_WITH(psEntry->pszName,
    3795         220 :                                      currentFilename.c_str())) &&
    3796         220 :                         strcmp(psEntry->pszName, ".") != 0 &&
    3797        1313 :                         strcmp(psEntry->pszName, "..") != 0 &&
    3798         220 :                         (oExtensions.empty() ||
    3799         219 :                          !strstr(psEntry->pszName, ".aux.xml")))
    3800             :                     {
    3801         870 :                         if (oExtensions.empty() ||
    3802         217 :                             cpl::contains(
    3803             :                                 oExtensions,
    3804         435 :                                 CPLString(CPLGetExtensionSafe(psEntry->pszName))
    3805         652 :                                     .tolower()) ||
    3806         186 :                             VSI_ISDIR(psEntry->nMode))
    3807             :                         {
    3808          72 :                             std::string osVal;
    3809          36 :                             if (osDir.empty() || osDir == ".")
    3810           4 :                                 osVal = psEntry->pszName;
    3811             :                             else
    3812          64 :                                 osVal = CPLFormFilenameSafe(
    3813          64 :                                     osDir.c_str(), psEntry->pszName, nullptr);
    3814          36 :                             if (VSI_ISDIR(psEntry->nMode))
    3815           4 :                                 osVal += osSep;
    3816          36 :                             oRet.push_back(std::move(osVal));
    3817             :                         }
    3818             :                     }
    3819         437 :                 }
    3820           5 :                 VSICloseDir(psDir);
    3821             :             }
    3822           6 :             return oRet;
    3823       16422 :         });
    3824       16422 : }
    3825             : 
    3826             : /************************************************************************/
    3827             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    3828             : /************************************************************************/
    3829             : 
    3830         594 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    3831             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    3832             :     bool positionalAndRequired, const char *helpMessage)
    3833             : {
    3834             :     auto &arg = AddArg(
    3835             :         GDAL_ARG_NAME_INPUT, 'i',
    3836             :         MsgOrDefault(helpMessage,
    3837             :                      CPLSPrintf("Input %s dataset",
    3838         594 :                                 GDALAlgorithmArgDatasetTypeName(type).c_str())),
    3839        1188 :         pValue, type);
    3840         594 :     if (positionalAndRequired)
    3841         587 :         arg.SetPositional().SetRequired();
    3842             : 
    3843         594 :     SetAutoCompleteFunctionForFilename(arg, type);
    3844             : 
    3845         594 :     AddValidationAction(
    3846         130 :         [pValue]()
    3847             :         {
    3848         129 :             if (pValue->GetName() == "-")
    3849           1 :                 pValue->Set("/vsistdin/");
    3850         129 :             return true;
    3851             :         });
    3852             : 
    3853         594 :     return arg;
    3854             : }
    3855             : 
    3856             : /************************************************************************/
    3857             : /*                 GDALAlgorithm::AddInputDatasetArg()                  */
    3858             : /************************************************************************/
    3859             : 
    3860       13032 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddInputDatasetArg(
    3861             :     std::vector<GDALArgDatasetValue> *pValue, GDALArgDatasetType type,
    3862             :     bool positionalAndRequired, const char *helpMessage)
    3863             : {
    3864             :     auto &arg =
    3865             :         AddArg(GDAL_ARG_NAME_INPUT, 'i',
    3866             :                MsgOrDefault(
    3867             :                    helpMessage,
    3868             :                    CPLSPrintf("Input %s datasets",
    3869       13032 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    3870       39096 :                pValue, type)
    3871       13032 :             .SetPackedValuesAllowed(false);
    3872       13032 :     if (positionalAndRequired)
    3873        1715 :         arg.SetPositional().SetRequired();
    3874             : 
    3875       13032 :     SetAutoCompleteFunctionForFilename(arg, type);
    3876             : 
    3877       13032 :     AddValidationAction(
    3878        5933 :         [pValue]()
    3879             :         {
    3880       11289 :             for (auto &val : *pValue)
    3881             :             {
    3882        5356 :                 if (val.GetName() == "-")
    3883           1 :                     val.Set("/vsistdin/");
    3884             :             }
    3885        5933 :             return true;
    3886             :         });
    3887       13032 :     return arg;
    3888             : }
    3889             : 
    3890             : /************************************************************************/
    3891             : /*                 GDALAlgorithm::AddOutputDatasetArg()                 */
    3892             : /************************************************************************/
    3893             : 
    3894        8095 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddOutputDatasetArg(
    3895             :     GDALArgDatasetValue *pValue, GDALArgDatasetType type,
    3896             :     bool positionalAndRequired, const char *helpMessage)
    3897             : {
    3898             :     auto &arg =
    3899             :         AddArg(GDAL_ARG_NAME_OUTPUT, 'o',
    3900             :                MsgOrDefault(
    3901             :                    helpMessage,
    3902             :                    CPLSPrintf("Output %s dataset",
    3903        8095 :                               GDALAlgorithmArgDatasetTypeName(type).c_str())),
    3904       24285 :                pValue, type)
    3905        8095 :             .SetIsInput(true)
    3906        8095 :             .SetIsOutput(true)
    3907        8095 :             .SetDatasetInputFlags(GADV_NAME)
    3908        8095 :             .SetDatasetOutputFlags(GADV_OBJECT);
    3909        8095 :     if (positionalAndRequired)
    3910        4251 :         arg.SetPositional().SetRequired();
    3911             : 
    3912        8095 :     AddValidationAction(
    3913       11243 :         [this, &arg, pValue]()
    3914             :         {
    3915        3465 :             if (pValue->GetName() == "-")
    3916           4 :                 pValue->Set("/vsistdout/");
    3917             : 
    3918        3465 :             auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    3919        3417 :             if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    3920        5519 :                 (!outputFormatArg->IsExplicitlySet() ||
    3921        8984 :                  outputFormatArg->Get<std::string>().empty()) &&
    3922        1315 :                 arg.IsExplicitlySet())
    3923             :             {
    3924             :                 const auto vrtCompatible =
    3925         987 :                     outputFormatArg->GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    3926         182 :                 if (vrtCompatible && !vrtCompatible->empty() &&
    3927        1169 :                     vrtCompatible->front() == "false" &&
    3928        1078 :                     EQUAL(
    3929             :                         CPLGetExtensionSafe(pValue->GetName().c_str()).c_str(),
    3930             :                         "VRT"))
    3931             :                 {
    3932           6 :                     ReportError(
    3933             :                         CE_Failure, CPLE_NotSupported,
    3934             :                         "VRT output is not supported.%s",
    3935           6 :                         outputFormatArg->GetDescription().find("GDALG") !=
    3936             :                                 std::string::npos
    3937             :                             ? " Consider using the GDALG driver instead (files "
    3938             :                               "with .gdalg.json extension)"
    3939             :                             : "");
    3940           6 :                     return false;
    3941             :                 }
    3942         981 :                 else if (pValue->GetName().size() > strlen(".gdalg.json") &&
    3943        1939 :                          EQUAL(pValue->GetName()
    3944             :                                    .substr(pValue->GetName().size() -
    3945             :                                            strlen(".gdalg.json"))
    3946             :                                    .c_str(),
    3947        2920 :                                ".gdalg.json") &&
    3948          27 :                          outputFormatArg->GetDescription().find("GDALG") ==
    3949             :                              std::string::npos)
    3950             :                 {
    3951           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    3952             :                                 "GDALG output is not supported");
    3953           0 :                     return false;
    3954             :                 }
    3955             :             }
    3956        3459 :             return true;
    3957             :         });
    3958             : 
    3959        8095 :     return arg;
    3960             : }
    3961             : 
    3962             : /************************************************************************/
    3963             : /*                   GDALAlgorithm::AddOverwriteArg()                   */
    3964             : /************************************************************************/
    3965             : 
    3966             : GDALInConstructionAlgorithmArg &
    3967        8011 : GDALAlgorithm::AddOverwriteArg(bool *pValue, const char *helpMessage)
    3968             : {
    3969             :     return AddArg(
    3970             :                GDAL_ARG_NAME_OVERWRITE, 0,
    3971             :                MsgOrDefault(
    3972             :                    helpMessage,
    3973             :                    _("Whether overwriting existing output dataset is allowed")),
    3974       16022 :                pValue)
    3975       16022 :         .SetDefault(false);
    3976             : }
    3977             : 
    3978             : /************************************************************************/
    3979             : /*                GDALAlgorithm::AddOverwriteLayerArg()                 */
    3980             : /************************************************************************/
    3981             : 
    3982             : GDALInConstructionAlgorithmArg &
    3983        3322 : GDALAlgorithm::AddOverwriteLayerArg(bool *pValue, const char *helpMessage)
    3984             : {
    3985        3322 :     AddValidationAction(
    3986        1456 :         [this]
    3987             :         {
    3988        1455 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    3989        1455 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    3990             :             {
    3991           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    3992             :                             "--update argument must exist for "
    3993             :                             "--overwrite-layer, even if hidden");
    3994           1 :                 return false;
    3995             :             }
    3996        1454 :             return true;
    3997             :         });
    3998             :     return AddArg(
    3999             :                GDAL_ARG_NAME_OVERWRITE_LAYER, 0,
    4000             :                MsgOrDefault(
    4001             :                    helpMessage,
    4002             :                    _("Whether overwriting existing output layer is allowed")),
    4003        6644 :                pValue)
    4004        3322 :         .SetDefault(false)
    4005             :         .AddAction(
    4006          19 :             [this]
    4007             :             {
    4008          19 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4009          19 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4010             :                 {
    4011          19 :                     updateArg->Set(true);
    4012             :                 }
    4013        6663 :             });
    4014             : }
    4015             : 
    4016             : /************************************************************************/
    4017             : /*                    GDALAlgorithm::AddUpdateArg()                     */
    4018             : /************************************************************************/
    4019             : 
    4020             : GDALInConstructionAlgorithmArg &
    4021        3873 : GDALAlgorithm::AddUpdateArg(bool *pValue, const char *helpMessage)
    4022             : {
    4023             :     return AddArg(GDAL_ARG_NAME_UPDATE, 0,
    4024             :                   MsgOrDefault(
    4025             :                       helpMessage,
    4026             :                       _("Whether to open existing dataset in update mode")),
    4027        7746 :                   pValue)
    4028        7746 :         .SetDefault(false);
    4029             : }
    4030             : 
    4031             : /************************************************************************/
    4032             : /*                  GDALAlgorithm::AddAppendLayerArg()                  */
    4033             : /************************************************************************/
    4034             : 
    4035             : GDALInConstructionAlgorithmArg &
    4036        3105 : GDALAlgorithm::AddAppendLayerArg(bool *pValue, const char *helpMessage)
    4037             : {
    4038        3105 :     AddValidationAction(
    4039        1412 :         [this]
    4040             :         {
    4041        1411 :             auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4042        1411 :             if (!(updateArg && updateArg->GetType() == GAAT_BOOLEAN))
    4043             :             {
    4044           1 :                 ReportError(CE_Failure, CPLE_AppDefined,
    4045             :                             "--update argument must exist for --append, even "
    4046             :                             "if hidden");
    4047           1 :                 return false;
    4048             :             }
    4049        1410 :             return true;
    4050             :         });
    4051             :     return AddArg(GDAL_ARG_NAME_APPEND, 0,
    4052             :                   MsgOrDefault(
    4053             :                       helpMessage,
    4054             :                       _("Whether appending to existing layer is allowed")),
    4055        6210 :                   pValue)
    4056        3105 :         .SetDefault(false)
    4057             :         .AddAction(
    4058          25 :             [this]
    4059             :             {
    4060          25 :                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4061          25 :                 if (updateArg && updateArg->GetType() == GAAT_BOOLEAN)
    4062             :                 {
    4063          25 :                     updateArg->Set(true);
    4064             :                 }
    4065        6235 :             });
    4066             : }
    4067             : 
    4068             : /************************************************************************/
    4069             : /*                GDALAlgorithm::AddOptionsSuggestions()                */
    4070             : /************************************************************************/
    4071             : 
    4072             : /* static */
    4073          29 : bool GDALAlgorithm::AddOptionsSuggestions(const char *pszXML, int datasetType,
    4074             :                                           const std::string &currentValue,
    4075             :                                           std::vector<std::string> &oRet)
    4076             : {
    4077          29 :     if (!pszXML)
    4078           0 :         return false;
    4079          58 :     CPLXMLTreeCloser poTree(CPLParseXMLString(pszXML));
    4080          29 :     if (!poTree)
    4081           0 :         return false;
    4082             : 
    4083          58 :     std::string typedOptionName = currentValue;
    4084          29 :     const auto posEqual = typedOptionName.find('=');
    4085          58 :     std::string typedValue;
    4086          29 :     if (posEqual != 0 && posEqual != std::string::npos)
    4087             :     {
    4088           2 :         typedValue = currentValue.substr(posEqual + 1);
    4089           2 :         typedOptionName.resize(posEqual);
    4090             :     }
    4091             : 
    4092         405 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4093         376 :          psChild = psChild->psNext)
    4094             :     {
    4095         389 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4096         402 :         if (pszName && typedOptionName == pszName &&
    4097          13 :             (strcmp(psChild->pszValue, "Option") == 0 ||
    4098           2 :              strcmp(psChild->pszValue, "Argument") == 0))
    4099             :         {
    4100          13 :             const char *pszType = CPLGetXMLValue(psChild, "type", "");
    4101          13 :             const char *pszMin = CPLGetXMLValue(psChild, "min", nullptr);
    4102          13 :             const char *pszMax = CPLGetXMLValue(psChild, "max", nullptr);
    4103          13 :             if (EQUAL(pszType, "string-select"))
    4104             :             {
    4105          90 :                 for (const CPLXMLNode *psChild2 = psChild->psChild; psChild2;
    4106          85 :                      psChild2 = psChild2->psNext)
    4107             :                 {
    4108          85 :                     if (EQUAL(psChild2->pszValue, "Value"))
    4109             :                     {
    4110          75 :                         oRet.push_back(CPLGetXMLValue(psChild2, "", ""));
    4111             :                     }
    4112             :                 }
    4113             :             }
    4114           8 :             else if (EQUAL(pszType, "boolean"))
    4115             :             {
    4116           3 :                 if (typedValue == "YES" || typedValue == "NO")
    4117             :                 {
    4118           1 :                     oRet.push_back(currentValue);
    4119           1 :                     return true;
    4120             :                 }
    4121           2 :                 oRet.push_back("NO");
    4122           2 :                 oRet.push_back("YES");
    4123             :             }
    4124           5 :             else if (EQUAL(pszType, "int"))
    4125             :             {
    4126           5 :                 if (pszMin && pszMax && atoi(pszMax) - atoi(pszMin) > 0 &&
    4127           2 :                     atoi(pszMax) - atoi(pszMin) < 25)
    4128             :                 {
    4129           1 :                     const int nMax = atoi(pszMax);
    4130          13 :                     for (int i = atoi(pszMin); i <= nMax; ++i)
    4131          12 :                         oRet.push_back(std::to_string(i));
    4132             :                 }
    4133             :             }
    4134             : 
    4135          12 :             if (oRet.empty())
    4136             :             {
    4137           4 :                 if (pszMin && pszMax)
    4138             :                 {
    4139           1 :                     oRet.push_back(std::string("##"));
    4140           2 :                     oRet.push_back(std::string("validity range: [")
    4141           1 :                                        .append(pszMin)
    4142           1 :                                        .append(",")
    4143           1 :                                        .append(pszMax)
    4144           1 :                                        .append("]"));
    4145             :                 }
    4146           3 :                 else if (pszMin)
    4147             :                 {
    4148           1 :                     oRet.push_back(std::string("##"));
    4149           1 :                     oRet.push_back(
    4150           1 :                         std::string("validity range: >= ").append(pszMin));
    4151             :                 }
    4152           2 :                 else if (pszMax)
    4153             :                 {
    4154           1 :                     oRet.push_back(std::string("##"));
    4155           1 :                     oRet.push_back(
    4156           1 :                         std::string("validity range: <= ").append(pszMax));
    4157             :                 }
    4158           1 :                 else if (const char *pszDescription =
    4159           1 :                              CPLGetXMLValue(psChild, "description", nullptr))
    4160             :                 {
    4161           1 :                     oRet.push_back(std::string("##"));
    4162           2 :                     oRet.push_back(std::string("type: ")
    4163           1 :                                        .append(pszType)
    4164           1 :                                        .append(", description: ")
    4165           1 :                                        .append(pszDescription));
    4166             :                 }
    4167             :             }
    4168             : 
    4169          12 :             return true;
    4170             :         }
    4171             :     }
    4172             : 
    4173         319 :     for (const CPLXMLNode *psChild = poTree.get()->psChild; psChild;
    4174         303 :          psChild = psChild->psNext)
    4175             :     {
    4176         303 :         const char *pszName = CPLGetXMLValue(psChild, "name", nullptr);
    4177         303 :         if (pszName && (strcmp(psChild->pszValue, "Option") == 0 ||
    4178           5 :                         strcmp(psChild->pszValue, "Argument") == 0))
    4179             :         {
    4180         300 :             const char *pszScope = CPLGetXMLValue(psChild, "scope", nullptr);
    4181         300 :             if (!pszScope ||
    4182          40 :                 (EQUAL(pszScope, "raster") &&
    4183          40 :                  (datasetType & GDAL_OF_RASTER) != 0) ||
    4184          20 :                 (EQUAL(pszScope, "vector") &&
    4185           0 :                  (datasetType & GDAL_OF_VECTOR) != 0))
    4186             :             {
    4187         280 :                 oRet.push_back(std::string(pszName).append("="));
    4188             :             }
    4189             :         }
    4190             :     }
    4191             : 
    4192          16 :     return false;
    4193             : }
    4194             : 
    4195             : /************************************************************************/
    4196             : /*             GDALAlgorithm::OpenOptionCompleteFunction()              */
    4197             : /************************************************************************/
    4198             : 
    4199             : //! @cond Doxygen_Suppress
    4200             : std::vector<std::string>
    4201           2 : GDALAlgorithm::OpenOptionCompleteFunction(const std::string &currentValue) const
    4202             : {
    4203           2 :     std::vector<std::string> oRet;
    4204             : 
    4205           2 :     int datasetType = GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    4206           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4207           4 :     if (inputArg && (inputArg->GetType() == GAAT_DATASET ||
    4208           2 :                      inputArg->GetType() == GAAT_DATASET_LIST))
    4209             :     {
    4210           2 :         datasetType = inputArg->GetDatasetType();
    4211             :     }
    4212             : 
    4213           2 :     auto inputFormat = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4214           4 :     if (inputFormat && inputFormat->GetType() == GAAT_STRING_LIST &&
    4215           2 :         inputFormat->IsExplicitlySet())
    4216             :     {
    4217             :         const auto &aosAllowedDrivers =
    4218           1 :             inputFormat->Get<std::vector<std::string>>();
    4219           1 :         if (aosAllowedDrivers.size() == 1)
    4220             :         {
    4221           2 :             auto poDriver = GetGDALDriverManager()->GetDriverByName(
    4222           1 :                 aosAllowedDrivers[0].c_str());
    4223           1 :             if (poDriver)
    4224             :             {
    4225           1 :                 AddOptionsSuggestions(
    4226           1 :                     poDriver->GetMetadataItem(GDAL_DMD_OPENOPTIONLIST),
    4227             :                     datasetType, currentValue, oRet);
    4228             :             }
    4229           1 :             return oRet;
    4230             :         }
    4231             :     }
    4232             : 
    4233           1 :     const auto AddSuggestions = [datasetType, &currentValue,
    4234         369 :                                  &oRet](const GDALArgDatasetValue &datasetValue)
    4235             :     {
    4236           1 :         auto poDM = GetGDALDriverManager();
    4237             : 
    4238           1 :         const auto &osDSName = datasetValue.GetName();
    4239           1 :         const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    4240           1 :         if (!osExt.empty())
    4241             :         {
    4242           1 :             std::set<std::string> oVisitedExtensions;
    4243         227 :             for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4244             :             {
    4245         226 :                 auto poDriver = poDM->GetDriver(i);
    4246         678 :                 if (((datasetType & GDAL_OF_RASTER) != 0 &&
    4247         226 :                      poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    4248          71 :                     ((datasetType & GDAL_OF_VECTOR) != 0 &&
    4249         452 :                      poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    4250          71 :                     ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    4251           0 :                      poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER)))
    4252             :                 {
    4253             :                     const char *pszExtensions =
    4254         155 :                         poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    4255         155 :                     if (pszExtensions)
    4256             :                     {
    4257             :                         const CPLStringList aosExts(
    4258         102 :                             CSLTokenizeString2(pszExtensions, " ", 0));
    4259         225 :                         for (const char *pszExt : cpl::Iterate(aosExts))
    4260             :                         {
    4261         127 :                             if (EQUAL(pszExt, osExt.c_str()) &&
    4262           3 :                                 !cpl::contains(oVisitedExtensions, pszExt))
    4263             :                             {
    4264           1 :                                 oVisitedExtensions.insert(pszExt);
    4265           1 :                                 if (AddOptionsSuggestions(
    4266             :                                         poDriver->GetMetadataItem(
    4267           1 :                                             GDAL_DMD_OPENOPTIONLIST),
    4268             :                                         datasetType, currentValue, oRet))
    4269             :                                 {
    4270           0 :                                     return;
    4271             :                                 }
    4272           1 :                                 break;
    4273             :                             }
    4274             :                         }
    4275             :                     }
    4276             :                 }
    4277             :             }
    4278             :         }
    4279           1 :     };
    4280             : 
    4281           1 :     if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4282             :     {
    4283           0 :         auto &datasetValue = inputArg->Get<GDALArgDatasetValue>();
    4284           0 :         AddSuggestions(datasetValue);
    4285             :     }
    4286           1 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4287             :     {
    4288           1 :         auto &datasetValues = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4289           1 :         if (datasetValues.size() == 1)
    4290           1 :             AddSuggestions(datasetValues[0]);
    4291             :     }
    4292             : 
    4293           1 :     return oRet;
    4294             : }
    4295             : 
    4296             : //! @endcond
    4297             : 
    4298             : /************************************************************************/
    4299             : /*                  GDALAlgorithm::AddOpenOptionsArg()                  */
    4300             : /************************************************************************/
    4301             : 
    4302             : GDALInConstructionAlgorithmArg &
    4303        8905 : GDALAlgorithm::AddOpenOptionsArg(std::vector<std::string> *pValue,
    4304             :                                  const char *helpMessage)
    4305             : {
    4306             :     auto &arg = AddArg(GDAL_ARG_NAME_OPEN_OPTION, 0,
    4307       17810 :                        MsgOrDefault(helpMessage, _("Open options")), pValue)
    4308       17810 :                     .AddAlias("oo")
    4309       17810 :                     .SetMetaVar("<KEY>=<VALUE>")
    4310        8905 :                     .SetPackedValuesAllowed(false)
    4311        8905 :                     .SetCategory(GAAC_ADVANCED);
    4312             : 
    4313          21 :     arg.AddValidationAction([this, &arg]()
    4314        8926 :                             { return ParseAndValidateKeyValue(arg); });
    4315             : 
    4316             :     arg.SetAutoCompleteFunction(
    4317           2 :         [this](const std::string &currentValue)
    4318        8907 :         { return OpenOptionCompleteFunction(currentValue); });
    4319             : 
    4320        8905 :     return arg;
    4321             : }
    4322             : 
    4323             : /************************************************************************/
    4324             : /*               GDALAlgorithm::AddOutputOpenOptionsArg()               */
    4325             : /************************************************************************/
    4326             : 
    4327             : GDALInConstructionAlgorithmArg &
    4328        3093 : GDALAlgorithm::AddOutputOpenOptionsArg(std::vector<std::string> *pValue,
    4329             :                                        const char *helpMessage)
    4330             : {
    4331             :     auto &arg =
    4332             :         AddArg(GDAL_ARG_NAME_OUTPUT_OPEN_OPTION, 0,
    4333        6186 :                MsgOrDefault(helpMessage, _("Output open options")), pValue)
    4334        6186 :             .AddAlias("output-oo")
    4335        6186 :             .SetMetaVar("<KEY>=<VALUE>")
    4336        3093 :             .SetPackedValuesAllowed(false)
    4337        3093 :             .SetCategory(GAAC_ADVANCED);
    4338             : 
    4339           0 :     arg.AddValidationAction([this, &arg]()
    4340        3093 :                             { return ParseAndValidateKeyValue(arg); });
    4341             : 
    4342             :     arg.SetAutoCompleteFunction(
    4343           0 :         [this](const std::string &currentValue)
    4344        3093 :         { return OpenOptionCompleteFunction(currentValue); });
    4345             : 
    4346        3093 :     return arg;
    4347             : }
    4348             : 
    4349             : /************************************************************************/
    4350             : /*                           ValidateFormat()                           */
    4351             : /************************************************************************/
    4352             : 
    4353        4480 : bool GDALAlgorithm::ValidateFormat(const GDALAlgorithmArg &arg,
    4354             :                                    bool bStreamAllowed,
    4355             :                                    bool bGDALGAllowed) const
    4356             : {
    4357        4480 :     if (arg.GetChoices().empty())
    4358             :     {
    4359             :         const auto Validate =
    4360       19163 :             [this, &arg, bStreamAllowed, bGDALGAllowed](const std::string &val)
    4361             :         {
    4362        4375 :             if (const auto extraFormats =
    4363        4375 :                     arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4364             :             {
    4365          60 :                 for (const auto &extraFormat : *extraFormats)
    4366             :                 {
    4367          48 :                     if (EQUAL(val.c_str(), extraFormat.c_str()))
    4368          14 :                         return true;
    4369             :                 }
    4370             :             }
    4371             : 
    4372        4361 :             if (bStreamAllowed && EQUAL(val.c_str(), "stream"))
    4373        1749 :                 return true;
    4374             : 
    4375        2618 :             if (EQUAL(val.c_str(), "GDALG") &&
    4376           6 :                 arg.GetName() == GDAL_ARG_NAME_OUTPUT_FORMAT)
    4377             :             {
    4378           2 :                 if (bGDALGAllowed)
    4379             :                 {
    4380           2 :                     return true;
    4381             :                 }
    4382             :                 else
    4383             :                 {
    4384           0 :                     ReportError(CE_Failure, CPLE_NotSupported,
    4385             :                                 "GDALG output is not supported.");
    4386           0 :                     return false;
    4387             :                 }
    4388             :             }
    4389             : 
    4390             :             const auto vrtCompatible =
    4391        2610 :                 arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4392         440 :             if (vrtCompatible && !vrtCompatible->empty() &&
    4393        3050 :                 vrtCompatible->front() == "false" && EQUAL(val.c_str(), "VRT"))
    4394             :             {
    4395           7 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4396             :                             "VRT output is not supported.%s",
    4397             :                             bGDALGAllowed
    4398             :                                 ? " Consider using the GDALG driver instead "
    4399             :                                   "(files with .gdalg.json extension)."
    4400             :                                 : "");
    4401           7 :                 return false;
    4402             :             }
    4403             : 
    4404             :             const auto allowedFormats =
    4405        2603 :                 arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4406        2624 :             if (allowedFormats && !allowedFormats->empty() &&
    4407           0 :                 std::find(allowedFormats->begin(), allowedFormats->end(),
    4408        2624 :                           val) != allowedFormats->end())
    4409             :             {
    4410           9 :                 return true;
    4411             :             }
    4412             : 
    4413             :             const auto excludedFormats =
    4414        2594 :                 arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4415        2606 :             if (excludedFormats && !excludedFormats->empty() &&
    4416           0 :                 std::find(excludedFormats->begin(), excludedFormats->end(),
    4417        2606 :                           val) != excludedFormats->end())
    4418             :             {
    4419           0 :                 ReportError(CE_Failure, CPLE_NotSupported,
    4420             :                             "%s output is not supported.", val.c_str());
    4421           0 :                 return false;
    4422             :             }
    4423             : 
    4424        2594 :             auto hDriver = GDALGetDriverByName(val.c_str());
    4425        2594 :             if (!hDriver)
    4426             :             {
    4427             :                 auto poMissingDriver =
    4428           4 :                     GetGDALDriverManager()->GetHiddenDriverByName(val.c_str());
    4429           4 :                 if (poMissingDriver)
    4430             :                 {
    4431             :                     const std::string msg =
    4432           0 :                         GDALGetMessageAboutMissingPluginDriver(poMissingDriver);
    4433           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4434             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4435             :                                 "not found but is known. However plugin %s",
    4436           0 :                                 arg.GetName().c_str(), val.c_str(),
    4437             :                                 msg.c_str());
    4438             :                 }
    4439             :                 else
    4440             :                 {
    4441           8 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4442             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4443             :                                 "does not exist.",
    4444           4 :                                 arg.GetName().c_str(), val.c_str());
    4445             :                 }
    4446           4 :                 return false;
    4447             :             }
    4448             : 
    4449        2590 :             const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4450        2590 :             if (caps)
    4451             :             {
    4452        7776 :                 for (const std::string &cap : *caps)
    4453             :                 {
    4454             :                     const char *pszVal =
    4455        5211 :                         GDALGetMetadataItem(hDriver, cap.c_str(), nullptr);
    4456        5211 :                     if (!(pszVal && pszVal[0]))
    4457             :                     {
    4458        1529 :                         if (cap == GDAL_DCAP_CREATECOPY &&
    4459           0 :                             std::find(caps->begin(), caps->end(),
    4460         763 :                                       GDAL_DCAP_RASTER) != caps->end() &&
    4461         763 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_RASTER,
    4462        1529 :                                                 nullptr) &&
    4463         763 :                             GDALGetMetadataItem(hDriver, GDAL_DCAP_CREATE,
    4464             :                                                 nullptr))
    4465             :                         {
    4466             :                             // if it supports Create, it supports CreateCopy
    4467             :                         }
    4468           3 :                         else if (cap == GDAL_DMD_EXTENSIONS)
    4469             :                         {
    4470           2 :                             ReportError(
    4471             :                                 CE_Failure, CPLE_AppDefined,
    4472             :                                 "Invalid value for argument '%s'. Driver '%s' "
    4473             :                                 "does "
    4474             :                                 "not advertise any file format extension.",
    4475           1 :                                 arg.GetName().c_str(), val.c_str());
    4476           3 :                             return false;
    4477             :                         }
    4478             :                         else
    4479             :                         {
    4480           2 :                             if (cap == GDAL_DCAP_CREATE)
    4481             :                             {
    4482           1 :                                 auto updateArg = GetArg(GDAL_ARG_NAME_UPDATE);
    4483           1 :                                 if (updateArg &&
    4484           2 :                                     updateArg->GetType() == GAAT_BOOLEAN &&
    4485           1 :                                     updateArg->IsExplicitlySet())
    4486             :                                 {
    4487           0 :                                     continue;
    4488             :                                 }
    4489             : 
    4490           2 :                                 ReportError(
    4491             :                                     CE_Failure, CPLE_AppDefined,
    4492             :                                     "Invalid value for argument '%s'. "
    4493             :                                     "Driver '%s' does not have write support.",
    4494           1 :                                     arg.GetName().c_str(), val.c_str());
    4495           1 :                                 return false;
    4496             :                             }
    4497             :                             else
    4498             :                             {
    4499           2 :                                 ReportError(
    4500             :                                     CE_Failure, CPLE_AppDefined,
    4501             :                                     "Invalid value for argument '%s'. Driver "
    4502             :                                     "'%s' "
    4503             :                                     "does "
    4504             :                                     "not expose the required '%s' capability.",
    4505           1 :                                     arg.GetName().c_str(), val.c_str(),
    4506             :                                     cap.c_str());
    4507           1 :                                 return false;
    4508             :                             }
    4509             :                         }
    4510             :                     }
    4511             :                 }
    4512             :             }
    4513        2587 :             return true;
    4514        4378 :         };
    4515             : 
    4516        4378 :         if (arg.GetType() == GAAT_STRING)
    4517             :         {
    4518        4368 :             return Validate(arg.Get<std::string>());
    4519             :         }
    4520          12 :         else if (arg.GetType() == GAAT_STRING_LIST)
    4521             :         {
    4522          19 :             for (const auto &val : arg.Get<std::vector<std::string>>())
    4523             :             {
    4524           9 :                 if (!Validate(val))
    4525           2 :                     return false;
    4526             :             }
    4527             :         }
    4528             :     }
    4529             : 
    4530         112 :     return true;
    4531             : }
    4532             : 
    4533             : /************************************************************************/
    4534             : /*                     FormatAutoCompleteFunction()                     */
    4535             : /************************************************************************/
    4536             : 
    4537             : /* static */
    4538           7 : std::vector<std::string> GDALAlgorithm::FormatAutoCompleteFunction(
    4539             :     const GDALAlgorithmArg &arg, bool /* bStreamAllowed */, bool bGDALGAllowed)
    4540             : {
    4541           7 :     std::vector<std::string> res;
    4542           7 :     auto poDM = GetGDALDriverManager();
    4543           7 :     const auto vrtCompatible = arg.GetMetadataItem(GAAMDI_VRT_COMPATIBLE);
    4544           7 :     const auto allowedFormats = arg.GetMetadataItem(GAAMDI_ALLOWED_FORMATS);
    4545           7 :     const auto excludedFormats = arg.GetMetadataItem(GAAMDI_EXCLUDED_FORMATS);
    4546           7 :     const auto caps = arg.GetMetadataItem(GAAMDI_REQUIRED_CAPABILITIES);
    4547           7 :     if (auto extraFormats = arg.GetMetadataItem(GAAMDI_EXTRA_FORMATS))
    4548           0 :         res = std::move(*extraFormats);
    4549        1588 :     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    4550             :     {
    4551        1581 :         auto poDriver = poDM->GetDriver(i);
    4552             : 
    4553           0 :         if (vrtCompatible && !vrtCompatible->empty() &&
    4554        1581 :             vrtCompatible->front() == "false" &&
    4555           0 :             EQUAL(poDriver->GetDescription(), "VRT"))
    4556             :         {
    4557             :             // do nothing
    4558             :         }
    4559        1581 :         else if (allowedFormats && !allowedFormats->empty() &&
    4560           0 :                  std::find(allowedFormats->begin(), allowedFormats->end(),
    4561        1581 :                            poDriver->GetDescription()) != allowedFormats->end())
    4562             :         {
    4563           0 :             res.push_back(poDriver->GetDescription());
    4564             :         }
    4565        1581 :         else if (excludedFormats && !excludedFormats->empty() &&
    4566           0 :                  std::find(excludedFormats->begin(), excludedFormats->end(),
    4567           0 :                            poDriver->GetDescription()) !=
    4568        1581 :                      excludedFormats->end())
    4569             :         {
    4570           0 :             continue;
    4571             :         }
    4572        1581 :         else if (caps)
    4573             :         {
    4574        1581 :             bool ok = true;
    4575        3127 :             for (const std::string &cap : *caps)
    4576             :             {
    4577        2355 :                 if (cap == GDAL_ALG_DCAP_RASTER_OR_MULTIDIM_RASTER)
    4578             :                 {
    4579           0 :                     if (!poDriver->GetMetadataItem(GDAL_DCAP_RASTER) &&
    4580           0 :                         !poDriver->GetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER))
    4581             :                     {
    4582           0 :                         ok = false;
    4583           0 :                         break;
    4584             :                     }
    4585             :                 }
    4586        2355 :                 else if (const char *pszVal =
    4587        2355 :                              poDriver->GetMetadataItem(cap.c_str());
    4588        1474 :                          pszVal && pszVal[0])
    4589             :                 {
    4590             :                 }
    4591        1269 :                 else if (cap == GDAL_DCAP_CREATECOPY &&
    4592           0 :                          (std::find(caps->begin(), caps->end(),
    4593         388 :                                     GDAL_DCAP_RASTER) != caps->end() &&
    4594        1657 :                           poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) &&
    4595         388 :                          poDriver->GetMetadataItem(GDAL_DCAP_CREATE))
    4596             :                 {
    4597             :                     // if it supports Create, it supports CreateCopy
    4598             :                 }
    4599             :                 else
    4600             :                 {
    4601         809 :                     ok = false;
    4602         809 :                     break;
    4603             :                 }
    4604             :             }
    4605        1581 :             if (ok)
    4606             :             {
    4607         772 :                 res.push_back(poDriver->GetDescription());
    4608             :             }
    4609             :         }
    4610             :     }
    4611           7 :     if (bGDALGAllowed)
    4612           4 :         res.push_back("GDALG");
    4613           7 :     return res;
    4614             : }
    4615             : 
    4616             : /************************************************************************/
    4617             : /*                 GDALAlgorithm::AddInputFormatsArg()                  */
    4618             : /************************************************************************/
    4619             : 
    4620             : GDALInConstructionAlgorithmArg &
    4621        8674 : GDALAlgorithm::AddInputFormatsArg(std::vector<std::string> *pValue,
    4622             :                                   const char *helpMessage)
    4623             : {
    4624             :     auto &arg = AddArg(GDAL_ARG_NAME_INPUT_FORMAT, 0,
    4625       17348 :                        MsgOrDefault(helpMessage, _("Input formats")), pValue)
    4626       17348 :                     .AddAlias("if")
    4627        8674 :                     .SetCategory(GAAC_ADVANCED);
    4628          12 :     arg.AddValidationAction([this, &arg]()
    4629        8686 :                             { return ValidateFormat(arg, false, false); });
    4630             :     arg.SetAutoCompleteFunction(
    4631           1 :         [&arg](const std::string &)
    4632        8675 :         { return FormatAutoCompleteFunction(arg, false, false); });
    4633        8674 :     return arg;
    4634             : }
    4635             : 
    4636             : /************************************************************************/
    4637             : /*                 GDALAlgorithm::AddOutputFormatArg()                  */
    4638             : /************************************************************************/
    4639             : 
    4640             : GDALInConstructionAlgorithmArg &
    4641        9078 : GDALAlgorithm::AddOutputFormatArg(std::string *pValue, bool bStreamAllowed,
    4642             :                                   bool bGDALGAllowed, const char *helpMessage)
    4643             : {
    4644             :     auto &arg = AddArg(GDAL_ARG_NAME_OUTPUT_FORMAT, 'f',
    4645             :                        MsgOrDefault(helpMessage,
    4646             :                                     bGDALGAllowed
    4647             :                                         ? _("Output format (\"GDALG\" allowed)")
    4648             :                                         : _("Output format")),
    4649       18156 :                        pValue)
    4650       18156 :                     .AddAlias("of")
    4651        9078 :                     .AddAlias("format");
    4652             :     arg.AddValidationAction(
    4653        4464 :         [this, &arg, bStreamAllowed, bGDALGAllowed]()
    4654       13542 :         { return ValidateFormat(arg, bStreamAllowed, bGDALGAllowed); });
    4655             :     arg.SetAutoCompleteFunction(
    4656           4 :         [&arg, bStreamAllowed, bGDALGAllowed](const std::string &)
    4657             :         {
    4658             :             return FormatAutoCompleteFunction(arg, bStreamAllowed,
    4659           4 :                                               bGDALGAllowed);
    4660        9078 :         });
    4661        9078 :     return arg;
    4662             : }
    4663             : 
    4664             : /************************************************************************/
    4665             : /*                GDALAlgorithm::AddOutputDataTypeArg()                 */
    4666             : /************************************************************************/
    4667             : GDALInConstructionAlgorithmArg &
    4668        1721 : GDALAlgorithm::AddOutputDataTypeArg(std::string *pValue,
    4669             :                                     const char *helpMessage)
    4670             : {
    4671             :     auto &arg =
    4672             :         AddArg(GDAL_ARG_NAME_OUTPUT_DATA_TYPE, 0,
    4673        3442 :                MsgOrDefault(helpMessage, _("Output data type")), pValue)
    4674        3442 :             .AddAlias("ot")
    4675        3442 :             .AddAlias("datatype")
    4676        5163 :             .AddMetadataItem("type", {"GDALDataType"})
    4677             :             .SetChoices("UInt8", "Int8", "UInt16", "Int16", "UInt32", "Int32",
    4678             :                         "UInt64", "Int64", "CInt16", "CInt32", "Float16",
    4679        1721 :                         "Float32", "Float64", "CFloat32", "CFloat64")
    4680        1721 :             .SetHiddenChoices("Byte");
    4681        1721 :     return arg;
    4682             : }
    4683             : 
    4684             : /************************************************************************/
    4685             : /*                    GDALAlgorithm::AddNodataArg()                     */
    4686             : /************************************************************************/
    4687             : 
    4688             : GDALInConstructionAlgorithmArg &
    4689         608 : GDALAlgorithm::AddNodataArg(std::string *pValue, bool noneAllowed,
    4690             :                             const std::string &optionName,
    4691             :                             const char *helpMessage)
    4692             : {
    4693             :     auto &arg = AddArg(
    4694             :         optionName, 0,
    4695             :         MsgOrDefault(helpMessage,
    4696             :                      noneAllowed
    4697             :                          ? _("Assign a specified nodata value to output bands "
    4698             :                              "('none', numeric value, 'nan', 'inf', '-inf')")
    4699             :                          : _("Assign a specified nodata value to output bands "
    4700             :                              "(numeric value, 'nan', 'inf', '-inf')")),
    4701         608 :         pValue);
    4702             :     arg.AddValidationAction(
    4703         356 :         [this, pValue, noneAllowed, optionName]()
    4704             :         {
    4705          77 :             if (!(noneAllowed && EQUAL(pValue->c_str(), "none")))
    4706             :             {
    4707          67 :                 char *endptr = nullptr;
    4708          67 :                 CPLStrtod(pValue->c_str(), &endptr);
    4709          67 :                 if (endptr != pValue->c_str() + pValue->size())
    4710             :                 {
    4711           1 :                     ReportError(CE_Failure, CPLE_IllegalArg,
    4712             :                                 "Value of '%s' should be %sa "
    4713             :                                 "numeric value, 'nan', 'inf' or '-inf'",
    4714             :                                 optionName.c_str(),
    4715             :                                 noneAllowed ? "'none', " : "");
    4716           1 :                     return false;
    4717             :                 }
    4718             :             }
    4719          76 :             return true;
    4720         608 :         });
    4721         608 :     return arg;
    4722             : }
    4723             : 
    4724             : /************************************************************************/
    4725             : /*                 GDALAlgorithm::AddOutputStringArg()                  */
    4726             : /************************************************************************/
    4727             : 
    4728             : GDALInConstructionAlgorithmArg &
    4729        5738 : GDALAlgorithm::AddOutputStringArg(std::string *pValue, const char *helpMessage)
    4730             : {
    4731             :     return AddArg(
    4732             :                GDAL_ARG_NAME_OUTPUT_STRING, 0,
    4733             :                MsgOrDefault(helpMessage,
    4734             :                             _("Output string, in which the result is placed")),
    4735       11476 :                pValue)
    4736        5738 :         .SetHiddenForCLI()
    4737        5738 :         .SetIsInput(false)
    4738       11476 :         .SetIsOutput(true);
    4739             : }
    4740             : 
    4741             : /************************************************************************/
    4742             : /*                    GDALAlgorithm::AddStdoutArg()                     */
    4743             : /************************************************************************/
    4744             : 
    4745             : GDALInConstructionAlgorithmArg &
    4746        1455 : GDALAlgorithm::AddStdoutArg(bool *pValue, const char *helpMessage)
    4747             : {
    4748             :     return AddArg(GDAL_ARG_NAME_STDOUT, 0,
    4749             :                   MsgOrDefault(helpMessage,
    4750             :                                _("Directly output on stdout. If enabled, "
    4751             :                                  "output-string will be empty")),
    4752        2910 :                   pValue)
    4753        2910 :         .SetHidden();
    4754             : }
    4755             : 
    4756             : /************************************************************************/
    4757             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    4758             : /************************************************************************/
    4759             : 
    4760             : GDALInConstructionAlgorithmArg &
    4761         206 : GDALAlgorithm::AddLayerNameArg(std::string *pValue, const char *helpMessage)
    4762             : {
    4763             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    4764         206 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    4765             : }
    4766             : 
    4767             : /************************************************************************/
    4768             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4769             : /************************************************************************/
    4770             : 
    4771             : GDALInConstructionAlgorithmArg &
    4772          50 : GDALAlgorithm::AddArrayNameArg(std::string *pValue, const char *helpMessage)
    4773             : {
    4774             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name")),
    4775         100 :                   pValue)
    4776           2 :         .SetAutoCompleteFunction([this](const std::string &)
    4777         102 :                                  { return AutoCompleteArrayName(); });
    4778             : }
    4779             : 
    4780             : /************************************************************************/
    4781             : /*                   GDALAlgorithm::AddArrayNameArg()                   */
    4782             : /************************************************************************/
    4783             : 
    4784             : GDALInConstructionAlgorithmArg &
    4785          76 : GDALAlgorithm::AddArrayNameArg(std::vector<std::string> *pValue,
    4786             :                                const char *helpMessage)
    4787             : {
    4788             :     return AddArg("array", 0, MsgOrDefault(helpMessage, _("Array name(s)")),
    4789         152 :                   pValue)
    4790           0 :         .SetAutoCompleteFunction([this](const std::string &)
    4791         152 :                                  { return AutoCompleteArrayName(); });
    4792             : }
    4793             : 
    4794             : /************************************************************************/
    4795             : /*                GDALAlgorithm::AutoCompleteArrayName()                */
    4796             : /************************************************************************/
    4797             : 
    4798           2 : std::vector<std::string> GDALAlgorithm::AutoCompleteArrayName() const
    4799             : {
    4800           2 :     std::vector<std::string> ret;
    4801           4 :     std::string osDSName;
    4802           2 :     auto inputArg = GetArg(GDAL_ARG_NAME_INPUT);
    4803           2 :     if (inputArg && inputArg->GetType() == GAAT_DATASET_LIST)
    4804             :     {
    4805           0 :         auto &inputDatasets = inputArg->Get<std::vector<GDALArgDatasetValue>>();
    4806           0 :         if (!inputDatasets.empty())
    4807             :         {
    4808           0 :             osDSName = inputDatasets[0].GetName();
    4809             :         }
    4810             :     }
    4811           2 :     else if (inputArg && inputArg->GetType() == GAAT_DATASET)
    4812             :     {
    4813           2 :         auto &inputDataset = inputArg->Get<GDALArgDatasetValue>();
    4814           2 :         osDSName = inputDataset.GetName();
    4815             :     }
    4816             : 
    4817           2 :     if (!osDSName.empty())
    4818             :     {
    4819           4 :         CPLStringList aosAllowedDrivers;
    4820           2 :         const auto ifArg = GetArg(GDAL_ARG_NAME_INPUT_FORMAT);
    4821           2 :         if (ifArg && ifArg->GetType() == GAAT_STRING_LIST)
    4822             :             aosAllowedDrivers =
    4823           2 :                 CPLStringList(ifArg->Get<std::vector<std::string>>());
    4824             : 
    4825           4 :         CPLStringList aosOpenOptions;
    4826           2 :         const auto ooArg = GetArg(GDAL_ARG_NAME_OPEN_OPTION);
    4827           2 :         if (ooArg && ooArg->GetType() == GAAT_STRING_LIST)
    4828             :             aosOpenOptions =
    4829           2 :                 CPLStringList(ooArg->Get<std::vector<std::string>>());
    4830             : 
    4831           2 :         if (auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    4832             :                 osDSName.c_str(), GDAL_OF_MULTIDIM_RASTER,
    4833           4 :                 aosAllowedDrivers.List(), aosOpenOptions.List(), nullptr)))
    4834             :         {
    4835           2 :             if (auto poRG = poDS->GetRootGroup())
    4836             :             {
    4837           1 :                 ret = poRG->GetMDArrayFullNamesRecursive();
    4838             :             }
    4839             :         }
    4840             :     }
    4841             : 
    4842           4 :     return ret;
    4843             : }
    4844             : 
    4845             : /************************************************************************/
    4846             : /*                  GDALAlgorithm::AddMemorySizeArg()                   */
    4847             : /************************************************************************/
    4848             : 
    4849             : GDALInConstructionAlgorithmArg &
    4850         210 : GDALAlgorithm::AddMemorySizeArg(size_t *pValue, std::string *pStrValue,
    4851             :                                 const std::string &optionName,
    4852             :                                 const char *helpMessage)
    4853             : {
    4854         420 :     return AddArg(optionName, 0, helpMessage, pStrValue)
    4855         210 :         .SetDefault(*pStrValue)
    4856             :         .AddValidationAction(
    4857         139 :             [this, pValue, pStrValue]()
    4858             :             {
    4859          47 :                 CPLDebug("GDAL", "StrValue `%s`", pStrValue->c_str());
    4860             :                 GIntBig nBytes;
    4861             :                 bool bUnitSpecified;
    4862          47 :                 if (CPLParseMemorySize(pStrValue->c_str(), &nBytes,
    4863          47 :                                        &bUnitSpecified) != CE_None)
    4864             :                 {
    4865           2 :                     return false;
    4866             :                 }
    4867          45 :                 if (!bUnitSpecified)
    4868             :                 {
    4869           1 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4870             :                                 "Memory size must have a unit or be a "
    4871             :                                 "percentage of usable RAM (2GB, 5%%, etc.)");
    4872           1 :                     return false;
    4873             :                 }
    4874             :                 if constexpr (sizeof(std::uint64_t) > sizeof(size_t))
    4875             :                 {
    4876             :                     // -1 to please CoverityScan
    4877             :                     if (static_cast<std::uint64_t>(nBytes) >
    4878             :                         std::numeric_limits<size_t>::max() - 1U)
    4879             :                     {
    4880             :                         ReportError(CE_Failure, CPLE_AppDefined,
    4881             :                                     "Memory size %s is too large.",
    4882             :                                     pStrValue->c_str());
    4883             :                         return false;
    4884             :                     }
    4885             :                 }
    4886             : 
    4887          44 :                 *pValue = static_cast<size_t>(nBytes);
    4888          44 :                 return true;
    4889         420 :             });
    4890             : }
    4891             : 
    4892             : /************************************************************************/
    4893             : /*                GDALAlgorithm::AddOutputLayerNameArg()                */
    4894             : /************************************************************************/
    4895             : 
    4896             : GDALInConstructionAlgorithmArg &
    4897         474 : GDALAlgorithm::AddOutputLayerNameArg(std::string *pValue,
    4898             :                                      const char *helpMessage)
    4899             : {
    4900             :     return AddArg(GDAL_ARG_NAME_OUTPUT_LAYER, 0,
    4901         474 :                   MsgOrDefault(helpMessage, _("Output layer name")), pValue);
    4902             : }
    4903             : 
    4904             : /************************************************************************/
    4905             : /*                   GDALAlgorithm::AddLayerNameArg()                   */
    4906             : /************************************************************************/
    4907             : 
    4908             : GDALInConstructionAlgorithmArg &
    4909         882 : GDALAlgorithm::AddLayerNameArg(std::vector<std::string> *pValue,
    4910             :                                const char *helpMessage)
    4911             : {
    4912             :     return AddArg(GDAL_ARG_NAME_INPUT_LAYER, 'l',
    4913         882 :                   MsgOrDefault(helpMessage, _("Input layer name")), pValue);
    4914             : }
    4915             : 
    4916             : /************************************************************************/
    4917             : /*                 GDALAlgorithm::AddGeometryTypeArg()                  */
    4918             : /************************************************************************/
    4919             : 
    4920             : GDALInConstructionAlgorithmArg &
    4921         409 : GDALAlgorithm::AddGeometryTypeArg(std::string *pValue, const char *helpMessage)
    4922             : {
    4923             :     return AddArg("geometry-type", 0,
    4924         818 :                   MsgOrDefault(helpMessage, _("Geometry type")), pValue)
    4925             :         .SetAutoCompleteFunction(
    4926           3 :             [](const std::string &currentValue)
    4927             :             {
    4928           3 :                 std::vector<std::string> oRet;
    4929          51 :                 for (const char *type :
    4930             :                      {"GEOMETRY", "POINT", "LINESTRING", "POLYGON",
    4931             :                       "MULTIPOINT", "MULTILINESTRING", "MULTIPOLYGON",
    4932             :                       "GEOMETRYCOLLECTION", "CURVE", "CIRCULARSTRING",
    4933             :                       "COMPOUNDCURVE", "SURFACE", "CURVEPOLYGON", "MULTICURVE",
    4934          54 :                       "MULTISURFACE", "POLYHEDRALSURFACE", "TIN"})
    4935             :                 {
    4936          68 :                     if (currentValue.empty() ||
    4937          17 :                         STARTS_WITH(type, currentValue.c_str()))
    4938             :                     {
    4939          35 :                         oRet.push_back(type);
    4940          35 :                         oRet.push_back(std::string(type).append("Z"));
    4941          35 :                         oRet.push_back(std::string(type).append("M"));
    4942          35 :                         oRet.push_back(std::string(type).append("ZM"));
    4943             :                     }
    4944             :                 }
    4945           3 :                 return oRet;
    4946         818 :             })
    4947             :         .AddValidationAction(
    4948         118 :             [this, pValue]()
    4949             :             {
    4950         107 :                 if (wkbFlatten(OGRFromOGCGeomType(pValue->c_str())) ==
    4951         115 :                         wkbUnknown &&
    4952           8 :                     !STARTS_WITH_CI(pValue->c_str(), "GEOMETRY"))
    4953             :                 {
    4954           3 :                     ReportError(CE_Failure, CPLE_AppDefined,
    4955             :                                 "Invalid geometry type '%s'", pValue->c_str());
    4956           3 :                     return false;
    4957             :                 }
    4958         104 :                 return true;
    4959         818 :             });
    4960             : }
    4961             : 
    4962             : /************************************************************************/
    4963             : /*         GDALAlgorithm::SetAutoCompleteFunctionForLayerName()         */
    4964             : /************************************************************************/
    4965             : 
    4966             : /* static */
    4967        2824 : void GDALAlgorithm::SetAutoCompleteFunctionForLayerName(
    4968             :     GDALInConstructionAlgorithmArg &layerArg, GDALAlgorithmArg &datasetArg)
    4969             : {
    4970        2824 :     CPLAssert(datasetArg.GetType() == GAAT_DATASET ||
    4971             :               datasetArg.GetType() == GAAT_DATASET_LIST);
    4972             : 
    4973             :     layerArg.SetAutoCompleteFunction(
    4974          18 :         [&datasetArg](const std::string &currentValue)
    4975             :         {
    4976           6 :             std::vector<std::string> ret;
    4977          12 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4978           6 :             GDALArgDatasetValue *dsVal = nullptr;
    4979           6 :             if (datasetArg.GetType() == GAAT_DATASET)
    4980             :             {
    4981           0 :                 dsVal = &(datasetArg.Get<GDALArgDatasetValue>());
    4982             :             }
    4983             :             else
    4984             :             {
    4985           6 :                 auto &val = datasetArg.Get<std::vector<GDALArgDatasetValue>>();
    4986           6 :                 if (val.size() == 1)
    4987             :                 {
    4988           6 :                     dsVal = &val[0];
    4989             :                 }
    4990             :             }
    4991           6 :             if (dsVal && !dsVal->GetName().empty())
    4992             :             {
    4993             :                 auto poDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    4994          12 :                     dsVal->GetName().c_str(), GDAL_OF_VECTOR));
    4995           6 :                 if (poDS)
    4996             :                 {
    4997          12 :                     for (auto &&poLayer : poDS->GetLayers())
    4998             :                     {
    4999           6 :                         if (currentValue == poLayer->GetDescription())
    5000             :                         {
    5001           1 :                             ret.clear();
    5002           1 :                             ret.push_back(poLayer->GetDescription());
    5003           1 :                             break;
    5004             :                         }
    5005           5 :                         ret.push_back(poLayer->GetDescription());
    5006             :                     }
    5007             :                 }
    5008             :             }
    5009          12 :             return ret;
    5010        2824 :         });
    5011        2824 : }
    5012             : 
    5013             : /************************************************************************/
    5014             : /*         GDALAlgorithm::SetAutoCompleteFunctionForFieldName()         */
    5015             : /************************************************************************/
    5016             : 
    5017         131 : void GDALAlgorithm::SetAutoCompleteFunctionForFieldName(
    5018             :     GDALInConstructionAlgorithmArg &fieldArg,
    5019             :     GDALInConstructionAlgorithmArg &layerNameArg,
    5020             :     std::vector<GDALArgDatasetValue> &datasetArg)
    5021             : {
    5022             : 
    5023             :     fieldArg.SetAutoCompleteFunction(
    5024          12 :         [&datasetArg, &layerNameArg](const std::string &currentValue)
    5025             :         {
    5026           8 :             std::set<std::string> ret;
    5027           4 :             if (!datasetArg.empty())
    5028             :             {
    5029           4 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5030             : 
    5031          14 :                 auto getLayerFields = [&ret, &currentValue](OGRLayer *poLayer)
    5032             :                 {
    5033           2 :                     auto poDefn = poLayer->GetLayerDefn();
    5034           2 :                     const int nFieldCount = poDefn->GetFieldCount();
    5035           8 :                     for (int iField = 0; iField < nFieldCount; iField++)
    5036             :                     {
    5037             :                         const char *fieldName =
    5038           6 :                             poDefn->GetFieldDefn(iField)->GetNameRef();
    5039           6 :                         if (currentValue == fieldName)
    5040             :                         {
    5041           0 :                             ret.clear();
    5042           0 :                             ret.insert(fieldName);
    5043           0 :                             break;
    5044             :                         }
    5045           6 :                         ret.insert(fieldName);
    5046             :                     }
    5047           2 :                 };
    5048             : 
    5049           2 :                 GDALArgDatasetValue &dsVal = datasetArg[0];
    5050             : 
    5051           2 :                 if (!dsVal.GetName().empty())
    5052             :                 {
    5053             :                     auto poDS = std::unique_ptr<GDALDataset>(
    5054           2 :                         GDALDataset::Open(dsVal.GetName().c_str(),
    5055           4 :                                           GDAL_OF_VECTOR | GDAL_OF_READONLY));
    5056           2 :                     if (poDS)
    5057             :                     {
    5058           2 :                         const auto &layerName = layerNameArg.Get<std::string>();
    5059           2 :                         if (layerName.empty())
    5060             :                         {
    5061             :                             // Loop through all layers
    5062           4 :                             for (auto &&poLayer : poDS->GetLayers())
    5063             :                             {
    5064           2 :                                 getLayerFields(poLayer);
    5065             :                             }
    5066             :                         }
    5067             :                         else
    5068             :                         {
    5069           0 :                             const auto poLayer = poDS->GetLayerByName(
    5070           0 :                                 layerNameArg.Get<std::string>().c_str());
    5071           0 :                             if (poLayer)
    5072             :                             {
    5073           0 :                                 getLayerFields(poLayer);
    5074             :                             }
    5075             :                         }
    5076             :                     }
    5077             :                 }
    5078             :             }
    5079           4 :             std::vector<std::string> retVector(ret.begin(), ret.end());
    5080           8 :             return retVector;
    5081         131 :         });
    5082         131 : }
    5083             : 
    5084             : /************************************************************************/
    5085             : /*                   GDALAlgorithm::AddFieldNameArg()                   */
    5086             : /************************************************************************/
    5087             : 
    5088             : GDALInConstructionAlgorithmArg &
    5089         131 : GDALAlgorithm::AddFieldNameArg(std::string *pValue, const char *helpMessage)
    5090             : {
    5091             :     return AddArg("field-name", 0, MsgOrDefault(helpMessage, _("Field name")),
    5092         131 :                   pValue);
    5093             : }
    5094             : 
    5095             : /************************************************************************/
    5096             : /*                GDALAlgorithm::ParseFieldDefinition()                 */
    5097             : /************************************************************************/
    5098          67 : bool GDALAlgorithm::ParseFieldDefinition(const std::string &posStrDef,
    5099             :                                          OGRFieldDefn *poFieldDefn,
    5100             :                                          std::string *posError)
    5101             : {
    5102             :     static const std::regex re(
    5103          67 :         R"(^([^:]+):([^(\s]+)(?:\((\d+)(?:,(\d+))?\))?$)");
    5104         134 :     std::smatch match;
    5105          67 :     if (std::regex_match(posStrDef, match, re))
    5106             :     {
    5107         132 :         const std::string name = match[1];
    5108         132 :         const std::string type = match[2];
    5109          66 :         const int width = match[3].matched ? std::stoi(match[3]) : 0;
    5110          66 :         const int precision = match[4].matched ? std::stoi(match[4]) : 0;
    5111          66 :         poFieldDefn->SetName(name.c_str());
    5112             : 
    5113          66 :         const auto typeEnum{OGRFieldDefn::GetFieldTypeByName(type.c_str())};
    5114          66 :         if (typeEnum == OFTString && !EQUAL(type.c_str(), "String"))
    5115             :         {
    5116           1 :             if (posError)
    5117           1 :                 *posError = "Unsupported field type: " + type;
    5118             : 
    5119           1 :             return false;
    5120             :         }
    5121          65 :         poFieldDefn->SetType(typeEnum);
    5122          65 :         poFieldDefn->SetWidth(width);
    5123          65 :         poFieldDefn->SetPrecision(precision);
    5124          65 :         return true;
    5125             :     }
    5126             : 
    5127           1 :     if (posError)
    5128             :         *posError = "Invalid field definition format. Expected "
    5129           1 :                     "<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]";
    5130             : 
    5131           1 :     return false;
    5132             : }
    5133             : 
    5134             : /************************************************************************/
    5135             : /*                GDALAlgorithm::AddFieldDefinitionArg()                */
    5136             : /************************************************************************/
    5137             : 
    5138             : GDALInConstructionAlgorithmArg &
    5139         120 : GDALAlgorithm::AddFieldDefinitionArg(std::vector<std::string> *pValues,
    5140             :                                      std::vector<OGRFieldDefn> *pFieldDefns,
    5141             :                                      const char *helpMessage)
    5142             : {
    5143             :     auto &arg =
    5144             :         AddArg("field", 0, MsgOrDefault(helpMessage, _("Field definition")),
    5145         240 :                pValues)
    5146         240 :             .SetMetaVar("<NAME>:<TYPE>[(<WIDTH>[,<PRECISION>])]")
    5147         120 :             .SetPackedValuesAllowed(true)
    5148         120 :             .SetRepeatedArgAllowed(true);
    5149             : 
    5150         132 :     auto validationFunction = [this, pFieldDefns, pValues]()
    5151             :     {
    5152          65 :         pFieldDefns->clear();
    5153         130 :         for (const auto &strValue : *pValues)
    5154             :         {
    5155          67 :             OGRFieldDefn fieldDefn("", OFTString);
    5156          67 :             std::string error;
    5157          67 :             if (!GDALAlgorithm::ParseFieldDefinition(strValue, &fieldDefn,
    5158             :                                                      &error))
    5159             :             {
    5160           2 :                 ReportError(CE_Failure, CPLE_AppDefined, "%s", error.c_str());
    5161           2 :                 return false;
    5162             :             }
    5163             :             // Check uniqueness of field names
    5164          67 :             for (const auto &existingFieldDefn : *pFieldDefns)
    5165             :             {
    5166           2 :                 if (EQUAL(existingFieldDefn.GetNameRef(),
    5167             :                           fieldDefn.GetNameRef()))
    5168             :                 {
    5169           0 :                     ReportError(CE_Failure, CPLE_AppDefined,
    5170             :                                 "Duplicate field name: '%s'",
    5171             :                                 fieldDefn.GetNameRef());
    5172           0 :                     return false;
    5173             :                 }
    5174             :             }
    5175          65 :             pFieldDefns->push_back(fieldDefn);
    5176             :         }
    5177          63 :         return true;
    5178         120 :     };
    5179             : 
    5180         120 :     arg.AddValidationAction(std::move(validationFunction));
    5181             : 
    5182         120 :     return arg;
    5183             : }
    5184             : 
    5185             : /************************************************************************/
    5186             : /*               GDALAlgorithm::AddFieldTypeSubtypeArg()                */
    5187             : /************************************************************************/
    5188             : 
    5189         262 : GDALInConstructionAlgorithmArg &GDALAlgorithm::AddFieldTypeSubtypeArg(
    5190             :     OGRFieldType *pTypeValue, OGRFieldSubType *pSubtypeValue,
    5191             :     std::string *pStrValue, const std::string &argName, const char *helpMessage)
    5192             : {
    5193             :     auto &arg =
    5194         524 :         AddArg(argName.empty() ? std::string("field-type") : argName, 0,
    5195         786 :                MsgOrDefault(helpMessage, _("Field type or subtype")), pStrValue)
    5196             :             .SetAutoCompleteFunction(
    5197           1 :                 [](const std::string &currentValue)
    5198             :                 {
    5199           1 :                     std::vector<std::string> oRet;
    5200           6 :                     for (int i = 1; i <= OGRFieldSubType::OFSTMaxSubType; i++)
    5201             :                     {
    5202             :                         const char *pszSubType =
    5203           5 :                             OGRFieldDefn::GetFieldSubTypeName(
    5204             :                                 static_cast<OGRFieldSubType>(i));
    5205           5 :                         if (pszSubType != nullptr)
    5206             :                         {
    5207           5 :                             if (currentValue.empty() ||
    5208           0 :                                 STARTS_WITH(pszSubType, currentValue.c_str()))
    5209             :                             {
    5210           5 :                                 oRet.push_back(pszSubType);
    5211             :                             }
    5212             :                         }
    5213             :                     }
    5214             : 
    5215          15 :                     for (int i = 0; i <= OGRFieldType::OFTMaxType; i++)
    5216             :                     {
    5217             :                         // Skip deprecated
    5218          14 :                         if (static_cast<OGRFieldType>(i) ==
    5219          13 :                                 OGRFieldType::OFTWideString ||
    5220             :                             static_cast<OGRFieldType>(i) ==
    5221             :                                 OGRFieldType::OFTWideStringList)
    5222           2 :                             continue;
    5223          12 :                         const char *pszType = OGRFieldDefn::GetFieldTypeName(
    5224             :                             static_cast<OGRFieldType>(i));
    5225          12 :                         if (pszType != nullptr)
    5226             :                         {
    5227          12 :                             if (currentValue.empty() ||
    5228           0 :                                 STARTS_WITH(pszType, currentValue.c_str()))
    5229             :                             {
    5230          12 :                                 oRet.push_back(pszType);
    5231             :                             }
    5232             :                         }
    5233             :                     }
    5234           1 :                     return oRet;
    5235         262 :                 });
    5236             : 
    5237             :     auto validationFunction =
    5238         845 :         [this, &arg, pTypeValue, pSubtypeValue, pStrValue]()
    5239             :     {
    5240         120 :         bool isValid{true};
    5241         120 :         *pTypeValue = OGRFieldDefn::GetFieldTypeByName(pStrValue->c_str());
    5242             : 
    5243             :         // String is returned for unknown types
    5244         120 :         if (!EQUAL(pStrValue->c_str(), "String") && *pTypeValue == OFTString)
    5245             :         {
    5246          16 :             isValid = false;
    5247             :         }
    5248             : 
    5249         120 :         *pSubtypeValue =
    5250         120 :             OGRFieldDefn::GetFieldSubTypeByName(pStrValue->c_str());
    5251             : 
    5252         120 :         if (*pSubtypeValue != OFSTNone)
    5253             :         {
    5254          15 :             isValid = true;
    5255          15 :             switch (*pSubtypeValue)
    5256             :             {
    5257           6 :                 case OFSTBoolean:
    5258             :                 case OFSTInt16:
    5259             :                 {
    5260           6 :                     *pTypeValue = OFTInteger;
    5261           6 :                     break;
    5262             :                 }
    5263           3 :                 case OFSTFloat32:
    5264             :                 {
    5265           3 :                     *pTypeValue = OFTReal;
    5266           3 :                     break;
    5267             :                 }
    5268           6 :                 default:
    5269             :                 {
    5270           6 :                     *pTypeValue = OFTString;
    5271           6 :                     break;
    5272             :                 }
    5273             :             }
    5274             :         }
    5275             : 
    5276         120 :         if (!isValid)
    5277             :         {
    5278           2 :             ReportError(CE_Failure, CPLE_AppDefined,
    5279             :                         "Invalid value for argument '%s': '%s'",
    5280           1 :                         arg.GetName().c_str(), pStrValue->c_str());
    5281             :         }
    5282             : 
    5283         120 :         return isValid;
    5284         262 :     };
    5285             : 
    5286         262 :     if (!pStrValue->empty())
    5287             :     {
    5288           0 :         arg.SetDefault(*pStrValue);
    5289           0 :         validationFunction();
    5290             :     }
    5291             : 
    5292         262 :     arg.AddValidationAction(std::move(validationFunction));
    5293             : 
    5294         262 :     return arg;
    5295             : }
    5296             : 
    5297             : /************************************************************************/
    5298             : /*                   GDALAlgorithm::ValidateBandArg()                   */
    5299             : /************************************************************************/
    5300             : 
    5301        3851 : bool GDALAlgorithm::ValidateBandArg() const
    5302             : {
    5303        3851 :     bool ret = true;
    5304        3851 :     const auto bandArg = GetArg(GDAL_ARG_NAME_BAND);
    5305        3851 :     const auto inputDatasetArg = GetArg(GDAL_ARG_NAME_INPUT);
    5306        1414 :     if (bandArg && bandArg->IsExplicitlySet() && inputDatasetArg &&
    5307         280 :         (inputDatasetArg->GetType() == GAAT_DATASET ||
    5308        5259 :          inputDatasetArg->GetType() == GAAT_DATASET_LIST) &&
    5309         143 :         (inputDatasetArg->GetDatasetType() & GDAL_OF_RASTER) != 0)
    5310             :     {
    5311         104 :         const auto CheckBand = [this](const GDALDataset *poDS, int nBand)
    5312             :         {
    5313          99 :             if (nBand > poDS->GetRasterCount())
    5314             :             {
    5315           5 :                 ReportError(CE_Failure, CPLE_AppDefined,
    5316             :                             "Value of 'band' should be greater or equal than "
    5317             :                             "1 and less or equal than %d.",
    5318             :                             poDS->GetRasterCount());
    5319           5 :                 return false;
    5320             :             }
    5321          94 :             return true;
    5322          86 :         };
    5323             : 
    5324             :         const auto ValidateForOneDataset =
    5325         292 :             [&bandArg, &CheckBand](const GDALDataset *poDS)
    5326             :         {
    5327          81 :             bool l_ret = true;
    5328          81 :             if (bandArg->GetType() == GAAT_INTEGER)
    5329             :             {
    5330          24 :                 l_ret = CheckBand(poDS, bandArg->Get<int>());
    5331             :             }
    5332          57 :             else if (bandArg->GetType() == GAAT_INTEGER_LIST)
    5333             :             {
    5334         130 :                 for (int nBand : bandArg->Get<std::vector<int>>())
    5335             :                 {
    5336          75 :                     l_ret = l_ret && CheckBand(poDS, nBand);
    5337             :                 }
    5338             :             }
    5339          81 :             return l_ret;
    5340          86 :         };
    5341             : 
    5342          86 :         if (inputDatasetArg->GetType() == GAAT_DATASET)
    5343             :         {
    5344             :             auto poDS =
    5345           6 :                 inputDatasetArg->Get<GDALArgDatasetValue>().GetDatasetRef();
    5346           6 :             if (poDS && !ValidateForOneDataset(poDS))
    5347           2 :                 ret = false;
    5348             :         }
    5349             :         else
    5350             :         {
    5351          80 :             CPLAssert(inputDatasetArg->GetType() == GAAT_DATASET_LIST);
    5352          79 :             for (auto &datasetValue :
    5353         238 :                  inputDatasetArg->Get<std::vector<GDALArgDatasetValue>>())
    5354             :             {
    5355          79 :                 auto poDS = datasetValue.GetDatasetRef();
    5356          79 :                 if (poDS && !ValidateForOneDataset(poDS))
    5357           3 :                     ret = false;
    5358             :             }
    5359             :         }
    5360             :     }
    5361        3851 :     return ret;
    5362             : }
    5363             : 
    5364             : /************************************************************************/
    5365             : /*            GDALAlgorithm::RunPreStepPipelineValidations()            */
    5366             : /************************************************************************/
    5367             : 
    5368        3070 : bool GDALAlgorithm::RunPreStepPipelineValidations() const
    5369             : {
    5370        3070 :     return ValidateBandArg();
    5371             : }
    5372             : 
    5373             : /************************************************************************/
    5374             : /*                     GDALAlgorithm::AddBandArg()                      */
    5375             : /************************************************************************/
    5376             : 
    5377             : GDALInConstructionAlgorithmArg &
    5378        1445 : GDALAlgorithm::AddBandArg(int *pValue, const char *helpMessage)
    5379             : {
    5380        1762 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5381             : 
    5382             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5383             :                   MsgOrDefault(helpMessage, _("Input band (1-based index)")),
    5384        2890 :                   pValue)
    5385             :         .AddValidationAction(
    5386          34 :             [pValue]()
    5387             :             {
    5388          34 :                 if (*pValue <= 0)
    5389             :                 {
    5390           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5391             :                              "Value of 'band' should greater or equal to 1.");
    5392           1 :                     return false;
    5393             :                 }
    5394          33 :                 return true;
    5395        2890 :             });
    5396             : }
    5397             : 
    5398             : /************************************************************************/
    5399             : /*                     GDALAlgorithm::AddBandArg()                      */
    5400             : /************************************************************************/
    5401             : 
    5402             : GDALInConstructionAlgorithmArg &
    5403         837 : GDALAlgorithm::AddBandArg(std::vector<int> *pValue, const char *helpMessage)
    5404             : {
    5405        1301 :     AddValidationAction([this]() { return ValidateBandArg(); });
    5406             : 
    5407             :     return AddArg(GDAL_ARG_NAME_BAND, 'b',
    5408             :                   MsgOrDefault(helpMessage, _("Input band(s) (1-based index)")),
    5409        1674 :                   pValue)
    5410             :         .AddValidationAction(
    5411         126 :             [pValue]()
    5412             :             {
    5413         397 :                 for (int val : *pValue)
    5414             :                 {
    5415         272 :                     if (val <= 0)
    5416             :                     {
    5417           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    5418             :                                  "Value of 'band' should greater or equal "
    5419             :                                  "to 1.");
    5420           1 :                         return false;
    5421             :                     }
    5422             :                 }
    5423         125 :                 return true;
    5424        1674 :             });
    5425             : }
    5426             : 
    5427             : /************************************************************************/
    5428             : /*                      ParseAndValidateKeyValue()                      */
    5429             : /************************************************************************/
    5430             : 
    5431         402 : bool GDALAlgorithm::ParseAndValidateKeyValue(GDALAlgorithmArg &arg)
    5432             : {
    5433         406 :     const auto Validate = [this, &arg](const std::string &val)
    5434             :     {
    5435         401 :         if (val.find('=') == std::string::npos)
    5436             :         {
    5437           5 :             ReportError(
    5438             :                 CE_Failure, CPLE_AppDefined,
    5439             :                 "Invalid value for argument '%s'. <KEY>=<VALUE> expected",
    5440           5 :                 arg.GetName().c_str());
    5441           5 :             return false;
    5442             :         }
    5443             : 
    5444         396 :         return true;
    5445         402 :     };
    5446             : 
    5447         402 :     if (arg.GetType() == GAAT_STRING)
    5448             :     {
    5449           0 :         return Validate(arg.Get<std::string>());
    5450             :     }
    5451         402 :     else if (arg.GetType() == GAAT_STRING_LIST)
    5452             :     {
    5453         402 :         std::vector<std::string> &vals = arg.Get<std::vector<std::string>>();
    5454         402 :         if (vals.size() == 1)
    5455             :         {
    5456             :             // Try to split A=B,C=D into A=B and C=D if there is no ambiguity
    5457         734 :             std::vector<std::string> newVals;
    5458         734 :             std::string curToken;
    5459         367 :             bool canSplitOnComma = true;
    5460         367 :             char lastSep = 0;
    5461         367 :             bool inString = false;
    5462         367 :             bool equalFoundInLastToken = false;
    5463        5318 :             for (char c : vals[0])
    5464             :             {
    5465        4955 :                 if (!inString && c == ',')
    5466             :                 {
    5467          10 :                     if (lastSep != '=' || !equalFoundInLastToken)
    5468             :                     {
    5469           2 :                         canSplitOnComma = false;
    5470           2 :                         break;
    5471             :                     }
    5472           8 :                     lastSep = c;
    5473           8 :                     newVals.push_back(curToken);
    5474           8 :                     curToken.clear();
    5475           8 :                     equalFoundInLastToken = false;
    5476             :                 }
    5477        4945 :                 else if (!inString && c == '=')
    5478             :                 {
    5479         366 :                     if (lastSep == '=')
    5480             :                     {
    5481           2 :                         canSplitOnComma = false;
    5482           2 :                         break;
    5483             :                     }
    5484         364 :                     equalFoundInLastToken = true;
    5485         364 :                     lastSep = c;
    5486         364 :                     curToken += c;
    5487             :                 }
    5488        4579 :                 else if (c == '"')
    5489             :                 {
    5490           4 :                     inString = !inString;
    5491           4 :                     curToken += c;
    5492             :                 }
    5493             :                 else
    5494             :                 {
    5495        4575 :                     curToken += c;
    5496             :                 }
    5497             :             }
    5498         367 :             if (canSplitOnComma && !inString && equalFoundInLastToken)
    5499             :             {
    5500         354 :                 if (!curToken.empty())
    5501         354 :                     newVals.emplace_back(std::move(curToken));
    5502         354 :                 vals = std::move(newVals);
    5503             :             }
    5504             :         }
    5505             : 
    5506         798 :         for (const auto &val : vals)
    5507             :         {
    5508         401 :             if (!Validate(val))
    5509           5 :                 return false;
    5510             :         }
    5511             :     }
    5512             : 
    5513         397 :     return true;
    5514             : }
    5515             : 
    5516             : /************************************************************************/
    5517             : /*                           IsGDALGOutput()                            */
    5518             : /************************************************************************/
    5519             : 
    5520        2060 : bool GDALAlgorithm::IsGDALGOutput() const
    5521             : {
    5522        2060 :     bool isGDALGOutput = false;
    5523        2060 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5524        2060 :     const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5525        3525 :     if (outputArg && outputArg->GetType() == GAAT_DATASET &&
    5526        1465 :         outputArg->IsExplicitlySet())
    5527             :     {
    5528        2873 :         if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING &&
    5529        1424 :             outputFormatArg->IsExplicitlySet())
    5530             :         {
    5531             :             const auto &val =
    5532         926 :                 outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    5533         926 :             isGDALGOutput = EQUAL(val.c_str(), "GDALG");
    5534             :         }
    5535             :         else
    5536             :         {
    5537             :             const auto &filename =
    5538         523 :                 outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>();
    5539         523 :             isGDALGOutput =
    5540        1019 :                 filename.GetName().size() > strlen(".gdalg.json") &&
    5541         496 :                 EQUAL(filename.GetName().c_str() + filename.GetName().size() -
    5542             :                           strlen(".gdalg.json"),
    5543             :                       ".gdalg.json");
    5544             :         }
    5545             :     }
    5546        2060 :     return isGDALGOutput;
    5547             : }
    5548             : 
    5549             : /************************************************************************/
    5550             : /*                         ProcessGDALGOutput()                         */
    5551             : /************************************************************************/
    5552             : 
    5553        2399 : GDALAlgorithm::ProcessGDALGOutputRet GDALAlgorithm::ProcessGDALGOutput()
    5554             : {
    5555        2399 :     if (!SupportsStreamedOutput())
    5556         786 :         return ProcessGDALGOutputRet::NOT_GDALG;
    5557             : 
    5558        1613 :     if (IsGDALGOutput())
    5559             :     {
    5560          11 :         const auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5561             :         const auto &filename =
    5562          11 :             outputArg->GDALAlgorithmArg::Get<GDALArgDatasetValue>().GetName();
    5563             :         VSIStatBufL sStat;
    5564          11 :         if (VSIStatL(filename.c_str(), &sStat) == 0)
    5565             :         {
    5566           0 :             const auto overwriteArg = GetArg(GDAL_ARG_NAME_OVERWRITE);
    5567           0 :             if (overwriteArg && overwriteArg->GetType() == GAAT_BOOLEAN)
    5568             :             {
    5569           0 :                 if (!overwriteArg->GDALAlgorithmArg::Get<bool>())
    5570             :                 {
    5571           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5572             :                              "File '%s' already exists. Specify the "
    5573             :                              "--overwrite option to overwrite it.",
    5574             :                              filename.c_str());
    5575           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5576             :                 }
    5577             :             }
    5578             :         }
    5579             : 
    5580          22 :         std::string osCommandLine;
    5581             : 
    5582          44 :         for (const auto &path : GDALAlgorithm::m_callPath)
    5583             :         {
    5584          33 :             if (!osCommandLine.empty())
    5585          22 :                 osCommandLine += ' ';
    5586          33 :             osCommandLine += path;
    5587             :         }
    5588             : 
    5589         250 :         for (const auto &arg : GetArgs())
    5590             :         {
    5591         265 :             if (arg->IsExplicitlySet() &&
    5592          41 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT &&
    5593          30 :                 arg->GetName() != GDAL_ARG_NAME_OUTPUT_FORMAT &&
    5594         280 :                 arg->GetName() != GDAL_ARG_NAME_UPDATE &&
    5595          15 :                 arg->GetName() != GDAL_ARG_NAME_OVERWRITE)
    5596             :             {
    5597          14 :                 osCommandLine += ' ';
    5598          14 :                 std::string strArg;
    5599          14 :                 if (!arg->Serialize(strArg))
    5600             :                 {
    5601           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    5602             :                              "Cannot serialize argument %s",
    5603           0 :                              arg->GetName().c_str());
    5604           0 :                     return ProcessGDALGOutputRet::GDALG_ERROR;
    5605             :                 }
    5606          14 :                 osCommandLine += strArg;
    5607             :             }
    5608             :         }
    5609             : 
    5610          11 :         osCommandLine += " --output-format stream --output streamed_dataset";
    5611             : 
    5612          11 :         std::string outStringUnused;
    5613          11 :         return SaveGDALG(filename, outStringUnused, osCommandLine)
    5614          11 :                    ? ProcessGDALGOutputRet::GDALG_OK
    5615          11 :                    : ProcessGDALGOutputRet::GDALG_ERROR;
    5616             :     }
    5617             : 
    5618        1602 :     return ProcessGDALGOutputRet::NOT_GDALG;
    5619             : }
    5620             : 
    5621             : /************************************************************************/
    5622             : /*                      GDALAlgorithm::SaveGDALG()                      */
    5623             : /************************************************************************/
    5624             : 
    5625          22 : /* static */ bool GDALAlgorithm::SaveGDALG(const std::string &filename,
    5626             :                                            std::string &outString,
    5627             :                                            const std::string &commandLine)
    5628             : {
    5629          44 :     CPLJSONDocument oDoc;
    5630          22 :     oDoc.GetRoot().Add("type", "gdal_streamed_alg");
    5631          22 :     oDoc.GetRoot().Add("command_line", commandLine);
    5632          22 :     oDoc.GetRoot().Add("gdal_version", GDALVersionInfo("VERSION_NUM"));
    5633             : 
    5634          22 :     if (!filename.empty())
    5635          21 :         return oDoc.Save(filename);
    5636             : 
    5637           1 :     outString = oDoc.GetRoot().Format(CPLJSONObject::PrettyFormat::Pretty);
    5638           1 :     return true;
    5639             : }
    5640             : 
    5641             : /************************************************************************/
    5642             : /*                GDALAlgorithm::AddCreationOptionsArg()                */
    5643             : /************************************************************************/
    5644             : 
    5645             : GDALInConstructionAlgorithmArg &
    5646        7934 : GDALAlgorithm::AddCreationOptionsArg(std::vector<std::string> *pValue,
    5647             :                                      const char *helpMessage)
    5648             : {
    5649             :     auto &arg = AddArg(GDAL_ARG_NAME_CREATION_OPTION, 0,
    5650       15868 :                        MsgOrDefault(helpMessage, _("Creation option")), pValue)
    5651       15868 :                     .AddAlias("co")
    5652       15868 :                     .SetMetaVar("<KEY>=<VALUE>")
    5653        7934 :                     .SetPackedValuesAllowed(false);
    5654         147 :     arg.AddValidationAction([this, &arg]()
    5655        8081 :                             { return ParseAndValidateKeyValue(arg); });
    5656             : 
    5657             :     arg.SetAutoCompleteFunction(
    5658          48 :         [this](const std::string &currentValue)
    5659             :         {
    5660          16 :             std::vector<std::string> oRet;
    5661             : 
    5662          16 :             int datasetType =
    5663             :                 GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER;
    5664          16 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5665          16 :             if (outputArg && (outputArg->GetType() == GAAT_DATASET ||
    5666           0 :                               outputArg->GetType() == GAAT_DATASET_LIST))
    5667             :             {
    5668          16 :                 datasetType = outputArg->GetDatasetType();
    5669             :             }
    5670             : 
    5671          16 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5672          32 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5673          16 :                 outputFormat->IsExplicitlySet())
    5674             :             {
    5675          12 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5676           6 :                     outputFormat->Get<std::string>().c_str());
    5677           6 :                 if (poDriver)
    5678             :                 {
    5679           6 :                     AddOptionsSuggestions(
    5680           6 :                         poDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    5681             :                         datasetType, currentValue, oRet);
    5682             :                 }
    5683           6 :                 return oRet;
    5684             :             }
    5685             : 
    5686          10 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5687             :             {
    5688          10 :                 auto poDM = GetGDALDriverManager();
    5689          10 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5690          10 :                 const auto &osDSName = datasetValue.GetName();
    5691          10 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5692          10 :                 if (!osExt.empty())
    5693             :                 {
    5694          10 :                     std::set<std::string> oVisitedExtensions;
    5695         709 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5696             :                     {
    5697         706 :                         auto poDriver = poDM->GetDriver(i);
    5698        2118 :                         if (((datasetType & GDAL_OF_RASTER) != 0 &&
    5699         706 :                              poDriver->GetMetadataItem(GDAL_DCAP_RASTER)) ||
    5700         213 :                             ((datasetType & GDAL_OF_VECTOR) != 0 &&
    5701        1412 :                              poDriver->GetMetadataItem(GDAL_DCAP_VECTOR)) ||
    5702         213 :                             ((datasetType & GDAL_OF_MULTIDIM_RASTER) != 0 &&
    5703           0 :                              poDriver->GetMetadataItem(
    5704           0 :                                  GDAL_DCAP_MULTIDIM_RASTER)))
    5705             :                         {
    5706             :                             const char *pszExtensions =
    5707         493 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5708         493 :                             if (pszExtensions)
    5709             :                             {
    5710             :                                 const CPLStringList aosExts(
    5711         320 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5712         710 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5713             :                                 {
    5714         416 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5715          16 :                                         !cpl::contains(oVisitedExtensions,
    5716             :                                                        pszExt))
    5717             :                                     {
    5718          10 :                                         oVisitedExtensions.insert(pszExt);
    5719          10 :                                         if (AddOptionsSuggestions(
    5720             :                                                 poDriver->GetMetadataItem(
    5721          10 :                                                     GDAL_DMD_CREATIONOPTIONLIST),
    5722             :                                                 datasetType, currentValue,
    5723             :                                                 oRet))
    5724             :                                         {
    5725           7 :                                             return oRet;
    5726             :                                         }
    5727           3 :                                         break;
    5728             :                                     }
    5729             :                                 }
    5730             :                             }
    5731             :                         }
    5732             :                     }
    5733             :                 }
    5734             :             }
    5735             : 
    5736           3 :             return oRet;
    5737        7934 :         });
    5738             : 
    5739        7934 :     return arg;
    5740             : }
    5741             : 
    5742             : /************************************************************************/
    5743             : /*             GDALAlgorithm::AddLayerCreationOptionsArg()              */
    5744             : /************************************************************************/
    5745             : 
    5746             : GDALInConstructionAlgorithmArg &
    5747        3791 : GDALAlgorithm::AddLayerCreationOptionsArg(std::vector<std::string> *pValue,
    5748             :                                           const char *helpMessage)
    5749             : {
    5750             :     auto &arg =
    5751             :         AddArg(GDAL_ARG_NAME_LAYER_CREATION_OPTION, 0,
    5752        7582 :                MsgOrDefault(helpMessage, _("Layer creation option")), pValue)
    5753        7582 :             .AddAlias("lco")
    5754        7582 :             .SetMetaVar("<KEY>=<VALUE>")
    5755        3791 :             .SetPackedValuesAllowed(false);
    5756          73 :     arg.AddValidationAction([this, &arg]()
    5757        3864 :                             { return ParseAndValidateKeyValue(arg); });
    5758             : 
    5759             :     arg.SetAutoCompleteFunction(
    5760           5 :         [this](const std::string &currentValue)
    5761             :         {
    5762           2 :             std::vector<std::string> oRet;
    5763             : 
    5764           2 :             auto outputFormat = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    5765           4 :             if (outputFormat && outputFormat->GetType() == GAAT_STRING &&
    5766           2 :                 outputFormat->IsExplicitlySet())
    5767             :             {
    5768           2 :                 auto poDriver = GetGDALDriverManager()->GetDriverByName(
    5769           1 :                     outputFormat->Get<std::string>().c_str());
    5770           1 :                 if (poDriver)
    5771             :                 {
    5772           1 :                     AddOptionsSuggestions(poDriver->GetMetadataItem(
    5773           1 :                                               GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5774             :                                           GDAL_OF_VECTOR, currentValue, oRet);
    5775             :                 }
    5776           1 :                 return oRet;
    5777             :             }
    5778             : 
    5779           1 :             auto outputArg = GetArg(GDAL_ARG_NAME_OUTPUT);
    5780           1 :             if (outputArg && outputArg->GetType() == GAAT_DATASET)
    5781             :             {
    5782           1 :                 auto poDM = GetGDALDriverManager();
    5783           1 :                 auto &datasetValue = outputArg->Get<GDALArgDatasetValue>();
    5784           1 :                 const auto &osDSName = datasetValue.GetName();
    5785           1 :                 const std::string osExt = CPLGetExtensionSafe(osDSName.c_str());
    5786           1 :                 if (!osExt.empty())
    5787             :                 {
    5788           1 :                     std::set<std::string> oVisitedExtensions;
    5789         227 :                     for (int i = 0; i < poDM->GetDriverCount(); ++i)
    5790             :                     {
    5791         226 :                         auto poDriver = poDM->GetDriver(i);
    5792         226 :                         if (poDriver->GetMetadataItem(GDAL_DCAP_VECTOR))
    5793             :                         {
    5794             :                             const char *pszExtensions =
    5795          90 :                                 poDriver->GetMetadataItem(GDAL_DMD_EXTENSIONS);
    5796          90 :                             if (pszExtensions)
    5797             :                             {
    5798             :                                 const CPLStringList aosExts(
    5799          61 :                                     CSLTokenizeString2(pszExtensions, " ", 0));
    5800         154 :                                 for (const char *pszExt : cpl::Iterate(aosExts))
    5801             :                                 {
    5802          95 :                                     if (EQUAL(pszExt, osExt.c_str()) &&
    5803           1 :                                         !cpl::contains(oVisitedExtensions,
    5804             :                                                        pszExt))
    5805             :                                     {
    5806           1 :                                         oVisitedExtensions.insert(pszExt);
    5807           1 :                                         if (AddOptionsSuggestions(
    5808             :                                                 poDriver->GetMetadataItem(
    5809           1 :                                                     GDAL_DS_LAYER_CREATIONOPTIONLIST),
    5810             :                                                 GDAL_OF_VECTOR, currentValue,
    5811             :                                                 oRet))
    5812             :                                         {
    5813           0 :                                             return oRet;
    5814             :                                         }
    5815           1 :                                         break;
    5816             :                                     }
    5817             :                                 }
    5818             :                             }
    5819             :                         }
    5820             :                     }
    5821             :                 }
    5822             :             }
    5823             : 
    5824           1 :             return oRet;
    5825        3791 :         });
    5826             : 
    5827        3791 :     return arg;
    5828             : }
    5829             : 
    5830             : /************************************************************************/
    5831             : /*                     GDALAlgorithm::AddBBOXArg()                      */
    5832             : /************************************************************************/
    5833             : 
    5834             : /** Add bbox=xmin,ymin,xmax,ymax argument. */
    5835             : GDALInConstructionAlgorithmArg &
    5836        1796 : GDALAlgorithm::AddBBOXArg(std::vector<double> *pValue, const char *helpMessage)
    5837             : {
    5838             :     auto &arg = AddArg("bbox", 0,
    5839             :                        MsgOrDefault(helpMessage,
    5840             :                                     _("Bounding box as xmin,ymin,xmax,ymax")),
    5841        3592 :                        pValue)
    5842        1796 :                     .SetRepeatedArgAllowed(false)
    5843        1796 :                     .SetMinCount(4)
    5844        1796 :                     .SetMaxCount(4)
    5845        1796 :                     .SetDisplayHintAboutRepetition(false);
    5846             :     arg.AddValidationAction(
    5847         162 :         [&arg]()
    5848             :         {
    5849         162 :             const auto &val = arg.Get<std::vector<double>>();
    5850         162 :             CPLAssert(val.size() == 4);
    5851         162 :             if (!(val[0] <= val[2]) || !(val[1] <= val[3]))
    5852             :             {
    5853           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5854             :                          "Value of 'bbox' should be xmin,ymin,xmax,ymax with "
    5855             :                          "xmin <= xmax and ymin <= ymax");
    5856           5 :                 return false;
    5857             :             }
    5858         157 :             return true;
    5859        1796 :         });
    5860        1796 :     return arg;
    5861             : }
    5862             : 
    5863             : /************************************************************************/
    5864             : /*                  GDALAlgorithm::AddActiveLayerArg()                  */
    5865             : /************************************************************************/
    5866             : 
    5867             : GDALInConstructionAlgorithmArg &
    5868        1701 : GDALAlgorithm::AddActiveLayerArg(std::string *pValue, const char *helpMessage)
    5869             : {
    5870             :     return AddArg("active-layer", 0,
    5871             :                   MsgOrDefault(helpMessage,
    5872             :                                _("Set active layer (if not specified, all)")),
    5873        1701 :                   pValue);
    5874             : }
    5875             : 
    5876             : /************************************************************************/
    5877             : /*                  GDALAlgorithm::AddNumThreadsArg()                   */
    5878             : /************************************************************************/
    5879             : 
    5880             : GDALInConstructionAlgorithmArg &
    5881         676 : GDALAlgorithm::AddNumThreadsArg(int *pValue, std::string *pStrValue,
    5882             :                                 const char *helpMessage)
    5883             : {
    5884             :     auto &arg =
    5885             :         AddArg(GDAL_ARG_NAME_NUM_THREADS, 'j',
    5886             :                MsgOrDefault(helpMessage, _("Number of jobs (or ALL_CPUS)")),
    5887         676 :                pStrValue);
    5888             : 
    5889             :     AddArg(GDAL_ARG_NAME_NUM_THREADS_INT_HIDDEN, 0,
    5890        1352 :            _("Number of jobs (read-only, hidden argument)"), pValue)
    5891         676 :         .SetHidden();
    5892             : 
    5893        2583 :     auto lambda = [this, &arg, pValue, pStrValue]
    5894             :     {
    5895         861 :         bool bOK = false;
    5896         861 :         const char *pszVal = CPLGetConfigOption("GDAL_NUM_THREADS", nullptr);
    5897             :         const int nLimit = std::clamp(
    5898         861 :             pszVal && !EQUAL(pszVal, "ALL_CPUS") ? atoi(pszVal) : INT_MAX, 1,
    5899        1722 :             CPLGetNumCPUs());
    5900             :         const int nNumThreads =
    5901         861 :             GDALGetNumThreads(pStrValue->c_str(), nLimit,
    5902             :                               /* bDefaultToAllCPUs = */ false, nullptr, &bOK);
    5903         861 :         if (bOK)
    5904             :         {
    5905         861 :             *pValue = nNumThreads;
    5906             :         }
    5907             :         else
    5908             :         {
    5909           0 :             ReportError(CE_Failure, CPLE_IllegalArg,
    5910             :                         "Invalid value for '%s' argument",
    5911           0 :                         arg.GetName().c_str());
    5912             :         }
    5913         861 :         return bOK;
    5914         676 :     };
    5915         676 :     if (!pStrValue->empty())
    5916             :     {
    5917         630 :         arg.SetDefault(*pStrValue);
    5918         630 :         lambda();
    5919             :     }
    5920         676 :     arg.AddValidationAction(std::move(lambda));
    5921         676 :     return arg;
    5922             : }
    5923             : 
    5924             : /************************************************************************/
    5925             : /*                 GDALAlgorithm::AddAbsolutePathArg()                  */
    5926             : /************************************************************************/
    5927             : 
    5928             : GDALInConstructionAlgorithmArg &
    5929         619 : GDALAlgorithm::AddAbsolutePathArg(bool *pValue, const char *helpMessage)
    5930             : {
    5931             :     return AddArg(
    5932             :         "absolute-path", 0,
    5933             :         MsgOrDefault(helpMessage, _("Whether the path to the input dataset "
    5934             :                                     "should be stored as an absolute path")),
    5935         619 :         pValue);
    5936             : }
    5937             : 
    5938             : /************************************************************************/
    5939             : /*               GDALAlgorithm::AddPixelFunctionNameArg()               */
    5940             : /************************************************************************/
    5941             : 
    5942             : GDALInConstructionAlgorithmArg &
    5943         137 : GDALAlgorithm::AddPixelFunctionNameArg(std::string *pValue,
    5944             :                                        const char *helpMessage)
    5945             : {
    5946             : 
    5947             :     const auto pixelFunctionNames =
    5948         137 :         VRTDerivedRasterBand::GetPixelFunctionNames();
    5949             :     return AddArg(
    5950             :                "pixel-function", 0,
    5951             :                MsgOrDefault(
    5952             :                    helpMessage,
    5953             :                    _("Specify a pixel function to calculate output value from "
    5954             :                      "overlapping inputs")),
    5955         274 :                pValue)
    5956         274 :         .SetChoices(pixelFunctionNames);
    5957             : }
    5958             : 
    5959             : /************************************************************************/
    5960             : /*               GDALAlgorithm::AddPixelFunctionArgsArg()               */
    5961             : /************************************************************************/
    5962             : 
    5963             : GDALInConstructionAlgorithmArg &
    5964         137 : GDALAlgorithm::AddPixelFunctionArgsArg(std::vector<std::string> *pValue,
    5965             :                                        const char *helpMessage)
    5966             : {
    5967             :     auto &pixelFunctionArgArg =
    5968             :         AddArg("pixel-function-arg", 0,
    5969             :                MsgOrDefault(
    5970             :                    helpMessage,
    5971             :                    _("Specify argument(s) to pass to the pixel function")),
    5972         274 :                pValue)
    5973         274 :             .SetMetaVar("<NAME>=<VALUE>")
    5974         137 :             .SetRepeatedArgAllowed(true);
    5975             :     pixelFunctionArgArg.AddValidationAction(
    5976           7 :         [this, &pixelFunctionArgArg]()
    5977         144 :         { return ParseAndValidateKeyValue(pixelFunctionArgArg); });
    5978             : 
    5979             :     pixelFunctionArgArg.SetAutoCompleteFunction(
    5980          12 :         [this](const std::string &currentValue)
    5981             :         {
    5982          12 :             std::string pixelFunction;
    5983           6 :             const auto pixelFunctionArg = GetArg("pixel-function");
    5984           6 :             if (pixelFunctionArg && pixelFunctionArg->GetType() == GAAT_STRING)
    5985             :             {
    5986           6 :                 pixelFunction = pixelFunctionArg->Get<std::string>();
    5987             :             }
    5988             : 
    5989           6 :             std::vector<std::string> ret;
    5990             : 
    5991           6 :             if (!pixelFunction.empty())
    5992             :             {
    5993           5 :                 const auto *pair = VRTDerivedRasterBand::GetPixelFunction(
    5994             :                     pixelFunction.c_str());
    5995           5 :                 if (!pair)
    5996             :                 {
    5997           1 :                     ret.push_back("**");
    5998             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    5999           1 :                     ret.push_back(std::string("\xC2\xA0"
    6000             :                                               "Invalid pixel function name"));
    6001             :                 }
    6002           4 :                 else if (pair->second.find("Argument name=") ==
    6003             :                          std::string::npos)
    6004             :                 {
    6005           1 :                     ret.push_back("**");
    6006             :                     // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    6007           1 :                     ret.push_back(
    6008           2 :                         std::string(
    6009             :                             "\xC2\xA0"
    6010             :                             "No pixel function arguments for pixel function '")
    6011           1 :                             .append(pixelFunction)
    6012           1 :                             .append("'"));
    6013             :                 }
    6014             :                 else
    6015             :                 {
    6016           3 :                     AddOptionsSuggestions(pair->second.c_str(), 0, currentValue,
    6017             :                                           ret);
    6018             :                 }
    6019             :             }
    6020             : 
    6021          12 :             return ret;
    6022         137 :         });
    6023             : 
    6024         137 :     return pixelFunctionArgArg;
    6025             : }
    6026             : 
    6027             : /************************************************************************/
    6028             : /*                   GDALAlgorithm::AddProgressArg()                    */
    6029             : /************************************************************************/
    6030             : 
    6031        8062 : void GDALAlgorithm::AddProgressArg()
    6032             : {
    6033             :     AddArg(GDAL_ARG_NAME_QUIET, 'q',
    6034       16124 :            _("Quiet mode (no progress bar or warning message)"), &m_quiet)
    6035       16124 :         .SetCategory(GAAC_COMMON)
    6036        8062 :         .AddAction([this]() { m_progressBarRequested = false; });
    6037             : 
    6038       16124 :     AddArg("progress", 0, _("Display progress bar"), &m_progressBarRequested)
    6039        8062 :         .SetHidden();
    6040        8062 : }
    6041             : 
    6042             : /************************************************************************/
    6043             : /*                         GDALAlgorithm::Run()                         */
    6044             : /************************************************************************/
    6045             : 
    6046        4421 : bool GDALAlgorithm::Run(GDALProgressFunc pfnProgress, void *pProgressData)
    6047             : {
    6048        4421 :     WarnIfDeprecated();
    6049             : 
    6050        4421 :     if (m_selectedSubAlg)
    6051             :     {
    6052         388 :         if (m_calledFromCommandLine)
    6053         233 :             m_selectedSubAlg->m_calledFromCommandLine = true;
    6054         388 :         return m_selectedSubAlg->Run(pfnProgress, pProgressData);
    6055             :     }
    6056             : 
    6057        4033 :     if (m_helpRequested || m_helpDocRequested)
    6058             :     {
    6059          16 :         if (m_calledFromCommandLine)
    6060          16 :             printf("%s", GetUsageForCLI(false).c_str()); /*ok*/
    6061          16 :         return true;
    6062             :     }
    6063             : 
    6064        4017 :     if (m_JSONUsageRequested)
    6065             :     {
    6066           3 :         if (m_calledFromCommandLine)
    6067           3 :             printf("%s", GetUsageAsJSON().c_str()); /*ok*/
    6068           3 :         return true;
    6069             :     }
    6070             : 
    6071        4014 :     if (!ValidateArguments())
    6072         112 :         return false;
    6073             : 
    6074        3902 :     switch (ProcessGDALGOutput())
    6075             :     {
    6076           0 :         case ProcessGDALGOutputRet::GDALG_ERROR:
    6077           0 :             return false;
    6078             : 
    6079          11 :         case ProcessGDALGOutputRet::GDALG_OK:
    6080          11 :             return true;
    6081             : 
    6082        3891 :         case ProcessGDALGOutputRet::NOT_GDALG:
    6083        3891 :             break;
    6084             :     }
    6085             : 
    6086        3891 :     if (m_executionForStreamOutput)
    6087             :     {
    6088          82 :         if (!CheckSafeForStreamOutput())
    6089             :         {
    6090           4 :             return false;
    6091             :         }
    6092             :     }
    6093             : 
    6094        3887 :     return RunImpl(pfnProgress, pProgressData);
    6095             : }
    6096             : 
    6097             : /************************************************************************/
    6098             : /*              GDALAlgorithm::CheckSafeForStreamOutput()               */
    6099             : /************************************************************************/
    6100             : 
    6101          37 : bool GDALAlgorithm::CheckSafeForStreamOutput()
    6102             : {
    6103          37 :     const auto outputFormatArg = GetArg(GDAL_ARG_NAME_OUTPUT_FORMAT);
    6104          37 :     if (outputFormatArg && outputFormatArg->GetType() == GAAT_STRING)
    6105             :     {
    6106          37 :         const auto &val = outputFormatArg->GDALAlgorithmArg::Get<std::string>();
    6107          37 :         if (!EQUAL(val.c_str(), "stream"))
    6108             :         {
    6109             :             // For security reasons, to avoid that reading a .gdalg.json file
    6110             :             // writes a file on the file system.
    6111           4 :             ReportError(
    6112             :                 CE_Failure, CPLE_NotSupported,
    6113             :                 "in streamed execution, --format stream should be used");
    6114           4 :             return false;
    6115             :         }
    6116             :     }
    6117          33 :     return true;
    6118             : }
    6119             : 
    6120             : /************************************************************************/
    6121             : /*                      GDALAlgorithm::Finalize()                       */
    6122             : /************************************************************************/
    6123             : 
    6124        1631 : bool GDALAlgorithm::Finalize()
    6125             : {
    6126        1631 :     bool ret = true;
    6127        1631 :     if (m_selectedSubAlg)
    6128         239 :         ret = m_selectedSubAlg->Finalize();
    6129             : 
    6130       29706 :     for (auto &arg : m_args)
    6131             :     {
    6132       28075 :         if (arg->GetType() == GAAT_DATASET)
    6133             :         {
    6134        1312 :             ret = arg->Get<GDALArgDatasetValue>().Close() && ret;
    6135             :         }
    6136       26763 :         else if (arg->GetType() == GAAT_DATASET_LIST)
    6137             :         {
    6138        2496 :             for (auto &ds : arg->Get<std::vector<GDALArgDatasetValue>>())
    6139             :             {
    6140        1180 :                 ret = ds.Close() && ret;
    6141             :             }
    6142             :         }
    6143             :     }
    6144        1631 :     return ret;
    6145             : }
    6146             : 
    6147             : /************************************************************************/
    6148             : /*                  GDALAlgorithm::GetArgNamesForCLI()                  */
    6149             : /************************************************************************/
    6150             : 
    6151             : std::pair<std::vector<std::pair<GDALAlgorithmArg *, std::string>>, size_t>
    6152         691 : GDALAlgorithm::GetArgNamesForCLI() const
    6153             : {
    6154        1382 :     std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6155             : 
    6156         691 :     size_t maxOptLen = 0;
    6157        8614 :     for (const auto &arg : m_args)
    6158             :     {
    6159        7923 :         if (arg->IsHidden() || arg->IsHiddenForCLI())
    6160        1703 :             continue;
    6161        6220 :         std::string opt;
    6162        6220 :         bool addComma = false;
    6163        6220 :         if (!arg->GetShortName().empty())
    6164             :         {
    6165        1429 :             opt += '-';
    6166        1429 :             opt += arg->GetShortName();
    6167        1429 :             addComma = true;
    6168             :         }
    6169        6220 :         for (char alias : arg->GetShortNameAliases())
    6170             :         {
    6171           0 :             if (addComma)
    6172           0 :                 opt += ", ";
    6173           0 :             opt += "-";
    6174           0 :             opt += alias;
    6175           0 :             addComma = true;
    6176             :         }
    6177        6970 :         for (const std::string &alias : arg->GetAliases())
    6178             :         {
    6179         750 :             if (addComma)
    6180         322 :                 opt += ", ";
    6181         750 :             opt += "--";
    6182         750 :             opt += alias;
    6183         750 :             addComma = true;
    6184             :         }
    6185        6220 :         if (!arg->GetName().empty())
    6186             :         {
    6187        6220 :             if (addComma)
    6188        1857 :                 opt += ", ";
    6189        6220 :             opt += "--";
    6190        6220 :             opt += arg->GetName();
    6191             :         }
    6192        6220 :         const auto &metaVar = arg->GetMetaVar();
    6193        6220 :         if (!metaVar.empty())
    6194             :         {
    6195        3878 :             opt += ' ';
    6196        3878 :             if (metaVar.front() != '<')
    6197        2761 :                 opt += '<';
    6198        3878 :             opt += metaVar;
    6199        3878 :             if (metaVar.back() != '>')
    6200        2785 :                 opt += '>';
    6201             :         }
    6202        6220 :         maxOptLen = std::max(maxOptLen, opt.size());
    6203        6220 :         options.emplace_back(arg.get(), opt);
    6204             :     }
    6205             : 
    6206        1382 :     return std::make_pair(std::move(options), maxOptLen);
    6207             : }
    6208             : 
    6209             : /************************************************************************/
    6210             : /*                   GDALAlgorithm::GetUsageForCLI()                    */
    6211             : /************************************************************************/
    6212             : 
    6213             : std::string
    6214         410 : GDALAlgorithm::GetUsageForCLI(bool shortUsage,
    6215             :                               const UsageOptions &usageOptions) const
    6216             : {
    6217         410 :     if (m_selectedSubAlg)
    6218           7 :         return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
    6219             : 
    6220         806 :     std::string osRet(usageOptions.isPipelineStep ? "*" : "Usage:");
    6221         806 :     std::string osPath;
    6222         811 :     for (const std::string &s : m_callPath)
    6223             :     {
    6224         408 :         if (!osPath.empty())
    6225          49 :             osPath += ' ';
    6226         408 :         osPath += s;
    6227             :     }
    6228         403 :     osRet += ' ';
    6229         403 :     osRet += osPath;
    6230             : 
    6231         403 :     bool hasNonPositionals = false;
    6232        4988 :     for (const auto &arg : m_args)
    6233             :     {
    6234        4585 :         if (!arg->IsHidden() && !arg->IsHiddenForCLI() && !arg->IsPositional())
    6235        3288 :             hasNonPositionals = true;
    6236             :     }
    6237             : 
    6238         403 :     if (HasSubAlgorithms())
    6239             :     {
    6240           9 :         if (m_callPath.size() == 1)
    6241             :         {
    6242           8 :             osRet += " <COMMAND>";
    6243           8 :             if (hasNonPositionals)
    6244           8 :                 osRet += " [OPTIONS]";
    6245           8 :             if (usageOptions.isPipelineStep)
    6246             :             {
    6247           5 :                 const size_t nLenFirstLine = osRet.size();
    6248           5 :                 osRet += '\n';
    6249           5 :                 osRet.append(nLenFirstLine, '-');
    6250           5 :                 osRet += '\n';
    6251             :             }
    6252           8 :             osRet += "\nwhere <COMMAND> is one of:\n";
    6253             :         }
    6254             :         else
    6255             :         {
    6256           1 :             osRet += " <SUBCOMMAND>";
    6257           1 :             if (hasNonPositionals)
    6258           1 :                 osRet += " [OPTIONS]";
    6259           1 :             if (usageOptions.isPipelineStep)
    6260             :             {
    6261           0 :                 const size_t nLenFirstLine = osRet.size();
    6262           0 :                 osRet += '\n';
    6263           0 :                 osRet.append(nLenFirstLine, '-');
    6264           0 :                 osRet += '\n';
    6265             :             }
    6266           1 :             osRet += "\nwhere <SUBCOMMAND> is one of:\n";
    6267             :         }
    6268           9 :         size_t maxNameLen = 0;
    6269          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6270             :         {
    6271          43 :             maxNameLen = std::max(maxNameLen, subAlgName.size());
    6272             :         }
    6273          52 :         for (const auto &subAlgName : GetSubAlgorithmNames())
    6274             :         {
    6275          86 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6276          43 :             if (subAlg && !subAlg->IsHidden())
    6277             :             {
    6278          43 :                 const std::string &name(subAlg->GetName());
    6279          43 :                 osRet += "  - ";
    6280          43 :                 osRet += name;
    6281          43 :                 osRet += ": ";
    6282          43 :                 osRet.append(maxNameLen - name.size(), ' ');
    6283          43 :                 osRet += subAlg->GetDescription();
    6284          43 :                 if (!subAlg->m_aliases.empty())
    6285             :                 {
    6286           6 :                     bool first = true;
    6287           6 :                     for (const auto &alias : subAlg->GetAliases())
    6288             :                     {
    6289           6 :                         if (alias ==
    6290             :                             GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR)
    6291           6 :                             break;
    6292           0 :                         if (first)
    6293           0 :                             osRet += " (alias: ";
    6294             :                         else
    6295           0 :                             osRet += ", ";
    6296           0 :                         osRet += alias;
    6297           0 :                         first = false;
    6298             :                     }
    6299           6 :                     if (!first)
    6300             :                     {
    6301           0 :                         osRet += ')';
    6302             :                     }
    6303             :                 }
    6304          43 :                 osRet += '\n';
    6305             :             }
    6306             :         }
    6307             : 
    6308           9 :         if (shortUsage && hasNonPositionals)
    6309             :         {
    6310           2 :             osRet += "\nTry '";
    6311           2 :             osRet += osPath;
    6312           2 :             osRet += " --help' for help.\n";
    6313             :         }
    6314             :     }
    6315             :     else
    6316             :     {
    6317         394 :         if (!m_args.empty())
    6318             :         {
    6319         394 :             if (hasNonPositionals)
    6320         394 :                 osRet += " [OPTIONS]";
    6321         582 :             for (const auto *arg : m_positionalArgs)
    6322             :             {
    6323         261 :                 if ((!arg->IsHidden() && !arg->IsHiddenForCLI()) ||
    6324          73 :                     (GetName() == "pipeline" && arg->GetName() == "pipeline"))
    6325             :                 {
    6326             :                     const bool optional =
    6327         199 :                         (!arg->IsRequired() && !(GetName() == "pipeline" &&
    6328          28 :                                                  arg->GetName() == "pipeline"));
    6329         171 :                     osRet += ' ';
    6330         171 :                     if (optional)
    6331          25 :                         osRet += '[';
    6332         171 :                     const std::string &metavar = arg->GetMetaVar();
    6333         171 :                     if (!metavar.empty() && metavar[0] == '<')
    6334             :                     {
    6335           4 :                         osRet += metavar;
    6336             :                     }
    6337             :                     else
    6338             :                     {
    6339         167 :                         osRet += '<';
    6340         167 :                         osRet += metavar;
    6341         167 :                         osRet += '>';
    6342             :                     }
    6343         213 :                     if (arg->GetType() == GAAT_DATASET_LIST &&
    6344          42 :                         arg->GetMaxCount() > 1)
    6345             :                     {
    6346          28 :                         osRet += "...";
    6347             :                     }
    6348         171 :                     if (optional)
    6349          25 :                         osRet += ']';
    6350             :                 }
    6351             :             }
    6352             :         }
    6353             : 
    6354         394 :         const size_t nLenFirstLine = osRet.size();
    6355         394 :         osRet += '\n';
    6356         394 :         if (usageOptions.isPipelineStep)
    6357             :         {
    6358         309 :             osRet.append(nLenFirstLine, '-');
    6359         309 :             osRet += '\n';
    6360             :         }
    6361             : 
    6362         394 :         if (shortUsage)
    6363             :         {
    6364          21 :             osRet += "Try '";
    6365          21 :             osRet += osPath;
    6366          21 :             osRet += " --help' for help.\n";
    6367          21 :             return osRet;
    6368             :         }
    6369             : 
    6370         373 :         osRet += '\n';
    6371         373 :         osRet += m_description;
    6372         373 :         osRet += '\n';
    6373             :     }
    6374             : 
    6375         382 :     if (!m_args.empty() && !shortUsage)
    6376             :     {
    6377         760 :         std::vector<std::pair<GDALAlgorithmArg *, std::string>> options;
    6378             :         size_t maxOptLen;
    6379         380 :         std::tie(options, maxOptLen) = GetArgNamesForCLI();
    6380         380 :         if (usageOptions.maxOptLen)
    6381         311 :             maxOptLen = usageOptions.maxOptLen;
    6382             : 
    6383         760 :         const std::string userProvidedOpt = "--<user-provided-option>=<value>";
    6384         380 :         if (m_arbitraryLongNameArgsAllowed)
    6385           2 :             maxOptLen = std::max(maxOptLen, userProvidedOpt.size());
    6386             : 
    6387             :         const auto OutputArg =
    6388        2319 :             [this, maxOptLen, &osRet](const GDALAlgorithmArg *arg,
    6389       20226 :                                       const std::string &opt)
    6390             :         {
    6391        2319 :             osRet += "  ";
    6392        2319 :             osRet += opt;
    6393        2319 :             osRet += "  ";
    6394        2319 :             osRet.append(maxOptLen - opt.size(), ' ');
    6395        2319 :             osRet += arg->GetDescription();
    6396             : 
    6397        2319 :             const auto &choices = arg->GetChoices();
    6398        2319 :             if (!choices.empty())
    6399             :             {
    6400         209 :                 osRet += ". ";
    6401         209 :                 osRet += arg->GetMetaVar();
    6402         209 :                 osRet += '=';
    6403         209 :                 bool firstChoice = true;
    6404        1665 :                 for (const auto &choice : choices)
    6405             :                 {
    6406        1456 :                     if (!firstChoice)
    6407        1247 :                         osRet += '|';
    6408        1456 :                     osRet += choice;
    6409        1456 :                     firstChoice = false;
    6410             :                 }
    6411             :             }
    6412             : 
    6413        4572 :             if (arg->GetType() == GAAT_DATASET ||
    6414        2253 :                 arg->GetType() == GAAT_DATASET_LIST)
    6415             :             {
    6416         132 :                 if (arg->GetDatasetInputFlags() == GADV_NAME &&
    6417          17 :                     arg->GetDatasetOutputFlags() == GADV_OBJECT)
    6418             :                 {
    6419           9 :                     osRet += " (created by algorithm)";
    6420             :                 }
    6421             :             }
    6422             : 
    6423        2319 :             if (arg->GetType() == GAAT_STRING && arg->HasDefaultValue())
    6424             :             {
    6425         175 :                 osRet += " (default: ";
    6426         175 :                 osRet += arg->GetDefault<std::string>();
    6427         175 :                 osRet += ')';
    6428             :             }
    6429        2144 :             else if (arg->GetType() == GAAT_BOOLEAN && arg->HasDefaultValue())
    6430             :             {
    6431          66 :                 if (arg->GetDefault<bool>())
    6432           0 :                     osRet += " (default: true)";
    6433             :             }
    6434        2078 :             else if (arg->GetType() == GAAT_INTEGER && arg->HasDefaultValue())
    6435             :             {
    6436          76 :                 osRet += " (default: ";
    6437          76 :                 osRet += CPLSPrintf("%d", arg->GetDefault<int>());
    6438          76 :                 osRet += ')';
    6439             :             }
    6440        2002 :             else if (arg->GetType() == GAAT_REAL && arg->HasDefaultValue())
    6441             :             {
    6442          49 :                 osRet += " (default: ";
    6443          49 :                 osRet += CPLSPrintf("%g", arg->GetDefault<double>());
    6444          49 :                 osRet += ')';
    6445             :             }
    6446        2351 :             else if (arg->GetType() == GAAT_STRING_LIST &&
    6447         398 :                      arg->HasDefaultValue())
    6448             :             {
    6449             :                 const auto &defaultVal =
    6450           9 :                     arg->GetDefault<std::vector<std::string>>();
    6451           9 :                 if (defaultVal.size() == 1)
    6452             :                 {
    6453           9 :                     osRet += " (default: ";
    6454           9 :                     osRet += defaultVal[0];
    6455           9 :                     osRet += ')';
    6456             :                 }
    6457             :             }
    6458        1971 :             else if (arg->GetType() == GAAT_INTEGER_LIST &&
    6459          27 :                      arg->HasDefaultValue())
    6460             :             {
    6461           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<int>>();
    6462           0 :                 if (defaultVal.size() == 1)
    6463             :                 {
    6464           0 :                     osRet += " (default: ";
    6465           0 :                     osRet += CPLSPrintf("%d", defaultVal[0]);
    6466           0 :                     osRet += ')';
    6467             :                 }
    6468             :             }
    6469        1944 :             else if (arg->GetType() == GAAT_REAL_LIST && arg->HasDefaultValue())
    6470             :             {
    6471           0 :                 const auto &defaultVal = arg->GetDefault<std::vector<double>>();
    6472           0 :                 if (defaultVal.size() == 1)
    6473             :                 {
    6474           0 :                     osRet += " (default: ";
    6475           0 :                     osRet += CPLSPrintf("%g", defaultVal[0]);
    6476           0 :                     osRet += ')';
    6477             :                 }
    6478             :             }
    6479             : 
    6480        2319 :             if (arg->GetDisplayHintAboutRepetition())
    6481             :             {
    6482        2352 :                 if (arg->GetMinCount() > 0 &&
    6483          92 :                     arg->GetMinCount() == arg->GetMaxCount())
    6484             :                 {
    6485          18 :                     if (arg->GetMinCount() != 1)
    6486           5 :                         osRet += CPLSPrintf(" [%d values]", arg->GetMaxCount());
    6487             :                 }
    6488        2316 :                 else if (arg->GetMinCount() > 0 &&
    6489          74 :                          arg->GetMaxCount() < GDALAlgorithmArgDecl::UNBOUNDED)
    6490             :                 {
    6491             :                     osRet += CPLSPrintf(" [%d..%d values]", arg->GetMinCount(),
    6492           8 :                                         arg->GetMaxCount());
    6493             :                 }
    6494        2234 :                 else if (arg->GetMinCount() > 0)
    6495             :                 {
    6496          66 :                     osRet += CPLSPrintf(" [%d.. values]", arg->GetMinCount());
    6497             :                 }
    6498        2168 :                 else if (arg->GetMaxCount() > 1)
    6499             :                 {
    6500         387 :                     osRet += " [may be repeated]";
    6501             :                 }
    6502             :             }
    6503             : 
    6504        2319 :             if (arg->IsRequired())
    6505             :             {
    6506         169 :                 osRet += " [required]";
    6507             :             }
    6508             : 
    6509        2319 :             osRet += '\n';
    6510             : 
    6511        2319 :             const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6512        2319 :             if (!mutualExclusionGroup.empty())
    6513             :             {
    6514         410 :                 std::string otherArgs;
    6515        3914 :                 for (const auto &otherArg : m_args)
    6516             :                 {
    6517        6762 :                     if (otherArg->IsHidden() || otherArg->IsHiddenForCLI() ||
    6518        3053 :                         otherArg.get() == arg)
    6519         861 :                         continue;
    6520        2848 :                     if (otherArg->GetMutualExclusionGroup() ==
    6521             :                         mutualExclusionGroup)
    6522             :                     {
    6523         278 :                         if (!otherArgs.empty())
    6524          77 :                             otherArgs += ", ";
    6525         278 :                         otherArgs += "--";
    6526         278 :                         otherArgs += otherArg->GetName();
    6527             :                     }
    6528             :                 }
    6529         205 :                 if (!otherArgs.empty())
    6530             :                 {
    6531         201 :                     osRet += "  ";
    6532         201 :                     osRet += "  ";
    6533         201 :                     osRet.append(maxOptLen, ' ');
    6534         201 :                     osRet += "Mutually exclusive with ";
    6535         201 :                     osRet += otherArgs;
    6536         201 :                     osRet += '\n';
    6537             :                 }
    6538             :             }
    6539        2319 :         };
    6540             : 
    6541         380 :         if (!m_positionalArgs.empty())
    6542             :         {
    6543         150 :             osRet += "\nPositional arguments:\n";
    6544        1601 :             for (const auto &[arg, opt] : options)
    6545             :             {
    6546        1451 :                 if (arg->IsPositional())
    6547         141 :                     OutputArg(arg, opt);
    6548             :             }
    6549             :         }
    6550             : 
    6551         380 :         if (hasNonPositionals)
    6552             :         {
    6553         380 :             bool hasCommon = false;
    6554         380 :             bool hasBase = false;
    6555         380 :             bool hasAdvanced = false;
    6556         380 :             bool hasEsoteric = false;
    6557         760 :             std::vector<std::string> categories;
    6558        3670 :             for (const auto &iter : options)
    6559             :             {
    6560        3290 :                 const auto &arg = iter.first;
    6561        3290 :                 if (!arg->IsPositional())
    6562             :                 {
    6563        3149 :                     const auto &category = arg->GetCategory();
    6564        3149 :                     if (category == GAAC_COMMON)
    6565             :                     {
    6566        1183 :                         hasCommon = true;
    6567             :                     }
    6568        1966 :                     else if (category == GAAC_BASE)
    6569             :                     {
    6570        1737 :                         hasBase = true;
    6571             :                     }
    6572         229 :                     else if (category == GAAC_ADVANCED)
    6573             :                     {
    6574         178 :                         hasAdvanced = true;
    6575             :                     }
    6576          51 :                     else if (category == GAAC_ESOTERIC)
    6577             :                     {
    6578          18 :                         hasEsoteric = true;
    6579             :                     }
    6580          33 :                     else if (std::find(categories.begin(), categories.end(),
    6581          33 :                                        category) == categories.end())
    6582             :                     {
    6583           9 :                         categories.push_back(category);
    6584             :                     }
    6585             :                 }
    6586             :             }
    6587         380 :             if (hasAdvanced || m_arbitraryLongNameArgsAllowed)
    6588          67 :                 categories.insert(categories.begin(), GAAC_ADVANCED);
    6589         380 :             if (hasBase)
    6590         334 :                 categories.insert(categories.begin(), GAAC_BASE);
    6591         380 :             if (hasCommon && !usageOptions.isPipelineStep)
    6592          66 :                 categories.insert(categories.begin(), GAAC_COMMON);
    6593         380 :             if (hasEsoteric)
    6594           6 :                 categories.push_back(GAAC_ESOTERIC);
    6595             : 
    6596         862 :             for (const auto &category : categories)
    6597             :             {
    6598         482 :                 osRet += "\n";
    6599         482 :                 if (category != GAAC_BASE)
    6600             :                 {
    6601         148 :                     osRet += category;
    6602         148 :                     osRet += ' ';
    6603             :                 }
    6604         482 :                 osRet += "Options:\n";
    6605        5193 :                 for (const auto &[arg, opt] : options)
    6606             :                 {
    6607        4711 :                     if (!arg->IsPositional() && arg->GetCategory() == category)
    6608        2178 :                         OutputArg(arg, opt);
    6609             :                 }
    6610         482 :                 if (m_arbitraryLongNameArgsAllowed && category == GAAC_ADVANCED)
    6611             :                 {
    6612           2 :                     osRet += "  ";
    6613           2 :                     osRet += userProvidedOpt;
    6614           2 :                     osRet += "  ";
    6615           2 :                     if (userProvidedOpt.size() < maxOptLen)
    6616           0 :                         osRet.append(maxOptLen - userProvidedOpt.size(), ' ');
    6617           2 :                     osRet += "Argument provided by user";
    6618           2 :                     osRet += '\n';
    6619             :                 }
    6620             :             }
    6621             :         }
    6622             :     }
    6623             : 
    6624         382 :     if (!m_longDescription.empty())
    6625             :     {
    6626           6 :         osRet += '\n';
    6627           6 :         osRet += m_longDescription;
    6628           6 :         osRet += '\n';
    6629             :     }
    6630             : 
    6631         382 :     if (!m_helpDocRequested && !usageOptions.isPipelineMain)
    6632             :     {
    6633         369 :         if (!m_helpURL.empty())
    6634             :         {
    6635         369 :             osRet += "\nFor more details, consult ";
    6636         369 :             osRet += GetHelpFullURL();
    6637         369 :             osRet += '\n';
    6638             :         }
    6639         369 :         osRet += GetUsageForCLIEnd();
    6640             :     }
    6641             : 
    6642         382 :     return osRet;
    6643             : }
    6644             : 
    6645             : /************************************************************************/
    6646             : /*                  GDALAlgorithm::GetUsageForCLIEnd()                  */
    6647             : /************************************************************************/
    6648             : 
    6649             : //! @cond Doxygen_Suppress
    6650         376 : std::string GDALAlgorithm::GetUsageForCLIEnd() const
    6651             : {
    6652         376 :     std::string osRet;
    6653             : 
    6654         376 :     if (!m_callPath.empty() && m_callPath[0] == "gdal")
    6655             :     {
    6656             :         osRet += "\nWARNING: the gdal command is provisionally provided as an "
    6657             :                  "alternative interface to GDAL and OGR command line "
    6658             :                  "utilities.\nThe project reserves the right to modify, "
    6659             :                  "rename, reorganize, and change the behavior of the utility\n"
    6660             :                  "until it is officially frozen in a future feature release of "
    6661          13 :                  "GDAL.\n";
    6662             :     }
    6663         376 :     return osRet;
    6664             : }
    6665             : 
    6666             : //! @endcond
    6667             : 
    6668             : /************************************************************************/
    6669             : /*                   GDALAlgorithm::GetUsageAsJSON()                    */
    6670             : /************************************************************************/
    6671             : 
    6672         561 : std::string GDALAlgorithm::GetUsageAsJSON() const
    6673             : {
    6674        1122 :     CPLJSONDocument oDoc;
    6675        1122 :     auto oRoot = oDoc.GetRoot();
    6676             : 
    6677         561 :     if (m_displayInJSONUsage)
    6678             :     {
    6679         559 :         oRoot.Add("name", m_name);
    6680         559 :         CPLJSONArray jFullPath;
    6681        1160 :         for (const std::string &s : m_callPath)
    6682             :         {
    6683         601 :             jFullPath.Add(s);
    6684             :         }
    6685         559 :         oRoot.Add("full_path", jFullPath);
    6686             :     }
    6687             : 
    6688         561 :     oRoot.Add("description", m_description);
    6689         561 :     if (!m_helpURL.empty())
    6690             :     {
    6691         560 :         oRoot.Add("short_url", m_helpURL);
    6692         560 :         oRoot.Add("url", GetHelpFullURL());
    6693             :     }
    6694             : 
    6695        1122 :     CPLJSONArray jSubAlgorithms;
    6696         761 :     for (const auto &subAlgName : GetSubAlgorithmNames())
    6697             :     {
    6698         400 :         auto subAlg = InstantiateSubAlgorithm(subAlgName);
    6699         200 :         if (subAlg && subAlg->m_displayInJSONUsage && !subAlg->IsHidden())
    6700             :         {
    6701         198 :             CPLJSONDocument oSubDoc;
    6702         198 :             CPL_IGNORE_RET_VAL(oSubDoc.LoadMemory(subAlg->GetUsageAsJSON()));
    6703         198 :             jSubAlgorithms.Add(oSubDoc.GetRoot());
    6704             :         }
    6705             :     }
    6706         561 :     oRoot.Add("sub_algorithms", jSubAlgorithms);
    6707             : 
    6708         561 :     if (m_arbitraryLongNameArgsAllowed)
    6709             :     {
    6710           1 :         oRoot.Add("user_provided_arguments_allowed", true);
    6711             :     }
    6712             : 
    6713        5333 :     const auto ProcessArg = [](const GDALAlgorithmArg *arg)
    6714             :     {
    6715        5333 :         CPLJSONObject jArg;
    6716        5333 :         jArg.Add("name", arg->GetName());
    6717        5333 :         jArg.Add("type", GDALAlgorithmArgTypeName(arg->GetType()));
    6718        5333 :         jArg.Add("description", arg->GetDescription());
    6719             : 
    6720        5333 :         const auto &metaVar = arg->GetMetaVar();
    6721        5333 :         if (!metaVar.empty() && metaVar != CPLString(arg->GetName()).toupper())
    6722             :         {
    6723        1611 :             if (metaVar.front() == '<' && metaVar.back() == '>' &&
    6724        1611 :                 metaVar.substr(1, metaVar.size() - 2).find('>') ==
    6725             :                     std::string::npos)
    6726          32 :                 jArg.Add("metavar", metaVar.substr(1, metaVar.size() - 2));
    6727             :             else
    6728         834 :                 jArg.Add("metavar", metaVar);
    6729             :         }
    6730             : 
    6731        5333 :         const auto &choices = arg->GetChoices();
    6732        5333 :         if (!choices.empty())
    6733             :         {
    6734         389 :             CPLJSONArray jChoices;
    6735        3400 :             for (const auto &choice : choices)
    6736        3011 :                 jChoices.Add(choice);
    6737         389 :             jArg.Add("choices", jChoices);
    6738             :         }
    6739        5333 :         if (arg->HasDefaultValue())
    6740             :         {
    6741        1165 :             switch (arg->GetType())
    6742             :             {
    6743         420 :                 case GAAT_BOOLEAN:
    6744         420 :                     jArg.Add("default", arg->GetDefault<bool>());
    6745         420 :                     break;
    6746         348 :                 case GAAT_STRING:
    6747         348 :                     jArg.Add("default", arg->GetDefault<std::string>());
    6748         348 :                     break;
    6749         199 :                 case GAAT_INTEGER:
    6750         199 :                     jArg.Add("default", arg->GetDefault<int>());
    6751         199 :                     break;
    6752         178 :                 case GAAT_REAL:
    6753         178 :                     jArg.Add("default", arg->GetDefault<double>());
    6754         178 :                     break;
    6755          18 :                 case GAAT_STRING_LIST:
    6756             :                 {
    6757             :                     const auto &val =
    6758          18 :                         arg->GetDefault<std::vector<std::string>>();
    6759          18 :                     if (val.size() == 1)
    6760             :                     {
    6761          17 :                         jArg.Add("default", val[0]);
    6762             :                     }
    6763             :                     else
    6764             :                     {
    6765           1 :                         CPLJSONArray jArr;
    6766           3 :                         for (const auto &s : val)
    6767             :                         {
    6768           2 :                             jArr.Add(s);
    6769             :                         }
    6770           1 :                         jArg.Add("default", jArr);
    6771             :                     }
    6772          18 :                     break;
    6773             :                 }
    6774           1 :                 case GAAT_INTEGER_LIST:
    6775             :                 {
    6776           1 :                     const auto &val = arg->GetDefault<std::vector<int>>();
    6777           1 :                     if (val.size() == 1)
    6778             :                     {
    6779           0 :                         jArg.Add("default", val[0]);
    6780             :                     }
    6781             :                     else
    6782             :                     {
    6783           1 :                         CPLJSONArray jArr;
    6784           3 :                         for (int i : val)
    6785             :                         {
    6786           2 :                             jArr.Add(i);
    6787             :                         }
    6788           1 :                         jArg.Add("default", jArr);
    6789             :                     }
    6790           1 :                     break;
    6791             :                 }
    6792           1 :                 case GAAT_REAL_LIST:
    6793             :                 {
    6794           1 :                     const auto &val = arg->GetDefault<std::vector<double>>();
    6795           1 :                     if (val.size() == 1)
    6796             :                     {
    6797           0 :                         jArg.Add("default", val[0]);
    6798             :                     }
    6799             :                     else
    6800             :                     {
    6801           1 :                         CPLJSONArray jArr;
    6802           3 :                         for (double d : val)
    6803             :                         {
    6804           2 :                             jArr.Add(d);
    6805             :                         }
    6806           1 :                         jArg.Add("default", jArr);
    6807             :                     }
    6808           1 :                     break;
    6809             :                 }
    6810           0 :                 case GAAT_DATASET:
    6811             :                 case GAAT_DATASET_LIST:
    6812           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    6813             :                              "Unhandled default value for arg %s",
    6814           0 :                              arg->GetName().c_str());
    6815           0 :                     break;
    6816             :             }
    6817             :         }
    6818             : 
    6819        5333 :         const auto [minVal, minValIsIncluded] = arg->GetMinValue();
    6820        5333 :         if (!std::isnan(minVal))
    6821             :         {
    6822         668 :             if (arg->GetType() == GAAT_INTEGER ||
    6823         260 :                 arg->GetType() == GAAT_INTEGER_LIST)
    6824         171 :                 jArg.Add("min_value", static_cast<int>(minVal));
    6825             :             else
    6826         237 :                 jArg.Add("min_value", minVal);
    6827         408 :             jArg.Add("min_value_is_included", minValIsIncluded);
    6828             :         }
    6829             : 
    6830        5333 :         const auto [maxVal, maxValIsIncluded] = arg->GetMaxValue();
    6831        5333 :         if (!std::isnan(maxVal))
    6832             :         {
    6833         199 :             if (arg->GetType() == GAAT_INTEGER ||
    6834          82 :                 arg->GetType() == GAAT_INTEGER_LIST)
    6835          35 :                 jArg.Add("max_value", static_cast<int>(maxVal));
    6836             :             else
    6837          82 :                 jArg.Add("max_value", maxVal);
    6838         117 :             jArg.Add("max_value_is_included", maxValIsIncluded);
    6839             :         }
    6840             : 
    6841        5333 :         jArg.Add("required", arg->IsRequired());
    6842        5333 :         if (GDALAlgorithmArgTypeIsList(arg->GetType()))
    6843             :         {
    6844        1493 :             jArg.Add("packed_values_allowed", arg->GetPackedValuesAllowed());
    6845        1493 :             jArg.Add("repeated_arg_allowed", arg->GetRepeatedArgAllowed());
    6846        1493 :             jArg.Add("min_count", arg->GetMinCount());
    6847        1493 :             jArg.Add("max_count", arg->GetMaxCount());
    6848             :         }
    6849        5333 :         jArg.Add("category", arg->GetCategory());
    6850             : 
    6851       10418 :         if (arg->GetType() == GAAT_DATASET ||
    6852        5085 :             arg->GetType() == GAAT_DATASET_LIST)
    6853             :         {
    6854             :             {
    6855         449 :                 CPLJSONArray jAr;
    6856         449 :                 if (arg->GetDatasetType() & GDAL_OF_RASTER)
    6857         307 :                     jAr.Add("raster");
    6858         449 :                 if (arg->GetDatasetType() & GDAL_OF_VECTOR)
    6859         179 :                     jAr.Add("vector");
    6860         449 :                 if (arg->GetDatasetType() & GDAL_OF_MULTIDIM_RASTER)
    6861          28 :                     jAr.Add("multidim_raster");
    6862         449 :                 jArg.Add("dataset_type", jAr);
    6863             :             }
    6864             : 
    6865         614 :             const auto GetFlags = [](int flags)
    6866             :             {
    6867         614 :                 CPLJSONArray jAr;
    6868         614 :                 if (flags & GADV_NAME)
    6869         449 :                     jAr.Add("name");
    6870         614 :                 if (flags & GADV_OBJECT)
    6871         572 :                     jAr.Add("dataset");
    6872         614 :                 return jAr;
    6873             :             };
    6874             : 
    6875         449 :             if (arg->IsInput())
    6876             :             {
    6877         449 :                 jArg.Add("input_flags", GetFlags(arg->GetDatasetInputFlags()));
    6878             :             }
    6879         449 :             if (arg->IsOutput())
    6880             :             {
    6881         165 :                 jArg.Add("output_flags",
    6882         330 :                          GetFlags(arg->GetDatasetOutputFlags()));
    6883             :             }
    6884             :         }
    6885             : 
    6886        5333 :         const auto &mutualExclusionGroup = arg->GetMutualExclusionGroup();
    6887        5333 :         if (!mutualExclusionGroup.empty())
    6888             :         {
    6889         655 :             jArg.Add("mutual_exclusion_group", mutualExclusionGroup);
    6890             :         }
    6891             : 
    6892       10666 :         const auto &metadata = arg->GetMetadata();
    6893        5333 :         if (!metadata.empty())
    6894             :         {
    6895         444 :             CPLJSONObject jMetadata;
    6896         927 :             for (const auto &[key, values] : metadata)
    6897             :             {
    6898         966 :                 CPLJSONArray jValue;
    6899        1169 :                 for (const auto &value : values)
    6900         686 :                     jValue.Add(value);
    6901         483 :                 jMetadata.Add(key, jValue);
    6902             :             }
    6903         444 :             jArg.Add("metadata", jMetadata);
    6904             :         }
    6905             : 
    6906       10666 :         return jArg;
    6907             :     };
    6908             : 
    6909             :     {
    6910         561 :         CPLJSONArray jArgs;
    6911        8820 :         for (const auto &arg : m_args)
    6912             :         {
    6913        8259 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && !arg->IsOutput())
    6914        5111 :                 jArgs.Add(ProcessArg(arg.get()));
    6915             :         }
    6916         561 :         oRoot.Add("input_arguments", jArgs);
    6917             :     }
    6918             : 
    6919             :     {
    6920         561 :         CPLJSONArray jArgs;
    6921        8820 :         for (const auto &arg : m_args)
    6922             :         {
    6923        8259 :             if (!arg->IsHiddenForAPI() && !arg->IsInput() && arg->IsOutput())
    6924          57 :                 jArgs.Add(ProcessArg(arg.get()));
    6925             :         }
    6926         561 :         oRoot.Add("output_arguments", jArgs);
    6927             :     }
    6928             : 
    6929             :     {
    6930         561 :         CPLJSONArray jArgs;
    6931        8820 :         for (const auto &arg : m_args)
    6932             :         {
    6933        8259 :             if (!arg->IsHiddenForAPI() && arg->IsInput() && arg->IsOutput())
    6934         165 :                 jArgs.Add(ProcessArg(arg.get()));
    6935             :         }
    6936         561 :         oRoot.Add("input_output_arguments", jArgs);
    6937             :     }
    6938             : 
    6939         561 :     if (m_supportsStreamedOutput)
    6940             :     {
    6941         115 :         oRoot.Add("supports_streamed_output", true);
    6942             :     }
    6943             : 
    6944        1122 :     return oDoc.SaveAsString();
    6945             : }
    6946             : 
    6947             : /************************************************************************/
    6948             : /*                   GDALAlgorithm::GetAutoComplete()                   */
    6949             : /************************************************************************/
    6950             : 
    6951             : std::vector<std::string>
    6952         242 : GDALAlgorithm::GetAutoComplete(std::vector<std::string> &args,
    6953             :                                bool lastWordIsComplete, bool showAllOptions)
    6954             : {
    6955         484 :     std::vector<std::string> ret;
    6956             : 
    6957             :     // Get inner-most algorithm
    6958         242 :     std::unique_ptr<GDALAlgorithm> curAlgHolder;
    6959         242 :     GDALAlgorithm *curAlg = this;
    6960         479 :     while (!args.empty() && !args.front().empty() && args.front()[0] != '-')
    6961             :     {
    6962             :         auto subAlg = curAlg->InstantiateSubAlgorithm(
    6963         344 :             args.front(), /* suggestionAllowed = */ false);
    6964         344 :         if (!subAlg)
    6965         106 :             break;
    6966         238 :         if (args.size() == 1 && !lastWordIsComplete)
    6967             :         {
    6968           5 :             int nCount = 0;
    6969         115 :             for (const auto &subAlgName : curAlg->GetSubAlgorithmNames())
    6970             :             {
    6971         110 :                 if (STARTS_WITH(subAlgName.c_str(), args.front().c_str()))
    6972           6 :                     nCount++;
    6973             :             }
    6974           5 :             if (nCount >= 2)
    6975             :             {
    6976          11 :                 for (const std::string &subAlgName :
    6977          23 :                      curAlg->GetSubAlgorithmNames())
    6978             :                 {
    6979          11 :                     subAlg = curAlg->InstantiateSubAlgorithm(subAlgName);
    6980          11 :                     if (subAlg && !subAlg->IsHidden())
    6981          11 :                         ret.push_back(subAlg->GetName());
    6982             :                 }
    6983           1 :                 return ret;
    6984             :             }
    6985             :         }
    6986         237 :         showAllOptions = false;
    6987         237 :         args.erase(args.begin());
    6988         237 :         curAlgHolder = std::move(subAlg);
    6989         237 :         curAlg = curAlgHolder.get();
    6990             :     }
    6991         241 :     if (curAlg != this)
    6992             :     {
    6993         129 :         curAlg->m_calledFromCommandLine = m_calledFromCommandLine;
    6994             :         return curAlg->GetAutoComplete(args, lastWordIsComplete,
    6995         129 :                                        /* showAllOptions = */ false);
    6996             :     }
    6997             : 
    6998         224 :     std::string option;
    6999         224 :     std::string value;
    7000         112 :     ExtractLastOptionAndValue(args, option, value);
    7001             : 
    7002         139 :     if (option.empty() && !args.empty() && !args.back().empty() &&
    7003          27 :         args.back()[0] == '-')
    7004             :     {
    7005          24 :         const auto &lastArg = args.back();
    7006             :         // List available options
    7007         339 :         for (const auto &arg : GetArgs())
    7008             :         {
    7009         583 :             if (arg->IsHidden() || arg->IsHiddenForCLI() ||
    7010         531 :                 (!showAllOptions &&
    7011         720 :                  (arg->GetName() == "help" || arg->GetName() == "config" ||
    7012         434 :                   arg->GetName() == "version" ||
    7013         217 :                   arg->GetName() == "json-usage")))
    7014             :             {
    7015         116 :                 continue;
    7016             :             }
    7017         199 :             if (!arg->GetShortName().empty())
    7018             :             {
    7019         126 :                 std::string str = std::string("-").append(arg->GetShortName());
    7020          42 :                 if (lastArg == str)
    7021           0 :                     ret.push_back(std::move(str));
    7022             :             }
    7023         199 :             if (lastArg != "-" && lastArg != "--")
    7024             :             {
    7025          52 :                 for (const std::string &alias : arg->GetAliases())
    7026             :                 {
    7027          48 :                     std::string str = std::string("--").append(alias);
    7028          16 :                     if (cpl::starts_with(str, lastArg))
    7029           3 :                         ret.push_back(std::move(str));
    7030             :                 }
    7031             :             }
    7032         199 :             if (!arg->GetName().empty())
    7033             :             {
    7034         597 :                 std::string str = std::string("--").append(arg->GetName());
    7035         199 :                 if (cpl::starts_with(str, lastArg))
    7036         165 :                     ret.push_back(std::move(str));
    7037             :             }
    7038             :         }
    7039          24 :         std::sort(ret.begin(), ret.end());
    7040             :     }
    7041          88 :     else if (!option.empty())
    7042             :     {
    7043             :         // List possible choices for current option
    7044          82 :         auto arg = GetArg(option);
    7045          82 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7046             :         {
    7047          82 :             ret = arg->GetChoices();
    7048          82 :             if (ret.empty())
    7049             :             {
    7050             :                 {
    7051          77 :                     CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7052          77 :                     SetParseForAutoCompletion();
    7053          77 :                     CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7054             :                 }
    7055          77 :                 ret = arg->GetAutoCompleteChoices(value);
    7056             :             }
    7057             :             else
    7058             :             {
    7059           5 :                 std::sort(ret.begin(), ret.end());
    7060             :             }
    7061          82 :             if (!ret.empty() && ret.back() == value)
    7062             :             {
    7063           2 :                 ret.clear();
    7064             :             }
    7065          80 :             else if (ret.empty())
    7066             :             {
    7067           9 :                 ret.push_back("**");
    7068             :                 // Non printable UTF-8 space, to avoid autocompletion to pickup on 'd'
    7069          18 :                 ret.push_back(std::string("\xC2\xA0"
    7070             :                                           "description: ")
    7071           9 :                                   .append(arg->GetDescription()));
    7072             :             }
    7073             :         }
    7074             :     }
    7075             :     else
    7076             :     {
    7077             :         // List possible sub-algorithms
    7078          59 :         for (const std::string &subAlgName : GetSubAlgorithmNames())
    7079             :         {
    7080         106 :             auto subAlg = InstantiateSubAlgorithm(subAlgName);
    7081          53 :             if (subAlg && !subAlg->IsHidden())
    7082          53 :                 ret.push_back(subAlg->GetName());
    7083             :         }
    7084           6 :         if (!ret.empty())
    7085             :         {
    7086           2 :             std::sort(ret.begin(), ret.end());
    7087             :         }
    7088             : 
    7089             :         // Try filenames
    7090           6 :         if (ret.empty() && !args.empty())
    7091             :         {
    7092             :             {
    7093           3 :                 CPLErrorStateBackuper oErrorQuieter(CPLQuietErrorHandler);
    7094           3 :                 SetParseForAutoCompletion();
    7095           3 :                 CPL_IGNORE_RET_VAL(ParseCommandLineArguments(args));
    7096             :             }
    7097             : 
    7098           3 :             const std::string &lastArg = args.back();
    7099           3 :             GDALAlgorithmArg *arg = nullptr;
    7100          18 :             for (const char *name : {GDAL_ARG_NAME_INPUT, "dataset", "filename",
    7101          21 :                                      "like", "source", "destination"})
    7102             :             {
    7103          18 :                 if (!arg)
    7104             :                 {
    7105           3 :                     auto newArg = GetArg(name);
    7106           3 :                     if (newArg)
    7107             :                     {
    7108           3 :                         if (!newArg->IsExplicitlySet())
    7109             :                         {
    7110           0 :                             arg = newArg;
    7111             :                         }
    7112           6 :                         else if (newArg->GetType() == GAAT_STRING ||
    7113           5 :                                  newArg->GetType() == GAAT_STRING_LIST ||
    7114           8 :                                  newArg->GetType() == GAAT_DATASET ||
    7115           2 :                                  newArg->GetType() == GAAT_DATASET_LIST)
    7116             :                         {
    7117             :                             VSIStatBufL sStat;
    7118           5 :                             if ((!lastArg.empty() && lastArg.back() == '/') ||
    7119           2 :                                 VSIStatL(lastArg.c_str(), &sStat) != 0)
    7120             :                             {
    7121           3 :                                 arg = newArg;
    7122             :                             }
    7123             :                         }
    7124             :                     }
    7125             :                 }
    7126             :             }
    7127           3 :             if (arg)
    7128             :             {
    7129           3 :                 ret = arg->GetAutoCompleteChoices(lastArg);
    7130             :             }
    7131             :         }
    7132             :     }
    7133             : 
    7134         112 :     return ret;
    7135             : }
    7136             : 
    7137             : /************************************************************************/
    7138             : /*                   GDALAlgorithm::GetFieldIndices()                   */
    7139             : /************************************************************************/
    7140             : 
    7141          44 : bool GDALAlgorithm::GetFieldIndices(const std::vector<std::string> &names,
    7142             :                                     OGRLayerH hLayer, std::vector<int> &indices)
    7143             : {
    7144          44 :     VALIDATE_POINTER1(hLayer, __func__, false);
    7145             : 
    7146          44 :     const OGRLayer &layer = *OGRLayer::FromHandle(hLayer);
    7147             : 
    7148          44 :     if (names.size() == 1 && names[0] == "ALL")
    7149             :     {
    7150          12 :         const int nSrcFieldCount = layer.GetLayerDefn()->GetFieldCount();
    7151          28 :         for (int i = 0; i < nSrcFieldCount; ++i)
    7152             :         {
    7153          16 :             indices.push_back(i);
    7154             :         }
    7155             :     }
    7156          32 :     else if (!names.empty() && !(names.size() == 1 && names[0] == "NONE"))
    7157             :     {
    7158           6 :         std::set<int> fieldsAdded;
    7159          14 :         for (const std::string &osFieldName : names)
    7160             :         {
    7161             : 
    7162             :             const int nIdx =
    7163          10 :                 layer.GetLayerDefn()->GetFieldIndex(osFieldName.c_str());
    7164             : 
    7165          10 :             if (nIdx < 0)
    7166             :             {
    7167           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    7168             :                          "Field '%s' does not exist in layer '%s'",
    7169           2 :                          osFieldName.c_str(), layer.GetName());
    7170           2 :                 return false;
    7171             :             }
    7172             : 
    7173           8 :             if (fieldsAdded.insert(nIdx).second)
    7174             :             {
    7175           7 :                 indices.push_back(nIdx);
    7176             :             }
    7177             :         }
    7178             :     }
    7179             : 
    7180          42 :     return true;
    7181             : }
    7182             : 
    7183             : /************************************************************************/
    7184             : /*              GDALAlgorithm::ExtractLastOptionAndValue()              */
    7185             : /************************************************************************/
    7186             : 
    7187         112 : void GDALAlgorithm::ExtractLastOptionAndValue(std::vector<std::string> &args,
    7188             :                                               std::string &option,
    7189             :                                               std::string &value) const
    7190             : {
    7191         112 :     if (!args.empty() && !args.back().empty() && args.back()[0] == '-')
    7192             :     {
    7193          82 :         const auto nPosEqual = args.back().find('=');
    7194          82 :         if (nPosEqual == std::string::npos)
    7195             :         {
    7196             :             // Deal with "gdal ... --option"
    7197          63 :             if (GetArg(args.back()))
    7198             :             {
    7199          39 :                 option = args.back();
    7200          39 :                 args.pop_back();
    7201             :             }
    7202             :         }
    7203             :         else
    7204             :         {
    7205             :             // Deal with "gdal ... --option=<value>"
    7206          19 :             if (GetArg(args.back().substr(0, nPosEqual)))
    7207             :             {
    7208          19 :                 option = args.back().substr(0, nPosEqual);
    7209          19 :                 value = args.back().substr(nPosEqual + 1);
    7210          19 :                 args.pop_back();
    7211             :             }
    7212             :         }
    7213             :     }
    7214          55 :     else if (args.size() >= 2 && !args[args.size() - 2].empty() &&
    7215          25 :              args[args.size() - 2][0] == '-')
    7216             :     {
    7217             :         // Deal with "gdal ... --option <value>"
    7218          24 :         auto arg = GetArg(args[args.size() - 2]);
    7219          24 :         if (arg && arg->GetType() != GAAT_BOOLEAN)
    7220             :         {
    7221          24 :             option = args[args.size() - 2];
    7222          24 :             value = args.back();
    7223          24 :             args.pop_back();
    7224             :         }
    7225             :     }
    7226             : 
    7227         112 :     const auto IsKeyValueOption = [](const std::string &osStr)
    7228             :     {
    7229         302 :         return osStr == "--co" || osStr == "--creation-option" ||
    7230         279 :                osStr == "--lco" || osStr == "--layer-creation-option" ||
    7231         300 :                osStr == "--oo" || osStr == "--open-option";
    7232             :     };
    7233             : 
    7234         112 :     if (IsKeyValueOption(option))
    7235             :     {
    7236          22 :         const auto nPosEqual = value.find('=');
    7237          22 :         if (nPosEqual != std::string::npos)
    7238             :         {
    7239          11 :             value.resize(nPosEqual);
    7240             :         }
    7241             :     }
    7242         112 : }
    7243             : 
    7244             : //! @cond Doxygen_Suppress
    7245             : 
    7246             : /************************************************************************/
    7247             : /*                  GDALContainerAlgorithm::RunImpl()                   */
    7248             : /************************************************************************/
    7249             : 
    7250           0 : bool GDALContainerAlgorithm::RunImpl(GDALProgressFunc, void *)
    7251             : {
    7252           0 :     return false;
    7253             : }
    7254             : 
    7255             : //! @endcond
    7256             : 
    7257             : /************************************************************************/
    7258             : /*                        GDALAlgorithmRelease()                        */
    7259             : /************************************************************************/
    7260             : 
    7261             : /** Release a handle to an algorithm.
    7262             :  *
    7263             :  * @since 3.11
    7264             :  */
    7265       12136 : void GDALAlgorithmRelease(GDALAlgorithmH hAlg)
    7266             : {
    7267       12136 :     delete hAlg;
    7268       12136 : }
    7269             : 
    7270             : /************************************************************************/
    7271             : /*                        GDALAlgorithmGetName()                        */
    7272             : /************************************************************************/
    7273             : 
    7274             : /** Return the algorithm name.
    7275             :  *
    7276             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7277             :  * @return algorithm name whose lifetime is bound to hAlg and which must not
    7278             :  * be freed.
    7279             :  * @since 3.11
    7280             :  */
    7281        5698 : const char *GDALAlgorithmGetName(GDALAlgorithmH hAlg)
    7282             : {
    7283        5698 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7284        5698 :     return hAlg->ptr->GetName().c_str();
    7285             : }
    7286             : 
    7287             : /************************************************************************/
    7288             : /*                    GDALAlgorithmGetDescription()                     */
    7289             : /************************************************************************/
    7290             : 
    7291             : /** Return the algorithm (short) description.
    7292             :  *
    7293             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7294             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7295             :  * not be freed.
    7296             :  * @since 3.11
    7297             :  */
    7298        5619 : const char *GDALAlgorithmGetDescription(GDALAlgorithmH hAlg)
    7299             : {
    7300        5619 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7301        5619 :     return hAlg->ptr->GetDescription().c_str();
    7302             : }
    7303             : 
    7304             : /************************************************************************/
    7305             : /*                  GDALAlgorithmGetLongDescription()                   */
    7306             : /************************************************************************/
    7307             : 
    7308             : /** Return the algorithm (longer) description.
    7309             :  *
    7310             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7311             :  * @return algorithm description whose lifetime is bound to hAlg and which must
    7312             :  * not be freed.
    7313             :  * @since 3.11
    7314             :  */
    7315           2 : const char *GDALAlgorithmGetLongDescription(GDALAlgorithmH hAlg)
    7316             : {
    7317           2 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7318           2 :     return hAlg->ptr->GetLongDescription().c_str();
    7319             : }
    7320             : 
    7321             : /************************************************************************/
    7322             : /*                    GDALAlgorithmGetHelpFullURL()                     */
    7323             : /************************************************************************/
    7324             : 
    7325             : /** Return the algorithm full URL.
    7326             :  *
    7327             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7328             :  * @return algorithm URL whose lifetime is bound to hAlg and which must
    7329             :  * not be freed.
    7330             :  * @since 3.11
    7331             :  */
    7332        4963 : const char *GDALAlgorithmGetHelpFullURL(GDALAlgorithmH hAlg)
    7333             : {
    7334        4963 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7335        4963 :     return hAlg->ptr->GetHelpFullURL().c_str();
    7336             : }
    7337             : 
    7338             : /************************************************************************/
    7339             : /*                   GDALAlgorithmHasSubAlgorithms()                    */
    7340             : /************************************************************************/
    7341             : 
    7342             : /** Return whether the algorithm has sub-algorithms.
    7343             :  *
    7344             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7345             :  * @since 3.11
    7346             :  */
    7347        9024 : bool GDALAlgorithmHasSubAlgorithms(GDALAlgorithmH hAlg)
    7348             : {
    7349        9024 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7350        9024 :     return hAlg->ptr->HasSubAlgorithms();
    7351             : }
    7352             : 
    7353             : /************************************************************************/
    7354             : /*                 GDALAlgorithmGetSubAlgorithmNames()                  */
    7355             : /************************************************************************/
    7356             : 
    7357             : /** Get the names of registered algorithms.
    7358             :  *
    7359             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7360             :  * @return a NULL terminated list of names, which must be destroyed with
    7361             :  * CSLDestroy()
    7362             :  * @since 3.11
    7363             :  */
    7364         707 : char **GDALAlgorithmGetSubAlgorithmNames(GDALAlgorithmH hAlg)
    7365             : {
    7366         707 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7367         707 :     return CPLStringList(hAlg->ptr->GetSubAlgorithmNames()).StealList();
    7368             : }
    7369             : 
    7370             : /************************************************************************/
    7371             : /*                GDALAlgorithmInstantiateSubAlgorithm()                */
    7372             : /************************************************************************/
    7373             : 
    7374             : /** Instantiate an algorithm by its name (or its alias).
    7375             :  *
    7376             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7377             :  * @param pszSubAlgName Algorithm name. Must NOT be null.
    7378             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease),
    7379             :  * or NULL if the algorithm does not exist or another error occurred.
    7380             :  * @since 3.11
    7381             :  */
    7382        8468 : GDALAlgorithmH GDALAlgorithmInstantiateSubAlgorithm(GDALAlgorithmH hAlg,
    7383             :                                                     const char *pszSubAlgName)
    7384             : {
    7385        8468 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7386        8468 :     VALIDATE_POINTER1(pszSubAlgName, __func__, nullptr);
    7387       16936 :     auto subAlg = hAlg->ptr->InstantiateSubAlgorithm(pszSubAlgName);
    7388             :     return subAlg
    7389       16936 :                ? std::make_unique<GDALAlgorithmHS>(std::move(subAlg)).release()
    7390       16936 :                : nullptr;
    7391             : }
    7392             : 
    7393             : /************************************************************************/
    7394             : /*               GDALAlgorithmParseCommandLineArguments()               */
    7395             : /************************************************************************/
    7396             : 
    7397             : /** Parse a command line argument, which does not include the algorithm
    7398             :  * name, to set the value of corresponding arguments.
    7399             :  *
    7400             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7401             :  * @param papszArgs NULL-terminated list of arguments, not including the algorithm name.
    7402             :  * @return true if successful, false otherwise
    7403             :  * @since 3.11
    7404             :  */
    7405             : 
    7406         352 : bool GDALAlgorithmParseCommandLineArguments(GDALAlgorithmH hAlg,
    7407             :                                             CSLConstList papszArgs)
    7408             : {
    7409         352 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7410         352 :     return hAlg->ptr->ParseCommandLineArguments(CPLStringList(papszArgs));
    7411             : }
    7412             : 
    7413             : /************************************************************************/
    7414             : /*                  GDALAlgorithmGetActualAlgorithm()                   */
    7415             : /************************************************************************/
    7416             : 
    7417             : /** Return the actual algorithm that is going to be invoked, when the
    7418             :  * current algorithm has sub-algorithms.
    7419             :  *
    7420             :  * Only valid after GDALAlgorithmParseCommandLineArguments() has been called.
    7421             :  *
    7422             :  * Note that the lifetime of the returned algorithm does not exceed the one of
    7423             :  * the hAlg instance that owns it.
    7424             :  *
    7425             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7426             :  * @return an handle to the algorithm (to be freed with GDALAlgorithmRelease).
    7427             :  * @since 3.11
    7428             :  */
    7429         913 : GDALAlgorithmH GDALAlgorithmGetActualAlgorithm(GDALAlgorithmH hAlg)
    7430             : {
    7431         913 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7432         913 :     return GDALAlgorithmHS::FromRef(hAlg->ptr->GetActualAlgorithm()).release();
    7433             : }
    7434             : 
    7435             : /************************************************************************/
    7436             : /*                          GDALAlgorithmRun()                          */
    7437             : /************************************************************************/
    7438             : 
    7439             : /** Execute the algorithm, starting with ValidateArguments() and then
    7440             :  * calling RunImpl().
    7441             :  *
    7442             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7443             :  * @param pfnProgress Progress callback. May be null.
    7444             :  * @param pProgressData Progress callback user data. May be null.
    7445             :  * @return true if successful, false otherwise
    7446             :  * @since 3.11
    7447             :  */
    7448             : 
    7449        2612 : bool GDALAlgorithmRun(GDALAlgorithmH hAlg, GDALProgressFunc pfnProgress,
    7450             :                       void *pProgressData)
    7451             : {
    7452        2612 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7453        2612 :     return hAlg->ptr->Run(pfnProgress, pProgressData);
    7454             : }
    7455             : 
    7456             : /************************************************************************/
    7457             : /*                       GDALAlgorithmFinalize()                        */
    7458             : /************************************************************************/
    7459             : 
    7460             : /** Complete any pending actions, and return the final status.
    7461             :  * This is typically useful for algorithm that generate an output dataset.
    7462             :  *
    7463             :  * Note that this function does *NOT* release memory associated with the
    7464             :  * algorithm. GDALAlgorithmRelease() must still be called afterwards.
    7465             :  *
    7466             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7467             :  * @return true if successful, false otherwise
    7468             :  * @since 3.11
    7469             :  */
    7470             : 
    7471         879 : bool GDALAlgorithmFinalize(GDALAlgorithmH hAlg)
    7472             : {
    7473         879 :     VALIDATE_POINTER1(hAlg, __func__, false);
    7474         879 :     return hAlg->ptr->Finalize();
    7475             : }
    7476             : 
    7477             : /************************************************************************/
    7478             : /*                    GDALAlgorithmGetUsageAsJSON()                     */
    7479             : /************************************************************************/
    7480             : 
    7481             : /** Return the usage of the algorithm as a JSON-serialized string.
    7482             :  *
    7483             :  * This can be used to dynamically generate interfaces to algorithms.
    7484             :  *
    7485             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7486             :  * @return a string that must be freed with CPLFree()
    7487             :  * @since 3.11
    7488             :  */
    7489           4 : char *GDALAlgorithmGetUsageAsJSON(GDALAlgorithmH hAlg)
    7490             : {
    7491           4 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7492           4 :     return CPLStrdup(hAlg->ptr->GetUsageAsJSON().c_str());
    7493             : }
    7494             : 
    7495             : /************************************************************************/
    7496             : /*                      GDALAlgorithmGetArgNames()                      */
    7497             : /************************************************************************/
    7498             : 
    7499             : /** Return the list of available argument names.
    7500             :  *
    7501             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7502             :  * @return a NULL terminated list of names, which must be destroyed with
    7503             :  * CSLDestroy()
    7504             :  * @since 3.11
    7505             :  */
    7506       15408 : char **GDALAlgorithmGetArgNames(GDALAlgorithmH hAlg)
    7507             : {
    7508       15408 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7509       30816 :     CPLStringList list;
    7510      342329 :     for (const auto &arg : hAlg->ptr->GetArgs())
    7511      326921 :         list.AddString(arg->GetName().c_str());
    7512       15408 :     return list.StealList();
    7513             : }
    7514             : 
    7515             : /************************************************************************/
    7516             : /*                        GDALAlgorithmGetArg()                         */
    7517             : /************************************************************************/
    7518             : 
    7519             : /** Return an argument from its name.
    7520             :  *
    7521             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7522             :  *
    7523             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7524             :  * @param pszArgName Argument name. Must NOT be null.
    7525             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7526             :  * or nullptr in case of error
    7527             :  * @since 3.11
    7528             :  */
    7529      327822 : GDALAlgorithmArgH GDALAlgorithmGetArg(GDALAlgorithmH hAlg,
    7530             :                                       const char *pszArgName)
    7531             : {
    7532      327822 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7533      327822 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7534      655644 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7535      327822 :                                  /* isConst = */ true);
    7536      327822 :     if (!arg)
    7537           3 :         return nullptr;
    7538      327819 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7539             : }
    7540             : 
    7541             : /************************************************************************/
    7542             : /*                    GDALAlgorithmGetArgNonConst()                     */
    7543             : /************************************************************************/
    7544             : 
    7545             : /** Return an argument from its name, possibly allowing creation of user-provided
    7546             :  * argument if the algorithm allow it.
    7547             :  *
    7548             :  * The lifetime of the returned object does not exceed the one of hAlg.
    7549             :  *
    7550             :  * @param hAlg Handle to an algorithm. Must NOT be null.
    7551             :  * @param pszArgName Argument name. Must NOT be null.
    7552             :  * @return an argument that must be released with GDALAlgorithmArgRelease(),
    7553             :  * or nullptr in case of error
    7554             :  * @since 3.12
    7555             :  */
    7556        9349 : GDALAlgorithmArgH GDALAlgorithmGetArgNonConst(GDALAlgorithmH hAlg,
    7557             :                                               const char *pszArgName)
    7558             : {
    7559        9349 :     VALIDATE_POINTER1(hAlg, __func__, nullptr);
    7560        9349 :     VALIDATE_POINTER1(pszArgName, __func__, nullptr);
    7561       18698 :     auto arg = hAlg->ptr->GetArg(pszArgName, /* suggestionAllowed = */ true,
    7562        9349 :                                  /* isConst = */ false);
    7563        9349 :     if (!arg)
    7564           1 :         return nullptr;
    7565        9348 :     return std::make_unique<GDALAlgorithmArgHS>(arg).release();
    7566             : }
    7567             : 
    7568             : /************************************************************************/
    7569             : /*                      GDALAlgorithmArgRelease()                       */
    7570             : /************************************************************************/
    7571             : 
    7572             : /** Release a handle to an argument.
    7573             :  *
    7574             :  * @since 3.11
    7575             :  */
    7576      337167 : void GDALAlgorithmArgRelease(GDALAlgorithmArgH hArg)
    7577             : {
    7578      337167 :     delete hArg;
    7579      337167 : }
    7580             : 
    7581             : /************************************************************************/
    7582             : /*                      GDALAlgorithmArgGetName()                       */
    7583             : /************************************************************************/
    7584             : 
    7585             : /** Return the name of an argument.
    7586             :  *
    7587             :  * @param hArg Handle to an argument. Must NOT be null.
    7588             :  * @return argument name whose lifetime is bound to hArg and which must not
    7589             :  * be freed.
    7590             :  * @since 3.11
    7591             :  */
    7592       18601 : const char *GDALAlgorithmArgGetName(GDALAlgorithmArgH hArg)
    7593             : {
    7594       18601 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7595       18601 :     return hArg->ptr->GetName().c_str();
    7596             : }
    7597             : 
    7598             : /************************************************************************/
    7599             : /*                      GDALAlgorithmArgGetType()                       */
    7600             : /************************************************************************/
    7601             : 
    7602             : /** Get the type of an argument
    7603             :  *
    7604             :  * @param hArg Handle to an argument. Must NOT be null.
    7605             :  * @since 3.11
    7606             :  */
    7607      417396 : GDALAlgorithmArgType GDALAlgorithmArgGetType(GDALAlgorithmArgH hArg)
    7608             : {
    7609      417396 :     VALIDATE_POINTER1(hArg, __func__, GAAT_STRING);
    7610      417396 :     return hArg->ptr->GetType();
    7611             : }
    7612             : 
    7613             : /************************************************************************/
    7614             : /*                   GDALAlgorithmArgGetDescription()                   */
    7615             : /************************************************************************/
    7616             : 
    7617             : /** Return the description of an argument.
    7618             :  *
    7619             :  * @param hArg Handle to an argument. Must NOT be null.
    7620             :  * @return argument description whose lifetime is bound to hArg and which must not
    7621             :  * be freed.
    7622             :  * @since 3.11
    7623             :  */
    7624       83313 : const char *GDALAlgorithmArgGetDescription(GDALAlgorithmArgH hArg)
    7625             : {
    7626       83313 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7627       83313 :     return hArg->ptr->GetDescription().c_str();
    7628             : }
    7629             : 
    7630             : /************************************************************************/
    7631             : /*                    GDALAlgorithmArgGetShortName()                    */
    7632             : /************************************************************************/
    7633             : 
    7634             : /** Return the short name, or empty string if there is none
    7635             :  *
    7636             :  * @param hArg Handle to an argument. Must NOT be null.
    7637             :  * @return short name whose lifetime is bound to hArg and which must not
    7638             :  * be freed.
    7639             :  * @since 3.11
    7640             :  */
    7641           1 : const char *GDALAlgorithmArgGetShortName(GDALAlgorithmArgH hArg)
    7642             : {
    7643           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7644           1 :     return hArg->ptr->GetShortName().c_str();
    7645             : }
    7646             : 
    7647             : /************************************************************************/
    7648             : /*                     GDALAlgorithmArgGetAliases()                     */
    7649             : /************************************************************************/
    7650             : 
    7651             : /** Return the aliases (potentially none)
    7652             :  *
    7653             :  * @param hArg Handle to an argument. Must NOT be null.
    7654             :  * @return a NULL terminated list of names, which must be destroyed with
    7655             :  * CSLDestroy()
    7656             : 
    7657             :  * @since 3.11
    7658             :  */
    7659           1 : char **GDALAlgorithmArgGetAliases(GDALAlgorithmArgH hArg)
    7660             : {
    7661           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7662           1 :     return CPLStringList(hArg->ptr->GetAliases()).StealList();
    7663             : }
    7664             : 
    7665             : /************************************************************************/
    7666             : /*                     GDALAlgorithmArgGetMetaVar()                     */
    7667             : /************************************************************************/
    7668             : 
    7669             : /** Return the "meta-var" hint.
    7670             :  *
    7671             :  * By default, the meta-var value is the long name of the argument in
    7672             :  * upper case.
    7673             :  *
    7674             :  * @param hArg Handle to an argument. Must NOT be null.
    7675             :  * @return meta-var hint whose lifetime is bound to hArg and which must not
    7676             :  * be freed.
    7677             :  * @since 3.11
    7678             :  */
    7679           1 : const char *GDALAlgorithmArgGetMetaVar(GDALAlgorithmArgH hArg)
    7680             : {
    7681           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7682           1 :     return hArg->ptr->GetMetaVar().c_str();
    7683             : }
    7684             : 
    7685             : /************************************************************************/
    7686             : /*                    GDALAlgorithmArgGetCategory()                     */
    7687             : /************************************************************************/
    7688             : 
    7689             : /** Return the argument category
    7690             :  *
    7691             :  * GAAC_COMMON, GAAC_BASE, GAAC_ADVANCED, GAAC_ESOTERIC or a custom category.
    7692             :  *
    7693             :  * @param hArg Handle to an argument. Must NOT be null.
    7694             :  * @return category whose lifetime is bound to hArg and which must not
    7695             :  * be freed.
    7696             :  * @since 3.11
    7697             :  */
    7698           1 : const char *GDALAlgorithmArgGetCategory(GDALAlgorithmArgH hArg)
    7699             : {
    7700           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7701           1 :     return hArg->ptr->GetCategory().c_str();
    7702             : }
    7703             : 
    7704             : /************************************************************************/
    7705             : /*                    GDALAlgorithmArgIsPositional()                    */
    7706             : /************************************************************************/
    7707             : 
    7708             : /** Return if the argument is a positional one.
    7709             :  *
    7710             :  * @param hArg Handle to an argument. Must NOT be null.
    7711             :  * @since 3.11
    7712             :  */
    7713           1 : bool GDALAlgorithmArgIsPositional(GDALAlgorithmArgH hArg)
    7714             : {
    7715           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    7716           1 :     return hArg->ptr->IsPositional();
    7717             : }
    7718             : 
    7719             : /************************************************************************/
    7720             : /*                     GDALAlgorithmArgIsRequired()                     */
    7721             : /************************************************************************/
    7722             : 
    7723             : /** Return whether the argument is required. Defaults to false.
    7724             :  *
    7725             :  * @param hArg Handle to an argument. Must NOT be null.
    7726             :  * @since 3.11
    7727             :  */
    7728      157605 : bool GDALAlgorithmArgIsRequired(GDALAlgorithmArgH hArg)
    7729             : {
    7730      157605 :     VALIDATE_POINTER1(hArg, __func__, false);
    7731      157605 :     return hArg->ptr->IsRequired();
    7732             : }
    7733             : 
    7734             : /************************************************************************/
    7735             : /*                    GDALAlgorithmArgGetMinCount()                     */
    7736             : /************************************************************************/
    7737             : 
    7738             : /** Return the minimum number of values for the argument.
    7739             :  *
    7740             :  * Defaults to 0.
    7741             :  * Only applies to list type of arguments.
    7742             :  *
    7743             :  * @param hArg Handle to an argument. Must NOT be null.
    7744             :  * @since 3.11
    7745             :  */
    7746           1 : int GDALAlgorithmArgGetMinCount(GDALAlgorithmArgH hArg)
    7747             : {
    7748           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    7749           1 :     return hArg->ptr->GetMinCount();
    7750             : }
    7751             : 
    7752             : /************************************************************************/
    7753             : /*                    GDALAlgorithmArgGetMaxCount()                     */
    7754             : /************************************************************************/
    7755             : 
    7756             : /** Return the maximum number of values for the argument.
    7757             :  *
    7758             :  * Defaults to 1 for scalar types, and INT_MAX for list types.
    7759             :  * Only applies to list type of arguments.
    7760             :  *
    7761             :  * @param hArg Handle to an argument. Must NOT be null.
    7762             :  * @since 3.11
    7763             :  */
    7764           1 : int GDALAlgorithmArgGetMaxCount(GDALAlgorithmArgH hArg)
    7765             : {
    7766           1 :     VALIDATE_POINTER1(hArg, __func__, 0);
    7767           1 :     return hArg->ptr->GetMaxCount();
    7768             : }
    7769             : 
    7770             : /************************************************************************/
    7771             : /*               GDALAlgorithmArgGetPackedValuesAllowed()               */
    7772             : /************************************************************************/
    7773             : 
    7774             : /** Return whether, for list type of arguments, several values, space
    7775             :  * separated, may be specified. That is "--foo=bar,baz".
    7776             :  * The default is true.
    7777             :  *
    7778             :  * @param hArg Handle to an argument. Must NOT be null.
    7779             :  * @since 3.11
    7780             :  */
    7781           1 : bool GDALAlgorithmArgGetPackedValuesAllowed(GDALAlgorithmArgH hArg)
    7782             : {
    7783           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    7784           1 :     return hArg->ptr->GetPackedValuesAllowed();
    7785             : }
    7786             : 
    7787             : /************************************************************************/
    7788             : /*               GDALAlgorithmArgGetRepeatedArgAllowed()                */
    7789             : /************************************************************************/
    7790             : 
    7791             : /** Return whether, for list type of arguments, the argument may be
    7792             :  * repeated. That is "--foo=bar --foo=baz".
    7793             :  * The default is true.
    7794             :  *
    7795             :  * @param hArg Handle to an argument. Must NOT be null.
    7796             :  * @since 3.11
    7797             :  */
    7798           1 : bool GDALAlgorithmArgGetRepeatedArgAllowed(GDALAlgorithmArgH hArg)
    7799             : {
    7800           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    7801           1 :     return hArg->ptr->GetRepeatedArgAllowed();
    7802             : }
    7803             : 
    7804             : /************************************************************************/
    7805             : /*                     GDALAlgorithmArgGetChoices()                     */
    7806             : /************************************************************************/
    7807             : 
    7808             : /** Return the allowed values (as strings) for the argument.
    7809             :  *
    7810             :  * Only honored for GAAT_STRING and GAAT_STRING_LIST types.
    7811             :  *
    7812             :  * @param hArg Handle to an argument. Must NOT be null.
    7813             :  * @return a NULL terminated list of names, which must be destroyed with
    7814             :  * CSLDestroy()
    7815             : 
    7816             :  * @since 3.11
    7817             :  */
    7818           1 : char **GDALAlgorithmArgGetChoices(GDALAlgorithmArgH hArg)
    7819             : {
    7820           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7821           1 :     return CPLStringList(hArg->ptr->GetChoices()).StealList();
    7822             : }
    7823             : 
    7824             : /************************************************************************/
    7825             : /*                  GDALAlgorithmArgGetMetadataItem()                   */
    7826             : /************************************************************************/
    7827             : 
    7828             : /** Return the values of the metadata item of an argument.
    7829             :  *
    7830             :  * @param hArg Handle to an argument. Must NOT be null.
    7831             :  * @param pszItem Name of the item. Must NOT be null.
    7832             :  * @return a NULL terminated list of values, which must be destroyed with
    7833             :  * CSLDestroy()
    7834             : 
    7835             :  * @since 3.11
    7836             :  */
    7837          63 : char **GDALAlgorithmArgGetMetadataItem(GDALAlgorithmArgH hArg,
    7838             :                                        const char *pszItem)
    7839             : {
    7840          63 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7841          63 :     VALIDATE_POINTER1(pszItem, __func__, nullptr);
    7842          63 :     const auto pVecOfStrings = hArg->ptr->GetMetadataItem(pszItem);
    7843          63 :     return pVecOfStrings ? CPLStringList(*pVecOfStrings).StealList() : nullptr;
    7844             : }
    7845             : 
    7846             : /************************************************************************/
    7847             : /*                  GDALAlgorithmArgIsExplicitlySet()                   */
    7848             : /************************************************************************/
    7849             : 
    7850             : /** Return whether the argument value has been explicitly set with Set()
    7851             :  *
    7852             :  * @param hArg Handle to an argument. Must NOT be null.
    7853             :  * @since 3.11
    7854             :  */
    7855         600 : bool GDALAlgorithmArgIsExplicitlySet(GDALAlgorithmArgH hArg)
    7856             : {
    7857         600 :     VALIDATE_POINTER1(hArg, __func__, false);
    7858         600 :     return hArg->ptr->IsExplicitlySet();
    7859             : }
    7860             : 
    7861             : /************************************************************************/
    7862             : /*                  GDALAlgorithmArgHasDefaultValue()                   */
    7863             : /************************************************************************/
    7864             : 
    7865             : /** Return if the argument has a declared default value.
    7866             :  *
    7867             :  * @param hArg Handle to an argument. Must NOT be null.
    7868             :  * @since 3.11
    7869             :  */
    7870           2 : bool GDALAlgorithmArgHasDefaultValue(GDALAlgorithmArgH hArg)
    7871             : {
    7872           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    7873           2 :     return hArg->ptr->HasDefaultValue();
    7874             : }
    7875             : 
    7876             : /************************************************************************/
    7877             : /*                GDALAlgorithmArgGetDefaultAsBoolean()                 */
    7878             : /************************************************************************/
    7879             : 
    7880             : /** Return the argument default value as a integer.
    7881             :  *
    7882             :  * Must only be called on arguments whose type is GAAT_BOOLEAN
    7883             :  *
    7884             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    7885             :  * argument has a default value.
    7886             :  *
    7887             :  * @param hArg Handle to an argument. Must NOT be null.
    7888             :  * @since 3.12
    7889             :  */
    7890           3 : bool GDALAlgorithmArgGetDefaultAsBoolean(GDALAlgorithmArgH hArg)
    7891             : {
    7892           3 :     VALIDATE_POINTER1(hArg, __func__, false);
    7893           3 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    7894             :     {
    7895           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7896             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    7897             :                  __func__);
    7898           1 :         return false;
    7899             :     }
    7900           2 :     return hArg->ptr->GetDefault<bool>();
    7901             : }
    7902             : 
    7903             : /************************************************************************/
    7904             : /*                 GDALAlgorithmArgGetDefaultAsString()                 */
    7905             : /************************************************************************/
    7906             : 
    7907             : /** Return the argument default value as a string.
    7908             :  *
    7909             :  * Must only be called on arguments whose type is GAAT_STRING.
    7910             :  *
    7911             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    7912             :  * argument has a default value.
    7913             :  *
    7914             :  * @param hArg Handle to an argument. Must NOT be null.
    7915             :  * @return string whose lifetime is bound to hArg and which must not
    7916             :  * be freed.
    7917             :  * @since 3.11
    7918             :  */
    7919           3 : const char *GDALAlgorithmArgGetDefaultAsString(GDALAlgorithmArgH hArg)
    7920             : {
    7921           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    7922           3 :     if (hArg->ptr->GetType() != GAAT_STRING)
    7923             :     {
    7924           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7925             :                  "%s must only be called on arguments of type GAAT_STRING",
    7926             :                  __func__);
    7927           2 :         return nullptr;
    7928             :     }
    7929           1 :     return hArg->ptr->GetDefault<std::string>().c_str();
    7930             : }
    7931             : 
    7932             : /************************************************************************/
    7933             : /*                GDALAlgorithmArgGetDefaultAsInteger()                 */
    7934             : /************************************************************************/
    7935             : 
    7936             : /** Return the argument default value as a integer.
    7937             :  *
    7938             :  * Must only be called on arguments whose type is GAAT_INTEGER
    7939             :  *
    7940             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    7941             :  * argument has a default value.
    7942             :  *
    7943             :  * @param hArg Handle to an argument. Must NOT be null.
    7944             :  * @since 3.12
    7945             :  */
    7946           3 : int GDALAlgorithmArgGetDefaultAsInteger(GDALAlgorithmArgH hArg)
    7947             : {
    7948           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    7949           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    7950             :     {
    7951           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7952             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    7953             :                  __func__);
    7954           2 :         return 0;
    7955             :     }
    7956           1 :     return hArg->ptr->GetDefault<int>();
    7957             : }
    7958             : 
    7959             : /************************************************************************/
    7960             : /*                 GDALAlgorithmArgGetDefaultAsDouble()                 */
    7961             : /************************************************************************/
    7962             : 
    7963             : /** Return the argument default value as a double.
    7964             :  *
    7965             :  * Must only be called on arguments whose type is GAAT_REAL
    7966             :  *
    7967             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    7968             :  * argument has a default value.
    7969             :  *
    7970             :  * @param hArg Handle to an argument. Must NOT be null.
    7971             :  * @since 3.12
    7972             :  */
    7973           3 : double GDALAlgorithmArgGetDefaultAsDouble(GDALAlgorithmArgH hArg)
    7974             : {
    7975           3 :     VALIDATE_POINTER1(hArg, __func__, 0);
    7976           3 :     if (hArg->ptr->GetType() != GAAT_REAL)
    7977             :     {
    7978           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7979             :                  "%s must only be called on arguments of type GAAT_REAL",
    7980             :                  __func__);
    7981           2 :         return 0;
    7982             :     }
    7983           1 :     return hArg->ptr->GetDefault<double>();
    7984             : }
    7985             : 
    7986             : /************************************************************************/
    7987             : /*               GDALAlgorithmArgGetDefaultAsStringList()               */
    7988             : /************************************************************************/
    7989             : 
    7990             : /** Return the argument default value as a string list.
    7991             :  *
    7992             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    7993             :  *
    7994             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    7995             :  * argument has a default value.
    7996             :  *
    7997             :  * @param hArg Handle to an argument. Must NOT be null.
    7998             :  * @return a NULL terminated list of names, which must be destroyed with
    7999             :  * CSLDestroy()
    8000             : 
    8001             :  * @since 3.12
    8002             :  */
    8003           3 : char **GDALAlgorithmArgGetDefaultAsStringList(GDALAlgorithmArgH hArg)
    8004             : {
    8005           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8006           3 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8007             :     {
    8008           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8009             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8010             :                  __func__);
    8011           2 :         return nullptr;
    8012             :     }
    8013           2 :     return CPLStringList(hArg->ptr->GetDefault<std::vector<std::string>>())
    8014           1 :         .StealList();
    8015             : }
    8016             : 
    8017             : /************************************************************************/
    8018             : /*              GDALAlgorithmArgGetDefaultAsIntegerList()               */
    8019             : /************************************************************************/
    8020             : 
    8021             : /** Return the argument default value as a integer list.
    8022             :  *
    8023             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8024             :  *
    8025             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8026             :  * argument has a default value.
    8027             :  *
    8028             :  * @param hArg Handle to an argument. Must NOT be null.
    8029             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8030             :  * @since 3.12
    8031             :  */
    8032           3 : const int *GDALAlgorithmArgGetDefaultAsIntegerList(GDALAlgorithmArgH hArg,
    8033             :                                                    size_t *pnCount)
    8034             : {
    8035           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8036           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8037           3 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8038             :     {
    8039           2 :         CPLError(
    8040             :             CE_Failure, CPLE_AppDefined,
    8041             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8042             :             __func__);
    8043           2 :         *pnCount = 0;
    8044           2 :         return nullptr;
    8045             :     }
    8046           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<int>>();
    8047           1 :     *pnCount = val.size();
    8048           1 :     return val.data();
    8049             : }
    8050             : 
    8051             : /************************************************************************/
    8052             : /*               GDALAlgorithmArgGetDefaultAsDoubleList()               */
    8053             : /************************************************************************/
    8054             : 
    8055             : /** Return the argument default value as a real list.
    8056             :  *
    8057             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8058             :  *
    8059             :  * GDALAlgorithmArgHasDefaultValue() must be called to determine if the
    8060             :  * argument has a default value.
    8061             :  *
    8062             :  * @param hArg Handle to an argument. Must NOT be null.
    8063             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8064             :  * @since 3.12
    8065             :  */
    8066           3 : const double *GDALAlgorithmArgGetDefaultAsDoubleList(GDALAlgorithmArgH hArg,
    8067             :                                                      size_t *pnCount)
    8068             : {
    8069           3 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8070           3 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8071           3 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8072             :     {
    8073           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    8074             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8075             :                  __func__);
    8076           2 :         *pnCount = 0;
    8077           2 :         return nullptr;
    8078             :     }
    8079           1 :     const auto &val = hArg->ptr->GetDefault<std::vector<double>>();
    8080           1 :     *pnCount = val.size();
    8081           1 :     return val.data();
    8082             : }
    8083             : 
    8084             : /************************************************************************/
    8085             : /*                      GDALAlgorithmArgIsHidden()                      */
    8086             : /************************************************************************/
    8087             : 
    8088             : /** Return whether the argument is hidden (for GDAL internal use)
    8089             :  *
    8090             :  * This is an alias for GDALAlgorithmArgIsHiddenForCLI() &&
    8091             :  * GDALAlgorithmArgIsHiddenForAPI().
    8092             :  *
    8093             :  * @param hArg Handle to an argument. Must NOT be null.
    8094             :  * @since 3.12
    8095             :  */
    8096           1 : bool GDALAlgorithmArgIsHidden(GDALAlgorithmArgH hArg)
    8097             : {
    8098           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8099           1 :     return hArg->ptr->IsHidden();
    8100             : }
    8101             : 
    8102             : /************************************************************************/
    8103             : /*                   GDALAlgorithmArgIsHiddenForCLI()                   */
    8104             : /************************************************************************/
    8105             : 
    8106             : /** Return whether the argument must not be mentioned in CLI usage.
    8107             :  *
    8108             :  * For example, "output-value" for "gdal raster info", which is only
    8109             :  * meant when the algorithm is used from a non-CLI context.
    8110             :  *
    8111             :  * @param hArg Handle to an argument. Must NOT be null.
    8112             :  * @since 3.11
    8113             :  */
    8114           1 : bool GDALAlgorithmArgIsHiddenForCLI(GDALAlgorithmArgH hArg)
    8115             : {
    8116           1 :     VALIDATE_POINTER1(hArg, __func__, false);
    8117           1 :     return hArg->ptr->IsHiddenForCLI();
    8118             : }
    8119             : 
    8120             : /************************************************************************/
    8121             : /*                   GDALAlgorithmArgIsHiddenForAPI()                   */
    8122             : /************************************************************************/
    8123             : 
    8124             : /** Return whether the argument must not be mentioned in the context of an
    8125             :  * API use.
    8126             :  * Said otherwise, if it is only for CLI usage.
    8127             :  *
    8128             :  * For example "--help"
    8129             :  *
    8130             :  * @param hArg Handle to an argument. Must NOT be null.
    8131             :  * @since 3.12
    8132             :  */
    8133      213201 : bool GDALAlgorithmArgIsHiddenForAPI(GDALAlgorithmArgH hArg)
    8134             : {
    8135      213201 :     VALIDATE_POINTER1(hArg, __func__, false);
    8136      213201 :     return hArg->ptr->IsHiddenForAPI();
    8137             : }
    8138             : 
    8139             : /************************************************************************/
    8140             : /*                    GDALAlgorithmArgIsOnlyForCLI()                    */
    8141             : /************************************************************************/
    8142             : 
    8143             : /** Return whether the argument must not be mentioned in the context of an
    8144             :  * API use.
    8145             :  * Said otherwise, if it is only for CLI usage.
    8146             :  *
    8147             :  * For example "--help"
    8148             :  *
    8149             :  * @param hArg Handle to an argument. Must NOT be null.
    8150             :  * @since 3.11
    8151             :  * @deprecated Use GDALAlgorithmArgIsHiddenForAPI() instead.
    8152             :  */
    8153           0 : bool GDALAlgorithmArgIsOnlyForCLI(GDALAlgorithmArgH hArg)
    8154             : {
    8155           0 :     VALIDATE_POINTER1(hArg, __func__, false);
    8156           0 :     return hArg->ptr->IsHiddenForAPI();
    8157             : }
    8158             : 
    8159             : /************************************************************************/
    8160             : /*                      GDALAlgorithmArgIsInput()                       */
    8161             : /************************************************************************/
    8162             : 
    8163             : /** Indicate whether the value of the argument is read-only during the
    8164             :  * execution of the algorithm.
    8165             :  *
    8166             :  * Default is true.
    8167             :  *
    8168             :  * @param hArg Handle to an argument. Must NOT be null.
    8169             :  * @since 3.11
    8170             :  */
    8171      210331 : bool GDALAlgorithmArgIsInput(GDALAlgorithmArgH hArg)
    8172             : {
    8173      210331 :     VALIDATE_POINTER1(hArg, __func__, false);
    8174      210331 :     return hArg->ptr->IsInput();
    8175             : }
    8176             : 
    8177             : /************************************************************************/
    8178             : /*                      GDALAlgorithmArgIsOutput()                      */
    8179             : /************************************************************************/
    8180             : 
    8181             : /** Return whether (at least part of) the value of the argument is set
    8182             :  * during the execution of the algorithm.
    8183             :  *
    8184             :  * For example, "output-value" for "gdal raster info"
    8185             :  * Default is false.
    8186             :  * An argument may return both IsInput() and IsOutput() as true.
    8187             :  * For example the "gdal raster convert" algorithm consumes the dataset
    8188             :  * name of its "output" argument, and sets the dataset object during its
    8189             :  * execution.
    8190             :  *
    8191             :  * @param hArg Handle to an argument. Must NOT be null.
    8192             :  * @since 3.11
    8193             :  */
    8194      116566 : bool GDALAlgorithmArgIsOutput(GDALAlgorithmArgH hArg)
    8195             : {
    8196      116566 :     VALIDATE_POINTER1(hArg, __func__, false);
    8197      116566 :     return hArg->ptr->IsOutput();
    8198             : }
    8199             : 
    8200             : /************************************************************************/
    8201             : /*                   GDALAlgorithmArgGetDatasetType()                   */
    8202             : /************************************************************************/
    8203             : 
    8204             : /** Get which type of dataset is allowed / generated.
    8205             :  *
    8206             :  * Binary-or combination of GDAL_OF_RASTER, GDAL_OF_VECTOR and
    8207             :  * GDAL_OF_MULTIDIM_RASTER.
    8208             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8209             :  *
    8210             :  * @param hArg Handle to an argument. Must NOT be null.
    8211             :  * @since 3.11
    8212             :  */
    8213           2 : GDALArgDatasetType GDALAlgorithmArgGetDatasetType(GDALAlgorithmArgH hArg)
    8214             : {
    8215           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8216           2 :     return hArg->ptr->GetDatasetType();
    8217             : }
    8218             : 
    8219             : /************************************************************************/
    8220             : /*                GDALAlgorithmArgGetDatasetInputFlags()                */
    8221             : /************************************************************************/
    8222             : 
    8223             : /** Indicates which components among name and dataset are accepted as
    8224             :  * input, when this argument serves as an input.
    8225             :  *
    8226             :  * If the GADV_NAME bit is set, it indicates a dataset name is accepted as
    8227             :  * input.
    8228             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8229             :  * accepted as input.
    8230             :  * If both bits are set, the algorithm can accept either a name or a dataset
    8231             :  * object.
    8232             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8233             :  *
    8234             :  * @param hArg Handle to an argument. Must NOT be null.
    8235             :  * @return string whose lifetime is bound to hAlg and which must not
    8236             :  * be freed.
    8237             :  * @since 3.11
    8238             :  */
    8239           2 : int GDALAlgorithmArgGetDatasetInputFlags(GDALAlgorithmArgH hArg)
    8240             : {
    8241           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8242           2 :     return hArg->ptr->GetDatasetInputFlags();
    8243             : }
    8244             : 
    8245             : /************************************************************************/
    8246             : /*               GDALAlgorithmArgGetDatasetOutputFlags()                */
    8247             : /************************************************************************/
    8248             : 
    8249             : /** Indicates which components among name and dataset are modified,
    8250             :  * when this argument serves as an output.
    8251             :  *
    8252             :  * If the GADV_NAME bit is set, it indicates a dataset name is generated as
    8253             :  * output (that is the algorithm will generate the name. Rarely used).
    8254             :  * If the GADV_OBJECT bit is set, it indicates a dataset object is
    8255             :  * generated as output, and available for use after the algorithm has
    8256             :  * completed.
    8257             :  * Only applies to arguments of type GAAT_DATASET or GAAT_DATASET_LIST.
    8258             :  *
    8259             :  * @param hArg Handle to an argument. Must NOT be null.
    8260             :  * @return string whose lifetime is bound to hAlg and which must not
    8261             :  * be freed.
    8262             :  * @since 3.11
    8263             :  */
    8264           2 : int GDALAlgorithmArgGetDatasetOutputFlags(GDALAlgorithmArgH hArg)
    8265             : {
    8266           2 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8267           2 :     return hArg->ptr->GetDatasetOutputFlags();
    8268             : }
    8269             : 
    8270             : /************************************************************************/
    8271             : /*              GDALAlgorithmArgGetMutualExclusionGroup()               */
    8272             : /************************************************************************/
    8273             : 
    8274             : /** Return the name of the mutual exclusion group to which this argument
    8275             :  * belongs to.
    8276             :  *
    8277             :  * Or empty string if it does not belong to any exclusion group.
    8278             :  *
    8279             :  * @param hArg Handle to an argument. Must NOT be null.
    8280             :  * @return string whose lifetime is bound to hArg and which must not
    8281             :  * be freed.
    8282             :  * @since 3.11
    8283             :  */
    8284           1 : const char *GDALAlgorithmArgGetMutualExclusionGroup(GDALAlgorithmArgH hArg)
    8285             : {
    8286           1 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8287           1 :     return hArg->ptr->GetMutualExclusionGroup().c_str();
    8288             : }
    8289             : 
    8290             : /************************************************************************/
    8291             : /*                    GDALAlgorithmArgGetAsBoolean()                    */
    8292             : /************************************************************************/
    8293             : 
    8294             : /** Return the argument value as a boolean.
    8295             :  *
    8296             :  * Must only be called on arguments whose type is GAAT_BOOLEAN.
    8297             :  *
    8298             :  * @param hArg Handle to an argument. Must NOT be null.
    8299             :  * @since 3.11
    8300             :  */
    8301           8 : bool GDALAlgorithmArgGetAsBoolean(GDALAlgorithmArgH hArg)
    8302             : {
    8303           8 :     VALIDATE_POINTER1(hArg, __func__, false);
    8304           8 :     if (hArg->ptr->GetType() != GAAT_BOOLEAN)
    8305             :     {
    8306           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8307             :                  "%s must only be called on arguments of type GAAT_BOOLEAN",
    8308             :                  __func__);
    8309           1 :         return false;
    8310             :     }
    8311           7 :     return hArg->ptr->Get<bool>();
    8312             : }
    8313             : 
    8314             : /************************************************************************/
    8315             : /*                    GDALAlgorithmArgGetAsString()                     */
    8316             : /************************************************************************/
    8317             : 
    8318             : /** Return the argument value as a string.
    8319             :  *
    8320             :  * Must only be called on arguments whose type is GAAT_STRING.
    8321             :  *
    8322             :  * @param hArg Handle to an argument. Must NOT be null.
    8323             :  * @return string whose lifetime is bound to hArg and which must not
    8324             :  * be freed.
    8325             :  * @since 3.11
    8326             :  */
    8327         354 : const char *GDALAlgorithmArgGetAsString(GDALAlgorithmArgH hArg)
    8328             : {
    8329         354 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8330         354 :     if (hArg->ptr->GetType() != GAAT_STRING)
    8331             :     {
    8332           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8333             :                  "%s must only be called on arguments of type GAAT_STRING",
    8334             :                  __func__);
    8335           1 :         return nullptr;
    8336             :     }
    8337         353 :     return hArg->ptr->Get<std::string>().c_str();
    8338             : }
    8339             : 
    8340             : /************************************************************************/
    8341             : /*                 GDALAlgorithmArgGetAsDatasetValue()                  */
    8342             : /************************************************************************/
    8343             : 
    8344             : /** Return the argument value as a GDALArgDatasetValueH.
    8345             :  *
    8346             :  * Must only be called on arguments whose type is GAAT_DATASET
    8347             :  *
    8348             :  * @param hArg Handle to an argument. Must NOT be null.
    8349             :  * @return handle to a GDALArgDatasetValue that must be released with
    8350             :  * GDALArgDatasetValueRelease(). The lifetime of that handle does not exceed
    8351             :  * the one of hArg.
    8352             :  * @since 3.11
    8353             :  */
    8354        3062 : GDALArgDatasetValueH GDALAlgorithmArgGetAsDatasetValue(GDALAlgorithmArgH hArg)
    8355             : {
    8356        3062 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8357        3062 :     if (hArg->ptr->GetType() != GAAT_DATASET)
    8358             :     {
    8359           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8360             :                  "%s must only be called on arguments of type GAAT_DATASET",
    8361             :                  __func__);
    8362           1 :         return nullptr;
    8363             :     }
    8364        3061 :     return std::make_unique<GDALArgDatasetValueHS>(
    8365        6122 :                &(hArg->ptr->Get<GDALArgDatasetValue>()))
    8366        3061 :         .release();
    8367             : }
    8368             : 
    8369             : /************************************************************************/
    8370             : /*                    GDALAlgorithmArgGetAsInteger()                    */
    8371             : /************************************************************************/
    8372             : 
    8373             : /** Return the argument value as a integer.
    8374             :  *
    8375             :  * Must only be called on arguments whose type is GAAT_INTEGER
    8376             :  *
    8377             :  * @param hArg Handle to an argument. Must NOT be null.
    8378             :  * @since 3.11
    8379             :  */
    8380          26 : int GDALAlgorithmArgGetAsInteger(GDALAlgorithmArgH hArg)
    8381             : {
    8382          26 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8383          26 :     if (hArg->ptr->GetType() != GAAT_INTEGER)
    8384             :     {
    8385           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8386             :                  "%s must only be called on arguments of type GAAT_INTEGER",
    8387             :                  __func__);
    8388           1 :         return 0;
    8389             :     }
    8390          25 :     return hArg->ptr->Get<int>();
    8391             : }
    8392             : 
    8393             : /************************************************************************/
    8394             : /*                    GDALAlgorithmArgGetAsDouble()                     */
    8395             : /************************************************************************/
    8396             : 
    8397             : /** Return the argument value as a double.
    8398             :  *
    8399             :  * Must only be called on arguments whose type is GAAT_REAL
    8400             :  *
    8401             :  * @param hArg Handle to an argument. Must NOT be null.
    8402             :  * @since 3.11
    8403             :  */
    8404           8 : double GDALAlgorithmArgGetAsDouble(GDALAlgorithmArgH hArg)
    8405             : {
    8406           8 :     VALIDATE_POINTER1(hArg, __func__, 0);
    8407           8 :     if (hArg->ptr->GetType() != GAAT_REAL)
    8408             :     {
    8409           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8410             :                  "%s must only be called on arguments of type GAAT_REAL",
    8411             :                  __func__);
    8412           1 :         return 0;
    8413             :     }
    8414           7 :     return hArg->ptr->Get<double>();
    8415             : }
    8416             : 
    8417             : /************************************************************************/
    8418             : /*                  GDALAlgorithmArgGetAsStringList()                   */
    8419             : /************************************************************************/
    8420             : 
    8421             : /** Return the argument value as a string list.
    8422             :  *
    8423             :  * Must only be called on arguments whose type is GAAT_STRING_LIST.
    8424             :  *
    8425             :  * @param hArg Handle to an argument. Must NOT be null.
    8426             :  * @return a NULL terminated list of names, which must be destroyed with
    8427             :  * CSLDestroy()
    8428             : 
    8429             :  * @since 3.11
    8430             :  */
    8431           4 : char **GDALAlgorithmArgGetAsStringList(GDALAlgorithmArgH hArg)
    8432             : {
    8433           4 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8434           4 :     if (hArg->ptr->GetType() != GAAT_STRING_LIST)
    8435             :     {
    8436           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8437             :                  "%s must only be called on arguments of type GAAT_STRING_LIST",
    8438             :                  __func__);
    8439           1 :         return nullptr;
    8440             :     }
    8441           6 :     return CPLStringList(hArg->ptr->Get<std::vector<std::string>>())
    8442           3 :         .StealList();
    8443             : }
    8444             : 
    8445             : /************************************************************************/
    8446             : /*                  GDALAlgorithmArgGetAsIntegerList()                  */
    8447             : /************************************************************************/
    8448             : 
    8449             : /** Return the argument value as a integer list.
    8450             :  *
    8451             :  * Must only be called on arguments whose type is GAAT_INTEGER_LIST.
    8452             :  *
    8453             :  * @param hArg Handle to an argument. Must NOT be null.
    8454             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8455             :  * @since 3.11
    8456             :  */
    8457           8 : const int *GDALAlgorithmArgGetAsIntegerList(GDALAlgorithmArgH hArg,
    8458             :                                             size_t *pnCount)
    8459             : {
    8460           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8461           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8462           8 :     if (hArg->ptr->GetType() != GAAT_INTEGER_LIST)
    8463             :     {
    8464           1 :         CPLError(
    8465             :             CE_Failure, CPLE_AppDefined,
    8466             :             "%s must only be called on arguments of type GAAT_INTEGER_LIST",
    8467             :             __func__);
    8468           1 :         *pnCount = 0;
    8469           1 :         return nullptr;
    8470             :     }
    8471           7 :     const auto &val = hArg->ptr->Get<std::vector<int>>();
    8472           7 :     *pnCount = val.size();
    8473           7 :     return val.data();
    8474             : }
    8475             : 
    8476             : /************************************************************************/
    8477             : /*                  GDALAlgorithmArgGetAsDoubleList()                   */
    8478             : /************************************************************************/
    8479             : 
    8480             : /** Return the argument value as a real list.
    8481             :  *
    8482             :  * Must only be called on arguments whose type is GAAT_REAL_LIST.
    8483             :  *
    8484             :  * @param hArg Handle to an argument. Must NOT be null.
    8485             :  * @param[out] pnCount Pointer to the number of values in the list. Must NOT be null.
    8486             :  * @since 3.11
    8487             :  */
    8488           8 : const double *GDALAlgorithmArgGetAsDoubleList(GDALAlgorithmArgH hArg,
    8489             :                                               size_t *pnCount)
    8490             : {
    8491           8 :     VALIDATE_POINTER1(hArg, __func__, nullptr);
    8492           8 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
    8493           8 :     if (hArg->ptr->GetType() != GAAT_REAL_LIST)
    8494             :     {
    8495           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    8496             :                  "%s must only be called on arguments of type GAAT_REAL_LIST",
    8497             :                  __func__);
    8498           1 :         *pnCount = 0;
    8499           1 :         return nullptr;
    8500             :     }
    8501           7 :     const auto &val = hArg->ptr->Get<std::vector<double>>();
    8502           7 :     *pnCount = val.size();
    8503           7 :     return val.data();
    8504             : }
    8505             : 
    8506             : /************************************************************************/
    8507             : /*                    GDALAlgorithmArgSetAsBoolean()                    */
    8508             : /************************************************************************/
    8509             : 
    8510             : /** Set the value for a GAAT_BOOLEAN argument.
    8511             :  *
    8512             :  * It cannot be called several times for a given argument.
    8513             :  * Validation checks and other actions are run.
    8514             :  *
    8515             :  * @param hArg Handle to an argument. Must NOT be null.
    8516             :  * @param value value.
    8517             :  * @return true if success.
    8518             :  * @since 3.11
    8519             :  */
    8520             : 
    8521         682 : bool GDALAlgorithmArgSetAsBoolean(GDALAlgorithmArgH hArg, bool value)
    8522             : {
    8523         682 :     VALIDATE_POINTER1(hArg, __func__, false);
    8524         682 :     return hArg->ptr->Set(value);
    8525             : }
    8526             : 
    8527             : /************************************************************************/
    8528             : /*                    GDALAlgorithmArgSetAsString()                     */
    8529             : /************************************************************************/
    8530             : 
    8531             : /** Set the value for a GAAT_STRING argument.
    8532             :  *
    8533             :  * It cannot be called several times for a given argument.
    8534             :  * Validation checks and other actions are run.
    8535             :  *
    8536             :  * @param hArg Handle to an argument. Must NOT be null.
    8537             :  * @param value value (may be null)
    8538             :  * @return true if success.
    8539             :  * @since 3.11
    8540             :  */
    8541             : 
    8542        3022 : bool GDALAlgorithmArgSetAsString(GDALAlgorithmArgH hArg, const char *value)
    8543             : {
    8544        3022 :     VALIDATE_POINTER1(hArg, __func__, false);
    8545        3022 :     return hArg->ptr->Set(value ? value : "");
    8546             : }
    8547             : 
    8548             : /************************************************************************/
    8549             : /*                    GDALAlgorithmArgSetAsInteger()                    */
    8550             : /************************************************************************/
    8551             : 
    8552             : /** Set the value for a GAAT_INTEGER (or GAAT_REAL) argument.
    8553             :  *
    8554             :  * It cannot be called several times for a given argument.
    8555             :  * Validation checks and other actions are run.
    8556             :  *
    8557             :  * @param hArg Handle to an argument. Must NOT be null.
    8558             :  * @param value value.
    8559             :  * @return true if success.
    8560             :  * @since 3.11
    8561             :  */
    8562             : 
    8563         471 : bool GDALAlgorithmArgSetAsInteger(GDALAlgorithmArgH hArg, int value)
    8564             : {
    8565         471 :     VALIDATE_POINTER1(hArg, __func__, false);
    8566         471 :     return hArg->ptr->Set(value);
    8567             : }
    8568             : 
    8569             : /************************************************************************/
    8570             : /*                    GDALAlgorithmArgSetAsDouble()                     */
    8571             : /************************************************************************/
    8572             : 
    8573             : /** Set the value for a GAAT_REAL argument.
    8574             :  *
    8575             :  * It cannot be called several times for a given argument.
    8576             :  * Validation checks and other actions are run.
    8577             :  *
    8578             :  * @param hArg Handle to an argument. Must NOT be null.
    8579             :  * @param value value.
    8580             :  * @return true if success.
    8581             :  * @since 3.11
    8582             :  */
    8583             : 
    8584         239 : bool GDALAlgorithmArgSetAsDouble(GDALAlgorithmArgH hArg, double value)
    8585             : {
    8586         239 :     VALIDATE_POINTER1(hArg, __func__, false);
    8587         239 :     return hArg->ptr->Set(value);
    8588             : }
    8589             : 
    8590             : /************************************************************************/
    8591             : /*                 GDALAlgorithmArgSetAsDatasetValue()                  */
    8592             : /************************************************************************/
    8593             : 
    8594             : /** Set the value for a GAAT_DATASET argument.
    8595             :  *
    8596             :  * It cannot be called several times for a given argument.
    8597             :  * Validation checks and other actions are run.
    8598             :  *
    8599             :  * @param hArg Handle to an argument. Must NOT be null.
    8600             :  * @param value Handle to a GDALArgDatasetValue. Must NOT be null.
    8601             :  * @return true if success.
    8602             :  * @since 3.11
    8603             :  */
    8604           2 : bool GDALAlgorithmArgSetAsDatasetValue(GDALAlgorithmArgH hArg,
    8605             :                                        GDALArgDatasetValueH value)
    8606             : {
    8607           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8608           2 :     VALIDATE_POINTER1(value, __func__, false);
    8609           2 :     return hArg->ptr->SetFrom(*(value->ptr));
    8610             : }
    8611             : 
    8612             : /************************************************************************/
    8613             : /*                     GDALAlgorithmArgSetDataset()                     */
    8614             : /************************************************************************/
    8615             : 
    8616             : /** Set dataset object, increasing its reference counter.
    8617             :  *
    8618             :  * @param hArg Handle to an argument. Must NOT be null.
    8619             :  * @param hDS Dataset object. May be null.
    8620             :  * @return true if success.
    8621             :  * @since 3.11
    8622             :  */
    8623             : 
    8624           2 : bool GDALAlgorithmArgSetDataset(GDALAlgorithmArgH hArg, GDALDatasetH hDS)
    8625             : {
    8626           2 :     VALIDATE_POINTER1(hArg, __func__, false);
    8627           2 :     return hArg->ptr->Set(GDALDataset::FromHandle(hDS));
    8628             : }
    8629             : 
    8630             : /************************************************************************/
    8631             : /*                  GDALAlgorithmArgSetAsStringList()                   */
    8632             : /************************************************************************/
    8633             : 
    8634             : /** Set the value for a GAAT_STRING_LIST argument.
    8635             :  *
    8636             :  * It cannot be called several times for a given argument.
    8637             :  * Validation checks and other actions are run.
    8638             :  *
    8639             :  * @param hArg Handle to an argument. Must NOT be null.
    8640             :  * @param value value as a NULL terminated list (may be null)
    8641             :  * @return true if success.
    8642             :  * @since 3.11
    8643             :  */
    8644             : 
    8645         700 : bool GDALAlgorithmArgSetAsStringList(GDALAlgorithmArgH hArg, CSLConstList value)
    8646             : {
    8647         700 :     VALIDATE_POINTER1(hArg, __func__, false);
    8648         700 :     return hArg->ptr->Set(
    8649        1400 :         static_cast<std::vector<std::string>>(CPLStringList(value)));
    8650             : }
    8651             : 
    8652             : /************************************************************************/
    8653             : /*                  GDALAlgorithmArgSetAsIntegerList()                  */
    8654             : /************************************************************************/
    8655             : 
    8656             : /** Set the value for a GAAT_INTEGER_LIST argument.
    8657             :  *
    8658             :  * It cannot be called several times for a given argument.
    8659             :  * Validation checks and other actions are run.
    8660             :  *
    8661             :  * @param hArg Handle to an argument. Must NOT be null.
    8662             :  * @param nCount Number of values in pnValues.
    8663             :  * @param pnValues Pointer to an array of integer values of size nCount.
    8664             :  * @return true if success.
    8665             :  * @since 3.11
    8666             :  */
    8667         100 : bool GDALAlgorithmArgSetAsIntegerList(GDALAlgorithmArgH hArg, size_t nCount,
    8668             :                                       const int *pnValues)
    8669             : {
    8670         100 :     VALIDATE_POINTER1(hArg, __func__, false);
    8671         100 :     return hArg->ptr->Set(std::vector<int>(pnValues, pnValues + nCount));
    8672             : }
    8673             : 
    8674             : /************************************************************************/
    8675             : /*                  GDALAlgorithmArgSetAsDoubleList()                   */
    8676             : /************************************************************************/
    8677             : 
    8678             : /** Set the value for a GAAT_REAL_LIST argument.
    8679             :  *
    8680             :  * It cannot be called several times for a given argument.
    8681             :  * Validation checks and other actions are run.
    8682             :  *
    8683             :  * @param hArg Handle to an argument. Must NOT be null.
    8684             :  * @param nCount Number of values in pnValues.
    8685             :  * @param pnValues Pointer to an array of double values of size nCount.
    8686             :  * @return true if success.
    8687             :  * @since 3.11
    8688             :  */
    8689         152 : bool GDALAlgorithmArgSetAsDoubleList(GDALAlgorithmArgH hArg, size_t nCount,
    8690             :                                      const double *pnValues)
    8691             : {
    8692         152 :     VALIDATE_POINTER1(hArg, __func__, false);
    8693         152 :     return hArg->ptr->Set(std::vector<double>(pnValues, pnValues + nCount));
    8694             : }
    8695             : 
    8696             : /************************************************************************/
    8697             : /*                    GDALAlgorithmArgSetDatasets()                     */
    8698             : /************************************************************************/
    8699             : 
    8700             : /** Set dataset objects to a GAAT_DATASET_LIST argument, increasing their reference counter.
    8701             :  *
    8702             :  * @param hArg Handle to an argument. Must NOT be null.
    8703             :  * @param nCount Number of values in pnValues.
    8704             :  * @param pahDS Pointer to an array of dataset of size nCount.
    8705             :  * @return true if success.
    8706             :  * @since 3.11
    8707             :  */
    8708             : 
    8709        1239 : bool GDALAlgorithmArgSetDatasets(GDALAlgorithmArgH hArg, size_t nCount,
    8710             :                                  GDALDatasetH *pahDS)
    8711             : {
    8712        1239 :     VALIDATE_POINTER1(hArg, __func__, false);
    8713        2478 :     std::vector<GDALArgDatasetValue> values;
    8714        2504 :     for (size_t i = 0; i < nCount; ++i)
    8715             :     {
    8716        1265 :         values.emplace_back(GDALDataset::FromHandle(pahDS[i]));
    8717             :     }
    8718        1239 :     return hArg->ptr->Set(std::move(values));
    8719             : }
    8720             : 
    8721             : /************************************************************************/
    8722             : /*                  GDALAlgorithmArgSetDatasetNames()                   */
    8723             : /************************************************************************/
    8724             : 
    8725             : /** Set dataset names to a GAAT_DATASET_LIST argument.
    8726             :  *
    8727             :  * @param hArg Handle to an argument. Must NOT be null.
    8728             :  * @param names Dataset names as a NULL terminated list (may be null)
    8729             :  * @return true if success.
    8730             :  * @since 3.11
    8731             :  */
    8732             : 
    8733         704 : bool GDALAlgorithmArgSetDatasetNames(GDALAlgorithmArgH hArg, CSLConstList names)
    8734             : {
    8735         704 :     VALIDATE_POINTER1(hArg, __func__, false);
    8736        1408 :     std::vector<GDALArgDatasetValue> values;
    8737        1473 :     for (size_t i = 0; names[i]; ++i)
    8738             :     {
    8739         769 :         values.emplace_back(names[i]);
    8740             :     }
    8741         704 :     return hArg->ptr->Set(std::move(values));
    8742             : }
    8743             : 
    8744             : /************************************************************************/
    8745             : /*                     GDALArgDatasetValueCreate()                      */
    8746             : /************************************************************************/
    8747             : 
    8748             : /** Instantiate an empty GDALArgDatasetValue
    8749             :  *
    8750             :  * @return new handle to free with GDALArgDatasetValueRelease()
    8751             :  * @since 3.11
    8752             :  */
    8753           1 : GDALArgDatasetValueH GDALArgDatasetValueCreate()
    8754             : {
    8755           1 :     return std::make_unique<GDALArgDatasetValueHS>().release();
    8756             : }
    8757             : 
    8758             : /************************************************************************/
    8759             : /*                     GDALArgDatasetValueRelease()                     */
    8760             : /************************************************************************/
    8761             : 
    8762             : /** Release a handle to a GDALArgDatasetValue
    8763             :  *
    8764             :  * @since 3.11
    8765             :  */
    8766        3062 : void GDALArgDatasetValueRelease(GDALArgDatasetValueH hValue)
    8767             : {
    8768        3062 :     delete hValue;
    8769        3062 : }
    8770             : 
    8771             : /************************************************************************/
    8772             : /*                     GDALArgDatasetValueGetName()                     */
    8773             : /************************************************************************/
    8774             : 
    8775             : /** Return the name component of the GDALArgDatasetValue
    8776             :  *
    8777             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    8778             :  * @return string whose lifetime is bound to hAlg and which must not
    8779             :  * be freed.
    8780             :  * @since 3.11
    8781             :  */
    8782           1 : const char *GDALArgDatasetValueGetName(GDALArgDatasetValueH hValue)
    8783             : {
    8784           1 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    8785           1 :     return hValue->ptr->GetName().c_str();
    8786             : }
    8787             : 
    8788             : /************************************************************************/
    8789             : /*                  GDALArgDatasetValueGetDatasetRef()                  */
    8790             : /************************************************************************/
    8791             : 
    8792             : /** Return the dataset component of the GDALArgDatasetValue.
    8793             :  *
    8794             :  * This does not modify the reference counter, hence the lifetime of the
    8795             :  * returned object is not guaranteed to exceed the one of hValue.
    8796             :  *
    8797             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    8798             :  * @since 3.11
    8799             :  */
    8800           3 : GDALDatasetH GDALArgDatasetValueGetDatasetRef(GDALArgDatasetValueH hValue)
    8801             : {
    8802           3 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    8803           3 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetRef());
    8804             : }
    8805             : 
    8806             : /************************************************************************/
    8807             : /*           GDALArgDatasetValueGetDatasetIncreaseRefCount()            */
    8808             : /************************************************************************/
    8809             : 
    8810             : /** Return the dataset component of the GDALArgDatasetValue, and increase its
    8811             :  * reference count if not null. Once done with the dataset, the caller should
    8812             :  * call GDALReleaseDataset().
    8813             :  *
    8814             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    8815             :  * @since 3.11
    8816             :  */
    8817             : GDALDatasetH
    8818        1025 : GDALArgDatasetValueGetDatasetIncreaseRefCount(GDALArgDatasetValueH hValue)
    8819             : {
    8820        1025 :     VALIDATE_POINTER1(hValue, __func__, nullptr);
    8821        1025 :     return GDALDataset::ToHandle(hValue->ptr->GetDatasetIncreaseRefCount());
    8822             : }
    8823             : 
    8824             : /************************************************************************/
    8825             : /*                     GDALArgDatasetValueSetName()                     */
    8826             : /************************************************************************/
    8827             : 
    8828             : /** Set dataset name
    8829             :  *
    8830             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    8831             :  * @param pszName Dataset name. May be null.
    8832             :  * @since 3.11
    8833             :  */
    8834             : 
    8835        1293 : void GDALArgDatasetValueSetName(GDALArgDatasetValueH hValue,
    8836             :                                 const char *pszName)
    8837             : {
    8838        1293 :     VALIDATE_POINTER0(hValue, __func__);
    8839        1293 :     hValue->ptr->Set(pszName ? pszName : "");
    8840             : }
    8841             : 
    8842             : /************************************************************************/
    8843             : /*                   GDALArgDatasetValueSetDataset()                    */
    8844             : /************************************************************************/
    8845             : 
    8846             : /** Set dataset object, increasing its reference counter.
    8847             :  *
    8848             :  * @param hValue Handle to a GDALArgDatasetValue. Must NOT be null.
    8849             :  * @param hDS Dataset object. May be null.
    8850             :  * @since 3.11
    8851             :  */
    8852             : 
    8853         729 : void GDALArgDatasetValueSetDataset(GDALArgDatasetValueH hValue,
    8854             :                                    GDALDatasetH hDS)
    8855             : {
    8856         729 :     VALIDATE_POINTER0(hValue, __func__);
    8857         729 :     hValue->ptr->Set(GDALDataset::FromHandle(hDS));
    8858             : }

Generated by: LCOV version 1.14