tools

various tools
git clone git://deadbeef.fr/tools.git
Log | Files | Refs | README | LICENSE

optparser.hpp (8292B)


      1 // Copyright (c) 2020 Morel BĂ©renger
      2 // 
      3 // This software is provided 'as-is', without any express or implied
      4 // warranty. In no event will the authors be held liable for any damages
      5 // arising from the use of this software.
      6 // 
      7 // Permission is granted to anyone to use this software for any purpose,
      8 // including commercial applications, and to alter it and redistribute it
      9 // freely, subject to the following restrictions:
     10 // 
     11 // 1. The origin of this software must not be misrepresented; you must not
     12 //    claim that you wrote the original software. If you use this software
     13 //    in a product, an acknowledgment in the product documentation would be
     14 //    appreciated but is not required.
     15 // 2. Altered source versions must be plainly marked as such, and must not be
     16 //    misrepresented as being the original software.
     17 // 3. This notice may not be removed or altered from any source distribution.
     18 
     19 // this file contains the definitions of structures and
     20 // functions to parse command-line options, with some C++
     21 // helpers (because templates rocks and C does not have
     22 // them).
     23 // C++ *should not* be required to use the core
     24 // functionnality, but actual C uses have not been tested:
     25 // you've been warned.
     26 // Other functions are planned to parse data from sources
     27 // different than argv, like getenv() or a configuration
     28 // file.
     29 // That's still on the TODO-list, though.
     30 
     31 #ifndef OPTPARSER_HPP
     32 #define OPTPARSER_HPP
     33 
     34 #include <stdbool.h>
     35 #include <stdint.h>
     36 
     37 #ifdef __cplusplus
     38 extern "C" {
     39 #else
     40 #define nullptr 0
     41 #endif
     42 
     43 #define STD_HELP { "help", "shows this message", 'h', 0, nullptr, nullptr, nullptr }
     44 
     45 //TODO I wish this could be avoided, but FILE* is used there.
     46 #include <stdio.h>
     47 
     48 // this could probably be optimized, but considering it should not be used that
     49 // often, I think it's better to just keep it as easy to use as possible.
     50 // Some doc:
     51 // * option: long name of the option, what people should use for readable shell
     52 //           code, but won't in direct calls, because it's faster to use a
     53 //           possible shorter version.
     54 //           On cmd line, prefixed with "--" and folowed by a "=" if option is
     55 //           not a flag. For example, the "foo" option's value would be set on
     56 //           cmd line with "--foo=value".
     57 //           Must be a null-byte terminated UTF-8 string.
     58 //           Character '=' is reserved and must not be used.
     59 // * short_opt: character used as the short name. Short names are prefixed with
     60 //              "-" and directly followed by the value to affect to the option,
     61 //              for example: "-fvalue".
     62 // * description: how the program should show describe the option to people that
     63 //                don't read the docs (hint: most of us). Default values should
     64 //                not be repeated in that text.
     65 //                Must be a null-byte terminated UTF-8 string.
     66 // * value: a pointer to the variable to set, if applicable. If nullptr,
     67 //          occurrences will be counted.
     68 // * set: how to set value given an UTF-8 null-terminated string.
     69 // * show: how to render the current value. Should be used by an "help" option.
     70 // * count: how many times the option was found. Can be used for various things,
     71 //          like exiting if --version, -v, --help or -h were found, or if a
     72 //          mandatory option is missing.
     73 //          If the option appears too often, the count will stop incrementing.
     74 // TODO: implement a way to know which arguments were ignored (no match found)
     75 // TODO: implement the "--" option to mean end of options (and return it's index?)
     76 struct opt_desc_t
     77 {
     78 	char const* option;
     79 	char const* description;
     80 	uint32_t short_option;
     81 	uint32_t count;
     82 	void* value;
     83 	bool (*set) ( void* val, char * arg );
     84 	bool (*show)( void const* val, FILE_PTR target );
     85 };
     86 
     87 //see parse_error_msgs in implementation for descriptions
     88 enum parse_error_t
     89 {
     90 	NONE,
     91 	IGNORED,
     92 	MAX_COUNT,
     93 	SET_NO_VAL,
     94 	SET_VAL_IGN,
     95 	SET_FAIL,
     96 	BAD_ARGS,
     97 	BAD_SETTER,
     98 };
     99 
    100 extern char const *parse_error_msgs[];
    101 #define arg_warning( arg, err ) fprintf( stderr, "Warning: in arg \"%s\" => %s(error code: %04x)\n", \
    102 		arg, parse_error_msgs[err], err )
    103 #define arg_error( arg, err ) fprintf( stderr, "Error: in arg \"%s\" => %s(error code: %04x)\n", \
    104 		arg, parse_error_msgs[err], err )
    105 
    106 // checks if a string is a command-line argument and processes it if yes
    107 // return true if the argument was ignored, false if it was successfully
    108 // processed.
    109 // Range of checked options is: [start,end)
    110 parse_error_t parse_cmd_opt( char* arg, opt_desc_t* start, opt_desc_t const* end );
    111 
    112 // prints a message describing options and their current value if applicable.
    113 void print_opts( FILE_PTR target, opt_desc_t const* start, opt_desc_t const* end );
    114 
    115 #ifdef __cplusplus
    116 } //end of extern "C"
    117 
    118 // add some pretty generic and useful code for C++ users.
    119 // C users will have to implement their owns wrappers (and maybe contribute it?).
    120 //
    121 // This list is wished to grow with time, but should never depend on things that
    122 // have runtime cost (containers, exceptions, RTTI, etc).
    123 // It is hoped that it's performance will be improved when possible.
    124 
    125 // Depending on what you really use, you may have to define the following functions:
    126 // * strncpy   for static C string set()
    127 // * fputs     for C string show()
    128 // * strtoull  for unsigned integers show()
    129 // * strtoll   for signed integets show()
    130 // * strtold   for floating numbers show()
    131 // * fprintf   for all number related show()
    132 
    133 // stdio.h is *not* included, to fasten build time and to let user use
    134 // unlocked functions, which should be faster but not thread-safe.
    135 
    136 #include <type_traits>
    137 #include <limits>
    138 
    139 #include "utils.hpp"
    140 
    141 // C "semi-static" strings in pointer
    142 template <typename T>
    143 bool set(
    144 		typename std::enable_if <std::is_same<char const**,T>::value, void*>::type val,
    145 		char* arg )
    146 {
    147 	if( !val )
    148 	{
    149 		return true;
    150 	}
    151 
    152 	*static_cast<char const**>( val ) = arg;
    153 	return false;
    154 }
    155 
    156 // C strings in statically allocated buffer
    157 template <typename T, size_t SZ>
    158 bool set(
    159 		typename std::enable_if <std::is_same<char*,T>::value, void*>::type val,
    160 		char* arg )
    161 {
    162 	if( !val )
    163 	{
    164 		return true;
    165 	}
    166 
    167 	strncpy( static_cast<char*>( val ), arg, SZ - 1);
    168 	static_cast<char*>( val )[SZ-1] = 0;
    169 	return false;
    170 }
    171 
    172 template <typename T>
    173 bool show(
    174 		typename std::enable_if <std::is_same<char*,T>::value, void const*>::type val,
    175 		FILE* target )
    176 {
    177 	if( !val )
    178 	{
    179 		return true;
    180 	}
    181 
    182 	fputs( "(current value: \"", target );
    183 	char const* current = *static_cast<char *const*>( val );
    184 	current = current ? current : "";
    185 	esc_fputs( current, target );
    186 	fputs( "\")", target );
    187 	return false;
    188 }
    189 
    190 // signed integrals
    191 template <typename T>
    192 bool set(
    193 		typename std::enable_if
    194 		<
    195 			std::is_integral<T>::value && std::is_signed<T>::value,
    196 			void*
    197 		>::type val, char* arg )
    198 {
    199 	if( !val )
    200 	{
    201 		return true;
    202 	}
    203 
    204 	char* end;
    205 	auto v = strtoll( arg, &end, 0 );
    206 	if( false
    207 			|| errno
    208 			|| 0 != *end
    209 			|| std::numeric_limits<T>::min() > v
    210 			|| std::numeric_limits<T>::max() < v )
    211 	{
    212 		return true;
    213 	}
    214 	*static_cast<T*>( val ) = static_cast<T>( v );
    215 	return false;
    216 }
    217 
    218 template <typename T>
    219 bool show(
    220 		typename std::enable_if
    221 		<
    222 			std::is_integral<T>::value && std::is_signed<T>::value,
    223 			void const*
    224 		>::type val, FILE* target )
    225 {
    226 	if( !val )
    227 	{
    228 		return true;
    229 	}
    230 
    231 	fprintf( target, "(current value: %d)", *static_cast<T const*>( val ) );
    232 	return false;
    233 }
    234 
    235 // unsigned integrals
    236 template <typename T>
    237 bool set(
    238 		typename std::enable_if
    239 		<
    240 			std::is_integral<T>::value && std::is_unsigned<T>::value,
    241 			void*
    242 		>::type val, char* arg )
    243 {
    244 	if( !val )
    245 	{
    246 		return true;
    247 	}
    248 
    249 	char* end;
    250 	auto v = strtoull( arg, &end, 0 );
    251 	if( false
    252 			|| errno
    253 			|| 0 != *end
    254 			|| std::numeric_limits<T>::min() > v
    255 			|| std::numeric_limits<T>::max() < v )
    256 	{
    257 		return true;
    258 	}
    259 	*static_cast<T*>( val ) = static_cast<T>( v );
    260 	return false;
    261 }
    262 
    263 // floating numbers
    264 template <typename T>
    265 bool set(
    266 		typename std::enable_if
    267 		<
    268 			std::is_floating_point<T>::value,
    269 			void*
    270 		>::type val, char* arg )
    271 {
    272 	if( !val )
    273 	{
    274 		return true;
    275 	}
    276 
    277 	char* end;
    278 	auto v = strtold( arg, &end );
    279 	if( false
    280 			|| errno
    281 			|| 0 != *end
    282 			|| std::numeric_limits<T>::min() > v
    283 			|| std::numeric_limits<T>::max() < v )
    284 	{
    285 		return true;
    286 	}
    287 	*static_cast<T*>( val ) = static_cast<T>( v );
    288 	return false;
    289 }
    290 
    291 #endif
    292 
    293 #endif