1314564Sdim//===-- GetOptInc.cpp -------------------------------------------*- C++ -*-===// 2314564Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6314564Sdim// 7314564Sdim//===----------------------------------------------------------------------===// 8314564Sdim 9292932Sdim#include "lldb/Host/common/GetOptInc.h" 10292932Sdim 11314564Sdim#if defined(REPLACE_GETOPT) || defined(REPLACE_GETOPT_LONG) || \ 12314564Sdim defined(REPLACE_GETOPT_LONG_ONLY) 13292932Sdim 14292932Sdim// getopt.cpp 15292932Sdim#include <errno.h> 16292932Sdim#include <stdlib.h> 17292932Sdim#include <string.h> 18292932Sdim 19292932Sdim#if defined(REPLACE_GETOPT) 20314564Sdimint opterr = 1; /* if error message should be printed */ 21314564Sdimint optind = 1; /* index into parent argv vector */ 22314564Sdimint optopt = '?'; /* character checked for validity */ 23314564Sdimint optreset; /* reset getopt */ 24314564Sdimchar *optarg; /* argument associated with option */ 25292932Sdim#endif 26292932Sdim 27292932Sdim#define PRINT_ERROR ((opterr) && (*options != ':')) 28292932Sdim 29314564Sdim#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ 30314564Sdim#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ 31314564Sdim#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ 32292932Sdim 33292932Sdim/* return values */ 34314564Sdim#define BADCH (int)'?' 35314564Sdim#define BADARG ((*options == ':') ? (int)':' : (int)'?') 36314564Sdim#define INORDER (int)1 37292932Sdim 38314564Sdim#define EMSG "" 39292932Sdim 40314564Sdimstatic int getopt_internal(int, char *const *, const char *, 41314564Sdim const struct option *, int *, int); 42314564Sdimstatic int parse_long_options(char *const *, const char *, 43314564Sdim const struct option *, int *, int); 44292932Sdimstatic int gcd(int, int); 45314564Sdimstatic void permute_args(int, int, int, char *const *); 46292932Sdim 47292932Sdimstatic const char *place = EMSG; /* option letter processing */ 48292932Sdim 49292932Sdim/* XXX: set optreset to 1 rather than these two */ 50292932Sdimstatic int nonopt_start = -1; /* first non option argument (for permute) */ 51292932Sdimstatic int nonopt_end = -1; /* first option after non options (for permute) */ 52292932Sdim 53292932Sdim/* 54292932Sdim* Compute the greatest common divisor of a and b. 55292932Sdim*/ 56314564Sdimstatic int gcd(int a, int b) { 57314564Sdim int c; 58292932Sdim 59314564Sdim c = a % b; 60314564Sdim while (c != 0) { 61314564Sdim a = b; 62314564Sdim b = c; 63292932Sdim c = a % b; 64314564Sdim } 65292932Sdim 66314564Sdim return (b); 67292932Sdim} 68292932Sdim 69292932Sdimstatic void pass() {} 70292932Sdim#define warnx(a, ...) pass(); 71292932Sdim 72292932Sdim/* 73292932Sdim* Exchange the block from nonopt_start to nonopt_end with the block 74292932Sdim* from nonopt_end to opt_end (keeping the same order of arguments 75292932Sdim* in each block). 76292932Sdim*/ 77314564Sdimstatic void permute_args(int panonopt_start, int panonopt_end, int opt_end, 78314564Sdim char *const *nargv) { 79314564Sdim int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 80314564Sdim char *swap; 81292932Sdim 82314564Sdim /* 83314564Sdim * compute lengths of blocks and number and size of cycles 84314564Sdim */ 85314564Sdim nnonopts = panonopt_end - panonopt_start; 86314564Sdim nopts = opt_end - panonopt_end; 87314564Sdim ncycle = gcd(nnonopts, nopts); 88314564Sdim cyclelen = (opt_end - panonopt_start) / ncycle; 89292932Sdim 90314564Sdim for (i = 0; i < ncycle; i++) { 91314564Sdim cstart = panonopt_end + i; 92314564Sdim pos = cstart; 93314564Sdim for (j = 0; j < cyclelen; j++) { 94314564Sdim if (pos >= panonopt_end) 95314564Sdim pos -= nnonopts; 96314564Sdim else 97314564Sdim pos += nopts; 98314564Sdim swap = nargv[pos]; 99314564Sdim /* LINTED const cast */ 100353358Sdim const_cast<char **>(nargv)[pos] = nargv[cstart]; 101314564Sdim /* LINTED const cast */ 102353358Sdim const_cast<char **>(nargv)[cstart] = swap; 103292932Sdim } 104314564Sdim } 105292932Sdim} 106292932Sdim 107292932Sdim/* 108292932Sdim* parse_long_options -- 109292932Sdim* Parse long options in argc/argv argument vector. 110292932Sdim* Returns -1 if short_too is set and the option does not match long_options. 111292932Sdim*/ 112314564Sdimstatic int parse_long_options(char *const *nargv, const char *options, 113314564Sdim const struct option *long_options, int *idx, 114314564Sdim int short_too) { 115314564Sdim char *current_argv, *has_equal; 116314564Sdim size_t current_argv_len; 117314564Sdim int i, match; 118292932Sdim 119314564Sdim current_argv = const_cast<char *>(place); 120314564Sdim match = -1; 121292932Sdim 122314564Sdim optind++; 123292932Sdim 124314564Sdim if ((has_equal = strchr(current_argv, '=')) != NULL) { 125314564Sdim /* argument found (--option=arg) */ 126314564Sdim current_argv_len = has_equal - current_argv; 127314564Sdim has_equal++; 128314564Sdim } else 129314564Sdim current_argv_len = strlen(current_argv); 130314564Sdim 131314564Sdim for (i = 0; long_options[i].name; i++) { 132314564Sdim /* find matching long option */ 133314564Sdim if (strncmp(current_argv, long_options[i].name, current_argv_len)) 134314564Sdim continue; 135314564Sdim 136314564Sdim if (strlen(long_options[i].name) == current_argv_len) { 137314564Sdim /* exact match */ 138314564Sdim match = i; 139314564Sdim break; 140292932Sdim } 141314564Sdim /* 142314564Sdim * If this is a known short option, don't allow 143314564Sdim * a partial match of a single character. 144314564Sdim */ 145314564Sdim if (short_too && current_argv_len == 1) 146314564Sdim continue; 147292932Sdim 148314564Sdim if (match == -1) /* partial match */ 149314564Sdim match = i; 150314564Sdim else { 151314564Sdim /* ambiguous abbreviation */ 152314564Sdim if (PRINT_ERROR) 153314564Sdim warnx(ambig, (int)current_argv_len, current_argv); 154314564Sdim optopt = 0; 155314564Sdim return (BADCH); 156314564Sdim } 157314564Sdim } 158314564Sdim if (match != -1) { /* option found */ 159314564Sdim if (long_options[match].has_arg == no_argument && has_equal) { 160314564Sdim if (PRINT_ERROR) 161314564Sdim warnx(noarg, (int)current_argv_len, current_argv); 162314564Sdim /* 163314564Sdim * XXX: GNU sets optopt to val regardless of flag 164314564Sdim */ 165314564Sdim if (long_options[match].flag == NULL) 166314564Sdim optopt = long_options[match].val; 167314564Sdim else 168314564Sdim optopt = 0; 169314564Sdim return (BADARG); 170314564Sdim } 171314564Sdim if (long_options[match].has_arg == required_argument || 172314564Sdim long_options[match].has_arg == optional_argument) { 173314564Sdim if (has_equal) 174314564Sdim optarg = has_equal; 175314564Sdim else if (long_options[match].has_arg == required_argument) { 176292932Sdim /* 177314564Sdim * optional argument doesn't use next nargv 178292932Sdim */ 179314564Sdim optarg = nargv[optind++]; 180314564Sdim } 181292932Sdim } 182314564Sdim if ((long_options[match].has_arg == required_argument) && 183314564Sdim (optarg == NULL)) { 184314564Sdim /* 185314564Sdim * Missing argument; leading ':' indicates no error 186314564Sdim * should be generated. 187314564Sdim */ 188314564Sdim if (PRINT_ERROR) 189314564Sdim warnx(recargstring, current_argv); 190314564Sdim /* 191314564Sdim * XXX: GNU sets optopt to val regardless of flag 192314564Sdim */ 193314564Sdim if (long_options[match].flag == NULL) 194314564Sdim optopt = long_options[match].val; 195314564Sdim else 196292932Sdim optopt = 0; 197314564Sdim --optind; 198314564Sdim return (BADARG); 199292932Sdim } 200314564Sdim } else { /* unknown option */ 201314564Sdim if (short_too) { 202314564Sdim --optind; 203314564Sdim return (-1); 204292932Sdim } 205314564Sdim if (PRINT_ERROR) 206314564Sdim warnx(illoptstring, current_argv); 207314564Sdim optopt = 0; 208314564Sdim return (BADCH); 209314564Sdim } 210314564Sdim if (idx) 211314564Sdim *idx = match; 212314564Sdim if (long_options[match].flag) { 213314564Sdim *long_options[match].flag = long_options[match].val; 214314564Sdim return (0); 215314564Sdim } else 216314564Sdim return (long_options[match].val); 217292932Sdim} 218292932Sdim 219292932Sdim/* 220292932Sdim* getopt_internal -- 221292932Sdim* Parse argc/argv argument vector. Called by user level routines. 222292932Sdim*/ 223314564Sdimstatic int getopt_internal(int nargc, char *const *nargv, const char *options, 224314564Sdim const struct option *long_options, int *idx, 225314564Sdim int flags) { 226314564Sdim const char *oli; /* option letter list index */ 227314564Sdim int optchar, short_too; 228314564Sdim static int posixly_correct = -1; 229292932Sdim 230314564Sdim if (options == NULL) 231314564Sdim return (-1); 232292932Sdim 233314564Sdim /* 234314564Sdim * XXX Some GNU programs (like cvs) set optind to 0 instead of 235314564Sdim * XXX using optreset. Work around this braindamage. 236314564Sdim */ 237314564Sdim if (optind == 0) 238314564Sdim optind = optreset = 1; 239292932Sdim 240314564Sdim /* 241314564Sdim * Disable GNU extensions if POSIXLY_CORRECT is set or options 242314564Sdim * string begins with a '+'. 243314564Sdim */ 244314564Sdim if (posixly_correct == -1 || optreset) 245314564Sdim posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); 246314564Sdim if (*options == '-') 247314564Sdim flags |= FLAG_ALLARGS; 248314564Sdim else if (posixly_correct || *options == '+') 249314564Sdim flags &= ~FLAG_PERMUTE; 250314564Sdim if (*options == '+' || *options == '-') 251314564Sdim options++; 252292932Sdim 253314564Sdim optarg = NULL; 254314564Sdim if (optreset) 255314564Sdim nonopt_start = nonopt_end = -1; 256292932Sdimstart: 257314564Sdim if (optreset || !*place) { /* update scanning pointer */ 258314564Sdim optreset = 0; 259314564Sdim if (optind >= nargc) { /* end of argument vector */ 260314564Sdim place = EMSG; 261314564Sdim if (nonopt_end != -1) { 262314564Sdim /* do permutation, if we have to */ 263314564Sdim permute_args(nonopt_start, nonopt_end, optind, nargv); 264314564Sdim optind -= nonopt_end - nonopt_start; 265314564Sdim } else if (nonopt_start != -1) { 266292932Sdim /* 267314564Sdim * If we skipped non-options, set optind 268314564Sdim * to the first of them. 269292932Sdim */ 270314564Sdim optind = nonopt_start; 271314564Sdim } 272314564Sdim nonopt_start = nonopt_end = -1; 273314564Sdim return (-1); 274292932Sdim } 275314564Sdim if (*(place = nargv[optind]) != '-' || 276314564Sdim (place[1] == '\0' && strchr(options, '-') == NULL)) { 277314564Sdim place = EMSG; /* found non-option */ 278314564Sdim if (flags & FLAG_ALLARGS) { 279314564Sdim /* 280314564Sdim * GNU extension: 281314564Sdim * return non-option as argument to option 1 282314564Sdim */ 283314564Sdim optarg = nargv[optind++]; 284314564Sdim return (INORDER); 285314564Sdim } 286314564Sdim if (!(flags & FLAG_PERMUTE)) { 287314564Sdim /* 288314564Sdim * If no permutation wanted, stop parsing 289314564Sdim * at first non-option. 290314564Sdim */ 291314564Sdim return (-1); 292314564Sdim } 293314564Sdim /* do permutation */ 294314564Sdim if (nonopt_start == -1) 295314564Sdim nonopt_start = optind; 296314564Sdim else if (nonopt_end != -1) { 297314564Sdim permute_args(nonopt_start, nonopt_end, optind, nargv); 298314564Sdim nonopt_start = optind - (nonopt_end - nonopt_start); 299314564Sdim nonopt_end = -1; 300314564Sdim } 301314564Sdim optind++; 302314564Sdim /* process next argument */ 303314564Sdim goto start; 304314564Sdim } 305314564Sdim if (nonopt_start != -1 && nonopt_end == -1) 306314564Sdim nonopt_end = optind; 307292932Sdim 308292932Sdim /* 309314564Sdim * If we have "-" do nothing, if "--" we are done. 310292932Sdim */ 311314564Sdim if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 312314564Sdim optind++; 313314564Sdim place = EMSG; 314314564Sdim /* 315314564Sdim * We found an option (--), so if we skipped 316314564Sdim * non-options, we have to permute. 317314564Sdim */ 318314564Sdim if (nonopt_end != -1) { 319314564Sdim permute_args(nonopt_start, nonopt_end, optind, nargv); 320314564Sdim optind -= nonopt_end - nonopt_start; 321314564Sdim } 322314564Sdim nonopt_start = nonopt_end = -1; 323314564Sdim return (-1); 324314564Sdim } 325314564Sdim } 326292932Sdim 327314564Sdim /* 328314564Sdim * Check long options if: 329314564Sdim * 1) we were passed some 330314564Sdim * 2) the arg is not just "-" 331314564Sdim * 3) either the arg starts with -- we are getopt_long_only() 332314564Sdim */ 333314564Sdim if (long_options != NULL && place != nargv[optind] && 334314564Sdim (*place == '-' || (flags & FLAG_LONGONLY))) { 335314564Sdim short_too = 0; 336314564Sdim if (*place == '-') 337314564Sdim place++; /* --foo long option */ 338314564Sdim else if (*place != ':' && strchr(options, *place) != NULL) 339314564Sdim short_too = 1; /* could be short option too */ 340314564Sdim 341314564Sdim optchar = parse_long_options(nargv, options, long_options, idx, short_too); 342314564Sdim if (optchar != -1) { 343314564Sdim place = EMSG; 344314564Sdim return (optchar); 345292932Sdim } 346314564Sdim } 347292932Sdim 348314564Sdim if ((optchar = (int)*place++) == (int)':' || 349314564Sdim (optchar == (int)'-' && *place != '\0') || 350314564Sdim (oli = strchr(options, optchar)) == NULL) { 351314564Sdim /* 352314564Sdim * If the user specified "-" and '-' isn't listed in 353314564Sdim * options, return -1 (non-option) as per POSIX. 354314564Sdim * Otherwise, it is an unknown option character (or ':'). 355314564Sdim */ 356314564Sdim if (optchar == (int)'-' && *place == '\0') 357314564Sdim return (-1); 358314564Sdim if (!*place) 359314564Sdim ++optind; 360314564Sdim if (PRINT_ERROR) 361314564Sdim warnx(illoptchar, optchar); 362314564Sdim optopt = optchar; 363314564Sdim return (BADCH); 364314564Sdim } 365314564Sdim if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 366314564Sdim /* -W long-option */ 367314564Sdim if (*place) /* no space */ 368314564Sdim /* NOTHING */; 369314564Sdim else if (++optind >= nargc) { /* no arg */ 370314564Sdim place = EMSG; 371314564Sdim if (PRINT_ERROR) 372314564Sdim warnx(recargchar, optchar); 373314564Sdim optopt = optchar; 374314564Sdim return (BADARG); 375314564Sdim } else /* white space */ 376314564Sdim place = nargv[optind]; 377314564Sdim optchar = parse_long_options(nargv, options, long_options, idx, 0); 378314564Sdim place = EMSG; 379314564Sdim return (optchar); 380314564Sdim } 381314564Sdim if (*++oli != ':') { /* doesn't take argument */ 382314564Sdim if (!*place) 383314564Sdim ++optind; 384314564Sdim } else { /* takes (optional) argument */ 385314564Sdim optarg = NULL; 386314564Sdim if (*place) /* no white space */ 387314564Sdim optarg = const_cast<char *>(place); 388314564Sdim else if (oli[1] != ':') { /* arg not optional */ 389314564Sdim if (++optind >= nargc) { /* no arg */ 390314564Sdim place = EMSG; 391292932Sdim if (PRINT_ERROR) 392314564Sdim warnx(recargchar, optchar); 393292932Sdim optopt = optchar; 394314564Sdim return (BADARG); 395314564Sdim } else 396314564Sdim optarg = nargv[optind]; 397292932Sdim } 398314564Sdim place = EMSG; 399314564Sdim ++optind; 400314564Sdim } 401314564Sdim /* dump back option letter */ 402314564Sdim return (optchar); 403292932Sdim} 404292932Sdim 405292932Sdim/* 406292932Sdim* getopt -- 407292932Sdim* Parse argc/argv argument vector. 408292932Sdim* 409292932Sdim* [eventually this will replace the BSD getopt] 410292932Sdim*/ 411292932Sdim#if defined(REPLACE_GETOPT) 412314564Sdimint getopt(int nargc, char *const *nargv, const char *options) { 413292932Sdim 414314564Sdim /* 415314564Sdim * We don't pass FLAG_PERMUTE to getopt_internal() since 416314564Sdim * the BSD getopt(3) (unlike GNU) has never done this. 417314564Sdim * 418314564Sdim * Furthermore, since many privileged programs call getopt() 419314564Sdim * before dropping privileges it makes sense to keep things 420314564Sdim * as simple (and bug-free) as possible. 421314564Sdim */ 422314564Sdim return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); 423292932Sdim} 424292932Sdim#endif 425292932Sdim 426292932Sdim/* 427292932Sdim* getopt_long -- 428292932Sdim* Parse argc/argv argument vector. 429292932Sdim*/ 430292932Sdim#if defined(REPLACE_GETOPT_LONG) 431314564Sdimint getopt_long(int nargc, char *const *nargv, const char *options, 432314564Sdim const struct option *long_options, int *idx) { 433314564Sdim return ( 434314564Sdim getopt_internal(nargc, nargv, options, long_options, idx, FLAG_PERMUTE)); 435292932Sdim} 436292932Sdim#endif 437292932Sdim 438292932Sdim/* 439292932Sdim* getopt_long_only -- 440292932Sdim* Parse argc/argv argument vector. 441292932Sdim*/ 442292932Sdim#if defined(REPLACE_GETOPT_LONG_ONLY) 443314564Sdimint getopt_long_only(int nargc, char *const *nargv, const char *options, 444314564Sdim const struct option *long_options, int *idx) { 445292932Sdim 446314564Sdim return (getopt_internal(nargc, nargv, options, long_options, idx, 447314564Sdim FLAG_PERMUTE | FLAG_LONGONLY)); 448292932Sdim} 449292932Sdim#endif 450292932Sdim 451292932Sdim#endif 452