1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009 Free Software Foundation, Inc.
4 This program 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 of the License, or
7 (at your option) any later version.
9 This program 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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include <libpspp/argv-parser.h>
23 #include <libpspp/assertion.h>
24 #include <libpspp/str.h>
28 struct argv_option_plus
30 struct argv_option base;
31 void (*cb) (int id, void *aux);
37 struct argv_option_plus *options;
38 size_t n_options, allocated_options;
41 /* Creates and returns a new argv_parser that initially is not
42 configured to parse any command-line options. */
44 argv_parser_create (void)
46 struct argv_parser *ap = xzalloc (sizeof *ap);
52 argv_parser_destroy (struct argv_parser *ap)
61 /* Adds the N options in OPTIONS to AP. When argv_parser_run is
62 later called for AP, each of the options in OPTIONS will be
63 handled by passing the option's 'id' member to CB along with
64 AUX. For an option that has an argument, the 'optarg' global
65 variable will be set to point to it before calling CB;
66 otherwise 'optarg' will be set to NULL. */
68 argv_parser_add_options (struct argv_parser *ap,
69 const struct argv_option *options, size_t n,
70 void (*cb) (int id, void *aux), void *aux)
72 const struct argv_option *src;
73 for (src = options; src < &options[n]; src++)
75 struct argv_option_plus *dst;
77 if (ap->n_options >= ap->allocated_options)
78 ap->options = x2nrealloc (ap->options, &ap->allocated_options,
81 dst = &ap->options[ap->n_options++];
88 /* Parses all ARGC command-line arguments in ARGV according to
89 the options configured in AP with argv_parser_add_options.
90 Returns true if all the command-line arguments were parsed
91 successfully, false if there was an error. Upon failure
92 return, if the external variable 'opterr' is nonzero (which is
93 the default), an error message will also be printed. Upon
94 successful return, external variable 'optind' will be set to
95 the index of the first non-option argument. */
97 argv_parser_run (struct argv_parser *ap, int argc, char **argv)
99 enum { LONGOPT_VAL_BASE = UCHAR_MAX + 1 };
100 const struct argv_option_plus *shortopt_ptrs[UCHAR_MAX + 1];
101 struct string shortopts;
102 struct option *longopts;
107 memset (shortopt_ptrs, 0, sizeof shortopt_ptrs);
108 ds_init_empty (&shortopts);
109 longopts = xmalloc ((ap->n_options + 1) * sizeof *longopts);
111 for (i = 0; i < ap->n_options; i++)
113 const struct argv_option_plus *aop = &ap->options[i];
115 if (aop->base.long_name != NULL)
117 struct option *o = &longopts[n_longopts++];
118 o->name = aop->base.long_name;
119 o->has_arg = aop->base.has_arg;
121 o->val = i + LONGOPT_VAL_BASE;
124 if (aop->base.short_name != 0)
126 unsigned char c = aop->base.short_name;
127 if (shortopt_ptrs[c] == NULL)
129 shortopt_ptrs[c] = aop;
130 ds_put_char (&shortopts, aop->base.short_name);
131 if (aop->base.has_arg != no_argument)
132 ds_put_char (&shortopts, ':');
133 if (aop->base.has_arg == optional_argument)
134 ds_put_char (&shortopts, ':');
139 fprintf (stderr, "option -%c multiply defined",
140 aop->base.short_name);
146 memset (&longopts[n_longopts], 0, sizeof *longopts);
151 int c = getopt_long (argc, argv, ds_cstr (&shortopts),
152 longopts, &indexptr);
164 else if (c >= LONGOPT_VAL_BASE && c < LONGOPT_VAL_BASE + n_longopts)
166 struct argv_option_plus *aop = &ap->options[c - LONGOPT_VAL_BASE];
167 aop->cb (aop->base.id, aop->aux);
169 else if (c >= SCHAR_MIN && c <= UCHAR_MAX)
171 const struct argv_option_plus *aop = shortopt_ptrs[(unsigned char) c];
172 aop->cb (aop->base.id, aop->aux);
179 ds_destroy (&shortopts);