Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: CLI front-end
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 "gdalalgorithm.h"
14 : #include "commonutils.h"
15 : #include "cpl_error.h"
16 :
17 : #include "gdal.h"
18 :
19 : #include <cassert>
20 : #include <utility>
21 :
22 : // #define DEBUG_COMPLETION
23 :
24 : /************************************************************************/
25 : /* EmitCompletion() */
26 : /************************************************************************/
27 :
28 : /** Return on stdout a space-separated list of choices for bash completion */
29 95 : static void EmitCompletion(std::unique_ptr<GDALAlgorithm> rootAlg,
30 : const std::vector<std::string> &argsIn,
31 : bool lastWordIsComplete)
32 : {
33 : #ifdef DEBUG_COMPLETION
34 : for (size_t i = 0; i < argsIn.size(); ++i)
35 : fprintf(stderr, "arg[%d]='%s'\n", static_cast<int>(i),
36 : argsIn[i].c_str());
37 : #endif
38 :
39 95 : std::vector<std::string> args = argsIn;
40 :
41 95 : std::string ret;
42 34363 : const auto addSpace = [&ret]()
43 : {
44 17225 : if (!ret.empty())
45 17138 : ret += " ";
46 17320 : };
47 :
48 282 : if (!args.empty() &&
49 94 : (args.back() == "--config" ||
50 184 : STARTS_WITH(args.back().c_str(), "--config=") ||
51 180 : (args.size() >= 2 && args[args.size() - 2] == "--config")))
52 : {
53 5 : if (args.back() == "--config=" || args.back().back() != '=')
54 : {
55 6 : CPLStringList aosConfigOptions(CPLGetKnownConfigOptions());
56 3192 : for (const char *pszOpt : cpl::Iterate(aosConfigOptions))
57 : {
58 3189 : addSpace();
59 3189 : ret += pszOpt;
60 3189 : ret += '=';
61 : }
62 3 : printf("%s", ret.c_str());
63 : }
64 5 : return;
65 : }
66 :
67 14126 : for (const auto &choice : rootAlg->GetAutoComplete(
68 14126 : args, lastWordIsComplete, /*showAllOptions = */ true))
69 : {
70 14036 : addSpace();
71 14036 : ret += CPLString(choice).replaceAll(" ", "\\ ");
72 : }
73 :
74 : #ifdef DEBUG_COMPLETION
75 : fprintf(stderr, "ret = '%s'\n", ret.c_str());
76 : #endif
77 90 : if (!ret.empty())
78 84 : printf("%s", ret.c_str());
79 : }
80 :
81 : /************************************************************************/
82 : /* main() */
83 : /************************************************************************/
84 :
85 149 : MAIN_START(argc, argv)
86 : {
87 149 : const bool bIsCompletion = argc >= 3 && strcmp(argv[1], "completion") == 0;
88 :
89 149 : if (bIsCompletion)
90 : {
91 190 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
92 95 : EarlySetConfigOptions(argc, argv);
93 : }
94 : else
95 : {
96 54 : EarlySetConfigOptions(argc, argv);
97 : }
98 :
99 149 : auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
100 447 : GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
101 149 : assert(alg);
102 :
103 : // Register GDAL drivers
104 149 : GDALAllRegister();
105 :
106 149 : if (bIsCompletion)
107 : {
108 95 : const bool bLastWordIsComplete =
109 95 : EQUAL(argv[argc - 1], "last_word_is_complete=true");
110 95 : if (STARTS_WITH(argv[argc - 1], "last_word_is_complete="))
111 2 : --argc;
112 :
113 : // Process lines like "gdal completion gdal raster last_word_is_complete=true|false"
114 95 : EmitCompletion(std::move(alg),
115 190 : std::vector<std::string>(argv + 3, argv + argc),
116 : bLastWordIsComplete);
117 95 : return 0;
118 : }
119 :
120 : // Prevent GDALGeneralCmdLineProcessor() to process --format XXX, unless
121 : // "gdal" is invoked only with it. Cf #12411
122 108 : std::vector<std::pair<char **, char *>> apOrigFormat;
123 54 : constexpr const char *pszFormatReplaced = "--format-XXXX";
124 54 : if (!(argc == 3 && strcmp(argv[1], "--format") == 0))
125 : {
126 266 : for (int i = 1; i < argc; ++i)
127 : {
128 213 : if (strcmp(argv[i], "--format") == 0)
129 : {
130 1 : apOrigFormat.emplace_back(argv + i, argv[i]);
131 1 : argv[i] = const_cast<char *>(pszFormatReplaced);
132 : }
133 : }
134 : }
135 :
136 : // Process generic cmomand options
137 54 : argc = GDALGeneralCmdLineProcessor(
138 : argc, &argv, GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
139 55 : for (auto &pair : apOrigFormat)
140 : {
141 1 : *(pair.first) = pair.second;
142 : }
143 :
144 54 : if (argc < 1)
145 2 : return (-argc);
146 :
147 104 : std::vector<std::string> args;
148 262 : for (int i = 1; i < argc; ++i)
149 419 : args.push_back(strcmp(argv[i], pszFormatReplaced) == 0 ? "--format"
150 209 : : argv[i]);
151 52 : CSLDestroy(argv);
152 :
153 52 : alg->SetCalledFromCommandLine();
154 :
155 52 : if (!alg->ParseCommandLineArguments(args))
156 : {
157 13 : if (strstr(CPLGetLastErrorMsg(), "Do you mean") == nullptr &&
158 10 : strstr(CPLGetLastErrorMsg(), "Should be one among") == nullptr &&
159 10 : strstr(CPLGetLastErrorMsg(), "Potential values for argument") ==
160 23 : nullptr &&
161 7 : strstr(CPLGetLastErrorMsg(),
162 : "Single potential value for argument") == nullptr)
163 : {
164 5 : fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str());
165 : }
166 13 : return 1;
167 : }
168 :
169 : {
170 39 : const auto stdoutArg = alg->GetActualAlgorithm().GetArg("stdout");
171 39 : if (stdoutArg && stdoutArg->GetType() == GAAT_BOOLEAN)
172 4 : stdoutArg->Set(true);
173 : }
174 :
175 : GDALProgressFunc pfnProgress =
176 39 : alg->IsProgressBarRequested() ? GDALTermProgress : nullptr;
177 39 : void *pProgressData = nullptr;
178 :
179 39 : int ret = 0;
180 39 : if (alg->Run(pfnProgress, pProgressData) && alg->Finalize())
181 : {
182 : const auto outputArg =
183 38 : alg->GetActualAlgorithm().GetArg("output-string");
184 46 : if (outputArg && outputArg->GetType() == GAAT_STRING &&
185 8 : outputArg->IsOutput())
186 : {
187 8 : printf("%s", outputArg->Get<std::string>().c_str());
188 : }
189 : }
190 : else
191 : {
192 1 : ret = 1;
193 : }
194 :
195 39 : return ret;
196 : }
197 :
198 0 : MAIN_END
|