pupa-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Argument parser


From: Marco Gerards
Subject: Argument parser
Date: 05 Dec 2003 21:29:22 +0100
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2

Hi,

For me it looked like a good idea to write a argument parser myself.
I did start writing a parser and I already wrote most code.

This parser is not an implementation of argp or popt, but a kind of
mix.  My goals were keeping it simple but feature rich.  To set up a
parser you would have to do the following things:

- Create an option list (struct pupa_arg_option options []).

- Create a structure that describes the parser (struct pupa_arg_parser
  parser)

- Create an array to store the arguments in.

- Write a function to parse the options (argp like).

- Call pupa_argp_parse.

Is this parser good enough for PUPA and which features are currently
missing, but required? (Having a look at the interfaces and the
example should be enough to get the idea :)).

Some features that I didn't implement:

- Children (multiple parsers that can be joined like argp has). This
  is not really useful to us.

- Aliases (one option that has multiple long names).  I think this is
  only useful for programs that should be POSIX compatible, right?

I've included 3 files:

- arg.c: The parser.
- arg.h: The interfaces to the parser.
- hello.c: A modified hello module that demonstrates how the parser
  can be used.

This code is not perfect and not fully implemented.  I'm just sending
it in to hear your opinion about this and to get feedback about the
required features and about the interfaces.  If anything is missing,
please tell me soon, I have a lot of time to implement new features
this weekend. :)

Thanks,
Marco

arg.h:

/*
 *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
 *  Copyright (C) 2003 Marco Gerards <address@hidden>
 *
 *  PUPA 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 2 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 PUPA; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef PUPA_ARG_HEADER
#define PUPA_ARG_HEADER 1

#include <pupa/symbol.h>
#include <pupa/err.h>
#include <pupa/types.h>

enum pupa_arg_type
  {
    ARG_TYPE_NONE,
    ARG_TYPE_STRING,
    ARG_TYPE_CHAR,
    ARG_TYPE_INT,
    ARG_TYPE_DEVICE,
    ARG_TYPE_FILEPATH
  };

typedef enum pupa_arg_type pupa_arg_type_t;

/* Flags for the option field op pupa_arg_option.  */
#define PUPA_ARG_OPTION_OPTIONAL        1 << 1

enum pupa_key_type
  {
    PUPA_KEY_ARG = -1,
    PUPA_KEY_END = -2
  };
typedef enum pupa_key_type pupa_arg_key_type_t;

struct pupa_arg_option
{
  char *longarg;
  char shortarg;
  int flags;
  char *doc;
  char *arg;
  pupa_arg_type_t type;
};

struct pupa_arg_parser
{
  struct pupa_arg_option *options;
  int (* parser) (int, char *, void *);
  char *cmdname;
  char *args_doc;
  char *doc;
};

int EXPORT_FUNC(pupa_arg_parse) (struct pupa_arg_parser *parser, int argc,
                                 char **argv, void *usr);

#endif /* ! PUPA_ARG_HEADER */

hello.c:

/* hello.c - test module for dynamic loading */
/*
 *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
 *  Copyright (C) 2003  NIIBE Yutaka <address@hidden>
 *
 *  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 2 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, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <pupa/types.h>
#include <pupa/misc.h>
#include <pupa/mm.h>
#include <pupa/err.h>
#include <pupa/dl.h>
#include <pupa/normal.h>
#include <pupa/arg.h>

struct hello_arguments
{
  int count;
};

static int
option_parser (int key, char *arg, void *args)
{
  struct hello_arguments *arguments = args;

  switch (key)
    {
    case 'c':
      arguments->count = pupa_strtoul (arg, 0, 0);
      break;
    case PUPA_KEY_END:
      break;
    default:
      pupa_error (PUPA_ERR_BAD_ARGUMENT, "Unknown argument");
    }
  return 0;
}

static struct pupa_arg_option options [] =
  {
    {"count", 'c', 0, "Amount of times to say hello", 0, ARG_TYPE_INT},
    {0, 0, 0, 0, 0, 0}
  };

static struct pupa_arg_parser parser =
  {
    options,
    option_parser,
    "hello",
    "[OPTIONS...]",
    "Hello world module for PUPA"
  };


static int
pupa_cmd_hello (int argc, char *argv[])
{
  int i;
  struct hello_arguments arguments =
    {
      .count = 1
    };

  if (pupa_arg_parse (&parser, argc, argv, &arguments))
    return 0;

  for (i = 0; i < arguments.count; i++)
    pupa_printf ("Hello World\n");
  return 0;
}

#ifdef PUPA_UTIL
void
pupa_hello_init (void)
{
  pupa_register_command ("hello", pupa_cmd_hello, PUPA_COMMAND_FLAG_BOTH,
                         "hello", "Say hello");
}

void
pupa_hello_fini (void)
{
  pupa_unregister_command ("hello");
}
#else /* ! PUPA_UTIL */
PUPA_MOD_INIT
{
  (void)mod;                    /* To stop warning. */
  pupa_register_command ("hello", pupa_cmd_hello, PUPA_COMMAND_FLAG_BOTH,
                         "hello", "Say hello");
}

PUPA_MOD_FINI
{
  pupa_unregister_command ("hello");
}
#endif /* ! PUPA_UTIL */


arg.c:

/* arg.c - argument parser */
/*
 *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
 *  Copyright (C) 2003 Marco Gerards <address@hidden>
 *
 *  PUPA 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 2 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 PUPA; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include "pupa/arg.h"
#include "pupa/misc.h"
#include "pupa/mm.h"
#include "pupa/err.h"

/* Build in parser for default options.  */
static struct pupa_arg_option help_options [] =
  {
    {"help", 'h', 0, "Display help", 0, ARG_TYPE_NONE},
    {"usage", 'u', 0, "Show how to use this command", 0, ARG_TYPE_NONE},
    {0, 0, 0, 0, 0, 0}
  };

static struct pupa_arg_option *
find_short (struct pupa_arg_parser *parser, char c)
{
  struct pupa_arg_option *found;

  static struct pupa_arg_option *fnd_short (struct pupa_arg_option *opt)
    {
      while (opt->doc)
        {
          if (opt->shortarg == c)
            return opt;
          opt++;
        }
      return 0;
    }

  found = fnd_short (parser->options);
  if (!found)
    found = fnd_short (help_options);
    
  return found;
}

static char *
find_long_option (char *s)
{
  char *argpos = pupa_strchr (s, '=');

  if (argpos)
    {
      *argpos = '\0';
      return ++argpos;
    }
  return 0;
}

static struct pupa_arg_option *
find_long (struct pupa_arg_parser *parser, char *s)
{
  struct pupa_arg_option *found;

  static struct pupa_arg_option *fnd_long (struct pupa_arg_option *opt)
    {
      while (opt->doc)
        {
          if (opt->longarg && !pupa_strcmp (opt->longarg, s))
            return opt;
          opt++;
        }
      return 0;
    }

  found = fnd_long (parser->options);
  if (!found)
    found = fnd_long (help_options);
    
  return found;
}


static void
show_usage (struct pupa_arg_parser *parser)
{
  pupa_printf ("%s %s\n", parser->cmdname, parser->args_doc);
}

static void
show_help (struct pupa_arg_parser *parser)
{
  static void showargs (struct pupa_arg_option *opt)
    {
      for (; opt->doc; opt++)
        {
          if (opt->shortarg && pupa_isgraph (opt->shortarg))
            pupa_printf ("-%c%c ", opt->shortarg, opt->longarg ? ',':' ');
          else
            pupa_printf ("    ");
          if (opt->longarg)
            {
              pupa_printf ("--%s", opt->longarg);
              if (opt->arg)
                pupa_printf ("=%s", opt->arg);
            }
          else
            pupa_printf ("\t");

          pupa_printf ("\t\t%s\n", opt->doc);
        }
    }  

  show_usage (parser);
  pupa_printf ("%s\n\n", parser->doc);
  showargs (parser->options);
  showargs (help_options);
  pupa_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
}


static int
parse_option (struct pupa_arg_parser *parser, int key, char *arg, void *usr)
{
  switch (key)
    {
    case 'h':
      show_help (parser);
      return -1;
      
    case 'u':
      show_usage (parser);
      return -1;
      
    default:
      if (parser->parser (key, arg, usr))
        return -1;
    }
  
  return 0;
}

int
pupa_arg_parse (struct pupa_arg_parser *parser, int argc, char **argv, void 
*usr)
{
  int curarg;
  char *longarg = 0;
  int complete = 0;

  for (curarg = 0; curarg < argc; curarg++)
    {
      char *arg = argv[curarg];
      struct pupa_arg_option *opt;
      char *option = 0;

      if (arg[0] != '-')
        {
          if (parse_option (parser, PUPA_KEY_ARG, arg, usr) || pupa_errno)
            goto fail;
          continue;
        }

      if (pupa_strlen (arg) == 2)
        {
          /* If the argument "--" is used just pass the other
             arguments.  */
          if (arg[1] == '-')
            {
              for (curarg++; curarg < argc; curarg++)
                if (parse_option (parser, PUPA_KEY_ARG, arg, usr) || pupa_errno)
                  goto fail;
              break;
            }
          opt = find_short (parser, arg[1]);
          if (!opt)
            {
              pupa_error (PUPA_ERR_BAD_ARGUMENT,
                          "Unknown (short) argument `%s'\n", arg);
              goto fail;
            }

          if (opt->type != ARG_TYPE_NONE)
            {
              curarg++;
              if (curarg < argc)
              option = argv[curarg];
            }
        }
      else if (pupa_strlen (arg) == 1)
        {
          pupa_error (PUPA_ERR_BAD_ARGUMENT, "Option too short.");
          goto fail;
        }
      else 
        {
          longarg = (char *) pupa_strdup (arg);
          if (! longarg)
            goto fail;

          option = find_long_option (longarg);
          arg = longarg;

          opt = find_long (parser, arg + 2);
          if (!opt)
            {
              pupa_error (PUPA_ERR_BAD_ARGUMENT, "Unknown argument `%s'\n", 
arg);
              goto fail;
            }
        }

      if (opt->type != ARG_TYPE_NONE)
        {
          if (!option)
            {
              pupa_error (PUPA_ERR_BAD_ARGUMENT, 
                          "Missing mandatory option for `%s'\n", opt->longarg);
              goto fail;
            }
          
          switch (opt->type)
            {
            case ARG_TYPE_NONE:
              /* This will never happen.  */
              break;
              
            case ARG_TYPE_STRING:
                  /* No need to do anything.  */
              break;
              
            case ARG_TYPE_CHAR:
              if (pupa_strlen (option) != 1)
                {
                  pupa_error (PUPA_ERR_BAD_ARGUMENT, 
                              "The argument is too long, one character is" 
                              "required for the argument `%s'\n", opt->longarg);
                  goto fail;
                }
              break;
              
            case ARG_TYPE_INT:
              {
                char *tail;
                
                pupa_strtoul (option, &tail, 0);
                if (tail == 0 || tail == option || *tail != '\0' || pupa_errno)
                  {
                    pupa_error (PUPA_ERR_BAD_ARGUMENT, 
                                "The argument `%s' requires an integer.", 
                                arg);

                    goto fail;
                  }
                break;
              }
              
            case ARG_TYPE_DEVICE:
            case ARG_TYPE_FILEPATH:
              /* XXX: Not implemented.  */
              break;
            }
          if (parse_option (parser, opt->shortarg, option, usr) || pupa_errno)
            goto fail;
        }
      else
        {
          if (option)
            {
              pupa_error (PUPA_ERR_BAD_ARGUMENT, "A value was assigned to the 
argument `%s' while it "
                          "doesn't require an argument\n", arg);
              goto fail;
            }

          if (parse_option (parser, opt->shortarg, 0, usr) || pupa_errno)
            goto fail;
        }
      pupa_free (longarg);
      longarg = 0;
    }      
  if (parse_option (parser, PUPA_KEY_END, 0, usr) || pupa_errno)
    goto fail;
  complete = 1;

 fail:
  pupa_free (longarg);
  return !complete;
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]