Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL Utilities
4 : * Purpose: Command line utility for GDAL identify, delete, rename and copy
5 : * (by file) operations.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : * ****************************************************************************
9 : * Copyright (c) 2007, Frank Warmerdam
10 : * Copyright (c) 2008-2009, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_string.h"
16 : #include "cpl_conv.h"
17 : #include "gdal_version.h"
18 : #include "gdal.h"
19 : #include "commonutils.h"
20 : #include "gdalargumentparser.h"
21 :
22 : /************************************************************************/
23 : /* GDALManageOptions() */
24 : /************************************************************************/
25 :
26 : struct GDALManageOptions
27 : {
28 : bool bRecursive = false;
29 : bool bForceRecurse = false;
30 : bool bReportFailures = false;
31 : std::string osNewName;
32 : std::string osDatasetName;
33 : std::vector<std::string> aosDatasetNames;
34 : std::string osDriverName;
35 : };
36 :
37 : /************************************************************************/
38 : /* ProcessIdentifyTarget() */
39 : /************************************************************************/
40 :
41 152 : static void ProcessIdentifyTarget(const char *pszTarget,
42 : char **papszSiblingList, bool bRecursive,
43 : bool bReportFailures, bool bForceRecurse)
44 :
45 : {
46 : GDALDriverH hDriver;
47 : VSIStatBufL sStatBuf;
48 : int i;
49 :
50 152 : hDriver = GDALIdentifyDriver(pszTarget, papszSiblingList);
51 :
52 152 : if (hDriver != nullptr)
53 119 : printf("%s: %s\n", pszTarget, GDALGetDriverShortName(hDriver));
54 33 : else if (bReportFailures)
55 11 : printf("%s: unrecognized\n", pszTarget);
56 :
57 152 : if (!bForceRecurse && (!bRecursive || hDriver != nullptr))
58 149 : return;
59 :
60 147 : if (VSIStatL(pszTarget, &sStatBuf) != 0 || !VSI_ISDIR(sStatBuf.st_mode))
61 144 : return;
62 :
63 3 : papszSiblingList = VSIReadDir(pszTarget);
64 153 : for (i = 0; papszSiblingList && papszSiblingList[i]; i++)
65 : {
66 150 : if (EQUAL(papszSiblingList[i], "..") || EQUAL(papszSiblingList[i], "."))
67 6 : continue;
68 :
69 : CPLString osSubTarget =
70 288 : CPLFormFilename(pszTarget, papszSiblingList[i], nullptr);
71 :
72 144 : ProcessIdentifyTarget(osSubTarget, papszSiblingList, bRecursive,
73 : bReportFailures, bForceRecurse);
74 : }
75 3 : CSLDestroy(papszSiblingList);
76 : }
77 :
78 : /************************************************************************/
79 : /* GDALManageAppOptionsGetParser() */
80 : /************************************************************************/
81 :
82 : static std::unique_ptr<GDALArgumentParser>
83 23 : GDALManageAppOptionsGetParser(GDALManageOptions *psOptions)
84 : {
85 : auto argParser = std::make_unique<GDALArgumentParser>(
86 23 : "gdalmanage", /* bForBinary */ true);
87 :
88 23 : argParser->add_description(
89 23 : _("Identify, delete, rename and copy raster data files."));
90 23 : argParser->add_epilog(_("For more details, consult the full documentation "
91 : "for the gdalmanage utility "
92 23 : "https://gdal.org/programs/gdalmanage.html"));
93 :
94 138 : auto addCommonOptions = [psOptions](GDALArgumentParser *subParser)
95 : {
96 46 : subParser->add_argument("-f")
97 92 : .metavar("<format>")
98 46 : .store_into(psOptions->osDriverName)
99 : .help(_("Specify format of raster file if unknown by the "
100 46 : "application."));
101 :
102 46 : subParser->add_argument("newdatasetname")
103 92 : .metavar("<newdatasetname>")
104 46 : .store_into(psOptions->osNewName)
105 46 : .help(_("Name of the new file."));
106 46 : };
107 :
108 : // Identify
109 :
110 : auto identifyParser =
111 23 : argParser->add_subparser("identify", /* bForBinary */ true);
112 23 : identifyParser->add_description(_("List data format of file(s)."));
113 :
114 23 : identifyParser->add_argument("-r")
115 23 : .flag()
116 23 : .store_into(psOptions->bRecursive)
117 23 : .help(_("Recursively scan files/folders for raster files."));
118 :
119 23 : identifyParser->add_argument("-fr")
120 23 : .flag()
121 23 : .store_into(psOptions->bRecursive)
122 23 : .store_into(psOptions->bForceRecurse)
123 : .help(_("Recursively scan folders for raster files, forcing "
124 23 : "recursion in folders recognized as valid formats."));
125 :
126 23 : identifyParser->add_argument("-u")
127 23 : .flag()
128 23 : .store_into(psOptions->bReportFailures)
129 23 : .help(_("Report failures if file type is unidentified."));
130 :
131 : // Note: this accepts multiple files
132 23 : identifyParser->add_argument("datasetname")
133 46 : .metavar("<datasetname>")
134 23 : .store_into(psOptions->aosDatasetNames)
135 23 : .remaining()
136 23 : .help(_("Name(s) of the file(s) to identify."));
137 :
138 : // Copy
139 :
140 23 : auto copyParser = argParser->add_subparser("copy", /* bForBinary */ true);
141 : copyParser->add_description(
142 23 : _("Create a copy of the raster file with a new name."));
143 :
144 23 : addCommonOptions(copyParser);
145 :
146 23 : copyParser->add_argument("datasetname")
147 46 : .metavar("<datasetname>")
148 23 : .store_into(psOptions->osDatasetName)
149 23 : .help(_("Name of the file to copy."));
150 :
151 : // Rename
152 :
153 : auto renameParser =
154 23 : argParser->add_subparser("rename", /* bForBinary */ true);
155 23 : renameParser->add_description(_("Change the name of the raster file."));
156 :
157 23 : addCommonOptions(renameParser);
158 :
159 23 : renameParser->add_argument("datasetname")
160 46 : .metavar("<datasetname>")
161 23 : .store_into(psOptions->osDatasetName)
162 23 : .help(_("Name of the file to rename."));
163 :
164 : // Delete
165 :
166 : auto deleteParser =
167 23 : argParser->add_subparser("delete", /* bForBinary */ true);
168 23 : deleteParser->add_description(_("Delete the raster file(s)."));
169 :
170 : // Note: this accepts multiple files
171 23 : deleteParser->add_argument("datasetname")
172 46 : .metavar("<datasetname>")
173 23 : .store_into(psOptions->aosDatasetNames)
174 23 : .remaining()
175 23 : .help(_("Name(s) of the file(s) to delete."));
176 :
177 23 : deleteParser->add_argument("-f")
178 46 : .metavar("<format>")
179 23 : .store_into(psOptions->osDriverName)
180 : .help(
181 23 : _("Specify format of raster file if unknown by the application."));
182 :
183 46 : return argParser;
184 : }
185 :
186 : /************************************************************************/
187 : /* main() */
188 : /************************************************************************/
189 :
190 23 : MAIN_START(argc, argv)
191 :
192 : {
193 :
194 23 : EarlySetConfigOptions(argc, argv);
195 :
196 23 : argc = GDALGeneralCmdLineProcessor(argc, &argv, 0);
197 23 : if (argc < 1)
198 0 : exit(-argc);
199 :
200 : /* -------------------------------------------------------------------- */
201 : /* Parse arguments. */
202 : /* -------------------------------------------------------------------- */
203 :
204 23 : if (argc < 2)
205 : {
206 : try
207 : {
208 2 : GDALManageOptions sOptions;
209 1 : auto argParser = GDALManageAppOptionsGetParser(&sOptions);
210 1 : fprintf(stderr, "%s\n", argParser->usage().c_str());
211 : }
212 0 : catch (const std::exception &err)
213 : {
214 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s",
215 0 : err.what());
216 : }
217 1 : CSLDestroy(argv);
218 1 : exit(1);
219 : }
220 :
221 22 : GDALAllRegister();
222 :
223 22 : GDALManageOptions psOptions;
224 22 : auto argParser = GDALManageAppOptionsGetParser(&psOptions);
225 :
226 : try
227 : {
228 22 : argParser->parse_args_without_binary_name(argv + 1);
229 18 : CSLDestroy(argv);
230 : }
231 2 : catch (const std::exception &error)
232 : {
233 2 : argParser->display_error_and_usage(error);
234 2 : CSLDestroy(argv);
235 2 : exit(1);
236 : }
237 :
238 : // For some obscure reason datasetname is parsed as mandatory
239 : // if used with remaining() in a subparser
240 18 : if (psOptions.aosDatasetNames.empty() && psOptions.osDatasetName.empty())
241 : {
242 : std::invalid_argument error(
243 : _("No dataset name provided. At least one dataset "
244 1 : "name is required."));
245 1 : argParser->display_error_and_usage(error);
246 1 : exit(1);
247 : }
248 :
249 17 : GDALDriverH hDriver = nullptr;
250 17 : if (!psOptions.osDriverName.empty())
251 : {
252 2 : hDriver = GDALGetDriverByName(psOptions.osDriverName.c_str());
253 2 : if (hDriver == nullptr)
254 : {
255 1 : CPLError(CE_Failure, CPLE_AppDefined, "Failed to find driver '%s'.",
256 : psOptions.osDriverName.c_str());
257 1 : exit(1);
258 : }
259 : }
260 :
261 : /* -------------------------------------------------------------------- */
262 : /* Split out based on operation. */
263 : /* -------------------------------------------------------------------- */
264 :
265 16 : if (argParser->is_subcommand_used("identify"))
266 : {
267 : // Process all files in aosDatasetName
268 15 : for (const auto &datasetName : psOptions.aosDatasetNames)
269 : {
270 8 : ProcessIdentifyTarget(
271 8 : datasetName.c_str(), nullptr, psOptions.bRecursive,
272 8 : psOptions.bReportFailures, psOptions.bForceRecurse);
273 : }
274 : }
275 9 : else if (argParser->is_subcommand_used("copy"))
276 : {
277 6 : GDALCopyDatasetFiles(hDriver, psOptions.osDatasetName.c_str(),
278 : psOptions.osNewName.c_str());
279 : }
280 3 : else if (argParser->is_subcommand_used("rename"))
281 : {
282 1 : GDALRenameDataset(hDriver, psOptions.osDatasetName.c_str(),
283 : psOptions.osNewName.c_str());
284 : }
285 2 : else if (argParser->is_subcommand_used("delete"))
286 : {
287 : // Process all files in aosDatasetName
288 5 : for (const auto &datasetName : psOptions.aosDatasetNames)
289 : {
290 3 : GDALDeleteDataset(hDriver, datasetName.c_str());
291 : }
292 : }
293 :
294 : /* -------------------------------------------------------------------- */
295 : /* Cleanup */
296 : /* -------------------------------------------------------------------- */
297 16 : GDALDestroy();
298 :
299 16 : exit(0);
300 : }
301 :
302 0 : MAIN_END
|