1/* 2 * Copyright (C) 2006 Michael Brown <mbrown@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 19FILE_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 */ 38char *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 */ 46int optind; 47 48/** 49 * Current option character index 50 * 51 * This is an index into the current element of argv[]. 52 */ 53int 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 */ 61int 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 */ 72static 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 */ 101static 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 */ 165static 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 */ 224int 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