Merge commit 'c992cb4e42cc223f67ede0e48d7ff3f4947af0c6' as 'test/compatibility/C/uWebSockets'
This commit is contained in:
		| @@ -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; | ||||
|     } | ||||
| }; | ||||
| @@ -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; | ||||
|             }); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| @@ -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; | ||||
| } | ||||
							
								
								
									
										407
									
								
								test/compatibility/C/uWebSockets/examples/helpers/optparse.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										407
									
								
								test/compatibility/C/uWebSockets/examples/helpers/optparse.h
									
									
									
									
									
										Normal 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 */ | ||||
		Reference in New Issue
	
	Block a user