%% options copyright owner = Dirk Krause copyright year = 2015-xxxx license = bsd %% header /** @file Process command line option in simple programs. You can specify a set of allowed options including the option arguments types. After processing command line arguments you can simply check which options were used and obtain option argument values. */ #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4ERROR_H_INCLUDED #include "dk4error.h" #endif /** Option specification for application module. */ typedef struct { dkChar c; /**< Short option character. */ const dkChar *longopt; /**< Long option string. */ int argtype; /**< Argument type. */ } dk4_option_specification_t; /** Option values can be different types. */ typedef union { dkChar *t; /**< String value. */ double d; /**< Double value. */ dk4_im_t i; /**< Integer value. */ dk4_um_t u; /**< Unsigned integer value. */ size_t s; /**< Size value. */ int b; /**< Boolean value. */ } dk4_option_value_t; /** Option with value. */ typedef struct { dk4_option_specification_t spec; /**< Option specification. */ dk4_option_value_t val; /**< Option value. */ int found; /**< Flag: Option found in cmd line. */ } dk4_option_t; /** Argument types. */ enum { /** No argument expected. */ DK4_OPT_ARG_NONE = 1, /** Boolean value expected. */ DK4_OPT_ARG_BOOL , /** Size specification expected. */ DK4_OPT_ARG_SIZE , /** Signed integer value expected. */ DK4_OPT_ARG_INT , /** Unsigned integer value expected. */ DK4_OPT_ARG_UNSIGNED , /** Double value expected. */ DK4_OPT_ARG_DOUBLE , /** String value expected. */ DK4_OPT_ARG_STRING }; #ifdef __cplusplus extern "C" { #endif /** Create option object. CRT on Windows: Optional. @param spec Specifications for the option. @param erp Error report, may be NULL. @return Pointer to new option object on success, NULL on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if spec is NULL, - DK4_E_SYNTAX
if neither short nor long option is defined in spec, - DK4_E_MEMORY
if the memory allocation failed. */ dk4_option_t * dk4opt_open(const dk4_option_specification_t *spec, dk4_er_t *erp); /** Retrieve boolean value (as int) from option. CRT on Windows: Optional. @param dptr Pointer to destination variable. @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line, - DK4_E_SYNTAX
if a string is stored not representing a boolean or the option was configured with an incompatible type. */ int dk4opt_get_bool(int *dptr, dk4_option_t *optptr, dk4_er_t *erp); /** Retrieve size_t value from option. CRT on Windows: Optional. @param dptr Pointer to destination variable. @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line, - DK4_E_SYNTAX
if the option was configured for an incompatible type or the option argument string can not be converted to a size_t, - DK4_E_MATH_OVERFLOW
if the option was configured for another numeric type and conversion results in an overflow. */ int dk4opt_get_size(size_t *dptr, dk4_option_t *optptr, dk4_er_t *erp); /** Retrieve integer value (dk4_im_t) from option. CRT on Windows: Optional. @param dptr Pointer to destination variable. @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line, - DK4_E_SYNTAX
if the option was configured for an incompatible type or the option argument string does not represent an integer number, - DK4_E_MATH_OVERFLOW
if the option was configured for another numeric type and conversion results in an overflow. */ int dk4opt_get_int(dk4_im_t *dptr, dk4_option_t *optptr, dk4_er_t *erp); /** Retrieve unsigned integer value (dk4_um_t) from option. CRT on Windows: Optional. @param dptr Pointer to destination variable. @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line, - DK4_E_SYNTAX
if the option was configured for an incompatible data type or the option string does not represent an integer number, - DK4_E_MATH_OVERFLOW
if the option was configured for an incompatible numeric type and a negative value is stored. */ int dk4opt_get_unsigned(dk4_um_t *dptr, dk4_option_t *optptr, dk4_er_t *erp); /** Retrieve double value from option. CRT on Windows: Optional. @param dptr Pointer to destination variable. @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line, - DK4_E_SYNTAX
if the option was configured for an incompatible data type or the string stored as option argument does not represent a double number. */ int dk4opt_get_double(double *dptr, dk4_option_t *optptr, dk4_er_t *erp); /** Retrieve string value from option. CRT on Windows: Optional, disabling CRT reduces functionality as we can not convert double values to strings. @param dptr Pointer to destination buffer. @param szdptr Size of destination buffer (number of dkChar). @param optptr Option to retrieve value from. @param erp Error report, may be NULL. @return 1 on success, 0 on error. Error codes: - DK4_E_INVALID_ARGUMENTS
if dptr or optptr is NULL or optptr is configured for another data type, - DK4_E_NOT_FOUND
if the option was not specified on the command line. - DK4_E_SYNTAX
if the option was configured for an incompatible data type, - DK4_E_BUFFER_TOO_SMALL
if the destination buffer is too small to hold a conversion result, - DK4_E_NOT_SUPPORTED
if the option was configured for the double data type and the use of sprintf() was denied during setup. */ int dk4opt_get_string( dkChar *dptr, size_t szdptr, dk4_option_t *optptr, dk4_er_t *erp ); /** Destroy option object, release memory. CRT on Windows: Optional. @param optptr Option object to destroy. */ void dk4opt_close(dk4_option_t *optptr); /** Compare option against other option or char or string. CRT on Windows: Optional. @param l Left pointer, always an option. @param r Right pointer, type depends on cr. @param cr Comparison criteria: 0=option/option by short character, 1=option/character, 2=option/option by long option text, 3=option/text. @return Comparison result. */ int dk4option_compare(const void *l, const void *r, int cr); /** Process command line options provided to the main function. @param optptr Options array. @param szoptptr Number of elements in array. @param argc Number of command line arguments. @param argv Command line arguments array. @param filenames Pointer array to store file names. @param nfilenames Elements in array (in: size, out: used). @param progname Flag: Argument 0 is program name. @param logstderr Flag: Logging to stderr allowed. @param erp Error report, may be NULL. @return 1 on success, 0 on error. */ int dk4opt_process_argv( dk4_option_t *optptr, size_t szoptptr, int argc, dkChar *argv[], dkChar **filenames, size_t *nfilenames, int progname, int logstderr, dk4_er_t *erp ); #ifdef __cplusplus } #endif %% module #include "dk4opt.h" #include "dk4strd.h" #include "dk4mem.h" $!trace-include /** Compare two options by short option. @param olp Left option structure. @param orp Right option structure. @return Comparison result. */ static int dk4option_short_compare(const dk4_option_t *olp, const dk4_option_t *orp) { int back = 0; if ((olp->spec).c > (orp->spec).c) { back = 1; } else { if ((olp->spec).c < (orp->spec).c) { back = -1; } } return back; } /** Compare two options by long option. @param olp Left option structure. @param orp Right option structure. @return Comparison result. */ static int dk4option_long_compare(const dk4_option_t *olp, const dk4_option_t *orp) { int back = 0; if (NULL != (olp->spec).longopt) { if (NULL != (orp->spec).longopt) { back = dk4str_cmp((olp->spec).longopt, (orp->spec).longopt); if (-1 > back) back = -1; if ( 1 < back) back = 1; } else { back = 1; } } else { if (NULL != (orp->spec).longopt) { back = -1; } } return back; } int dk4option_compare(const void *l, const void *r, int cr) { const dk4_option_t *olp; const dk4_option_t *orp; const dkChar *cp; int back = 0; if (NULL != l) { olp = (const dk4_option_t *)l; if (NULL != r) { switch (cr) { case 3: { cp = (const dkChar *)r; if (NULL != (olp->spec).longopt) { back = dk4str_cmp((olp->spec).longopt, cp); if (-1 > back) back = -1; if ( 1 < back) back = 1; } else { back = -1; } } break; case 2: { orp = (const dk4_option_t *)r; back = dk4option_long_compare(olp, orp); if (0 == back) { back = dk4option_short_compare(olp, orp); } } break; case 1: { cp = (const dkChar *)r; if ((olp->spec).c > *cp) { back = 1; } else { if ((olp->spec).c < *cp) { back = -1; } } } break; default: { orp = (const dk4_option_t *)r; back = dk4option_short_compare(olp, orp); if (0 == back) { back = dk4option_long_compare(olp, orp); } } break; } } else { back = 1; } } else { if (NULL != r) { back = -1; } } return back; } void dk4opt_close(dk4_option_t *optptr) { $? "+ dk4opt_close" if (NULL != optptr) { $? ". valid option ptr" /* We can not sanitize the memory for option name and option value as the option name is constant and the option value was provided by the shell and is owned by the shell. */ (optptr->spec).longopt = NULL; (optptr->val).t = NULL; dk4mem_free(optptr); } $? "- dk4opt_close" } dk4_option_t * dk4opt_open(const dk4_option_specification_t *spec, dk4_er_t *erp) { dk4_option_t *back = NULL; if (NULL != spec) { if ((NULL != spec->longopt) || (dkT('\0') != spec->c)) { back = dk4mem_new(dk4_option_t,1,NULL); if (NULL != back) { DK4_MEMCPY(&(back->spec),spec,sizeof(dk4_option_specification_t)); back->found = 0; switch ((back->spec).argtype) { case DK4_OPT_ARG_BOOL: { (back->val).b = 0; } break; case DK4_OPT_ARG_SIZE: { (back->val).s = 0; } break; case DK4_OPT_ARG_INT: { (back->val).i = (dk4_im_t)0L; } break; case DK4_OPT_ARG_UNSIGNED: { (back->val).u = (dk4_um_t)0UL; } break; case DK4_OPT_ARG_DOUBLE: { (back->val).d = 0.0; } break; case DK4_OPT_ARG_STRING: { (back->val).t = NULL; } break; } } else { dk4error_set_elsize_nelem( erp, DK4_E_MEMORY_ALLOCATION_FAILED, sizeof(dk4_option_t), 1 ); } } else { dk4error_set_simple_error_code(erp, DK4_E_SYNTAX); } } else { dk4error_set_simple_error_code(erp, DK4_E_INVALID_ARGUMENTS); } return back; }