You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
397 lines
8.5 KiB
397 lines
8.5 KiB
/* |
|
* This file is a part of PikoTools |
|
* and is distributed under the (new) BSD licence. |
|
* Author: Tomasz Sowa <t.sowa@ttmath.org> |
|
*/ |
|
|
|
/* |
|
* Copyright (c) 2016-2021, Tomasz Sowa |
|
* All rights reserved. |
|
* |
|
* Redistribution and use in source and binary forms, with or without |
|
* modification, are permitted provided that the following conditions are met: |
|
* |
|
* * Redistributions of source code must retain the above copyright notice, |
|
* this list of conditions and the following disclaimer. |
|
* |
|
* * Redistributions in binary form must reproduce the above copyright |
|
* notice, this list of conditions and the following disclaimer in the |
|
* documentation and/or other materials provided with the distribution. |
|
* |
|
* * Neither the name Tomasz Sowa nor the names of contributors to this |
|
* project may be used to endorse or promote products derived |
|
* from this software without specific prior written permission. |
|
* |
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
|
* THE POSSIBILITY OF SUCH DAMAGE. |
|
*/ |
|
|
|
|
|
#include "mainoptionsparser.h" |
|
#include "utf8/utf8.h" |
|
#include <string.h> |
|
|
|
|
|
|
|
namespace pt |
|
{ |
|
|
|
|
|
|
|
MainOptionsParser::MainOptionsParser() |
|
{ |
|
space = 0; |
|
arguments_required_space = 0; |
|
should_use_utf8 = true; |
|
last_status = status_ok; |
|
non_option_arguments_name = L"args"; |
|
} |
|
|
|
|
|
|
|
MainOptionsParser::~MainOptionsParser() |
|
{ |
|
} |
|
|
|
|
|
void MainOptionsParser::use_utf8(bool utf8) |
|
{ |
|
should_use_utf8 = utf8; |
|
} |
|
|
|
|
|
void MainOptionsParser::set_non_options_arguments_name(const wchar_t * name) |
|
{ |
|
non_option_arguments_name = name; |
|
} |
|
|
|
|
|
void MainOptionsParser::set_non_options_arguments_name(const std::wstring & name) |
|
{ |
|
non_option_arguments_name = name; |
|
} |
|
|
|
|
|
|
|
std::wstring & MainOptionsParser::get_wrong_option() |
|
{ |
|
return last_error_option; |
|
} |
|
|
|
MainOptionsParser::Status MainOptionsParser::parse(int argc, const char ** argv, Space & out_space) |
|
{ |
|
space = &out_space; |
|
arguments_required_space = nullptr; |
|
|
|
return parse(argc, argv); |
|
} |
|
|
|
|
|
MainOptionsParser::Status MainOptionsParser::parse(int argc, const char ** argv, Space & out_space, const Space & arguments) |
|
{ |
|
space = &out_space; |
|
arguments_required_space = &arguments;; |
|
|
|
return parse(argc, argv); |
|
} |
|
|
|
|
|
MainOptionsParser::Status MainOptionsParser::parse(int argc, const char ** argv) |
|
{ |
|
last_status = status_ok; |
|
last_error_option.clear(); |
|
space->set_empty_object(); |
|
|
|
for(size_t i=1 ; i < (size_t)argc && last_status == status_ok ; ) |
|
{ |
|
parse((size_t)argc, argv, i); |
|
} |
|
|
|
options.clear(); |
|
option.clear(); |
|
argument.clear(); |
|
arguments.clear(); |
|
|
|
return last_status; |
|
} |
|
|
|
|
|
void MainOptionsParser::parse(size_t argc, const char ** argv, size_t & argv_index) |
|
{ |
|
const char * pchar = argv[argv_index]; |
|
|
|
if( *pchar == '-' ) |
|
{ |
|
if( *(pchar+1) == '-' && *(pchar+2) == 0 ) |
|
{ |
|
// two hyphens only "--" |
|
argv_index += 1; |
|
parse_non_option_arguments(argc, argv, argv_index); |
|
} |
|
else |
|
if( *(pchar+1) == '-' ) |
|
{ |
|
// two hyphens and a string, such as "--abc" |
|
parse_long_option(argc, argv, argv_index); |
|
} |
|
else |
|
if( *(pchar+1) != 0 ) |
|
{ |
|
// one hyphen and a string, such as "-abc" |
|
parse_short_option(argc, argv, argv_index); |
|
} |
|
else |
|
{ |
|
parse_non_option_arguments(argc, argv, argv_index); |
|
} |
|
} |
|
else |
|
{ |
|
parse_non_option_arguments(argc, argv, argv_index); |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::convert_str(const char * src, std::wstring & dst) |
|
{ |
|
if( should_use_utf8 ) |
|
{ |
|
UTF8ToWide(src, dst); |
|
} |
|
else |
|
{ |
|
dst.clear(); |
|
|
|
for( ; *src ; ++src ) |
|
dst += (wchar_t)(unsigned char)*src; |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::convert_str(const char * src, size_t len, std::wstring & dst) |
|
{ |
|
if( should_use_utf8 ) |
|
{ |
|
UTF8ToWide(src, len, dst); |
|
} |
|
else |
|
{ |
|
dst.clear(); |
|
|
|
for(size_t i=0 ; i < len ; ++i) |
|
dst += (wchar_t)(unsigned char)src[i]; |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::convert_str(const std::wstring & src, Space & space) |
|
{ |
|
if( should_use_utf8 ) |
|
{ |
|
space.set_empty_wstring(); |
|
space.value.value_wstring = src; |
|
} |
|
else |
|
{ |
|
space.set_empty_string(); |
|
std::string & dst = space.value.value_string; |
|
|
|
dst.clear(); |
|
|
|
for(size_t i=0 ; i < src.size() ; ++i) |
|
dst += (char)src[i]; |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::parse_short_option(size_t argc, const char ** argv, size_t & argv_index) |
|
{ |
|
convert_str(argv[argv_index] + 1, options); |
|
const wchar_t * options_pchar = options.c_str(); |
|
|
|
arguments.clear(); |
|
bool was_argument = false; |
|
argv_index += 1; |
|
|
|
for( ; *options_pchar && !was_argument && last_status == status_ok ; ++options_pchar ) |
|
{ |
|
option = *options_pchar; |
|
size_t args_len = how_many_arguments_required(option); |
|
|
|
if( args_len > 0 ) |
|
{ |
|
was_argument = true; |
|
|
|
if( *(options_pchar+1) ) |
|
{ |
|
// first argument is directly behind the option |
|
argument = options_pchar + 1; |
|
arguments.push_back(argument); |
|
args_len -= 1; |
|
} |
|
|
|
parse_arguments(argc, argv, argv_index, args_len); |
|
} |
|
|
|
add_option_to_space(option, arguments); |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::parse_long_option(size_t argc, const char ** argv, size_t & argv_index) |
|
{ |
|
const char * option_begin = argv[argv_index] + 2; // skip first two hyphens -- |
|
const char * option_end = option_begin; |
|
bool is_equal_form = false; // is the option in the form with equal sign, such as: option=argument |
|
|
|
while( *option_end != 0 && *option_end != '=' ) |
|
{ |
|
option_end += 1; |
|
} |
|
|
|
if( *option_end == '=' ) |
|
{ |
|
is_equal_form = true; |
|
convert_str(option_begin, option_end - option_begin, option); |
|
convert_str(option_end + 1, argument); |
|
} |
|
else |
|
{ |
|
convert_str(option_begin, option); |
|
} |
|
|
|
argv_index += 1; |
|
size_t args_len = how_many_arguments_required(option); |
|
arguments.clear(); |
|
|
|
if( is_equal_form ) |
|
{ |
|
if( args_len == 0 ) |
|
{ |
|
if( !argument.empty() ) |
|
{ |
|
// report an error |
|
last_status = status_argument_provided; |
|
last_error_option = option; |
|
} |
|
} |
|
else |
|
if( args_len == 1 ) |
|
{ |
|
// argument can be empty in such a case: option= |
|
// we treat it as if the argument would not be provided |
|
if( !argument.empty() ) |
|
{ |
|
arguments.push_back(argument); |
|
args_len -= 1; |
|
} |
|
} |
|
else |
|
{ |
|
// args_len is > 1 but when using option=argument form |
|
// we can provide only one argument |
|
last_status = status_argument_not_provided; |
|
last_error_option = option; |
|
} |
|
} |
|
|
|
if( last_status == status_ok ) |
|
{ |
|
parse_arguments(argc, argv, argv_index, args_len); |
|
add_option_to_space(option, arguments); |
|
} |
|
} |
|
|
|
|
|
void MainOptionsParser::parse_arguments(size_t argc, const char ** argv, size_t & argv_index, size_t args_len) |
|
{ |
|
for( ; args_len > 0 && argv_index < argc ; --args_len, ++argv_index) |
|
{ |
|
convert_str(argv[argv_index], argument); |
|
arguments.push_back(argument); |
|
} |
|
|
|
if( args_len > 0 ) |
|
{ |
|
last_status = status_argument_not_provided; |
|
last_error_option = option; |
|
} |
|
} |
|
|
|
|
|
|
|
void MainOptionsParser::parse_non_option_arguments(size_t argc, const char ** argv, size_t & argv_index) |
|
{ |
|
Space * table_with_args = new Space(); |
|
table_with_args->set_empty_table(); |
|
|
|
for( ; argv_index < argc ; ++argv_index) |
|
{ |
|
convert_str(argv[argv_index], argument); |
|
table_with_args->add(argument); |
|
} |
|
|
|
space->add(non_option_arguments_name, table_with_args); |
|
} |
|
|
|
|
|
void MainOptionsParser::add_option_to_space(const std::wstring & option, const std::vector<std::wstring> & arguments) |
|
{ |
|
Space * option_table = space->get_object_field(option); |
|
|
|
if( !option_table ) |
|
{ |
|
option_table = &space->add_empty_space(option); |
|
} |
|
|
|
if( !option_table->is_table()) |
|
{ |
|
option_table->set_empty_table(); |
|
} |
|
|
|
Space * arguments_table = new Space(); |
|
arguments_table->set_empty_table(); |
|
|
|
for(const std::wstring & arg : arguments) |
|
{ |
|
Space & space_arg = arguments_table->add_empty_space(); |
|
convert_str(arg, space_arg); |
|
} |
|
|
|
option_table->add(arguments_table); |
|
} |
|
|
|
|
|
|
|
size_t MainOptionsParser::how_many_arguments_required(const std::wstring & arg) |
|
{ |
|
size_t res = 0; |
|
|
|
if( arguments_required_space && arguments_required_space->is_object() ) |
|
{ |
|
long res_long = arguments_required_space->to_llong(arg, 0); |
|
|
|
if( res_long < 0 ) |
|
res_long = 0; |
|
|
|
res = (size_t)res_long; |
|
// argument 'arg' needs 'res' options |
|
} |
|
|
|
return res; |
|
} |
|
|
|
|
|
|
|
} // namespace |
|
|
|
|
|
|