1/**************************************************************************** 2 3getopt.c - Read command line options 4 5AUTHOR: Gregory Pietsch 6CREATED Fri Jan 10 21:13:05 1997 7 8DESCRIPTION: 9 10The getopt() function parses the command line arguments. Its arguments argc 11and argv are the argument count and array as passed to the main() function 12on program invocation. The argument optstring is a list of available option 13characters. If such a character is followed by a colon (`:'), the option 14takes an argument, which is placed in optarg. If such a character is 15followed by two colons, the option takes an optional argument, which is 16placed in optarg. If the option does not take an argument, optarg is NULL. 17 18The external variable optind is the index of the next array element of argv 19to be processed; it communicates from one call to the next which element to 20process. 21 22The getopt_long() function works like getopt() except that it also accepts 23long options started by two dashes `--'. If these take values, it is either 24in the form 25 26--arg=value 27 28 or 29 30--arg value 31 32It takes the additional arguments longopts which is a pointer to the first 33element of an array of type GETOPT_LONG_OPTION_T. The last element of the 34array has to be filled with NULL for the name field. 35 36The longind pointer points to the index of the current long option relative 37to longopts if it is non-NULL. 38 39The getopt() function returns the option character if the option was found 40successfully, `:' if there was a missing parameter for one of the options, 41`?' for an unknown option character, and EOF for the end of the option list. 42 43The getopt_long() function's return value is described in the header file. 44 45The function getopt_long_only() is identical to getopt_long(), except that a 46plus sign `+' can introduce long options as well as `--'. 47 48The following describes how to deal with options that follow non-option 49argv-elements. 50 51If the caller did not specify anything, the default is REQUIRE_ORDER if the 52environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. 53 54REQUIRE_ORDER means don't recognize them as options; stop option processing 55when the first non-option is seen. This is what Unix does. This mode of 56operation is selected by either setting the environment variable 57POSIXLY_CORRECT, or using `+' as the first character of the optstring 58parameter. 59 60PERMUTE is the default. We permute the contents of ARGV as we scan, so that 61eventually all the non-options are at the end. This allows options to be 62given in any order, even with programs that were not written to expect this. 63 64RETURN_IN_ORDER is an option available to programs that were written to 65expect options and other argv-elements in any order and that care about the 66ordering of the two. We describe each non-option argv-element as if it were 67the argument of an option with character code 1. Using `-' as the first 68character of the optstring parameter selects this mode of operation. 69 70The special argument `--' forces an end of option-scanning regardless of the 71value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause 72getopt() and friends to return EOF with optind != argc. 73 74COPYRIGHT NOTICE AND DISCLAIMER: 75 76Copyright (C) 1997 Gregory Pietsch 77 78This file and the accompanying getopt.h header file are hereby placed in the 79public domain without restrictions. Just give the author credit, don't 80claim you wrote it or prevent anyone else from using it. 81 82Gregory Pietsch's current e-mail address: 83gpietsch@comcast.net 84****************************************************************************/ 85 86/* include files */ 87#include <stdio.h> 88#include <stdlib.h> 89#include <string.h> 90#ifndef GETOPT_H 91#include "getopt.h" 92#endif 93 94/* macros */ 95 96/* types */ 97typedef enum GETOPT_ORDERING_T 98{ 99 PERMUTE, 100 RETURN_IN_ORDER, 101 REQUIRE_ORDER 102} GETOPT_ORDERING_T; 103 104/* globally-defined variables */ 105char *optarg = NULL; 106int optind = 0; 107int opterr = 1; 108int optopt = '?'; 109 110/* functions */ 111 112/* reverse_argv_elements: reverses num elements starting at argv */ 113static void 114reverse_argv_elements (char **argv, int num) 115{ 116 int i; 117 char *tmp; 118 119 for (i = 0; i < (num >> 1); i++) 120 { 121 tmp = argv[i]; 122 argv[i] = argv[num - i - 1]; 123 argv[num - i - 1] = tmp; 124 } 125} 126 127/* permute: swap two blocks of argv-elements given their lengths */ 128static void 129permute (char **argv, int len1, int len2) 130{ 131 reverse_argv_elements (argv, len1); 132 reverse_argv_elements (argv, len1 + len2); 133 reverse_argv_elements (argv, len2); 134} 135 136/* is_option: is this argv-element an option or the end of the option list? */ 137static int 138is_option (char *argv_element, int only) 139{ 140 return ((argv_element == NULL) 141 || (argv_element[0] == '-') || (only && argv_element[0] == '+')); 142} 143 144/* getopt_internal: the function that does all the dirty work */ 145static int 146getopt_internal (int argc, char **argv, char *shortopts, 147 GETOPT_LONG_OPTION_T * longopts, int *longind, int only) 148{ 149 GETOPT_ORDERING_T ordering = PERMUTE; 150 static size_t optwhere = 0; 151 size_t permute_from = 0; 152 int num_nonopts = 0; 153 int optindex = 0; 154 size_t match_chars = 0; 155 char *possible_arg = NULL; 156 int longopt_match = -1; 157 int has_arg = -1; 158 char *cp = NULL; 159 int arg_next = 0; 160 161 /* first, deal with silly parameters and easy stuff */ 162 if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL)) 163 return (optopt = '?'); 164 if (optind >= argc || argv[optind] == NULL) 165 return EOF; 166 if (strcmp (argv[optind], "--") == 0) 167 { 168 optind++; 169 return EOF; 170 } 171 /* if this is our first time through */ 172 if (optind == 0) 173 optind = optwhere = 1; 174 175 /* define ordering */ 176 if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+')) 177 { 178 ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER; 179 shortopts++; 180 } 181 else 182 ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE; 183 184 /* 185 * based on ordering, find our next option, if we're at the beginning of 186 * one 187 */ 188 if (optwhere == 1) 189 { 190 switch (ordering) 191 { 192 case PERMUTE: 193 permute_from = optind; 194 num_nonopts = 0; 195 while (!is_option (argv[optind], only)) 196 { 197 optind++; 198 num_nonopts++; 199 } 200 if (argv[optind] == NULL) 201 { 202 /* no more options */ 203 optind = permute_from; 204 return EOF; 205 } 206 else if (strcmp (argv[optind], "--") == 0) 207 { 208 /* no more options, but have to get `--' out of the way */ 209 permute (argv + permute_from, num_nonopts, 1); 210 optind = permute_from + 1; 211 return EOF; 212 } 213 break; 214 case RETURN_IN_ORDER: 215 if (!is_option (argv[optind], only)) 216 { 217 optarg = argv[optind++]; 218 return (optopt = 1); 219 } 220 break; 221 case REQUIRE_ORDER: 222 if (!is_option (argv[optind], only)) 223 return EOF; 224 break; 225 } 226 } 227 /* we've got an option, so parse it */ 228 229 /* first, is it a long option? */ 230 if (longopts != NULL 231 && (memcmp (argv[optind], "--", 2) == 0 232 || (only && argv[optind][0] == '+')) && optwhere == 1) 233 { 234 /* handle long options */ 235 if (memcmp (argv[optind], "--", 2) == 0) 236 optwhere = 2; 237 longopt_match = -1; 238 possible_arg = strchr (argv[optind] + optwhere, '='); 239 if (possible_arg == NULL) 240 { 241 /* no =, so next argv might be arg */ 242 match_chars = strlen (argv[optind]); 243 possible_arg = argv[optind] + match_chars; 244 match_chars = match_chars - optwhere; 245 } 246 else 247 match_chars = (possible_arg - argv[optind]) - optwhere; 248 for (optindex = 0; longopts[optindex].name != NULL; optindex++) 249 { 250 if (memcmp (argv[optind] + optwhere, 251 longopts[optindex].name, match_chars) == 0) 252 { 253 /* do we have an exact match? */ 254 if (match_chars == strlen (longopts[optindex].name)) 255 { 256 longopt_match = optindex; 257 break; 258 } 259 /* do any characters match? */ 260 else 261 { 262 if (longopt_match < 0) 263 longopt_match = optindex; 264 else 265 { 266 /* we have ambiguous options */ 267 if (opterr) 268 fprintf (stderr, "%s: option `%s' is ambiguous " 269 "(could be `--%s' or `--%s')\n", 270 argv[0], 271 argv[optind], 272 longopts[longopt_match].name, 273 longopts[optindex].name); 274 return (optopt = '?'); 275 } 276 } 277 } 278 } 279 if (longopt_match >= 0) 280 has_arg = longopts[longopt_match].has_arg; 281 } 282 /* if we didn't find a long option, is it a short option? */ 283 if (longopt_match < 0 && shortopts != NULL) 284 { 285 cp = strchr (shortopts, argv[optind][optwhere]); 286 if (cp == NULL) 287 { 288 /* couldn't find option in shortopts */ 289 if (opterr) 290 fprintf (stderr, 291 "%s: invalid option -- `-%c'\n", 292 argv[0], argv[optind][optwhere]); 293 optwhere++; 294 if (argv[optind][optwhere] == '\0') 295 { 296 optind++; 297 optwhere = 1; 298 } 299 return (optopt = '?'); 300 } 301 has_arg = ((cp[1] == ':') 302 ? ((cp[2] == ':') ? OPTIONAL_ARG : required_argument) : no_argument); 303 possible_arg = argv[optind] + optwhere + 1; 304 optopt = *cp; 305 } 306 /* get argument and reset optwhere */ 307 arg_next = 0; 308 switch (has_arg) 309 { 310 case OPTIONAL_ARG: 311 if (*possible_arg == '=') 312 possible_arg++; 313 if (*possible_arg != '\0') 314 { 315 optarg = possible_arg; 316 optwhere = 1; 317 } 318 else 319 optarg = NULL; 320 break; 321 case required_argument: 322 if (*possible_arg == '=') 323 possible_arg++; 324 if (*possible_arg != '\0') 325 { 326 optarg = possible_arg; 327 optwhere = 1; 328 } 329 else if (optind + 1 >= argc) 330 { 331 if (opterr) 332 { 333 fprintf (stderr, "%s: argument required for option `", argv[0]); 334 if (longopt_match >= 0) 335 fprintf (stderr, "--%s'\n", longopts[longopt_match].name); 336 else 337 fprintf (stderr, "-%c'\n", *cp); 338 } 339 optind++; 340 return (optopt = ':'); 341 } 342 else 343 { 344 optarg = argv[optind + 1]; 345 arg_next = 1; 346 optwhere = 1; 347 } 348 break; 349 case no_argument: 350 if (longopt_match < 0) 351 { 352 optwhere++; 353 if (argv[optind][optwhere] == '\0') 354 optwhere = 1; 355 } 356 else 357 optwhere = 1; 358 optarg = NULL; 359 break; 360 } 361 362 /* do we have to permute or otherwise modify optind? */ 363 if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0) 364 { 365 permute (argv + permute_from, num_nonopts, 1 + arg_next); 366 optind = permute_from + 1 + arg_next; 367 } 368 else if (optwhere == 1) 369 optind = optind + 1 + arg_next; 370 371 /* finally return */ 372 if (longopt_match >= 0) 373 { 374 if (longind != NULL) 375 *longind = longopt_match; 376 if (longopts[longopt_match].flag != NULL) 377 { 378 *(longopts[longopt_match].flag) = longopts[longopt_match].val; 379 return 0; 380 } 381 else 382 return longopts[longopt_match].val; 383 } 384 else 385 return optopt; 386} 387 388#ifndef _AIX 389int 390getopt (int argc, char **argv, char *optstring) 391{ 392 return getopt_internal (argc, argv, optstring, NULL, NULL, 0); 393} 394#endif 395 396int 397getopt_long (int argc, char **argv, const char *shortopts, 398 const GETOPT_LONG_OPTION_T * longopts, int *longind) 399{ 400 return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0); 401} 402 403int 404getopt_long_only (int argc, char **argv, const char *shortopts, 405 const GETOPT_LONG_OPTION_T * longopts, int *longind) 406{ 407 return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1); 408} 409 410/* end of file GETOPT.C */ 411