Add generic library for option parsing.
* configure.ac: Build libmailutils/opt * include/mailutils/Makefile.am: Add opt.h * include/mailutils/opt.h: New file. * include/mailutils/util.h (mu_c_type_t): New datatype. (mu_str_to_c): New proto. * libmailutils/Makefile.am: Add opt to subdirs. * libmailutils/opt/Makefile.am: New file. * libmailutils/opt/help.c: New file. * libmailutils/opt/opt.c: New file. * libmailutils/opt/progname.c: New file. * libmailutils/string/Makefile.am: Add new files. * libmailutils/string/str_to_c.c: New file. * libmailutils/string/to_sn.c: New file. * libmailutils/string/to_un.c: New file. * libmailutils/tests/.gitignore: Update. * libmailutils/tests/Makefile.am: Update. * libmailutils/tests/parseopt.c: New file.
Showing
16 changed files
with
1684 additions
and
3 deletions
... | @@ -1519,6 +1519,7 @@ AC_CONFIG_FILES([ | ... | @@ -1519,6 +1519,7 @@ AC_CONFIG_FILES([ |
1519 | libmailutils/mailer/Makefile | 1519 | libmailutils/mailer/Makefile |
1520 | libmailutils/mime/Makefile | 1520 | libmailutils/mime/Makefile |
1521 | libmailutils/msgset/Makefile | 1521 | libmailutils/msgset/Makefile |
1522 | libmailutils/opt/Makefile | ||
1522 | libmailutils/property/Makefile | 1523 | libmailutils/property/Makefile |
1523 | libmailutils/server/Makefile | 1524 | libmailutils/server/Makefile |
1524 | libmailutils/string/Makefile | 1525 | libmailutils/string/Makefile | ... | ... |
include/mailutils/opt.h
0 → 100644
1 | /* opt.h -- general-purpose command line option parser | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 3, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | |||
18 | #ifndef _MAILUTILS_OPT_H | ||
19 | #define _MAILUTILS_OPT_H | ||
20 | #include <stdio.h> | ||
21 | #include <mailutils/types.h> | ||
22 | #include <mailutils/list.h> | ||
23 | #include <mailutils/util.h> | ||
24 | #include <mailutils/cctype.h> | ||
25 | |||
26 | extern char *mu_progname; | ||
27 | extern char *mu_absprogname; | ||
28 | |||
29 | void mu_set_progname (char const *arg); | ||
30 | |||
31 | #define MU_OPTION_DEFAULT 0 | ||
32 | #define MU_OPTION_ARG_OPTIONAL 0x01 | ||
33 | #define MU_OPTION_HIDDEN 0x02 | ||
34 | #define MU_OPTION_ALIAS 0x04 | ||
35 | #define MU_OPTION_IMMEDIATE 0x08 | ||
36 | |||
37 | struct mu_parseopt; | ||
38 | |||
39 | struct mu_option | ||
40 | { | ||
41 | char *opt_long; /* Long option name */ | ||
42 | int opt_short; /* Short option character */ | ||
43 | char *opt_arg; /* Argument name */ | ||
44 | int opt_flags; /* Flags (see above) */ | ||
45 | char *opt_doc; /* Human-readable documentation */ | ||
46 | mu_c_type_t opt_type; /* Option type */ | ||
47 | void *opt_ptr; /* Data pointer */ | ||
48 | void (*opt_set) (struct mu_parseopt *, struct mu_option *, char const *); | ||
49 | /* Function to set the option */ | ||
50 | }; | ||
51 | |||
52 | #define MU_OPTION_GROUP(text) { NULL, 0, NULL, 0, text } | ||
53 | #define MU_OPTION_END { NULL, 0, NULL, 0, NULL } | ||
54 | |||
55 | #define MU_OPTION_IS_END(opt) \ | ||
56 | (!(opt)->opt_long && !(opt)->opt_short && !(opt)->opt_doc) | ||
57 | |||
58 | #define MU_OPTION_IS_OPTION(opt) \ | ||
59 | ((opt)->opt_short || (opt)->opt_long) | ||
60 | #define MU_OPTION_IS_GROUP_HEADER(opt) \ | ||
61 | (!MU_OPTION_IS_OPTION(opt) && (opt)->opt_doc) | ||
62 | #define MU_OPTION_IS_VALID_SHORT_OPTION(opt) \ | ||
63 | ((opt)->opt_short > 0 && (opt)->opt_short < 127 && \ | ||
64 | mu_isalnum ((opt)->opt_short)) | ||
65 | #define MU_OPTION_IS_VALID_LONG_OPTION(opt) \ | ||
66 | ((opt)->opt_long != NULL) | ||
67 | |||
68 | typedef struct mu_option_cache *mu_option_cache_ptr_t; | ||
69 | |||
70 | struct mu_option_cache | ||
71 | { | ||
72 | struct mu_option *cache_opt; | ||
73 | char *cache_arg; | ||
74 | }; | ||
75 | |||
76 | #define MU_PARSEOPT_DEFAULT 0 | ||
77 | /* Don't ignore the first element of ARGV. By default it is the program | ||
78 | name */ | ||
79 | #define MU_PARSEOPT_ARGV0 0x00000001 | ||
80 | /* Ignore command line errors. */ | ||
81 | #define MU_PARSEOPT_IGNORE_ERRORS 0x00000002 | ||
82 | /* Don't order arguments so that options come first. */ | ||
83 | #define MU_PARSEOPT_IN_ORDER 0x00000004 | ||
84 | /* Don't provide standard options: -h, --help, --usage, --version */ | ||
85 | #define MU_PARSEOPT_NO_STDOPT 0x00000008 | ||
86 | /* Don't exit on errors */ | ||
87 | #define MU_PARSEOPT_NO_ERREXIT 0x00000010 | ||
88 | /* Apply all options immediately */ | ||
89 | #define MU_PARSEOPT_IMMEDIATE 0x00000020 | ||
90 | |||
91 | /* Don't sort options */ | ||
92 | #define MU_PARSEOPT_NO_SORT 0x00001000 | ||
93 | |||
94 | #define MU_PARSEOPT_PROG_NAME 0x00002000 | ||
95 | #define MU_PARSEOPT_PROG_DOC 0x00004000 | ||
96 | #define MU_PARSEOPT_PROG_ARGS 0x00008000 | ||
97 | #define MU_PARSEOPT_BUG_ADDRESS 0x00010000 | ||
98 | #define MU_PARSEOPT_PACKAGE_NAME 0x00020000 | ||
99 | #define MU_PARSEOPT_PACKAGE_URL 0x00040000 | ||
100 | #define MU_PARSEOPT_DATA 0x00080000 | ||
101 | #define MU_PARSEOPT_HELP_HOOK 0x00100000 | ||
102 | |||
103 | /* Reuse mu_parseopt struct initialized previously */ | ||
104 | #define MU_PARSEOPT_REUSE 0x80000000 | ||
105 | /* Mask for immutable flag bits */ | ||
106 | #define MU_PARSEOPT_IMMUTABLE_MASK 0xFFFFF000 | ||
107 | |||
108 | |||
109 | struct mu_parseopt | ||
110 | { | ||
111 | /* Input data: */ | ||
112 | int po_argc; /* Number of argiments */ | ||
113 | char **po_argv; /* Array of arguments */ | ||
114 | size_t po_optc; /* Number of elements in optv */ | ||
115 | struct mu_option **po_optv; /* Array of ptrs to option structures */ | ||
116 | int po_flags; | ||
117 | |||
118 | char *po_data; /* Call-specific data */ | ||
119 | |||
120 | /* Informational: */ | ||
121 | char const *po_prog_name; | ||
122 | char const *po_prog_doc; | ||
123 | char const *po_prog_args; | ||
124 | char const *po_bug_address; | ||
125 | char const *po_package_name; | ||
126 | char const *po_package_url; | ||
127 | |||
128 | void (*po_help_hook) (FILE *stream); /* FIXME: should take mu_Stream_t ?*/ | ||
129 | |||
130 | /* Output data */ | ||
131 | int po_ind; /* Index of the next option */ | ||
132 | int po_opterr; /* Index of the element in po_argv that | ||
133 | caused last error, or -1 if no errors */ | ||
134 | mu_list_t po_optlist; | ||
135 | |||
136 | /* Auxiliary data */ | ||
137 | char *po_cur; /* Points to the next character */ | ||
138 | int po_chr; /* Single-char option */ | ||
139 | |||
140 | /* The following two keep the position of the first non-optional argument | ||
141 | and the number of contiguous non-optional arguments after it. | ||
142 | Obviously, the following holds true: | ||
143 | |||
144 | arg_start + arg_count == opt_ind | ||
145 | |||
146 | If permutation is not allowed (MU_OPTION_PARSE_IN_ORDER flag is set), | ||
147 | arg_count is always 0. | ||
148 | */ | ||
149 | int po_arg_start; | ||
150 | int po_arg_count; | ||
151 | |||
152 | }; | ||
153 | |||
154 | int mu_parseopt (struct mu_parseopt *p, | ||
155 | int argc, char **argv, struct mu_option **optv, | ||
156 | int flags); | ||
157 | |||
158 | int mu_parseopt_apply (struct mu_parseopt *p); | ||
159 | void mu_parseopt_free (struct mu_parseopt *p); | ||
160 | |||
161 | void mu_option_describe_options (struct mu_option **optbuf, size_t optcnt); | ||
162 | void mu_program_help (struct mu_parseopt *p); | ||
163 | void mu_program_usage (struct mu_parseopt *p); | ||
164 | |||
165 | void mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt, | ||
166 | char const *arg); | ||
167 | |||
168 | #endif |
... | @@ -152,6 +152,35 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const char *prompt, | ... | @@ -152,6 +152,35 @@ int mu_getpass (mu_stream_t in, mu_stream_t out, const char *prompt, |
152 | char **passptr); | 152 | char **passptr); |
153 | 153 | ||
154 | /* ----------------------- */ | 154 | /* ----------------------- */ |
155 | /* String conversions. */ | ||
156 | /* ----------------------- */ | ||
157 | |||
158 | enum mu_c_type | ||
159 | { | ||
160 | mu_c_string, | ||
161 | mu_c_short, | ||
162 | mu_c_ushort, | ||
163 | mu_c_int, | ||
164 | mu_c_uint, | ||
165 | mu_c_long, | ||
166 | mu_c_ulong, | ||
167 | mu_c_size, | ||
168 | mu_c_off, | ||
169 | mu_c_time, | ||
170 | mu_c_bool, | ||
171 | mu_c_ipv4, | ||
172 | mu_c_cidr, | ||
173 | mu_c_host, | ||
174 | mu_c_incr, /* C int value, incremented each time mu_str_to_c is | ||
175 | invoked */ | ||
176 | }; | ||
177 | |||
178 | typedef enum mu_c_type mu_c_type_t; | ||
179 | |||
180 | int mu_str_to_c (char const *string, mu_c_type_t type, void *tgt, | ||
181 | char **errmsg); | ||
182 | |||
183 | /* ----------------------- */ | ||
155 | /* Assorted functions. */ | 184 | /* Assorted functions. */ |
156 | /* ----------------------- */ | 185 | /* ----------------------- */ |
157 | int mu_getmaxfd (void); | 186 | int mu_getmaxfd (void); |
... | @@ -208,7 +237,6 @@ int mu_file_safety_compose (int *res, const char *name, int defval); | ... | @@ -208,7 +237,6 @@ int mu_file_safety_compose (int *res, const char *name, int defval); |
208 | int mu_file_mode_to_safety_criteria (int mode); | 237 | int mu_file_mode_to_safety_criteria (int mode); |
209 | int mu_safety_criteria_to_file_mode (int crit); | 238 | int mu_safety_criteria_to_file_mode (int crit); |
210 | 239 | ||
211 | |||
212 | #ifdef __cplusplus | 240 | #ifdef __cplusplus |
213 | } | 241 | } |
214 | #endif | 242 | #endif | ... | ... |
... | @@ -18,7 +18,7 @@ | ... | @@ -18,7 +18,7 @@ |
18 | 18 | ||
19 | SUBDIRS = \ | 19 | SUBDIRS = \ |
20 | auth base address list sockaddr cidr cfg diag\ | 20 | auth base address list sockaddr cidr cfg diag\ |
21 | filter mailbox mailer mime msgset server string stream stdstream\ | 21 | filter mailbox mailer mime msgset opt server string stream stdstream\ |
22 | property url imapio datetime . tests | 22 | property url imapio datetime . tests |
23 | 23 | ||
24 | lib_LTLIBRARIES = libmailutils.la | 24 | lib_LTLIBRARIES = libmailutils.la |
... | @@ -42,6 +42,7 @@ libmailutils_la_LIBADD = \ | ... | @@ -42,6 +42,7 @@ libmailutils_la_LIBADD = \ |
42 | mailer/libmailer.la\ | 42 | mailer/libmailer.la\ |
43 | mime/libmime.la\ | 43 | mime/libmime.la\ |
44 | msgset/libmsgset.la\ | 44 | msgset/libmsgset.la\ |
45 | opt/libopt.la\ | ||
45 | property/libproperty.la\ | 46 | property/libproperty.la\ |
46 | server/libserver.la\ | 47 | server/libserver.la\ |
47 | string/libstring.la\ | 48 | string/libstring.la\ | ... | ... |
libmailutils/opt/Makefile.am
0 → 100644
1 | # GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | # Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | # | ||
4 | # This library is free software; you can redistribute it and/or | ||
5 | # modify it under the terms of the GNU Lesser General Public | ||
6 | # License as published by the Free Software Foundation; either | ||
7 | # version 3 of the License, or (at your option) any later version. | ||
8 | # | ||
9 | # This library is distributed in the hope that it will be useful, | ||
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | # Lesser General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU Lesser General | ||
15 | # Public License along with this library. If not, see | ||
16 | # <http://www.gnu.org/licenses/>. | ||
17 | |||
18 | noinst_LTLIBRARIES = libopt.la | ||
19 | |||
20 | libopt_la_SOURCES = \ | ||
21 | opt.c\ | ||
22 | help.c\ | ||
23 | progname.c | ||
24 | |||
25 | AM_CPPFLAGS = \ | ||
26 | @MU_LIB_COMMON_INCLUDES@ -I/libmailutils |
libmailutils/opt/help.c
0 → 100644
1 | /* help.c -- general-purpose command line option parser | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 3, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #ifdef HAVE_CONFIG_H | ||
18 | # include <config.h> | ||
19 | #endif | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | #include <mailutils/alloc.h> | ||
23 | #include <mailutils/opt.h> | ||
24 | #include <mailutils/cctype.h> | ||
25 | #include <mailutils/nls.h> | ||
26 | |||
27 | #define LMARGIN 2 | ||
28 | #define DESCRCOLUMN 30 | ||
29 | #define RMARGIN 79 | ||
30 | #define GROUPCOLUMN 2 | ||
31 | #define USAGECOLUMN 13 | ||
32 | |||
33 | static void | ||
34 | indent (size_t start, size_t col) | ||
35 | { | ||
36 | for (; start < col; start++) | ||
37 | putchar (' '); | ||
38 | } | ||
39 | |||
40 | static void | ||
41 | print_option_descr (const char *descr, size_t lmargin, size_t rmargin) | ||
42 | { | ||
43 | while (*descr) | ||
44 | { | ||
45 | size_t s = 0; | ||
46 | size_t i; | ||
47 | size_t width = rmargin - lmargin; | ||
48 | |||
49 | for (i = 0; ; i++) | ||
50 | { | ||
51 | if (descr[i] == 0 || descr[i] == ' ' || descr[i] == '\t') | ||
52 | { | ||
53 | if (i > width) | ||
54 | break; | ||
55 | s = i; | ||
56 | if (descr[i] == 0) | ||
57 | break; | ||
58 | } | ||
59 | } | ||
60 | fwrite (descr, 1, s, stdout); | ||
61 | fputc ('\n', stdout); | ||
62 | descr += s; | ||
63 | if (*descr) | ||
64 | { | ||
65 | indent (0, lmargin); | ||
66 | descr++; | ||
67 | } | ||
68 | } | ||
69 | } | ||
70 | |||
71 | |||
72 | static size_t | ||
73 | print_option (struct mu_option **optbuf, size_t optcnt, size_t num, | ||
74 | int *argsused) | ||
75 | { | ||
76 | struct mu_option *opt = optbuf[num]; | ||
77 | size_t next, i; | ||
78 | int delim; | ||
79 | int w; | ||
80 | |||
81 | if (MU_OPTION_IS_GROUP_HEADER (opt)) | ||
82 | { | ||
83 | if (num) | ||
84 | putchar ('\n'); | ||
85 | indent (0, GROUPCOLUMN); | ||
86 | print_option_descr (gettext (opt->opt_doc), GROUPCOLUMN, RMARGIN); | ||
87 | putchar ('\n'); | ||
88 | return num + 1; | ||
89 | } | ||
90 | |||
91 | /* count aliases */ | ||
92 | for (next = num + 1; | ||
93 | next < optcnt && optbuf[next]->opt_flags & MU_OPTION_ALIAS; | ||
94 | next++); | ||
95 | |||
96 | if (opt->opt_flags & MU_OPTION_HIDDEN) | ||
97 | return next; | ||
98 | |||
99 | w = 0; | ||
100 | for (i = num; i < next; i++) | ||
101 | { | ||
102 | if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i])) | ||
103 | { | ||
104 | if (w == 0) | ||
105 | { | ||
106 | indent (0, LMARGIN); | ||
107 | w = LMARGIN; | ||
108 | } | ||
109 | else | ||
110 | w += printf (", "); | ||
111 | w += printf ("-%c", optbuf[i]->opt_short); | ||
112 | delim = ' '; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | for (i = num; i < next; i++) | ||
117 | { | ||
118 | if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i])) | ||
119 | { | ||
120 | if (w == 0) | ||
121 | { | ||
122 | indent (0, LMARGIN); | ||
123 | w = LMARGIN; | ||
124 | } | ||
125 | else | ||
126 | w += printf (", "); | ||
127 | w += printf ("--%s", optbuf[i]->opt_long); | ||
128 | delim = '='; | ||
129 | } | ||
130 | } | ||
131 | |||
132 | if (opt->opt_arg) | ||
133 | { | ||
134 | *argsused = 1; | ||
135 | w += printf ("%c%s", delim, gettext (opt->opt_arg)); | ||
136 | } | ||
137 | if (w >= DESCRCOLUMN) | ||
138 | { | ||
139 | putchar ('\n'); | ||
140 | w = 0; | ||
141 | } | ||
142 | indent (w, DESCRCOLUMN); | ||
143 | print_option_descr (gettext (opt->opt_doc), DESCRCOLUMN, RMARGIN); | ||
144 | |||
145 | return next; | ||
146 | } | ||
147 | |||
148 | void | ||
149 | mu_option_describe_options (struct mu_option **optbuf, size_t optcnt) | ||
150 | { | ||
151 | unsigned i; | ||
152 | int argsused = 0; | ||
153 | |||
154 | for (i = 0; i < optcnt; ) | ||
155 | i = print_option (optbuf, optcnt, i, &argsused); | ||
156 | putchar ('\n'); | ||
157 | |||
158 | if (argsused) | ||
159 | { | ||
160 | print_option_descr (_("Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options."), 0, RMARGIN); | ||
161 | putchar ('\n'); | ||
162 | } | ||
163 | } | ||
164 | |||
165 | void | ||
166 | mu_program_help (struct mu_parseopt *po) | ||
167 | { | ||
168 | printf ("%s", _("Usage:")); | ||
169 | if (po->po_prog_name) | ||
170 | printf (" %s", po->po_prog_name); | ||
171 | printf (" [%s]...", _("OPTION")); | ||
172 | if (po->po_prog_args) | ||
173 | printf (" %s", gettext (po->po_prog_args)); | ||
174 | putchar ('\n'); | ||
175 | |||
176 | if (po->po_prog_doc) | ||
177 | print_option_descr (gettext (po->po_prog_doc), 0, RMARGIN); | ||
178 | putchar ('\n'); | ||
179 | |||
180 | mu_option_describe_options (po->po_optv, po->po_optc); | ||
181 | |||
182 | if (po->po_help_hook) | ||
183 | po->po_help_hook (stdout); | ||
184 | |||
185 | if (po->po_bug_address) | ||
186 | /* TRANSLATORS: The placeholder indicates the bug-reporting address | ||
187 | for this package. Please add _another line_ saying | ||
188 | "Report translation bugs to <...>\n" with the address for translation | ||
189 | bugs (typically your translation team's web or email address). */ | ||
190 | printf (_("Report bugs to %s.\n"), po->po_bug_address); | ||
191 | |||
192 | if (po->po_package_name && po->po_package_url) | ||
193 | printf (_("%s home page: <%s>\n"), | ||
194 | po->po_package_name, po->po_package_url); | ||
195 | } | ||
196 | |||
197 | static struct mu_option **option_tab; | ||
198 | |||
199 | static int | ||
200 | cmpidx_short (const void *a, const void *b) | ||
201 | { | ||
202 | unsigned const *ai = (unsigned const *)a; | ||
203 | unsigned const *bi = (unsigned const *)b; | ||
204 | |||
205 | return option_tab[*ai]->opt_short - option_tab[*bi]->opt_short; | ||
206 | } | ||
207 | |||
208 | static int | ||
209 | cmpidx_long (const void *a, const void *b) | ||
210 | { | ||
211 | unsigned const *ai = (unsigned const *)a; | ||
212 | unsigned const *bi = (unsigned const *)b; | ||
213 | struct mu_option const *ap = option_tab[*ai]; | ||
214 | struct mu_option const *bp = option_tab[*bi]; | ||
215 | return strcmp (ap->opt_long, bp->opt_long); | ||
216 | } | ||
217 | |||
218 | void | ||
219 | mu_program_usage (struct mu_parseopt *po) | ||
220 | { | ||
221 | unsigned i; | ||
222 | unsigned n; | ||
223 | char buf[RMARGIN+1]; | ||
224 | unsigned *idxbuf; | ||
225 | unsigned nidx; | ||
226 | |||
227 | struct mu_option **optbuf = po->po_optv; | ||
228 | size_t optcnt = po->po_optc; | ||
229 | |||
230 | #define FLUSH \ | ||
231 | do \ | ||
232 | { \ | ||
233 | buf[n] = 0; \ | ||
234 | printf ("%s\n", buf); \ | ||
235 | n = USAGECOLUMN; \ | ||
236 | memset (buf, ' ', n); \ | ||
237 | } \ | ||
238 | while (0) | ||
239 | #define ADDC(c) \ | ||
240 | do \ | ||
241 | { \ | ||
242 | if (n == RMARGIN) FLUSH; \ | ||
243 | buf[n++] = c; \ | ||
244 | } \ | ||
245 | while (0) | ||
246 | |||
247 | option_tab = optbuf; | ||
248 | |||
249 | idxbuf = mu_calloc (optcnt, sizeof (idxbuf[0])); | ||
250 | |||
251 | n = snprintf (buf, sizeof buf, "%s %s ", _("Usage:"), mu_progname); | ||
252 | |||
253 | /* Print a list of short options without arguments. */ | ||
254 | for (i = nidx = 0; i < optcnt; i++) | ||
255 | if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]) && !optbuf[i]->opt_arg) | ||
256 | idxbuf[nidx++] = i; | ||
257 | |||
258 | if (nidx) | ||
259 | { | ||
260 | qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short); | ||
261 | |||
262 | ADDC ('['); | ||
263 | ADDC ('-'); | ||
264 | for (i = 0; i < nidx; i++) | ||
265 | { | ||
266 | ADDC (optbuf[idxbuf[i]]->opt_short); | ||
267 | } | ||
268 | ADDC (']'); | ||
269 | } | ||
270 | |||
271 | /* Print a list of short options with arguments. */ | ||
272 | for (i = nidx = 0; i < optcnt; i++) | ||
273 | { | ||
274 | if (MU_OPTION_IS_VALID_SHORT_OPTION (optbuf[i]) && optbuf[i]->opt_arg) | ||
275 | idxbuf[nidx++] = i; | ||
276 | } | ||
277 | |||
278 | if (nidx) | ||
279 | { | ||
280 | qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_short); | ||
281 | |||
282 | for (i = 0; i < nidx; i++) | ||
283 | { | ||
284 | struct mu_option *opt = optbuf[idxbuf[i]]; | ||
285 | const char *arg = gettext (opt->opt_arg); | ||
286 | size_t len = 5 + strlen (arg) + 1; | ||
287 | |||
288 | if (n + len > RMARGIN) FLUSH; | ||
289 | buf[n++] = ' '; | ||
290 | buf[n++] = '['; | ||
291 | buf[n++] = '-'; | ||
292 | buf[n++] = opt->opt_short; | ||
293 | buf[n++] = ' '; | ||
294 | strcpy (&buf[n], arg); | ||
295 | n += strlen (arg); | ||
296 | buf[n++] = ']'; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | /* Print a list of long options */ | ||
301 | for (i = nidx = 0; i < optcnt; i++) | ||
302 | { | ||
303 | if (MU_OPTION_IS_VALID_LONG_OPTION (optbuf[i])) | ||
304 | idxbuf[nidx++] = i; | ||
305 | } | ||
306 | |||
307 | if (nidx) | ||
308 | { | ||
309 | qsort (idxbuf, nidx, sizeof (idxbuf[0]), cmpidx_long); | ||
310 | |||
311 | for (i = 0; i < nidx; i++) | ||
312 | { | ||
313 | struct mu_option *opt = optbuf[idxbuf[i]]; | ||
314 | const char *arg = opt->opt_arg ? gettext (opt->opt_arg) : NULL; | ||
315 | size_t len = 3 + strlen (opt->opt_long) | ||
316 | + (arg ? 1 + strlen (arg) : 0); | ||
317 | if (n + len > RMARGIN) FLUSH; | ||
318 | buf[n++] = ' '; | ||
319 | buf[n++] = '['; | ||
320 | buf[n++] = '-'; | ||
321 | buf[n++] = '-'; | ||
322 | strcpy (&buf[n], opt->opt_long); | ||
323 | n += strlen (opt->opt_long); | ||
324 | if (opt->opt_arg) | ||
325 | { | ||
326 | buf[n++] = '='; | ||
327 | strcpy (&buf[n], arg); | ||
328 | n += strlen (arg); | ||
329 | } | ||
330 | buf[n++] = ']'; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | FLUSH; | ||
335 | free (idxbuf); | ||
336 | } | ||
337 |
libmailutils/opt/opt.c
0 → 100644
1 | /* opt.c -- general-purpose command line option parser | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 3, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #ifdef HAVE_CONFIG_H | ||
18 | # include <config.h> | ||
19 | #endif | ||
20 | #include <stdlib.h> | ||
21 | #include <stdarg.h> | ||
22 | #include <string.h> | ||
23 | #include <mailutils/alloc.h> | ||
24 | #include <mailutils/opt.h> | ||
25 | #include <mailutils/nls.h> | ||
26 | #include <mailutils/errno.h> | ||
27 | |||
28 | #define EXIT_SUCCESS 0 | ||
29 | #define EXIT_ERROR 1 | ||
30 | |||
31 | /* Compare pointers to two option structs */ | ||
32 | static int | ||
33 | optcmp (const void *a, const void *b) | ||
34 | { | ||
35 | struct mu_option const *ap = *(struct mu_option const **)a; | ||
36 | struct mu_option const *bp = *(struct mu_option const **)b; | ||
37 | |||
38 | while (ap->opt_flags & MU_OPTION_ALIAS) | ||
39 | ap--; | ||
40 | while (bp->opt_flags & MU_OPTION_ALIAS) | ||
41 | bp--; | ||
42 | |||
43 | if (MU_OPTION_IS_VALID_SHORT_OPTION (ap) | ||
44 | && MU_OPTION_IS_VALID_SHORT_OPTION (bp)) | ||
45 | return ap->opt_short - bp->opt_short; | ||
46 | if (MU_OPTION_IS_VALID_LONG_OPTION (ap) | ||
47 | && MU_OPTION_IS_VALID_LONG_OPTION (bp)) | ||
48 | return strcmp (ap->opt_long, bp->opt_long); | ||
49 | if (MU_OPTION_IS_VALID_LONG_OPTION (ap)) | ||
50 | return 1; | ||
51 | return -1; | ||
52 | } | ||
53 | |||
54 | /* Sort a group of options in OPTBUF, starting at index START (first | ||
55 | option slot after a group header (if any). The group spans up to | ||
56 | next group header or end of options */ | ||
57 | static size_t | ||
58 | sort_group (struct mu_option **optbuf, size_t start) | ||
59 | { | ||
60 | size_t i; | ||
61 | |||
62 | for (i = start; optbuf[i] && !MU_OPTION_IS_GROUP_HEADER (optbuf[i]); i++) | ||
63 | ; | ||
64 | |||
65 | qsort (&optbuf[start], i - start, sizeof (optbuf[0]), optcmp); | ||
66 | return i; | ||
67 | } | ||
68 | |||
69 | /* Print help summary and exit. */ | ||
70 | static void | ||
71 | fn_help (struct mu_parseopt *po, struct mu_option *opt, char const *unused) | ||
72 | { | ||
73 | mu_program_help (po); | ||
74 | exit (EXIT_SUCCESS); | ||
75 | } | ||
76 | |||
77 | /* Print usage summary and exit. */ | ||
78 | static void | ||
79 | fn_usage (struct mu_parseopt *po, struct mu_option *opt, char const *unused) | ||
80 | { | ||
81 | mu_program_usage (po); | ||
82 | exit (EXIT_SUCCESS); | ||
83 | } | ||
84 | |||
85 | /* Default options */ | ||
86 | struct mu_option mu_default_options[] = { | ||
87 | MU_OPTION_GROUP(""), | ||
88 | { "help", 'h', NULL, MU_OPTION_IMMEDIATE, N_("give this help list"), | ||
89 | mu_c_string, NULL, fn_help }, | ||
90 | { "version", 'V', NULL, MU_OPTION_IMMEDIATE, N_("print program version"), | ||
91 | mu_c_string, NULL, /* FIXME: fn_version */ }, | ||
92 | { "usage", 0, NULL, MU_OPTION_IMMEDIATE, N_("give a short usage message"), | ||
93 | mu_c_string, NULL, fn_usage | ||
94 | }, | ||
95 | MU_OPTION_END | ||
96 | }; | ||
97 | |||
98 | /* Output error message */ | ||
99 | static void | ||
100 | parse_error (struct mu_parseopt *po, char const *fmt, ...) | ||
101 | { | ||
102 | va_list ap; | ||
103 | |||
104 | if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS) | ||
105 | return; | ||
106 | |||
107 | if (po->po_prog_name) | ||
108 | fprintf (stderr, "%s: ", po->po_prog_name); | ||
109 | va_start (ap, fmt); | ||
110 | vfprintf (stderr, fmt, ap); | ||
111 | va_end (ap); | ||
112 | fputc ('\n', stderr); | ||
113 | } | ||
114 | |||
115 | static void | ||
116 | mu_option_cache_destroy (void *ptr) | ||
117 | { | ||
118 | struct mu_option_cache *cache = ptr; | ||
119 | free (cache->cache_arg); | ||
120 | free (cache); | ||
121 | } | ||
122 | |||
123 | static int parseopt_apply (void *item, void *data); | ||
124 | |||
125 | /* If OPT is an immediate option, evaluate it right away. Otherwise, | ||
126 | add option OPT with argument ARG to the cache in PO. */ | ||
127 | void | ||
128 | add_option_cache (struct mu_parseopt *po, struct mu_option *opt, | ||
129 | char const *arg) | ||
130 | { | ||
131 | struct mu_option_cache *cache = mu_alloc (sizeof (*cache)); | ||
132 | cache->cache_opt = opt; | ||
133 | cache->cache_arg = arg ? mu_strdup (arg) : NULL; | ||
134 | |||
135 | if (opt->opt_flags & MU_OPTION_IMMEDIATE) | ||
136 | { | ||
137 | parseopt_apply (cache, po); | ||
138 | mu_option_cache_destroy (cache); | ||
139 | } | ||
140 | else | ||
141 | { | ||
142 | mu_list_append (po->po_optlist, cache); | ||
143 | } | ||
144 | } | ||
145 | |||
146 | /* Find first option for which I is an alias */ | ||
147 | struct mu_option * | ||
148 | option_unalias (struct mu_parseopt *po, int i) | ||
149 | { | ||
150 | while (i > 0 && po->po_optv[i]->opt_flags & MU_OPTION_ALIAS) | ||
151 | --i; | ||
152 | return po->po_optv[i]; | ||
153 | } | ||
154 | |||
155 | /* Find a descriptor of short option CHR */ | ||
156 | struct mu_option * | ||
157 | find_short_option (struct mu_parseopt *po, int chr) | ||
158 | { | ||
159 | size_t i; | ||
160 | |||
161 | for (i = 0; i < po->po_optc; i++) | ||
162 | { | ||
163 | if (MU_OPTION_IS_VALID_SHORT_OPTION (po->po_optv[i]) | ||
164 | && po->po_optv[i]->opt_short == chr) | ||
165 | return option_unalias (po, i); | ||
166 | } | ||
167 | parse_error (po, _("unrecognized option '-%c'"), chr); | ||
168 | return NULL; | ||
169 | } | ||
170 | |||
171 | /* Find a descriptor of long option OPTSTR. If it has argument, return | ||
172 | it in *ARGPTR. */ | ||
173 | struct mu_option * | ||
174 | find_long_option (struct mu_parseopt *po, char const *optstr, | ||
175 | char **argptr) | ||
176 | { | ||
177 | size_t i; | ||
178 | size_t optlen; | ||
179 | size_t ind; | ||
180 | int found = 0; | ||
181 | |||
182 | optlen = strcspn (optstr, "="); | ||
183 | |||
184 | for (i = 0; i < po->po_optc; i++) | ||
185 | { | ||
186 | if (MU_OPTION_IS_VALID_LONG_OPTION (po->po_optv[i]) | ||
187 | && optlen <= strlen (po->po_optv[i]->opt_long) | ||
188 | && memcmp (po->po_optv[i]->opt_long, optstr, optlen) == 0) | ||
189 | { | ||
190 | switch (found) | ||
191 | { | ||
192 | case 0: | ||
193 | ind = i; | ||
194 | found++; | ||
195 | break; | ||
196 | |||
197 | case 1: | ||
198 | if (po->po_flags & MU_PARSEOPT_IGNORE_ERRORS) | ||
199 | return NULL; | ||
200 | parse_error (po, | ||
201 | _("option '--%*.*s' is ambiguous; possibilities:"), | ||
202 | optlen, optlen, optstr); | ||
203 | fprintf (stderr, "--%s\n", po->po_optv[ind]->opt_long); | ||
204 | found++; | ||
205 | |||
206 | case 2: | ||
207 | fprintf (stderr, "--%s\n", po->po_optv[i]->opt_long); | ||
208 | } | ||
209 | } | ||
210 | } | ||
211 | |||
212 | switch (found) | ||
213 | { | ||
214 | case 0: | ||
215 | parse_error (po, _("unrecognized option '--%s'"), optstr); | ||
216 | break; | ||
217 | |||
218 | case 1: | ||
219 | if (optstr[optlen]) | ||
220 | ++optlen; | ||
221 | *argptr = (char *)(optstr + optlen); | ||
222 | return option_unalias (po, ind); | ||
223 | |||
224 | case 2: | ||
225 | break; | ||
226 | } | ||
227 | |||
228 | return NULL; | ||
229 | } | ||
230 | |||
231 | |||
232 | /* Consume next option from PO. On success, update PO members as | ||
233 | described below and return 0. On end of options, return 1. | ||
234 | |||
235 | If the consumed option is a short option, then | ||
236 | |||
237 | po_chr keeps its option character, and | ||
238 | po_cur points to the next option character to be processed | ||
239 | |||
240 | Otherwise, if the consumed option is a long one, then | ||
241 | |||
242 | po_chr is 0 | ||
243 | po_cur points to the first character after -- | ||
244 | */ | ||
245 | static int | ||
246 | next_opt (struct mu_parseopt *po) | ||
247 | { | ||
248 | if (!*po->po_cur) | ||
249 | { | ||
250 | if (!(po->po_flags & MU_PARSEOPT_IN_ORDER) && po->po_arg_count) | ||
251 | { | ||
252 | /* Array to save arguments in */ | ||
253 | char *save[2]; | ||
254 | /* Number of arguments processed (at most two) */ | ||
255 | int n = po->po_ind - (po->po_arg_start + po->po_arg_count); | ||
256 | |||
257 | if (n > 2) | ||
258 | abort (); | ||
259 | |||
260 | /* Store the processed elements away */ | ||
261 | save[0] = po->po_argv[po->po_arg_start + po->po_arg_count]; | ||
262 | if (n == 2) | ||
263 | save[1] = po->po_argv[po->po_arg_start + po->po_arg_count + 1]; | ||
264 | |||
265 | /* Shift the array */ | ||
266 | memmove (po->po_argv + po->po_arg_start + n, | ||
267 | po->po_argv + po->po_arg_start, | ||
268 | po->po_arg_count * sizeof (po->po_argv[0])); | ||
269 | |||
270 | /* Place stored elements in the vacating slots */ | ||
271 | po->po_argv[po->po_arg_start] = save[0]; | ||
272 | if (n == 2) | ||
273 | po->po_argv[po->po_arg_start + 1] = save[1]; | ||
274 | |||
275 | /* Fix up start index */ | ||
276 | po->po_arg_start += n; | ||
277 | } | ||
278 | |||
279 | if (po->po_ind == po->po_argc) | ||
280 | return 1; | ||
281 | |||
282 | while (1) | ||
283 | { | ||
284 | po->po_cur = po->po_argv[po->po_ind++]; | ||
285 | if (!po->po_cur) | ||
286 | return 1; | ||
287 | if (po->po_cur[0] == '-' && po->po_cur[1]) | ||
288 | break; | ||
289 | if (!(po->po_flags & MU_PARSEOPT_IN_ORDER)) | ||
290 | { | ||
291 | po->po_arg_count++; | ||
292 | continue; | ||
293 | } | ||
294 | else | ||
295 | return 1; | ||
296 | } | ||
297 | |||
298 | if (*++po->po_cur == '-') | ||
299 | { | ||
300 | if (*++po->po_cur == 0) | ||
301 | /* End of options */ | ||
302 | return 1; | ||
303 | |||
304 | /* It's a long option */ | ||
305 | po->po_chr = 0; | ||
306 | return 0; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | po->po_chr = *po->po_cur++; | ||
311 | |||
312 | return 0; | ||
313 | } | ||
314 | |||
315 | /* Parse options */ | ||
316 | static int | ||
317 | parse (struct mu_parseopt *po) | ||
318 | { | ||
319 | int rc; | ||
320 | |||
321 | rc = mu_list_create (&po->po_optlist); | ||
322 | if (rc) | ||
323 | return rc; | ||
324 | mu_list_set_destroy_item (po->po_optlist, mu_option_cache_destroy); | ||
325 | |||
326 | po->po_ind = 0; | ||
327 | if (!(po->po_flags & MU_PARSEOPT_ARGV0)) | ||
328 | { | ||
329 | po->po_ind++; | ||
330 | if (!(po->po_flags & MU_PARSEOPT_PROG_NAME)) | ||
331 | { | ||
332 | char *p = strrchr (po->po_argv[0], '/'); | ||
333 | if (p) | ||
334 | po->po_prog_name = p + 1; | ||
335 | else | ||
336 | po->po_prog_name = (char*) po->po_argv[0]; | ||
337 | } | ||
338 | } | ||
339 | else if (!(po->po_flags & MU_PARSEOPT_PROG_NAME)) | ||
340 | po->po_prog_name = NULL; | ||
341 | |||
342 | po->po_arg_start = po->po_ind; | ||
343 | po->po_arg_count = 0; | ||
344 | |||
345 | po->po_cur = ""; | ||
346 | |||
347 | po->po_opterr = -1; | ||
348 | |||
349 | while (next_opt (po) == 0) | ||
350 | { | ||
351 | struct mu_option *opt; | ||
352 | char *long_opt; | ||
353 | |||
354 | if (po->po_chr) | ||
355 | { | ||
356 | opt = find_short_option (po, po->po_chr); | ||
357 | long_opt = NULL; | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | long_opt = po->po_cur; | ||
362 | opt = find_long_option (po, long_opt, &po->po_cur); | ||
363 | } | ||
364 | |||
365 | if (opt) | ||
366 | { | ||
367 | char *arg = NULL; | ||
368 | |||
369 | if (opt->opt_arg) | ||
370 | { | ||
371 | if (po->po_cur[0]) | ||
372 | { | ||
373 | arg = po->po_cur; | ||
374 | po->po_cur = ""; | ||
375 | } | ||
376 | else if (opt->opt_flags & MU_OPTION_ARG_OPTIONAL) | ||
377 | /* ignore it */; | ||
378 | else if (po->po_ind < po->po_argc) | ||
379 | arg = po->po_argv[po->po_ind++]; | ||
380 | else | ||
381 | { | ||
382 | if (long_opt) | ||
383 | parse_error (po, | ||
384 | _("option '--%s' requires an argument"), | ||
385 | long_opt); | ||
386 | else | ||
387 | parse_error (po, | ||
388 | _("option '-%c' requires an argument"), | ||
389 | po->po_chr); | ||
390 | po->po_opterr = po->po_ind; | ||
391 | if (po->po_flags & MU_PARSEOPT_NO_ERREXIT) | ||
392 | { | ||
393 | if (!(po->po_flags & MU_PARSEOPT_IN_ORDER)) | ||
394 | po->po_arg_count++; | ||
395 | continue; | ||
396 | } | ||
397 | exit (EXIT_ERROR); | ||
398 | } | ||
399 | } | ||
400 | else | ||
401 | { | ||
402 | if (long_opt | ||
403 | && po->po_cur[0] | ||
404 | && !(po->po_flags & MU_OPTION_ARG_OPTIONAL)) | ||
405 | { | ||
406 | parse_error (po, | ||
407 | _("option '--%s' doesn't allow an argument"), | ||
408 | long_opt); | ||
409 | po->po_opterr = po->po_ind; | ||
410 | if (po->po_flags & MU_PARSEOPT_NO_ERREXIT) | ||
411 | { | ||
412 | if (!(po->po_flags & MU_PARSEOPT_IN_ORDER)) | ||
413 | po->po_arg_count++; | ||
414 | continue; | ||
415 | } | ||
416 | exit (EXIT_ERROR); | ||
417 | } | ||
418 | arg = NULL; | ||
419 | } | ||
420 | |||
421 | add_option_cache (po, opt, arg); | ||
422 | } | ||
423 | else | ||
424 | { | ||
425 | po->po_opterr = po->po_ind; | ||
426 | if (po->po_flags & MU_PARSEOPT_NO_ERREXIT) | ||
427 | { | ||
428 | if (!(po->po_flags & MU_PARSEOPT_IN_ORDER)) | ||
429 | po->po_arg_count++; | ||
430 | continue; | ||
431 | } | ||
432 | exit (EXIT_ERROR); | ||
433 | } | ||
434 | } | ||
435 | |||
436 | if (po->po_arg_count) | ||
437 | po->po_ind = po->po_arg_start; | ||
438 | return 0; | ||
439 | } | ||
440 | |||
441 | /* Initialize structure mu_parseopt with given options and flags. */ | ||
442 | static int | ||
443 | parseopt_init (struct mu_parseopt *po, struct mu_option **options, | ||
444 | int flags) | ||
445 | { | ||
446 | struct mu_option *opt; | ||
447 | size_t i, j; | ||
448 | |||
449 | memset (po, 0, sizeof *po); | ||
450 | po->po_argc = 0; | ||
451 | po->po_argv = NULL; | ||
452 | po->po_optc = 0; | ||
453 | po->po_flags = flags; | ||
454 | |||
455 | /* Fix up flags */ | ||
456 | if (flags & MU_PARSEOPT_IGNORE_ERRORS) | ||
457 | flags |= MU_PARSEOPT_NO_ERREXIT; | ||
458 | |||
459 | if (!(flags & MU_PARSEOPT_PROG_DOC)) | ||
460 | po->po_prog_doc = NULL; | ||
461 | if (!(flags & MU_PARSEOPT_PROG_ARGS)) | ||
462 | po->po_prog_args = NULL; | ||
463 | if (!(flags & MU_PARSEOPT_BUG_ADDRESS)) | ||
464 | po->po_bug_address = NULL; | ||
465 | if (!(flags & MU_PARSEOPT_PACKAGE_NAME)) | ||
466 | po->po_package_name = NULL; | ||
467 | if (!(flags & MU_PARSEOPT_PACKAGE_URL)) | ||
468 | po->po_package_url = NULL; | ||
469 | if (!(flags & MU_PARSEOPT_PACKAGE_URL)) | ||
470 | po->po_data = NULL; | ||
471 | if (!(flags & MU_PARSEOPT_HELP_HOOK)) | ||
472 | po->po_help_hook = NULL; | ||
473 | |||
474 | /* Count the options */ | ||
475 | po->po_optc = 0; | ||
476 | for (i = 0; options[i]; i++) | ||
477 | for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++) | ||
478 | ++po->po_optc; | ||
479 | |||
480 | if (!(flags & MU_PARSEOPT_NO_STDOPT)) | ||
481 | for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++) | ||
482 | ++po->po_optc; | ||
483 | |||
484 | /* Allocate the working buffer of option pointers */ | ||
485 | po->po_optv = mu_calloc (po->po_optc + 1, sizeof (*po->po_optv)); | ||
486 | if (!po->po_optv) | ||
487 | return -1; | ||
488 | |||
489 | /* Fill in the array */ | ||
490 | j = 0; | ||
491 | for (i = 0; options[i]; i++) | ||
492 | for (opt = options[i]; !MU_OPTION_IS_END (opt); opt++, j++) | ||
493 | { | ||
494 | if (!opt->opt_set) | ||
495 | opt->opt_set = mu_option_set_value; | ||
496 | po->po_optv[j] = opt; | ||
497 | } | ||
498 | |||
499 | if (!(flags & MU_PARSEOPT_NO_STDOPT)) | ||
500 | for (i = 0; !MU_OPTION_IS_END (&mu_default_options[i]); i++, j++) | ||
501 | po->po_optv[j] = &mu_default_options[i]; | ||
502 | |||
503 | po->po_optv[j] = NULL; | ||
504 | |||
505 | /* Ensure sane start of options. This is necessary, in particular, | ||
506 | because optcmp backs up until it finds an element with cleared | ||
507 | MU_OPTION_ALIAS bit. */ | ||
508 | po->po_optv[0]->opt_flags &= MU_OPTION_ALIAS; | ||
509 | if (!(flags & MU_PARSEOPT_NO_SORT)) | ||
510 | { | ||
511 | /* Sort the options */ | ||
512 | size_t start; | ||
513 | |||
514 | for (start = 0; start < po->po_optc; ) | ||
515 | { | ||
516 | if (MU_OPTION_IS_GROUP_HEADER (po->po_optv[start])) | ||
517 | start = sort_group (po->po_optv, start + 1); | ||
518 | else | ||
519 | start = sort_group (po->po_optv, start); | ||
520 | } | ||
521 | } | ||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | /* Parse command line from ARGC/ARGV. Valid options are given in | ||
526 | OPTIONS. FLAGS control the parsing. */ | ||
527 | int | ||
528 | mu_parseopt (struct mu_parseopt *po, | ||
529 | int argc, char **argv, struct mu_option **options, | ||
530 | int flags) | ||
531 | { | ||
532 | int rc; | ||
533 | |||
534 | if (flags & MU_PARSEOPT_REUSE) | ||
535 | { | ||
536 | mu_list_clear (po->po_optlist); | ||
537 | po->po_flags = (po->po_flags & MU_PARSEOPT_IMMUTABLE_MASK) | ||
538 | | (flags & ~MU_PARSEOPT_IMMUTABLE_MASK); | ||
539 | } | ||
540 | else | ||
541 | { | ||
542 | rc = parseopt_init (po, options, flags); | ||
543 | if (rc) | ||
544 | return rc; | ||
545 | } | ||
546 | po->po_argc = argc; | ||
547 | po->po_argv = argv; | ||
548 | |||
549 | rc = parse (po); | ||
550 | |||
551 | if (rc == 0) | ||
552 | { | ||
553 | if (po->po_opterr >= 0) | ||
554 | rc = -1; | ||
555 | else | ||
556 | { | ||
557 | if (po->po_flags & MU_PARSEOPT_IMMEDIATE) | ||
558 | rc = mu_parseopt_apply (po); | ||
559 | } | ||
560 | } | ||
561 | |||
562 | return rc; | ||
563 | } | ||
564 | |||
565 | void | ||
566 | mu_parseopt_free (struct mu_parseopt *popt) | ||
567 | { | ||
568 | free (popt->po_optv); | ||
569 | mu_list_destroy (&popt->po_optlist); | ||
570 | } | ||
571 | |||
572 | static int | ||
573 | parseopt_apply (void *item, void *data) | ||
574 | { | ||
575 | struct mu_option_cache *cp = item; | ||
576 | struct mu_parseopt *popt = data; | ||
577 | cp->cache_opt->opt_set (popt, cp->cache_opt, cp->cache_arg); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | int | ||
582 | mu_parseopt_apply (struct mu_parseopt *popt) | ||
583 | { | ||
584 | return mu_list_foreach (popt->po_optlist, parseopt_apply, popt); | ||
585 | } | ||
586 | |||
587 | void | ||
588 | mu_option_set_value (struct mu_parseopt *po, struct mu_option *opt, | ||
589 | char const *arg) | ||
590 | { | ||
591 | if (opt->opt_ptr) | ||
592 | { | ||
593 | char *errmsg; | ||
594 | int rc; | ||
595 | |||
596 | if (arg == NULL) | ||
597 | { | ||
598 | if (opt->opt_arg == NULL) | ||
599 | arg = "1"; | ||
600 | else | ||
601 | { | ||
602 | *(void**)opt->opt_ptr = NULL; | ||
603 | return; | ||
604 | } | ||
605 | } | ||
606 | rc = mu_str_to_c (arg, opt->opt_type, opt->opt_ptr, &errmsg); | ||
607 | if (rc) | ||
608 | { | ||
609 | char const *errtext; | ||
610 | if (errmsg) | ||
611 | errtext = errmsg; | ||
612 | else | ||
613 | errtext = mu_strerror (rc); | ||
614 | |||
615 | if (opt->opt_long) | ||
616 | parse_error (po, "--%s: %s", opt->opt_long, errtext); | ||
617 | else | ||
618 | parse_error (po, "-%c: %s", opt->opt_short, errtext); | ||
619 | free (errmsg); | ||
620 | |||
621 | if (!(po->po_flags & MU_PARSEOPT_NO_ERREXIT)) | ||
622 | exit (EXIT_ERROR); | ||
623 | } | ||
624 | } | ||
625 | } |
libmailutils/opt/progname.c
0 → 100644
1 | /* help.c -- general-purpose command line option parser | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU General Public License as | ||
6 | published by the Free Software Foundation; either version 3, or (at | ||
7 | your option) any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | */ | ||
17 | #ifdef HAVE_CONFIG_H | ||
18 | # include <config.h> | ||
19 | #endif | ||
20 | #include <stdlib.h> | ||
21 | #include <string.h> | ||
22 | #include <mailutils/alloc.h> | ||
23 | #include <mailutils/opt.h> | ||
24 | |||
25 | char *mu_progname; | ||
26 | char *mu_absprogname; | ||
27 | |||
28 | void | ||
29 | mu_set_progname (char const *arg) | ||
30 | { | ||
31 | char *p; | ||
32 | |||
33 | free (mu_absprogname); | ||
34 | mu_absprogname = mu_strdup (arg); | ||
35 | |||
36 | p = strrchr (arg, '/'); | ||
37 | if (p) | ||
38 | ++p; | ||
39 | else | ||
40 | p = (char*) arg; | ||
41 | free (mu_progname); | ||
42 | mu_progname = mu_strdup (p); | ||
43 | } |
... | @@ -25,6 +25,7 @@ libstring_la_SOURCES = \ | ... | @@ -25,6 +25,7 @@ libstring_la_SOURCES = \ |
25 | cstrupper.c\ | 25 | cstrupper.c\ |
26 | hexstr.c\ | 26 | hexstr.c\ |
27 | stpcpy.c\ | 27 | stpcpy.c\ |
28 | str_to_c.c\ | ||
28 | strltrim.c\ | 29 | strltrim.c\ |
29 | strskip.c\ | 30 | strskip.c\ |
30 | stripws.c\ | 31 | stripws.c\ |
... | @@ -40,4 +41,6 @@ libstring_la_SOURCES = \ | ... | @@ -40,4 +41,6 @@ libstring_la_SOURCES = \ |
40 | wordsplit.c\ | 41 | wordsplit.c\ |
41 | xdecode.c | 42 | xdecode.c |
42 | 43 | ||
43 | AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils | 44 | EXTRA_DIST=to_sn.c to_un.c |
45 | |||
46 | AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils -I${srcdir} | ... | ... |
libmailutils/string/str_to_c.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 3 of the License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General | ||
15 | Public License along with this library. If not, see | ||
16 | <http://www.gnu.org/licenses/>. */ | ||
17 | |||
18 | #if HAVE_CONFIG_H | ||
19 | # include <config.h> | ||
20 | #endif | ||
21 | |||
22 | #include <stdlib.h> | ||
23 | #include <inttypes.h> | ||
24 | #include <limits.h> | ||
25 | #include <string.h> | ||
26 | #include <mailutils/cidr.h> | ||
27 | #include <mailutils/error.h> | ||
28 | #include <mailutils/errno.h> | ||
29 | #include <mailutils/util.h> | ||
30 | #include <mailutils/alloc.h> | ||
31 | #include <mailutils/cctype.h> | ||
32 | #include <mailutils/io.h> | ||
33 | #include <mailutils/nls.h> | ||
34 | |||
35 | typedef int (*str_to_c_t) (void *tgt, char const *string, char **errmsg); | ||
36 | |||
37 | static int | ||
38 | to_string (void *tgt, char const *string, char **errmsg) | ||
39 | { | ||
40 | char **cptr = tgt; | ||
41 | if (string) | ||
42 | { | ||
43 | *cptr = mu_strdup (string); | ||
44 | if (!*cptr) | ||
45 | return errno; | ||
46 | } | ||
47 | else | ||
48 | *cptr = NULL; | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | #define STR_TO_FUN to_short | ||
53 | #define STR_TO_TYPE signed short | ||
54 | #define STR_TO_MIN SHRT_MIN | ||
55 | #define STR_TO_MAX SHRT_MAX | ||
56 | #include "to_sn.c" | ||
57 | |||
58 | #define STR_TO_FUN to_ushort | ||
59 | #define STR_TO_TYPE unsigned short | ||
60 | #define STR_TO_MAX USHRT_MAX | ||
61 | #include "to_un.c" | ||
62 | |||
63 | #define STR_TO_FUN to_int | ||
64 | #define STR_TO_TYPE signed int | ||
65 | #define STR_TO_MIN INT_MIN | ||
66 | #define STR_TO_MAX INT_MAX | ||
67 | #include "to_sn.c" | ||
68 | |||
69 | #define STR_TO_FUN to_uint | ||
70 | #define STR_TO_TYPE unsigned int | ||
71 | #define STR_TO_MAX UINT_MAX | ||
72 | #include "to_un.c" | ||
73 | |||
74 | #define STR_TO_FUN to_long | ||
75 | #define STR_TO_TYPE signed long | ||
76 | #define STR_TO_MIN LONG_MIN | ||
77 | #define STR_TO_MAX LONG_MAX | ||
78 | #include "to_sn.c" | ||
79 | |||
80 | #define STR_TO_FUN to_ulong | ||
81 | #define STR_TO_TYPE unsigned long | ||
82 | #define STR_TO_MAX ULONG_MAX | ||
83 | #include "to_un.c" | ||
84 | |||
85 | #define STR_TO_FUN to_size_t | ||
86 | #define STR_TO_TYPE size_t | ||
87 | #define STR_TO_MAX ((size_t)-1) | ||
88 | #include "to_un.c" | ||
89 | |||
90 | static int | ||
91 | time_multiplier (const char *str, unsigned *m, unsigned *plen) | ||
92 | { | ||
93 | static struct timetab | ||
94 | { | ||
95 | char *name; | ||
96 | unsigned mul; | ||
97 | } tab[] = { | ||
98 | { "seconds", 1 }, | ||
99 | { "minutes", 60 }, | ||
100 | { "hours", 60*60 }, | ||
101 | { "days", 24*60*60 }, | ||
102 | { "weeks", 7*24*60*60 }, | ||
103 | { "months", 31*7*24*60*60 }, | ||
104 | { NULL } | ||
105 | }; | ||
106 | struct timetab *p; | ||
107 | int slen; | ||
108 | |||
109 | for (slen = 0; str[slen]; slen++) | ||
110 | if (mu_isspace (str[slen])) | ||
111 | break; | ||
112 | |||
113 | for (p = tab; p->name; p++) | ||
114 | { | ||
115 | if (p->name[0] == mu_tolower (str[0])) | ||
116 | { | ||
117 | int nlen = strlen (p->name); | ||
118 | |||
119 | if (nlen > slen) | ||
120 | nlen = slen; | ||
121 | |||
122 | if (strncasecmp (p->name, str, nlen) == 0) { | ||
123 | *m = p->mul; | ||
124 | if (plen) | ||
125 | *plen = nlen; | ||
126 | return 0; | ||
127 | } | ||
128 | } | ||
129 | } | ||
130 | return 1; | ||
131 | } | ||
132 | |||
133 | static int | ||
134 | to_time_t (void *tgt, char const *string, char **errmsg) | ||
135 | { | ||
136 | time_t *ptr = tgt; | ||
137 | int rc = 0; | ||
138 | time_t interval = 0; | ||
139 | |||
140 | while (*string) | ||
141 | { | ||
142 | char *p; | ||
143 | unsigned long n; | ||
144 | unsigned mul, len; | ||
145 | |||
146 | while (*string && mu_isspace (*string)) | ||
147 | string++; | ||
148 | |||
149 | if (!mu_isdigit (*string) && time_multiplier (string, &mul, &len) == 0) | ||
150 | { | ||
151 | n = 1; | ||
152 | string += len; | ||
153 | } | ||
154 | else | ||
155 | { | ||
156 | n = strtoul (string, &p, 10); | ||
157 | if (*p && !mu_isspace (*p)) | ||
158 | { | ||
159 | string = p; | ||
160 | rc = 1; | ||
161 | break; | ||
162 | } | ||
163 | |||
164 | while (*p && mu_isspace (*p)) | ||
165 | p++; | ||
166 | |||
167 | string = p; | ||
168 | if (*string) | ||
169 | { | ||
170 | if ((rc = time_multiplier (string, &mul, &len))) | ||
171 | break; | ||
172 | string += len; | ||
173 | } | ||
174 | else | ||
175 | mul = 1; | ||
176 | } | ||
177 | interval += n * mul; | ||
178 | } | ||
179 | |||
180 | if (rc) | ||
181 | { | ||
182 | mu_asprintf (errmsg, _("invalid time specification near %s"), string); | ||
183 | return EINVAL; | ||
184 | } | ||
185 | |||
186 | *ptr = interval; | ||
187 | return 0; | ||
188 | } | ||
189 | |||
190 | static int | ||
191 | to_bool (void *tgt, char const *string, char **errmsg) | ||
192 | { | ||
193 | int *ptr = tgt; | ||
194 | |||
195 | if (strcmp (string, "yes") == 0 | ||
196 | || strcmp (string, "on") == 0 | ||
197 | || strcmp (string, "t") == 0 | ||
198 | || strcmp (string, "true") == 0 | ||
199 | || strcmp (string, "1") == 0) | ||
200 | *ptr = 1; | ||
201 | else if (strcmp (string, "no") == 0 | ||
202 | || strcmp (string, "off") == 0 | ||
203 | || strcmp (string, "nil") == 0 | ||
204 | || strcmp (string, "false") == 0 | ||
205 | || strcmp (string, "0") == 0) | ||
206 | *ptr = 0; | ||
207 | else | ||
208 | return EINVAL; | ||
209 | |||
210 | return 0; | ||
211 | } | ||
212 | |||
213 | #if 0 | ||
214 | static int | ||
215 | to_ipv4 (void *tgt, char const *string, char **errmsg) | ||
216 | { | ||
217 | struct in_addr *ptr = tgt; | ||
218 | struct in_addr addr; | ||
219 | |||
220 | if (inet_aton (string, &addr) == 0) | ||
221 | { | ||
222 | mu_diag_at_locus (MU_LOG_ERROR, &mu_cfg_locus, _("not an IPv4")); | ||
223 | mu_cfg_error_count++; | ||
224 | return 1; | ||
225 | } | ||
226 | addr.s_addr = ntohl (addr.s_addr); | ||
227 | |||
228 | *ptr = addr; | ||
229 | return 0; | ||
230 | } | ||
231 | #endif | ||
232 | |||
233 | static int | ||
234 | to_cidr (void *tgt, char const *string, char **errmsg) | ||
235 | { | ||
236 | struct mu_cidr *ptr = tgt; | ||
237 | return mu_cidr_from_string (ptr, string); | ||
238 | } | ||
239 | |||
240 | static int | ||
241 | to_incr (void *tgt, char const *string, char **errmsg) | ||
242 | { | ||
243 | ++*(int*)tgt; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static str_to_c_t str_to_c[] = { | ||
248 | [mu_c_string] = to_string, | ||
249 | [mu_c_short] = to_short, | ||
250 | [mu_c_ushort] = to_ushort, | ||
251 | [mu_c_int] = to_int, | ||
252 | [mu_c_uint] = to_uint, | ||
253 | [mu_c_long] = to_long, | ||
254 | [mu_c_ulong] = to_ulong, | ||
255 | [mu_c_size] = to_size_t, | ||
256 | /* FIXME [mu_c_off] = { to_off, generic_dealloc }, */ | ||
257 | [mu_c_time] = to_time_t, | ||
258 | [mu_c_bool] = to_bool, | ||
259 | /* FIXME [mu_c_ipv4] = to_ipv4, */ | ||
260 | [mu_c_cidr] = to_cidr, | ||
261 | /* FIXME [mu_c_host] = { to_host, generic_dealloc } */ | ||
262 | [mu_c_incr] = to_incr | ||
263 | }; | ||
264 | |||
265 | int | ||
266 | mu_str_to_c (char const *string, enum mu_c_type type, void *tgt, char **errmsg) | ||
267 | { | ||
268 | *errmsg = NULL; | ||
269 | if ((size_t)type >= sizeof (str_to_c) / sizeof (str_to_c[0])) | ||
270 | return EINVAL; | ||
271 | if (!str_to_c[type]) | ||
272 | return ENOSYS; | ||
273 | return str_to_c[type] (tgt, string, errmsg); | ||
274 | } |
libmailutils/string/to_sn.c
0 → 100644
1 | #ifndef STR_TO_FUN | ||
2 | # error "STR_TO_FUN not defined" | ||
3 | #endif | ||
4 | #ifndef STR_TO_TYPE | ||
5 | # error "STR_TO_TYPE not defined" | ||
6 | #endif | ||
7 | #ifndef STR_TO_MIN | ||
8 | # error "STR_TO_MIN not defined" | ||
9 | #endif | ||
10 | #ifndef STR_TO_MAX | ||
11 | # error "STR_TO_MAX not defined" | ||
12 | #endif | ||
13 | |||
14 | static int | ||
15 | STR_TO_FUN (void *tgt, char const *string, char **errmsg) | ||
16 | { | ||
17 | STR_TO_TYPE *ptr = tgt; | ||
18 | intmax_t v; | ||
19 | char *p; | ||
20 | |||
21 | errno = 0; | ||
22 | v = strtoimax (string, &p, 10); | ||
23 | if (errno) | ||
24 | return errno; | ||
25 | if (*p) | ||
26 | return EINVAL; | ||
27 | if (STR_TO_MIN <= v && v <= STR_TO_MAX) | ||
28 | { | ||
29 | *ptr = (STR_TO_TYPE) v; | ||
30 | return 0; | ||
31 | } | ||
32 | return ERANGE; | ||
33 | } | ||
34 | #undef STR_TO_FUN | ||
35 | #undef STR_TO_TYPE | ||
36 | #undef STR_TO_MIN | ||
37 | #undef STR_TO_MAX |
libmailutils/string/to_un.c
0 → 100644
1 | #ifndef STR_TO_FUN | ||
2 | # error "STR_TO_FUN not defined" | ||
3 | #endif | ||
4 | #ifndef STR_TO_TYPE | ||
5 | # error "STR_TO_TYPE not defined" | ||
6 | #endif | ||
7 | #ifndef STR_TO_MAX | ||
8 | # error "STR_TO_MAX not defined" | ||
9 | #endif | ||
10 | |||
11 | static int | ||
12 | STR_TO_FUN (void *tgt, char const *string, char **errmsg) | ||
13 | { | ||
14 | STR_TO_TYPE *ptr = tgt; | ||
15 | uintmax_t v; | ||
16 | char *p; | ||
17 | |||
18 | errno = 0; | ||
19 | v = strtoumax (string, &p, 10); | ||
20 | if (errno) | ||
21 | return errno; | ||
22 | if (*p) | ||
23 | return EINVAL; | ||
24 | if (v <= STR_TO_MAX) | ||
25 | { | ||
26 | *ptr = (STR_TO_TYPE) v; | ||
27 | return 0; | ||
28 | } | ||
29 | return ERANGE; | ||
30 | } | ||
31 | |||
32 | #undef STR_TO_FUN | ||
33 | #undef STR_TO_TYPE | ||
34 | #undef STR_TO_MAX |
libmailutils/tests/parseopt.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 2016 Free Software Foundation, Inc. | ||
3 | |||
4 | GNU Mailutils is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 3, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | GNU Mailutils is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #ifdef HAVE_CONFIG_H | ||
18 | # include <config.h> | ||
19 | #endif | ||
20 | #include <mailutils/mailutils.h> | ||
21 | #include <mailutils/opt.h> | ||
22 | |||
23 | char *file_name; | ||
24 | char *find_value; | ||
25 | char *opt_value = "initial"; | ||
26 | int jobs = 0; | ||
27 | int x_option; | ||
28 | int a_option; | ||
29 | int d_option; | ||
30 | |||
31 | struct mu_option group_a[] = { | ||
32 | MU_OPTION_GROUP("Group A"), | ||
33 | { "file", 'f', "FILE", MU_OPTION_DEFAULT, | ||
34 | "set file name", | ||
35 | mu_c_string, &file_name | ||
36 | }, | ||
37 | { "find", 'F', "VALUE", MU_OPTION_DEFAULT, | ||
38 | "find VALUE", | ||
39 | mu_c_string, &find_value }, | ||
40 | { "optional", 'o', "FILE", MU_OPTION_ARG_OPTIONAL, | ||
41 | "optional argument", | ||
42 | mu_c_string, &opt_value }, | ||
43 | { NULL, 'x', NULL, MU_OPTION_DEFAULT, | ||
44 | "short-only option", | ||
45 | mu_c_incr, &x_option }, | ||
46 | { "all", 'a', NULL, MU_OPTION_DEFAULT, | ||
47 | "no arguments to this one", | ||
48 | mu_c_bool, &a_option }, | ||
49 | MU_OPTION_END | ||
50 | }; | ||
51 | |||
52 | struct mu_option group_b[] = { | ||
53 | MU_OPTION_GROUP("Group B"), | ||
54 | { "debug", 'd', NULL, MU_OPTION_DEFAULT, | ||
55 | "another option", | ||
56 | mu_c_incr, &d_option }, | ||
57 | { "verbose", 'v', NULL, MU_OPTION_ALIAS }, | ||
58 | { "jobs", 'j', "N", MU_OPTION_DEFAULT, | ||
59 | "sets numeric value", | ||
60 | mu_c_int, &jobs }, | ||
61 | MU_OPTION_END | ||
62 | }; | ||
63 | |||
64 | struct mu_option *optv[] = { group_a, group_b, NULL }; | ||
65 | |||
66 | #define S(s) ((s)?(s):"(null)") | ||
67 | |||
68 | int | ||
69 | main (int argc, char *argv[]) | ||
70 | { | ||
71 | struct mu_parseopt po; | ||
72 | int rc; | ||
73 | int i; | ||
74 | |||
75 | mu_stdstream_setup (MU_STDSTREAM_RESET_NONE); | ||
76 | |||
77 | rc = mu_parseopt (&po, argc, argv, optv, MU_PARSEOPT_DEFAULT); | ||
78 | printf ("rc=%d\n", rc); | ||
79 | mu_parseopt_apply (&po); | ||
80 | |||
81 | argc -= po.po_ind; | ||
82 | argv += po.po_ind; | ||
83 | |||
84 | mu_parseopt_free (&po); | ||
85 | |||
86 | printf ("file_name=%s\n", S(file_name)); | ||
87 | printf ("find_value=%s\n", S(find_value)); | ||
88 | printf ("opt_value=%s\n", S(opt_value)); | ||
89 | printf ("x_option=%d\n", x_option); | ||
90 | printf ("a_option=%d\n", a_option); | ||
91 | printf ("d_option=%d\n", d_option); | ||
92 | printf ("jobs=%d\n", jobs); | ||
93 | |||
94 | printf ("argv:\n"); | ||
95 | for (i = 0; i < argc; i++) | ||
96 | { | ||
97 | printf ("%d: %s\n", i, argv[i]); | ||
98 | } | ||
99 | |||
100 | return 0; | ||
101 | } |
-
Please register or sign in to post a comment