Rewrote the command line parser using argp.
[pspp-builds.git] / src / ui / command-line.c
diff --git a/src/ui/command-line.c b/src/ui/command-line.c
new file mode 100644 (file)
index 0000000..46dddd4
--- /dev/null
@@ -0,0 +1,166 @@
+/* PSPPIRE - a graphical user interface for PSPP.
+   Copyright (C) 2008  Free Software Foundation
+
+   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 "command-line.h"
+#include <argp.h>
+#include <gl/xalloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libpspp/compiler.h>
+#include <assert.h>
+
+
+struct clp_child
+{
+  void *aux;
+};
+
+struct command_line_processor
+{
+  struct argp master_parser;
+
+  struct clp_child *child_lookup_table;
+  struct argp_child *children;
+  int n_children;
+
+  const char *doc;
+  const char *args_doc;
+
+  void *aux;
+};
+
+
+/* Convenience function for use in parsing functions.
+   Returns the object for this parser */
+struct command_line_processor *
+get_subject (struct argp_state *state)
+{
+  const struct argp *root = state->root_argp;
+
+  const struct argp_child *children = root->children;
+
+  return  (struct command_line_processor *) children[0].argp;
+}
+
+
+/* Create a command line processor.
+   DOC is typically the name of the program and short description.
+   ARGS_DOC is a short description of the non option arguments.
+   AUX is an arbitrary pointer.
+ */
+struct command_line_processor *
+command_line_processor_create (const char *doc, const char *args_doc, void *aux)
+{
+  struct command_line_processor *clp = xzalloc (sizeof (*clp));
+
+  clp->children = NULL;
+  clp->child_lookup_table = NULL;
+
+  clp->doc = doc;
+  clp->args_doc = args_doc;
+  clp->aux = aux;
+
+  return clp;
+}
+
+/* Destroy a command line processor */
+void
+command_line_processor_destroy (struct command_line_processor *clp)
+{
+  free (clp->children);
+  free (clp->child_lookup_table);
+  free (clp);
+}
+
+
+/* Add a CHILD to the processor CLP, with the doc string DOC.
+   AUX is an auxilliary pointer, specific to CHILD.
+   If AUX is not known or not needed then it may be set to NULL
+*/
+void
+command_line_processor_add_options (struct command_line_processor *clp, const struct argp *child,
+                              const char *doc, void *aux)
+{
+  clp->n_children++;
+
+  clp->children = xrealloc (clp->children, (clp->n_children + 1) * sizeof (*clp->children));
+  memset (&clp->children[clp->n_children - 1], 0, sizeof (*clp->children));
+
+  clp->child_lookup_table = xrealloc (clp->child_lookup_table,
+                                     clp->n_children * sizeof (*clp->child_lookup_table));
+
+  clp->child_lookup_table [clp->n_children - 1].aux = aux;
+
+  clp->children [clp->n_children - 1].argp = child;
+  clp->children [clp->n_children - 1].header = doc;
+  clp->children [clp->n_children].argp = NULL;
+}
+
+
+/* Set the aux paramter for CHILD in CLP to AUX.
+   Any previous value will be overwritten.
+ */
+void
+command_line_processor_replace_aux (struct command_line_processor *clp, const struct argp *child, void *aux)
+{
+  int i;
+  for (i = 0 ; i < clp->n_children; ++i )
+    {
+      if (child->options == clp->children[i].argp->options)
+       {
+         clp->child_lookup_table[i].aux = aux;
+         break;
+       }
+    }
+  assert (i < clp->n_children);
+}
+
+
+static error_t
+top_level_parser (int key UNUSED, char *arg UNUSED, struct argp_state *state)
+{
+  int i;
+  struct command_line_processor *clp = state->input;
+
+  if ( key == ARGP_KEY_INIT)
+    {
+
+      for (i = 0;  i < clp->n_children ; ++i)
+       {
+         state->child_inputs[i] = clp->child_lookup_table[i].aux;
+       }
+    }
+
+  return ARGP_ERR_UNKNOWN;
+}
+
+
+/* Parse the command line specified by (ARGC, ARGV) using CLP */
+void
+command_line_processor_parse (struct command_line_processor *clp, int argc, char **argv)
+{
+  clp->master_parser.parser = top_level_parser;
+  clp->master_parser.args_doc = clp->args_doc;
+
+  clp->master_parser.doc = clp->doc;
+
+  clp->master_parser.children = clp->children;
+
+  argp_parse (&clp->master_parser, argc, argv, 0, 0, clp);
+}
+