LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3380 3586 94.3 %
Date: 2025-09-17 18:03:44 Functions: 259 265 97.7 %

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

Generated by: LCOV version 1.14