LCOV - code coverage report
Current view: top level - apps - gdalargumentparser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 179 198 90.4 %
Date: 2024-05-03 15:49:35 Functions: 24 27 88.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  GDAL Utilities
       3             :  * Purpose:  GDAL argument parser
       4             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       5             :  *
       6             :  * ****************************************************************************
       7             :  * Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  * Permission is hereby granted, free of charge, to any person obtaining a
      10             :  * copy of this software and associated documentation files (the "Software"),
      11             :  * to deal in the Software without restriction, including without limitation
      12             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      13             :  * and/or sell copies of the Software, and to permit persons to whom the
      14             :  * Software is furnished to do so, subject to the following conditions:
      15             :  *
      16             :  * The above copyright notice and this permission notice shall be included
      17             :  * in all copies or substantial portions of the Software.
      18             :  *
      19             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      20             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      22             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25             :  * DEALINGS IN THE SOFTWARE.
      26             :  ****************************************************************************/
      27             : 
      28             : #include "gdal_version_full/gdal_version.h"
      29             : 
      30             : #include "gdal.h"
      31             : #include "gdalargumentparser.h"
      32             : #include "commonutils.h"
      33             : 
      34             : #include <algorithm>
      35             : 
      36             : /************************************************************************/
      37             : /*                         GDALArgumentParser()                         */
      38             : /************************************************************************/
      39             : 
      40        3488 : GDALArgumentParser::GDALArgumentParser(const std::string &program_name,
      41        3488 :                                        bool bForBinary)
      42        3488 :     : ArgumentParser(program_name, "", default_arguments::none)
      43             : {
      44        3488 :     set_usage_max_line_width(120);
      45        3488 :     set_usage_break_on_mutex();
      46        3488 :     add_usage_newline();
      47             : 
      48        3488 :     if (bForBinary)
      49             :     {
      50         675 :         add_argument("-h", "--help")
      51         675 :             .flag()
      52             :             .action(
      53           0 :                 [this, program_name](const auto &)
      54             :                 {
      55           0 :                     std::cout << usage() << std::endl << std::endl;
      56           0 :                     std::cout << _("Note: ") << program_name
      57           0 :                               << _(" --long-usage for full help.") << std::endl;
      58           0 :                     std::exit(0);
      59         675 :                 })
      60         675 :             .help(_("Shows short help message and exits."));
      61             : 
      62         675 :         add_argument("--long-usage")
      63         675 :             .flag()
      64             :             .action(
      65           0 :                 [this](const auto & /*unused*/)
      66             :                 {
      67           0 :                     std::cout << *this;
      68           0 :                     std::exit(0);
      69         675 :                 })
      70         675 :             .help(_("Shows long help message and exits."));
      71             : 
      72         675 :         add_argument("--help-general")
      73         675 :             .flag()
      74         675 :             .help(_("Report detailed help on general options."));
      75             : 
      76         675 :         add_argument("--utility_version")
      77         675 :             .flag()
      78         675 :             .hidden()
      79             :             .action(
      80          25 :                 [program_name](const auto &)
      81             :                 {
      82          25 :                     printf("%s was compiled against GDAL %s and "
      83             :                            "is running against GDAL %s\n",
      84             :                            program_name.c_str(), GDAL_RELEASE_NAME,
      85             :                            GDALVersionInfo("RELEASE_NAME"));
      86          25 :                     std::exit(0);
      87         675 :                 })
      88         675 :             .help(_("Shows compile-time and run-time GDAL version."));
      89             : 
      90         675 :         add_usage_newline();
      91             :     }
      92        3488 : }
      93             : 
      94             : /************************************************************************/
      95             : /*                      display_error_and_usage()                       */
      96             : /************************************************************************/
      97             : 
      98           4 : void GDALArgumentParser::display_error_and_usage(const std::exception &err)
      99             : {
     100           4 :     std::cerr << _("Error: ") << err.what() << std::endl;
     101           4 :     std::cerr << usage() << std::endl << std::endl;
     102           4 :     std::cout << _("Note: ") << m_program_name
     103           4 :               << _(" --long-usage for full help.") << std::endl;
     104           4 : }
     105             : 
     106             : /************************************************************************/
     107             : /*                         add_quiet_argument()                         */
     108             : /************************************************************************/
     109             : 
     110        3246 : Argument &GDALArgumentParser::add_quiet_argument(bool *pVar)
     111             : {
     112             :     auto &arg =
     113        3246 :         this->add_argument("-q", "--quiet")
     114        3246 :             .flag()
     115             :             .help(
     116             :                 _("Quiet mode. No progress message is emitted on the standard "
     117        3246 :                   "output."));
     118        3246 :     if (pVar)
     119        1099 :         arg.store_into(*pVar);
     120             : 
     121        3246 :     return arg;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                      add_input_format_argument()                     */
     126             : /************************************************************************/
     127             : 
     128        1628 : Argument &GDALArgumentParser::add_input_format_argument(CPLStringList *pvar)
     129             : {
     130        1628 :     return add_argument("-if")
     131        1628 :         .append()
     132        3256 :         .metavar("<format>")
     133             :         .action(
     134          16 :             [pvar](const std::string &s)
     135             :             {
     136           8 :                 if (pvar)
     137             :                 {
     138           8 :                     if (GDALGetDriverByName(s.c_str()) == nullptr)
     139             :                     {
     140           2 :                         CPLError(CE_Warning, CPLE_AppDefined,
     141             :                                  "%s is not a recognized driver", s.c_str());
     142             :                     }
     143           8 :                     pvar->AddString(s.c_str());
     144             :                 }
     145        1628 :             })
     146             :         .help(
     147        3256 :             _("Format/driver name(s) to be attempted to open the input file."));
     148             : }
     149             : 
     150             : /************************************************************************/
     151             : /*                      add_output_format_argument()                    */
     152             : /************************************************************************/
     153             : 
     154        3102 : Argument &GDALArgumentParser::add_output_format_argument(std::string &var)
     155             : {
     156        3102 :     auto &arg = add_argument("-of")
     157        6204 :                     .metavar("<output_format>")
     158        3102 :                     .store_into(var)
     159        3102 :                     .help(_("Output format."));
     160        3102 :     add_hidden_alias_for(arg, "-f");
     161        3102 :     return arg;
     162             : }
     163             : 
     164             : /************************************************************************/
     165             : /*                     add_creation_options_argument()                  */
     166             : /************************************************************************/
     167             : 
     168        1534 : Argument &GDALArgumentParser::add_creation_options_argument(CPLStringList &var)
     169             : {
     170        1534 :     return add_argument("-co")
     171        3068 :         .metavar("<NAME>=<VALUE>")
     172        1534 :         .append()
     173        2175 :         .action([&var](const std::string &s) { var.AddString(s.c_str()); })
     174        3068 :         .help(_("Creation option(s)."));
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                   add_metadata_item_options_argument()               */
     179             : /************************************************************************/
     180             : 
     181             : Argument &
     182        2053 : GDALArgumentParser::add_metadata_item_options_argument(CPLStringList &var)
     183             : {
     184        2053 :     return add_argument("-mo")
     185        4106 :         .metavar("<NAME>=<VALUE>")
     186        2053 :         .append()
     187        2080 :         .action([&var](const std::string &s) { var.AddString(s.c_str()); })
     188        4106 :         .help(_("Metadata item option(s)."));
     189             : }
     190             : 
     191             : /************************************************************************/
     192             : /*                       add_open_options_argument()                    */
     193             : /************************************************************************/
     194             : 
     195           0 : Argument &GDALArgumentParser::add_open_options_argument(CPLStringList &var)
     196             : {
     197           0 :     return add_open_options_argument(&var);
     198             : }
     199             : 
     200             : /************************************************************************/
     201             : /*                       add_open_options_argument()                    */
     202             : /************************************************************************/
     203             : 
     204        3100 : Argument &GDALArgumentParser::add_open_options_argument(CPLStringList *pvar)
     205             : {
     206        3100 :     auto &arg = add_argument("-oo")
     207        6200 :                     .metavar("<NAME>=<VALUE>")
     208        3100 :                     .append()
     209        3100 :                     .help(_("Open option(s) for input dataset."));
     210        3100 :     if (pvar)
     211             :     {
     212          14 :         arg.action([pvar](const std::string &s)
     213         626 :                    { pvar->AddString(s.c_str()); });
     214             :     }
     215             : 
     216        3100 :     return arg;
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                       add_output_type_argument()                     */
     221             : /************************************************************************/
     222             : 
     223        2286 : Argument &GDALArgumentParser::add_output_type_argument(GDALDataType &eDT)
     224             : {
     225        2286 :     return add_argument("-ot")
     226        4572 :         .metavar("Byte|Int8|[U]Int{16|32|64}|CInt{16|32}|[C]Float{32|64}")
     227             :         .action(
     228         948 :             [&eDT](const std::string &s)
     229             :             {
     230         316 :                 eDT = GDALGetDataTypeByName(s.c_str());
     231         316 :                 if (eDT == GDT_Unknown)
     232             :                 {
     233             :                     throw std::invalid_argument(
     234           0 :                         std::string("Unknown output pixel type: ").append(s));
     235             :                 }
     236        2602 :             })
     237        4572 :         .help(_("Output data type."));
     238             : }
     239             : 
     240             : Argument &
     241         736 : GDALArgumentParser::add_layer_creation_options_argument(CPLStringList &var)
     242             : {
     243         736 :     return add_argument("-lco")
     244        1472 :         .metavar("<NAME>=<VALUE>")
     245         736 :         .append()
     246        1024 :         .action([&var](const std::string &s) { var.AddString(s.c_str()); })
     247        1472 :         .help(_("Layer creation options (format specific)."));
     248             : }
     249             : 
     250             : /************************************************************************/
     251             : /*                     parse_args_without_binary_name()                 */
     252             : /************************************************************************/
     253             : 
     254        3406 : void GDALArgumentParser::parse_args_without_binary_name(CSLConstList papszArgs)
     255             : {
     256        6793 :     CPLStringList aosArgs;
     257        3406 :     aosArgs.AddString(m_program_name.c_str());
     258       20984 :     for (CSLConstList papszIter = papszArgs; papszIter && *papszIter;
     259             :          ++papszIter)
     260       17578 :         aosArgs.AddString(*papszIter);
     261        3406 :     parse_args(aosArgs);
     262        3361 : }
     263             : 
     264             : /************************************************************************/
     265             : /*                           find_argument()                            */
     266             : /************************************************************************/
     267             : 
     268             : std::map<std::string, ArgumentParser::argument_it>::iterator
     269        8991 : GDALArgumentParser::find_argument(const std::string &name)
     270             : {
     271        8991 :     auto arg_map_it = m_argument_map.find(name);
     272        8991 :     if (arg_map_it == m_argument_map.end())
     273             :     {
     274             :         // Attempt case insensitive lookup
     275             :         arg_map_it =
     276             :             std::find_if(m_argument_map.begin(), m_argument_map.end(),
     277         280 :                          [&name](const auto &oArg)
     278         142 :                          { return EQUAL(name.c_str(), oArg.first.c_str()); });
     279             :     }
     280        8991 :     return arg_map_it;
     281             : }
     282             : 
     283             : /************************************************************************/
     284             : /*                    get_non_positional_arguments()                    */
     285             : /************************************************************************/
     286             : 
     287             : CPLStringList
     288         698 : GDALArgumentParser::get_non_positional_arguments(const CPLStringList &aosArgs)
     289             : {
     290         698 :     CPLStringList args;
     291             : 
     292             :     // Simplified logic borrowed from ArgumentParser::parse_args_internal()
     293             :     // that make sure that positional arguments are moved after optional ones,
     294             :     // as this is what ArgumentParser::parse_args() only supports.
     295             :     // This doesn't support advanced settings, such as sub-parsers or compound
     296             :     // argument
     297        2792 :     std::vector<std::string> raw_arguments{m_program_name};
     298         698 :     raw_arguments.insert(raw_arguments.end(), aosArgs.List(),
     299        1396 :                          aosArgs.List() + aosArgs.size());
     300        1396 :     auto arguments = preprocess_arguments(raw_arguments);
     301         698 :     auto end = std::end(arguments);
     302         698 :     auto positional_argument_it = std::begin(m_positional_arguments);
     303        2198 :     for (auto it = std::next(std::begin(arguments)); it != end;)
     304             :     {
     305        1501 :         const auto &current_argument = *it;
     306        1501 :         if (Argument::is_positional(current_argument, m_prefix_chars))
     307             :         {
     308         270 :             if (positional_argument_it != std::end(m_positional_arguments))
     309             :             {
     310         270 :                 auto argument = positional_argument_it++;
     311             :                 auto next_it =
     312         270 :                     argument->consume(it, end, "", /* dry_run = */ true);
     313         270 :                 it = next_it;
     314         270 :                 continue;
     315             :             }
     316             :             else
     317             :             {
     318           0 :                 if (m_positional_arguments.empty())
     319             :                 {
     320             :                     throw std::runtime_error(
     321           0 :                         "Zero positional arguments expected");
     322             :                 }
     323             :                 else
     324             :                 {
     325             :                     throw std::runtime_error(
     326             :                         "Maximum number of positional arguments "
     327           0 :                         "exceeded, failed to parse '" +
     328           0 :                         current_argument + "'");
     329             :                 }
     330             :             }
     331             :         }
     332             : 
     333        1231 :         auto arg_map_it = find_argument(current_argument);
     334        1231 :         if (arg_map_it != m_argument_map.end())
     335             :         {
     336        1231 :             auto argument = arg_map_it->second;
     337             :             auto next_it = argument->consume(
     338        1231 :                 std::next(it), end, arg_map_it->first, /* dry_run = */ true);
     339             :             // Add official argument name (correcting possible case)
     340        1230 :             args.AddString(arg_map_it->first.c_str());
     341        1230 :             ++it;
     342             :             // Add its values
     343        2389 :             for (; it != next_it; ++it)
     344             :             {
     345        1159 :                 args.AddString(it->c_str());
     346             :             }
     347        1230 :             it = next_it;
     348             :         }
     349             :         else
     350             :         {
     351           0 :             throw std::runtime_error("Unknown argument: " + current_argument);
     352             :         }
     353             :     }
     354             : 
     355        1394 :     return args;
     356             : }
     357             : 
     358         530 : Argument &GDALArgumentParser::add_inverted_logic_flag(const std::string &name,
     359             :                                                       bool *store_into,
     360             :                                                       const std::string &help)
     361             : {
     362        1060 :     return add_argument(name)
     363         530 :         .default_value(true)
     364        1060 :         .implicit_value(false)
     365             :         .action(
     366          22 :             [store_into](const auto &)
     367             :             {
     368          11 :                 if (store_into)
     369          11 :                     *store_into = false;
     370         530 :             })
     371        1060 :         .help(help);
     372             : }
     373             : 
     374             : /************************************************************************/
     375             : /*                           parse_args()                               */
     376             : /************************************************************************/
     377             : 
     378        3483 : void GDALArgumentParser::parse_args(const CPLStringList &aosArgs)
     379             : {
     380        6941 :     std::vector<std::string> reorderedArgs;
     381        6941 :     std::vector<std::string> positionalArgs;
     382             : 
     383             :     // ArgumentParser::parse_args() expects the first argument to be the
     384             :     // binary name
     385        3483 :     if (!aosArgs.empty())
     386             :     {
     387        3483 :         reorderedArgs.push_back(aosArgs[0]);
     388             :     }
     389             : 
     390             :     // Simplified logic borrowed from ArgumentParser::parse_args_internal()
     391             :     // that make sure that positional arguments are moved after optional ones,
     392             :     // as this is what ArgumentParser::parse_args() only supports.
     393             :     // This doesn't support advanced settings, such as sub-parsers or compound
     394             :     // argument
     395             :     std::vector<std::string> raw_arguments{aosArgs.List(),
     396        6941 :                                            aosArgs.List() + aosArgs.size()};
     397        6941 :     auto arguments = preprocess_arguments(raw_arguments);
     398        3483 :     auto end = std::end(arguments);
     399        3483 :     auto positional_argument_it = std::begin(m_positional_arguments);
     400       12346 :     for (auto it = std::next(std::begin(arguments)); it != end;)
     401             :     {
     402        8864 :         const auto &current_argument = *it;
     403        8864 :         if (Argument::is_positional(current_argument, m_prefix_chars))
     404             :         {
     405        1104 :             if (positional_argument_it != std::end(m_positional_arguments))
     406             :             {
     407        1103 :                 auto argument = positional_argument_it++;
     408             :                 auto next_it =
     409        1103 :                     argument->consume(it, end, "", /* dry_run = */ true);
     410        2359 :                 for (; it != next_it; ++it)
     411             :                 {
     412        1265 :                     if (!Argument::is_positional(*it, m_prefix_chars))
     413             :                     {
     414           9 :                         next_it = it;
     415           9 :                         break;
     416             :                     }
     417        1256 :                     positionalArgs.push_back(*it);
     418             :                 }
     419        1103 :                 it = next_it;
     420        1103 :                 continue;
     421             :             }
     422             :             else
     423             :             {
     424           1 :                 if (m_positional_arguments.empty())
     425             :                 {
     426             :                     throw std::runtime_error(
     427           1 :                         "Zero positional arguments expected");
     428             :                 }
     429             :                 else
     430             :                 {
     431             :                     throw std::runtime_error(
     432             :                         "Maximum number of positional arguments "
     433           0 :                         "exceeded, failed to parse '" +
     434           0 :                         current_argument + "'");
     435             :                 }
     436             :             }
     437             :         }
     438             : 
     439        7760 :         auto arg_map_it = find_argument(current_argument);
     440        7760 :         if (arg_map_it != m_argument_map.end())
     441             :         {
     442        7760 :             auto argument = arg_map_it->second;
     443             :             auto next_it = argument->consume(
     444        7760 :                 std::next(it), end, arg_map_it->first, /* dry_run = */ true);
     445             :             // Add official argument name (correcting possible case)
     446        7760 :             reorderedArgs.push_back(arg_map_it->first);
     447        7760 :             ++it;
     448             :             // Add its values
     449       16706 :             for (; it != next_it; ++it)
     450             :             {
     451        8946 :                 reorderedArgs.push_back(*it);
     452             :             }
     453        7760 :             it = next_it;
     454             :         }
     455             :         else
     456             :         {
     457           0 :             throw std::runtime_error("Unknown argument: " + current_argument);
     458             :         }
     459             :     }
     460             : 
     461        3482 :     reorderedArgs.insert(reorderedArgs.end(), positionalArgs.begin(),
     462        6964 :                          positionalArgs.end());
     463             : 
     464        3482 :     ArgumentParser::parse_args(reorderedArgs);
     465        3428 : }

Generated by: LCOV version 1.14