Replace getopt/ketopt with parg

master
Unknown 2019-02-17 04:22:12 +01:00
parent 0fed381201
commit 456cfb7f0f
5 changed files with 767 additions and 349 deletions

View File

@ -45,7 +45,8 @@ set(sources
db-redis.h
db-sqlite3.cpp
db-sqlite3.h
ketopt.h
parg.h
parg.c
)
if(WIN32)
@ -64,6 +65,7 @@ if(WIN32)
include_directories(${SYSTEM_INCLUDE_DIR})
endif()
set(leveldb_lib "")
if(USE_LEVELDB)
set(leveldb_lib ${LEVELDB_LIBRARY})
@ -88,6 +90,7 @@ if(USE_REDIS)
endif()
#link_directories (../wingetopt)
# Fügen Sie der ausführbaren Datei für dieses Projekt eine Quelle hinzu.
add_executable (Minetestmapper ${sources})
set_target_properties(Minetestmapper PROPERTIES COMPILE_FLAGS -DBUILDER_STATIC_DEFINE)

View File

@ -29,7 +29,7 @@
#include "porting.h"
#include "util.h"
#include "version.h"
#include "ketopt.h"
#include "parg.h"
using namespace std;
@ -50,80 +50,82 @@ int Mapper::start(int argc, char *argv[]) {
auto begin = std::chrono::high_resolution_clock::now();
// TODO: Get rid of getopt and use a proper cmdarg parsing lib or write something own.
static ko_longopt_t long_options[] =
static struct parg_option long_options[] =
{
{ "help", ko_no_argument, 'h' },
{ "version", ko_no_argument, 'V' },
{ "input", ko_required_argument, 'i' },
{ "output", ko_required_argument, 'o' },
{ "colors", ko_required_argument, 'C' },
{ "heightmap-nodes", ko_required_argument, OPT_HEIGHTMAPNODESFILE },
{ "heightmap-colors", ko_required_argument, OPT_HEIGHTMAPCOLORSFILE },
{ "heightmap", ko_optional_argument, OPT_HEIGHTMAP },
{ "heightmap-yscale", ko_required_argument, OPT_HEIGHTMAPYSCALE },
{ "height-level-0", ko_required_argument, OPT_HEIGHT_LEVEL0 },
{ "bgcolor", ko_required_argument, 'b' },
{ "blockcolor", ko_required_argument, OPT_BLOCKCOLOR },
{ "scalecolor", ko_required_argument, 's' },
{ "origincolor", ko_required_argument, 'r' },
{ "playercolor", ko_required_argument, 'p' },
{ "draworigin", ko_no_argument, 'R' },
{ "drawplayers", ko_no_argument, 'P' },
{ "drawscale", ko_optional_argument, 'S' },
{ "sidescale-interval", ko_required_argument, OPT_SCALEINTERVAL },
{ "drawheightscale", ko_no_argument, OPT_DRAWHEIGHTSCALE },
{ "heightscale-interval", ko_required_argument, OPT_SCALEINTERVAL },
{ "drawalpha", ko_optional_argument, 'e' },
{ "drawair", ko_no_argument, OPT_DRAWAIR },
{ "drawnodes", ko_required_argument, OPT_DRAWNODES },
{ "ignorenodes", ko_required_argument, OPT_DRAWNODES },
{ "drawpoint", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawline", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawcircle", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawellipse", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawrectangle", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawarrow", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawtext", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmappoint", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmapline", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmapcircle", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmapellipse", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmaprectangle", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmaparrow", ko_required_argument, OPT_DRAW_OBJECT },
{ "drawmaptext", ko_required_argument, OPT_DRAW_OBJECT },
{ "noshading", ko_no_argument, 'H' },
{ "geometry", ko_required_argument, 'g' },
{ "cornergeometry", ko_required_argument, 'g' },
{ "centergeometry", ko_required_argument, 'g' },
{ "geometrymode", ko_required_argument, 'G' },
{ "forcegeometry", ko_no_argument, 'G' },
{ "min-y", ko_required_argument, 'a' },
{ "max-y", ko_required_argument, 'c' },
{ "backend", ko_required_argument, 'd' },
{ "disable-blocklist-prefetch", ko_optional_argument, OPT_NO_BLOCKLIST_PREFETCH },
{ "database-format", ko_required_argument, OPT_DATABASE_FORMAT },
{ "prescan-world", ko_required_argument, OPT_PRESCAN_WORLD },
{ "sqlite-cacheworldrow", ko_no_argument, OPT_SQLITE_CACHEWORLDROW },
{ "sqlite3-limit-prescan-query-size", ko_optional_argument, OPT_SQLITE_LIMIT_PRESCAN_QUERY },
{ "tiles", ko_required_argument, 't' },
{ "tileorigin", ko_required_argument, 'T' },
{ "tilecenter", ko_required_argument, 'T' },
{ "tilebordercolor", ko_required_argument, 'B' },
{ "scalefactor", ko_required_argument, OPT_SCALEFACTOR },
{ "chunksize", ko_required_argument, OPT_CHUNKSIZE },
{ "silence-suggestions", ko_required_argument, OPT_SILENCE_SUGGESTIONS },
{ "verbose", ko_optional_argument, 'v' },
{ "verbose-search-colors", ko_optional_argument, OPT_VERBOSE_SEARCH_COLORS },
{ "progress", ko_no_argument, OPT_PROGRESS_INDICATOR },
{ nullptr, 0, 0 }
{ "help", PARG_NOARG, nullptr, 'h' },
{ "version", PARG_NOARG, nullptr, 'V' },
{ "input", PARG_REQARG, nullptr, 'i' },
{ "output", PARG_REQARG, nullptr, 'o' },
{ "colors", PARG_REQARG, nullptr, 'C' },
{ "heightmap-nodes", PARG_REQARG, nullptr, OPT_HEIGHTMAPNODESFILE },
{ "heightmap-colors", PARG_REQARG, nullptr, OPT_HEIGHTMAPCOLORSFILE },
{ "heightmap", PARG_OPTARG, nullptr, OPT_HEIGHTMAP },
{ "heightmap-yscale", PARG_REQARG, nullptr, OPT_HEIGHTMAPYSCALE },
{ "height-level-0", PARG_REQARG, nullptr, OPT_HEIGHT_LEVEL0 },
{ "bgcolor", PARG_REQARG, nullptr, 'b' },
{ "blockcolor", PARG_REQARG, nullptr, OPT_BLOCKCOLOR },
{ "scalecolor", PARG_REQARG, nullptr, 's' },
{ "origincolor", PARG_REQARG, nullptr, 'r' },
{ "playercolor", PARG_REQARG, nullptr, 'p' },
{ "draworigin", PARG_NOARG, nullptr, 'R' },
{ "drawplayers", PARG_NOARG, nullptr, 'P' },
{ "drawscale", PARG_OPTARG, nullptr, 'S' },
{ "sidescale-interval", PARG_REQARG, nullptr, OPT_SCALEINTERVAL },
{ "drawheightscale", PARG_NOARG, nullptr, OPT_DRAWHEIGHTSCALE },
{ "heightscale-interval", PARG_REQARG, nullptr, OPT_SCALEINTERVAL },
{ "drawalpha", PARG_OPTARG, nullptr, 'e' },
{ "drawair", PARG_NOARG, nullptr, OPT_DRAWAIR },
{ "drawnodes", PARG_REQARG, nullptr, OPT_DRAWNODES },
{ "ignorenodes", PARG_REQARG, nullptr, OPT_DRAWNODES },
{ "drawpoint", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawline", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawcircle", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawellipse", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawrectangle", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawarrow", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawtext", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmappoint", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmapline", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmapcircle", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmapellipse", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmaprectangle", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmaparrow", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "drawmaptext", PARG_REQARG, nullptr, OPT_DRAW_OBJECT },
{ "noshading", PARG_NOARG, nullptr, 'H' },
{ "geometry", PARG_REQARG, nullptr, 'g' },
{ "cornergeometry", PARG_REQARG, nullptr, 'g' },
{ "centergeometry", PARG_REQARG, nullptr, 'g' },
{ "geometrymode", PARG_REQARG, nullptr, 'G' },
{ "forcegeometry", PARG_NOARG, nullptr, 'G' },
{ "min-y", PARG_REQARG, nullptr, 'a' },
{ "max-y", PARG_REQARG, nullptr, 'c' },
{ "backend", PARG_REQARG, nullptr, 'd' },
{ "disable-blocklist-prefetch", PARG_OPTARG, nullptr, OPT_NO_BLOCKLIST_PREFETCH },
{ "database-format", PARG_REQARG, nullptr, OPT_DATABASE_FORMAT },
{ "prescan-world", PARG_REQARG, nullptr, OPT_PRESCAN_WORLD },
{ "sqlite-cacheworldrow", PARG_NOARG, nullptr, OPT_SQLITE_CACHEWORLDROW },
{ "sqlite3-limit-prescan-query-size", PARG_OPTARG, nullptr, OPT_SQLITE_LIMIT_PRESCAN_QUERY },
{ "tiles", PARG_REQARG, nullptr, 't' },
{ "tileorigin", PARG_REQARG, nullptr, 'T' },
{ "tilecenter", PARG_REQARG, nullptr, 'T' },
{ "tilebordercolor", PARG_REQARG, nullptr, 'B' },
{ "scalefactor", PARG_REQARG, nullptr, OPT_SCALEFACTOR },
{ "chunksize", PARG_REQARG, nullptr, OPT_CHUNKSIZE },
{ "silence-suggestions", PARG_REQARG, nullptr, OPT_SILENCE_SUGGESTIONS },
{ "verbose", PARG_OPTARG, nullptr, 'v' },
{ "verbose-search-colors", PARG_OPTARG, nullptr, OPT_VERBOSE_SEARCH_COLORS },
{ "progress", PARG_NOARG, nullptr, OPT_PROGRESS_INDICATOR },
{ nullptr, 0, nullptr, 0 }
};
try {
//int option_index = 0;
ketopt_t opt = KETOPT_INIT;
int option_index = 0;
int c = 0;
struct parg_state ps;
parg_init(&ps);
while (true) {
c = ketopt(&opt, argc, argv, 0, "hi:o:", long_options);
c = parg_getopt_long(&ps, argc, argv, "hi:o:", long_options, &option_index);
if (c == -1) {
if (input.empty() || output.empty()) {
std::cerr << "Input (world directory) or output (PNG filename) missing" << std::endl;
@ -134,19 +136,21 @@ int Mapper::start(int argc, char *argv[]) {
}
switch (c) {
case '?':
if (opt.opt == 0) // Unknown long option
std::cerr << "Unknown Option: '" << argv[opt.ind - 1] << "'." << std::endl;
else
std::cerr << "Unknown Option: '-" << static_cast<char>(opt.opt) << "'." << std::endl;
usage();
if (option_index) {
cout << "Comandline Error: Option '--" << long_options[option_index].name << "' does not take an argument: "
<< argv[ps.optind - 1] << endl;
}
else {
cout << "Comandline Error: Unknown option '" << argv[ps.optind - 1]<< "'" << endl;
}
return EXIT_FAILURE;
break;
case 1:
cout << "Comandline Error: Not a option '" << argv[ps.optind - 1] <<"'"<< endl;
return EXIT_FAILURE;
break;
case ':':
if (opt.longidx != -1)
std::cerr << "Missing Argument: --'" << long_options[opt.longidx].name << "'." << std::endl;
else
std::cerr << "Missing Argument: -'" << opt.opt << "'." << std::endl;
usage();
cout << "Comandline Error: Option does not take an argument '" << argv[ps.optind - 1]<< "'" << endl;
return EXIT_FAILURE;
break;
case 'h':
@ -158,29 +162,29 @@ int Mapper::start(int argc, char *argv[]) {
return 0;
break;
case 'i':
input = opt.arg;
input = ps.optarg;
break;
case 'o':
output = opt.arg;
output = ps.optarg;
break;
case 'C':
nodeColorsFile = opt.arg;
nodeColorsFile = ps.optarg;
break;
case OPT_HEIGHTMAPNODESFILE:
heightMapNodesFile = opt.arg;
heightMapNodesFile = ps.optarg;
break;
case OPT_HEIGHTMAPCOLORSFILE:
heightMapColorsFile = opt.arg;
heightMapColorsFile = ps.optarg;
break;
case 'b':
generator.setBgColor(Color(opt.arg, 0));
generator.setBgColor(Color(ps.optarg, 0));
break;
case OPT_NO_BLOCKLIST_PREFETCH:
if (opt.arg && *opt.arg) {
if (strlower(opt.arg) == "force")
if (ps.optarg && *ps.optarg) {
if (strlower(ps.optarg) == "force")
generator.setGenerateNoPrefetch(TileGenerator::BlockListPrefetch::NoPrefetchForced);
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "'; expected 'force' or nothing." << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "'; expected 'force' or nothing." << std::endl;
usage();
return EXIT_FAILURE;
}
@ -190,54 +194,54 @@ int Mapper::start(int argc, char *argv[]) {
}
break;
case OPT_DATABASE_FORMAT: {
std::string option = strlower(opt.arg);
if (option == "minetest-i64")
std::string opt = strlower(ps.optarg);
if (opt == "minetest-i64")
generator.setDBFormat(BlockPos::I64, false);
else if (option == "freeminer-axyz")
else if (opt == "freeminer-axyz")
generator.setDBFormat(BlockPos::AXYZ, false);
else if (option == "mixed")
else if (opt == "mixed")
generator.setDBFormat(BlockPos::Unknown, false);
else if (option == "query")
else if (opt == "query")
generator.setDBFormat(BlockPos::Unknown, true);
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
}
break;
break;
case OPT_PRESCAN_WORLD: {
std::string option = strlower(opt.arg);
std::string opt = strlower(ps.optarg);
generator.setGenerateNoPrefetch(TileGenerator::BlockListPrefetch::Prefetch);
if (option == "disabled-force")
if (opt == "disabled-force")
generator.setGenerateNoPrefetch(TileGenerator::BlockListPrefetch::NoPrefetchForced);
else if (option == "disabled")
else if (opt == "disabled")
generator.setGenerateNoPrefetch(TileGenerator::BlockListPrefetch::NoPrefetch);
else if (option == "auto")
else if (opt == "auto")
generator.setScanEntireWorld(false);
else if (option == "full")
else if (opt == "full")
generator.setScanEntireWorld(true);
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
}
break;
break;
case OPT_SQLITE_LIMIT_PRESCAN_QUERY:
if (!opt.arg || !*opt.arg) {
if (!ps.optarg || !*ps.optarg) {
#ifdef USE_SQLITE3
DBSQLite3::setLimitBlockListQuerySize();
#endif
}
else {
if (!isdigit(opt.arg[0])) {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': must be a positive number" << std::endl;
if (!isdigit(ps.optarg[0])) {
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': must be a positive number" << std::endl;
usage();
return EXIT_FAILURE;
}
#ifdef USE_SQLITE3
int size = atoi(opt.arg);
int size = atoi(ps.optarg);
DBSQLite3::setLimitBlockListQuerySize(size);
#endif
}
@ -245,9 +249,9 @@ int Mapper::start(int argc, char *argv[]) {
case OPT_HEIGHTMAP:
generator.setHeightMap(true);
heightMap = true;
if (opt.arg && *opt.arg) {
if (ps.optarg && *ps.optarg) {
loadHeightMapColorsFile = false;
std::string color = strlower(opt.arg);
std::string color = strlower(ps.optarg);
if (color == "grey" || color == "gray")
generator.setHeightMapColor(Color(0, 0, 0), Color(255, 255, 255));
else if (color == "black")
@ -263,41 +267,41 @@ int Mapper::start(int argc, char *argv[]) {
}
break;
case OPT_HEIGHTMAPYSCALE:
if (isdigit(opt.arg[0]) || ((opt.arg[0] == '-' || opt.arg[0] == '+') && isdigit(opt.arg[1]))) {
float scale = static_cast<float>(atof(opt.arg));
if (isdigit(ps.optarg[0]) || ((ps.optarg[0] == '-' || ps.optarg[0] == '+') && isdigit(ps.optarg[1]))) {
float scale = static_cast<float>(atof(ps.optarg));
generator.setHeightMapYScale(scale);
}
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
break;
case OPT_HEIGHT_LEVEL0:
if (isdigit(opt.arg[0]) || ((opt.arg[0] == '-' || opt.arg[0] == '+') && isdigit(opt.arg[1]))) {
int level = atoi(opt.arg);
if (isdigit(ps.optarg[0]) || ((ps.optarg[0] == '-' || ps.optarg[0] == '+') && isdigit(ps.optarg[1]))) {
int level = atoi(ps.optarg);
generator.setSeaLevel(level);
}
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
break;
case OPT_BLOCKCOLOR:
generator.setBlockDefaultColor(Color(opt.arg, 0));
generator.setBlockDefaultColor(Color(ps.optarg, 0));
break;
case 's':
generator.setScaleColor(Color(opt.arg, 0));
generator.setScaleColor(Color(ps.optarg, 0));
break;
case 'r':
generator.setOriginColor(Color(opt.arg, 1));
generator.setOriginColor(Color(ps.optarg, 1));
break;
case 'p':
generator.setPlayerColor(Color(opt.arg, 1));
generator.setPlayerColor(Color(ps.optarg, 1));
break;
case 'B':
generator.setTileBorderColor(Color(opt.arg, 0));
generator.setTileBorderColor(Color(ps.optarg, 0));
break;
case 'R':
generator.setDrawOrigin(true);
@ -306,19 +310,19 @@ int Mapper::start(int argc, char *argv[]) {
generator.setDrawPlayers(true);
break;
case 'S':
if (opt.arg && *opt.arg) {
std::string option = strlower(opt.arg);
if (option == "left")
if (ps.optarg && *ps.optarg) {
std::string opt = strlower(ps.optarg);
if (opt == "left")
generator.setDrawScale(DRAWSCALE_LEFT);
else if (option == "top")
else if (opt == "top")
generator.setDrawScale(DRAWSCALE_TOP);
else if (option == "left,top")
else if (opt == "left,top")
generator.setDrawScale(DRAWSCALE_LEFT | DRAWSCALE_TOP);
else if (option == "top,left")
else if (opt == "top,left")
generator.setDrawScale(DRAWSCALE_LEFT | DRAWSCALE_TOP);
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name
<< "': '" << opt.arg << "' (expected: left,top)" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name
<< "': '" << ps.optarg << "' (expected: left,top)" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -332,29 +336,29 @@ int Mapper::start(int argc, char *argv[]) {
break;
case OPT_SCALEINTERVAL: {
istringstream arg;
arg.str(opt.arg);
arg.str(ps.optarg);
int major;
int minor;
char sep;
arg >> major;
if (major < 0 || !isdigit(*opt.arg) || arg.fail()) {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name
<< "': '" << opt.arg << "' (expected: <major>[,<minor>]" << std::endl;
if (major < 0 || !isdigit(*ps.optarg) || arg.fail()) {
std::cerr << "Invalid parameter to '" << long_options[option_index].name
<< "': '" << ps.optarg << "' (expected: <major>[,<minor>]" << std::endl;
usage();
return EXIT_FAILURE;
}
arg >> std::ws >> sep >> std::ws;
if (!arg.fail()) {
if ((sep != ',' && sep != ':') || !isdigit(arg.peek())) {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name
<< "': '" << opt.arg << "' (expected: <major>[,<minor>]" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name
<< "': '" << ps.optarg << "' (expected: <major>[,<minor>]" << std::endl;
usage();
return EXIT_FAILURE;
}
arg >> minor;
if (minor < 0) {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name
<< "': '" << opt.arg << "' (expected: <major>[,<minor>]" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name
<< "': '" << ps.optarg << "' (expected: <major>[,<minor>]" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -364,7 +368,7 @@ int Mapper::start(int argc, char *argv[]) {
}
if (minor && sep == ':') {
if (major % minor) {
std::cerr << long_options[opt.ind].name << ": Cannot divide major interval in "
std::cerr << long_options[option_index].name << ": Cannot divide major interval in "
<< minor << " subintervals (not divisible)" << std::endl;
return EXIT_FAILURE;
}
@ -372,25 +376,25 @@ int Mapper::start(int argc, char *argv[]) {
}
if ((minor % major) == 0)
minor = 0;
if (long_options[opt.ind].name[0] == 's') {
if (long_options[option_index].name[0] == 's') {
generator.setSideScaleInterval(major, minor);
}
else if (long_options[opt.ind].name[0] == 'h') {
else if (long_options[option_index].name[0] == 'h') {
generator.setHeightScaleInterval(major, minor);
}
else {
std::cerr << "Internal error: option " << long_options[opt.ind].name << " not handled" << std::endl;
std::cerr << "Internal error: option " << long_options[option_index].name << " not handled" << std::endl;
return EXIT_FAILURE;
}
}
break;
break;
case OPT_SILENCE_SUGGESTIONS: {
for (size_t i = 0; i < strlen(opt.arg); i++) {
opt.arg[i] = tolower(opt.arg[i]);
if (opt.arg[i] == ',')
opt.arg[i] = ' ';
string optarg = strlower(ps.optarg);
for(char &c : optarg) {
if (c == ',')
c = ' ';
}
std::istringstream iss(opt.arg);
std::istringstream iss(optarg);
std::string flag;
iss >> std::skipws >> flag;
while (!iss.fail()) {
@ -407,18 +411,18 @@ int Mapper::start(int argc, char *argv[]) {
#endif
}
else {
std::cerr << "Invalid flag to '" << long_options[opt.ind].name << "': '" << flag << "'" << std::endl;
std::cerr << "Invalid flag to '" << long_options[option_index].name << "': '" << flag << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
iss >> flag;
}
}
break;
break;
case 'v':
if (opt.arg && isdigit(opt.arg[0]) && opt.arg[1] == '\0') {
generator.verboseStatistics = opt.arg[0] - '0';
generator.verboseCoordinates = opt.arg[0] - '0';
if (ps.optarg && isdigit(ps.optarg[0]) && ps.optarg[1] == '\0') {
generator.verboseStatistics = ps.optarg[0] - '0';
generator.verboseCoordinates = ps.optarg[0] - '0';
}
else {
generator.verboseStatistics = 1;
@ -426,29 +430,30 @@ int Mapper::start(int argc, char *argv[]) {
}
break;
case OPT_VERBOSE_SEARCH_COLORS:
if (opt.arg && isdigit(opt.arg[0]) && opt.arg[1] == '\0') {
generator.verboseReadColors = opt.arg[0] - '0';
if (ps.optarg && isdigit(ps.optarg[0]) && ps.optarg[1] == '\0') {
generator.verboseReadColors = ps.optarg[0] - '0';
}
else {
generator.verboseReadColors++;
}
break;
case 'e':
// Todo: Unnecessary calls to strlower. Optimize
generator.setDrawAlpha(true);
if (!opt.arg || !*opt.arg)
if (!ps.optarg || !*ps.optarg)
PixelAttribute::setMixMode(PixelAttribute::AlphaMixAverage);
else if (string(opt.arg) == "cumulative" || strlower(opt.arg) == "nodarken")
else if (string(ps.optarg) == "cumulative" || strlower(ps.optarg) == "nodarken")
// "nodarken" is supported for backwards compatibility
PixelAttribute::setMixMode(PixelAttribute::AlphaMixCumulative);
else if (string(opt.arg) == "darken" || strlower(opt.arg) == "cumulative-darken")
else if (string(ps.optarg) == "darken" || strlower(ps.optarg) == "cumulative-darken")
// "darken" is supported for backwards compatibility
PixelAttribute::setMixMode(PixelAttribute::AlphaMixCumulativeDarken);
else if (strlower(opt.arg) == "average")
else if (strlower(ps.optarg) == "average")
PixelAttribute::setMixMode(PixelAttribute::AlphaMixAverage);
else if (strlower(opt.arg) == "none")
else if (strlower(ps.optarg) == "none")
generator.setDrawAlpha(false);
else {
std::cerr << "Invalid parameter to '" << long_options[opt.ind].name << "': '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid parameter to '" << long_options[option_index].name << "': '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -457,12 +462,12 @@ int Mapper::start(int argc, char *argv[]) {
generator.setDrawAir(true);
break;
case OPT_DRAWNODES: {
bool draw = long_options[opt.ind].name[0] == 'd';
for (char *c = opt.arg; *c; c++) {
*c = tolower(*c);
if (*c == ',') *c = ' ';
bool draw = long_options[option_index].name[0] == 'd';
string optarg = strlower(ps.optarg);
for (char &c : optarg){
if (c == ',') c = ' ';
}
istringstream iss(opt.arg);
istringstream iss(optarg);
string flag;
iss >> std::skipws >> flag;
while (!iss.fail()) {
@ -478,14 +483,14 @@ int Mapper::start(int argc, char *argv[]) {
else if (flag == "air")
generator.setDrawAir(enable);
else {
std::cerr << "Invalid " << long_options[opt.ind].name << " flag '" << flag << "'" << std::endl;
std::cerr << "Invalid " << long_options[option_index].name << " flag '" << flag << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
iss >> flag;
}
}
break;
break;
case 'H':
generator.setShading(false);
break;
@ -501,48 +506,48 @@ int Mapper::start(int argc, char *argv[]) {
break;
case 'a': {
istringstream iss;
iss.str(opt.arg);
iss.str(ps.optarg);
int miny;
iss >> miny;
generator.setMinY(miny);
}
break;
break;
case 'c': {
istringstream iss;
iss.str(opt.arg);
iss.str(ps.optarg);
int maxy;
iss >> maxy;
generator.setMaxY(maxy);
}
break;
break;
case OPT_CHUNKSIZE: {
istringstream iss;
iss.str(opt.arg);
iss.str(ps.optarg);
int size;
iss >> size;
if (iss.fail() || size < 0) {
std::cerr << "Invalid chunk size (" << opt.arg << ")" << std::endl;
std::cerr << "Invalid chunk size (" << ps.optarg << ")" << std::endl;
usage();
return EXIT_FAILURE;
}
generator.setChunkSize(size);
}
break;
break;
case OPT_SCALEFACTOR: {
istringstream arg;
arg.str(opt.arg);
arg.str(ps.optarg);
int one;
char colon;
int factor = 1;
arg >> one >> std::ws;
if (arg.fail() || one != 1) {
std::cerr << "Invalid scale factor specification (" << opt.arg << ") - expected: 1:<n>" << std::endl;
std::cerr << "Invalid scale factor specification (" << ps.optarg << ") - expected: 1:<n>" << std::endl;
return EXIT_FAILURE;
}
if (!arg.eof()) {
arg >> colon >> factor >> std::ws;
if (arg.fail() || colon != ':' || factor<0 || !arg.eof()) {
std::cerr << "Invalid scale factor specification (" << opt.arg << ") - expected: 1:<n>" << std::endl;
std::cerr << "Invalid scale factor specification (" << ps.optarg << ") - expected: 1:<n>" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -553,10 +558,10 @@ int Mapper::start(int argc, char *argv[]) {
}
generator.setScaleFactor(factor);
}
break;
break;
case 't': {
istringstream tilesize;
tilesize.str(strlower(opt.arg));
tilesize.str(strlower(ps.optarg));
if (tilesize.str() == "block") {
generator.setTileSize(BLOCK_SIZE, BLOCK_SIZE);
generator.setTileOrigin(TILECORNER_AT_WORLDCENTER, TILECORNER_AT_WORLDCENTER);
@ -570,7 +575,7 @@ int Mapper::start(int argc, char *argv[]) {
char c;
tilesize >> size;
if (tilesize.fail() || size<0) {
std::cerr << "Invalid tile size specification (" << opt.arg << ")" << std::endl;
std::cerr << "Invalid tile size specification (" << ps.optarg << ")" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -578,7 +583,7 @@ int Mapper::start(int argc, char *argv[]) {
tilesize >> c >> border;
if (!tilesize.fail()) {
if (c != '+' || border < 1) {
std::cerr << "Invalid tile border size specification (" << opt.arg << ")" << std::endl;
std::cerr << "Invalid tile border size specification (" << ps.optarg << ")" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -586,11 +591,11 @@ int Mapper::start(int argc, char *argv[]) {
}
}
}
break;
break;
case 'T': {
bool origin = long_options[opt.ind].name[4] == 'o';
bool origin = long_options[option_index].name[4] == 'o';
istringstream iss;
iss.str(strlower(opt.arg));
iss.str(strlower(ps.optarg));
NodeCoord coord;
if (iss.str() == "world") {
if (origin)
@ -607,7 +612,7 @@ int Mapper::start(int argc, char *argv[]) {
else {
bool result = true;
if (!parseCoordinates(iss, coord, 2, 0, ',')) {
iss.str(opt.arg);
iss.str(ps.optarg);
result = parseCoordinates(iss, coord, 2, 0, ':');
}
if (result) {
@ -621,15 +626,15 @@ int Mapper::start(int argc, char *argv[]) {
}
}
else {
std::cerr << "Invalid " << long_options[opt.ind].name << " parameter (" << opt.arg << ")" << std::endl;
std::cerr << "Invalid " << long_options[option_index].name << " parameter (" << ps.optarg << ")" << std::endl;
usage();
return EXIT_FAILURE;
}
}
}
break;
break;
case 'G':
if (long_options[opt.ind].name[0] == 'f') {
if (long_options[option_index].name[0] == 'f') {
// '--forcegeometry'
// Old behavior - for compatibility.
generator.setShrinkGeometry(false);
@ -637,13 +642,14 @@ int Mapper::start(int argc, char *argv[]) {
if (!foundGeometrySpec)
generator.setBlockGeometry(true);
}
else if (opt.arg && *opt.arg) {
for (char *c = opt.arg; *c; c++) {
*c = tolower(*c);
if (*c == ',') *c = ' ';
else if (ps.optarg && *ps.optarg) {
string optarg = strlower(ps.optarg);
for (char &c : optarg) {
if (c == ',')
c = ' ';
}
istringstream iss;
iss.str(opt.arg);
iss.str(optarg);
string flag;
iss >> std::skipws >> flag;
while (!iss.fail()) {
@ -671,23 +677,23 @@ int Mapper::start(int argc, char *argv[]) {
break;
case 'g': {
istringstream iss;
iss.str(opt.arg);
iss.str(ps.optarg);
NodeCoord coord1;
NodeCoord coord2;
bool legacy;
FuzzyBool center = FuzzyBool::Maybe;
if (long_options[opt.ind].name[0] == 'c' && long_options[opt.ind].name[1] == 'e')
if (long_options[option_index].name[0] == 'c' && long_options[option_index].name[1] == 'e')
center = FuzzyBool::Yes;
if (long_options[opt.ind].name[0] == 'c' && long_options[opt.ind].name[1] == 'o')
if (long_options[option_index].name[0] == 'c' && long_options[option_index].name[1] == 'o')
center = FuzzyBool::No;
if (!parseMapGeometry(iss, coord1, coord2, legacy, center)) {
std::cerr << "Invalid geometry specification '" << opt.arg << "'" << std::endl;
std::cerr << "Invalid geometry specification '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
// Set defaults
if (!foundGeometrySpec) {
if (long_options[opt.ind].name[0] == 'g' && legacy) {
if (long_options[option_index].name[0] == 'g' && legacy) {
// Compatibility when using the option 'geometry'
generator.setBlockGeometry(true);
generator.setShrinkGeometry(true);
@ -708,11 +714,11 @@ int Mapper::start(int argc, char *argv[]) {
generator.setGeometry(coord1, coord2);
foundGeometrySpec = true;
}
break;
break;
case OPT_DRAW_OBJECT: {
TileGenerator::DrawObject drawObject;
drawObject.world = long_options[opt.ind].name[4] != 'm';
char object = long_options[opt.ind].name[4 + (drawObject.world ? 0 : 3)];
drawObject.world = long_options[option_index].name[4] != 'm';
char object = long_options[option_index].name[4 + (drawObject.world ? 0 : 3)];
switch (object) {
case 'p':
drawObject.type = TileGenerator::DrawObject::Point;
@ -735,14 +741,14 @@ int Mapper::start(int argc, char *argv[]) {
break;
default:
std::cerr << "Internal error: unrecognised object ("
<< long_options[opt.ind].name
<< long_options[option_index].name
<< ")" << std::endl;
return EXIT_FAILURE;
break;
}
istringstream iss;
iss.str(opt.arg);
iss.str(ps.optarg);
NodeCoord coord1;
NodeCoord coord2;
NodeCoord dimensions;
@ -756,8 +762,8 @@ int Mapper::start(int argc, char *argv[]) {
needDimensions = FuzzyBool::Yes;
if (!parseGeometry(iss, coord1, coord2, dimensions, legacy, centered, 2, needDimensions)) {
std::cerr << "Invalid drawing geometry specification for "
<< long_options[opt.ind].name
<< " '" << opt.arg << "'" << std::endl;
<< long_options[option_index].name
<< " '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -800,8 +806,8 @@ int Mapper::start(int argc, char *argv[]) {
iss >> std::ws >> colorStr;
if (iss.fail()) {
std::cerr << "Missing color for "
<< long_options[opt.ind].name
<< " '" << opt.arg << "'" << std::endl;
<< long_options[option_index].name
<< " '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -813,8 +819,8 @@ int Mapper::start(int argc, char *argv[]) {
std::getline(iss, localizedText);
if (localizedText.empty() || iss.fail()) {
std::cerr << "Invalid or missing text for "
<< long_options[opt.ind].name
<< " '" << opt.arg << "'" << std::endl;
<< long_options[option_index].name
<< " '" << ps.optarg << "'" << std::endl;
usage();
return EXIT_FAILURE;
}
@ -848,11 +854,13 @@ int Mapper::start(int argc, char *argv[]) {
generator.drawObject(drawObject);
}
}
break;
break;
case 'd':
generator.setBackend(strlower(opt.arg));
generator.setBackend(strlower(ps.optarg));
break;
default:
cout << "Internal Error: Comandline option not handled." << endl
<< "Please file a bug to https://github.com/adrido/minetest-mapper-cpp/" << endl;
return EXIT_FAILURE;
}
}

View File

@ -1,139 +0,0 @@
#pragma once
/*
This File was taken from https://github.com/attractivechaos/klib/blob/master/ketopt.h and is licensed under MIT license.
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 <string.h> /* for strchr() and strncmp() */
#define ko_no_argument 0
#define ko_required_argument 1
#define ko_optional_argument 2
typedef struct {
int ind; /* equivalent to optind */
int opt; /* equivalent to optopt */
char *arg; /* equivalent to optarg */
int longidx; /* index of a long option; or -1 if short */
/* private variables not intended for external uses */
int i, pos, n_args;
} ketopt_t;
typedef struct {
const char *name;
int has_arg;
int val;
} ko_longopt_t;
static ketopt_t KETOPT_INIT = { 1, 0, 0, -1, 1, 0, 0 };
static void ketopt_permute(char *argv[], int j, int n) /* move argv[j] over n elements to the left */
{
int k;
char *p = argv[j];
for (k = 0; k < n; ++k)
argv[j - k] = argv[j - k - 1];
argv[j - k] = p;
}
/**
* Parse command-line options and arguments
*
* This fuction has a similar interface to GNU's getopt_long(). Each call
* parses one option and returns the option name. s->arg points to the option
* argument if present. The function returns -1 when all command-line arguments
* are parsed. In this case, s->ind is the index of the first non-option
* argument.
*
* @param s status; shall be initialized to KETOPT_INIT on the first call
* @param argc length of argv[]
* @param argv list of command-line arguments; argv[0] is ignored
* @param permute non-zero to move options ahead of non-option arguments
* @param ostr option string
* @param longopts long options
*
* @return ASCII for a short option; ko_longopt_t::val for a long option; -1 if
* argv[] is fully processed; '?' for an unknown option or an ambiguous
* long option; ':' if an option argument is missing
*/
static int ketopt(ketopt_t *s, int argc, char *argv[], int permute, const char *ostr, const ko_longopt_t *longopts)
{
int opt = -1, i0, j;
if (permute) {
while (s->i < argc && (argv[s->i][0] != '-' || argv[s->i][1] == '\0'))
++s->i, ++s->n_args;
}
s->arg = 0, s->longidx = -1, i0 = s->i;
if (s->i >= argc || argv[s->i][0] != '-' || argv[s->i][1] == '\0') {
s->ind = s->i - s->n_args;
return -1;
}
if (argv[s->i][0] == '-' && argv[s->i][1] == '-') { /* "--" or a long option */
if (argv[s->i][2] == '\0') { /* a bare "--" */
ketopt_permute(argv, s->i, s->n_args);
++s->i, s->ind = s->i - s->n_args;
return -1;
}
s->opt = 0, opt = '?', s->pos = -1;
if (longopts) { /* parse long options */
int k, n_matches = 0;
const ko_longopt_t *o = 0;
for (j = 2; argv[s->i][j] != '\0' && argv[s->i][j] != '='; ++j) {} /* find the end of the option name */
for (k = 0; longopts[k].name != 0; ++k)
if (strncmp(&argv[s->i][2], longopts[k].name, j - 2) == 0)
++n_matches, o = &longopts[k];
if (n_matches == 1) {
s->opt = opt = o->val, s->longidx = (int)(o - longopts);
if (argv[s->i][j] == '=') s->arg = &argv[s->i][j + 1];
if (o->has_arg == 1 && argv[s->i][j] == '\0') {
if (s->i < argc - 1) s->arg = argv[++s->i];
else opt = ':'; /* missing option argument */
}
}
}
}
else { /* a short option */
const char *p;
if (s->pos == 0) s->pos = 1;
opt = s->opt = argv[s->i][s->pos++];
p = strchr(ostr, opt);
if (p == 0) {
opt = '?'; /* unknown option */
}
else if (p[1] == ':') {
if (argv[s->i][s->pos] == 0) {
if (s->i < argc - 1) s->arg = argv[++s->i];
else opt = ':'; /* missing option argument */
}
else s->arg = &argv[s->i][s->pos];
s->pos = -1;
}
}
if (s->pos < 0 || argv[s->i][s->pos] == 0) {
++s->i, s->pos = 0;
if (s->n_args > 0) /* permute */
for (j = i0; j < s->i; ++j)
ketopt_permute(argv, j, s->n_args);
}
s->ind = s->i - s->n_args;
return opt;
}

354
Minetestmapper/parg.c Normal file
View File

@ -0,0 +1,354 @@
/*
* parg - parse argv
*
* Written in 2015-2016 by Joergen Ibsen
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty. <http://creativecommons.org/publicdomain/zero/1.0/>
*/
#include "parg.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
/*
* Check if state is at end of argv.
*/
static int
is_argv_end(const struct parg_state *ps, int argc, char *const argv[])
{
return ps->optind >= argc || argv[ps->optind] == NULL;
}
/*
* Match nextchar against optstring.
*/
static int
match_short(struct parg_state *ps, int argc, char *const argv[],
const char *optstring)
{
const char *p = strchr(optstring, *ps->nextchar);
if (p == NULL) {
ps->optopt = *ps->nextchar++;
return '?';
}
/* If no option argument, return option */
if (p[1] != ':') {
return *ps->nextchar++;
}
/* If more characters, return as option argument */
if (ps->nextchar[1] != '\0') {
ps->optarg = &ps->nextchar[1];
ps->nextchar = NULL;
return *p;
}
/* If option argument is optional, return option */
if (p[2] == ':') {
return *ps->nextchar++;
}
/* Option argument required, so return next argv element */
if (is_argv_end(ps, argc, argv)) {
ps->optopt = *ps->nextchar++;
return optstring[0] == ':' ? ':' : '?';
}
ps->optarg = argv[ps->optind++];
ps->nextchar = NULL;
return *p;
}
/*
* Match string at nextchar against longopts.
*/
static int
match_long(struct parg_state *ps, int argc, char *const argv[],
const char *optstring,
const struct parg_option *longopts, int *longindex)
{
size_t len;
int num_match = 0;
int match = -1;
int i;
len = strcspn(ps->nextchar, "=");
for (i = 0; longopts[i].name; ++i) {
if (strncmp(ps->nextchar, longopts[i].name, len) == 0) {
match = i;
num_match++;
/* Take if exact match */
if (longopts[i].name[len] == '\0') {
num_match = 1;
break;
}
}
}
/* Return '?' on no or ambiguous match */
if (num_match != 1) {
ps->optopt = 0;
ps->nextchar = NULL;
return '?';
}
assert(match != -1);
if (longindex) {
*longindex = match;
}
if (ps->nextchar[len] == '=') {
/* Option argument present, check if extraneous */
if (longopts[match].has_arg == PARG_NOARG) {
ps->optopt = longopts[match].flag ? 0 : longopts[match].val;
ps->nextchar = NULL;
return optstring[0] == ':' ? ':' : '?';
}
else {
ps->optarg = &ps->nextchar[len + 1];
}
}
else if (longopts[match].has_arg == PARG_REQARG) {
/* Option argument required, so return next argv element */
if (is_argv_end(ps, argc, argv)) {
ps->optopt = longopts[match].flag ? 0 : longopts[match].val;
ps->nextchar = NULL;
return optstring[0] == ':' ? ':' : '?';
}
ps->optarg = argv[ps->optind++];
}
ps->nextchar = NULL;
if (longopts[match].flag != NULL) {
*longopts[match].flag = longopts[match].val;
return 0;
}
return longopts[match].val;
}
void
parg_init(struct parg_state *ps)
{
ps->optarg = NULL;
ps->optind = 1;
ps->optopt = '?';
ps->nextchar = NULL;
}
int
parg_getopt(struct parg_state *ps, int argc, char *const argv[],
const char *optstring)
{
return parg_getopt_long(ps, argc, argv, optstring, NULL, NULL);
}
int
parg_getopt_long(struct parg_state *ps, int argc, char *const argv[],
const char *optstring,
const struct parg_option *longopts, int *longindex)
{
assert(ps != NULL);
assert(argv != NULL);
assert(optstring != NULL);
ps->optarg = NULL;
if (argc < 2) {
return -1;
}
/* Advance to next element if needed */
if (ps->nextchar == NULL || *ps->nextchar == '\0') {
if (is_argv_end(ps, argc, argv)) {
return -1;
}
ps->nextchar = argv[ps->optind++];
/* Check for nonoption element (including '-') */
if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') {
ps->optarg = ps->nextchar;
ps->nextchar = NULL;
return 1;
}
/* Check for '--' */
if (ps->nextchar[1] == '-') {
if (ps->nextchar[2] == '\0') {
ps->nextchar = NULL;
return -1;
}
if (longopts != NULL) {
ps->nextchar += 2;
return match_long(ps, argc, argv, optstring,
longopts, longindex);
}
}
ps->nextchar++;
}
/* Match nextchar */
return match_short(ps, argc, argv, optstring);
}
/*
* Reverse elements of `v` from `i` to `j`.
*/
static void
reverse(char *v[], int i, int j)
{
while (j - i > 1) {
char *tmp = v[i];
v[i] = v[j - 1];
v[j - 1] = tmp;
++i;
--j;
}
}
/*
* Reorder elements of `argv` with no special cases.
*
* This function assumes there is no `--` element, and the last element
* is not an option missing a required argument.
*
* The algorithm is described here:
* http://hardtoc.com/2016/11/07/reordering-arguments.html
*/
static int
parg_reorder_simple(int argc, char *argv[],
const char *optstring,
const struct parg_option *longopts)
{
struct parg_state ps;
int change;
int l = 0;
int m = 0;
int r = 0;
if (argc < 2) {
return argc;
}
do {
int nextind;
int c;
parg_init(&ps);
nextind = ps.optind;
/* Parse until end of argument */
do {
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
} while (ps.nextchar != NULL && *ps.nextchar != '\0');
change = 0;
do {
/* Find next non-option */
for (l = nextind; c != 1 && c != -1;) {
l = ps.optind;
do {
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
} while (ps.nextchar != NULL && *ps.nextchar != '\0');
}
/* Find next option */
for (m = l; c == 1;) {
m = ps.optind;
do {
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
} while (ps.nextchar != NULL && *ps.nextchar != '\0');
}
/* Find next non-option */
for (r = m; c != 1 && c != -1;) {
r = ps.optind;
do {
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
} while (ps.nextchar != NULL && *ps.nextchar != '\0');
}
/* Find next option */
for (nextind = r; c == 1;) {
nextind = ps.optind;
do {
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
} while (ps.nextchar != NULL && *ps.nextchar != '\0');
}
if (m < r) {
change = 1;
reverse(argv, l, m);
reverse(argv, m, r);
reverse(argv, l, r);
}
} while (c != -1);
} while (change != 0);
return l + (r - m);
}
int
parg_reorder(int argc, char *argv[],
const char *optstring,
const struct parg_option *longopts)
{
struct parg_state ps;
int lastind;
int optend;
int c;
assert(argv != NULL);
assert(optstring != NULL);
if (argc < 2) {
return argc;
}
parg_init(&ps);
/* Find end of normal arguments */
do {
lastind = ps.optind;
c = parg_getopt_long(&ps, argc, argv, optstring, longopts, NULL);
/* Check for trailing option with error */
if ((c == '?' || c == ':') && is_argv_end(&ps, argc, argv)) {
lastind = ps.optind - 1;
break;
}
} while (c != -1);
optend = parg_reorder_simple(lastind, argv, optstring, longopts);
/* Rotate `--` or trailing option with error into position */
if (lastind < argc) {
reverse(argv, optend, lastind);
reverse(argv, optend, lastind + 1);
++optend;
}
return optend;
}

192
Minetestmapper/parg.h Normal file
View File

@ -0,0 +1,192 @@
/*
* parg - parse argv
*
* Written in 2015-2016 by Joergen Ibsen
*
* To the extent possible under law, the author(s) have dedicated all
* copyright and related and neighboring rights to this software to the
* public domain worldwide. This software is distributed without any
* warranty. <http://creativecommons.org/publicdomain/zero/1.0/>
*/
#ifndef PARG_H_INCLUDED
#define PARG_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
#define PARG_VER_MAJOR 1 /**< Major version number */
#define PARG_VER_MINOR 0 /**< Minor version number */
#define PARG_VER_PATCH 2 /**< Patch version number */
#define PARG_VER_STRING "1.0.2" /**< Version number as a string */
/**
* Structure containing state between calls to parser.
*
* @see parg_init
*/
struct parg_state {
const char *optarg; /**< Pointer to option argument, if any */
int optind; /**< Next index in argv to process */
int optopt; /**< Option value resulting in error, if any */
const char *nextchar; /**< Next character to process */
};
/**
* Structure for supplying long options to `parg_getopt_long()`.
*
* @see parg_getopt_long
*/
struct parg_option {
const char *name; /**< Name of option */
int has_arg; /**< Option argument status */
int *flag; /**< Pointer to flag variable */
int val; /**< Value of option */
};
/**
* Values for `has_arg` flag in `parg_option`.
*
* @see parg_option
*/
typedef enum {
PARG_NOARG, /**< No argument */
PARG_REQARG, /**< Required argument */
PARG_OPTARG /**< Optional argument */
} parg_arg_num;
/**
* Initialize `ps`.
*
* Must be called before using state with a parser.
*
* @see parg_state
*
* @param ps pointer to state
*/
void
parg_init(struct parg_state *ps);
/**
* Parse next short option in `argv`.
*
* Elements in `argv` that contain short options start with a single dash
* followed by one or more option characters, and optionally an option
* argument for the last option character. Examples are '`-d`', '`-ofile`',
* and '`-dofile`'.
*
* Consecutive calls to this function match the command-line arguments in
* `argv` against the short option characters in `optstring`.
*
* If an option character in `optstring` is followed by a colon, '`:`', the
* option requires an argument. If it is followed by two colons, the option
* may take an optional argument.
*
* If a match is found, `optarg` points to the option argument, if any, and
* the value of the option character is returned.
*
* If a match is found, but is missing a required option argument, `optopt`
* is set to the option character. If the first character in `optstring` is
* '`:`', then '`:`' is returned, otherwise '`?`' is returned.
*
* If no option character in `optstring` matches a short option, `optopt`
* is set to the option character, and '`?`' is returned.
*
* If an element of argv does not contain options (a nonoption element),
* `optarg` points to the element, and `1` is returned.
*
* An element consisting of a single dash, '`-`', is returned as a nonoption.
*
* Parsing stops and `-1` is returned, when the end of `argv` is reached, or
* if an element contains '`--`'.
*
* Works similarly to `getopt`, if `optstring` were prefixed by '`-`'.
*
* @param ps pointer to state
* @param argc number of elements in `argv`
* @param argv array of pointers to command-line arguments
* @param optstring string containing option characters
* @return option value on match, `1` on nonoption element, `-1` on end of
* arguments, '`?`' on unmatched option, '`?`' or '`:`' on option argument
* error
*/
int
parg_getopt(struct parg_state *ps, int argc, char *const argv[],
const char *optstring);
/**
* Parse next long or short option in `argv`.
*
* Elements in `argv` that contain a long option start with two dashes
* followed by a string, and optionally an equal sign and an option argument.
* Examples are '`--help`' and '`--size=5`'.
*
* If no exact match is found, an unambiguous prefix of a long option will
* match. For example, if '`foo`' and '`foobar`' are valid long options, then
* '`--fo`' is ambiguous and will not match, '`--foo`' matches exactly, and
* '`--foob`' is an unambiguous prefix and will match.
*
* If a long option match is found, and `flag` is `NULL`, `val` is returned.
*
* If a long option match is found, and `flag` is not `NULL`, `val` is stored
* in the variable `flag` points to, and `0` is returned.
*
* If a long option match is found, but is missing a required option argument,
* or has an option argument even though it takes none, `optopt` is set to
* `val` if `flag` is `NULL`, and `0` otherwise. If the first character in
* `optstring` is '`:`', then '`:`' is returned, otherwise '`?`' is returned.
*
* If `longindex` is not `NULL`, the index of the entry in `longopts` that
* matched is stored there.
*
* If no long option in `longopts` matches a long option, '`?`' is returned.
*
* Handling of nonoptions and short options is like `parg_getopt()`.
*
* If no short options are required, an empty string, `""`, should be passed
* as `optstring`.
*
* Works similarly to `getopt_long`, if `optstring` were prefixed by '`-`'.
*
* @see parg_getopt
*
* @param ps pointer to state
* @param argc number of elements in `argv`
* @param argv array of pointers to command-line arguments
* @param optstring string containing option characters
* @param longopts array of `parg_option` structures
* @param longindex pointer to variable to store index of matching option in
* @return option value on match, `0` for flag option, `1` on nonoption
* element, `-1` on end of arguments, '`?`' on unmatched or ambiguous option,
* '`?`' or '`:`' on option argument error
*/
int
parg_getopt_long(struct parg_state *ps, int argc, char *const argv[],
const char *optstring,
const struct parg_option *longopts, int *longindex);
/**
* Reorder elements of `argv` so options appear first.
*
* If there are no long options, `longopts` may be `NULL`.
*
* The return value can be used as `argc` parameter for `parg_getopt()` and
* `parg_getopt_long()`.
*
* @param argc number of elements in `argv`
* @param argv array of pointers to command-line arguments
* @param optstring string containing option characters
* @param longopts array of `parg_option` structures
* @return index of first nonoption in `argv` on success, `-1` on error
*/
int
parg_reorder(int argc, char *argv[],
const char *optstring,
const struct parg_option *longopts);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PARG_H_INCLUDED */