From be0cd1cddd18a29264091fabc69bf2ec7a1f2cd2 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Thu, 7 Dec 2006 03:54:57 +0000 Subject: [PATCH] Added a functional version of getopt() and getopt_long(), ready for use in our commands. --- src/core/getopt.c | 280 +++++++++++++++++++++++++++++++++++++++++++ src/include/getopt.h | 75 ++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 src/core/getopt.c create mode 100644 src/include/getopt.h diff --git a/src/core/getopt.c b/src/core/getopt.c new file mode 100644 index 00000000..df275539 --- /dev/null +++ b/src/core/getopt.c @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2006 Michael Brown . + * + * 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 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 +#include +#include +#include + +/** @file + * + * Parse command-line options + * + */ + +/** + * Option argument + * + * This will point to the argument for the most recently returned + * option, if applicable. + */ +char *optarg; + +/** + * Current option index + * + * This is an index into the argv[] array. When getopt() returns -1, + * @c optind is the index to the first element that is not an option. + */ +int optind = 1; + +/** + * Current option character index + * + * This is an index into the current element of argv[]. + */ +static int nextchar = 0; + +/** + * Unrecognised option + * + * When an unrecognised option is encountered, the actual option + * character is stored in @c optopt. + */ +int optopt; + +/** + * Reset getopt() internal state + * + * Due to a limitation of the POSIX getopt() API, it is necessary to + * add a call to reset_getopt() before each set of calls to getopt() + * or getopt_long(). This arises because POSIX assumes that each + * process will parse command line arguments no more than once; this + * assumption is not valid within Etherboot. We work around the + * limitation by arranging for execv() to call reset_getopt() before + * executing the command. + */ +void reset_getopt ( void ) { + optind = 1; + nextchar = 0; +} + +/** + * Get option argument from argv[] array + * + * @v argc Argument count + * @v argv Argument list + * @ret argument Option argument, or NULL + * + * Grab the next element of argv[], if it exists and is not an option. + */ +static const char * get_argv_argument ( int argc, char * const argv[] ) { + char *arg; + + /* Don't overrun argv[] */ + if ( optind >= argc ) + return NULL; + arg = argv[optind]; + + /* If next argv element is an option, then it's not usable as + * an argument. + */ + if ( *arg == '-' ) + return NULL; + + /** Consume this argv element, and return it */ + optind++; + return arg; +} + +/** + * Match long option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v longopt Long option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this long option + */ +static int match_long_option ( int argc, char * const argv[], + const char *opttext, + const struct option *longopt, int *option ) { + size_t optlen; + const char *argument = NULL; + + /* Compare option name */ + optlen = strlen ( longopt->name ); + if ( strncmp ( opttext, longopt->name, optlen ) != 0 ) + return 0; + + /* Check for inline argument */ + if ( opttext[optlen] == '=' ) { + argument = &opttext[ optlen + 1 ]; + } else if ( opttext[optlen] ) { + /* Long option with trailing garbage - no match */ + return 0; + } + + /* Consume this argv element */ + optind++; + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( longopt->has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%s\" requires an argument\n", + longopt->name ); + *option = ':'; + return 1; + } + + /* If we have an argument where we shouldn't have one, sulk */ + if ( ( longopt->has_arg == no_argument ) && argument ) { + printf ( "Option \"%s\" takes no argument\n", longopt->name ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + if ( longopt->flag ) { + *(longopt->flag) = longopt->val; + *option = 0; + } else { + *option = longopt->val; + } + return 1; +} + +/** + * Match short option + * + * @v argc Argument count + * @v argv Argument list + * @v opttext Option text within current argv[] element + * @v shortopt Option character from option specification + * @ret option Option to return from getopt() + * @ret matched Found a match for this short option + */ +static int match_short_option ( int argc, char * const argv[], + const char *opttext, int shortopt, + enum getopt_argument_requirement has_arg, + int *option ) { + const char *argument = NULL; + + /* Compare option character */ + if ( *opttext != shortopt ) + return 0; + + /* Consume option character */ + opttext++; + nextchar++; + if ( *opttext ) { + if ( has_arg != no_argument ) { + /* Consume remainder of element as inline argument */ + argument = opttext; + optind++; + nextchar = 0; + } + } else { + /* Reached end of argv element */ + optind++; + nextchar = 0; + } + + /* If we want an argument but don't have one yet, try to grab + * the next argv element + */ + if ( ( has_arg != no_argument ) && ( ! argument ) ) + argument = get_argv_argument ( argc, argv ); + + /* If we need an argument but don't have one, sulk */ + if ( ( has_arg == required_argument ) && ( ! argument ) ) { + printf ( "Option \"%c\" requires an argument\n", shortopt ); + *option = ':'; + return 1; + } + + /* Store values and return success */ + optarg = ( char * ) argument; + *option = shortopt; + return 1; +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v optstring Option specification string + * @v longopts Long option specification table + * @ret longindex Index of long option (or NULL) + * @ret option Option found, or -1 for no more options + * + */ +int getopt_long ( int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex ) { + const char *opttext = argv[optind]; + const struct option *longopt; + int shortopt; + enum getopt_argument_requirement has_arg; + int option; + + /* Check for end of options */ + if ( *(opttext++) != '-' ) + return -1; + + /* Check for long options */ + if ( *(opttext++) == '-' ) { + for ( longopt = longopts ; longopt->name ; longopt++ ) { + if ( ! match_long_option ( argc, argv, opttext, + longopt, &option ) ) + continue; + if ( longindex ) + *longindex = ( longopt - longopts ); + return option; + } + optopt = '?'; + printf ( "Unrecognised option \"--%s\"\n", opttext ); + return '?'; + } + + /* Check for short options */ + if ( nextchar < 1 ) + nextchar = 1; + opttext = ( argv[optind] + nextchar ); + while ( ( shortopt = *(optstring++) ) ) { + has_arg = no_argument; + while ( *optstring == ':' ) { + has_arg++; + optstring++; + } + if ( match_short_option ( argc, argv, opttext, shortopt, + has_arg, &option ) ) { + return option; + } + } + optopt = *opttext; + printf ( "Unrecognised option \"-%c\"\n", optopt ); + return '?'; +} diff --git a/src/include/getopt.h b/src/include/getopt.h new file mode 100644 index 00000000..e9d3d647 --- /dev/null +++ b/src/include/getopt.h @@ -0,0 +1,75 @@ +#ifndef _GETOPT_H +#define _GETOPT_H + +/** @file + * + * Parse command-line options + * + */ + +#include + +enum getopt_argument_requirement { + /** Option does not take an argument */ + no_argument = 0, + /** Option requires an argument */ + required_argument = 1, + /** Option may have an argument */ + optional_argument = 2, +}; + +/** A long option, as used for getopt_long() */ +struct option { + /** Long name of this option */ + const char *name; + /** Option takes an argument + * + * Must be one of @c no_argument, @c required_argument, or @c + * optional_argument. + */ + int has_arg; + /** Location into which to store @c val, or NULL. + * + * See the description for @c val for more details. + */ + int *flag; + /** Value to return + * + * If @c flag is NULL, then this is the value that will be + * returned by getopt_long() when this option is found, and + * should therefore be set to the equivalent short option + * character. + * + * If @c flag is non-NULL, then this value will be written to + * the location pointed to by @flag, and getopt_long() will + * return 0. + */ + int val; +}; + +extern char *optarg; +extern int optind; +extern int optopt; + +extern int getopt_long ( int argc, char * const argv[], const char *optstring, + const struct option *longopts, int *longindex ); + +/** + * Parse command-line options + * + * @v argv Argument count + * @v argv Argument list + * @v optstring Option specification string + * @ret option Option found, or -1 for no more options + * + * See getopt_long() for full details. + */ +static inline int getopt ( int argc, char * const argv[], + const char *optstring ) { + static const struct option no_options[] = { + { NULL, 0, NULL, 0 } + }; + return getopt_long ( argc, argv, optstring, no_options, NULL ); +} + +#endif /* _GETOPT_H */