Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/apps/gdalalg_dispatcher.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  gdal subcommand dispatcher
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
#ifndef GDALALG_DISPATCHER_INCLUDED
14
#define GDALALG_DISPATCHER_INCLUDED
15
16
#include "gdalalgorithm.h"
17
18
#include "gdal_priv.h"
19
#include "cpl_error.h"
20
21
//! @cond Doxygen_Suppress
22
23
/************************************************************************/
24
/*                       GDALDispatcherAlgorithm                        */
25
/************************************************************************/
26
27
template <class RasterDispatcher, class VectorDispatcher>
28
class GDALDispatcherAlgorithm : public GDALAlgorithm
29
{
30
  public:
31
    GDALDispatcherAlgorithm(const std::string &name,
32
                            const std::string &description,
33
                            const std::string &helpURL)
34
0
        : GDALAlgorithm(name, description, helpURL),
35
0
          m_rasterDispatcher(std::make_unique<RasterDispatcher>(
36
0
              /* standalone = */ true, /* openForMixedRasterVector = */ true)),
37
          m_vectorDispatcher(
38
0
              std::make_unique<VectorDispatcher>(/* standalone = */ true))
39
0
    {
40
        // A "info" dispatcher command is a shortcut for something like
41
        // "raster info", "vector info". Best to expose the latter.
42
0
        SetDisplayInJSONUsage(false);
43
0
    }
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterConvertAlgorithm, GDALVectorConvertAlgorithm>::GDALDispatcherAlgorithm(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterInfoAlgorithm, GDALVectorInfoAlgorithm>::GDALDispatcherAlgorithm(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&)
44
45
    bool
46
    ParseCommandLineArguments(const std::vector<std::string> &args) override;
47
48
    std::string GetUsageForCLI(bool shortUsage,
49
                               const UsageOptions &usageOptions) const override;
50
51
  private:
52
    std::unique_ptr<RasterDispatcher> m_rasterDispatcher{};
53
    std::unique_ptr<VectorDispatcher> m_vectorDispatcher{};
54
    bool m_showUsage = true;
55
56
    bool RunImpl(GDALProgressFunc, void *) override
57
0
    {
58
0
        CPLError(CE_Failure, CPLE_AppDefined,
59
0
                 "The Run() method should not be called directly on the \"gdal "
60
0
                 "%s\" program.",
61
0
                 GetName().c_str());
62
0
        return false;
63
0
    }
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterInfoAlgorithm, GDALVectorInfoAlgorithm>::RunImpl(int (*)(double, char const*, void*), void*)
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterConvertAlgorithm, GDALVectorConvertAlgorithm>::RunImpl(int (*)(double, char const*, void*), void*)
64
};
65
66
/************************************************************************/
67
/*         GDALDispatcherAlgorithm::ParseCommandLineArguments()         */
68
/************************************************************************/
69
70
template <class RasterDispatcher, class VectorDispatcher>
71
bool GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::
72
    ParseCommandLineArguments(const std::vector<std::string> &args)
73
0
{
74
0
    if (args.size() == 1 && (args[0] == "-h" || args[0] == "--help"))
75
0
        return GDALAlgorithm::ParseCommandLineArguments(args);
76
77
0
    if (IsCalledFromCommandLine())
78
0
    {
79
0
        m_rasterDispatcher->SetCalledFromCommandLine();
80
0
        m_vectorDispatcher->SetCalledFromCommandLine();
81
0
    }
82
83
    // We first try to process with the raster specific algorithm (that has
84
    // been instantiated in a special way to accept both raster and vector
85
    // input datasets). If the raster specific algorithm can parse successfully
86
    // the arguments *and* the dataset is a raster one, then continue processing
87
    // with it. Otherwise try with the vector specific algorithm.
88
89
0
    bool ok;
90
0
    std::string osLastError;
91
0
    if (args.size() > 1)
92
0
    {
93
        // Silence errors as it might be rather for the vector algorithm
94
0
        CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
95
0
        const auto nCounter = CPLGetErrorCounter();
96
0
        ok = m_rasterDispatcher->ParseCommandLineArguments(args);
97
0
        if (CPLGetErrorCounter() > nCounter &&
98
0
            CPLGetLastErrorType() == CE_Failure)
99
0
            osLastError = CPLGetLastErrorMsg();
100
0
    }
101
0
    else
102
0
    {
103
        // If there's just a single argument, we don't need to silence errors
104
        // as this will trigger a legitimate error message about the subcommand.
105
0
        ok = m_rasterDispatcher->ParseCommandLineArguments(args);
106
0
    }
107
108
0
    if (m_rasterDispatcher->PropagateSpecialActionTo(this))
109
0
    {
110
0
        return true;
111
0
    }
112
113
0
    if (ok)
114
0
    {
115
0
        auto poDS = m_rasterDispatcher->GetInputDatasetRef();
116
        // cppcheck-suppress knownConditionTrueFalse
117
0
        if (poDS &&
118
0
            (poDS->GetRasterCount() > 0 || poDS->GetMetadata("SUBDATASETS")))
119
0
        {
120
0
            if (poDS->GetLayerCount() != 0)
121
0
            {
122
0
                m_showUsage = false;
123
0
                CPLError(CE_Failure, CPLE_AppDefined,
124
0
                         "'%s' has both raster and vector content. "
125
0
                         "Please use 'gdal raster %s' or 'gdal vector %s'.",
126
0
                         poDS->GetDescription(), GetName().c_str(),
127
0
                         GetName().c_str());
128
0
                return false;
129
0
            }
130
131
0
            m_selectedSubAlg = m_rasterDispatcher.get();
132
0
            std::vector<std::string> callPath(m_callPath);
133
0
            callPath.push_back("raster");
134
0
            m_selectedSubAlg->SetCallPath(callPath);
135
136
0
            return true;
137
0
        }
138
0
    }
139
0
    else if (args.size() <= 1)
140
0
    {
141
0
        return false;
142
0
    }
143
144
0
    auto poDSFromRaster = m_rasterDispatcher->GetInputDatasetRef();
145
    // cppcheck-suppress knownConditionTrueFalse
146
0
    if (poDSFromRaster)
147
0
    {
148
0
        m_vectorDispatcher->SetInputDataset(poDSFromRaster);
149
0
    }
150
151
0
    std::vector<std::string> argsWithoutInput;
152
0
    bool skipNext = false;
153
0
    std::string osLikelyDatasetName;
154
0
    size_t nCountLikelyDatasetName = 0;
155
0
    for (const auto &arg : args)
156
0
    {
157
0
        if (arg == "-i" || arg == "--input")
158
0
        {
159
0
            skipNext = true;
160
0
        }
161
0
        else if (!skipNext)
162
0
        {
163
0
            if (!STARTS_WITH(arg.c_str(), "--input=") &&
164
0
                !(poDSFromRaster && arg == poDSFromRaster->GetDescription()))
165
0
            {
166
0
                if (!arg.empty() && arg[0] != '-')
167
0
                {
168
0
                    ++nCountLikelyDatasetName;
169
0
                    osLikelyDatasetName = arg;
170
0
                }
171
0
                argsWithoutInput.push_back(arg);
172
0
            }
173
0
        }
174
0
        else
175
0
        {
176
0
            skipNext = false;
177
0
        }
178
0
    }
179
180
0
    {
181
0
        CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
182
0
        ok = m_vectorDispatcher->ParseCommandLineArguments(argsWithoutInput);
183
0
    }
184
0
    if (ok)
185
0
    {
186
0
        m_selectedSubAlg = m_vectorDispatcher.get();
187
0
        std::vector<std::string> callPath(m_callPath);
188
0
        callPath.push_back("vector");
189
0
        m_selectedSubAlg->SetCallPath(callPath);
190
191
0
        return true;
192
0
    }
193
194
0
    bool ret = false;
195
0
    bool managedToOpenDS = false;
196
0
    for (const auto &arg : args)
197
0
    {
198
0
        VSIStatBufL sStat;
199
0
        if (VSIStatL(arg.c_str(), &sStat) == 0)
200
0
        {
201
0
            auto poDS =
202
0
                std::unique_ptr<GDALDataset>(GDALDataset::Open(arg.c_str()));
203
0
            if (poDS)
204
0
            {
205
0
                managedToOpenDS = true;
206
0
                if (poDS->GetRasterCount() > 0 ||
207
0
                    poDS->GetMetadata("SUBDATASETS"))
208
0
                {
209
0
                    if (poDS->GetLayerCount() != 0)
210
0
                    {
211
0
                        m_showUsage = false;
212
0
                        CPLError(CE_Failure, CPLE_AppDefined,
213
0
                                 "'%s' has both raster and vector content. "
214
0
                                 "Please use 'gdal raster %s' or 'gdal "
215
0
                                 "vector %s'.",
216
0
                                 poDS->GetDescription(), GetName().c_str(),
217
0
                                 GetName().c_str());
218
0
                        return false;
219
0
                    }
220
0
                    m_rasterDispatcher = std::make_unique<RasterDispatcher>();
221
0
                    auto poDSRaw = poDS.get();
222
0
                    m_rasterDispatcher->SetInputDataset(poDS.release());
223
0
                    poDSRaw->Release();
224
0
                    m_selectedSubAlg = m_rasterDispatcher.get();
225
0
                    std::vector<std::string> callPath(m_callPath);
226
0
                    callPath.push_back("raster");
227
0
                    m_selectedSubAlg->SetCallPath(callPath);
228
0
                    ret = m_selectedSubAlg->ParseCommandLineArguments(
229
0
                        argsWithoutInput);
230
0
                }
231
0
                else if (poDS->GetLayerCount() != 0)
232
0
                {
233
0
                    m_vectorDispatcher = std::make_unique<VectorDispatcher>();
234
0
                    auto poDSRaw = poDS.get();
235
0
                    m_vectorDispatcher->SetInputDataset(poDS.release());
236
0
                    poDSRaw->Release();
237
0
                    m_selectedSubAlg = m_vectorDispatcher.get();
238
0
                    std::vector<std::string> callPath(m_callPath);
239
0
                    callPath.push_back("vector");
240
0
                    m_selectedSubAlg->SetCallPath(callPath);
241
0
                    ret = m_selectedSubAlg->ParseCommandLineArguments(
242
0
                        argsWithoutInput);
243
0
                }
244
0
            }
245
0
            break;
246
0
        }
247
0
    }
248
249
0
    if (!ret && !managedToOpenDS &&
250
0
        (osLastError.find("not recognized") != std::string::npos ||
251
0
         (nCountLikelyDatasetName == 1 &&
252
0
          cpl::starts_with(osLastError, osLikelyDatasetName))))
253
0
    {
254
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", osLastError.c_str());
255
0
    }
256
257
0
    return ret;
258
0
}
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterInfoAlgorithm, GDALVectorInfoAlgorithm>::ParseCommandLineArguments(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterConvertAlgorithm, GDALVectorConvertAlgorithm>::ParseCommandLineArguments(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)
259
260
/************************************************************************/
261
/*              GDALDispatcherAlgorithm::GetUsageForCLI()               */
262
/************************************************************************/
263
264
template <class RasterDispatcher, class VectorDispatcher>
265
std::string
266
GDALDispatcherAlgorithm<RasterDispatcher, VectorDispatcher>::GetUsageForCLI(
267
    bool shortUsage, const UsageOptions &usageOptions) const
268
0
{
269
0
    if (m_selectedSubAlg)
270
0
    {
271
0
        return m_selectedSubAlg->GetUsageForCLI(shortUsage, usageOptions);
272
0
    }
273
0
    if (m_showUsage)
274
0
    {
275
0
        return GDALAlgorithm::GetUsageForCLI(shortUsage, usageOptions);
276
0
    }
277
0
    return std::string();
278
0
}
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterInfoAlgorithm, GDALVectorInfoAlgorithm>::GetUsageForCLI(bool, GDALAlgorithm::UsageOptions const&) const
Unexecuted instantiation: GDALDispatcherAlgorithm<GDALRasterConvertAlgorithm, GDALVectorConvertAlgorithm>::GetUsageForCLI(bool, GDALAlgorithm::UsageOptions const&) const
279
280
//! @endcond
281
282
#endif  // GDALALG_DISPATCHER_INCLUDED