LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3088 3264 94.6 %
Date: 2025-08-01 10:10:57 Functions: 237 240 98.8 %

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

Generated by: LCOV version 1.14