LCOV - code coverage report
Current view: top level - apps - gdal.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 102 104 98.1 %
Date: 2025-07-09 17:50:03 Functions: 3 3 100.0 %

          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       34435 :     const auto addSpace = [&ret]()
      43             :     {
      44       17261 :         if (!ret.empty())
      45       17174 :             ret += " ";
      46       17356 :     };
      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        3222 :             for (const char *pszOpt : cpl::Iterate(aosConfigOptions))
      57             :             {
      58        3219 :                 addSpace();
      59        3219 :                 ret += pszOpt;
      60        3219 :                 ret += '=';
      61             :             }
      62           3 :             printf("%s", ret.c_str());
      63             :         }
      64           5 :         return;
      65             :     }
      66             : 
      67       14132 :     for (const auto &choice : rootAlg->GetAutoComplete(
      68       14132 :              args, lastWordIsComplete, /*showAllOptions = */ true))
      69             :     {
      70       14042 :         addSpace();
      71       14042 :         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         165 : MAIN_START(argc, argv)
      86             : {
      87         165 :     const bool bIsCompletion = argc >= 3 && strcmp(argv[1], "completion") == 0;
      88             : 
      89         165 :     if (bIsCompletion)
      90             :     {
      91         190 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
      92          95 :         EarlySetConfigOptions(argc, argv);
      93             :     }
      94             :     else
      95             :     {
      96          70 :         EarlySetConfigOptions(argc, argv);
      97         315 :         for (int i = 1; i < argc; ++i)
      98             :         {
      99             :             // Used by gdal raster tile --parallel-method=spawn to pass
     100             :             // config options in a stealth way
     101         259 :             if (strcmp(argv[i], "--config-options-in-stdin") == 0)
     102             :             {
     103          28 :                 std::string line;
     104          14 :                 constexpr int LINE_SIZE = 10 * 1024;
     105          14 :                 line.resize(LINE_SIZE);
     106          78 :                 while (fgets(line.data(), LINE_SIZE, stdin))
     107             :                 {
     108         114 :                     if (strcmp(line.c_str(), "--config\n") == 0 &&
     109          50 :                         fgets(line.data(), LINE_SIZE, stdin))
     110             :                     {
     111         100 :                         std::string osLine(line.c_str());
     112          50 :                         if (!osLine.empty() && osLine.back() == '\n')
     113             :                         {
     114          50 :                             osLine.pop_back();
     115          50 :                             char *pszUnescaped = CPLUnescapeString(
     116             :                                 osLine.c_str(), nullptr, CPLES_URL);
     117          50 :                             char *pszKey = nullptr;
     118             :                             const char *pszValue =
     119          50 :                                 CPLParseNameValue(pszUnescaped, &pszKey);
     120          50 :                             if (pszKey && pszValue)
     121             :                             {
     122          50 :                                 CPLSetConfigOption(pszKey, pszValue);
     123             :                             }
     124          50 :                             CPLFree(pszKey);
     125          50 :                             CPLFree(pszUnescaped);
     126             :                         }
     127             :                     }
     128          14 :                     else if (strcmp(line.c_str(), "--END\n") == 0)
     129             :                     {
     130           0 :                         break;
     131             :                     }
     132             :                 }
     133             : 
     134          14 :                 break;
     135             :             }
     136             :         }
     137             :     }
     138             : 
     139         165 :     auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
     140         495 :         GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
     141         165 :     assert(alg);
     142             : 
     143             :     // Register GDAL drivers
     144         165 :     GDALAllRegister();
     145             : 
     146         165 :     if (bIsCompletion)
     147             :     {
     148          95 :         const bool bLastWordIsComplete =
     149          95 :             EQUAL(argv[argc - 1], "last_word_is_complete=true");
     150          95 :         if (STARTS_WITH(argv[argc - 1], "last_word_is_complete="))
     151           2 :             --argc;
     152             : 
     153             :         // Process lines like "gdal completion gdal raster last_word_is_complete=true|false"
     154          95 :         EmitCompletion(std::move(alg),
     155         190 :                        std::vector<std::string>(argv + 3, argv + argc),
     156             :                        bLastWordIsComplete);
     157          95 :         return 0;
     158             :     }
     159             : 
     160             :     // Prevent GDALGeneralCmdLineProcessor() to process --format XXX, unless
     161             :     // "gdal" is invoked only with it. Cf #12411
     162         140 :     std::vector<std::pair<char **, char *>> apOrigFormat;
     163          70 :     constexpr const char *pszFormatReplaced = "--format-XXXX";
     164          70 :     if (!(argc == 3 && strcmp(argv[1], "--format") == 0))
     165             :     {
     166         704 :         for (int i = 1; i < argc; ++i)
     167             :         {
     168         635 :             if (strcmp(argv[i], "--format") == 0)
     169             :             {
     170           1 :                 apOrigFormat.emplace_back(argv + i, argv[i]);
     171           1 :                 argv[i] = const_cast<char *>(pszFormatReplaced);
     172             :             }
     173             :         }
     174             :     }
     175             : 
     176             :     // Process generic cmomand options
     177          70 :     argc = GDALGeneralCmdLineProcessor(
     178             :         argc, &argv, GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
     179          71 :     for (auto &pair : apOrigFormat)
     180             :     {
     181           1 :         *(pair.first) = pair.second;
     182             :     }
     183             : 
     184          70 :     if (argc < 1)
     185           4 :         return (-argc);
     186             : 
     187         132 :     std::vector<std::string> args;
     188         640 :     for (int i = 1; i < argc; ++i)
     189        1147 :         args.push_back(strcmp(argv[i], pszFormatReplaced) == 0 ? "--format"
     190         573 :                                                                : argv[i]);
     191          66 :     CSLDestroy(argv);
     192             : 
     193          66 :     alg->SetCalledFromCommandLine();
     194             : 
     195          66 :     if (!alg->ParseCommandLineArguments(args))
     196             :     {
     197          13 :         if (strstr(CPLGetLastErrorMsg(), "Do you mean") == nullptr &&
     198          10 :             strstr(CPLGetLastErrorMsg(), "Should be one among") == nullptr &&
     199          10 :             strstr(CPLGetLastErrorMsg(), "Potential values for argument") ==
     200          23 :                 nullptr &&
     201           7 :             strstr(CPLGetLastErrorMsg(),
     202             :                    "Single potential value for argument") == nullptr)
     203             :         {
     204           5 :             fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str());
     205             :         }
     206          13 :         return 1;
     207             :     }
     208             : 
     209             :     {
     210          53 :         const auto stdoutArg = alg->GetActualAlgorithm().GetArg("stdout");
     211          53 :         if (stdoutArg && stdoutArg->GetType() == GAAT_BOOLEAN)
     212           4 :             stdoutArg->Set(true);
     213             :     }
     214             : 
     215             :     GDALProgressFunc pfnProgress =
     216          53 :         alg->IsProgressBarRequested() ? GDALTermProgress : nullptr;
     217          53 :     void *pProgressData = nullptr;
     218             : 
     219          53 :     int ret = 0;
     220          53 :     if (alg->Run(pfnProgress, pProgressData) && alg->Finalize())
     221             :     {
     222             :         const auto outputArg =
     223          50 :             alg->GetActualAlgorithm().GetArg("output-string");
     224          58 :         if (outputArg && outputArg->GetType() == GAAT_STRING &&
     225           8 :             outputArg->IsOutput())
     226             :         {
     227           8 :             printf("%s", outputArg->Get<std::string>().c_str());
     228             :         }
     229             :     }
     230             :     else
     231             :     {
     232           3 :         ret = 1;
     233             :     }
     234             : 
     235          53 :     return ret;
     236             : }
     237             : 
     238           0 : MAIN_END

Generated by: LCOV version 1.14