From 5c164b239dcc33abb89c16642d351f222b199275 Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Tue, 19 Mar 2024 14:39:49 +1100 Subject: [PATCH] rewrite argument parsing --- CMakeLists.txt | 7 +- COPYING.txt | 2 +- ext/ultragetopt/CMakeLists.txt | 22 - ext/ultragetopt/ultragetopt.c | 826 --------------------------------- ext/ultragetopt/ultragetopt.h | 105 ----- src/CMakeLists.txt | 11 +- src/argparse.cpp | 180 +++++++ src/argparse.hpp | 110 +++++ src/tmx2gba.cpp | 234 +++++----- src/tmxreader.cpp | 2 +- 10 files changed, 407 insertions(+), 1092 deletions(-) delete mode 100644 ext/ultragetopt/CMakeLists.txt delete mode 100644 ext/ultragetopt/ultragetopt.c delete mode 100644 ext/ultragetopt/ultragetopt.h create mode 100644 src/argparse.cpp create mode 100644 src/argparse.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d47d3f0..29a2773 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.12 FATAL_ERROR) project(tmx2gba) # Options option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF) option(ASAN "Enable address sanitiser" OFF) -# C++11 & C99 -set(CMAKE_CXX_STANDARD 11) +# C++20 & C99 +set(CMAKE_CXX_STANDARD 20) set(CMAKE_C_STANDARD 99) # Enable strong warnings @@ -26,7 +26,6 @@ endif() add_subdirectory(ext/base64) add_subdirectory(ext/miniz) add_subdirectory(ext/rapidxml) -add_subdirectory(ext/ultragetopt) # Main tmx2gba sources add_subdirectory(src) diff --git a/COPYING.txt b/COPYING.txt index d7d7cde..9de3ef9 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -1,4 +1,4 @@ -Copyright (C) 2015-2022 a dinosaur +Copyright (C) 2015-2024 a dinosaur This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/ext/ultragetopt/CMakeLists.txt b/ext/ultragetopt/CMakeLists.txt deleted file mode 100644 index 15e1c0e..0000000 --- a/ext/ultragetopt/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -include(CheckIncludeFile) -include(CheckFunctionExists) - -check_include_file(strings.h HAVE_STRINGS_H) -check_function_exists(strcasecmp HAVE_STRCASECMP) -check_function_exists(_stricmp HAVE__STRICMP) -check_function_exists(strncasecmp HAVE_STRNCASECMP) -check_function_exists(_strnicmp HAVE__STRNICMP) - -add_library(ultragetopt - ultragetopt.c ultragetopt.h) -add_library(External::ultragetopt ALIAS ultragetopt) - -target_include_directories(ultragetopt - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -target_compile_definitions(ultragetopt PRIVATE - $<$:HAVE_STRINGS_H=1> - $<$:HAVE_STRCASECMP=1> - $<$:HAVE__STRICMP=1> - $<$:HAVE_STRNCASECMP=1> - $<$:HAVE__STRNICMP=1>) diff --git a/ext/ultragetopt/ultragetopt.c b/ext/ultragetopt/ultragetopt.c deleted file mode 100644 index 84bfade..0000000 --- a/ext/ultragetopt/ultragetopt.c +++ /dev/null @@ -1,826 +0,0 @@ -/* Ultra-Getopt - A replacement for getopt() with support for many common - * extensions, MS-DOS formatted option strings, and much more. - * - * To use this library as a replacement for vendor-provided getopt() functions, - * define ULTRAGETOPT_REPLACE_GETOPT and include "ultragetopt.h" after the - * vendor-provided headers for getopt() functions. - * - * Copyright (C) 2007-2011, Kevin Locke - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include /* islower() isupper() tolower() toupper() */ -#include -#include /* fprintf() */ -#include /* getenv() */ -#include /* strcmp(), strncmp(), strchr() */ - -#if HAVE_STRINGS_H -# include /* strcasecmp(), strncasecmp() */ -#endif - -#undef ULTRAGETOPT_REPLACE_GETOPT /* Protect against project-wide defines */ -#include "ultragetopt.h" - -/* Define replacements for missing functions */ -#if !HAVE_STRCASECMP && HAVE__STRICMP -# define strcasecmp _stricmp -#endif - -#if !HAVE_STRNCASECMP && HAVE__STRNICMP -# define strncasecmp _strnicmp -#endif - -#if !HAVE_STRCHR && HAVE_INDEX -# define strchr index -#endif - -/* Supported defines: - * ULTRAGETOPT_LIKE_BSD Behave like BSD getopt() - * ULTRAGETOPT_LIKE_DARWIN Behave like Darwin (Mac OS) getopt() - * ULTRAGETOPT_LIKE_GNU Behave like GNU getopt() - * ULTRAGETOPT_LIKE_POSIX Behave like POSIX definition of getopt() - * - * ULTRAGETOPT_ASSIGNSPACE Parse "-o value" as "value" rather than " value" - * Note: Only applicable when argv[x] == "-o value" - * Not for argv[x] == "-o" [x+1] == "value" - * ULTRAGETOPT_DEFAULTOPTOPT Set optopt to this value by default on each - * call to getopt() - * ULTRAGETOPT_HYPHENARG Accept -option -arg as -option with argument - * "-arg" rather than -option missing argument - * Note: A single "-" is always accepted as an - * argument - * ULTRAGETOPT_LONGOPTADJACENT Accept adjacent arguments to long options - * (e.g. --optionarg) based on first longest-match - * ULTRAGETOPT_OPTIONPERMUTE Permute options, do not stop at first non-option - * Behaves like GNU getopt where leading '+' or - * $POSIXLY_CORRECT both stop this @ runtime - * ULTRAGETOPT_SHORTOPTASSIGN Support -o=file syntax for short options - * ULTRAGETOPT_SEPARATEDOPTIONAL Accept separated optional arguments - * Parse -o file as -o with argument file - * ULTRAGETOPT_DOS_DASH Support - and -- options in ultragetopt*_dos() - * ULTRAGETOPT_BSD_ERRORS Print error messages matching BSD getopt - * ULTRAGETOPT_DARWIN_ERRORS Print error messages matching Darwin getopt - * ULTRAGETOPT_GNU_ERRORS Print error messages matching GNU getopt - * ULTRAGETOPT_NO_EATDASHDASH Do not increment optind when argv[optind] is -- - * ULTRAGETOPT_NO_OPTIONALARG Do not support GNU "::" optional argument - * Always supported in *_long*() - * ULTRAGETOPT_NO_OPTIONASSIGN Do not support --option=value syntax - */ - -#ifdef ULTRAGETOPT_LIKE_POSIX -# define ULTRAGETOPT_NO_OPTIONALARG -# define ULTRAGETOPT_NO_OPTIONASSIGN -# undef ULTRAGETOPT_NO_EATDASHDASH -# undef ULTRAGETOPT_ASSIGNSPACE -# undef ULTRAGETOPT_BSD_ERRORS -# undef ULTRAGETOPT_DARWIN_ERRORS -# undef ULTRAGETOPT_GNU_ERRORS -# undef ULTRAGETOPT_OPTIONPERMUTE -# undef ULTRAGETOPT_SHORTOPTASSIGN -#elif defined(ULTRAGETOPT_LIKE_GNU) -# define ULTRAGETOPT_GNU_ERRORS -# define ULTRAGETOPT_HYPHENARG -# define ULTRAGETOPT_OPTIONPERMUTE -# undef ULTRAGETOPT_ASSIGNSPACE -# undef ULTRAGETOPT_NO_OPTIONALARG -# undef ULTRAGETOPT_NO_OPTIONASSIGN -# undef ULTRAGETOPT_NO_EATDASHDASH -# undef ULTRAGETOPT_SHORTOPTASSIGN -# undef ULTRAGETOPT_SEPARATEOPTIONAL -# undef ULTRAGETOPT_LONGOPTADJACENT -#elif defined(ULTRAGETOPT_LIKE_BSD) -# define ULTRAGETOPT_BSD_ERRORS -# define ULTRAGETOPT_OPTIONPERMUTE -# define ULTRAGETOPT_DEFAULTOPTOPT '?' -# undef ULTRAGETOPT_ASSIGNSPACE -# undef ULTRAGETOPT_NO_OPTIONALARG -# undef ULTRAGETOPT_NO_OPTIONASSIGN -# undef ULTRAGETOPT_NO_EATDASHDASH -# undef ULTRAGETOPT_SHORTOPTASSIGN -# undef ULTRAGETOPT_SEPARATEOPTIONAL -# undef ULTRAGETOPT_LONGOPTADJACENT -#elif defined(ULTRAGETOPT_LIKE_DARWIN) -# define ULTRAGETOPT_DARWIN_ERRORS -# define ULTRAGETOPT_OPTIONPERMUTE -# undef ULTRAGETOPT_ASSIGNSPACE -# undef ULTRAGETOPT_NO_OPTIONALARG -# undef ULTRAGETOPT_NO_OPTIONASSIGN -# undef ULTRAGETOPT_SHORTOPTASSIGN -# undef ULTRAGETOPT_NO_EATDASHDASH -# undef ULTRAGETOPT_SEPARATEOPTIONAL -# undef ULTRAGETOPT_LONGOPTADJACENT -#endif - -#ifdef ULTRAGETOPT_NO_OPTIONASSIGN -static const char *const unixassigners = ""; -static const char *const dosassigners = ":"; -#elif defined(ULTRAGETOPT_OPTIONSPACE) -static const char *const unixassigners = "= "; -static const char *const dosassigners = ":= "; -#else -static const char *const unixassigners = "="; -static const char *const dosassigners = ":="; -#endif - -#ifdef ULTRAGETOPT_DOS_DASH -static const char *const unixleaders = "-"; -static const char *const dosleaders = "/-"; -#else -static const char *const unixleaders = "-"; -static const char *const dosleaders = "/"; -#endif - -/* Flags for all variants of ultragetopt*() */ -static const int getoptflags = 0 -#ifdef ULTRAGETOPT_SEPARATEDOPTIONAL - | UGO_SEPARATEDOPTIONAL -#endif -#ifdef ULTRAGETOPT_SHORTOPTASSIGN - | UGO_SHORTOPTASSIGN -#endif -#ifdef ULTRAGETOPT_NO_EATDASHDASH - | UGO_NOEATDASHDASH -#endif -#ifdef ULTRAGETOPT_HYPHENARG - | UGO_HYPHENARG -#endif - ; - -#ifdef ULTRAGETOPT_GNU_ERRORS -static const char *const errorarg = - "%s: option `%.*s' doesn't allow an argument\n"; -static const char *const errornoarg = - "%s: option `%.*s' requires an argument \n"; -static const char *const erroropt = - "%s: unrecognized option `%.*s'\n"; -static const char *const errorargc = - "%s: option `-%c' does not take an argument\n"; -static const char *const errornoargc = - "%s: option requires an argument -- `-%c'\n"; -static const char *const erroroptc = - "%s: invalid option -- %c\n"; -#elif defined(ULTRAGETOPT_BSD_ERRORS) -static const char *const errorarg = - "%s: option doesn't take an argument -- %.*s\n"; -static const char *const errornoarg = - "%s: option requires an argument -- %.*s\n"; -static const char *const erroropt = - "%s: unknown option -- %.*s\n"; -static const char *const errorargc = - "%s: option doesn't take an argument -- %c\n"; -static const char *const errornoargc = - "%s: option requires an argument -- %c\n"; -static const char *const erroroptc = - "%s: unknown option -- %c\n"; -#elif defined(ULTRAGETOPT_DARWIN_ERRORS) -static const char *const errorarg = - "%s: option `%.*s' doesn't allow an argument\n"; /* with -- */ -static const char *const errornoarg = - "%s: option `%.*s' requires an argument\n"; -static const char *const erroropt = - "%s: unrecognized option `%.*s'\n"; /* with -- */ -static const char *const errorargc = - "%s: option doesn't take an argument -- %c\n"; -static const char *const errornoargc = - "%s: option requires an argument -- %c\n"; -static const char *const erroroptc = - "%s: invalid option -- %c\n"; -#else /* POSIX-like */ -static const char *const errorarg = - "%s: option does not take an argument -- %.*s\n"; -static const char *const errornoarg = - "%s: option requires an argument -- %.*s\n"; -static const char *const erroropt = - "%s: illegal option -- %.*s\n"; -static const char *const errorargc = - "%s: option does not take an argument -- %c\n"; -static const char *const errornoargc = - "%s: option requires an argument -- %c\n"; -static const char *const erroroptc = - "%s: illegal option -- %c\n"; -#endif - -/* Globals to match optarg, optind, opterr, optopt, optreset */ -char *ultraoptarg = NULL; -int ultraoptind = 1; -int ultraopterr = 1; -int ultraoptreset = 0; -#ifdef ULTRAGETOPT_DEFAULTOPTOPT -int ultraoptopt = ULTRAGETOPT_DEFAULTOPTOPT -0; -#else -int ultraoptopt = 0; -#endif - -static int ultraoptnum = 0; /* How many options of the current multi-option - argument have been processed? (e.g. -vvv) */ - -/* Add format error checking for gcc versions that support it */ -#if defined(__GNUC__) && __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR > 6) -static void print_error(int flags, const char *template, ...) - __attribute__ ((format (printf, 2, 3))); -#endif - -/* Print errors only if not suppressed */ -static void print_error(int flags, const char *template, ...) -{ - va_list ap; - - va_start(ap, template); - if (ultraopterr != 0 && !(flags & UGO_NOPRINTERR)) - vfprintf(stderr, template, ap); - va_end(ap); -} - -/* Check if an argument string looks like an option string */ -static inline int like_option(const char *arg, const char *optleaders) -{ - return arg != NULL && - arg[0] != '\0' && /* >= 2 characters long */ - arg[1] != '\0' && - strchr(optleaders, arg[0]) && /* Starts with optleader */ - (arg[2] != '\0' || arg[0] != arg[1]); /* Not -- */ -} - -/* Check if an argument string looks like the option terminator string */ -static inline int like_optterm(const char *arg, const char *optleaders) -{ - return arg != NULL && - arg[0] != '\0' && - arg[1] != '\0' && - arg[2] == '\0' && - arg[0] == arg[1] && - strchr(optleaders, arg[0]); -} - -/* Check if an argument string looks like an option argument string */ -static inline int like_optarg(const char *arg, const char *optleaders, - int allow_option) -{ - return arg != NULL && - (allow_option || - (!like_option(arg, optleaders) && !like_optterm(arg, optleaders))); -} - -/* If argv[curopt] matches a long option, return the index of that option - * Otherwise, return -1 - * If it has an adjacent argument, return pointer to it in longarg, else NULL - */ -static int match_longopt(int curopt, char *const argv[], - const struct option *longopts, const char *assigners, - const char *optleaders, int flags, char **longarg) -{ - size_t alen, optnamelen = 0; - char *optname; - char *temp; - int i; - int (*optncmp)(const char *s1, const char *s2, size_t n); - - if (longarg == NULL) - longarg = &temp; - *longarg = NULL; - - if (flags & UGO_CASEINSENSITIVE) - optncmp = strncasecmp; - else - optncmp = strncmp; - - if (longopts == NULL) - return -1; - - if (!like_option(argv[curopt], optleaders)) - return -1; - - if (flags & UGO_SINGLELEADERONLY) { - optname = argv[curopt]+1; - } else if (!strchr(optleaders, argv[curopt][1])) { - /* Possible short option */ - if (flags & UGO_SINGLELEADERLONG) - optname = argv[curopt]+1; - else - return -1; - } else { - optname = argv[curopt]+2; - } - - /* Do first longest-match search if requested */ - if (flags & UGO_LONGOPTADJACENT) { - size_t matchlen = 0; - int matchind = -1; - for (i=0; longopts[i].name != NULL; i++) { - size_t longnamelen = strlen(longopts[i].name); - if (longnamelen > matchlen - && optncmp(optname, longopts[i].name, longnamelen) == 0) { - matchind = i; - matchlen = longnamelen; - } - } - - if (matchlen > 0) { - /* See if our match has an adjacent argument */ - if (optname[matchlen] != '\0') { - /* Strip assigner character if present */ - if (strchr(assigners, optname[matchlen])) - *longarg = optname+matchlen+1; - else - *longarg = optname+matchlen; - } - - return matchind; - } - - return -1; - } - - /* Check for assigner in the option */ - alen = strlen(assigners); - for (i=0; (unsigned)i < alen; i++) { - char *asn = strchr(optname, assigners[i]); - if (asn != NULL) { - optnamelen = asn - optname; - *longarg = asn+1; - break; - } - } - - if (optnamelen == 0) - optnamelen = strlen(optname); - - for (i=0; longopts[i].name != NULL; i++) - if (optncmp(optname, longopts[i].name, optnamelen) == 0 - && strlen(longopts[i].name) == optnamelen) - return i; - - return -1; -} - -/* Check if an option has a separate argument (in the following argv[] index) */ -static int has_separate_argument(int curopt, int argc, char *const argv[], - const char *shortopts, - const struct option *longopts, - const char *assigners, const char *optleaders, - int flags) -{ - int longind; - char *longarg; - - assert(curopt < argc && like_option(argv[curopt], optleaders)); - - /* Check if we have a long option */ - longind = match_longopt(ultraoptind, argv, longopts, assigners, optleaders, - flags, &longarg); - if (longind >= 0) { - if (longopts[longind].has_arg == no_argument - || longarg != NULL - || (longopts[longind].has_arg == optional_argument - && !(flags & UGO_SEPARATEDOPTIONAL))) - return 0; - - return like_optarg(argv[curopt+1], optleaders, - (flags & UGO_HYPHENARG) && - longopts[longind].has_arg == required_argument); - } else if (!strchr(optleaders, argv[curopt][1])) { - /* Short option */ - char *optpos; - - optpos = strchr(shortopts, argv[curopt][1]); - if ((flags & UGO_CASEINSENSITIVE) && optpos == NULL) { - if (islower(argv[curopt][1])) - optpos = strchr(shortopts, toupper(argv[curopt][1])); - else - optpos = strchr(shortopts, tolower(argv[curopt][1])); - } - - - return optpos != NULL /* Option found */ - && optpos[1] == ':' /* Option takes argument */ - && (optpos[2] != ':' || (flags & UGO_SEPARATEDOPTIONAL)) - && argv[curopt][2] == '\0' /* Argument is not adjacent */ - && like_optarg(argv[curopt+1], /* Is an argument */ - optleaders, - (flags & UGO_HYPHENARG) && optpos[2] != ':'); - } - - /* No match */ - return 0; -} - -/* Bring the next option, or terminator, up to ultraoptind if there is one - * Returns number of words shifted forward - */ -static int permute_options(int argc, char *argv[], const char *shortopts, - const struct option *longopts, - const char *assigners, const char *optleaders, - int flags) -{ - int curopt = ultraoptind; - - /* If we already have an option or no more possible, give up */ - if (curopt >= argc || like_option(argv[curopt], optleaders)) - return 0; - - for ( ; curopt < argc && argv[curopt]; curopt++) { - int shiftarg = 0; - int i; - - /* Permute options and the option terminator */ - if (like_option(argv[curopt], optleaders)) { - /* Check if we need to shift argument too */ - shiftarg = has_separate_argument(curopt, argc, argv, shortopts, - longopts, assigners, optleaders, - flags); - } else if (!like_optterm(argv[curopt], optleaders)) { - continue; - } - - /* Shift option */ - for (i=curopt; i>ultraoptind; i--) { - char *temp = argv[i]; - argv[i] = argv[i-1]; - argv[i-1] = temp; - - if (shiftarg) { - temp = argv[i+1]; - argv[i+1] = argv[i]; - argv[i] = temp; - } - } - - /* All done */ - if (shiftarg) - return 2; - else - return 1; - } - - /* Couldn't find an option, bummer */ - return 0; -} - -/* Handle a longopts[longind] matches argv[ultraoptind] actions */ -static int handle_longopt(int longind, char *longarg, int noseparg, - char *const argv[], - const struct option *longopts, int *indexptr, - const char *optleaders, int flags) -{ - /* Handle assignment arguments */ - if (longarg && longopts[longind].has_arg == no_argument) { - print_error(flags, errorarg, argv[0], - longarg-argv[ultraoptind]-1, argv[ultraoptind]); - /* TODO: What is a good value to put in ultraoptopt? */ - /* Looks like GNU getopt() uses val */ - ultraoptopt = longopts[longind].val; - ultraoptind++; - return '?'; - } else if (longarg) { - ultraoptind++; - ultraoptarg = longarg; - - if (indexptr) - *indexptr = longind; - - if (longopts[longind].flag) { - *(longopts[longind].flag) = longopts[longind].val; - return 0; - } else - return longopts[longind].val; - } - - /* Handle missing required argument */ - if (longopts[longind].has_arg == required_argument - && (noseparg - || !like_optarg(argv[ultraoptind+1], - optleaders, - flags & UGO_HYPHENARG))) { - print_error(flags, errornoarg, argv[0], - strlen(argv[ultraoptind]), argv[ultraoptind]); - ultraoptind++; - if (flags & UGO_MISSINGCOLON) - return ':'; - else - return '?'; - } - - /* Handle available argument */ - if ((longopts[longind].has_arg == required_argument - || (longopts[longind].has_arg == optional_argument - && (flags & UGO_SEPARATEDOPTIONAL))) - && !noseparg - && like_optarg(argv[ultraoptind+1], - optleaders, - (flags & UGO_HYPHENARG) && - longopts[longind].has_arg == required_argument)) { - ultraoptarg = argv[ultraoptind+1]; - ultraoptind += 2; - } else - ultraoptind++; - - if (indexptr) - *indexptr = longind; - - if (longopts[longind].flag) { - *(longopts[longind].flag) = longopts[longind].val; - return 0; - } else - return longopts[longind].val; -} - -int ultragetopt_tunable(int argc, char *const argv[], const char *shortopts, - const struct option *longopts, int *indexptr, - const char *assigners, const char *optleaders, - int flags) -{ - char *opt; /* Option we are processing */ - char *optpos; /* Pointer to opt in shortopts */ - int noseparg = 0; /* Force option not to have a separate argument */ - - if (ultraoptreset) { - ultraoptind = 1; - ultraopterr = 1; - ultraoptnum = 0; - ultraoptreset = 0; - } - - ultraoptarg = NULL; -#ifdef ULTRAGETOPT_DEFAULTOPTOPT - ultraoptopt = ULTRAGETOPT_DEFAULTOPTOPT -0; -#endif - - /* Sanity check (These are specified verbatim in SUS) */ - if (ultraoptind > argc - || argv[ultraoptind] == NULL) - return -1; - - /* No permuting when $POSIXLY_CORRECT is set (to match GNU getopt) */ - if (getenv("POSIXLY_CORRECT")) - flags &= ~UGO_OPTIONPERMUTE; - - /* Get flags from shortopts */ - for ( ; shortopts && *shortopts; shortopts++) { - if (*shortopts == '+') - flags &= ~UGO_OPTIONPERMUTE; - else if (*shortopts == '-') - flags |= UGO_NONOPTARG; - else if (*shortopts == ':') { - flags |= UGO_NOPRINTERR; - flags |= UGO_MISSINGCOLON; - } else - break; - } - - /* Found non-option */ - if (!like_option(argv[ultraoptind], optleaders)) { - int shifted; - - if (like_optterm(argv[ultraoptind], optleaders)) { - if (!(flags & UGO_NOEATDASHDASH)) - ultraoptind++; - - return -1; - } - - if (flags & UGO_NONOPTARG) { - ultraoptarg = argv[ultraoptind]; - ultraoptind++; - return 1; - } - - if (!(flags & UGO_OPTIONPERMUTE)) - return -1; - - shifted = permute_options(argc, (char **)argv, shortopts, longopts, - assigners, optleaders, flags); - if (shifted == 0) - return -1; - else if (shifted == 1) - noseparg = 1; - - if (like_optterm(argv[ultraoptind], optleaders)) { - if (!(flags & UGO_NOEATDASHDASH)) - ultraoptind++; - - return -1; - } - } - - /* At this point we must have an option of some sort */ - assert(like_option(argv[ultraoptind], optleaders)); - - /* Handle --* */ - if (argv[ultraoptind][0] == argv[ultraoptind][1]) { - int longind; - char *longarg; - - /* Handle long option */ - longind = match_longopt(ultraoptind, argv, longopts, assigners, - optleaders, flags, &longarg); - if (longind < 0) { - if (longarg == NULL) - print_error(flags, erroropt, argv[0], - strlen(argv[ultraoptind]), argv[ultraoptind]); - else - print_error(flags, erroropt, argv[0], - longarg - argv[ultraoptind] - 1, argv[ultraoptind]); - - /* TODO: What is a good value for optopt in this case? */ - /* Looks like BSD uses 0 */ - ultraoptopt = 0; - ultraoptind++; - return '?'; - } - - return handle_longopt(longind, longarg, noseparg, argv, - longopts, indexptr, optleaders, flags); - } - - /* See if it matches a long-only option */ - if (longopts != NULL && - ultraoptnum == 0 && - ((flags & UGO_SINGLELEADERLONG) || - (flags & UGO_SINGLELEADERONLY))) { - int longind; - char *longarg; - - longind = match_longopt(ultraoptind, argv, longopts, assigners, - optleaders, flags, &longarg); - - if (longind >= 0) - return handle_longopt(longind, longarg, noseparg, argv, - longopts, indexptr, optleaders, flags); - } - - /* No long matches, process short option */ - opt = argv[ultraoptind] + ultraoptnum + 1; - optpos = strchr(shortopts, opt[0]); - if (optpos == NULL && (flags & UGO_CASEINSENSITIVE)) { - if (islower(opt[0])) - optpos = strchr(shortopts, toupper(opt[0])); - else - optpos = strchr(shortopts, tolower(opt[0])); - } - - /* This could indicate ultraoptnum not being reset properly */ - assert(opt[0] != '\0'); - - /* Check for invalid or unrecognized option */ - if (optpos == NULL || opt[0] == ':') { - print_error(flags, erroroptc, argv[0], opt[0]); - - ultraoptopt = opt[0]; - if (opt[1] != '\0') - ultraoptnum++; - else { - ultraoptnum = 0; - ultraoptind++; - } - - return '?'; - } - - /* Handle arguments */ - if (optpos[1] == ':') { - ultraoptnum = 0; - - /* Handle adjacent arguments -ofile.txt */ - if (opt[1] != '\0') { - /* Skip over assignment character */ - if ((flags & UGO_SHORTOPTASSIGN) && strchr(assigners, opt[1])) - ultraoptarg = opt + 2; - else - ultraoptarg = opt + 1; - - ultraoptind++; - return optpos[0]; - } - - /* Handle optional argument not present */ - if ((flags & UGO_OPTIONALARG) /* accept optionals */ - && optpos[2] == ':' /* opt takes optional */ - && (argv[ultraoptind+1] == NULL /* optional doesn't exist */ - || !(flags & UGO_SEPARATEDOPTIONAL) /* separated not accepted */ - || like_option(argv[ultraoptind+1], optleaders))) { - ultraoptind++; - return optpos[0]; - } - - /* Handle separated argument missing */ - if (ultraoptind+2 > argc - || noseparg - || !like_optarg(argv[ultraoptind+1], - optleaders, - (flags & UGO_HYPHENARG))) { - ultraoptind++; - print_error(flags, errornoargc, argv[0], opt[0]); - - ultraoptopt = opt[0]; - if (flags & UGO_MISSINGCOLON) - return ':'; - else - return '?'; - } - - ultraoptind += 2; - ultraoptarg = argv[ultraoptind-1]; - return optpos[0]; - } - - /* Handle argumentless option with assigned option */ - if ((flags & UGO_SHORTOPTASSIGN) - && opt[1] != '\0' && strchr(assigners, opt[1])) { - print_error(flags, errorargc, argv[0], opt[0]); - ultraoptnum = 0; - ultraoptopt = opt[0]; - ultraoptind++; - return '?'; - } - - if (opt[1] != '\0') { - ultraoptnum++; - } else { - ultraoptnum = 0; - ultraoptind++; - } - - return optpos[0]; -} - -/* POSIX-compliant getopt - * - * Handles optional argument '::' specifier as an extension for compatibility - * with glibc - */ -int ultragetopt(int argc, char * const argv[], const char *optstring) -{ - int flags = getoptflags; - -#ifdef ULTRAGETOPT_OPTIONPERMUTE - flags |= UGO_OPTIONPERMUTE; -#endif - -#ifndef ULTRAGETOPT_NO_OPTIONALARG - flags |= UGO_OPTIONALARG; -#endif - - return ultragetopt_tunable(argc, argv, optstring, NULL, NULL, - unixassigners, unixleaders, flags); -} - -/* GNU getopt_long workalike - * - * Argument reordering not yet implemented - * Leading + and - under consideration (behavior violates POSIX...) - */ -int ultragetopt_long(int argc, char *const argv[], const char *shortopts, - const struct option *longopts, int *indexptr) -{ - return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr, - unixassigners, unixleaders, - getoptflags | UGO_OPTIONPERMUTE | UGO_OPTIONALARG); -} - -/* GNU getopt_long_only workalike */ -int ultragetopt_long_only(int argc, char *const argv[], const char *shortopts, - const struct option *longopts, int *indexptr) -{ - return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr, - unixassigners, unixleaders, - getoptflags | UGO_SINGLELEADERLONG - | UGO_OPTIONPERMUTE | UGO_OPTIONALARG); -} - -int ultragetopt_dos(int argc, char * const argv[], const char *optstring) -{ - return ultragetopt_tunable(argc, argv, optstring, NULL, NULL, - dosassigners, dosleaders, - getoptflags | UGO_CASEINSENSITIVE); -} - -int ultragetopt_long_dos(int argc, char *const argv[], const char *shortopts, - const struct option *longopts, int *indexptr) -{ - return ultragetopt_tunable(argc, argv, shortopts, longopts, indexptr, - dosassigners, dosleaders, - getoptflags | UGO_CASEINSENSITIVE - | UGO_SINGLELEADERLONG | UGO_SINGLELEADERONLY - | UGO_OPTIONPERMUTE | UGO_OPTIONALARG); -} - -/* vim:set sts=4 sw=4: */ diff --git a/ext/ultragetopt/ultragetopt.h b/ext/ultragetopt/ultragetopt.h deleted file mode 100644 index 2f8d359..0000000 --- a/ext/ultragetopt/ultragetopt.h +++ /dev/null @@ -1,105 +0,0 @@ - -/* define ULTRAGETOPT_REPLACE_GETOPT for ultragetopt*() to replace getopt*() */ -/* define ULTRAGETOPT_ONLY_DOS for ultragetopt*_dos() to replace ultragetopt*() */ - -#ifndef INCLUDED_GETOPT_H -#define INCLUDED_GETOPT_H 1 - -#ifdef __cplusplus -extern "C" { -#endif - -#define ULTRAGETOPT_REPLACE_GETOPT -#define ULTRAGETOPT_LIKE_GNU -#define ULTRAGETOPT_LINKAGE extern - -/* Flag values to pass to getopt_tunable() */ -#define UGO_CASEINSENSITIVE 0x1 -#define UGO_SINGLELEADERLONG 0x2 -#define UGO_OPTIONPERMUTE 0x4 -#define UGO_NONOPTARG 0x8 -#define UGO_NOPRINTERR 0x10 -#define UGO_OPTIONALARG 0x20 -#define UGO_MISSINGCOLON 0x40 -#define UGO_SEPARATEDOPTIONAL 0x80 -#define UGO_SHORTOPTASSIGN 0x100 -#define UGO_NOEATDASHDASH 0x200 -#define UGO_LONGOPTADJACENT 0x400 -#define UGO_HYPHENARG 0x800 -#define UGO_SINGLELEADERONLY 0x1000 - -#ifndef required_argument -# define no_argument 0 -# define required_argument 1 -# define optional_argument 2 - -struct option { - const char *name; /* Name of the option */ - int has_arg; /* Does the option take an argument? */ - int *flag; /* Location to store val when option encountered */ - int val; /* Value to return (or store in flag) for option */ -}; -#endif /* required_argument */ - -ULTRAGETOPT_LINKAGE char *ultraoptarg; -ULTRAGETOPT_LINKAGE int ultraoptind, ultraopterr, ultraoptopt, ultraoptreset; - -ULTRAGETOPT_LINKAGE int ultragetopt(int argc, char *const argv[], - const char *optstring); - -ULTRAGETOPT_LINKAGE int ultragetopt_long(int argc, char *const argv[], - const char *shortopts, const struct option *longopts, int *indexptr); - -ULTRAGETOPT_LINKAGE int ultragetopt_long_only(int argc, char *const argv[], - const char *shortopts, const struct option *longopts, int *indexptr); - -ULTRAGETOPT_LINKAGE int ultragetopt_dos(int argc, char * const argv[], - const char *optstring); - -ULTRAGETOPT_LINKAGE int ultragetopt_long_dos(int argc, char *const argv[], - const char *shortopts, const struct option *longopts, int *indexptr); - -/* Getopt with modifiable (tunable) behavior - also the backend for all other - * getopt functions. - * assigners - string of characters accepted to assign to an option - * (e.g. --outfile=file.txt where '=' is the assigner) - * optleaders - string of characters that indicate an option - * (usually "-" on UNIX, "/" on DOS) - * flags - see README for list of accepted flags - */ -ULTRAGETOPT_LINKAGE int ultragetopt_tunable(int argc, char *const argv[], - const char *shortopts, const struct option *longopts, int *indexptr, - const char *assigners, const char *optleaders, int flags); - -#ifdef ULTRAGETOPT_REPLACE_GETOPT -# define optarg ultraoptarg -# define optind ultraoptind -# define opterr ultraopterr -# define optopt ultraoptopt -# define optreset ultraoptreset -# define getopt(argc, argv, optstring) \ - ultragetopt(argc, argv, optstring) -# define getopt_long(argc, argv, shortopts, longopts, indexptr) \ - ultragetopt_long(argc, argv, shortopts, longopts, indexptr) -# define getopt_long_only(argc, argv, shortopts, longopts, indexptr) \ - ultragetopt_long_only(argc, argv, shortopts, longopts, indexptr) -# define getopt_dos(argc, argv, optstring) \ - ultragetopt_dos(argc, argv, optstring) -# define getopt_long_dos(argc, argv, shortopts, longopts, indexptr) \ - ultragetopt_long_dos(argc, argv, shortopts, longopts, indexptr) -#endif /* GETOPT_NO_EXTENSIONS */ - -#ifdef ULTRAGETOPT_DOS_ONLY -# define ultragetopt(argc, argv, optstring) \ - ultragetopt_dos(argc, argv, optstring) -# define ultragetopt_long(argc, argv, shortopts, longopts, indexptr) \ - ultragetopt_long_dos(argc, argv, shortopts, longopts, indexptr) -# define ultragetopt_long_only(argc, argv, shortopts, longopts, indexptr) \ - ultragetopt_long_dos(argc, argv, shortopts, longopts, indexptr) -#endif /* ULTRAGETOPT_DOS_ONLY */ - -#ifdef __cplusplus -} -#endif - -#endif /* INCLUDED_GETOPT_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ce6ecf2..e348053 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,15 @@ -add_executable(tmx2gba - tmx2gba.cpp +add_executable(tmx2gba + argparse.hpp argparse.cpp tmxlayer.hpp tmxobject.hpp tmxreader.hpp tmxreader.cpp - tmxtileset.hpp) + tmxtileset.hpp + tmx2gba.cpp) + target_link_libraries(tmx2gba External::base64 External::miniz - External::rapidxml - External::ultragetopt) + External::rapidxml) if (TMX2GBA_DKP_INSTALL) if (DEFINED ENV{DEVKITPRO}) diff --git a/src/argparse.cpp b/src/argparse.cpp new file mode 100644 index 0000000..8715a2e --- /dev/null +++ b/src/argparse.cpp @@ -0,0 +1,180 @@ +/* argparse.cpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */ + +#include "argparse.hpp" +#include +#include +#include + + +ArgParse::ArgParser::ArgParser( + const std::string_view argv0, + std::initializer_list