1caf54c4fSMartin Matuska /*- 2*bd66c1b4SMartin Matuska * SPDX-License-Identifier: BSD-2-Clause 3*bd66c1b4SMartin Matuska * 4caf54c4fSMartin Matuska * Copyright (c) 2003-2008 Tim Kientzle 5caf54c4fSMartin Matuska * All rights reserved. 6caf54c4fSMartin Matuska */ 7caf54c4fSMartin Matuska 8caf54c4fSMartin Matuska /* 9caf54c4fSMartin Matuska * Command line parser for tar. 10caf54c4fSMartin Matuska */ 11caf54c4fSMartin Matuska 12caf54c4fSMartin Matuska #include "bsdtar_platform.h" 13caf54c4fSMartin Matuska 14caf54c4fSMartin Matuska #ifdef HAVE_ERRNO_H 15caf54c4fSMartin Matuska #include <errno.h> 16caf54c4fSMartin Matuska #endif 17caf54c4fSMartin Matuska #ifdef HAVE_STDLIB_H 18caf54c4fSMartin Matuska #include <stdlib.h> 19caf54c4fSMartin Matuska #endif 20caf54c4fSMartin Matuska #ifdef HAVE_STRING_H 21caf54c4fSMartin Matuska #include <string.h> 22caf54c4fSMartin Matuska #endif 23caf54c4fSMartin Matuska 24caf54c4fSMartin Matuska #include "bsdtar.h" 25caf54c4fSMartin Matuska #include "err.h" 26caf54c4fSMartin Matuska 27caf54c4fSMartin Matuska /* 28caf54c4fSMartin Matuska * Short options for tar. Please keep this sorted. 29caf54c4fSMartin Matuska */ 30caf54c4fSMartin Matuska static const char *short_options 31acc60b03SMartin Matuska = "aBb:C:cf:HhI:JjkLlmnOoPpqrSs:T:tUuvW:wX:xyZz"; 32caf54c4fSMartin Matuska 33caf54c4fSMartin Matuska /* 34caf54c4fSMartin Matuska * Long options for tar. Please keep this list sorted. 35caf54c4fSMartin Matuska * 36caf54c4fSMartin Matuska * The symbolic names for options that lack a short equivalent are 37caf54c4fSMartin Matuska * defined in bsdtar.h. Also note that so far I've found no need 38caf54c4fSMartin Matuska * to support optional arguments to long options. That would be 39caf54c4fSMartin Matuska * a small change to the code below. 40caf54c4fSMartin Matuska */ 41caf54c4fSMartin Matuska 426c95142eSMartin Matuska static const struct bsdtar_option { 43caf54c4fSMartin Matuska const char *name; 44caf54c4fSMartin Matuska int required; /* 1 if this option requires an argument. */ 45caf54c4fSMartin Matuska int equivalent; /* Equivalent short option. */ 46caf54c4fSMartin Matuska } tar_longopts[] = { 47caf54c4fSMartin Matuska { "absolute-paths", 0, 'P' }, 48caf54c4fSMartin Matuska { "append", 0, 'r' }, 4964287048SMartin Matuska { "acls", 0, OPTION_ACLS }, 50acc60b03SMartin Matuska { "auto-compress", 0, 'a' }, 51acc60b03SMartin Matuska { "b64encode", 0, OPTION_B64ENCODE }, 52caf54c4fSMartin Matuska { "block-size", 1, 'b' }, 53cfa49a9bSMartin Matuska { "blocking-factor", 1, 'b' }, 54caf54c4fSMartin Matuska { "bunzip2", 0, 'j' }, 55caf54c4fSMartin Matuska { "bzip", 0, 'j' }, 56caf54c4fSMartin Matuska { "bzip2", 0, 'j' }, 57caf54c4fSMartin Matuska { "cd", 1, 'C' }, 58caf54c4fSMartin Matuska { "check-links", 0, OPTION_CHECK_LINKS }, 59caf54c4fSMartin Matuska { "chroot", 0, OPTION_CHROOT }, 60cdf63a70SMartin Matuska { "clear-nochange-fflags", 0, OPTION_CLEAR_NOCHANGE_FFLAGS }, 61caf54c4fSMartin Matuska { "compress", 0, 'Z' }, 62caf54c4fSMartin Matuska { "confirmation", 0, 'w' }, 63caf54c4fSMartin Matuska { "create", 0, 'c' }, 64caf54c4fSMartin Matuska { "dereference", 0, 'L' }, 65caf54c4fSMartin Matuska { "directory", 1, 'C' }, 6664287048SMartin Matuska { "disable-copyfile", 0, OPTION_NO_MAC_METADATA }, 67caf54c4fSMartin Matuska { "exclude", 1, OPTION_EXCLUDE }, 68caf54c4fSMartin Matuska { "exclude-from", 1, 'X' }, 6952c2bb75SMartin Matuska { "exclude-vcs", 0, OPTION_EXCLUDE_VCS }, 70caf54c4fSMartin Matuska { "extract", 0, 'x' }, 71caf54c4fSMartin Matuska { "fast-read", 0, 'q' }, 7264287048SMartin Matuska { "fflags", 0, OPTION_FFLAGS }, 73caf54c4fSMartin Matuska { "file", 1, 'f' }, 74caf54c4fSMartin Matuska { "files-from", 1, 'T' }, 75caf54c4fSMartin Matuska { "format", 1, OPTION_FORMAT }, 76caf54c4fSMartin Matuska { "gid", 1, OPTION_GID }, 77caf54c4fSMartin Matuska { "gname", 1, OPTION_GNAME }, 78b9128a37SMartin Matuska { "group", 1, OPTION_GROUP }, 79acc60b03SMartin Matuska { "grzip", 0, OPTION_GRZIP }, 80caf54c4fSMartin Matuska { "gunzip", 0, 'z' }, 81caf54c4fSMartin Matuska { "gzip", 0, 'z' }, 82caf54c4fSMartin Matuska { "help", 0, OPTION_HELP }, 83acc60b03SMartin Matuska { "hfsCompression", 0, OPTION_HFS_COMPRESSION }, 84cdf63a70SMartin Matuska { "ignore-zeros", 0, OPTION_IGNORE_ZEROS }, 85caf54c4fSMartin Matuska { "include", 1, OPTION_INCLUDE }, 86caf54c4fSMartin Matuska { "insecure", 0, 'P' }, 876c95142eSMartin Matuska { "interactive", 0, 'w' }, 88caf54c4fSMartin Matuska { "keep-newer-files", 0, OPTION_KEEP_NEWER_FILES }, 89caf54c4fSMartin Matuska { "keep-old-files", 0, 'k' }, 90caf54c4fSMartin Matuska { "list", 0, 't' }, 91acc60b03SMartin Matuska { "lrzip", 0, OPTION_LRZIP }, 92cdf63a70SMartin Matuska { "lz4", 0, OPTION_LZ4 }, 936c95142eSMartin Matuska { "lzip", 0, OPTION_LZIP }, 94caf54c4fSMartin Matuska { "lzma", 0, OPTION_LZMA }, 95acc60b03SMartin Matuska { "lzop", 0, OPTION_LZOP }, 9664287048SMartin Matuska { "mac-metadata", 0, OPTION_MAC_METADATA }, 97caf54c4fSMartin Matuska { "modification-time", 0, 'm' }, 98caf54c4fSMartin Matuska { "newer", 1, OPTION_NEWER_CTIME }, 99caf54c4fSMartin Matuska { "newer-ctime", 1, OPTION_NEWER_CTIME }, 100caf54c4fSMartin Matuska { "newer-ctime-than", 1, OPTION_NEWER_CTIME_THAN }, 101caf54c4fSMartin Matuska { "newer-mtime", 1, OPTION_NEWER_MTIME }, 102caf54c4fSMartin Matuska { "newer-mtime-than", 1, OPTION_NEWER_MTIME_THAN }, 103caf54c4fSMartin Matuska { "newer-than", 1, OPTION_NEWER_CTIME_THAN }, 10464287048SMartin Matuska { "no-acls", 0, OPTION_NO_ACLS }, 10564287048SMartin Matuska { "no-fflags", 0, OPTION_NO_FFLAGS }, 10664287048SMartin Matuska { "no-mac-metadata", 0, OPTION_NO_MAC_METADATA }, 107833a452eSMartin Matuska { "no-read-sparse", 0, OPTION_NO_READ_SPARSE }, 108caf54c4fSMartin Matuska { "no-recursion", 0, 'n' }, 109f9762417SMartin Matuska { "no-safe-writes", 0, OPTION_NO_SAFE_WRITES }, 110caf54c4fSMartin Matuska { "no-same-owner", 0, OPTION_NO_SAME_OWNER }, 111caf54c4fSMartin Matuska { "no-same-permissions", 0, OPTION_NO_SAME_PERMISSIONS }, 11264287048SMartin Matuska { "no-xattr", 0, OPTION_NO_XATTRS }, 11364287048SMartin Matuska { "no-xattrs", 0, OPTION_NO_XATTRS }, 1146c95142eSMartin Matuska { "nodump", 0, OPTION_NODUMP }, 115acc60b03SMartin Matuska { "nopreserveHFSCompression",0, OPTION_NOPRESERVE_HFS_COMPRESSION }, 1166c95142eSMartin Matuska { "norecurse", 0, 'n' }, 117caf54c4fSMartin Matuska { "null", 0, OPTION_NULL }, 118caf54c4fSMartin Matuska { "numeric-owner", 0, OPTION_NUMERIC_OWNER }, 119acc60b03SMartin Matuska { "older", 1, OPTION_OLDER_CTIME }, 120acc60b03SMartin Matuska { "older-ctime", 1, OPTION_OLDER_CTIME }, 121acc60b03SMartin Matuska { "older-ctime-than", 1, OPTION_OLDER_CTIME_THAN }, 122acc60b03SMartin Matuska { "older-mtime", 1, OPTION_OLDER_MTIME }, 123acc60b03SMartin Matuska { "older-mtime-than", 1, OPTION_OLDER_MTIME_THAN }, 124acc60b03SMartin Matuska { "older-than", 1, OPTION_OLDER_CTIME_THAN }, 125caf54c4fSMartin Matuska { "one-file-system", 0, OPTION_ONE_FILE_SYSTEM }, 126caf54c4fSMartin Matuska { "options", 1, OPTION_OPTIONS }, 127b9128a37SMartin Matuska { "owner", 1, OPTION_OWNER }, 128cdf63a70SMartin Matuska { "passphrase", 1, OPTION_PASSPHRASE }, 129caf54c4fSMartin Matuska { "posix", 0, OPTION_POSIX }, 130caf54c4fSMartin Matuska { "preserve-permissions", 0, 'p' }, 131caf54c4fSMartin Matuska { "read-full-blocks", 0, 'B' }, 132833a452eSMartin Matuska { "read-sparse", 0, OPTION_READ_SPARSE }, 133f9762417SMartin Matuska { "safe-writes", 0, OPTION_SAFE_WRITES }, 134caf54c4fSMartin Matuska { "same-owner", 0, OPTION_SAME_OWNER }, 135caf54c4fSMartin Matuska { "same-permissions", 0, 'p' }, 136caf54c4fSMartin Matuska { "strip-components", 1, OPTION_STRIP_COMPONENTS }, 137caf54c4fSMartin Matuska { "to-stdout", 0, 'O' }, 138caf54c4fSMartin Matuska { "totals", 0, OPTION_TOTALS }, 139caf54c4fSMartin Matuska { "uid", 1, OPTION_UID }, 140caf54c4fSMartin Matuska { "uname", 1, OPTION_UNAME }, 141caf54c4fSMartin Matuska { "uncompress", 0, 'Z' }, 142caf54c4fSMartin Matuska { "unlink", 0, 'U' }, 143caf54c4fSMartin Matuska { "unlink-first", 0, 'U' }, 144caf54c4fSMartin Matuska { "update", 0, 'u' }, 145caf54c4fSMartin Matuska { "use-compress-program", 1, OPTION_USE_COMPRESS_PROGRAM }, 146acc60b03SMartin Matuska { "uuencode", 0, OPTION_UUENCODE }, 147caf54c4fSMartin Matuska { "verbose", 0, 'v' }, 148caf54c4fSMartin Matuska { "version", 0, OPTION_VERSION }, 14964287048SMartin Matuska { "xattrs", 0, OPTION_XATTRS }, 150caf54c4fSMartin Matuska { "xz", 0, 'J' }, 1515c831a5bSMartin Matuska { "zstd", 0, OPTION_ZSTD }, 152caf54c4fSMartin Matuska { NULL, 0, 0 } 153caf54c4fSMartin Matuska }; 154caf54c4fSMartin Matuska 155caf54c4fSMartin Matuska /* 156caf54c4fSMartin Matuska * This getopt implementation has two key features that common 157caf54c4fSMartin Matuska * getopt_long() implementations lack. Apart from those, it's a 158caf54c4fSMartin Matuska * straightforward option parser, considerably simplified by not 159caf54c4fSMartin Matuska * needing to support the wealth of exotic getopt_long() features. It 160caf54c4fSMartin Matuska * has, of course, been shamelessly tailored for bsdtar. (If you're 161caf54c4fSMartin Matuska * looking for a generic getopt_long() implementation for your 162caf54c4fSMartin Matuska * project, I recommend Gregory Pietsch's public domain getopt_long() 163caf54c4fSMartin Matuska * implementation.) The two additional features are: 164caf54c4fSMartin Matuska * 165caf54c4fSMartin Matuska * Old-style tar arguments: The original tar implementation treated 166caf54c4fSMartin Matuska * the first argument word as a list of single-character option 167caf54c4fSMartin Matuska * letters. All arguments follow as separate words. For example, 168caf54c4fSMartin Matuska * tar xbf 32 /dev/tape 169caf54c4fSMartin Matuska * Here, the "xbf" is three option letters, "32" is the argument for 170caf54c4fSMartin Matuska * "b" and "/dev/tape" is the argument for "f". We support this usage 171caf54c4fSMartin Matuska * if the first command-line argument does not begin with '-'. We 172caf54c4fSMartin Matuska * also allow regular short and long options to follow, e.g., 173caf54c4fSMartin Matuska * tar xbf 32 /dev/tape -P --format=pax 174caf54c4fSMartin Matuska * 175caf54c4fSMartin Matuska * -W long options: There's an obscure GNU convention (only rarely 176caf54c4fSMartin Matuska * supported even there) that allows "-W option=argument" as an 177caf54c4fSMartin Matuska * alternative way to support long options. This was supported in 178caf54c4fSMartin Matuska * early bsdtar as a way to access long options on platforms that did 179caf54c4fSMartin Matuska * not support getopt_long() and is preserved here for backwards 180caf54c4fSMartin Matuska * compatibility. (Of course, if I'd started with a custom 181caf54c4fSMartin Matuska * command-line parser from the beginning, I would have had normal 182caf54c4fSMartin Matuska * long option support on every platform so that hack wouldn't have 183caf54c4fSMartin Matuska * been necessary. Oh, well. Some mistakes you just have to live 184caf54c4fSMartin Matuska * with.) 185caf54c4fSMartin Matuska * 186caf54c4fSMartin Matuska * TODO: We should be able to use this to pull files and intermingled 187caf54c4fSMartin Matuska * options (such as -C) from the command line in write mode. That 188caf54c4fSMartin Matuska * will require a little rethinking of the argument handling in 189caf54c4fSMartin Matuska * bsdtar.c. 190caf54c4fSMartin Matuska * 191caf54c4fSMartin Matuska * TODO: If we want to support arbitrary command-line options from -T 192caf54c4fSMartin Matuska * input (as GNU tar does), we may need to extend this to handle option 1936c95142eSMartin Matuska * words from sources other than argv/argc. I'm not really sure if I 194caf54c4fSMartin Matuska * like that feature of GNU tar, so it's certainly not a priority. 195caf54c4fSMartin Matuska */ 196caf54c4fSMartin Matuska 197caf54c4fSMartin Matuska int 198caf54c4fSMartin Matuska bsdtar_getopt(struct bsdtar *bsdtar) 199caf54c4fSMartin Matuska { 200caf54c4fSMartin Matuska enum { state_start = 0, state_old_tar, state_next_word, 201caf54c4fSMartin Matuska state_short, state_long }; 202caf54c4fSMartin Matuska 20313d826ffSMartin Matuska const struct bsdtar_option *popt, *match, *match2; 20413d826ffSMartin Matuska const char *p, *long_prefix; 205caf54c4fSMartin Matuska size_t optlength; 20613d826ffSMartin Matuska int opt; 20713d826ffSMartin Matuska int required; 208caf54c4fSMartin Matuska 20913d826ffSMartin Matuska again: 21013d826ffSMartin Matuska match = NULL; 21113d826ffSMartin Matuska match2 = NULL; 21213d826ffSMartin Matuska long_prefix = "--"; 21313d826ffSMartin Matuska opt = '?'; 21413d826ffSMartin Matuska required = 0; 2156c95142eSMartin Matuska bsdtar->argument = NULL; 216caf54c4fSMartin Matuska 217caf54c4fSMartin Matuska /* First time through, initialize everything. */ 2186c95142eSMartin Matuska if (bsdtar->getopt_state == state_start) { 219caf54c4fSMartin Matuska /* Skip program name. */ 220caf54c4fSMartin Matuska ++bsdtar->argv; 221caf54c4fSMartin Matuska --bsdtar->argc; 222caf54c4fSMartin Matuska if (*bsdtar->argv == NULL) 223caf54c4fSMartin Matuska return (-1); 224caf54c4fSMartin Matuska /* Decide between "new style" and "old style" arguments. */ 225caf54c4fSMartin Matuska if (bsdtar->argv[0][0] == '-') { 2266c95142eSMartin Matuska bsdtar->getopt_state = state_next_word; 227caf54c4fSMartin Matuska } else { 2286c95142eSMartin Matuska bsdtar->getopt_state = state_old_tar; 2296c95142eSMartin Matuska bsdtar->getopt_word = *bsdtar->argv++; 230caf54c4fSMartin Matuska --bsdtar->argc; 231caf54c4fSMartin Matuska } 232caf54c4fSMartin Matuska } 233caf54c4fSMartin Matuska 234caf54c4fSMartin Matuska /* 235caf54c4fSMartin Matuska * We're parsing old-style tar arguments 236caf54c4fSMartin Matuska */ 2376c95142eSMartin Matuska if (bsdtar->getopt_state == state_old_tar) { 238caf54c4fSMartin Matuska /* Get the next option character. */ 2396c95142eSMartin Matuska opt = *bsdtar->getopt_word++; 240caf54c4fSMartin Matuska if (opt == '\0') { 241caf54c4fSMartin Matuska /* New-style args can follow old-style. */ 2426c95142eSMartin Matuska bsdtar->getopt_state = state_next_word; 243caf54c4fSMartin Matuska } else { 244caf54c4fSMartin Matuska /* See if it takes an argument. */ 245caf54c4fSMartin Matuska p = strchr(short_options, opt); 246caf54c4fSMartin Matuska if (p == NULL) 247caf54c4fSMartin Matuska return ('?'); 248caf54c4fSMartin Matuska if (p[1] == ':') { 2496c95142eSMartin Matuska bsdtar->argument = *bsdtar->argv; 2506c95142eSMartin Matuska if (bsdtar->argument == NULL) { 251caf54c4fSMartin Matuska lafe_warnc(0, 252caf54c4fSMartin Matuska "Option %c requires an argument", 253caf54c4fSMartin Matuska opt); 254caf54c4fSMartin Matuska return ('?'); 255caf54c4fSMartin Matuska } 256caf54c4fSMartin Matuska ++bsdtar->argv; 257caf54c4fSMartin Matuska --bsdtar->argc; 258caf54c4fSMartin Matuska } 259caf54c4fSMartin Matuska } 260caf54c4fSMartin Matuska } 261caf54c4fSMartin Matuska 262caf54c4fSMartin Matuska /* 263caf54c4fSMartin Matuska * We're ready to look at the next word in argv. 264caf54c4fSMartin Matuska */ 2656c95142eSMartin Matuska if (bsdtar->getopt_state == state_next_word) { 266caf54c4fSMartin Matuska /* No more arguments, so no more options. */ 267caf54c4fSMartin Matuska if (bsdtar->argv[0] == NULL) 268caf54c4fSMartin Matuska return (-1); 269caf54c4fSMartin Matuska /* Doesn't start with '-', so no more options. */ 270caf54c4fSMartin Matuska if (bsdtar->argv[0][0] != '-') 271caf54c4fSMartin Matuska return (-1); 272caf54c4fSMartin Matuska /* "--" marks end of options; consume it and return. */ 273caf54c4fSMartin Matuska if (strcmp(bsdtar->argv[0], "--") == 0) { 274caf54c4fSMartin Matuska ++bsdtar->argv; 275caf54c4fSMartin Matuska --bsdtar->argc; 276caf54c4fSMartin Matuska return (-1); 277caf54c4fSMartin Matuska } 278caf54c4fSMartin Matuska /* Get next word for parsing. */ 2796c95142eSMartin Matuska bsdtar->getopt_word = *bsdtar->argv++; 280caf54c4fSMartin Matuska --bsdtar->argc; 2816c95142eSMartin Matuska if (bsdtar->getopt_word[1] == '-') { 282caf54c4fSMartin Matuska /* Set up long option parser. */ 2836c95142eSMartin Matuska bsdtar->getopt_state = state_long; 2846c95142eSMartin Matuska bsdtar->getopt_word += 2; /* Skip leading '--' */ 285caf54c4fSMartin Matuska } else { 286caf54c4fSMartin Matuska /* Set up short option parser. */ 2876c95142eSMartin Matuska bsdtar->getopt_state = state_short; 2886c95142eSMartin Matuska ++bsdtar->getopt_word; /* Skip leading '-' */ 289caf54c4fSMartin Matuska } 290caf54c4fSMartin Matuska } 291caf54c4fSMartin Matuska 292caf54c4fSMartin Matuska /* 293caf54c4fSMartin Matuska * We're parsing a group of POSIX-style single-character options. 294caf54c4fSMartin Matuska */ 2956c95142eSMartin Matuska if (bsdtar->getopt_state == state_short) { 296caf54c4fSMartin Matuska /* Peel next option off of a group of short options. */ 2976c95142eSMartin Matuska opt = *bsdtar->getopt_word++; 298caf54c4fSMartin Matuska if (opt == '\0') { 299caf54c4fSMartin Matuska /* End of this group; recurse to get next option. */ 3006c95142eSMartin Matuska bsdtar->getopt_state = state_next_word; 30113d826ffSMartin Matuska goto again; 302caf54c4fSMartin Matuska } 303caf54c4fSMartin Matuska 304caf54c4fSMartin Matuska /* Does this option take an argument? */ 305caf54c4fSMartin Matuska p = strchr(short_options, opt); 306caf54c4fSMartin Matuska if (p == NULL) 307caf54c4fSMartin Matuska return ('?'); 308caf54c4fSMartin Matuska if (p[1] == ':') 309caf54c4fSMartin Matuska required = 1; 310caf54c4fSMartin Matuska 311caf54c4fSMartin Matuska /* If it takes an argument, parse that. */ 312caf54c4fSMartin Matuska if (required) { 3136c95142eSMartin Matuska /* If arg is run-in, bsdtar->getopt_word already points to it. */ 3146c95142eSMartin Matuska if (bsdtar->getopt_word[0] == '\0') { 315caf54c4fSMartin Matuska /* Otherwise, pick up the next word. */ 3166c95142eSMartin Matuska bsdtar->getopt_word = *bsdtar->argv; 3176c95142eSMartin Matuska if (bsdtar->getopt_word == NULL) { 318caf54c4fSMartin Matuska lafe_warnc(0, 319caf54c4fSMartin Matuska "Option -%c requires an argument", 320caf54c4fSMartin Matuska opt); 321caf54c4fSMartin Matuska return ('?'); 322caf54c4fSMartin Matuska } 323caf54c4fSMartin Matuska ++bsdtar->argv; 324caf54c4fSMartin Matuska --bsdtar->argc; 325caf54c4fSMartin Matuska } 326caf54c4fSMartin Matuska if (opt == 'W') { 3276c95142eSMartin Matuska bsdtar->getopt_state = state_long; 328caf54c4fSMartin Matuska long_prefix = "-W "; /* For clearer errors. */ 329caf54c4fSMartin Matuska } else { 3306c95142eSMartin Matuska bsdtar->getopt_state = state_next_word; 3316c95142eSMartin Matuska bsdtar->argument = bsdtar->getopt_word; 332caf54c4fSMartin Matuska } 333caf54c4fSMartin Matuska } 334caf54c4fSMartin Matuska } 335caf54c4fSMartin Matuska 336caf54c4fSMartin Matuska /* We're reading a long option, including -W long=arg convention. */ 3376c95142eSMartin Matuska if (bsdtar->getopt_state == state_long) { 338caf54c4fSMartin Matuska /* After this long option, we'll be starting a new word. */ 3396c95142eSMartin Matuska bsdtar->getopt_state = state_next_word; 340caf54c4fSMartin Matuska 341caf54c4fSMartin Matuska /* Option name ends at '=' if there is one. */ 3426c95142eSMartin Matuska p = strchr(bsdtar->getopt_word, '='); 343caf54c4fSMartin Matuska if (p != NULL) { 3446c95142eSMartin Matuska optlength = (size_t)(p - bsdtar->getopt_word); 3456c95142eSMartin Matuska bsdtar->argument = (char *)(uintptr_t)(p + 1); 346caf54c4fSMartin Matuska } else { 3476c95142eSMartin Matuska optlength = strlen(bsdtar->getopt_word); 348caf54c4fSMartin Matuska } 349caf54c4fSMartin Matuska 350caf54c4fSMartin Matuska /* Search the table for an unambiguous match. */ 351caf54c4fSMartin Matuska for (popt = tar_longopts; popt->name != NULL; popt++) { 352caf54c4fSMartin Matuska /* Short-circuit if first chars don't match. */ 3536c95142eSMartin Matuska if (popt->name[0] != bsdtar->getopt_word[0]) 354caf54c4fSMartin Matuska continue; 355caf54c4fSMartin Matuska /* If option is a prefix of name in table, record it.*/ 3566c95142eSMartin Matuska if (strncmp(bsdtar->getopt_word, popt->name, optlength) == 0) { 357caf54c4fSMartin Matuska match2 = match; /* Record up to two matches. */ 358caf54c4fSMartin Matuska match = popt; 359caf54c4fSMartin Matuska /* If it's an exact match, we're done. */ 360caf54c4fSMartin Matuska if (strlen(popt->name) == optlength) { 361caf54c4fSMartin Matuska match2 = NULL; /* Forget the others. */ 362caf54c4fSMartin Matuska break; 363caf54c4fSMartin Matuska } 364caf54c4fSMartin Matuska } 365caf54c4fSMartin Matuska } 366caf54c4fSMartin Matuska 367caf54c4fSMartin Matuska /* Fail if there wasn't a unique match. */ 368caf54c4fSMartin Matuska if (match == NULL) { 369caf54c4fSMartin Matuska lafe_warnc(0, 370caf54c4fSMartin Matuska "Option %s%s is not supported", 3716c95142eSMartin Matuska long_prefix, bsdtar->getopt_word); 372caf54c4fSMartin Matuska return ('?'); 373caf54c4fSMartin Matuska } 374caf54c4fSMartin Matuska if (match2 != NULL) { 375caf54c4fSMartin Matuska lafe_warnc(0, 376caf54c4fSMartin Matuska "Ambiguous option %s%s (matches --%s and --%s)", 3776c95142eSMartin Matuska long_prefix, bsdtar->getopt_word, match->name, match2->name); 378caf54c4fSMartin Matuska return ('?'); 379caf54c4fSMartin Matuska } 380caf54c4fSMartin Matuska 381caf54c4fSMartin Matuska /* We've found a unique match; does it need an argument? */ 382caf54c4fSMartin Matuska if (match->required) { 383caf54c4fSMartin Matuska /* Argument required: get next word if necessary. */ 3846c95142eSMartin Matuska if (bsdtar->argument == NULL) { 3856c95142eSMartin Matuska bsdtar->argument = *bsdtar->argv; 3866c95142eSMartin Matuska if (bsdtar->argument == NULL) { 387caf54c4fSMartin Matuska lafe_warnc(0, 388caf54c4fSMartin Matuska "Option %s%s requires an argument", 389caf54c4fSMartin Matuska long_prefix, match->name); 390caf54c4fSMartin Matuska return ('?'); 391caf54c4fSMartin Matuska } 392caf54c4fSMartin Matuska ++bsdtar->argv; 393caf54c4fSMartin Matuska --bsdtar->argc; 394caf54c4fSMartin Matuska } 395caf54c4fSMartin Matuska } else { 396caf54c4fSMartin Matuska /* Argument forbidden: fail if there is one. */ 3976c95142eSMartin Matuska if (bsdtar->argument != NULL) { 398caf54c4fSMartin Matuska lafe_warnc(0, 399caf54c4fSMartin Matuska "Option %s%s does not allow an argument", 400caf54c4fSMartin Matuska long_prefix, match->name); 401caf54c4fSMartin Matuska return ('?'); 402caf54c4fSMartin Matuska } 403caf54c4fSMartin Matuska } 404caf54c4fSMartin Matuska return (match->equivalent); 405caf54c4fSMartin Matuska } 406caf54c4fSMartin Matuska 407caf54c4fSMartin Matuska return (opt); 408caf54c4fSMartin Matuska } 409