Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2006 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of the
      7  * License, or any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write to the Free Software
     16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <stdint.h>
     22 #include <string.h>
     23 #include <stdio.h>
     24 #include <getopt.h>
     25 
     26 /** @file
     27  *
     28  * Parse command-line options
     29  *
     30  */
     31 
     32 /**
     33  * Option argument
     34  *
     35  * This will point to the argument for the most recently returned
     36  * option, if applicable.
     37  */
     38 char *optarg;
     39 
     40 /**
     41  * Current option index
     42  *
     43  * This is an index into the argv[] array.  When getopt() returns -1,
     44  * @c optind is the index to the first element that is not an option.
     45  */
     46 int optind;
     47 
     48 /**
     49  * Current option character index
     50  *
     51  * This is an index into the current element of argv[].
     52  */
     53 int nextchar;
     54 
     55 /**
     56  * Unrecognised option
     57  *
     58  * When an unrecognised option is encountered, the actual option
     59  * character is stored in @c optopt.
     60  */
     61 int optopt;
     62 
     63 /**
     64  * Get option argument from argv[] array
     65  *
     66  * @v argc		Argument count
     67  * @v argv		Argument list
     68  * @ret argument	Option argument, or NULL
     69  *
     70  * Grab the next element of argv[], if it exists and is not an option.
     71  */
     72 static const char * get_argv_argument ( int argc, char * const argv[] ) {
     73 	char *arg;
     74 
     75 	/* Don't overrun argv[] */
     76 	if ( optind >= argc )
     77 		return NULL;
     78 	arg = argv[optind];
     79 
     80 	/* If next argv element is an option, then it's not usable as
     81 	 * an argument.
     82 	 */
     83 	if ( *arg == '-' )
     84 		return NULL;
     85 
     86 	/** Consume this argv element, and return it */
     87 	optind++;
     88 	return arg;
     89 }
     90 
     91 /**
     92  * Match long option
     93  *
     94  * @v argc		Argument count
     95  * @v argv		Argument list
     96  * @v opttext		Option text within current argv[] element
     97  * @v longopt		Long option specification
     98  * @ret option		Option to return from getopt()
     99  * @ret matched		Found a match for this long option
    100  */
    101 static int match_long_option ( int argc, char * const argv[],
    102 			       const char *opttext,
    103 			       const struct option *longopt, int *option ) {
    104 	size_t optlen;
    105 	const char *argument = NULL;
    106 
    107 	/* Compare option name */
    108 	optlen = strlen ( longopt->name );
    109 	if ( strncmp ( opttext, longopt->name, optlen ) != 0 )
    110 		return 0;
    111 
    112 	/* Check for inline argument */
    113 	if ( opttext[optlen] == '=' ) {
    114 		argument = &opttext[ optlen + 1 ];
    115 	} else if ( opttext[optlen] ) {
    116 		/* Long option with trailing garbage - no match */
    117 		return 0;
    118 	}
    119 
    120 	/* Consume this argv element */
    121 	optind++;
    122 
    123 	/* If we want an argument but don't have one yet, try to grab
    124 	 * the next argv element
    125 	 */
    126 	if ( ( longopt->has_arg != no_argument ) && ( ! argument ) )
    127 		argument = get_argv_argument ( argc, argv );
    128 
    129 	/* If we need an argument but don't have one, sulk */
    130 	if ( ( longopt->has_arg == required_argument ) && ( ! argument ) ) {
    131 		printf ( "Option \"%s\" requires an argument\n",
    132 			 longopt->name );
    133 		*option = ':';
    134 		return 1;
    135 	}
    136 
    137 	/* If we have an argument where we shouldn't have one, sulk */
    138 	if ( ( longopt->has_arg == no_argument ) && argument ) {
    139 		printf ( "Option \"%s\" takes no argument\n", longopt->name );
    140 		*option = ':';
    141 		return 1;
    142 	}
    143 
    144 	/* Store values and return success */
    145 	optarg = ( char * ) argument;
    146 	if ( longopt->flag ) {
    147 		*(longopt->flag) = longopt->val;
    148 		*option = 0;
    149 	} else {
    150 		*option = longopt->val;
    151 	}
    152 	return 1;
    153 }
    154 
    155 /**
    156  * Match short option
    157  *
    158  * @v argc		Argument count
    159  * @v argv		Argument list
    160  * @v opttext		Option text within current argv[] element
    161  * @v shortopt		Option character from option specification
    162  * @ret option		Option to return from getopt()
    163  * @ret matched		Found a match for this short option
    164  */
    165 static int match_short_option ( int argc, char * const argv[],
    166 				const char *opttext, int shortopt,
    167 				enum getopt_argument_requirement has_arg,
    168 				int *option ) {
    169 	const char *argument = NULL;
    170 
    171 	/* Compare option character */
    172 	if ( *opttext != shortopt )
    173 		return 0;
    174 
    175 	/* Consume option character */
    176 	opttext++;
    177 	nextchar++;
    178 	if ( *opttext ) {
    179 		if ( has_arg != no_argument ) {
    180 			/* Consume remainder of element as inline argument */
    181 			argument = opttext;
    182 			optind++;
    183 			nextchar = 0;
    184 		}
    185 	} else {
    186 		/* Reached end of argv element */
    187 		optind++;
    188 		nextchar = 0;
    189 	}
    190 
    191 	/* If we want an argument but don't have one yet, try to grab
    192 	 * the next argv element
    193 	 */
    194 	if ( ( has_arg != no_argument ) && ( ! argument ) )
    195 		argument = get_argv_argument ( argc, argv );
    196 
    197 	/* If we need an argument but don't have one, sulk */
    198 	if ( ( has_arg == required_argument ) && ( ! argument ) ) {
    199 		printf ( "Option \"%c\" requires an argument\n", shortopt );
    200 		*option = ':';
    201 		return 1;
    202 	}
    203 
    204 	/* Store values and return success */
    205 	optarg = ( char * ) argument;
    206 	*option = shortopt;
    207 	return 1;
    208 }
    209 
    210 /**
    211  * Parse command-line options
    212  *
    213  * @v argc		Argument count
    214  * @v argv		Argument list
    215  * @v optstring		Option specification string
    216  * @v longopts		Long option specification table
    217  * @ret longindex	Index of long option (or NULL)
    218  * @ret option		Option found, or -1 for no more options
    219  *
    220  * Note that the caller must arrange for reset_getopt() to be called
    221  * before each set of calls to getopt_long().  In Etherboot, this is
    222  * done automatically by execv().
    223  */
    224 int getopt_long ( int argc, char * const argv[], const char *optstring,
    225 		  const struct option *longopts, int *longindex ) {
    226 	const char *opttext = argv[optind];
    227 	const struct option *longopt;
    228 	int shortopt;
    229 	enum getopt_argument_requirement has_arg;
    230 	int option;
    231 
    232 	/* Check for end of argv array */
    233 	if ( optind >= argc )
    234 		return -1;
    235 
    236 	/* Check for end of options */
    237 	if ( *(opttext++) != '-' )
    238 		return -1;
    239 
    240 	/* Check for long options */
    241 	if ( *(opttext++) == '-' ) {
    242 		for ( longopt = longopts ; longopt->name ; longopt++ ) {
    243 			if ( ! match_long_option ( argc, argv, opttext,
    244 						   longopt, &option ) )
    245 				continue;
    246 			if ( longindex )
    247 				*longindex = ( longopt - longopts );
    248 			return option;
    249 		}
    250 		optopt = '?';
    251 		printf ( "Unrecognised option \"--%s\"\n", opttext );
    252 		return '?';
    253 	}
    254 
    255 	/* Check for short options */
    256 	if ( nextchar < 1 )
    257 		nextchar = 1;
    258 	opttext = ( argv[optind] + nextchar );
    259 	while ( ( shortopt = *(optstring++) ) ) {
    260 		has_arg = no_argument;
    261 		while ( *optstring == ':' ) {
    262 			has_arg++;
    263 			optstring++;
    264 		}
    265 		if ( match_short_option ( argc, argv, opttext, shortopt,
    266 					  has_arg, &option ) ) {
    267 			return option;
    268 		}
    269 	}
    270 	optopt = *opttext;
    271 	printf ( "Unrecognised option \"-%c\"\n", optopt );
    272 	return '?';
    273 }
    274