LCOV - code coverage report
Current view: top level - apps - gdal.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 120 123 97.6 %
Date: 2025-08-01 10:10:57 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          97 : 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          97 :     std::vector<std::string> args = argsIn;
      40             : 
      41          97 :     std::string ret;
      42       34739 :     const auto addSpace = [&ret]()
      43             :     {
      44       17414 :         if (!ret.empty())
      45       17325 :             ret += " ";
      46       17511 :     };
      47             : 
      48         288 :     if (!args.empty() &&
      49          96 :         (args.back() == "--config" ||
      50         188 :          STARTS_WITH(args.back().c_str(), "--config=") ||
      51         184 :          (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        3225 :             for (const char *pszOpt : cpl::Iterate(aosConfigOptions))
      57             :             {
      58        3222 :                 addSpace();
      59        3222 :                 ret += pszOpt;
      60        3222 :                 ret += '=';
      61             :             }
      62           3 :             printf("%s", ret.c_str());
      63             :         }
      64           5 :         return;
      65             :     }
      66             : 
      67       14284 :     for (const auto &choice : rootAlg->GetAutoComplete(
      68       14284 :              args, lastWordIsComplete, /*showAllOptions = */ true))
      69             :     {
      70       14192 :         addSpace();
      71       14192 :         ret += CPLString(choice).replaceAll(" ", "\\ ");
      72             :     }
      73             : 
      74             : #ifdef DEBUG_COMPLETION
      75             :     fprintf(stderr, "ret = '%s'\n", ret.c_str());
      76             : #endif
      77          92 :     if (!ret.empty())
      78          86 :         printf("%s", ret.c_str());
      79             : }
      80             : 
      81             : /************************************************************************/
      82             : /*                                main()                                */
      83             : /************************************************************************/
      84             : 
      85         188 : MAIN_START(argc, argv)
      86             : {
      87         188 :     const bool bIsCompletion = argc >= 3 && strcmp(argv[1], "completion") == 0;
      88             : 
      89         188 :     if (bIsCompletion)
      90             :     {
      91         194 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
      92          97 :         EarlySetConfigOptions(argc, argv);
      93             :     }
      94             :     else
      95             :     {
      96          91 :         EarlySetConfigOptions(argc, argv);
      97         403 :         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         338 :             if (strcmp(argv[i], "--config-options-in-stdin") == 0)
     102             :             {
     103          52 :                 std::string line;
     104          26 :                 constexpr int LINE_SIZE = 10 * 1024;
     105          26 :                 line.resize(LINE_SIZE);
     106         100 :                 while (fgets(line.data(), LINE_SIZE, stdin))
     107             :                 {
     108         174 :                     if (strcmp(line.c_str(), "--config\n") == 0 &&
     109          74 :                         fgets(line.data(), LINE_SIZE, stdin))
     110             :                     {
     111         148 :                         std::string osLine(line.c_str());
     112          74 :                         if (!osLine.empty() && osLine.back() == '\n')
     113             :                         {
     114          74 :                             osLine.pop_back();
     115          74 :                             char *pszUnescaped = CPLUnescapeString(
     116             :                                 osLine.c_str(), nullptr, CPLES_URL);
     117          74 :                             char *pszKey = nullptr;
     118             :                             const char *pszValue =
     119          74 :                                 CPLParseNameValue(pszUnescaped, &pszKey);
     120          74 :                             if (pszKey && pszValue)
     121             :                             {
     122          74 :                                 CPLSetConfigOption(pszKey, pszValue);
     123             :                             }
     124          74 :                             CPLFree(pszKey);
     125          74 :                             CPLFree(pszUnescaped);
     126             :                         }
     127             :                     }
     128          26 :                     else if (strcmp(line.c_str(), "END_CONFIG\n") == 0)
     129             :                     {
     130          26 :                         break;
     131             :                     }
     132             :                 }
     133             : 
     134          26 :                 break;
     135             :             }
     136             :         }
     137             :     }
     138             : 
     139         188 :     auto alg = GDALGlobalAlgorithmRegistry::GetSingleton().Instantiate(
     140         564 :         GDALGlobalAlgorithmRegistry::ROOT_ALG_NAME);
     141         188 :     assert(alg);
     142             : 
     143             :     // Register GDAL drivers
     144         188 :     GDALAllRegister();
     145             : 
     146         188 :     if (bIsCompletion)
     147             :     {
     148          97 :         const bool bLastWordIsComplete =
     149          97 :             EQUAL(argv[argc - 1], "last_word_is_complete=true");
     150          97 :         if (STARTS_WITH(argv[argc - 1], "last_word_is_complete="))
     151           2 :             --argc;
     152          95 :         else if (argc >= 2 && STARTS_WITH(argv[argc - 2], "prev=") &&
     153           2 :                  STARTS_WITH(argv[argc - 1], "cur="))
     154             :         {
     155           2 :             const char *pszPrevVal = argv[argc - 2] + strlen("prev=");
     156           2 :             const char *pszCurVal = argv[argc - 1] + strlen("cur=");
     157           4 :             std::string osCurVal;
     158           2 :             const bool bIsPrevValEqual = (strcmp(pszPrevVal, "=") == 0);
     159           2 :             if (bIsPrevValEqual)
     160             :             {
     161           1 :                 osCurVal = std::string("=").append(pszCurVal);
     162           1 :                 pszCurVal = osCurVal.c_str();
     163             :             }
     164           2 :             int iMatch = 0;
     165          15 :             for (int i = 3; i < argc - 1; ++i)
     166             :             {
     167          20 :                 if (bIsPrevValEqual ? (strstr(argv[i], pszCurVal) != nullptr)
     168           7 :                                     : (strcmp(argv[i], pszCurVal) == 0))
     169             :                 {
     170           2 :                     if (iMatch == 0)
     171           2 :                         iMatch = i;
     172             :                     else
     173           0 :                         iMatch = -1;
     174             :                 }
     175             :             }
     176           2 :             if (iMatch > 0)
     177           2 :                 argc = iMatch + 1;
     178             :             else
     179           0 :                 argc -= 2;
     180             :         }
     181             : 
     182             :         // Process lines like "gdal completion gdal raster last_word_is_complete=true|false"
     183          97 :         EmitCompletion(std::move(alg),
     184         194 :                        std::vector<std::string>(argv + 3, argv + argc),
     185             :                        bLastWordIsComplete);
     186          97 :         return 0;
     187             :     }
     188             : 
     189             :     // Prevent GDALGeneralCmdLineProcessor() to process --format XXX, unless
     190             :     // "gdal" is invoked only with it. Cf #12411
     191         182 :     std::vector<std::pair<char **, char *>> apOrigFormat;
     192          91 :     constexpr const char *pszFormatReplaced = "--format-XXXX";
     193          91 :     if (!(argc == 3 && strcmp(argv[1], "--format") == 0))
     194             :     {
     195        1112 :         for (int i = 1; i < argc; ++i)
     196             :         {
     197        1022 :             if (strcmp(argv[i], "--format") == 0)
     198             :             {
     199           4 :                 apOrigFormat.emplace_back(argv + i, argv[i]);
     200           4 :                 argv[i] = const_cast<char *>(pszFormatReplaced);
     201             :             }
     202             :         }
     203             :     }
     204             : 
     205             :     // Process generic cmomand options
     206          91 :     argc = GDALGeneralCmdLineProcessor(
     207             :         argc, &argv, GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
     208          95 :     for (auto &pair : apOrigFormat)
     209             :     {
     210           4 :         *(pair.first) = pair.second;
     211             :     }
     212             : 
     213          91 :     if (argc < 1)
     214           5 :         return (-argc);
     215             : 
     216         172 :     std::vector<std::string> args;
     217         998 :     for (int i = 1; i < argc; ++i)
     218        1820 :         args.push_back(strcmp(argv[i], pszFormatReplaced) == 0 ? "--format"
     219         908 :                                                                : argv[i]);
     220          86 :     CSLDestroy(argv);
     221             : 
     222          86 :     alg->SetCalledFromCommandLine();
     223             : 
     224          86 :     if (!alg->ParseCommandLineArguments(args))
     225             :     {
     226          13 :         if (strstr(CPLGetLastErrorMsg(), "Do you mean") == nullptr &&
     227          10 :             strstr(CPLGetLastErrorMsg(), "Should be one among") == nullptr &&
     228          10 :             strstr(CPLGetLastErrorMsg(), "Potential values for argument") ==
     229          23 :                 nullptr &&
     230           7 :             strstr(CPLGetLastErrorMsg(),
     231             :                    "Single potential value for argument") == nullptr)
     232             :         {
     233           5 :             fprintf(stderr, "%s", alg->GetUsageForCLI(true).c_str());
     234             :         }
     235          13 :         return 1;
     236             :     }
     237             : 
     238             :     {
     239          73 :         const auto stdoutArg = alg->GetActualAlgorithm().GetArg("stdout");
     240          73 :         if (stdoutArg && stdoutArg->GetType() == GAAT_BOOLEAN)
     241          39 :             stdoutArg->Set(true);
     242             :     }
     243             : 
     244             :     GDALProgressFunc pfnProgress =
     245          73 :         alg->IsProgressBarRequested() ? GDALTermProgress : nullptr;
     246          73 :     void *pProgressData = nullptr;
     247             : 
     248          73 :     int ret = 0;
     249          73 :     if (alg->Run(pfnProgress, pProgressData) && alg->Finalize())
     250             :     {
     251             :         const auto outputArg =
     252          70 :             alg->GetActualAlgorithm().GetArg("output-string");
     253         113 :         if (outputArg && outputArg->GetType() == GAAT_STRING &&
     254          43 :             outputArg->IsOutput())
     255             :         {
     256          43 :             printf("%s", outputArg->Get<std::string>().c_str());
     257             :         }
     258             :     }
     259             :     else
     260             :     {
     261           3 :         ret = 1;
     262             :     }
     263             : 
     264          73 :     return ret;
     265             : }
     266             : 
     267           0 : MAIN_END

Generated by: LCOV version 1.14