Add ktx
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow, github.com/MarkCallow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @class argparser
|
||||
* @~English
|
||||
*
|
||||
* @brief parse a command argument list.
|
||||
*
|
||||
* Can't use getopt_long because it declares argv as char* const*. We
|
||||
* need const char* const* when we're parsing an embedded string. Also
|
||||
* Windows C library does not have getopt_long.
|
||||
*
|
||||
* @author Mark Callow, github.com/MarkCallow.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include "argparser.h"
|
||||
#include <regex>
|
||||
|
||||
using namespace std;
|
||||
|
||||
/*
|
||||
* Construct from a string of arguments.
|
||||
*/
|
||||
argvector::argvector(const string& sArgs)
|
||||
{
|
||||
const string sep(" \t");
|
||||
// std::regex does not support negative lookbehind assertions. Darn!
|
||||
// RE to extract argument between string start and separator.
|
||||
// - Match 0 is whole matching string including separator.
|
||||
// - Match 1 is the argument.
|
||||
// - Match 2 is an empty string or a backslash.
|
||||
// Use negative character class as it is easiest way to handle
|
||||
// utf-8 file names with non-Latin characters. $ must not be last
|
||||
// in the character class or it will be taken literally not as
|
||||
// end of string.
|
||||
// Use raw literal to avoid excess backslashes.
|
||||
regex re(R"--(^([^$\\ \t]+)(\\?)(?:[ \t]+|$))--");
|
||||
size_t pos;
|
||||
|
||||
pos = sArgs.find_first_not_of(sep);
|
||||
assert(pos != string::npos);
|
||||
|
||||
auto first = sArgs.begin() + pos;
|
||||
auto last = sArgs.end();
|
||||
bool continuation = false;
|
||||
for (smatch sm; regex_search(first, last, sm, re);) {
|
||||
bool needContinuation = false;
|
||||
#define DEBUG_REGEX 0
|
||||
#if DEBUG_REGEX
|
||||
std::cout << "prefix: " << sm.prefix() << '\n';
|
||||
std::cout << "suffix: " << sm.suffix() << '\n';
|
||||
std::cout << "match size: " << sm.size() << '\n';
|
||||
for(uint32_t i = 0; i < sm.size(); i++) {
|
||||
std::cout << "match " << i << ": " << "\"" << sm.str(i) << "\"" << '\n';
|
||||
}
|
||||
#endif
|
||||
string arg;
|
||||
// All this because std::regex does not support negative
|
||||
// lookbehind assertions.
|
||||
arg = sm.str(1);
|
||||
if (sm.str(2)[0] == '\\') {
|
||||
arg += " ";
|
||||
needContinuation = true;
|
||||
}
|
||||
if (continuation) {
|
||||
this->back() += arg;
|
||||
} else {
|
||||
push_back(arg);
|
||||
}
|
||||
continuation = needContinuation;
|
||||
first = sm.suffix().first;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct from an array of C strings
|
||||
*/
|
||||
argvector::argvector(int argc, const char* const* argv)
|
||||
{
|
||||
for (int i = 0; i < argc; i++) {
|
||||
push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Functions the same as getopt_long. See `man 3 getopt_long`.
|
||||
*/
|
||||
int
|
||||
argparser::getopt(string* shortopts, const struct option* longopts,
|
||||
int* /*longindex*/)
|
||||
{
|
||||
if (optind == argv.size())
|
||||
return -1;
|
||||
|
||||
string arg;
|
||||
arg = argv[optind];
|
||||
if (arg[0] != '-' || (arg[0] == '-' && arg.size() == 1))
|
||||
return -1;
|
||||
optind++;
|
||||
|
||||
int retval = '?';
|
||||
if (arg.compare(0, 2, "--") == 0) {
|
||||
if (arg.size() == 2) return -1; // " -- " separates options and files
|
||||
const struct option* opt = longopts;
|
||||
while (opt->name != nullptr) {
|
||||
if (arg.compare(2, string::npos, opt->name) == 0) {
|
||||
retval = opt->val;
|
||||
if (opt->has_arg != option::no_argument) {
|
||||
if (optind >= argv.size() || (optarg = argv[optind++])[0] == '-') {
|
||||
optarg.clear();
|
||||
optind--;
|
||||
if (opt->has_arg == option::required_argument)
|
||||
retval = ':';
|
||||
}
|
||||
}
|
||||
if (opt->flag != nullptr) {
|
||||
*opt->flag = opt->val;
|
||||
retval = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
opt++;
|
||||
}
|
||||
} else if (shortopts != nullptr && arg.compare(0, 1, "-") == 0) {
|
||||
size_t pos = shortopts->find(arg.substr(1, 1));
|
||||
if (pos != string::npos) {
|
||||
retval = (*shortopts)[pos];
|
||||
if (pos < shortopts->length()
|
||||
&& ((*shortopts)[++pos] == ':' || (*shortopts)[pos] == ';')) {
|
||||
if (optind >= argv.size() || (optarg = argv[optind++])[0] == '-') {
|
||||
optarg.clear();
|
||||
optind--;
|
||||
if ((*shortopts)[pos] == ':') // required argument
|
||||
retval = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
//================== Helper for apps' processArgs ========================
|
||||
|
||||
std::istream& operator >> (std::istream& stream, const skip& x)
|
||||
{
|
||||
std::ios_base::fmtflags f = stream.flags();
|
||||
stream >> std::noskipws;
|
||||
|
||||
char c;
|
||||
const char* text = x.text;
|
||||
while (stream && *text++)
|
||||
stream >> c;
|
||||
|
||||
stream.flags(f);
|
||||
return stream;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user