Path: blob/main/contrib/libfido2/openbsd-compat/getopt_long.c
39562 views
/* $OpenBSD: getopt_long.c,v 1.25 2011/03/05 22:10:11 guenther Exp $ */1/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */23/*4* Copyright (c) 2002 Todd C. Miller <[email protected]>5*6* Permission to use, copy, modify, and distribute this software for any7* purpose with or without fee is hereby granted, provided that the above8* copyright notice and this permission notice appear in all copies.9*10* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17*18* Sponsored in part by the Defense Advanced Research Projects19* Agency (DARPA) and Air Force Research Laboratory, Air Force20* Materiel Command, USAF, under agreement number F39502-99-1-0512.21*/22/*-23* Copyright (c) 2000 The NetBSD Foundation, Inc.24* All rights reserved.25*26* This code is derived from software contributed to The NetBSD Foundation27* by Dieter Baron and Thomas Klausner.28*29* Redistribution and use in source and binary forms, with or without30* modification, are permitted provided that the following conditions31* are met:32* 1. Redistributions of source code must retain the above copyright33* notice, this list of conditions and the following disclaimer.34* 2. Redistributions in binary form must reproduce the above copyright35* notice, this list of conditions and the following disclaimer in the36* documentation and/or other materials provided with the distribution.37*38* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS39* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED40* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR41* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS42* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR43* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF44* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS45* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN46* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)47* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE48* POSSIBILITY OF SUCH DAMAGE.49*/5051/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt_long.c */52#include "openbsd-compat.h"5354#if !defined(HAVE_GETOPT)5556#if 057#include <err.h>58#include <getopt.h>59#endif60#include <errno.h>61#include <stdlib.h>62#include <string.h>63#include <stdarg.h>6465int opterr = 1; /* if error message should be printed */66int optind = 1; /* index into parent argv vector */67int optopt = '?'; /* character checked for validity */68int optreset; /* reset getopt */69char *optarg; /* argument associated with option */7071#define PRINT_ERROR ((opterr) && (*options != ':'))7273#define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */74#define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */75#define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */7677/* return values */78#define BADCH (int)'?'79#define BADARG ((*options == ':') ? (int)':' : (int)'?')80#define INORDER (int)18182#define EMSG ""8384static int getopt_internal(int, char * const *, const char *,85const struct option *, int *, int);86static int parse_long_options(char * const *, const char *,87const struct option *, int *, int);88static int gcd(int, int);89static void permute_args(int, int, int, char * const *);9091static char *place = EMSG; /* option letter processing */9293/* XXX: set optreset to 1 rather than these two */94static int nonopt_start = -1; /* first non option argument (for permute) */95static int nonopt_end = -1; /* first option after non options (for permute) */9697/* Error messages */98static const char recargchar[] = "option requires an argument -- %c";99static const char recargstring[] = "option requires an argument -- %s";100static const char ambig[] = "ambiguous option -- %.*s";101static const char noarg[] = "option doesn't take an argument -- %.*s";102static const char illoptchar[] = "unknown option -- %c";103static const char illoptstring[] = "unknown option -- %s";104105/*106* Compute the greatest common divisor of a and b.107*/108static int109gcd(int a, int b)110{111int c;112113c = a % b;114while (c != 0) {115a = b;116b = c;117c = a % b;118}119120return (b);121}122123/*124* Exchange the block from nonopt_start to nonopt_end with the block125* from nonopt_end to opt_end (keeping the same order of arguments126* in each block).127*/128static void129permute_args(int panonopt_start, int panonopt_end, int opt_end,130char * const *nargv)131{132int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;133char *swap;134135/*136* compute lengths of blocks and number and size of cycles137*/138nnonopts = panonopt_end - panonopt_start;139nopts = opt_end - panonopt_end;140ncycle = gcd(nnonopts, nopts);141cyclelen = (opt_end - panonopt_start) / ncycle;142143for (i = 0; i < ncycle; i++) {144cstart = panonopt_end+i;145pos = cstart;146for (j = 0; j < cyclelen; j++) {147if (pos >= panonopt_end)148pos -= nnonopts;149else150pos += nopts;151swap = nargv[pos];152/* LINTED const cast */153((char **) nargv)[pos] = nargv[cstart];154/* LINTED const cast */155((char **)nargv)[cstart] = swap;156}157}158}159160/*161* parse_long_options --162* Parse long options in argc/argv argument vector.163* Returns -1 if short_too is set and the option does not match long_options.164*/165static int166parse_long_options(char * const *nargv, const char *options,167const struct option *long_options, int *idx, int short_too)168{169char *current_argv, *has_equal;170size_t current_argv_len;171int i, match;172173current_argv = place;174match = -1;175176optind++;177178if ((has_equal = strchr(current_argv, '=')) != NULL) {179/* argument found (--option=arg) */180current_argv_len = has_equal - current_argv;181has_equal++;182} else183current_argv_len = strlen(current_argv);184185for (i = 0; long_options[i].name; i++) {186/* find matching long option */187if (strncmp(current_argv, long_options[i].name,188current_argv_len))189continue;190191if (strlen(long_options[i].name) == current_argv_len) {192/* exact match */193match = i;194break;195}196/*197* If this is a known short option, don't allow198* a partial match of a single character.199*/200if (short_too && current_argv_len == 1)201continue;202203if (match == -1) /* partial match */204match = i;205else {206/* ambiguous abbreviation */207if (PRINT_ERROR)208warnx(ambig, (int)current_argv_len,209current_argv);210optopt = 0;211return (BADCH);212}213}214if (match != -1) { /* option found */215if (long_options[match].has_arg == no_argument216&& has_equal) {217if (PRINT_ERROR)218warnx(noarg, (int)current_argv_len,219current_argv);220/*221* XXX: GNU sets optopt to val regardless of flag222*/223if (long_options[match].flag == NULL)224optopt = long_options[match].val;225else226optopt = 0;227return (BADARG);228}229if (long_options[match].has_arg == required_argument ||230long_options[match].has_arg == optional_argument) {231if (has_equal)232optarg = has_equal;233else if (long_options[match].has_arg ==234required_argument) {235/*236* optional argument doesn't use next nargv237*/238optarg = nargv[optind++];239}240}241if ((long_options[match].has_arg == required_argument)242&& (optarg == NULL)) {243/*244* Missing argument; leading ':' indicates no error245* should be generated.246*/247if (PRINT_ERROR)248warnx(recargstring,249current_argv);250/*251* XXX: GNU sets optopt to val regardless of flag252*/253if (long_options[match].flag == NULL)254optopt = long_options[match].val;255else256optopt = 0;257--optind;258return (BADARG);259}260} else { /* unknown option */261if (short_too) {262--optind;263return (-1);264}265if (PRINT_ERROR)266warnx(illoptstring, current_argv);267optopt = 0;268return (BADCH);269}270if (idx)271*idx = match;272if (long_options[match].flag) {273*long_options[match].flag = long_options[match].val;274return (0);275} else276return (long_options[match].val);277}278279/*280* getopt_internal --281* Parse argc/argv argument vector. Called by user level routines.282*/283static int284getopt_internal(int nargc, char * const *nargv, const char *options,285const struct option *long_options, int *idx, int flags)286{287char *oli; /* option letter list index */288int optchar, short_too;289static int posixly_correct = -1;290291if (options == NULL)292return (-1);293294/*295* XXX Some GNU programs (like cvs) set optind to 0 instead of296* XXX using optreset. Work around this braindamage.297*/298if (optind == 0)299optind = optreset = 1;300301/*302* Disable GNU extensions if POSIXLY_CORRECT is set or options303* string begins with a '+'.304*/305if (posixly_correct == -1 || optreset)306posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);307if (*options == '-')308flags |= FLAG_ALLARGS;309else if (posixly_correct || *options == '+')310flags &= ~FLAG_PERMUTE;311if (*options == '+' || *options == '-')312options++;313314optarg = NULL;315if (optreset)316nonopt_start = nonopt_end = -1;317start:318if (optreset || !*place) { /* update scanning pointer */319optreset = 0;320if (optind >= nargc) { /* end of argument vector */321place = EMSG;322if (nonopt_end != -1) {323/* do permutation, if we have to */324permute_args(nonopt_start, nonopt_end,325optind, nargv);326optind -= nonopt_end - nonopt_start;327}328else if (nonopt_start != -1) {329/*330* If we skipped non-options, set optind331* to the first of them.332*/333optind = nonopt_start;334}335nonopt_start = nonopt_end = -1;336return (-1);337}338if (*(place = nargv[optind]) != '-' ||339(place[1] == '\0' && strchr(options, '-') == NULL)) {340place = EMSG; /* found non-option */341if (flags & FLAG_ALLARGS) {342/*343* GNU extension:344* return non-option as argument to option 1345*/346optarg = nargv[optind++];347return (INORDER);348}349if (!(flags & FLAG_PERMUTE)) {350/*351* If no permutation wanted, stop parsing352* at first non-option.353*/354return (-1);355}356/* do permutation */357if (nonopt_start == -1)358nonopt_start = optind;359else if (nonopt_end != -1) {360permute_args(nonopt_start, nonopt_end,361optind, nargv);362nonopt_start = optind -363(nonopt_end - nonopt_start);364nonopt_end = -1;365}366optind++;367/* process next argument */368goto start;369}370if (nonopt_start != -1 && nonopt_end == -1)371nonopt_end = optind;372373/*374* If we have "-" do nothing, if "--" we are done.375*/376if (place[1] != '\0' && *++place == '-' && place[1] == '\0') {377optind++;378place = EMSG;379/*380* We found an option (--), so if we skipped381* non-options, we have to permute.382*/383if (nonopt_end != -1) {384permute_args(nonopt_start, nonopt_end,385optind, nargv);386optind -= nonopt_end - nonopt_start;387}388nonopt_start = nonopt_end = -1;389return (-1);390}391}392393/*394* Check long options if:395* 1) we were passed some396* 2) the arg is not just "-"397* 3) either the arg starts with -- we are getopt_long_only()398*/399if (long_options != NULL && place != nargv[optind] &&400(*place == '-' || (flags & FLAG_LONGONLY))) {401short_too = 0;402if (*place == '-')403place++; /* --foo long option */404else if (*place != ':' && strchr(options, *place) != NULL)405short_too = 1; /* could be short option too */406407optchar = parse_long_options(nargv, options, long_options,408idx, short_too);409if (optchar != -1) {410place = EMSG;411return (optchar);412}413}414415if ((optchar = (int)*place++) == (int)':' ||416(optchar == (int)'-' && *place != '\0') ||417(oli = strchr(options, optchar)) == NULL) {418/*419* If the user specified "-" and '-' isn't listed in420* options, return -1 (non-option) as per POSIX.421* Otherwise, it is an unknown option character (or ':').422*/423if (optchar == (int)'-' && *place == '\0')424return (-1);425if (!*place)426++optind;427if (PRINT_ERROR)428warnx(illoptchar, optchar);429optopt = optchar;430return (BADCH);431}432if (long_options != NULL && optchar == 'W' && oli[1] == ';') {433/* -W long-option */434if (*place) /* no space */435/* NOTHING */;436else if (++optind >= nargc) { /* no arg */437place = EMSG;438if (PRINT_ERROR)439warnx(recargchar, optchar);440optopt = optchar;441return (BADARG);442} else /* white space */443place = nargv[optind];444optchar = parse_long_options(nargv, options, long_options,445idx, 0);446place = EMSG;447return (optchar);448}449if (*++oli != ':') { /* doesn't take argument */450if (!*place)451++optind;452} else { /* takes (optional) argument */453optarg = NULL;454if (*place) /* no white space */455optarg = place;456else if (oli[1] != ':') { /* arg not optional */457if (++optind >= nargc) { /* no arg */458place = EMSG;459if (PRINT_ERROR)460warnx(recargchar, optchar);461optopt = optchar;462return (BADARG);463} else464optarg = nargv[optind];465}466place = EMSG;467++optind;468}469/* dump back option letter */470return (optchar);471}472473/*474* getopt --475* Parse argc/argv argument vector.476*477* [eventually this will replace the BSD getopt]478*/479int480getopt(int nargc, char * const *nargv, const char *options)481{482483/*484* We don't pass FLAG_PERMUTE to getopt_internal() since485* the BSD getopt(3) (unlike GNU) has never done this.486*487* Furthermore, since many privileged programs call getopt()488* before dropping privileges it makes sense to keep things489* as simple (and bug-free) as possible.490*/491return (getopt_internal(nargc, nargv, options, NULL, NULL, 0));492}493494#if 0495/*496* getopt_long --497* Parse argc/argv argument vector.498*/499int500getopt_long(int nargc, char * const *nargv, const char *options,501const struct option *long_options, int *idx)502{503504return (getopt_internal(nargc, nargv, options, long_options, idx,505FLAG_PERMUTE));506}507508/*509* getopt_long_only --510* Parse argc/argv argument vector.511*/512int513getopt_long_only(int nargc, char * const *nargv, const char *options,514const struct option *long_options, int *idx)515{516517return (getopt_internal(nargc, nargv, options, long_options, idx,518FLAG_PERMUTE|FLAG_LONGONLY));519}520#endif521522#endif /* !defined(HAVE_GETOPT) */523524525