From 456cfb7f0f67dccfd1402329a08c145ac59fb86f Mon Sep 17 00:00:00 2001 From: Unknown Date: Sun, 17 Feb 2019 04:22:12 +0100 Subject: [PATCH] Replace getopt/ketopt with parg --- Minetestmapper/CMakeLists.txt | 5 +- Minetestmapper/Mapper.cpp | 426 +++++++++++++++++----------------- Minetestmapper/ketopt.h | 139 ----------- Minetestmapper/parg.c | 354 ++++++++++++++++++++++++++++ Minetestmapper/parg.h | 192 +++++++++++++++ 5 files changed, 767 insertions(+), 349 deletions(-) delete mode 100644 Minetestmapper/ketopt.h create mode 100644 Minetestmapper/parg.c create mode 100644 Minetestmapper/parg.h diff --git a/Minetestmapper/CMakeLists.txt b/Minetestmapper/CMakeLists.txt index 8ec26c1..5b25a92 100644 --- a/Minetestmapper/CMakeLists.txt +++ b/Minetestmapper/CMakeLists.txt @@ -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) diff --git a/Minetestmapper/Mapper.cpp b/Minetestmapper/Mapper.cpp index 1a125ba..12d223e 100644 --- a/Minetestmapper/Mapper.cpp +++ b/Minetestmapper/Mapper.cpp @@ -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(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(atof(opt.arg)); + if (isdigit(ps.optarg[0]) || ((ps.optarg[0] == '-' || ps.optarg[0] == '+') && isdigit(ps.optarg[1]))) { + float scale = static_cast(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: [,]" << std::endl; + if (major < 0 || !isdigit(*ps.optarg) || arg.fail()) { + std::cerr << "Invalid parameter to '" << long_options[option_index].name + << "': '" << ps.optarg << "' (expected: [,]" << 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: [,]" << std::endl; + std::cerr << "Invalid parameter to '" << long_options[option_index].name + << "': '" << ps.optarg << "' (expected: [,]" << std::endl; usage(); return EXIT_FAILURE; } arg >> minor; if (minor < 0) { - std::cerr << "Invalid parameter to '" << long_options[opt.ind].name - << "': '" << opt.arg << "' (expected: [,]" << std::endl; + std::cerr << "Invalid parameter to '" << long_options[option_index].name + << "': '" << ps.optarg << "' (expected: [,]" << 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:" << std::endl; + std::cerr << "Invalid scale factor specification (" << ps.optarg << ") - expected: 1:" << 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:" << std::endl; + std::cerr << "Invalid scale factor specification (" << ps.optarg << ") - expected: 1:" << 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; } } diff --git a/Minetestmapper/ketopt.h b/Minetestmapper/ketopt.h deleted file mode 100644 index 88139cb..0000000 --- a/Minetestmapper/ketopt.h +++ /dev/null @@ -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 /* 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; -} diff --git a/Minetestmapper/parg.c b/Minetestmapper/parg.c new file mode 100644 index 0000000..5130974 --- /dev/null +++ b/Minetestmapper/parg.c @@ -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. + */ + +#include "parg.h" + +#include +#include +#include + + /* + * 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; +} diff --git a/Minetestmapper/parg.h b/Minetestmapper/parg.h new file mode 100644 index 0000000..e8673fc --- /dev/null +++ b/Minetestmapper/parg.h @@ -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. + */ + +#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 */