Path: blob/main/contrib/libarchive/unzip/cmdline.c
39483 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2003-2008 Tim Kientzle4* All rights reserved.5*/67/*8* Command line parser for bsdunzip.9*/1011#include "bsdunzip_platform.h"12#ifdef HAVE_ERRNO_H13#include <errno.h>14#endif15#ifdef HAVE_STDLIB_H16#include <stdlib.h>17#endif18#ifdef HAVE_STRING_H19#include <string.h>20#endif2122#include "bsdunzip.h"23#include "err.h"2425/*26* Short options for bsdunzip. Please keep this sorted.27*/28static const char *short_options29= "aCcd:fI:jLlnO:opP:qtuvx:yZ:";3031/*32* Long options for bsdunzip. Please keep this list sorted.33*34* The symbolic names for options that lack a short equivalent are35* defined in bsdunzip.h. Also note that so far I've found no need36* to support optional arguments to long options. That would be37* a small change to the code below.38*/3940static const struct bsdunzip_option {41const char *name;42int required; /* 1 if this option requires an argument. */43int equivalent; /* Equivalent short option. */44} bsdunzip_longopts[] = {45{ "version", 0, OPTION_VERSION },46{ NULL, 0, 0 }47};4849/*50* This getopt implementation has two key features that common51* getopt_long() implementations lack. Apart from those, it's a52* straightforward option parser, considerably simplified by not53* needing to support the wealth of exotic getopt_long() features. It54* has, of course, been shamelessly tailored for bsdunzip. (If you're55* looking for a generic getopt_long() implementation for your56* project, I recommend Gregory Pietsch's public domain getopt_long()57* implementation.) The two additional features are:58*/5960int61bsdunzip_getopt(struct bsdunzip *bsdunzip)62{63enum { state_start = 0, state_next_word, state_short, state_long };6465const struct bsdunzip_option *popt, *match, *match2;66const char *p, *long_prefix;67size_t optlength;68int opt;69int required;7071again:72match = NULL;73match2 = NULL;74long_prefix = "--";75opt = OPTION_NONE;76required = 0;77bsdunzip->argument = NULL;7879/* First time through, initialize everything. */80if (bsdunzip->getopt_state == state_start) {81/* Skip program name. */82++bsdunzip->argv;83--bsdunzip->argc;84if (*bsdunzip->argv == NULL)85return (-1);86bsdunzip->getopt_state = state_next_word;87}8889/*90* We're ready to look at the next word in argv.91*/92if (bsdunzip->getopt_state == state_next_word) {93/* No more arguments, so no more options. */94if (bsdunzip->argv[0] == NULL)95return (-1);96/* Doesn't start with '-', so no more options. */97if (bsdunzip->argv[0][0] != '-')98return (-1);99/* "--" marks end of options; consume it and return. */100if (strcmp(bsdunzip->argv[0], "--") == 0) {101++bsdunzip->argv;102--bsdunzip->argc;103bsdunzip_optind++;104return (-1);105}106/* Get next word for parsing. */107bsdunzip->getopt_word = *bsdunzip->argv++;108--bsdunzip->argc;109bsdunzip_optind++;110if (bsdunzip->getopt_word[1] == '-') {111/* Set up long option parser. */112bsdunzip->getopt_state = state_long;113bsdunzip->getopt_word += 2; /* Skip leading '--' */114} else {115/* Set up short option parser. */116bsdunzip->getopt_state = state_short;117++bsdunzip->getopt_word; /* Skip leading '-' */118}119}120121/*122* We're parsing a group of POSIX-style single-character options.123*/124if (bsdunzip->getopt_state == state_short) {125/* Peel next option off of a group of short options. */126opt = *bsdunzip->getopt_word++;127if (opt == '\0') {128/* End of this group; recurse to get next option. */129bsdunzip->getopt_state = state_next_word;130goto again;131}132133/* Does this option take an argument? */134p = strchr(short_options, opt);135if (p == NULL)136return ('?');137if (p[1] == ':')138required = 1;139140/* If it takes an argument, parse that. */141if (required) {142/* If arg is run-in, bsdunzip->getopt_word already points to it. */143if (bsdunzip->getopt_word[0] == '\0') {144/* Otherwise, pick up the next word. */145bsdunzip->getopt_word = *bsdunzip->argv;146if (bsdunzip->getopt_word == NULL) {147lafe_warnc(0,148"Option -%c requires an argument",149opt);150return ('?');151}152++bsdunzip->argv;153--bsdunzip->argc;154bsdunzip_optind++;155}156bsdunzip->getopt_state = state_next_word;157bsdunzip->argument = bsdunzip->getopt_word;158}159}160161/* We're reading a long option */162if (bsdunzip->getopt_state == state_long) {163/* After this long option, we'll be starting a new word. */164bsdunzip->getopt_state = state_next_word;165166/* Option name ends at '=' if there is one. */167p = strchr(bsdunzip->getopt_word, '=');168if (p != NULL) {169optlength = (size_t)(p - bsdunzip->getopt_word);170bsdunzip->argument = (char *)(uintptr_t)(p + 1);171} else {172optlength = strlen(bsdunzip->getopt_word);173}174175/* Search the table for an unambiguous match. */176for (popt = bsdunzip_longopts; popt->name != NULL; popt++) {177/* Short-circuit if first chars don't match. */178if (popt->name[0] != bsdunzip->getopt_word[0])179continue;180/* If option is a prefix of name in table, record it.*/181if (strncmp(bsdunzip->getopt_word, popt->name, optlength) == 0) {182match2 = match; /* Record up to two matches. */183match = popt;184/* If it's an exact match, we're done. */185if (strlen(popt->name) == optlength) {186match2 = NULL; /* Forget the others. */187break;188}189}190}191192/* Fail if there wasn't a unique match. */193if (match == NULL) {194lafe_warnc(0,195"Option %s%s is not supported",196long_prefix, bsdunzip->getopt_word);197return ('?');198}199if (match2 != NULL) {200lafe_warnc(0,201"Ambiguous option %s%s (matches --%s and --%s)",202long_prefix, bsdunzip->getopt_word, match->name, match2->name);203return ('?');204}205206/* We've found a unique match; does it need an argument? */207if (match->required) {208/* Argument required: get next word if necessary. */209if (bsdunzip->argument == NULL) {210bsdunzip->argument = *bsdunzip->argv;211if (bsdunzip->argument == NULL) {212lafe_warnc(0,213"Option %s%s requires an argument",214long_prefix, match->name);215return ('?');216}217++bsdunzip->argv;218--bsdunzip->argc;219bsdunzip_optind++;220}221} else {222/* Argument forbidden: fail if there is one. */223if (bsdunzip->argument != NULL) {224lafe_warnc(0,225"Option %s%s does not allow an argument",226long_prefix, match->name);227return ('?');228}229}230return (match->equivalent);231}232233return (opt);234}235236237