/***
Oak Video Editor - Non-Linear Video Editor
Copyright (C) 2026 Oak Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
***/
#include "common/commandlineparser.h"
#include
#include
#include
#include
#include "../src/commandlineparser.h"
#include "refcounted.h"
namespace
{
/**
* @brief State boxed behind an option handle's ctx pointer.
*
* The option pointer is borrowed: the option itself is owned by the
* parser, so releasing the box never destroys it.
*/
struct OptionState {
CommandLineParser::Option *option;
};
/**
* @brief State boxed behind a positional-argument handle's ctx pointer.
*
* The argument pointer is borrowed: the argument itself is owned by the
* parser, so releasing the box never destroys it.
*/
struct PositionalArgumentState {
CommandLineParser::PositionalArgument *argument;
};
CommandLineParser *clp(OakCommandLineParser parser)
{
return oakcommon::handle_impl(parser.ctx);
}
CommandLineParser::Option *clo(OakCommandLineOption option)
{
OptionState *state =
oakcommon::handle_impl(option.ctx);
return state ? state->option : nullptr;
}
CommandLineParser::PositionalArgument *clpa(
OakCommandLinePositionalArgument argument)
{
PositionalArgumentState *state =
oakcommon::handle_impl(argument.ctx);
return state ? state->argument : nullptr;
}
} // namespace
OakCommandLineParser oakcommon_commandlineparser_init(void)
{
try {
return oakcommon::make_handle_in_place();
} catch (...) {
OakCommandLineParser h = {};
return h;
}
}
void oakcommon_commandlineparser_free(OakCommandLineParser *parser)
{
oakcommon::free_handle(parser);
}
int oakcommon_commandlineparser_set_app_info(OakCommandLineParser parser,
const char *name,
const char *version)
{
if (!clp(parser) || !name) {
return OAKCOMMON_E_INVALID;
}
try {
clp(parser)->set_app_info(name, version ? version : "");
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineparser_add_option(
OakCommandLineParser parser, const char *const *names, int name_count,
const char *description, int takes_arg, const char *arg_placeholder,
int hidden, OakCommandLineOption *out_option)
{
if (!clp(parser) || !names || name_count <= 0) {
return OAKCOMMON_E_INVALID;
}
try {
std::vector strings;
strings.reserve(static_cast(name_count));
for (int i = 0; i < name_count; i++) {
if (!names[i]) {
return OAKCOMMON_E_INVALID;
}
strings.emplace_back(names[i]);
}
const CommandLineParser::Option *option = clp(parser)->add_option(
strings, description ? description : "", takes_arg != 0,
arg_placeholder ? arg_placeholder : "", hidden != 0);
if (out_option) {
*out_option = oakcommon::make_handle(
OptionState{const_cast(option)});
if (!out_option->ctx) {
return OAKCOMMON_E_NOMEM;
}
}
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineparser_add_positional_argument(
OakCommandLineParser parser, const char *name,
const char *description, int required,
OakCommandLinePositionalArgument *out_argument)
{
if (!clp(parser) || !name) {
return OAKCOMMON_E_INVALID;
}
try {
const CommandLineParser::PositionalArgument *argument =
clp(parser)->add_positional_argument(
name, description ? description : "", required != 0);
if (out_argument) {
*out_argument =
oakcommon::make_handle(
PositionalArgumentState{
const_cast(
argument)});
if (!out_argument->ctx) {
return OAKCOMMON_E_NOMEM;
}
}
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineparser_process(OakCommandLineParser parser,
const char *const *argv, int argc)
{
if (!clp(parser) || !argv || argc < 0) {
return OAKCOMMON_E_INVALID;
}
try {
std::vector args;
args.reserve(static_cast(argc));
for (int i = 0; i < argc; i++) {
args.emplace_back(argv[i] ? argv[i] : "");
}
clp(parser)->process(args);
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineparser_print_help(OakCommandLineParser parser,
const char *filename)
{
if (!clp(parser) || !filename) {
return OAKCOMMON_E_INVALID;
}
try {
clp(parser)->print_help(filename);
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineoption_is_set(OakCommandLineOption option,
bool *is_set)
{
if (!clo(option) || !is_set) {
return OAKCOMMON_E_INVALID;
}
try {
*is_set = clo(option)->is_set();
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
/**
* @brief Shared two-stage string getter.
*
* Returns the required buffer size in bytes (including the terminating
* NUL) as a non-negative value, or a negative error code.
*/
static int copy_setting(const std::string &value, char *buf, int buf_size)
{
int required = static_cast(value.size()) + 1;
if (buf && buf_size > 0) {
size_t copy_len = value.size();
if (copy_len > static_cast(buf_size) - 1) {
copy_len = static_cast(buf_size) - 1;
}
memcpy(buf, value.data(), copy_len);
buf[copy_len] = '\0';
}
return required;
}
int oakcommon_commandlineoption_get_setting(OakCommandLineOption option,
char *buf, int buf_size)
{
if (!clo(option)) {
return OAKCOMMON_E_INVALID;
}
try {
return copy_setting(clo(option)->get_setting(), buf, buf_size);
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlineoption_set_setting(OakCommandLineOption option,
const char *value)
{
if (!clo(option) || !value) {
return OAKCOMMON_E_INVALID;
}
try {
clo(option)->set_setting(value);
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlinepositionalargument_get_setting(
OakCommandLinePositionalArgument argument, char *buf, int buf_size)
{
if (!clpa(argument)) {
return OAKCOMMON_E_INVALID;
}
try {
return copy_setting(clpa(argument)->get_setting(), buf, buf_size);
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
int oakcommon_commandlinepositionalargument_set_setting(
OakCommandLinePositionalArgument argument, const char *value)
{
if (!clpa(argument) || !value) {
return OAKCOMMON_E_INVALID;
}
try {
clpa(argument)->set_setting(value);
return OAKCOMMON_OK;
} catch (...) {
return OAKCOMMON_E_FAILED;
}
}
void oakcommon_commandlineoption_free(OakCommandLineOption *option)
{
oakcommon::free_handle(option);
}
void oakcommon_commandlinepositionalargument_free(
OakCommandLinePositionalArgument *argument)
{
oakcommon::free_handle(argument);
}