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

Generated by: LCOV version 1.14