LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3510 3746 93.7 %
Date: 2026-03-26 23:25:44 Functions: 271 278 97.5 %

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

Generated by: LCOV version 1.14