diff options
author | Gustavo Sverzut Barbieri <barbieri@profusion.mobi> | 2013-12-17 21:06:53 -0200 |
---|---|---|
committer | Gustavo Sverzut Barbieri <barbieri@profusion.mobi> | 2013-12-17 21:06:53 -0200 |
commit | 0b86e5119f328b6fa979bcbff6391a6458c6efe8 (patch) | |
tree | feeaf8fdd1b446a2699ae65050d258d4260ed41d /src/lib/ecore | |
parent | 1c1f9ea0e1a2c4014d5cb5ca0f749164dcd384e5 (diff) |
getopt: add positional argument handling.
positional arguments must appear at the end of the description array
(after the last option) and should have a metavar set and not have
shortname or longname. Simple, elegant and fit :-)
There is a new function to parse the positional arguments,
ecore_getopt_parse_positional() because we may want to not try to
parse them in the case of a quit-option such as --help, --license,
--copyright, --version or some user-defined action. This avoids us
producing errors of missing positional arguments when printing help
and adds some flexibility as well.
This should make Tasn happy :-)
Diffstat (limited to 'src/lib/ecore')
-rw-r--r-- | src/lib/ecore/Ecore_Getopt.h | 3 | ||||
-rw-r--r-- | src/lib/ecore/ecore_getopt.c | 299 |
2 files changed, 296 insertions, 6 deletions
diff --git a/src/lib/ecore/Ecore_Getopt.h b/src/lib/ecore/Ecore_Getopt.h index 2cc73ad161..64e3eed78d 100644 --- a/src/lib/ecore/Ecore_Getopt.h +++ b/src/lib/ecore/Ecore_Getopt.h | |||
@@ -409,6 +409,9 @@ EAPI void ecore_getopt_help(FILE *fp, const Ecore_Getopt *info); | |||
409 | EAPI Eina_Bool ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser); | 409 | EAPI Eina_Bool ecore_getopt_parser_has_duplicates(const Ecore_Getopt *parser); |
410 | EAPI int ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv); | 410 | EAPI int ecore_getopt_parse(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv); |
411 | 411 | ||
412 | EAPI int ecore_getopt_parse_positional(const Ecore_Getopt *parser, Ecore_Getopt_Value *values, int argc, char **argv, int start); | ||
413 | |||
414 | |||
412 | EAPI Eina_List *ecore_getopt_list_free(Eina_List *list); | 415 | EAPI Eina_List *ecore_getopt_list_free(Eina_List *list); |
413 | 416 | ||
414 | /* helper functions to be used with ECORE_GETOPT_CALLBACK_*() */ | 417 | /* helper functions to be used with ECORE_GETOPT_CALLBACK_*() */ |
diff --git a/src/lib/ecore/ecore_getopt.c b/src/lib/ecore/ecore_getopt.c index 2a7850f48b..2af5d47183 100644 --- a/src/lib/ecore/ecore_getopt.c +++ b/src/lib/ecore/ecore_getopt.c | |||
@@ -43,6 +43,12 @@ static int _argc = 0; | |||
43 | static int cols = 80; | 43 | static int cols = 80; |
44 | static int helpcol = 80 / 3; | 44 | static int helpcol = 80 / 3; |
45 | 45 | ||
46 | |||
47 | static Eina_Bool _ecore_getopt_desc_is_sentinel(const Ecore_Getopt_Desc *desc); | ||
48 | static Ecore_Getopt_Desc_Arg_Requirement _ecore_getopt_desc_arg_requirement(const Ecore_Getopt_Desc *desc); | ||
49 | static void _ecore_getopt_help_desc_setup_metavar(const Ecore_Getopt_Desc *desc, char *metavar, int *metavarlen, int maxsize); | ||
50 | |||
51 | |||
46 | static void | 52 | static void |
47 | _ecore_getopt_help_print_replace_program(FILE *fp, | 53 | _ecore_getopt_help_print_replace_program(FILE *fp, |
48 | const Ecore_Getopt *parser EINA_UNUSED, | 54 | const Ecore_Getopt *parser EINA_UNUSED, |
@@ -98,7 +104,42 @@ _ecore_getopt_help_usage(FILE *fp, | |||
98 | 104 | ||
99 | if (!parser->usage) | 105 | if (!parser->usage) |
100 | { | 106 | { |
101 | fprintf(fp, _("%s [options]\n"), prog); | 107 | const Ecore_Getopt_Desc *d; |
108 | |||
109 | fprintf(fp, _("%s [options]"), prog); | ||
110 | |||
111 | for (d = parser->descs; !_ecore_getopt_desc_is_sentinel(d); d++); | ||
112 | |||
113 | if (d->metavar) | ||
114 | { | ||
115 | for (; d->metavar != NULL; d++) | ||
116 | { | ||
117 | Ecore_Getopt_Desc_Arg_Requirement ar; | ||
118 | char metavar[32]; | ||
119 | int metavarlen = 0; | ||
120 | |||
121 | ar = _ecore_getopt_desc_arg_requirement(d); | ||
122 | _ecore_getopt_help_desc_setup_metavar(d, metavar, | ||
123 | &metavarlen, | ||
124 | sizeof(metavar)); | ||
125 | |||
126 | fputc(' ', fp); | ||
127 | if (ar != ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES) | ||
128 | fputc('[', fp); | ||
129 | fputs(metavar, fp); | ||
130 | |||
131 | if (ar != ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES) | ||
132 | fputc(']', fp); | ||
133 | |||
134 | if (d->action == ECORE_GETOPT_ACTION_APPEND) | ||
135 | { | ||
136 | fprintf(fp, " [%s] ...", metavar); | ||
137 | break; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | |||
142 | fputc('\n', fp); | ||
102 | return; | 143 | return; |
103 | } | 144 | } |
104 | 145 | ||
@@ -358,7 +399,8 @@ static int | |||
358 | _ecore_getopt_help_desc_show_arg(FILE *fp, | 399 | _ecore_getopt_help_desc_show_arg(FILE *fp, |
359 | Ecore_Getopt_Desc_Arg_Requirement requirement, | 400 | Ecore_Getopt_Desc_Arg_Requirement requirement, |
360 | const char *metavar, | 401 | const char *metavar, |
361 | int metavarlen) | 402 | int metavarlen, |
403 | Eina_Bool show_attr) | ||
362 | { | 404 | { |
363 | int used; | 405 | int used; |
364 | 406 | ||
@@ -375,9 +417,13 @@ _ecore_getopt_help_desc_show_arg(FILE *fp, | |||
375 | 417 | ||
376 | if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) | 418 | if (requirement != ECORE_GETOPT_DESC_ARG_REQUIREMENT_NO) |
377 | { | 419 | { |
378 | fputc('=', fp); | 420 | if (show_attr) |
421 | { | ||
422 | fputc('=', fp); | ||
423 | used++; | ||
424 | } | ||
379 | fputs(metavar, fp); | 425 | fputs(metavar, fp); |
380 | used += metavarlen + 1; | 426 | used += metavarlen; |
381 | } | 427 | } |
382 | 428 | ||
383 | if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) | 429 | if (requirement == ECORE_GETOPT_DESC_ARG_REQUIREMENT_OPTIONAL) |
@@ -597,7 +643,7 @@ _ecore_getopt_help_desc(FILE *fp, | |||
597 | fputc(desc->shortname, fp); | 643 | fputc(desc->shortname, fp); |
598 | used += 2; | 644 | used += 2; |
599 | used += _ecore_getopt_help_desc_show_arg | 645 | used += _ecore_getopt_help_desc_show_arg |
600 | (fp, arg_req, metavar, metavarlen); | 646 | (fp, arg_req, metavar, metavarlen, EINA_TRUE); |
601 | } | 647 | } |
602 | 648 | ||
603 | if (desc->shortname && desc->longname) | 649 | if (desc->shortname && desc->longname) |
@@ -614,7 +660,12 @@ _ecore_getopt_help_desc(FILE *fp, | |||
614 | fputs(desc->longname, fp); | 660 | fputs(desc->longname, fp); |
615 | used += 2 + namelen; | 661 | used += 2 + namelen; |
616 | used += _ecore_getopt_help_desc_show_arg | 662 | used += _ecore_getopt_help_desc_show_arg |
617 | (fp, arg_req, metavar, metavarlen); | 663 | (fp, arg_req, metavar, metavarlen, EINA_TRUE); |
664 | } | ||
665 | else if ((!desc->shortname) && (desc->metavar)) | ||
666 | { | ||
667 | used += _ecore_getopt_help_desc_show_arg | ||
668 | (fp, arg_req, metavar, metavarlen, EINA_FALSE); | ||
618 | } | 669 | } |
619 | 670 | ||
620 | if (!desc->help) | 671 | if (!desc->help) |
@@ -668,6 +719,14 @@ _ecore_getopt_help_options(FILE *fp, | |||
668 | _ecore_getopt_help_desc(fp, desc); | 719 | _ecore_getopt_help_desc(fp, desc); |
669 | 720 | ||
670 | fputc('\n', fp); | 721 | fputc('\n', fp); |
722 | |||
723 | if (!desc->metavar) return; | ||
724 | |||
725 | fputs(_("Positional arguments:\n"), fp); | ||
726 | for (; desc->metavar != NULL; desc++) | ||
727 | _ecore_getopt_help_desc(fp, desc); | ||
728 | |||
729 | fputc('\n', fp); | ||
671 | } | 730 | } |
672 | 731 | ||
673 | /** | 732 | /** |
@@ -877,6 +936,14 @@ _ecore_getopt_desc_print_error(const Ecore_Getopt_Desc *desc, | |||
877 | fputs("--", stderr); | 936 | fputs("--", stderr); |
878 | fputs(desc->longname, stderr); | 937 | fputs(desc->longname, stderr); |
879 | } | 938 | } |
939 | else if ((!desc->shortname) && (desc->metavar)) | ||
940 | { | ||
941 | char metavar[32]; | ||
942 | int metavarlen = 0; | ||
943 | _ecore_getopt_help_desc_setup_metavar(desc, metavar, &metavarlen, | ||
944 | sizeof(metavar)); | ||
945 | fputs(metavar, stderr); | ||
946 | } | ||
880 | 947 | ||
881 | fputs(": ", stderr); | 948 | fputs(": ", stderr); |
882 | 949 | ||
@@ -1667,6 +1734,98 @@ _ecore_getopt_parse_arg(const Ecore_Getopt *parser, | |||
1667 | return _ecore_getopt_parse_arg_short(parser, values, argc, argv, idx, nonargs, arg + 1); | 1734 | return _ecore_getopt_parse_arg_short(parser, values, argc, argv, idx, nonargs, arg + 1); |
1668 | } | 1735 | } |
1669 | 1736 | ||
1737 | static Eina_Bool | ||
1738 | _ecore_getopt_parse_pos(const Ecore_Getopt *parser, | ||
1739 | const Ecore_Getopt_Desc **p_desc, | ||
1740 | Ecore_Getopt_Value *values, | ||
1741 | int argc, | ||
1742 | char **argv, | ||
1743 | int *idx, | ||
1744 | int *nonargs) | ||
1745 | { | ||
1746 | const Ecore_Getopt_Desc *desc = *p_desc; | ||
1747 | Ecore_Getopt_Desc_Arg_Requirement arg_req; | ||
1748 | char metavar[32]; | ||
1749 | int metavarlen = 0; | ||
1750 | const char *arg_val; | ||
1751 | int desc_idx; | ||
1752 | Ecore_Getopt_Value *value; | ||
1753 | Eina_Bool ret; | ||
1754 | |||
1755 | _ecore_getopt_help_desc_setup_metavar | ||
1756 | (desc, metavar, &metavarlen, sizeof(metavar)); | ||
1757 | |||
1758 | desc_idx = desc - parser->descs; | ||
1759 | value = values + desc_idx; | ||
1760 | |||
1761 | arg_req = _ecore_getopt_desc_arg_requirement(desc); | ||
1762 | if (*idx >= argc) | ||
1763 | { | ||
1764 | (*p_desc)++; | ||
1765 | if (arg_req == ECORE_GETOPT_DESC_ARG_REQUIREMENT_YES) | ||
1766 | { | ||
1767 | /* TODO: should we consider callback here as well? */ | ||
1768 | if ((desc->action == ECORE_GETOPT_ACTION_APPEND) && | ||
1769 | (value->listp) && (*value->listp)) | ||
1770 | { | ||
1771 | printf("append desc: %s (%d), value: %p\n", | ||
1772 | desc->metavar, desc_idx, value->listp); | ||
1773 | return EINA_TRUE; | ||
1774 | } | ||
1775 | |||
1776 | fprintf(stderr, | ||
1777 | _("ERROR: missing required positional argument %s.\n"), | ||
1778 | metavar); | ||
1779 | return EINA_FALSE; | ||
1780 | } | ||
1781 | return EINA_TRUE; | ||
1782 | } | ||
1783 | |||
1784 | arg_val = argv[*idx]; | ||
1785 | |||
1786 | switch (desc->action) | ||
1787 | { | ||
1788 | case ECORE_GETOPT_ACTION_STORE: | ||
1789 | ret = _ecore_getopt_parse_store(parser, desc, value, arg_val); | ||
1790 | (*p_desc)++; | ||
1791 | break; | ||
1792 | |||
1793 | case ECORE_GETOPT_ACTION_CHOICE: | ||
1794 | ret = _ecore_getopt_parse_choice(parser, desc, value, arg_val); | ||
1795 | (*p_desc)++; | ||
1796 | break; | ||
1797 | |||
1798 | case ECORE_GETOPT_ACTION_APPEND: | ||
1799 | ret = _ecore_getopt_parse_append(parser, desc, value, arg_val); | ||
1800 | /* no changes to p_desc, we keep appending until the end */ | ||
1801 | break; | ||
1802 | |||
1803 | case ECORE_GETOPT_ACTION_CALLBACK: | ||
1804 | ret = _ecore_getopt_parse_callback(parser, desc, value, arg_val); | ||
1805 | (*p_desc)++; | ||
1806 | break; | ||
1807 | |||
1808 | default: | ||
1809 | fprintf(stderr, _("ERROR: unsupported action type %d " | ||
1810 | "for positional argument %s\n"), | ||
1811 | desc->action, metavar); | ||
1812 | (*p_desc)++; | ||
1813 | ret = EINA_FALSE; | ||
1814 | break; | ||
1815 | } | ||
1816 | |||
1817 | if (ret) | ||
1818 | { | ||
1819 | (*idx)++; | ||
1820 | (*nonargs)++; | ||
1821 | } | ||
1822 | |||
1823 | if ((!ret) && parser->strict) | ||
1824 | return EINA_FALSE; | ||
1825 | |||
1826 | return EINA_TRUE; | ||
1827 | } | ||
1828 | |||
1670 | static const Ecore_Getopt_Desc * | 1829 | static const Ecore_Getopt_Desc * |
1671 | _ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser, | 1830 | _ecore_getopt_parse_find_short_other(const Ecore_Getopt *parser, |
1672 | const Ecore_Getopt_Desc *orig) | 1831 | const Ecore_Getopt_Desc *orig) |
@@ -1803,6 +1962,15 @@ _ecore_getopt_find_help(const Ecore_Getopt *parser) | |||
1803 | * and copyright may be translated, standard/global gettext() call | 1962 | * and copyright may be translated, standard/global gettext() call |
1804 | * will be applied on them if ecore was compiled with such support. | 1963 | * will be applied on them if ecore was compiled with such support. |
1805 | * | 1964 | * |
1965 | * This function will @b not parse positional arguments! If these are | ||
1966 | * declared (metavar is defined with both shortname and longname being | ||
1967 | * empty), then you must call ecore_getopt_parse_positional() with the | ||
1968 | * last argument (@c start) being the result of this function. This is | ||
1969 | * done so you can have "quit options", those that once called you | ||
1970 | * want to exit without doing further parsing, as is the case with | ||
1971 | * help, license, copyright, version and eventually others you may | ||
1972 | * define. | ||
1973 | * | ||
1806 | * @param parser description of how to work. | 1974 | * @param parser description of how to work. |
1807 | * @param values where to store values, it is assumed that this is a vector | 1975 | * @param values where to store values, it is assumed that this is a vector |
1808 | * of the same size as @c parser->descs. Values should be previously | 1976 | * of the same size as @c parser->descs. Values should be previously |
@@ -1812,6 +1980,8 @@ _ecore_getopt_find_help(const Ecore_Getopt *parser) | |||
1812 | * @param argv command line parameters. | 1980 | * @param argv command line parameters. |
1813 | * | 1981 | * |
1814 | * @return index of first non-option parameter or -1 on error. | 1982 | * @return index of first non-option parameter or -1 on error. |
1983 | * | ||
1984 | * @see ecore_getopt_parse_positional() | ||
1815 | */ | 1985 | */ |
1816 | EAPI int | 1986 | EAPI int |
1817 | ecore_getopt_parse(const Ecore_Getopt *parser, | 1987 | ecore_getopt_parse(const Ecore_Getopt *parser, |
@@ -1878,6 +2048,123 @@ error: | |||
1878 | } | 2048 | } |
1879 | 2049 | ||
1880 | /** | 2050 | /** |
2051 | * Parse command line positional parameters. | ||
2052 | * | ||
2053 | * Walks the command line positional parameters (those that do not | ||
2054 | * start with "-" or "--") and parse them based on @a parser | ||
2055 | * description, doing actions based on @c parser->descs->action, like | ||
2056 | * storing values of some type. | ||
2057 | * | ||
2058 | * It is expected that @a values is of the same size than @c | ||
2059 | * parser->descs, same as with ecore_getopt_parse(). | ||
2060 | * | ||
2061 | * All values are expected to be initialized before use. | ||
2062 | * | ||
2063 | * Unlike the ecore_getopt_parse(), only the following options are | ||
2064 | * supported: | ||
2065 | * - @c ECORE_GETOPT_ACTION_STORE | ||
2066 | * - @c ECORE_GETOPT_ACTION_CHOICE | ||
2067 | * - @c ECORE_GETOPT_ACTION_APPEND | ||
2068 | * - @c ECORE_GETOPT_ACTION_CALLBACK | ||
2069 | * | ||
2070 | * There is a special case for @c ECORE_GETOPT_ACTION_APPEND as it | ||
2071 | * will consume all remaining elements. It is also special in the | ||
2072 | * sense that it will allocate memory and thus need to be freed. For | ||
2073 | * consistency between all of appended subtypes, @c eina_list->data | ||
2074 | * will contain an allocated memory with the value, that is, for @c | ||
2075 | * ECORE_GETOPT_TYPE_STR it will contain a copy of the argument, @c | ||
2076 | * ECORE_GETOPT_TYPE_INT a pointer to an allocated integer and so on. | ||
2077 | * | ||
2078 | * If parser is in strict mode (see @c Ecore_Getopt->strict), then any | ||
2079 | * error will abort parsing and @c -1 is returned. Otherwise it will try | ||
2080 | * to continue as far as possible. | ||
2081 | * | ||
2082 | * Translation of help strings (description) and metavar may be done, | ||
2083 | * standard/global gettext() call will be applied on them if ecore was | ||
2084 | * compiled with such support. | ||
2085 | * | ||
2086 | * @param parser description of how to work. | ||
2087 | * @param values where to store values, it is assumed that this is a vector | ||
2088 | * of the same size as @c parser->descs. Values should be previously | ||
2089 | * initialized. | ||
2090 | * @param argc how many elements in @a argv. If not provided it will be | ||
2091 | * retrieved with ecore_app_args_get(). | ||
2092 | * @param argv command line parameters. | ||
2093 | * @param start the initial position argument to look at, usually the | ||
2094 | * return of ecore_getopt_parse(). If less than 1, will try to | ||
2095 | * find it automatically. | ||
2096 | * | ||
2097 | * @return index of first non-option parameter or -1 on error. If the | ||
2098 | * last positional argument is of action @c | ||
2099 | * ECORE_GETOPT_ACTION_APPEND then it will be the same as @a argc. | ||
2100 | */ | ||
2101 | EAPI int | ||
2102 | ecore_getopt_parse_positional(const Ecore_Getopt *parser, | ||
2103 | Ecore_Getopt_Value *values, | ||
2104 | int argc, | ||
2105 | char **argv, | ||
2106 | int start) | ||
2107 | { | ||
2108 | const Ecore_Getopt_Desc *desc; | ||
2109 | int nonargs; | ||
2110 | |||
2111 | if (!parser) | ||
2112 | { | ||
2113 | fputs(_("ERROR: no parser provided.\n"), stderr); | ||
2114 | return -1; | ||
2115 | } | ||
2116 | if (!values) | ||
2117 | { | ||
2118 | fputs(_("ERROR: no values provided.\n"), stderr); | ||
2119 | return -1; | ||
2120 | } | ||
2121 | |||
2122 | if ((argc < 1) || (!argv)) | ||
2123 | ecore_app_args_get(&argc, &argv); | ||
2124 | |||
2125 | if (argc < 1) | ||
2126 | { | ||
2127 | fputs(_("ERROR: no arguments provided.\n"), stderr); | ||
2128 | return -1; | ||
2129 | } | ||
2130 | |||
2131 | if (argv[0]) | ||
2132 | prog = argv[0]; | ||
2133 | else | ||
2134 | prog = parser->prog; | ||
2135 | |||
2136 | if (start > argc) | ||
2137 | start = argc; | ||
2138 | else if (start < 1) | ||
2139 | start = _ecore_getopt_parse_find_nonargs_base(parser, argc, argv); | ||
2140 | |||
2141 | nonargs = start; | ||
2142 | for (desc = parser->descs; !_ecore_getopt_desc_is_sentinel(desc); desc++); | ||
2143 | while (desc->metavar) | ||
2144 | if (!_ecore_getopt_parse_pos | ||
2145 | (parser, &desc, values, argc, argv, &start, &nonargs)) | ||
2146 | goto error; | ||
2147 | |||
2148 | return nonargs; | ||
2149 | |||
2150 | error: | ||
2151 | { | ||
2152 | const Ecore_Getopt_Desc *help; | ||
2153 | fputs(_("ERROR: invalid positional arguments found."), stderr); | ||
2154 | |||
2155 | help = _ecore_getopt_find_help(parser); | ||
2156 | if (!help) | ||
2157 | fputc('\n', stderr); | ||
2158 | else if (help->longname) | ||
2159 | fprintf(stderr, _(" See --%s.\n"), help->longname); | ||
2160 | else | ||
2161 | fprintf(stderr, _(" See -%c.\n"), help->shortname); | ||
2162 | } | ||
2163 | |||
2164 | return -1; | ||
2165 | } | ||
2166 | |||
2167 | /** | ||
1881 | * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND. | 2168 | * Utility to free list and nodes allocated by @a ECORE_GETOPT_ACTION_APPEND. |
1882 | * | 2169 | * |
1883 | * @param list pointer to list to be freed. | 2170 | * @param list pointer to list to be freed. |