From 1c49cd516ee8ec1dc9ca8027ba1d8c8cf00466ed Mon Sep 17 00:00:00 2001 From: barbieri Date: Mon, 24 Nov 2008 00:49:04 +0000 Subject: [PATCH] Easy to use and powerful getopt implementation. Create a parser description and give it the parameters, that's it. You can store values (automatically converting types!), count occurrences, make it true or false, create a list, choose from a list of items or even specify your own callback to process arguments! It was inspired by Python's optparse: http://www.python.org/doc/2.5.2/lib/module-optparse.html git-svn-id: http://svn.enlightenment.org/svn/e/trunk/ecore@37781 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- src/lib/ecore/Ecore_Getopt.h | 385 ++++++++++ src/lib/ecore/Makefile.am | 6 +- src/lib/ecore/ecore_getopt.c | 1635 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 2024 insertions(+), 2 deletions(-) create mode 100644 src/lib/ecore/Ecore_Getopt.h create mode 100644 src/lib/ecore/ecore_getopt.c diff --git a/src/lib/ecore/Ecore_Getopt.h b/src/lib/ecore/Ecore_Getopt.h new file mode 100644 index 0000000..be74695 --- /dev/null +++ b/src/lib/ecore/Ecore_Getopt.h @@ -0,0 +1,385 @@ +#ifndef _ECORE_GETOPT_H +#define _ECORE_GETOPT_H + +#include + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORE_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORE_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif /* ! _WIN32 */ + +/** + * @file Ecore_Getopt.h + * @brief Contains powerful getopt replacement. + * + * This replacement handles both short (-X) or long options (--ABC) + * options, with various actions supported, like storing one value and + * already converting to required type, counting number of + * occurrences, setting true or false values, show help, license, + * copyright and even support user-defined callbacks. + * + * It is provided a set of C Pre Processor macros so definition is + * straightforward. + * + * Values will be stored elsewhere indicated by an array of pointers + * to values, it is given in separate to parser description so you can + * use multiple values with the same parser. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + ECORE_GETOPT_ACTION_STORE, + ECORE_GETOPT_ACTION_STORE_CONST, + ECORE_GETOPT_ACTION_STORE_TRUE, + ECORE_GETOPT_ACTION_STORE_FALSE, + ECORE_GETOPT_ACTION_CHOICE, + ECORE_GETOPT_ACTION_APPEND, + ECORE_GETOPT_ACTION_COUNT, + ECORE_GETOPT_ACTION_CALLBACK, + ECORE_GETOPT_ACTION_HELP, + ECORE_GETOPT_ACTION_VERSION, + ECORE_GETOPT_ACTION_COPYRIGHT, + ECORE_GETOPT_ACTION_LICENSE + } Ecore_Getopt_Action; + + typedef enum { + ECORE_GETOPT_TYPE_STR, + ECORE_GETOPT_TYPE_BOOL, + ECORE_GETOPT_TYPE_SHORT, + ECORE_GETOPT_TYPE_INT, + ECORE_GETOPT_TYPE_LONG, + ECORE_GETOPT_TYPE_USHORT, + ECORE_GETOPT_TYPE_UINT, + ECORE_GETOPT_TYPE_ULONG, + ECORE_GETOPT_TYPE_DOUBLE + } Ecore_Getopt_Type; + + typedef enum { + ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO = 0, + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES = 1, + ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL = 3 + } Ecore_Getopt_Desc_Arg_Requirement; + + typedef union _Ecore_Getopt_Value Ecore_Getopt_Value; + + typedef struct _Ecore_Getopt_Desc_Store Ecore_Getopt_Desc_Store; + typedef struct _Ecore_Getopt_Desc_Callback Ecore_Getopt_Desc_Callback; + typedef struct _Ecore_Getopt_Desc Ecore_Getopt_Desc; + typedef struct _Ecore_Getopt Ecore_Getopt; + + union _Ecore_Getopt_Value + { + char **strp; + unsigned char *boolp; + short *shortp; + int *intp; + long *longp; + unsigned short *ushortp; + unsigned int *uintp; + unsigned long *ulongp; + double *doublep; + Eina_List **listp; + void **ptrp; + }; + + struct _Ecore_Getopt_Desc_Store + { + Ecore_Getopt_Type type; /**< type of data being handled */ + Ecore_Getopt_Desc_Arg_Requirement arg_req; + union + { + const char *strv; + unsigned char boolv; + short shortv; + int intv; + long longv; + unsigned short ushortv; + unsigned int uintv; + unsigned long ulongv; + double doublev; + } def; + }; + + struct _Ecore_Getopt_Desc_Callback + { + unsigned char (*func)(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, const char *str, void *data, Ecore_Getopt_Value *storage); + const void *data; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *def; + }; + + struct _Ecore_Getopt_Desc + { + char shortname; /**< used with a single dash */ + const char *longname; /**< used with double dashes */ + const char *help; /**< used by --help/ecore_getopt_help() */ + const char *metavar; /**< used by ecore_getopt_help() with nargs > 0 */ + + Ecore_Getopt_Action action; /**< define how to handle it */ + union + { + const Ecore_Getopt_Desc_Store store; + const void *store_const; + const char *const *choices; /* NULL terminated. */ + const Ecore_Getopt_Type append_type; + const Ecore_Getopt_Desc_Callback callback; + } action_param; + }; + + struct _Ecore_Getopt + { + const char *prog; /**< to be used when ecore_app_args_get() fails */ + const char *usage; /**< usage example, %prog is replaced */ + const char *version; /**< if exists, --version will work */ + const char *copyright; /**< if exists, --copyright will work */ + const char *license; /**< if exists, --license will work */ + const char *description; /**< long description, possible multiline */ + unsigned char strict : 1; /**< fail on errors */ + const Ecore_Getopt_Desc descs[]; /* NULL terminated. */ + }; + +#define ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, arg_requirement, default_value) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_STORE, \ + {.store = {type, arg_requirement, default_value}}} + +#define ECORE_GETOPT_STORE(shortname, longname, help, type) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {}) + +#define ECORE_GETOPT_STORE_STR(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_STR) +#define ECORE_GETOPT_STORE_BOOL(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_BOOL) +#define ECORE_GETOPT_STORE_SHORT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_SHORT) +#define ECORE_GETOPT_STORE_INT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_INT) +#define ECORE_GETOPT_STORE_LONG(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_LONG) +#define ECORE_GETOPT_STORE_USHORT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_USHORT) +#define ECORE_GETOPT_STORE_UINT(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_UINT) +#define ECORE_GETOPT_STORE_ULONG(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_ULONG) +#define ECORE_GETOPT_STORE_DOUBLE(shortname, longname, help) \ + ECORE_GETOPT_STORE(shortname, longname, help, ECORE_GETOPT_TYPE_DOUBLE) + + +#define ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, type) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, {}) + +#define ECORE_GETOPT_STORE_METAVAR_STR(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_STR) +#define ECORE_GETOPT_STORE_METAVAR_BOOL(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_BOOL) +#define ECORE_GETOPT_STORE_METAVAR_SHORT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_SHORT) +#define ECORE_GETOPT_STORE_METAVAR_INT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_INT) +#define ECORE_GETOPT_STORE_METAVAR_LONG(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_LONG) +#define ECORE_GETOPT_STORE_METAVAR_USHORT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_USHORT) +#define ECORE_GETOPT_STORE_METAVAR_UINT(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_UINT) +#define ECORE_GETOPT_STORE_METAVAR_ULONG(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_ULONG) +#define ECORE_GETOPT_STORE_METAVAR_DOUBLE(shortname, longname, help, metavar) \ + ECORE_GETOPT_STORE_METAVAR(shortname, longname, help, metavar, ECORE_GETOPT_TYPE_DOUBLE) + + +#define ECORE_GETOPT_STORE_DEF(shortname, longname, help, type, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, NULL, type, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL, \ + default_value) + +#define ECORE_GETOPT_STORE_DEF_STR(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_STR, \ + {.strv = default_value}) +#define ECORE_GETOPT_STORE_DEF_BOOL(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_BOOL, \ + {.boolv = default_value}) +#define ECORE_GETOPT_STORE_DEF_SHORT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_SHORT, \ + {.shortv = default_value}) +#define ECORE_GETOPT_STORE_DEF_INT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_INT, \ + {.intv = default_value}) +#define ECORE_GETOPT_STORE_DEF_LONG(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_LONG, \ + {.longv = default_value}) +#define ECORE_GETOPT_STORE_DEF_USHORT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_USHORT, \ + {.ushortv = default_value}) +#define ECORE_GETOPT_STORE_DEF_UINT(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_UINT, \ + {.uintv, default_value}) +#define ECORE_GETOPT_STORE_DEF_ULONG(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_ULONG, \ + {.ulongv = default_value}) +#define ECORE_GETOPT_STORE_DEF_DOUBLE(shortname, longname, help, default_value) \ + ECORE_GETOPT_STORE_DEF(shortname, longname, help, \ + ECORE_GETOPT_TYPE_DOUBLE, \ + {.doublev = default_value}) + +#define ECORE_GETOPT_STORE_FULL_STR(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_STR, \ + arg_requirement, \ + {.strv = default_value}) +#define ECORE_GETOPT_STORE_FULL_BOOL(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_BOOL, \ + arg_requirement, \ + {.boolv = default_value}) +#define ECORE_GETOPT_STORE_FULL_SHORT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_SHORT, \ + arg_requirement, \ + {.shortv = default_value}) +#define ECORE_GETOPT_STORE_FULL_INT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_INT, \ + arg_requirement, \ + {.intv = default_value}) +#define ECORE_GETOPT_STORE_FULL_LONG(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_LONG, \ + arg_requirement, \ + {.longv = default_value}) +#define ECORE_GETOPT_STORE_FULL_USHORT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_USHORT, \ + arg_requirement, \ + {.ushortv = default_value}) +#define ECORE_GETOPT_STORE_FULL_UINT(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_UINT, \ + arg_requirement, \ + {.uintv, default_value}) +#define ECORE_GETOPT_STORE_FULL_ULONG(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_ULONG, \ + arg_requirement, \ + {.ulongv = default_value}) +#define ECORE_GETOPT_STORE_FULL_DOUBLE(shortname, longname, help, metavar, arg_requirement, default_value) \ + ECORE_GETOPT_STORE_FULL(shortname, longname, help, metavar, \ + ECORE_GETOPT_TYPE_DOUBLE, \ + arg_requirement, \ + {.doublev = default_value}) + +#define ECORE_GETOPT_STORE_CONST(shortname, longname, help, value) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_CONST, \ + {.store_const = value}} +#define ECORE_GETOPT_STORE_TRUE(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_TRUE} +#define ECORE_GETOPT_STORE_FALSE(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_STORE_FALSE} + +#define ECORE_GETOPT_CHOICE(shortname, longname, help, choices_array) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_CHOICE, \ + {.choices = choices_array}} +#define ECORE_GETOPT_CHOICE_METAVAR(shortname, longname, help, metavar, choices_array) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CHOICE, \ + {.choices = choices_array}} + + +#define ECORE_GETOPT_APPEND(shortname, longname, help, sub_type) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_APPEND, \ + {.append_type = sub_type}} +#define ECORE_GETOPT_APPEND_METAVAR(shortname, longname, help, metavar, type) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_APPEND, \ + {.append_type = type}} + +#define ECORE_GETOPT_COUNT(shortname, longname, help) \ + {shortname, longname, help, NULL, ECORE_GETOPT_ACTION_COUNT} + +#define ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, callback_func, callback_data, argument_requirement, default_value) \ + {shortname, longname, help, metavar, ECORE_GETOPT_ACTION_CALLBACK, \ + {.callback = {callback_func, callback_data, \ + argument_requirement, default_value}}} +#define ECORE_GETOPT_CALLBACK_NOARGS(shortname, longname, help, callback_func, callback_data) \ + ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, NULL, \ + callback_func, callback_data, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO, \ + NULL) +#define ECORE_GETOPT_CALLBACK_ARGS(shortname, longname, help, metavar, callback_func, callback_data) \ + ECORE_GETOPT_CALLBACK_FULL(shortname, longname, help, metavar, \ + callback_func, callback_data, \ + ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES, \ + NULL) + +#define ECORE_GETOPT_HELP(shortname, longname) \ + {shortname, longname, "show this message.", NULL, \ + ECORE_GETOPT_ACTION_HELP} +#define ECORE_GETOPT_VERSION(shortname, longname) \ + {shortname, longname, "show program version.", NULL, \ + ECORE_GETOPT_ACTION_VERSION} +#define ECORE_GETOPT_COPYRIGHT(shortname, longname) \ + {shortname, longname, "show copyright.", NULL, \ + ECORE_GETOPT_ACTION_COPYRIGHT} +#define ECORE_GETOPT_LICENSE(shortname, longname) \ + {shortname, longname, "show license.", NULL, \ + ECORE_GETOPT_ACTION_LICENSE} +#define ECORE_GETOPT_SENTINEL {0, NULL} + +#define ECORE_GETOPT_VALUE_STR(val) {.strp = &(val)} +#define ECORE_GETOPT_VALUE_BOOL(val) {.boolp = &(val)} +#define ECORE_GETOPT_VALUE_SHORT(val) {.shortp = &(val)} +#define ECORE_GETOPT_VALUE_INT(val) {.intp = &(val)} +#define ECORE_GETOPT_VALUE_LONG(val) {.longp = &(val)} +#define ECORE_GETOPT_VALUE_USHORT(val) {.ushortp = &(val)} +#define ECORE_GETOPT_VALUE_UINT(val) {.uintp = &(val)} +#define ECORE_GETOPT_VALUE_ULONG(val) {.ulongp = &(val)} +#define ECORE_GETOPT_VALUE_DOUBLE(val) {.doublep = &(val)} +#define ECORE_GETOPT_VALUE_PTR(val) {.ptrp = &(val)} +#define ECORE_GETOPT_VALUE_PTR_CAST(val) {.ptrp = (void **)&(val)} +#define ECORE_GETOPT_VALUE_LIST(val) {.listp = &(val)} +#define ECORE_GETOPT_VALUE_NONE {.ptrp = NULL} + + EAPI void ecore_getopt_help(FILE *fp, const Ecore_Getopt *info); + + EAPI unsigned char ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser); + EAPI int ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv); + + EAPI Eina_List *ecore_getopt_list_free(Eina_List *list); + +#ifdef __cplusplus +} +#endif +#endif /* _ECORE_GETOPT_H */ diff --git a/src/lib/ecore/Makefile.am b/src/lib/ecore/Makefile.am index 587415e..245098a 100644 --- a/src/lib/ecore/Makefile.am +++ b/src/lib/ecore/Makefile.am @@ -7,7 +7,8 @@ lib_LTLIBRARIES = libecore.la include_HEADERS = \ Ecore.h \ Ecore_Data.h \ -Ecore_Str.h +Ecore_Str.h \ +Ecore_Getopt.h libecore_la_SOURCES = \ ecore.c \ @@ -32,7 +33,8 @@ ecore_time.c \ ecore_timer.c \ ecore_tree.c \ ecore_value.c \ -ecore_poll.c +ecore_poll.c \ +ecore_getopt.c libecore_la_LIBADD = @dlopen_libs@ @EINA_LIBS@ @EVIL_LIBS@ @WIN32_LIBS@ -lm libecore_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ diff --git a/src/lib/ecore/ecore_getopt.c b/src/lib/ecore/ecore_getopt.c new file mode 100644 index 0000000..4607714 --- /dev/null +++ b/src/lib/ecore/ecore_getopt.c @@ -0,0 +1,1635 @@ +#include "Ecore_Getopt.h" +#include +#include +#include +#include +#include + +static const char *prog = NULL; +static char **argv = NULL; +static int argc = 0; +static int cols = 80; +static int helpcol = 80 / 3; + +static void +_ecore_getopt_help_print_replace_program(FILE *fp, const Ecore_Getopt *parser, const char *text) +{ + do + { + const char *d = strchr(text, '%'); + + if (!d) + { + fputs(text, fp); + break; + } + + fwrite(text, 1, d - text, fp); + d++; + if (strncmp(d, "prog", sizeof("prog") - 1) == 0) + { + fputs(prog ? prog : "???", fp); + d += sizeof("prog") - 1; + } + else + { + if (d[0] == '%') + d++; + fputc('%', fp); + } + + text = d; + } + while (text[0] != '\0'); + + fputc('\n', fp); +} + +static void +_ecore_getopt_version(FILE *fp, const Ecore_Getopt *parser) +{ + fputs("Version: ", fp); + _ecore_getopt_help_print_replace_program(fp, parser, parser->version); +} + +static void +_ecore_getopt_help_usage(FILE *fp, const Ecore_Getopt *parser) +{ + fputs("Usage: ", fp); + + if (!parser->usage) + { + fprintf(fp, "%s [options]\n", prog); + return; + } + + _ecore_getopt_help_print_replace_program(fp, parser, parser->usage); +} + +static int +_ecore_getopt_help_line(FILE *fp, const int base, const int total, int used, const char *text, int len) +{ + int linebreak = 0; + do + { + /* process line considering spaces (new line and tabs are spaces!) */ + while ((used < total) && (len > 0)) + { + int i, todo; + + todo = total - used; + if (todo > len) + todo = len; + + const char *space = NULL; + for (i = 0; i < todo; i++) + if (isspace(text[i])) + { + space = text + i; + break; + } + + if (space) + { + fwrite(text, 1, i, fp); + i++; + text += i; + len -= i; + used += i; + + if (linebreak) + { + linebreak = 0; + continue; + } + + if (space[0] == '\n') + break; + else if (space[0] == '\t') + { + int c; + + used--; + c = ((used / 8) + 1) * 8; + if (c < total) + { + for (; used < c; used++) + fputc(' ', fp); + } + else + { + text--; + len++; + break; + } + } + else if (used < total) + fputc(space[0], fp); + } + else + { + fwrite(text, 1, i, fp); + text += i; + len -= i; + used += i; + } + linebreak = 0; + } + if (len <= 0) + break; + linebreak = 1; + fputc('\n', fp); + for (used = 0; used < base; used++) + fputc(' ', fp); + } + while (1); + + return used; +} + +static void +_ecore_getopt_help_description(FILE *fp, const Ecore_Getopt *parser) +{ + const char *p, *prg, *ver; + int used, prglen, verlen; + + p = parser->description; + if (!p) + return; + + fputc('\n', fp); + + prg = prog ? prog : "???"; + ver = parser->version ? parser->version : "???"; + + prglen = strlen(prg); + verlen = strlen(ver); + + used = 0; + + do + { + const char *d = strchr(p, '%'); + + if (!d) + { + _ecore_getopt_help_line(fp, 0, cols, used, p, strlen(p)); + break; + } + + used = _ecore_getopt_help_line(fp, 0, cols, used, p, d - p); + d++; + if (strncmp(d, "prog", sizeof("prog") - 1) == 0) + { + used = _ecore_getopt_help_line(fp, 0, cols, used, prg, prglen); + d += sizeof("prog") - 1; + } + else if (strncmp(d, "version", sizeof("version") - 1) == 0) + { + used = _ecore_getopt_help_line(fp, 0, cols, used, ver, verlen); + d += sizeof("version") - 1; + } + else + { + if (d[0] == '%') + d++; + used = _ecore_getopt_help_line(fp, 0, cols, used, "%", 1); + } + + p = d; + } + while (p[0] != '\0'); + + fputs("\n\n", fp); +} + +static void +_ecore_getopt_copyright(FILE *fp, const Ecore_Getopt *parser) +{ + fputs("Copyright:\n ", fp); + _ecore_getopt_help_line + (fp, 3, cols, 3, parser->copyright, strlen(parser->copyright)); + fputc('\n', fp); +} + +static void +_ecore_getopt_license(FILE *fp, const Ecore_Getopt *parser) +{ + fputs("License:\n ", fp); + _ecore_getopt_help_line + (fp, 3, cols, 3, parser->license, strlen(parser->license)); + fputc('\n', fp); +} + +static Ecore_Getopt_Desc_Arg_Requirement +_ecore_getopt_desc_arg_requirement(const Ecore_Getopt_Desc *desc) +{ + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + return desc->action_param.store.arg_req; + case ECORE_GETOPT_ACTION_STORE_CONST: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_STORE_TRUE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_STORE_FALSE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_CHOICE: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES; + case ECORE_GETOPT_ACTION_APPEND: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES; + case ECORE_GETOPT_ACTION_COUNT: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_CALLBACK: + return desc->action_param.callback.arg_req; + case ECORE_GETOPT_ACTION_HELP: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + case ECORE_GETOPT_ACTION_VERSION: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + default: + return ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO; + } +} + +static void +_ecore_getopt_help_desc_setup_metavar(const Ecore_Getopt_Desc *desc, char *metavar, int *metavarlen, int maxsize) +{ + if (desc->metavar) + { + *metavarlen = strlen(desc->metavar); + if (*metavarlen > maxsize - 1) + *metavarlen = maxsize - 1; + + memcpy(metavar, desc->metavar, *metavarlen); + metavar[*metavarlen] = '\0'; + } + else if (desc->longname) + { + int i; + + *metavarlen = strlen(desc->longname); + if (*metavarlen > maxsize - 1) + *metavarlen = maxsize - 1; + + for (i = 0; i < *metavarlen; i++) + metavar[i] = toupper(desc->longname[i]); + metavar[i] = '\0'; + } +} + +static int +_ecore_getopt_help_desc_show_arg(FILE *fp, Ecore_Getopt_Desc_Arg_Requirement requirement, const char *metavar, int metavarlen) +{ + int used; + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + return 0; + + used = 0; + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) + { + fputc('[', fp); + used++; + } + + if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + fputc('=', fp); + fputs(metavar, fp); + used += metavarlen + 1; + } + + if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) + { + fputc(']', fp); + used++; + } + + return used; +} + +static int +_ecore_getopt_help_desc_store(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc) +{ + const Ecore_Getopt_Desc_Store *store = &desc->action_param.store; + char buf[64]; + const char *str; + int len; + + fputc('\n', fp); + for (used = 0; used < base; used++) + fputc(' ', fp); + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + str = "STR"; + len = sizeof("STR") - 1; + break; + case ECORE_GETOPT_TYPE_BOOL: + str = "BOOL"; + len = sizeof("BOOL") - 1; + break; + case ECORE_GETOPT_TYPE_SHORT: + str = "SHORT"; + len = sizeof("SHORT") - 1; + break; + case ECORE_GETOPT_TYPE_INT: + str = "INT"; + len = sizeof("INT") - 1; + break; + case ECORE_GETOPT_TYPE_LONG: + str = "LONG"; + len = sizeof("LONG") - 1; + break; + case ECORE_GETOPT_TYPE_USHORT: + str = "USHORT"; + len = sizeof("USHORT") - 1; + break; + case ECORE_GETOPT_TYPE_UINT: + str = "UINT"; + len = sizeof("UINT") - 1; + break; + case ECORE_GETOPT_TYPE_ULONG: + str = "ULONG"; + len = sizeof("ULONG") - 1; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + str = "DOUBLE"; + len = sizeof("DOUBLE") - 1; + break; + default: + str = "???"; + len = sizeof("???") - 1; + } + + used = _ecore_getopt_help_line + (fp, base, total, used, "Type: ", sizeof("Type: ") - 1); + used = _ecore_getopt_help_line(fp, base, total, used, str, len); + + if (store->arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES) + goto end; + + used = _ecore_getopt_help_line + (fp, base, total, used, ". ", sizeof(". ") - 1); + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + str = store->def.strv; + len = str ? strlen(str) : 0; + break; + case ECORE_GETOPT_TYPE_BOOL: + str = store->def.boolv ? "TRUE" : "FALSE"; + len = store->def.boolv ? sizeof("TRUE") - 1 : sizeof("FALSE") - 1; + break; + case ECORE_GETOPT_TYPE_SHORT: + str = buf; + len = snprintf(buf, sizeof(buf), "%hd", store->def.shortv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_INT: + str = buf; + len = snprintf(buf, sizeof(buf), "%d", store->def.intv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_LONG: + str = buf; + len = snprintf(buf, sizeof(buf), "%ld", store->def.longv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_USHORT: + str = buf; + len = snprintf(buf, sizeof(buf), "%hu", store->def.ushortv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_UINT: + str = buf; + len = snprintf(buf, sizeof(buf), "%u", store->def.uintv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_ULONG: + str = buf; + len = snprintf(buf, sizeof(buf), "%lu", store->def.ulongv); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + str = buf; + len = snprintf(buf, sizeof(buf), "%f", store->def.doublev); + if (len > sizeof(buf) - 1) + len = sizeof(buf) - 1; + break; + default: + str = "???"; + len = sizeof("???") - 1; + } + + used = _ecore_getopt_help_line + (fp, base, total, used, "Default: ", sizeof("Default: ") - 1); + used = _ecore_getopt_help_line(fp, base, total, used, str, len); + + end: + return _ecore_getopt_help_line(fp, base, total, used, ".", 1); +} + +static int +_ecore_getopt_help_desc_choices(FILE *fp, const int base, const int total, int used, const Ecore_Getopt_Desc *desc) +{ + const char *const *itr; + const char sep[] = ", "; + const int seplen = sizeof(sep) - 1; + + if (used > 0) + { + fputc('\n', fp); + used = 0; + } + for (; used < base; used++) + fputc(' ', fp); + + used = _ecore_getopt_help_line + (fp, base, total, used, "Choices: ", sizeof("Choices: ") - 1); + + for (itr = desc->action_param.choices; *itr != NULL; itr++) + { + used = _ecore_getopt_help_line + (fp, base, total, used, *itr, strlen(*itr)); + if (itr[1] != NULL) + used = _ecore_getopt_help_line(fp, base, total, used, sep, seplen); + } + + return _ecore_getopt_help_line(fp, base, total, used, ".", 1); +} + +static void +_ecore_getopt_help_desc(FILE *fp, const Ecore_Getopt_Desc *desc) +{ + Ecore_Getopt_Desc_Arg_Requirement arg_req; + char metavar[32] = "ARG"; + int metavarlen = 3; + int used; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + _ecore_getopt_help_desc_setup_metavar + (desc, metavar, &metavarlen, sizeof(metavar)); + + fputs(" ", fp); + used = 2; + + if (desc->shortname) + { + fputc('-', fp); + fputc(desc->shortname, fp); + used += 2; + used += _ecore_getopt_help_desc_show_arg + (fp, arg_req, metavar, metavarlen); + } + + if (desc->shortname && desc->longname) + { + fputs(", ", fp); + used += 2; + } + + if (desc->longname) + { + int namelen = strlen(desc->longname); + + fputs("--", fp); + fputs(desc->longname, fp); + used += 2 + namelen; + used += _ecore_getopt_help_desc_show_arg + (fp, arg_req, metavar, metavarlen); + } + + if (!desc->help) + goto end; + + if (used + 3 >= helpcol) + { + fputc('\n', fp); + used = 0; + } + + for (; used < helpcol; used++) + fputc(' ', fp); + + used = _ecore_getopt_help_line + (fp, helpcol, cols, used, desc->help, strlen(desc->help)); + + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + used = _ecore_getopt_help_desc_store(fp, helpcol, cols, used, desc); + break; + case ECORE_GETOPT_ACTION_CHOICE: + used = _ecore_getopt_help_desc_choices(fp, helpcol, cols, used, desc); + break; + default: + break; + } + + end: + fputc('\n', fp); +} + +static unsigned char +_ecore_getopt_desc_is_sentinel(const Ecore_Getopt_Desc *desc) +{ + return (desc->shortname == '\0') && (desc->longname == NULL); +} + +static void +_ecore_getopt_help_options(FILE *fp, const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc; + + fputs("Options:\n", fp); + + for (desc = parser->descs; !_ecore_getopt_desc_is_sentinel(desc); desc++) + _ecore_getopt_help_desc(fp, desc); + + fputc('\n', fp); +} + +/** + * Show nicely formatted help message for the given parser. + * + * Message will be print to stderr. + */ +void +ecore_getopt_help(FILE *fp, const Ecore_Getopt *parser) +{ + const char *var; + + if (!parser) return; + + if (argc < 1) + { + ecore_app_args_get(&argc, &argv); + if ((argc > 0) && (argv[0] != NULL)) + prog = argv[0]; + else + prog = parser->prog; + } + + var = getenv("COLUMNS"); + if (var) + { + cols = atoi(var); + if (cols < 20) + cols = 20; + + helpcol = cols / 3; + } + + _ecore_getopt_help_usage(fp, parser); + _ecore_getopt_help_description(fp, parser); + _ecore_getopt_help_options(fp, parser); +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_long(const Ecore_Getopt *parser, const char *name) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char *p = strchr(name, '='); + int len = 0; + + if (p) + len = p - name; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (!desc->longname) + continue; + + if (p) + { + if ((strncmp(name, desc->longname, len) == 0) && + (desc->longname[len] == '\0')) + return desc; + } + else + { + if (strcmp(name, desc->longname) == 0) + return desc; + } + } + + return NULL; +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_short(const Ecore_Getopt *parser, char name) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + if (name == desc->shortname) + return desc; + return NULL; +} + +static int +_ecore_getopt_parse_find_nonargs_base(const Ecore_Getopt *parser, int argc, char **argv) +{ + char *nonargs[argc]; + int src, dst, used, base; + + src = 1; + dst = 1; + used = 0; + base = 0; + while (src < argc) + { + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + char *arg = argv[src]; + + if (arg[0] != '-') + goto found_nonarg; + + if (arg[1] == '-') + { + if (arg[2] == '\0') /* explicit end of options, "--" */ + { + base = 1; + break; + } + desc = _ecore_getopt_parse_find_long(parser, arg + 2); + } + else + desc = _ecore_getopt_parse_find_short(parser, arg[1]); + + if (!desc) + { + if (arg[1] == '-') + fprintf(stderr, "ERROR: unknown option --%s.\n", arg + 2); + else + fprintf(stderr, "ERROR: unknown option -%c.\n", arg[1]); + if (parser->strict) + { + memmove(argv + dst, nonargs, used * sizeof(char *)); + return -1; + } + else + goto found_nonarg; + } + + if (src != dst) + argv[dst] = argv[src]; + src++; + dst++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + continue; + + if (strchr(arg, '=')) + continue; + + if ((src >= argc) || (argv[src][0] == '-')) + continue; + + if (src != dst) + argv[dst] = argv[src]; + src++; + dst++; + continue; + + found_nonarg: + nonargs[used] = arg; + used++; + src++; + } + + if (!base) /* '--' not found */ + base = dst; + else + { + base = dst; + if (src != dst) + argv[dst] = argv[src]; + src++; + dst++; + } + + memmove(argv + dst, nonargs, used * sizeof(char *)); + return base; +} + +static void +_ecore_getopt_desc_print_error(const Ecore_Getopt_Desc *desc, const char *fmt, ...) +{ + va_list ap; + + fputs("ERROR: ", stderr); + + if (desc->shortname) + { + fputc('-', stderr); + fputc(desc->shortname, stderr); + } + + if (desc->shortname && desc->longname) + fputs(", ", stderr); + + if (desc->longname) + { + fputs("--", stderr); + fputs(desc->longname, stderr); + } + + fputs(": ", stderr); + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +static unsigned char +_ecore_getopt_parse_bool(const char *str, unsigned char *v) +{ + if ((strcmp(str, "0") == 0) || + (strcasecmp(str, "f") == 0) || + (strcasecmp(str, "false") == 0) || + (strcasecmp(str, "no") == 0) || + (strcasecmp(str, "off") == 0)) + { + *v = 0; + return 1; + } + else if ((strcmp(str, "1") == 0) || + (strcasecmp(str, "t") == 0) || + (strcasecmp(str, "true") == 0) || + (strcasecmp(str, "yes") == 0) || + (strcasecmp(str, "on") == 0)) + { + *v = 1; + return 1; + } + + return 0; +} + +static unsigned char +_ecore_getopt_parse_long(const char *str, long int *v) +{ + char *endptr = NULL; + *v = strtol(str, &endptr, 0); + return endptr > str; +} + +static unsigned char +_ecore_getopt_parse_double(const char *str, double *v) +{ + char *endptr = NULL; + *v = strtod(str, &endptr); + return endptr > str; +} + +static unsigned char +_ecore_getopt_parse_store(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val) +{ + const Ecore_Getopt_Desc_Store *store = &desc->action_param.store; + long int v; + double d; + unsigned char b; + + if (!value->ptrp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + + switch (store->arg_req) + { + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO: + goto use_optional; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL: + if (!arg_val) + goto use_optional; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES: + break; + } + + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + *value->strp = (char *)arg_val; + return 1; + case ECORE_GETOPT_TYPE_BOOL: + if (_ecore_getopt_parse_bool(arg_val, &b)) + { + *value->boolp = b; + return 1; + } + else + { + _ecore_getopt_desc_print_error + (desc, "unknown boolean value %s.\n", arg_val); + return 0; + } + case ECORE_GETOPT_TYPE_SHORT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->shortp = v; + return 1; + case ECORE_GETOPT_TYPE_INT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->intp = v; + return 1; + case ECORE_GETOPT_TYPE_LONG: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->longp = v; + return 1; + case ECORE_GETOPT_TYPE_USHORT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->ushortp = v; + return 1; + case ECORE_GETOPT_TYPE_UINT: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->uintp = v; + return 1; + case ECORE_GETOPT_TYPE_ULONG: + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + *value->ulongp = v; + return 1; + case ECORE_GETOPT_TYPE_DOUBLE: + if (!_ecore_getopt_parse_double(arg_val, &d)) + goto error; + *value->doublep = d; + break; + } + + return 1; + + error: + _ecore_getopt_desc_print_error(desc, "invalid number format %s\n", arg_val); + return 0; + + use_optional: + switch (store->type) + { + case ECORE_GETOPT_TYPE_STR: + *value->strp = (char *)store->def.strv; + break; + case ECORE_GETOPT_TYPE_BOOL: + *value->boolp = store->def.boolv; + break; + case ECORE_GETOPT_TYPE_SHORT: + *value->shortp = store->def.shortv; + break; + case ECORE_GETOPT_TYPE_INT: + *value->intp = store->def.intv; + break; + case ECORE_GETOPT_TYPE_LONG: + *value->longp = store->def.longv; + break; + case ECORE_GETOPT_TYPE_USHORT: + *value->ushortp = store->def.ushortv; + break; + case ECORE_GETOPT_TYPE_UINT: + *value->uintp = store->def.uintv; + break; + case ECORE_GETOPT_TYPE_ULONG: + *value->ulongp = store->def.ulongv; + break; + case ECORE_GETOPT_TYPE_DOUBLE: + *value->doublep = store->def.doublev; + break; + } + + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_const(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (!val->ptrp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + + *val->ptrp = (void *)desc->action_param.store_const; + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_true(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (!val->boolp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + *val->boolp = 1; + return 1; +} + +static unsigned char +_ecore_getopt_parse_store_false(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (!val->boolp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + *val->boolp = 0; + return 1; +} + +static unsigned char +_ecore_getopt_parse_choice(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + const char * const *pchoice; + + if (!val->strp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + + pchoice = desc->action_param.choices; + for (; *pchoice != NULL; pchoice++) + if (strcmp(*pchoice, arg_val) == 0) + { + *val->strp = (char *)*pchoice; + return 1; + } + + _ecore_getopt_desc_print_error + (desc, "invalid choice \"%s\". Valid values are: ", arg_val); + + pchoice = desc->action_param.choices; + for (; *pchoice != NULL; pchoice++) + { + fputs(*pchoice, stderr); + if (pchoice[1] != NULL) + fputs(", ", stderr); + } + + fputs(".\n", stderr); + return 0; +} + +static unsigned char +_ecore_getopt_parse_append(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + void *data; + long int v; + double d; + unsigned char b; + + if (!arg_val) + { + _ecore_getopt_desc_print_error(desc, "missing parameter to append.\n"); + return 0; + } + + if (!val->listp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + + switch (desc->action_param.append_type) + { + case ECORE_GETOPT_TYPE_STR: + data = strdup(arg_val); + break; + case ECORE_GETOPT_TYPE_BOOL: + { + if (_ecore_getopt_parse_bool(arg_val, &b)) + { + data = malloc(sizeof(unsigned char)); + if (data) + *(unsigned char *)data = b; + } + else + { + _ecore_getopt_desc_print_error + (desc, "unknown boolean value %s.\n", arg_val); + return 0; + } + } + break; + case ECORE_GETOPT_TYPE_SHORT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(short)); + if (data) + *(short *)data = (short)v; + } + break; + case ECORE_GETOPT_TYPE_INT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(int)); + if (data) + *(int *)data = (int)v; + } + break; + case ECORE_GETOPT_TYPE_LONG: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(long)); + if (data) + *(long *)data = v; + } + break; + case ECORE_GETOPT_TYPE_USHORT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned short)); + if (data) + *(unsigned short *)data = (unsigned short)v; + } + break; + case ECORE_GETOPT_TYPE_UINT: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned int)); + if (data) + *(unsigned int *)data = (unsigned int)v; + } + break; + case ECORE_GETOPT_TYPE_ULONG: + { + if (!_ecore_getopt_parse_long(arg_val, &v)) + goto error; + data = malloc(sizeof(unsigned long)); + if (data) + *(unsigned long *)data = v; + } + break; + case ECORE_GETOPT_TYPE_DOUBLE: + { + if (!_ecore_getopt_parse_double(arg_val, &d)) + goto error; + data = malloc(sizeof(double)); + if (data) + *(double *)data = d; + } + break; + } + + if (!data) + { + _ecore_getopt_desc_print_error(desc, "could not parse value.\n"); + return 0; + } + + *val->listp = eina_list_append(*val->listp, data); + return 1; + + error: + _ecore_getopt_desc_print_error(desc, "invalid number format %s\n", arg_val); + return 0; +} + +static unsigned char +_ecore_getopt_parse_count(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (!val->intp) + { + _ecore_getopt_desc_print_error(desc, "value has no pointer set.\n"); + return 0; + } + + (*val->intp)++; + return 1; +} + +static unsigned char +_ecore_getopt_parse_callback(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + const Ecore_Getopt_Desc_Callback *cb = &desc->action_param.callback; + + switch (cb->arg_req) + { + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO: + arg_val = cb->def; + break; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL: + if (!arg_val) + arg_val = cb->def; + break; + case ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES: + break; + } + + if (cb->arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + if ((!arg_val) || (arg_val[0] == '\0')) + { + _ecore_getopt_desc_print_error(desc, "missing parameter.\n"); + return 0; + } + + if (!val->ptrp) + { + _ecore_getopt_desc_print_error + (desc, "value has no pointer set.\n"); + return 0; + } + } + + if (!cb->func) + { + _ecore_getopt_desc_print_error(desc, "missing callback function!\n"); + return 0; + } + + return cb->func(parser, desc, arg_val, (void *)cb->data, val); +} + +static unsigned char +_ecore_getopt_parse_help(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (val->boolp) + (*val->boolp) = 1; + ecore_getopt_help(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_version(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->version) + { + _ecore_getopt_desc_print_error(desc, "no version was defined.\n"); + return 0; + } + _ecore_getopt_version(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_copyright(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->copyright) + { + _ecore_getopt_desc_print_error(desc, "no copyright was defined.\n"); + return 0; + } + _ecore_getopt_copyright(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_parse_license(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *val, const char *arg_val) +{ + if (val->boolp) + (*val->boolp) = 1; + if (!parser->license) + { + _ecore_getopt_desc_print_error(desc, "no license was defined.\n"); + return 0; + } + _ecore_getopt_license(stdout, parser); + return 1; +} + +static unsigned char +_ecore_getopt_desc_handle(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *desc, Ecore_Getopt_Value *value, const char *arg_val) +{ + switch (desc->action) + { + case ECORE_GETOPT_ACTION_STORE: + return _ecore_getopt_parse_store(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_CONST: + return _ecore_getopt_parse_store_const(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_TRUE: + return _ecore_getopt_parse_store_true(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_STORE_FALSE: + return _ecore_getopt_parse_store_false(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_CHOICE: + return _ecore_getopt_parse_choice(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_APPEND: + return _ecore_getopt_parse_append(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_COUNT: + return _ecore_getopt_parse_count(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_CALLBACK: + return _ecore_getopt_parse_callback(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_HELP: + return _ecore_getopt_parse_help(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_VERSION: + return _ecore_getopt_parse_version(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_COPYRIGHT: + return _ecore_getopt_parse_copyright(parser, desc, value, arg_val); + case ECORE_GETOPT_ACTION_LICENSE: + return _ecore_getopt_parse_license(parser, desc, value, arg_val); + default: + return 0; + } +} + +static unsigned char +_ecore_getopt_parse_arg_long(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int *idx, int *nonargs, const char *arg) +{ + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *arg_val; + int desc_idx; + Ecore_Getopt_Value *value; + unsigned char ret; + + desc = _ecore_getopt_parse_find_long(parser, arg); + if (!desc) + { + fprintf(stderr, "ERROR: unknown option --%s, ignored.\n", arg); + if (parser->strict) + return 0; + + (*idx)++; + return 1; + } + + (*idx)++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + arg_val = strchr(arg, '='); + if (arg_val) + arg_val++; + else + { + if ((*idx < *nonargs) && (argv[*idx][0] != '-')) + { + arg_val = argv[*idx]; + (*idx)++; + } + else + arg_val = NULL; + } + + if (arg_val && arg_val[0] == '\0') + arg_val = NULL; + + if ((!arg_val) && (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)) + { + fprintf(stderr, "ERROR: option --%s requires an argument!\n", arg); + if (parser->strict) + return 0; + return 1; + } + } + else + arg_val = NULL; + + desc_idx = desc - parser->descs; + value = values + desc_idx; + ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val); + if ((!ret) && parser->strict) + return 0; + + return 1; +} + +static unsigned char +_ecore_getopt_parse_arg_short(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int *idx, int *nonargs, const char *arg) +{ + int run = 1; + while (run && (arg[0] != '\0')) + { + int opt = arg[0]; + const Ecore_Getopt_Desc *desc; + Ecore_Getopt_Desc_Arg_Requirement arg_req; + const char *arg_val; + int desc_idx; + Ecore_Getopt_Value *value; + unsigned char ret; + + desc = _ecore_getopt_parse_find_short(parser, arg[0]); + if (!desc) + { + fprintf(stderr, "ERROR: unknown option -%c, ignored.\n", arg[0]); + if (parser->strict) + return 0; + + arg++; + continue; + } + + arg++; + + arg_req = _ecore_getopt_desc_arg_requirement(desc); + if (arg_req != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) + { + (*idx)++; + run = 0; + + if (arg[0] == '=') + arg_val = arg + 1; + else if (arg[0] != '\0') + arg_val = arg; + else + { + if ((*idx < *nonargs) && (argv[*idx][0] != '-')) + { + arg_val = argv[*idx]; + (*idx)++; + } + else + arg_val = NULL; + } + + if (arg_val && arg_val[0] == '\0') + arg_val = NULL; + + if ((!arg_val) && + (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES)) + { + fprintf(stderr, "ERROR: option -%c requires an argument!\n", + opt); + if (parser->strict) + return 0; + return 1; + } + } + else + arg_val = NULL; + + desc_idx = desc - parser->descs; + value = values + desc_idx; + ret = _ecore_getopt_desc_handle(parser, desc, value, arg_val); + if ((!ret) && parser->strict) + return 0; + } + + if (run) + (*idx)++; + + return 1; +} + +static unsigned char +_ecore_getopt_parse_arg(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int *idx, int *nonargs) +{ + char *arg = argv[*idx]; + + if (arg[0] != '-') + { + char **dst, **src, **src_end; + + dst = argv + *idx; + src = dst + 1; + src_end = src + *nonargs - *idx - 1; + + for (; src < src_end; src++, dst++) + *dst = *src; + + *dst = arg; + (*nonargs)--; + return 1; + } + + if (arg[1] == '-') + return _ecore_getopt_parse_arg_long + (parser, values, argc, argv, idx, nonargs, arg + 2); + else + return _ecore_getopt_parse_arg_short + (parser, values, argc, argv, idx, nonargs, arg + 1); +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char c = orig->shortname; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc == orig) + return NULL; + + if (c == desc->shortname) + return desc; + } + + return NULL; +} + +static const Ecore_Getopt_Desc * +_ecore_getopt_parse_find_long_other(const Ecore_Getopt *parser, const Ecore_Getopt_Desc *orig) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + const char *name = orig->longname; + + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc == orig) + return NULL; + + if (desc->longname && (strcmp(name, desc->longname) == 0)) + return desc; + } + + return NULL; +} + +/** + * Check parser for duplicate entries, print them out. + * + * @return 1 if there are duplicates, 0 otherwise. + */ +unsigned char +ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + { + if (desc->shortname) + { + const Ecore_Getopt_Desc *other; + other = _ecore_getopt_parse_find_short_other(parser, desc); + if (other) + { + _ecore_getopt_desc_print_error + (desc, "short name -%c already exists.", desc->shortname); + + if (other->longname) + fprintf(stderr, " Other is --%s.\n", other->longname); + else + fputc('\n', stderr); + return 1; + } + } + + if (desc->longname) + { + const Ecore_Getopt_Desc *other; + other = _ecore_getopt_parse_find_long_other(parser, desc); + if (other) + { + _ecore_getopt_desc_print_error + (desc, "long name --%s already exists.", desc->longname); + + if (other->shortname) + fprintf(stderr, " Other is -%c.\n", other->shortname); + else + fputc('\n', stderr); + return 1; + } + } + } + return 0; +} + +const Ecore_Getopt_Desc * +_ecore_getopt_find_help(const Ecore_Getopt *parser) +{ + const Ecore_Getopt_Desc *desc = parser->descs; + for (; !_ecore_getopt_desc_is_sentinel(desc); desc++) + if (desc->action == ECORE_GETOPT_ACTION_HELP) + return desc; + return NULL; +} + +/** + * Parse command line parameters. + * + * Walks the command line parameters and parse them based on @a parser + * description, doing actions based on @c parser->descs->action, like + * showing help text, license, copyright, storing values in values and + * so on. + * + * It is expected that values is of the same size than @c parser->descs, + * options that do not need a value it will be left untouched. + * + * All values are expected to be initialized before use. Options with + * action @c ECORE_GETOPT_ACTION_STORE and non required arguments + * (others than @c ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES), are expected + * to provide a value in @c def to be used. + * + * The following actions will store 1 on value as a boolean + * (@c value->boolp) if it's not NULL to indicate these actions were executed: + * - @c ECORE_GETOPT_ACTION_HELP + * - @c ECORE_GETOPT_ACTION_VERSION + * - @c ECORE_GETOPT_ACTION_COPYRIGHT + * - @c ECORE_GETOPT_ACTION_LICENSE + * + * Just @c ECORE_GETOPT_ACTION_APPEND will allocate memory and thus + * need to be freed. For consistency between all of appended subtypes, + * @c eina_list->data will contain an allocated memory with the value, + * that is, for @c ECORE_GETOPT_TYPE_STR it will contain a copy of the + * argument, @c ECORE_GETOPT_TYPE_INT a pointer to an allocated + * integer and so on. + * + * If parser is in strict mode (see @c Ecore_Getopt->strict), then any + * error will abort parsing and -1 is returned. Otherwise it will try + * to continue as far as possible. + * + * This function may reorder @a argv elements. + * + * @param parser description of how to work. + * @param value where to store values, it is assumed that this is a vector + * of the same size as @c parser->descs. Values should be previously + * initialized. + * @param argc how many elements in @a argv. If not provided it will be + * retrieved with ecore_app_args_get(). + * @param argv command line parameters. + * + * @return index of first non-option parameter or -1 on error. + */ +int +ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv) +{ + int i, nonargs; + + if (!parser) + { + fputs("ERROR: no parser provided.\n", stderr); + return -1; + } + if (!values) + { + fputs("ERROR: no values provided.\n", stderr); + return -1; + } + + if ((argc < 1) || (argv == NULL)) + ecore_app_args_get(&argc, &argv); + + if (argc < 1) + { + fputs("ERROR: no arguments provided.\n", stderr); + return -1; + } + + if (argv[0] != NULL) + prog = argv[0]; + else + prog = parser->prog; + + nonargs = _ecore_getopt_parse_find_nonargs_base(parser, argc, argv); + if (nonargs < 0) + goto error; + + if (nonargs > argc) + nonargs = argc; + + i = 1; + while (i < nonargs) + if (!_ecore_getopt_parse_arg(parser, values, argc, argv, &i, &nonargs)) + goto error; + + return nonargs; + + error: + { + const Ecore_Getopt_Desc *help; + fputs("ERROR: invalid options found.", stderr); + + help = _ecore_getopt_find_help(parser); + if (!help) + fputc('\n', stderr); + else if (help->longname) + fprintf(stderr, " See --%s.\n", help->longname); + else + fprintf(stderr, " See -%c.\n", help->shortname); + } + + return -1; +} + +/** + * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND. + * + * @param list pointer to list to be freed. + * @return always NULL, so you can easily make your list head NULL. + */ +Eina_List * +ecore_getopt_list_free(Eina_List *list) +{ + while (list) + { + free(list->data); + list = eina_list_remove_list(list, list); + } + return NULL; +} -- 2.7.4