LCOV - code coverage report
Current view: top level - gcore - gdalalgorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3449 3665 94.1 %
Date: 2025-10-21 22:35:35 Functions: 266 273 97.4 %

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

Generated by: LCOV version 1.14