Path: blob/main/contrib/llvm-project/lldb/source/Host/common/GetOptInc.cpp
39607 views
//===-- GetOptInc.cpp -----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Host/common/GetOptInc.h"910#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || \11defined(REPLACE_GETOPT_LONG_ONLY)1213// getopt.cpp14#include <cerrno>15#include <cstdlib>16#include <cstring>1718#if defined(REPLACE_GETOPT)19int opterr = 1; /* if error message should be printed */20int optind = 1; /* index into parent argv vector */21int optopt = '?'; /* character checked for validity */22int optreset; /* reset getopt */23char *optarg; /* argument associated with option */24#endif2526#define PRINT_ERROR ((opterr) && (*options != ':'))2728#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */29#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */30#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */3132/* return values */33#define BADCH (int)'?'34#define BADARG ((*options == ':') ? (int)':' : (int)'?')35#define INORDER (int)13637#define EMSG ""3839static int getopt_internal(int, char *const *, const char *,40const struct option *, int *, int);41static int parse_long_options(char *const *, const char *,42const struct option *, int *, int);43static int gcd(int, int);44static void permute_args(int, int, int, char *const *);4546static const char *place = EMSG; /* option letter processing */4748/* XXX: set optreset to 1 rather than these two */49static int nonopt_start = -1; /* first non option argument (for permute) */50static int nonopt_end = -1; /* first option after non options (for permute) */5152/*53* Compute the greatest common divisor of a and b.54*/55static int gcd(int a, int b) {56int c;5758c = a % b;59while (c != 0) {60a = b;61b = c;62c = a % b;63}6465return (b);66}6768static void pass() {}69#define warnx(a, ...) pass();7071/*72* Exchange the block from nonopt_start to nonopt_end with the block73* from nonopt_end to opt_end (keeping the same order of arguments74* in each block).75*/76static void permute_args(int panonopt_start, int panonopt_end, int opt_end,77char *const *nargv) {78int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;79char *swap;8081/*82* compute lengths of blocks and number and size of cycles83*/84nnonopts = panonopt_end - panonopt_start;85nopts = opt_end - panonopt_end;86ncycle = gcd(nnonopts, nopts);87cyclelen = (opt_end - panonopt_start) / ncycle;8889for (i = 0; i < ncycle; i++) {90cstart = panonopt_end + i;91pos = cstart;92for (j = 0; j < cyclelen; j++) {93if (pos >= panonopt_end)94pos -= nnonopts;95else96pos += nopts;97swap = nargv[pos];98/* LINTED const cast */99const_cast<char **>(nargv)[pos] = nargv[cstart];100/* LINTED const cast */101const_cast<char **>(nargv)[cstart] = swap;102}103}104}105106/*107* parse_long_options --108* Parse long options in argc/argv argument vector.109* Returns -1 if short_too is set and the option does not match long_options.110*/111static int parse_long_options(char *const *nargv, const char *options,112const struct option *long_options, int *idx,113int short_too) {114char *current_argv, *has_equal;115size_t current_argv_len;116int i, match;117118current_argv = const_cast<char *>(place);119match = -1;120121optind++;122123if ((has_equal = strchr(current_argv, '=')) != NULL) {124/* argument found (--option=arg) */125current_argv_len = has_equal - current_argv;126has_equal++;127} else128current_argv_len = strlen(current_argv);129130for (i = 0; long_options[i].name; i++) {131/* find matching long option */132if (strncmp(current_argv, long_options[i].name, current_argv_len))133continue;134135if (strlen(long_options[i].name) == current_argv_len) {136/* exact match */137match = i;138break;139}140/*141* If this is a known short option, don't allow142* a partial match of a single character.143*/144if (short_too && current_argv_len == 1)145continue;146147if (match == -1) /* partial match */148match = i;149else {150/* ambiguous abbreviation */151if (PRINT_ERROR)152warnx(ambig, (int)current_argv_len, current_argv);153optopt = 0;154return (BADCH);155}156}157if (match != -1) { /* option found */158if (long_options[match].has_arg == no_argument && has_equal) {159if (PRINT_ERROR)160warnx(noarg, (int)current_argv_len, current_argv);161/*162* XXX: GNU sets optopt to val regardless of flag163*/164if (long_options[match].flag == NULL)165optopt = long_options[match].val;166else167optopt = 0;168return (BADARG);169}170if (long_options[match].has_arg == required_argument ||171long_options[match].has_arg == optional_argument) {172if (has_equal)173optarg = has_equal;174else if (long_options[match].has_arg == required_argument) {175/*176* optional argument doesn't use next nargv177*/178optarg = nargv[optind++];179}180}181if ((long_options[match].has_arg == required_argument) &&182(optarg == NULL)) {183/*184* Missing argument; leading ':' indicates no error185* should be generated.186*/187if (PRINT_ERROR)188warnx(recargstring, current_argv);189/*190* XXX: GNU sets optopt to val regardless of flag191*/192if (long_options[match].flag == NULL)193optopt = long_options[match].val;194else195optopt = 0;196--optind;197return (BADARG);198}199} else { /* unknown option */200if (short_too) {201--optind;202return (-1);203}204if (PRINT_ERROR)205warnx(illoptstring, current_argv);206optopt = 0;207return (BADCH);208}209if (idx)210*idx = match;211if (long_options[match].flag) {212*long_options[match].flag = long_options[match].val;213return (0);214} else215return (long_options[match].val);216}217218/*219* getopt_internal --220* Parse argc/argv argument vector. Called by user level routines.221*/222static int getopt_internal(int nargc, char *const *nargv, const char *options,223const struct option *long_options, int *idx,224int flags) {225const char *oli; /* option letter list index */226int optchar, short_too;227static int posixly_correct = -1;228229if (options == NULL)230return (-1);231232/*233* XXX Some GNU programs (like cvs) set optind to 0 instead of234* XXX using optreset. Work around this braindamage.235*/236if (optind == 0)237optind = optreset = 1;238239/*240* Disable GNU extensions if POSIXLY_CORRECT is set or options241* string begins with a '+'.242*/243if (posixly_correct == -1 || optreset)244posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);245if (*options == '-')246flags |= FLAG_ALLARGS;247else if (posixly_correct || *options == '+')248flags &= ~FLAG_PERMUTE;249if (*options == '+' || *options == '-')250options++;251252optarg = NULL;253if (optreset)254nonopt_start = nonopt_end = -1;255start:256if (optreset || !*place) { /* update scanning pointer */257optreset = 0;258if (optind >= nargc) { /* end of argument vector */259place = EMSG;260if (nonopt_end != -1) {261/* do permutation, if we have to */262permute_args(nonopt_start, nonopt_end, optind, nargv);263optind -= nonopt_end - nonopt_start;264} else if (nonopt_start != -1) {265/*266* If we skipped non-options, set optind267* to the first of them.268*/269optind = nonopt_start;270}271nonopt_start = nonopt_end = -1;272return (-1);273}274if (*(place = nargv[optind]) != '-' ||275(place[1] == '\0' && strchr(options, '-') == NULL)) {276place = EMSG; /* found non-option */277if (flags & FLAG_ALLARGS) {278/*279* GNU extension:280* return non-option as argument to option 1281*/282optarg = nargv[optind++];283return (INORDER);284}285if (!(flags & FLAG_PERMUTE)) {286/*287* If no permutation wanted, stop parsing288* at first non-option.289*/290return (-1);291}292/* do permutation */293if (nonopt_start == -1)294nonopt_start = optind;295else if (nonopt_end != -1) {296permute_args(nonopt_start, nonopt_end, optind, nargv);297nonopt_start = optind - (nonopt_end - nonopt_start);298nonopt_end = -1;299}300optind++;301/* process next argument */302goto start;303}304if (nonopt_start != -1 && nonopt_end == -1)305nonopt_end = optind;306307/*308* If we have "-" do nothing, if "--" we are done.309*/310if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {311optind++;312place = EMSG;313/*314* We found an option (--), so if we skipped315* non-options, we have to permute.316*/317if (nonopt_end != -1) {318permute_args(nonopt_start, nonopt_end, optind, nargv);319optind -= nonopt_end - nonopt_start;320}321nonopt_start = nonopt_end = -1;322return (-1);323}324}325326/*327* Check long options if:328* 1) we were passed some329* 2) the arg is not just "-"330* 3) either the arg starts with -- we are getopt_long_only()331*/332if (long_options != NULL && place != nargv[optind] &&333(*place == '-' || (flags & FLAG_LONGONLY))) {334short_too = 0;335if (*place == '-')336place++; /* --foo long option */337else if (*place != ':' && strchr(options, *place) != NULL)338short_too = 1; /* could be short option too */339340optchar = parse_long_options(nargv, options, long_options, idx, short_too);341if (optchar != -1) {342place = EMSG;343return (optchar);344}345}346347if ((optchar = (int)*place++) == (int)':' ||348(optchar == (int)'-' && *place != '\0') ||349(oli = strchr(options, optchar)) == NULL) {350/*351* If the user specified "-" and '-' isn't listed in352* options, return -1 (non-option) as per POSIX.353* Otherwise, it is an unknown option character (or ':').354*/355if (optchar == (int)'-' && *place == '\0')356return (-1);357if (!*place)358++optind;359if (PRINT_ERROR)360warnx(illoptchar, optchar);361optopt = optchar;362return (BADCH);363}364if (long_options != NULL && optchar == 'W' && oli[1] == ';') {365/* -W long-option */366if (*place) /* no space */367/* NOTHING */;368else if (++optind >= nargc) { /* no arg */369place = EMSG;370if (PRINT_ERROR)371warnx(recargchar, optchar);372optopt = optchar;373return (BADARG);374} else /* white space */375place = nargv[optind];376optchar = parse_long_options(nargv, options, long_options, idx, 0);377place = EMSG;378return (optchar);379}380if (*++oli != ':') { /* doesn't take argument */381if (!*place)382++optind;383} else { /* takes (optional) argument */384optarg = NULL;385if (*place) /* no white space */386optarg = const_cast<char *>(place);387else if (oli[1] != ':') { /* arg not optional */388if (++optind >= nargc) { /* no arg */389place = EMSG;390if (PRINT_ERROR)391warnx(recargchar, optchar);392optopt = optchar;393return (BADARG);394} else395optarg = nargv[optind];396}397place = EMSG;398++optind;399}400/* dump back option letter */401return (optchar);402}403404/*405* getopt --406* Parse argc/argv argument vector.407*408* [eventually this will replace the BSD getopt]409*/410#if defined(REPLACE_GETOPT)411int getopt(int nargc, char *const *nargv, const char *options) {412413/*414* We don't pass FLAG_PERMUTE to getopt_internal() since415* the BSD getopt(3) (unlike GNU) has never done this.416*417* Furthermore, since many privileged programs call getopt()418* before dropping privileges it makes sense to keep things419* as simple (and bug-free) as possible.420*/421return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));422}423#endif424425/*426* getopt_long --427* Parse argc/argv argument vector.428*/429#if defined(REPLACE_GETOPT_LONG)430int getopt_long(int nargc, char *const *nargv, const char *options,431const struct option *long_options, int *idx) {432return (433getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE));434}435#endif436437/*438* getopt_long_only --439* Parse argc/argv argument vector.440*/441#if defined(REPLACE_GETOPT_LONG_ONLY)442int getopt_long_only(int nargc, char *const *nargv, const char *options,443const struct option *long_options, int *idx) {444445return (getopt_internal(nargc, nargv, options, long_options, idx,446FLAG_PERMUTE | FLAG_LONGONLY));447}448#endif449450#endif451452453