Skip to content

Commit

Permalink
Add gdalargumentparser.h/cpp
Browse files Browse the repository at this point in the history
  • Loading branch information
rouault committed Mar 12, 2024
1 parent 2a3cbf4 commit c66a063
Show file tree
Hide file tree
Showing 3 changed files with 305 additions and 0 deletions.
1 change: 1 addition & 0 deletions apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_library(
appslib OBJECT
commonutils.h
gdal_utils.h
gdalargumentparser.cpp
gdalinfo_lib.cpp
gdalbuildvrt_lib.cpp
gdal_grid_lib.cpp
Expand Down
222 changes: 222 additions & 0 deletions apps/gdalargumentparser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
/******************************************************************************
* Project: GDAL Utilities
* Purpose: GDAL argument parser
* Author: Even Rouault <even.rouault at spatialys.com>
*
* ****************************************************************************
* Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/

#include "gdal_version_full/gdal_version.h"

#include "gdal.h"
#include "gdalargumentparser.h"
#include "commonutils.h"

/************************************************************************/
/* GDALArgumentParser() */
/************************************************************************/

GDALArgumentParser::GDALArgumentParser(const std::string &program_name,
bool bForBinary)
: ArgumentParser(program_name, "", default_arguments::none)
{
set_usage_max_line_width(120);
set_usage_break_on_mutex();
add_usage_newline();

if (bForBinary)
{
add_argument("-h", "--help")
.action(
[this, program_name](const auto &)
{
std::cout << usage() << std::endl << std::endl;
std::cout << _("Note: ") << program_name
<< _(" --long-usage for full help.") << std::endl;
std::exit(0);
})
.flag()
.help(_("Shows short help message and exits"));
add_argument("--long-usage")
.action(
[this](const auto & /*unused*/)
{
std::cout << *this;
std::exit(0);
})
.flag()
.help(_("Shows long help message and exits"));
add_argument("--help-general")
.flag()
.help(_("Report detailed help on general options"));
add_argument("--utility_version")
.action(
[program_name](const auto &)
{
printf("%s was compiled against GDAL %s and "
"is running against GDAL %s\n",
program_name.c_str(), GDAL_RELEASE_NAME,
GDALVersionInfo("RELEASE_NAME"));
std::exit(0);
})
.flag()
.help(_("Shows compile-time and run-time GDAL version"));
}
}

/************************************************************************/
/* display_error_and_usage() */
/************************************************************************/

void GDALArgumentParser::display_error_and_usage(const std::exception &err)
{
std::cerr << _("Error: ") << err.what() << std::endl;
std::cerr << usage() << std::endl << std::endl;
std::cout << _("Note: ") << m_program_name
<< _(" --long-usage for full help.") << std::endl;
}

/************************************************************************/
/* add_quiet_argument() */
/************************************************************************/

void GDALArgumentParser::add_quiet_argument(bool *pVar)
{
auto &arg =
this->add_argument("-q", "--quiet")
.flag()
.help(
_("Quiet mode. No progress message is emitted on the standard "
"output."));
if (pVar)
arg.store_into(*pVar);
}

/************************************************************************/
/* add_output_format_argument() */
/************************************************************************/

void GDALArgumentParser::add_output_format_argument(std::string &var)
{
auto &arg = add_argument("-of")
.metavar("<output_format>")
.store_into(var)
.help(_("Output format"));
add_hidden_alias_for(arg, "-f");
}

/************************************************************************/
/* add_creation_options_argument() */
/************************************************************************/

void GDALArgumentParser::add_creation_options_argument(CPLStringList &var)
{
add_argument("-co")
.metavar("<KEY=VALUE>")
.append()
.action([&var](const std::string &s) { var.AddString(s.c_str()); })
.help("Creation option(s)");
}

/************************************************************************/
/* parse_args_without_binary_name() */
/************************************************************************/

void GDALArgumentParser::parse_args_without_binary_name(CSLConstList papszArgs)
{
CPLStringList aosArgs;
aosArgs.AddString(m_program_name.c_str());
for (CSLConstList papszIter = papszArgs; papszIter && *papszIter;
++papszIter)
aosArgs.AddString(*papszIter);
parse_args(aosArgs);
}

/************************************************************************/
/* parse_args() */
/************************************************************************/

void GDALArgumentParser::parse_args(const CPLStringList &aosArgs)
{
std::vector<std::string> reorderedArgs;
std::vector<std::string> positionalArgs;

// ArgumentParser::parse_args() expects the first argument to be the
// binary name
if (!aosArgs.empty())
{
reorderedArgs.push_back(aosArgs[0]);
}

// Simplified logic borrowed from ArgumentParser::parse_args_internal()
// that make sure that positional arguments are moved after optional ones,
// as this is what ArgumentParser::parse_args() only supports.
// This doesn't support advanced settings, such as sub-parsers or compound
// argument
std::vector<std::string> raw_arguments{aosArgs.List(),
aosArgs.List() + aosArgs.size()};
auto arguments = preprocess_arguments(raw_arguments);
auto end = std::end(arguments);
auto positional_argument_it = std::begin(m_positional_arguments);
for (auto it = std::next(std::begin(arguments)); it != end;)
{
const auto &current_argument = *it;
if (Argument::is_positional(current_argument, m_prefix_chars))
{
auto argument = positional_argument_it++;
auto next_it = argument->consume(it, end, "", /* dry_run = */ true);
for (; it != next_it; ++it)
{
if ((*it)[0] == '-')
{
next_it = it;
break;
}
positionalArgs.push_back(*it);
}
it = next_it;
continue;
}

auto arg_map_it = m_argument_map.find(current_argument);
if (arg_map_it != m_argument_map.end())
{
auto argument = arg_map_it->second;
auto next_it = argument->consume(
std::next(it), end, arg_map_it->first, /* dry_run = */ true);
for (; it != next_it; ++it)
{
reorderedArgs.push_back(*it);
}
it = next_it;
}
else
{
throw std::runtime_error("Unknown argument: " + current_argument);
}
}

reorderedArgs.insert(reorderedArgs.end(), positionalArgs.begin(),
positionalArgs.end());

ArgumentParser::parse_args(reorderedArgs);
}
82 changes: 82 additions & 0 deletions apps/gdalargumentparser.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/******************************************************************************
* Project: GDAL Utilities
* Purpose: GDAL argument parser
* Author: Even Rouault <even.rouault at spatialys.com>
*
* ****************************************************************************
* Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/

#ifndef GDALARGUMENTPARSER_H
#define GDALARGUMENTPARSER_H

#include "cpl_port.h"
#include "cpl_conv.h"
#include "cpl_string.h"

// Rename argparse namespace to a GDAL private one
#define argparse gdal_argparse

// Use our locale-unaware strtod()
#define ARGPARSE_CUSTOM_STRTOD CPLStrtodM

#include "argparse/argparse.hpp"

using namespace argparse;

// Place-holder macro using gettext() convention to indicate (future) translatable strings
#ifndef _
#define _(x) (x)
#endif

/** Parse command-line arguments for GDAL utilities.
*
* Add helpers over the standard argparse::ArgumentParser class
*
* @since GDAL 3.9
*/
class CPL_DLL GDALArgumentParser : public ArgumentParser
{
public:
//! Constructor
explicit GDALArgumentParser(const std::string &program_name,
bool bForBinary);

//! Format an exception as an error message and display the program usage
void display_error_and_usage(const std::exception &err);

//! Add -q/--quiet argument, and store its value in *pVar (if pVar not null)
void add_quiet_argument(bool *pVar);

//! Add "-of format_name" argument for output format, and store its value into var.
void add_output_format_argument(std::string &var);

//! Add "-co KEY=VALUE" argument for creation options, and store its value into var.
void add_creation_options_argument(CPLStringList &var);

//! Parse command line arguments, without the initial program name.
void parse_args_without_binary_name(CSLConstList papszArgs);

//! Parse command line arguments, with the initial program name.
void parse_args(const CPLStringList &aosArgs);
};

#endif /* GDALARGUMENTPARSER_H */

0 comments on commit c66a063

Please sign in to comment.