/* $Id: getopt.c 4022 2008-03-31 06:11:07Z rra $ * * Replacement implementation of getopt. * * This is a replacement implementation for getopt based on the my_getopt * distribution by Benjamin Sittler. Only the getopt interface is included, * since remctl doesn't use GNU long options, and the code has been rearranged * and reworked somewhat to fit with the remctl coding style. * * Copyright 1997, 2000, 2001, 2002 Benjamin Sittler * Copyright 2008 Russ Allbery * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. */ #include #include #include /* * If we're running the test suite, rename getopt and the global variables to * avoid conflicts with the system version. */ #if TESTING # define getopt test_getopt int test_getopt(int, char **, const char *); # define optind test_optind # define opterr test_opterr # define optopt test_optopt # define optarg test_optarg #endif /* Initialize global interface variables. */ int optind = 1; int opterr = 1; int optopt = 0; char *optarg = NULL; /* * This is the plain old UNIX getopt, with GNU-style extensions. If you're * porting some piece of UNIX software, this is all you need. It supports * GNU-style permutation and optional arguments, but does not support the GNU * -W extension. * * This function is not re-entrant or thread-safe, has static variables, and * generally isn't a great interface, but normally you only call it once. */ int getopt(int argc, char *argv[], const char *optstring) { const char *p; size_t offset = 0; char mode = '\0'; int colon_mode = 0; int option = -1; /* Holds the current position in the parameter being parsed. */ static int charind = 0; /* * By default, getopt permutes argv as it scans and leaves all non-options * at the end. This can be changed with the first character of optstring * or the environment variable POSIXLY_CORRECT. With a first character of * '+' or when POSIXLY_CORRECT is set, option processing stops at the * first non-option. If the first character is '-', each non-option argv * element is handled as if it were the argument of an option with * character code 1. mode holds this character. * * After the optional leading '+' and '-', optstring may contain ':'. If * present, missing arguments return ':' instead of '?'. colon_mode holds * this setting. */ if (getenv("POSIXLY_CORRECT") != NULL) { mode = '+'; colon_mode = '+'; } else { if (optstring[offset] == '+' || optstring[offset] == '-') { mode = optstring[offset]; offset++; } if (optstring[offset] == ':') { colon_mode = 1; offset++; } } /* * charind holds where we left off. If it's set, we were in the middle * of an argv element; if not, we pick up with the next element of * optind. */ optarg = NULL; if (charind == 0) { if (optind >= argc) option = -1; else if (strcmp(argv[optind], "--") == 0) { optind++; option = -1; } else if (argv[optind][0] != '-' || argv[optind][1] == '\0') { char *tmp; int i, j, k, end; if (mode == '+') option = -1; else if (mode == '-') { optarg = argv[optind]; optind++; option = 1; } else { for (i = optind + 1, j = optind; i < argc; i++) if ((argv[i][0] == '-') && (argv[i][1] != '\0')) { optind = i; option = getopt(argc, argv, optstring); while (i > j) { --i; tmp = argv[i]; end = (charind == 0) ? optind - 1 : optind; for (k = i; k + 1 <= end; k++) { argv[k] = argv[k + 1]; } argv[end] = tmp; --optind; } break; } if (i == argc) option = -1; } return option; } else { charind = 1; } } if (charind != 0) { optopt = argv[optind][charind]; for (p = optstring + offset; *p != '\0'; p++) if (optopt == *p) { p++; if (*p == ':') { if (argv[optind][charind + 1] != '\0') { optarg = &argv[optind][charind + 1]; optind++; charind = 0; } else { p++; if (*p != ':') { charind = 0; optind++; if (optind >= argc) { if (opterr) fprintf(stderr, "%s: option requires" " an argument -- %c\n", argv[0], optopt); option = colon_mode ? ':' : '?'; goto done; } else { optarg = argv[optind]; optind++; } } } } option = optopt; } if (option == -1) { if (opterr) fprintf(stderr, "%s: illegal option -- %c\n", argv[0], optopt); option = '?'; } } done: if (charind != 0) { charind++; if (argv[optind][charind] == '\0') { optind++; charind = 0; } } if (optind > argc) optind = argc; return option; }