mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
rewrite argument parsing
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.12 FATAL_ERROR)
|
||||||
project(tmx2gba)
|
project(tmx2gba)
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF)
|
option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF)
|
||||||
option(ASAN "Enable address sanitiser" OFF)
|
option(ASAN "Enable address sanitiser" OFF)
|
||||||
|
|
||||||
# C++11 & C99
|
# C++20 & C99
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
|
||||||
# Enable strong warnings
|
# Enable strong warnings
|
||||||
@@ -26,7 +26,6 @@ endif()
|
|||||||
add_subdirectory(ext/base64)
|
add_subdirectory(ext/base64)
|
||||||
add_subdirectory(ext/miniz)
|
add_subdirectory(ext/miniz)
|
||||||
add_subdirectory(ext/rapidxml)
|
add_subdirectory(ext/rapidxml)
|
||||||
add_subdirectory(ext/ultragetopt)
|
|
||||||
|
|
||||||
# Main tmx2gba sources
|
# Main tmx2gba sources
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|||||||
@@ -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
|
This software is provided 'as-is', without any express or implied
|
||||||
warranty. In no event will the authors be held liable for any damages
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
|||||||
@@ -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
|
|
||||||
$<$<BOOL:${HAVE_STRINGS_H}>:HAVE_STRINGS_H=1>
|
|
||||||
$<$<BOOL:${HAVE_STRCASECMP}>:HAVE_STRCASECMP=1>
|
|
||||||
$<$<BOOL:${HAVE__STRICMP}>:HAVE__STRICMP=1>
|
|
||||||
$<$<BOOL:${HAVE_STRNCASECMP}>:HAVE_STRNCASECMP=1>
|
|
||||||
$<$<BOOL:${HAVE__STRNICMP}>:HAVE__STRNICMP=1>)
|
|
||||||
@@ -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 <kevin@kevinlocke.name>
|
|
||||||
*
|
|
||||||
* 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 <assert.h>
|
|
||||||
#include <ctype.h> /* islower() isupper() tolower() toupper() */
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h> /* fprintf() */
|
|
||||||
#include <stdlib.h> /* getenv() */
|
|
||||||
#include <string.h> /* strcmp(), strncmp(), strchr() */
|
|
||||||
|
|
||||||
#if HAVE_STRINGS_H
|
|
||||||
# include <strings.h> /* 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: */
|
|
||||||
@@ -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 */
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
add_executable(tmx2gba
|
add_executable(tmx2gba
|
||||||
tmx2gba.cpp
|
argparse.hpp argparse.cpp
|
||||||
tmxlayer.hpp
|
tmxlayer.hpp
|
||||||
tmxobject.hpp
|
tmxobject.hpp
|
||||||
tmxreader.hpp tmxreader.cpp
|
tmxreader.hpp tmxreader.cpp
|
||||||
tmxtileset.hpp)
|
tmxtileset.hpp
|
||||||
|
tmx2gba.cpp)
|
||||||
|
|
||||||
target_link_libraries(tmx2gba
|
target_link_libraries(tmx2gba
|
||||||
External::base64
|
External::base64
|
||||||
External::miniz
|
External::miniz
|
||||||
External::rapidxml
|
External::rapidxml)
|
||||||
External::ultragetopt)
|
|
||||||
|
|
||||||
if (TMX2GBA_DKP_INSTALL)
|
if (TMX2GBA_DKP_INSTALL)
|
||||||
if (DEFINED ENV{DEVKITPRO})
|
if (DEFINED ENV{DEVKITPRO})
|
||||||
|
|||||||
180
src/argparse.cpp
Normal file
180
src/argparse.cpp
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
/* argparse.cpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
||||||
|
|
||||||
|
#include "argparse.hpp"
|
||||||
|
#include <iomanip>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
|
||||||
|
ArgParse::ArgParser::ArgParser(
|
||||||
|
const std::string_view argv0,
|
||||||
|
std::initializer_list<Option> options,
|
||||||
|
HandleOption&& handler
|
||||||
|
) noexcept :
|
||||||
|
name(std::filesystem::path(argv0).filename().string()),
|
||||||
|
options(options),
|
||||||
|
handler(std::forward<HandleOption>(handler)) {}
|
||||||
|
|
||||||
|
|
||||||
|
void ArgParse::ArgParser::ShowShortUsage(std::ostream& out) const
|
||||||
|
{
|
||||||
|
out << "Usage: " << name;
|
||||||
|
for (const auto& it : options)
|
||||||
|
{
|
||||||
|
if (it.argumentName)
|
||||||
|
{
|
||||||
|
// Option with argument
|
||||||
|
it.required
|
||||||
|
? out << " <-" << it.flag << ' ' << it.argumentName << '>'
|
||||||
|
: out << " [-" << it.flag << ' ' << it.argumentName << ']';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Argument-less flag
|
||||||
|
it.required
|
||||||
|
? out << " <-" << it.flag << '>'
|
||||||
|
: out << " [-" << it.flag << ']';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ArgParse::ArgParser::ShowHelpUsage(std::ostream& out) const
|
||||||
|
{
|
||||||
|
// Base usage
|
||||||
|
out << "Usage: " << name << " [-";
|
||||||
|
for (const auto& it : options)
|
||||||
|
if (!it.required)
|
||||||
|
out << it.flag;
|
||||||
|
out << "] <-";
|
||||||
|
for (const auto& it : options)
|
||||||
|
if (it.required)
|
||||||
|
out << it.flag;
|
||||||
|
out << ">" << std::endl;
|
||||||
|
|
||||||
|
// Determine the alignment width from the longest argument
|
||||||
|
auto paramLength = [](const Option& p) -> int { return p.argumentName
|
||||||
|
? static_cast<int>(std::strlen(p.argumentName) + 3)
|
||||||
|
: 1; };
|
||||||
|
auto longestParam = std::max(options, [=](auto a, auto b) -> bool { return paramLength(a) < paramLength(b); });
|
||||||
|
auto alignWidth = paramLength(longestParam) + 3;
|
||||||
|
|
||||||
|
// print argument descriptions
|
||||||
|
for (const auto& it : options)
|
||||||
|
{
|
||||||
|
auto decorateArgument = [=] { return " <" + std::string(it.argumentName) + "> "; };
|
||||||
|
out << " -" << it.flag
|
||||||
|
<< std::left << std::setw(alignWidth) << std::setfill('-') << (it.argumentName ? decorateArgument() : " ")
|
||||||
|
<< " " << it.helpString << std::endl;
|
||||||
|
}
|
||||||
|
out << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ArgParse::ParseCtrl ArgParse::ParserState::Next(const std::string_view token)
|
||||||
|
{
|
||||||
|
auto getFlag = [](const std::string_view s) { return s[0] == '-' && s[1] ? std::optional<int>(s[1]) : std::nullopt; };
|
||||||
|
auto getOption = [&](int flag) -> std::optional<std::reference_wrapper<const Option>>
|
||||||
|
{
|
||||||
|
for (auto& opt : options)
|
||||||
|
if (opt.flag == flag)
|
||||||
|
return std::optional(std::cref(opt));
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (expectArg)
|
||||||
|
{
|
||||||
|
expectArg = false;
|
||||||
|
return handler(flagChar, token);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto flag = getFlag(token);
|
||||||
|
if (flag.has_value())
|
||||||
|
{
|
||||||
|
flagChar = flag.value();
|
||||||
|
const auto opt = getOption(flagChar);
|
||||||
|
if (opt.has_value())
|
||||||
|
{
|
||||||
|
bool expect = opt.value().get().argumentName != nullptr;
|
||||||
|
if (token.length() <= 2)
|
||||||
|
{
|
||||||
|
expectArg = expect;
|
||||||
|
if (!expectArg)
|
||||||
|
return handler(flagChar, "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return handler(flagChar, expect ? token.substr(2) : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!token.empty())
|
||||||
|
{
|
||||||
|
return ParseCtrl::QUIT_ERR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseCtrl::CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ReadParamFile(std::vector<std::string>& tokens, std::istream& file)
|
||||||
|
{
|
||||||
|
bool inQuote = false;
|
||||||
|
std::string quoteStr;
|
||||||
|
const auto store = [&](const std::string_view token, bool quote)
|
||||||
|
{
|
||||||
|
if (quote)
|
||||||
|
quoteStr = token;
|
||||||
|
else
|
||||||
|
tokens.emplace_back(token);
|
||||||
|
};
|
||||||
|
|
||||||
|
while (!file.eof())
|
||||||
|
{
|
||||||
|
if (!inQuote)
|
||||||
|
{
|
||||||
|
std::string token;
|
||||||
|
file >> token;
|
||||||
|
if (!token.empty())
|
||||||
|
{
|
||||||
|
std::string::size_type beg = 0, end;
|
||||||
|
while ((end = token.find_first_of('"', beg)) != std::string::npos)
|
||||||
|
{
|
||||||
|
auto size = end - beg;
|
||||||
|
if (size > 0)
|
||||||
|
store(token.substr(beg, size), !inQuote);
|
||||||
|
inQuote = !inQuote;
|
||||||
|
beg = end + 1;
|
||||||
|
}
|
||||||
|
if (beg > 0)
|
||||||
|
{
|
||||||
|
auto size = token.length() - beg;
|
||||||
|
if (size > 0)
|
||||||
|
token = token.substr(beg);
|
||||||
|
else
|
||||||
|
token.clear();
|
||||||
|
}
|
||||||
|
if (!token.empty())
|
||||||
|
store(token, inQuote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const int c = file.get();
|
||||||
|
if (c == '"')
|
||||||
|
{
|
||||||
|
tokens.emplace_back(quoteStr);
|
||||||
|
quoteStr.clear();
|
||||||
|
inQuote = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
quoteStr.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !inQuote;
|
||||||
|
}
|
||||||
110
src/argparse.hpp
Normal file
110
src/argparse.hpp
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
/* argparse.hpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
||||||
|
|
||||||
|
#ifndef ARGPARSE_HPP
|
||||||
|
#define ARGPARSE_HPP
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <ostream>
|
||||||
|
#include <span>
|
||||||
|
#include <ranges>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ArgParse
|
||||||
|
{
|
||||||
|
struct Option
|
||||||
|
{
|
||||||
|
char flag;
|
||||||
|
const char* argumentName;
|
||||||
|
bool required;
|
||||||
|
const char* helpString;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ParseCtrl
|
||||||
|
{
|
||||||
|
CONTINUE,
|
||||||
|
QUIT_EARLY,
|
||||||
|
QUIT_ERR_UNKNOWN,
|
||||||
|
QUIT_ERR_UNEXPECTED,
|
||||||
|
QUIT_ERR_EXPECTARG,
|
||||||
|
QUIT_ERR_INVALID,
|
||||||
|
QUIT_ERR_RANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ParseErr
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
OPT_UNKNOWN,
|
||||||
|
UNEXPECTED,
|
||||||
|
ARG_EXPECTED,
|
||||||
|
ARG_INVALID,
|
||||||
|
ARG_RANGE
|
||||||
|
};
|
||||||
|
|
||||||
|
using HandleOption = std::function<ParseCtrl(int, const std::string_view)>;
|
||||||
|
|
||||||
|
class ParserState
|
||||||
|
{
|
||||||
|
bool expectArg = false;
|
||||||
|
int flagChar;
|
||||||
|
HandleOption handler;
|
||||||
|
const std::initializer_list<Option>& options;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParserState(HandleOption handler, const std::initializer_list<Option>& options) noexcept
|
||||||
|
: handler(handler), options(options) {}
|
||||||
|
[[nodiscard]] bool ExpectingArg() const { return expectArg; }
|
||||||
|
[[nodiscard]] ParseCtrl Next(const std::string_view token);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ArgParser
|
||||||
|
{
|
||||||
|
const std::string name;
|
||||||
|
std::initializer_list<Option> options;
|
||||||
|
HandleOption handler;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ArgParser(const std::string_view argv0, std::initializer_list<Option> options, HandleOption&& handler) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] const std::string_view GetName() const { return name; }
|
||||||
|
|
||||||
|
void ShowShortUsage(std::ostream& out) const;
|
||||||
|
void ShowHelpUsage(std::ostream& out) const;
|
||||||
|
|
||||||
|
template <typename V>
|
||||||
|
ParseErr Parse(V args)
|
||||||
|
{
|
||||||
|
ParserState state(handler, options);
|
||||||
|
for (auto arg : args)
|
||||||
|
{
|
||||||
|
switch (state.Next(arg))
|
||||||
|
{
|
||||||
|
case ParseCtrl::CONTINUE: continue;
|
||||||
|
case ParseCtrl::QUIT_EARLY: return ParseErr::OK;
|
||||||
|
case ParseCtrl::QUIT_ERR_UNKNOWN: return ParseErr::OPT_UNKNOWN;
|
||||||
|
case ParseCtrl::QUIT_ERR_UNEXPECTED: return ParseErr::UNEXPECTED;
|
||||||
|
case ParseCtrl::QUIT_ERR_EXPECTARG: return ParseErr::ARG_EXPECTED;
|
||||||
|
case ParseCtrl::QUIT_ERR_INVALID: return ParseErr::ARG_INVALID;
|
||||||
|
case ParseCtrl::QUIT_ERR_RANGE: return ParseErr::ARG_RANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state.ExpectingArg() ? ParseErr::ARG_EXPECTED : ParseErr::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ParseErr Parse(std::initializer_list<std::string_view> args)
|
||||||
|
{
|
||||||
|
return Parse<std::initializer_list<std::string_view>>(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ParseErr Parse(std::span<char*> args)
|
||||||
|
{
|
||||||
|
return Parse(args | std::views::transform([](char const* v){ return std::string_view(v); }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
extern bool ReadParamFile(std::vector<std::string>& tokens, std::istream& file);
|
||||||
|
|
||||||
|
#endif//ARGPARSE_HPP
|
||||||
234
src/tmx2gba.cpp
234
src/tmx2gba.cpp
@@ -1,31 +1,20 @@
|
|||||||
/* tmx2gba.cpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
|
/* tmx2gba.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
||||||
|
|
||||||
|
#include "argparse.hpp"
|
||||||
#include "tmxreader.hpp"
|
#include "tmxreader.hpp"
|
||||||
#include "tmxlayer.hpp"
|
#include "tmxlayer.hpp"
|
||||||
#include "tmxobject.hpp"
|
#include "tmxobject.hpp"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ultragetopt.h>
|
|
||||||
|
|
||||||
|
|
||||||
const std::string helpUsage = "Usage: tmx2gba [-h] [-f file] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>";
|
using ArgParse::ArgParser;
|
||||||
const std::string helpShort = "Run 'tmx2gba -h' to view all available options.";
|
using ArgParse::ParseCtrl;
|
||||||
const std::string helpFull = R"(
|
using ArgParse::ParseErr;
|
||||||
-h ------------ Display this help & command info.
|
|
||||||
-l <name> ----- Name of layer to use (default first layer in TMX).
|
|
||||||
-y <name> ----- Layer for palette mappings.
|
|
||||||
-c <name> ----- Output a separate 8bit collision map of the specified layer.
|
|
||||||
-r <offset> --- Offset tile indices (default 0).
|
|
||||||
-p <0-15> ----- Select which palette to use for 4-bit tilesets.
|
|
||||||
-m <name;id> -- Map an object name to an ID, will enable object exports.
|
|
||||||
-i <path> ----- Path to input TMX file.
|
|
||||||
-o <path> ----- Path to output files.
|
|
||||||
-f <file> ----- Specify a file to use for flags, will override any options specified on the command line.)";
|
|
||||||
|
|
||||||
struct Arguments
|
struct Arguments
|
||||||
{
|
{
|
||||||
@@ -36,68 +25,127 @@ struct Arguments
|
|||||||
int offset = 0;
|
int offset = 0;
|
||||||
int palette = 0;
|
int palette = 0;
|
||||||
std::vector<std::string> objMappings;
|
std::vector<std::string> objMappings;
|
||||||
bool objExport = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void ParseArgs(int argc, char** argv, Arguments& p)
|
static void DisplayError(const ArgParser& parser, const std::string& message, bool helpPrompt = true)
|
||||||
{
|
{
|
||||||
int opt;
|
std::cerr << parser.GetName() << ": " << message << std::endl;
|
||||||
optreset = 1;
|
parser.ShowShortUsage(std::cerr);
|
||||||
while ((opt = getopt(argc, argv, "hr:l:c:p:y:m:i:o:f:")) > 0)
|
if (helpPrompt)
|
||||||
|
std::cerr << "Run '" << parser.GetName() << " -h' to view all available options." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CheckParse(const ArgParser& parser, ParseErr err)
|
||||||
|
{
|
||||||
|
switch (err)
|
||||||
{
|
{
|
||||||
switch (opt)
|
case ParseErr::OK:
|
||||||
{
|
return true;
|
||||||
case ('h'):
|
case ParseErr::OPT_UNKNOWN:
|
||||||
p.help = true;
|
DisplayError(parser, "Unrecognised option.");
|
||||||
return;
|
return false;
|
||||||
|
case ParseErr::UNEXPECTED:
|
||||||
case ('l'): p.layer = optarg; break;
|
DisplayError(parser, "Unexpected token.");
|
||||||
case ('c'): p.collisionlay = optarg; break;
|
return false;
|
||||||
case ('y'): p.paletteLay = optarg; break;
|
case ParseErr::ARG_EXPECTED:
|
||||||
case ('r'): p.offset = std::stoi(optarg); break;
|
DisplayError(parser, "Requires an argument.");
|
||||||
case ('p'): p.palette = std::stoi(optarg); break;
|
return false;
|
||||||
|
case ParseErr::ARG_INVALID:
|
||||||
case ('m'):
|
DisplayError(parser, "Invalid argument.", false);
|
||||||
p.objExport = true;
|
return false;
|
||||||
p.objMappings.emplace_back(optarg);
|
case ParseErr::ARG_RANGE:
|
||||||
break;
|
DisplayError(parser, "Argument out of range.", false);
|
||||||
|
return false;
|
||||||
case ('i'): p.inPath = optarg; break;
|
|
||||||
case ('o'): p.outPath = optarg; break;
|
|
||||||
case ('f'): p.flagFile = optarg; break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckArgs(const Arguments& params)
|
bool ParseArgs(int argc, char** argv, Arguments& params)
|
||||||
{
|
{
|
||||||
|
auto parser = ArgParser(argv[0], {
|
||||||
|
{ 'h', nullptr, false, "Display this help & command info" },
|
||||||
|
{ 'l', "name", false, "Name of layer to use (default first layer in TMX)" },
|
||||||
|
{ 'y', "name", false, "Layer for palette mappings" },
|
||||||
|
{ 'c', "name", false, "Output a separate 8bit collision map of the specified layer" },
|
||||||
|
{ 'r', "offset", false, "Offset tile indices (default 0)" },
|
||||||
|
{ 'p', "0-15", false, "Select which palette to use for 4-bit tilesets" },
|
||||||
|
{ 'm', "name;id", false, "Map an object name to an ID, will enable object exports" },
|
||||||
|
{ 'i', "inpath", true, "Path to input TMX file" },
|
||||||
|
{ 'o', "outpath", true, "Path to output files" },
|
||||||
|
{ 'f', "file", false, "Specify a file to use for flags, will override any options"
|
||||||
|
" specified on the command line" }
|
||||||
|
}, [&](int opt, const std::string_view arg) -> ParseCtrl
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
case 'h': params.help = true; return ParseCtrl::QUIT_EARLY;
|
||||||
|
case 'l': params.layer = arg; return ParseCtrl::CONTINUE;
|
||||||
|
case 'c': params.collisionlay = arg; return ParseCtrl::CONTINUE;
|
||||||
|
case 'y': params.paletteLay = arg; return ParseCtrl::CONTINUE;
|
||||||
|
case 'r': params.offset = std::stoi(std::string(arg)); return ParseCtrl::CONTINUE;
|
||||||
|
case 'p': params.palette = std::stoi(std::string(arg)); return ParseCtrl::CONTINUE;
|
||||||
|
case 'm': params.objMappings.emplace_back(arg); return ParseCtrl::CONTINUE;
|
||||||
|
case 'i': params.inPath = arg; return ParseCtrl::CONTINUE;
|
||||||
|
case 'o': params.outPath = arg; return ParseCtrl::CONTINUE;
|
||||||
|
case 'f': params.flagFile = arg; return ParseCtrl::CONTINUE;
|
||||||
|
|
||||||
|
default: return ParseCtrl::QUIT_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::invalid_argument const& e) { return ParseCtrl::QUIT_ERR_INVALID; }
|
||||||
|
catch (std::out_of_range const& e) { return ParseCtrl::QUIT_ERR_RANGE; }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!CheckParse(parser, parser.Parse(std::span(argv + 1, argc - 1))))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (params.help)
|
||||||
|
{
|
||||||
|
parser.ShowHelpUsage(std::cout);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.flagFile.empty())
|
||||||
|
{
|
||||||
|
std::ifstream paramFile(params.flagFile);
|
||||||
|
if (!paramFile.is_open())
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to open param file." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> tokens;
|
||||||
|
if (!ReadParamFile(tokens, paramFile))
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to read param file: Unterminated quote string." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CheckParse(parser, parser.Parse(tokens)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check my paranoia
|
// Check my paranoia
|
||||||
if (params.inPath.empty())
|
if (params.inPath.empty())
|
||||||
{
|
{
|
||||||
std::cerr << "No input file specified." << std::endl;
|
DisplayError(parser, "No input file specified.");
|
||||||
std::cout << helpUsage << std::endl << helpShort << std::endl;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (params.outPath.empty())
|
if (params.outPath.empty())
|
||||||
{
|
{
|
||||||
std::cerr << "No output file specified." << std::endl;
|
DisplayError(parser, "No output file specified.");
|
||||||
std::cout << helpUsage << std::endl << helpShort << std::endl;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (params.palette < 0 || params.palette > 15)
|
if (params.palette < 0 || params.palette > 15)
|
||||||
{
|
{
|
||||||
std::cerr << "Invalid palette index." << std::endl;
|
DisplayError(parser, "Invalid palette index.");
|
||||||
std::cout << helpUsage << std::endl << helpShort << std::endl;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template <typename T> constexpr const char* DatType();
|
template <typename T> constexpr const char* DatType();
|
||||||
template <> constexpr const char* DatType<uint8_t>() { return ".byte"; }
|
template <> constexpr const char* DatType<uint8_t>() { return ".byte"; }
|
||||||
template <> constexpr const char* DatType<uint16_t>() { return ".hword"; }
|
template <> constexpr const char* DatType<uint16_t>() { return ".hword"; }
|
||||||
@@ -139,84 +187,14 @@ void WriteArray(std::ofstream& aOut, const std::vector<T>& aDat, int aPerCol = 1
|
|||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Arguments p;
|
Arguments p;
|
||||||
ParseArgs(argc, argv, p);
|
if (!ParseArgs(argc, argv, p))
|
||||||
|
return 1;
|
||||||
if (p.help)
|
if (p.help)
|
||||||
{
|
|
||||||
std::cout << helpUsage << std::endl << helpFull << std::endl;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (!p.flagFile.empty())
|
|
||||||
{
|
|
||||||
std::ifstream paramFile(p.flagFile);
|
|
||||||
if (!paramFile.is_open())
|
|
||||||
{
|
|
||||||
std::cerr << "Failed to open param file." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> fileArgTokens;
|
|
||||||
fileArgTokens.push_back("auu~~");
|
|
||||||
bool carry = false;
|
|
||||||
std::string rawToken;
|
|
||||||
while (!paramFile.eof())
|
|
||||||
{
|
|
||||||
if (carry)
|
|
||||||
{
|
|
||||||
std::string tmp;
|
|
||||||
paramFile >> tmp;
|
|
||||||
rawToken += " ";
|
|
||||||
rawToken += tmp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rawToken.clear();
|
|
||||||
paramFile >> rawToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawToken.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool qFr = rawToken[0] == '"';
|
|
||||||
bool qBk = rawToken[rawToken.length() - 1] == '"';
|
|
||||||
if (qFr && qBk)
|
|
||||||
{
|
|
||||||
fileArgTokens.push_back(rawToken.substr(1, rawToken.length() - 2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (qFr)
|
|
||||||
{
|
|
||||||
fileArgTokens.push_back(rawToken.substr(1, rawToken.length() - 1));
|
|
||||||
carry = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
if (qBk)
|
|
||||||
{
|
|
||||||
fileArgTokens.push_back(rawToken.substr(0, rawToken.length() - 1));
|
|
||||||
carry = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fileArgTokens.push_back(rawToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<const char*> fileArgs;
|
|
||||||
fileArgs.reserve(fileArgTokens.size());
|
|
||||||
for (const auto& token : fileArgTokens)
|
|
||||||
fileArgs.push_back(token.c_str());
|
|
||||||
fileArgs.push_back(nullptr);
|
|
||||||
|
|
||||||
ParseArgs(static_cast<int>(fileArgs.size()) - 1, (char**)fileArgs.data(), p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CheckArgs(p))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
// Object mappings
|
// Object mappings
|
||||||
std::map<std::string, uint32_t> objMapping;
|
std::map<std::string, uint32_t> objMapping;
|
||||||
if (p.objExport)
|
if (!p.objMappings.empty())
|
||||||
{
|
{
|
||||||
for (const auto& objToken : p.objMappings)
|
for (const auto& objToken : p.objMappings)
|
||||||
{
|
{
|
||||||
@@ -375,7 +353,7 @@ int main(int argc, char** argv)
|
|||||||
foutS << std::endl;
|
foutS << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p.objExport)
|
if (!p.objMappings.empty())
|
||||||
{
|
{
|
||||||
std::vector<uint32_t> objDat;
|
std::vector<uint32_t> objDat;
|
||||||
for (size_t i = 0; i < tmx.GetObjectCount(); ++i)
|
for (size_t i = 0; i < tmx.GetObjectCount(); ++i)
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ void TmxReader::ReadTileset(rapidxml::xml_node<>* aXNode)
|
|||||||
// Read first global ID
|
// Read first global ID
|
||||||
xAttrib = aXNode->first_attribute("firstgid");
|
xAttrib = aXNode->first_attribute("firstgid");
|
||||||
if (xAttrib != nullptr)
|
if (xAttrib != nullptr)
|
||||||
firstGid = std::stoul(xAttrib->value());
|
firstGid = static_cast<uint32_t>(std::stoul(xAttrib->value()));
|
||||||
|
|
||||||
mTilesets.push_back(new TmxTileset(name, source, firstGid));
|
mTilesets.push_back(new TmxTileset(name, source, firstGid));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user