Merge commit 'c992cb4e42cc223f67ede0e48d7ff3f4947af0c6' as 'test/compatibility/C/uWebSockets'

This commit is contained in:
Benjamin Sergeant
2020-01-04 15:41:03 -08:00
67 changed files with 9563 additions and 0 deletions

View File

@ -0,0 +1,52 @@
#include "App.h"
struct us_listen_socket_t *listen_socket;
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
};
/* Very simple WebSocket broadcasting echo server */
uWS::App().ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1204,
/* Handlers */
.open = [](auto *ws, auto *req) {
/* Let's make every connection subscribe to the "broadcast" topic */
ws->subscribe("broadcast");
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
/* Exit gracefully if we get a closedown message (ASAN debug) */
if (message == "closedown") {
/* Bye bye */
us_listen_socket_close(0, listen_socket);
ws->close();
}
/* Simply broadcast every single message we get */
ws->publish("broadcast", message, opCode);
},
.drain = [](auto *ws) {
/* Check getBufferedAmount here */
},
.ping = [](auto *ws) {
},
.pong = [](auto *ws) {
},
.close = [](auto *ws, int code, std::string_view message) {
/* We automatically unsubscribe from any topic here */
}
}).listen(9001, [](auto *token) {
listen_socket = token;
if (token) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}

View File

@ -0,0 +1,50 @@
/* We simply call the root header file "App.h", giving you uWS::App and uWS::SSLApp */
#include "App.h"
/* This is a simple WebSocket echo server example.
* You may compile it with "WITH_OPENSSL=1 make" or with "make" */
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
/* Fill with user data */
};
/* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support.
* You may swap to using uWS:App() if you don't need SSL */
uWS::SSLApp({
/* There are example certificates in uWebSockets.js repo */
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
}).ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1204,
/* Handlers */
.open = [](auto *ws, auto *req) {
/* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode);
},
.drain = [](auto *ws) {
/* Check ws->getBufferedAmount() here */
},
.ping = [](auto *ws) {
/* Not implemented yet */
},
.pong = [](auto *ws) {
/* Not implemented yet */
},
.close = [](auto *ws, int code, std::string_view message) {
/* You may access ws->getUserData() here */
}
}).listen(9001, [](auto *token) {
if (token) {
std::cout << "Listening on port " << 9001 << std::endl;
}
}).run();
}

View File

@ -0,0 +1,57 @@
#include "App.h"
#include <thread>
#include <algorithm>
int main() {
/* ws->getUserData returns one of these */
struct PerSocketData {
};
/* Simple echo websocket server, using multiple threads */
std::vector<std::thread *> threads(std::thread::hardware_concurrency());
std::transform(threads.begin(), threads.end(), threads.begin(), [](std::thread *t) {
return new std::thread([]() {
/* Very simple WebSocket echo server */
uWS::App().ws<PerSocketData>("/*", {
/* Settings */
.compression = uWS::SHARED_COMPRESSOR,
.maxPayloadLength = 16 * 1024,
.idleTimeout = 10,
.maxBackpressure = 1 * 1024 * 1204,
/* Handlers */
.open = [](auto *ws, auto *req) {
},
.message = [](auto *ws, std::string_view message, uWS::OpCode opCode) {
ws->send(message, opCode);
},
.drain = [](auto *ws) {
/* Check getBufferedAmount here */
},
.ping = [](auto *ws) {
},
.pong = [](auto *ws) {
},
.close = [](auto *ws, int code, std::string_view message) {
}
}).listen(9001, [](auto *token) {
if (token) {
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << 9001 << std::endl;
} else {
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 9001" << std::endl;
}
}).run();
});
});
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
t->join();
});
}

View File

@ -0,0 +1,20 @@
#include "App.h"
/* Note that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support */
int main() {
/* Overly simple hello world app */
uWS::SSLApp({
.key_file_name = "../misc/key.pem",
.cert_file_name = "../misc/cert.pem",
.passphrase = "1234"
}).get("/*", [](auto *res, auto *req) {
res->end("Hello world!");
}).listen(3000, [](auto *token) {
if (token) {
std::cout << "Listening on port " << 3000 << std::endl;
}
}).run();
std::cout << "Failed to listen on port 3000" << std::endl;
}

View File

@ -0,0 +1,28 @@
#include "App.h"
#include <thread>
#include <algorithm>
int main() {
/* Overly simple hello world app, using multiple threads */
std::vector<std::thread *> threads(std::thread::hardware_concurrency());
std::transform(threads.begin(), threads.end(), threads.begin(), [](std::thread *t) {
return new std::thread([]() {
uWS::App().get("/*", [](auto *res, auto *req) {
res->end("Hello world!");
}).listen(3000, [](auto *token) {
if (token) {
std::cout << "Thread " << std::this_thread::get_id() << " listening on port " << 3000 << std::endl;
} else {
std::cout << "Thread " << std::this_thread::get_id() << " failed to listen on port 3000" << std::endl;
}
}).run();
});
});
std::for_each(threads.begin(), threads.end(), [](std::thread *t) {
t->join();
});
}

View File

@ -0,0 +1,90 @@
/* This is a simple HTTP(S) web server much like Python's SimpleHTTPServer */
#include <App.h>
/* Helpers for this example */
#include "helpers/AsyncFileReader.h"
#include "helpers/AsyncFileStreamer.h"
#include "helpers/Middleware.h"
/* optparse */
#define OPTPARSE_IMPLEMENTATION
#include "helpers/optparse.h"
int main(int argc, char **argv) {
int option;
struct optparse options;
optparse_init(&options, argv);
struct optparse_long longopts[] = {
{"port", 'p', OPTPARSE_REQUIRED},
{"help", 'h', OPTPARSE_NONE},
{"passphrase", 'a', OPTPARSE_REQUIRED},
{"key", 'k', OPTPARSE_REQUIRED},
{"cert", 'c', OPTPARSE_REQUIRED},
{"dh_params", 'd', OPTPARSE_REQUIRED},
{0}
};
int port = 3000;
struct us_socket_context_options_t ssl_options = {};
while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
switch (option) {
case 'p':
port = atoi(options.optarg);
break;
case 'a':
ssl_options.passphrase = options.optarg;
break;
case 'c':
ssl_options.cert_file_name = options.optarg;
break;
case 'k':
ssl_options.key_file_name = options.optarg;
break;
case 'd':
ssl_options.dh_params_file_name = options.optarg;
break;
case 'h':
case '?':
fail:
std::cout << "Usage: " << argv[0] << " [--help] [--port <port>] [--key <ssl key>] [--cert <ssl cert>] [--passphrase <ssl key passphrase>] [--dh_params <ssl dh params file>] <public root>" << std::endl;
return 0;
}
}
char *root = optparse_arg(&options);
if (!root) {
goto fail;
}
AsyncFileStreamer asyncFileStreamer(root);
/* Either serve over HTTP or HTTPS */
struct us_socket_context_options_t empty_ssl_options = {};
if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) {
/* HTTPS */
uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) {
serveFile(res, req);
asyncFileStreamer.streamFile(res, req->getUrl());
}).listen(port, [port, root](auto *token) {
if (token) {
std::cout << "Serving " << root << " over HTTPS a " << port << std::endl;
}
}).run();
} else {
/* HTTP */
uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) {
serveFile(res, req);
asyncFileStreamer.streamFile(res, req->getUrl());
}).listen(port, [port, root](auto *token) {
if (token) {
std::cout << "Serving " << root << " over HTTP a " << port << std::endl;
}
}).run();
}
std::cout << "Failed to listen to port " << port << std::endl;
}

View File

@ -0,0 +1,130 @@
#include <map>
#include <cstring>
#include <fstream>
#include <sstream>
#include <iostream>
#include <future>
/* This is just a very simple and inefficient demo of async responses,
* please do roll your own variant or use a database or Node.js's async
* features instead of this really bad demo */
struct AsyncFileReader {
private:
/* The cache we have in memory for this file */
std::string cache;
int cacheOffset;
bool hasCache;
/* The pending async file read (yes we only support one pending read) */
std::function<void(std::string_view)> pendingReadCb;
int fileSize;
std::string fileName;
std::ifstream fin;
uWS::Loop *loop;
public:
/* Construct a demo async. file reader for fileName */
AsyncFileReader(std::string fileName) : fileName(fileName) {
fin.open(fileName, std::ios::binary);
// get fileSize
fin.seekg(0, fin.end);
fileSize = fin.tellg();
//std::cout << "File size is: " << fileSize << std::endl;
// cache up 1 mb!
cache.resize(1024 * 1024);
//std::cout << "Caching 1 MB at offset = " << 0 << std::endl;
fin.seekg(0, fin.beg);
fin.read(cache.data(), cache.length());
cacheOffset = 0;
hasCache = true;
// get loop for thread
loop = uWS::Loop::get();
}
/* Returns any data already cached for this offset */
std::string_view peek(int offset) {
/* Did we hit the cache? */
if (hasCache && offset >= cacheOffset && ((offset - cacheOffset) < cache.length())) {
/* Cache hit */
//std::cout << "Cache hit!" << std::endl;
/*if (fileSize - offset < cache.length()) {
std::cout << "LESS THAN WHAT WE HAVE!" << std::endl;
}*/
int chunkSize = std::min<int>(fileSize - offset, cache.length() - offset + cacheOffset);
return std::string_view(cache.data() + offset - cacheOffset, chunkSize);
} else {
/* Cache miss */
//std::cout << "Cache miss!" << std::endl;
return std::string_view(nullptr, 0);
}
}
/* Asynchronously request more data at offset */
void request(int offset, std::function<void(std::string_view)> cb) {
// in this case, what do we do?
// we need to queue up this chunk request and callback!
// if queue is full, either block or close the connection via abort!
if (!hasCache) {
// already requesting a chunk!
std::cout << "ERROR: already requesting a chunk!" << std::endl;
return;
}
// disable cache
hasCache = false;
std::async(std::launch::async, [this, cb, offset]() {
//std::cout << "ASYNC Caching 1 MB at offset = " << offset << std::endl;
// den har stängts! öppna igen!
if (!fin.good()) {
fin.close();
//std::cout << "Reopening fin!" << std::endl;
fin.open(fileName, std::ios::binary);
}
fin.seekg(offset, fin.beg);
fin.read(cache.data(), cache.length());
cacheOffset = offset;
loop->defer([this, cb, offset]() {
int chunkSize = std::min<int>(cache.length(), fileSize - offset);
// båda dessa sker, wtf?
if (chunkSize == 0) {
std::cout << "Zero size!?" << std::endl;
}
if (chunkSize != cache.length()) {
std::cout << "LESS THAN A CACHE 1 MB!" << std::endl;
}
hasCache = true;
cb(std::string_view(cache.data(), chunkSize));
});
});
}
/* Abort any pending async. request */
void abort() {
}
int getFileSize() {
return fileSize;
}
};

View File

@ -0,0 +1,84 @@
#include <filesystem>
struct AsyncFileStreamer {
std::map<std::string_view, AsyncFileReader *> asyncFileReaders;
std::string root;
AsyncFileStreamer(std::string root) : root(root) {
// for all files in this path, init the map of AsyncFileReaders
updateRootCache();
}
void updateRootCache() {
// todo: if the root folder changes, we want to reload the cache
for(auto &p : std::filesystem::recursive_directory_iterator(root)) {
std::string url = p.path().string().substr(root.length());
if (url == "/index.html") {
url = "/";
}
char *key = new char[url.length()];
memcpy(key, url.data(), url.length());
asyncFileReaders[std::string_view(key, url.length())] = new AsyncFileReader(p.path().string());
}
}
template <bool SSL>
void streamFile(uWS::HttpResponse<SSL> *res, std::string_view url) {
auto it = asyncFileReaders.find(url);
if (it == asyncFileReaders.end()) {
std::cout << "Did not find file: " << url << std::endl;
} else {
streamFile(res, it->second);
}
}
template <bool SSL>
static void streamFile(uWS::HttpResponse<SSL> *res, AsyncFileReader *asyncFileReader) {
/* Peek from cache */
std::string_view chunk = asyncFileReader->peek(res->getWriteOffset());
if (!chunk.length() || res->tryEnd(chunk, asyncFileReader->getFileSize()).first) {
/* Request new chunk */
// todo: we need to abort this callback if peer closed!
// this also means Loop::defer needs to support aborting (functions should embedd an atomic boolean abort or something)
// Loop::defer(f) -> integer
// Loop::abort(integer)
// hmm? no?
// us_socket_up_ref eftersom vi delar ägandeskapet
if (chunk.length() < asyncFileReader->getFileSize()) {
asyncFileReader->request(res->getWriteOffset(), [res, asyncFileReader](std::string_view chunk) {
// check if we were closed in the mean time
//if (us_socket_is_closed()) {
// free it here
//return;
//}
/* We were aborted for some reason */
if (!chunk.length()) {
// todo: make sure to check for is_closed internally after all callbacks!
res->close();
} else {
AsyncFileStreamer::streamFile(res, asyncFileReader);
}
});
}
} else {
/* We failed writing everything, so let's continue when we can */
res->onWritable([res, asyncFileReader](int offset) {
// här kan skiten avbrytas!
AsyncFileStreamer::streamFile(res, asyncFileReader);
// todo: I don't really know what this is supposed to mean?
return false;
})->onAborted([]() {
std::cout << "ABORTED!" << std::endl;
});
}
}
};

View File

@ -0,0 +1,19 @@
/* Middleware to fill out content-type */
inline bool hasExt(std::string_view file, std::string_view ext) {
if (ext.size() > file.size()) {
return false;
}
return std::equal(ext.rbegin(), ext.rend(), file.rbegin());
}
/* This should be a filter / middleware like app.use(handler) */
template <bool SSL>
uWS::HttpResponse<SSL> *serveFile(uWS::HttpResponse<SSL> *res, uWS::HttpRequest *req) {
res->writeStatus(uWS::HTTP_200_OK);
if (hasExt(req->getUrl(), ".svg")) {
res->writeHeader("Content-Type", "image/svg+xml");
}
return res;
}

View File

@ -0,0 +1,407 @@
/* Nicked from third-party https://github.com/skeeto/optparse 2018-09-24 */
/* µWebSockets is not the origin of this software file */
/* ------------------------------------------------------ */
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = 1;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */