--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <libpspp/argv-parser.h>
+
+#include <limits.h>
+
+#include <libpspp/assertion.h>
+#include <libpspp/str.h>
+
+#include "xalloc.h"
+
+struct argv_option_plus
+ {
+ struct argv_option base;
+ void (*cb) (int id, void *aux);
+ void *aux;
+ };
+
+struct argv_parser
+ {
+ struct argv_option_plus *options;
+ size_t n_options, allocated_options;
+ };
+
+/* Creates and returns a new argv_parser that initially is not
+ configured to parse any command-line options. */
+struct argv_parser *
+argv_parser_create (void)
+{
+ struct argv_parser *ap = xzalloc (sizeof *ap);
+ return ap;
+}
+
+/* Destroys AP. */
+void
+argv_parser_destroy (struct argv_parser *ap)
+{
+ if (ap != NULL)
+ {
+ free (ap->options);
+ free (ap);
+ }
+}
+
+/* Adds the N options in OPTIONS to AP. When argv_parser_run is
+ later called for AP, each of the options in OPTIONS will be
+ handled by passing the option's 'id' member to CB along with
+ AUX. For an option that has an argument, the 'optarg' global
+ variable will be set to point to it before calling CB;
+ otherwise 'optarg' will be set to NULL. */
+void
+argv_parser_add_options (struct argv_parser *ap,
+ const struct argv_option *options, size_t n,
+ void (*cb) (int id, void *aux), void *aux)
+{
+ const struct argv_option *src;
+ for (src = options; src < &options[n]; src++)
+ {
+ struct argv_option_plus *dst;
+
+ if (ap->n_options >= ap->allocated_options)
+ ap->options = x2nrealloc (ap->options, &ap->allocated_options,
+ sizeof *ap->options);
+
+ dst = &ap->options[ap->n_options++];
+ dst->base = *src;
+ dst->cb = cb;
+ dst->aux = aux;
+ }
+}
+
+/* Parses all ARGC command-line arguments in ARGV according to
+ the options configured in AP with argv_parser_add_options.
+ Returns true if all the command-line arguments were parsed
+ successfully, false if there was an error. Upon failure
+ return, if the external variable 'opterr' is nonzero (which is
+ the default), an error message will also be printed. Upon
+ successful return, external variable 'optind' will be set to
+ the index of the first non-option argument. */
+bool
+argv_parser_run (struct argv_parser *ap, int argc, char **argv)
+{
+ enum { LONGOPT_VAL_BASE = UCHAR_MAX + 1 };
+ const struct argv_option_plus *shortopt_ptrs[UCHAR_MAX + 1];
+ struct string shortopts;
+ struct option *longopts;
+ size_t n_longopts;
+ bool retval;
+ size_t i;
+
+ memset (shortopt_ptrs, 0, sizeof shortopt_ptrs);
+ ds_init_empty (&shortopts);
+ longopts = xmalloc ((ap->n_options + 1) * sizeof *longopts);
+ n_longopts = 0;
+ for (i = 0; i < ap->n_options; i++)
+ {
+ const struct argv_option_plus *aop = &ap->options[i];
+
+ if (aop->base.long_name != NULL)
+ {
+ struct option *o = &longopts[n_longopts++];
+ o->name = aop->base.long_name;
+ o->has_arg = aop->base.has_arg;
+ o->flag = NULL;
+ o->val = i + LONGOPT_VAL_BASE;
+ }
+
+ if (aop->base.short_name != 0)
+ {
+ unsigned char c = aop->base.short_name;
+ if (shortopt_ptrs[c] == NULL)
+ {
+ shortopt_ptrs[c] = aop;
+ ds_put_char (&shortopts, aop->base.short_name);
+ if (aop->base.has_arg != no_argument)
+ ds_put_char (&shortopts, ':');
+ if (aop->base.has_arg == optional_argument)
+ ds_put_char (&shortopts, ':');
+ }
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "option -%c multiply defined",
+ aop->base.short_name);
+ retval = false;
+ goto exit;
+ }
+ }
+ }
+ memset (&longopts[n_longopts], 0, sizeof *longopts);
+
+ for (;;)
+ {
+ int indexptr;
+ int c = getopt_long (argc, argv, ds_cstr (&shortopts),
+ longopts, &indexptr);
+
+ if (c == -1)
+ {
+ retval = true;
+ break;
+ }
+ else if (c == '?')
+ {
+ retval = false;
+ break;
+ }
+ else if (c >= LONGOPT_VAL_BASE && c < LONGOPT_VAL_BASE + n_longopts)
+ {
+ struct argv_option_plus *aop = &ap->options[c - LONGOPT_VAL_BASE];
+ aop->cb (aop->base.id, aop->aux);
+ }
+ else if (c >= SCHAR_MIN && c <= UCHAR_MAX)
+ {
+ const struct argv_option_plus *aop = shortopt_ptrs[(unsigned char) c];
+ aop->cb (aop->base.id, aop->aux);
+ }
+ else
+ NOT_REACHED ();
+ }
+
+exit:
+ ds_destroy (&shortopts);
+ free (longopts);
+ return retval;
+}
--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2009 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_ARGV_PARSER_H
+#define LIBPSPP_ARGV_PARSER_H 1
+
+/* Simple, modular command-line argument parser.
+
+ glibc has two option parsers, but neither one of them feels
+ quite right:
+
+ - getopt_long is simple, but not modular, in that there is
+ no easy way to make it accept multiple collections of
+ options supported by different modules.
+
+ - argp is more sophisticated and more complete, and hence
+ more complex. It still lacks one important feature for
+ modularity: there is no straightforward way for option
+ groups that are implemented independently to have separate
+ auxiliary data.
+
+ The parser implemented in this file is meant to be simple and
+ modular. It is based internally on getopt_long. */
+
+#include <getopt.h>
+#include <stdbool.h>
+
+struct argv_option
+ {
+ const char *long_name; /* Long option name, NULL if none. */
+ int short_name; /* Short option character, 0 if none. */
+ int has_arg; /* no_argument, required_argument, or
+ optional_argument. */
+ int id; /* Value passed to callback. */
+ };
+
+struct argv_parser *argv_parser_create (void);
+void argv_parser_destroy (struct argv_parser *);
+
+void argv_parser_add_options (struct argv_parser *,
+ const struct argv_option *options, size_t n,
+ void (*cb) (int id, void *aux), void *aux);
+bool argv_parser_run (struct argv_parser *, int argc, char **argv);
+
+#endif /* libpspp/argv-parser.h */