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

Generated by: LCOV version 1.14