/** * argparser – command line argument parser library * * Copyright © 2013, 2014 Mattias Andrée (maandree@member.fsf.org) * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this library. If not, see . */ #ifndef ARGPARSER_H #define ARGPARSER_H #include #include #ifndef ARGS_CONST # define ARGS_CONST __attribute__((const)) #endif #ifndef ARGS_PURE # define ARGS_PURE __attribute__((pure)) #endif /** * Option types */ typedef enum args_option_type { /** * The option never takes any arguments */ ARGUMENTLESS, /** * The option takes the next argument */ ARGUMENTED, /** * The option may have an argument, either sticky * or otherwise accepted by `stickless` */ OPTARGUMENTED, /** * The option takes all following options */ VARIADIC } args_option_type_t; /** * Tristate type */ typedef enum args_tristate { /** * False */ FALSE, /** * True */ TRUE, /** * Automatic */ AUTO } args_tristate_t; typedef struct args_option { /** * The type of the option */ args_option_type_t type; /** * Alterative option names */ const char** alternatives; /** * Number of elements in `alternatives` */ size_t alternatives_count; /** * Standard option name */ const char* standard; /** * Argument name, not for argumentless options */ const char* argument; /** * Help text, multi-line */ const char* help; /** * User-data used by methods associated with the option */ void* user_data; /** * Arguments passed to the option, `NULL` when argumentless */ char** arguments; /** * The number of elements in `arguments` */ size_t arguments_count; /** * Invoked when the option is used * * @param user_data User-data * @param standard The used option alternative * @param used The standard option alternative */ void (*trigger)(void* user_data, const char* standard, const char* used); /** * Invoked when the option is used * * @param user_data User-data * @param standard The used option alternative * @param used The standard option alternative * @param value The used value */ void (*trigger_v)(void* user_data, const char* standard, const char* used, char* value); /** * Should return true if the next argument can used for the argument without being sticky * * @param user_data User-data * @param argument The next argument * @return Whether the argument can be used without being sticky */ int (*stickless)(void* user_data, const char* argument); /** * Should return true if the next argument can used for the argument * * @param user_data User-data * @param value The next argument * @return Whether the argument can be used */ int (*variadic_end)(void* user_data, char* value); } args_option_t; /** * Settings for argument parser */ typedef struct args_settings { /** * Whether the Linux VT is being used */ int linuxvt; /** * Whether to use single dash/plus long options */ int alternative; /** * Whether to all arguments after the first file * should also be parsed as files */ int stop_at_first_file; /** * Whether to use colours */ args_tristate_t use_colours; /** * The name of the executed command, will be freed by the parser */ char* program; /** * Short, single-line, description of the program */ const char* description; /** * Formated, multi-line, usage text, `NULL` if none */ const char* usage; /** * Long, multi-line, description of the program, `NULL` if none */ const char* longdescription; /** * The error output stream */ FILE* error_out; /** * The warning output stream */ FILE* warning_out; /** * The help output stream */ FILE* help_out; /** * Abbreviated option expander, `NULL` for disabled * * @param argument The option that not recognised * @param options All recognised options, order by order of appearance in the help, i.e. by inclusion * @param standards The corresponding standard option for options in `options`, as a consequence of * the order in `options` all identical values (will have identical address) in * `standards` will directly follow eachother * @param count The number of elements in `options` and `standards` * @return The only possible expansion, otherwise `NULL` */ const char* (*abbreviations)(const char* stub, const char** options, const char** standards, size_t count); } args_settings_t; /** * The state of the parser */ typedef struct args_state { /** * The passed arguments */ char** arguments; /** * The number of passed arguments */ size_t arguments_count; /** * The number of unrecognised arguments */ size_t unrecognised_count; /** * The concatenation of `files` with blankspaces as delimiters, `NULL` if no files */ char* message; /** * The arguments passed that is not tied to an option */ char** files; /** * The number of elements in `files` */ size_t files_count; /** * Options, in order */ args_option* options; /** * Number of elements in `options` */ size_t options_count; /** * All recognised options */ const char** all_options; /** * The standard argument for all recognised options, * if `all_options_standard[i] == all_options_standard[j]`, * then `all_options[i]` and `all_options[j]` are synonyms */ const char** all_options_standard; /** * Number of elements in `all_options` and `all_options_standard` */ size_t all_options_count; /** * Queue of objects that needs to be freed on dispose */ void** freequeue; /** * The number of elements in `freequeue` */ ssize_t freeptr; } args_state_t; /** * Argument parser class */ typedef struct args_parser { /** * Settings for argument parser */ args_settings_t settings; /** * The state of the parser */ args_state_t state; } args_parser_t; /** * Initialise an argument parser * * @param parser The memory address of the parser to initialise * @return Zero on success, -1 on error, `errno` will be set accordingly */ int args_initialise(args_parser_t* parser); /** * Disposes of all resources, run this when you are done * * @param parser The parser */ void args_dispose(args_parser_t* parser); /** * Maps up options that are alternatives to the standard alternative for each option * * @param parser The parser * @return Zero on success, -1 on error */ int args_support_alternatives(args_parser_t* parser); /** * Checks the correctness of the number of used non-option arguments * * @param parser The parser * @param min The minimum number of files * @return Whether the usage was correct */ int args_test_files_min(args_parser_t* parser, size_t min); /** * Checks the correctness of the number of used non-option arguments * * @param parser The parser * @param max The maximum number of files * @return Whether the usage was correct */ int args_test_files_max(args_parser_t* parser, size_t max); /** * Checks the correctness of the number of used non-option arguments * * @param parser The parser * @param min The minimum number of files * @param max The maximum number of files * @return Whether the usage was correct */ int args_test_files(args_parser_t* parser, size_t min, size_t max); /** * Checks for out of context option usage * * @param parser The parser * @param ...:const char* Allowed options * @return Whether only allowed options was used, -1 on error */ int args_test_allowed(args_parser_t* parser, ...); /** * Checks for out of context option usage * * @param parser The parser * @param allowed Allowed options * @param count The number of elements in `allowed` * @return Whether only allowed options was used */ int args_test_allowed_l(args_parser_t* parser, const char** allowed, size_t count) ARGS_PURE; /** * Checks for option conflicts * * @param parser The parser * @param ...:const char* Mutually exclusive options * @return Whether at most one exclusive option was used, -1 on error */ int args_test_exclusiveness(args_parser_t* parser, ...); /** * Checks for option conflicts * * @param parser The parser * @param exclusives Mutually exclusive options * @param count The number of elements in `exclusives` * @return Whether at most one exclusive option was used */ int args_test_exclusiveness_l(args_parser_t* parser, const char** exclusives, size_t count) ARGS_PURE; /** * Dummy trigger * * @param user_data User-data * @param used The used option alternative * @param standard The standard option alternative */ void args_noop_trigger(void* user_data, const char* used, const char* standard) ARGS_CONST; /** * Dummy trigger * * @param user_data User-data * @param used The used option alternative * @param standard The standard option alternative * @param value The used value */ void args_noop_trigger_v(void* user_data, const char* used, const char* standard, char* value) ARGS_CONST; /** * Stickless evaluator to always evaluates to false * * @param user_data User-data * @param argument The next argument * @return Whether the argument can be used without being sticky */ int args_no_stickless(void* user_data, const char* value) ARGS_CONST; /** * Default stickless evaluator * * @param user_data User-data * @param argument The next argument * @return Whether the argument can be used without being sticky */ int args_default_stickless(void* user_data, const char* argument) ARGS_CONST; /** * Evalutator for end argument of variadic options that always evalutes to false * * @param user_data User-data * @param value The next argument * @return Whether the argument can be used without being sticky */ int args_no_variadic_end(void* user_data, char* value) ARGS_CONST; /** * The standard abbrevation expander * * @param argument The option that not recognised * @param standards The corresponding standard option for options in `options` * @param count The number of elements in `options` and `standards` * @return The only possible expansion, otherwise `NULL` */ const char* args_standard_abbreviations(const char* argument, const char** options, const char** standards, size_t count); #endif