From ff415f69a93812418cc60a33dd14452fc6b2901a Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Tue, 25 Jun 2024 15:50:13 +0200 Subject: Support generation of asm/obj files --- make.c | 2 + src/codegen.c | 26 +- src/main.c | 51 +++- vendor/optparse-master/.gitignore | 4 + vendor/optparse-master/Makefile | 10 + vendor/optparse-master/README.md | 241 +++++++++++++++ vendor/optparse-master/UNLICENSE | 24 ++ vendor/optparse-master/examples/Makefile | 18 ++ vendor/optparse-master/examples/long.c | 56 ++++ vendor/optparse-master/examples/short.c | 47 +++ vendor/optparse-master/examples/subcommands.c | 120 ++++++++ vendor/optparse-master/optparse.h | 409 ++++++++++++++++++++++++++ vendor/optparse-master/test.c | 302 +++++++++++++++++++ 13 files changed, 1302 insertions(+), 8 deletions(-) create mode 100644 vendor/optparse-master/.gitignore create mode 100644 vendor/optparse-master/Makefile create mode 100644 vendor/optparse-master/README.md create mode 100644 vendor/optparse-master/UNLICENSE create mode 100644 vendor/optparse-master/examples/Makefile create mode 100644 vendor/optparse-master/examples/long.c create mode 100644 vendor/optparse-master/examples/short.c create mode 100644 vendor/optparse-master/examples/subcommands.c create mode 100644 vendor/optparse-master/optparse.h create mode 100644 vendor/optparse-master/test.c diff --git a/make.c b/make.c index 5566859..be85ecf 100644 --- a/make.c +++ b/make.c @@ -12,6 +12,7 @@ #define TARGET "oryx" #define GMPDIR "vendor/gmp-6.3.0" +#define OPDIR "vendor/optparse-master" enum { SIMD_AVX2 = 1 << 0, @@ -21,6 +22,7 @@ enum { static char *cflags_all[] = { ("-I" GMPDIR), + ("-I" OPDIR), "-pipe", "-std=c11", "-Wall", diff --git a/src/codegen.c b/src/codegen.c index 1567adf..a9b1430 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -29,7 +29,6 @@ LLVMInitialize##x##TargetMC(); \ } while (false) - /* A context structure we can pass to all the codegen functions just so they have easy access to everything */ struct cgctx { @@ -53,9 +52,11 @@ struct cgctx { strview_t namespace; }; +static void codegenast(struct cgctx); static LLVMTypeRef type2llvm(struct cgctx, type_t); -static void codegenast(struct cgctx); +extern bool lflag, sflag; +extern const char *oflag; void codegen(const char *file, mpq_t *folds, scope_t *scps, type_t *types, ast_t ast, @@ -103,7 +104,26 @@ codegen(const char *file, mpq_t *folds, scope_t *scps, type_t *types, ast_t ast, if (LLVMVerifyModule(ctx.mod, LLVMReturnStatusAction, &error) == 1) err("codegen: %s", error); - LLVMDumpModule(ctx.mod); + if (lflag) { + if (oflag == NULL) + LLVMDumpModule(ctx.mod); + else if (LLVMPrintModuleToFile(llmod, oflag, &error) == 1) + err("codegen: %s", error); + } else { + LLVMCodeGenFileType ft; + const char *dst = oflag == NULL ? "out.o" : oflag; + + if (sflag) { + size_t n = strlen(dst); + char *buf = memcpy(tmpalloc(ctx.s, n + 1, 1), dst, n); + buf[n - 1] = 's'; + buf[n - 0] = 0; + dst = buf; + ft = LLVMAssemblyFile; + } else + ft = LLVMObjectFile; + LLVMTargetMachineEmitToFile(llmach, llmod, dst, ft, &error); + } #if DEBUG tmpfree(ctx.s); diff --git a/src/main.c b/src/main.c index a8dabea..cc6cfd7 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,9 @@ #include #include +#define OPTPARSE_API static +#define OPTPARSE_IMPLEMENTATION +#include #include "alloc.h" #include "analyzer.h" @@ -20,16 +23,54 @@ static char *readfile(const char *file, size_t *bufsz) __attribute__((returns_nonnull, nonnull)); +bool lflag, sflag; +const char *oflag; + int main(int argc, char **argv) { - if (argc != 2) { - fputs("Usage: oryx file\n", stderr); - exit(EXIT_FAILURE); + struct optparse_long longopts[] = { + {"assembly", 's', OPTPARSE_NONE}, + {"emit-llvm", 'l', OPTPARSE_NONE}, + {"output", 'o', OPTPARSE_REQUIRED}, + {0}, + }; + + int opt; + struct optparse opts; + optparse_init(&opts, argv); + + while ((opt = optparse_long(&opts, longopts, NULL)) != -1) { + switch (opt) { + case 'o': + oflag = opts.optarg; + break; + case 'l': + lflag = true; + break; + case 's': + sflag = true; + break; + default: + /* TODO: Add warn() to errors.h */ + fprintf(stderr, "oryx: %s\n", opts.errmsg); +usage: + fprintf(stderr, "Usage: oryx [-l | -s] [-o file] source\n"); + exit(EXIT_FAILURE); + } } + if (lflag && sflag) + goto usage; + + argc -= opts.optind; + argv += opts.optind; + + if (argc != 1) + goto usage; + size_t srclen; - char *src = readfile(argv[1], &srclen); + char *src = readfile(argv[0], &srclen); aux_t aux; mpq_t *folds; @@ -40,7 +81,7 @@ main(int argc, char **argv) lexemes_t toks = lexstring(src, srclen); ast_t ast = parsetoks(toks, &aux); analyzeprog(ast, aux, toks, &a, &types, &scps, &folds); - codegen(argv[1], folds, scps, types, ast, aux, toks); + codegen(argv[0], folds, scps, types, ast, aux, toks); #if DEBUG for (size_t i = 0; i < ast.len; i++) { diff --git a/vendor/optparse-master/.gitignore b/vendor/optparse-master/.gitignore new file mode 100644 index 0000000..59c5841 --- /dev/null +++ b/vendor/optparse-master/.gitignore @@ -0,0 +1,4 @@ +test +examples/long +examples/short +examples/subcommands diff --git a/vendor/optparse-master/Makefile b/vendor/optparse-master/Makefile new file mode 100644 index 0000000..392b4eb --- /dev/null +++ b/vendor/optparse-master/Makefile @@ -0,0 +1,10 @@ +CFLAGS = -ansi -pedantic -Wall -Wextra -g3 + +test : test.c optparse.h + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ test.c $(LDLIBS) + +run : test + ./test -abdfoo -c bar subcommand example.txt -a + +clean : + rm -f test diff --git a/vendor/optparse-master/README.md b/vendor/optparse-master/README.md new file mode 100644 index 0000000..2a85d56 --- /dev/null +++ b/vendor/optparse-master/README.md @@ -0,0 +1,241 @@ +# Optparse + +Optparse is a public domain, portable, reentrant, embeddable, getopt-like +option parser. As a single header file, it's trivially dropped into any +project. It supports POSIX getopt option strings, GNU-style long options, +argument permutation, and subcommand processing. + +To get the implementation, define `OPTPARSE_IMPLEMENTATION` before +including `optparse.h`. + +~~~c +#define OPTPARSE_IMPLEMENTATION +#include "optparse.h" +~~~ + +Optionally define `OPTPARSE_API` to control the API's visibility +and/or linkage (`static`, `__attribute__`, `__declspec`). + +~~~c +#define OPTPARSE_API static +#include "optparse.h" +~~~ + +## Why not getopt()? + +The POSIX getopt option parser has three fatal flaws. These flaws are +solved by Optparse. + +1. The getopt 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. +For portable code this means getopt is only good for one run, over one +argv with one option string. It also means subcommand options cannot +be reliably processed with getopt. Most implementations provide an +implementation-specific method to reset the parser, but this is not +portable. Optparse provides an `optparse_arg()` function for stepping +through non-option arguments, and parsing of options can continue +again at any time with a different option string. The Optparse struct +itself could be passed around to subcommand handlers for additional +subcommand option parsing. If a full parser reset is needed, +`optparse_init()` can be called again. + +3. In getopt, error messages are printed to stderr. This can be +disabled with opterr, but the messages themselves are still +inaccessible. Optparse solves this by writing the error message to its +errmsg field, which can be printed to anywhere. The downside to +Optparse is that this error message will always be in English rather +than the current locale. + +## Permutation + +By default, argv is permuted as it is parsed, moving non-option +arguments to the end of the array. This can be disabled by setting the +`permute` field to 0 after initialization. + +~~~c +struct optparse options; +optparse_init(&options, argv); +options.permute = 0; +~~~ + +## Drop-in Replacement + +Optparse's interface should be familiar with anyone accustomed to +getopt. It's nearly a drop-in replacement. The option string has the +same format and the parser struct fields have the same names as the +getopt global variables (optarg, optind, optopt). + +The long option parser `optparse_long()` API is very similar to GNU's +`getopt_long()` and can serve as a portable, embedded replacement. + +Optparse does not allocate memory. Furthermore, Optparse has no +dependencies, including libc itself, so it can be used in situations +where the standard C library cannot. + +See `optparse.h` for full API documentation. + +## Example Usage + +Here's a traditional getopt setup. + +~~~c +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + bool amend = false; + bool brief = false; + const char *color = "white"; + int delay = 0; + + int option; + while ((option = getopt(argc, argv, "abc:d::")) != -1) { + switch (option) { + case 'a': + amend = true; + break; + case 'b': + brief = true; + break; + case 'c': + color = optarg; + break; + case 'd': + delay = optarg ? atoi(optarg) : 1; + break; + case '?': + exit(EXIT_FAILURE); + } + } + + /* Print remaining arguments. */ + for (; optind < argc; optind++) + printf("%s\n", argv[optind]); + return 0; +} +~~~ + +Here's the same thing translated to Optparse. + +~~~c +#include +#include +#include + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "optparse.h" + +int main(int argc, char **argv) +{ + bool amend = false; + bool brief = false; + const char *color = "white"; + int delay = 0; + + char *arg; + int option; + struct optparse options; + + optparse_init(&options, argv); + while ((option = optparse(&options, "abc:d::")) != -1) { + switch (option) { + case 'a': + amend = true; + break; + case 'b': + brief = true; + break; + case 'c': + color = options.optarg; + break; + case 'd': + delay = options.optarg ? atoi(options.optarg) : 1; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + exit(EXIT_FAILURE); + } + } + + /* Print remaining arguments. */ + while ((arg = optparse_arg(&options))) + printf("%s\n", arg); + return 0; +} +~~~ + +And here's a conversion to long options. + +~~~c +#include +#include +#include + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "optparse.h" + +int main(int argc, char **argv) +{ + struct optparse_long longopts[] = { + {"amend", 'a', OPTPARSE_NONE}, + {"brief", 'b', OPTPARSE_NONE}, + {"color", 'c', OPTPARSE_REQUIRED}, + {"delay", 'd', OPTPARSE_OPTIONAL}, + {0} + }; + + bool amend = false; + bool brief = false; + const char *color = "white"; + int delay = 0; + + char *arg; + int option; + struct optparse options; + + optparse_init(&options, argv); + while ((option = optparse_long(&options, longopts, NULL)) != -1) { + switch (option) { + case 'a': + amend = true; + break; + case 'b': + brief = true; + break; + case 'c': + color = options.optarg; + break; + case 'd': + delay = options.optarg ? atoi(options.optarg) : 1; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + exit(EXIT_FAILURE); + } + } + + /* Print remaining arguments. */ + while ((arg = optparse_arg(&options))) + printf("%s\n", arg); + + return 0; +} +~~~ + +## Subcommand Parsing + +To parse subcommands, first parse options with permutation disabled. These +are the "global" options that come before the subcommand. Then parse the +remainder, optionally permuting, as a new option array. + +See `examples/subcommands.c` for a complete, working example. diff --git a/vendor/optparse-master/UNLICENSE b/vendor/optparse-master/UNLICENSE new file mode 100644 index 0000000..68a49da --- /dev/null +++ b/vendor/optparse-master/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/vendor/optparse-master/examples/Makefile b/vendor/optparse-master/examples/Makefile new file mode 100644 index 0000000..11ae2d0 --- /dev/null +++ b/vendor/optparse-master/examples/Makefile @@ -0,0 +1,18 @@ +CC = cc +CFLAGS = -ansi -pedantic -Wall -Wextra -Wno-unused-function -Wno-unused-but-set-variable -g3 +LDFLAGS = +LDLIBS = + +all: short$(EXE) long$(EXE) subcommands$(EXE) + +short$(EXE): short.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ short.c ../optparse.h $(LDLIBS) + +long$(EXE): long.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ long.c ../optparse.h $(LDLIBS) + +subcommands$(EXE): subcommands.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ subcommands.c ../optparse.h $(LDLIBS) + +clean: + rm -f short$(EXE) long$(EXE) subcommands$(EXE) diff --git a/vendor/optparse-master/examples/long.c b/vendor/optparse-master/examples/long.c new file mode 100644 index 0000000..00993fa --- /dev/null +++ b/vendor/optparse-master/examples/long.c @@ -0,0 +1,56 @@ +/* This is free and unencumbered software released into the public domain. */ +#include +#include +#include + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "../optparse.h" + +int main(int argc, char **argv) +{ + struct optparse_long longopts[] = { + {"amend", 'a', OPTPARSE_NONE}, + {"brief", 'b', OPTPARSE_NONE}, + {"color", 'c', OPTPARSE_REQUIRED}, + {"delay", 'd', OPTPARSE_OPTIONAL}, + {0} + }; + + bool amend = false; + bool brief = false; + const char *color = "white"; + int delay = 0; + + char *arg; + int option; + struct optparse options; + + (void)argc; + optparse_init(&options, argv); + while ((option = optparse_long(&options, longopts, NULL)) != -1) { + switch (option) { + case 'a': + amend = true; + break; + case 'b': + brief = true; + break; + case 'c': + color = options.optarg; + break; + case 'd': + delay = options.optarg ? atoi(options.optarg) : 1; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + exit(EXIT_FAILURE); + } + } + + /* Print remaining arguments. */ + while ((arg = optparse_arg(&options))) + printf("%s\n", arg); + + return 0; +} diff --git a/vendor/optparse-master/examples/short.c b/vendor/optparse-master/examples/short.c new file mode 100644 index 0000000..781ea8a --- /dev/null +++ b/vendor/optparse-master/examples/short.c @@ -0,0 +1,47 @@ +/* This is free and unencumbered software released into the public domain. */ +#include +#include +#include + +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "../optparse.h" + +int main(int argc, char **argv) +{ + bool amend = false; + bool brief = false; + const char *color = "white"; + int delay = 0; + + char *arg; + int option; + struct optparse options; + + (void)argc; + optparse_init(&options, argv); + while ((option = optparse(&options, "abc:d::")) != -1) { + switch (option) { + case 'a': + amend = true; + break; + case 'b': + brief = true; + break; + case 'c': + color = options.optarg; + break; + case 'd': + delay = options.optarg ? atoi(options.optarg) : 1; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + exit(EXIT_FAILURE); + } + } + + /* Print remaining arguments. */ + while ((arg = optparse_arg(&options))) + printf("%s\n", arg); + return 0; +} diff --git a/vendor/optparse-master/examples/subcommands.c b/vendor/optparse-master/examples/subcommands.c new file mode 100644 index 0000000..3d6238c --- /dev/null +++ b/vendor/optparse-master/examples/subcommands.c @@ -0,0 +1,120 @@ +/* This is free and unencumbered software released into the public domain. */ +#include +#include +#include +#include +#include + +#define OPTPARSE_IMPLEMENTATION +#include "../optparse.h" + +static int cmd_echo(char **argv) +{ + int i, option; + bool newline = true; + struct optparse options; + + optparse_init(&options, argv); + options.permute = 0; + while ((option = optparse(&options, "hn")) != -1) { + switch (option) { + case 'h': + puts("usage: echo [-hn] [ARG]..."); + return 0; + case 'n': + newline = false; + break; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + return 1; + } + } + argv += options.optind; + + for (i = 0; argv[i]; i++) { + printf("%s%s", i ? " " : "", argv[i]); + } + if (newline) { + putchar('\n'); + } + + fflush(stdout); + return !!ferror(stdout); +} + +static int cmd_sleep(char **argv) +{ + int i, option; + struct optparse options; + + optparse_init(&options, argv); + while ((option = optparse(&options, "h")) != -1) { + switch (option) { + case 'h': + puts("usage: sleep [-h] [NUMBER]..."); + return 0; + case '?': + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + return 1; + } + } + + for (i = 0; argv[i]; i++) { + if (sleep(atoi(argv[i]))) { + return 1; + } + } + return 0; +} + +static void +usage(FILE *f) +{ + fprintf(f, "usage: example [-h] [OPTION]...\n"); +} + +int main(int argc, char **argv) +{ + int i, option; + char **subargv; + struct optparse options; + + static const struct { + char name[8]; + int (*cmd)(char **); + } cmds[] = { + {"echo", cmd_echo }, + {"sleep", cmd_sleep}, + }; + int ncmds = sizeof(cmds) / sizeof(*cmds); + + (void)argc; + optparse_init(&options, argv); + options.permute = 0; + while ((option = optparse(&options, "h")) != -1) { + switch (option) { + case 'h': + usage(stdout); + return 0; + case '?': + usage(stderr); + fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); + return 1; + } + } + + subargv = argv + options.optind; + if (!subargv[0]) { + fprintf(stderr, "%s: missing subcommand\n", argv[0]); + usage(stderr); + return 1; + } + + for (i = 0; i < ncmds; i++) { + if (!strcmp(cmds[i].name, subargv[0])) { + return cmds[i].cmd(subargv); + } + } + fprintf(stderr, "%s: invalid subcommand: %s\n", argv[0], subargv[0]); + return 1; +} diff --git a/vendor/optparse-master/optparse.h b/vendor/optparse-master/optparse.h new file mode 100644 index 0000000..160d0fc --- /dev/null +++ b/vendor/optparse-master/optparse.h @@ -0,0 +1,409 @@ +/* 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 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" + +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 = argv[0] != 0; + 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 && longopts[i].shortname < 127) { + 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 */ + +#pragma GCC diagnostic pop + +#endif /* OPTPARSE_H */ diff --git a/vendor/optparse-master/test.c b/vendor/optparse-master/test.c new file mode 100644 index 0000000..bc613db --- /dev/null +++ b/vendor/optparse-master/test.c @@ -0,0 +1,302 @@ +#define OPTPARSE_IMPLEMENTATION +#define OPTPARSE_API static +#include "optparse.h" + +#include +#include +#include + +static void +print_argv(char **argv) +{ + while (*argv) { + printf("%s ", *argv++); + } + printf("\n"); +} + +static void +try_optparse(char **argv) +{ + int opt; + char *arg; + struct optparse options; + + print_argv(argv); + optparse_init(&options, argv); + while ((opt = optparse(&options, "abc:d::")) != -1) { + if (opt == '?') { + printf("%s: %s\n", argv[0], options.errmsg); + } + printf("%c (%d) = '%s'\n", opt, options.optind, options.optarg); + } + printf("optind = %d\n", options.optind); + while ((arg = optparse_arg(&options))) { + printf("argument: %s\n", arg); + } +} + +static void +try_optparse_long(char **argv) +{ + char *arg; + int opt, longindex; + struct optparse options; + struct optparse_long longopts[] = { + {"amend", 'a', OPTPARSE_NONE}, + {"brief", 'b', OPTPARSE_NONE}, + {"color", 'c', OPTPARSE_REQUIRED}, + {"delay", 'd', OPTPARSE_OPTIONAL}, + {"erase", 256, OPTPARSE_REQUIRED}, + {0, 0, 0} + }; + + print_argv(argv); + optparse_init(&options, argv); + while ((opt = optparse_long(&options, longopts, &longindex)) != -1) { + char buf[2] = {0, 0}; + if (opt == '?') { + printf("%s: %s\n", argv[0], options.errmsg); + } + buf[0] = opt; + printf("%-6s(%d, %d) = '%s'\n", + opt < 127 ? buf : longopts[longindex].longname, + options.optind, longindex, options.optarg); + } + printf("optind = %d\n", options.optind); + while ((arg = optparse_arg(&options))) { + printf("argument: %s\n", arg); + } +} + +static int +manual_test(int argc, char **argv) +{ + size_t size = (argc + 1) * sizeof(*argv); + char **argv_copy = malloc(size); + + memcpy(argv_copy, argv, size); + printf("\nOPTPARSE\n"); + try_optparse(argv_copy); + + printf("\nOPTPARSE LONG\n"); + try_optparse_long(argv); + return 0; +} + +static int +testsuite(void) +{ + struct config { + char amend; + char brief; + char *color; + int delay; + int erase; + }; + struct { + char *argv[8]; + struct config conf; + char *args[8]; + char *err; + } t[] = { + { + {"", "--", "foobar", 0}, + {0, 0, 0, 0, 0}, + {"foobar", 0}, + 0 + }, + { + {"", "-a", "-b", "-c", "-d", "10", "-e", 0}, + {1, 1, "", 10, 1}, + {0}, + 0 + }, + { + { + "", + "--amend", + "--brief", + "--color", + "--delay", + "10", + "--erase", + 0 + }, + {1, 1, "", 10, 1}, + {0}, + 0 + }, + { + {"", "-a", "-b", "-cred", "-d", "10", "-e", 0}, + {1, 1, "red", 10, 1}, + {0}, + 0 + }, + { + {"", "-abcblue", "-d10", "foobar", 0}, + {1, 1, "blue", 10, 0}, + {"foobar", 0}, + 0 + }, + { + {"", "--color=red", "-d", "10", "--", "foobar", 0}, + {0, 0, "red", 10, 0}, + {"foobar", 0}, + 0 + }, + { + {"", "-eeeeee", 0}, + {0, 0, 0, 0, 6}, + {0}, + 0 + }, + { + {"", "--delay", 0}, + {0, 0, 0, 0, 0}, + {0}, + OPTPARSE_MSG_MISSING + }, + { + {"", "--foo", "bar", 0}, + {0, 0, 0, 0, 0}, + {"--foo", "bar", 0}, + OPTPARSE_MSG_INVALID + }, + { + {"", "-x", 0}, + {0, 0, 0, 0, 0}, + {"-x", 0}, + OPTPARSE_MSG_INVALID + }, + { + {"", "-", 0}, + {0, 0, 0, 0, 0}, + {"-", 0}, + 0 + }, + { + {"", "-e", "foo", "bar", "baz", "-a", "quux", 0}, + {1, 0, 0, 0, 1}, + {"foo", "bar", "baz", "quux", 0}, + 0 + }, + { + {"", "foo", "--delay", "1234", "bar", "-cred", 0}, + {0, 0, "red", 1234, 0}, + {"foo", "bar", 0}, + 0 + }, + }; + int ntests = sizeof(t) / sizeof(*t); + int i, nfails = 0; + struct optparse_long longopts[] = { + {"amend", 'a', OPTPARSE_NONE}, + {"brief", 'b', OPTPARSE_NONE}, + {"color", 'c', OPTPARSE_OPTIONAL}, + {"delay", 'd', OPTPARSE_REQUIRED}, + {"erase", 'e', OPTPARSE_NONE}, + {0, 0, 0} + }; + + for (i = 0; i < ntests; i++) { + int j, opt, longindex; + char *arg, *err = 0; + struct optparse options; + struct config conf = {0, 0, 0, 0, 0}; + + optparse_init(&options, t[i].argv); + while ((opt = optparse_long(&options, longopts, &longindex)) != -1) { + switch (opt) { + case 'a': conf.amend = 1; break; + case 'b': conf.brief = 1; break; + case 'c': conf.color = options.optarg ? options.optarg : ""; break; + case 'd': conf.delay = atoi(options.optarg); break; + case 'e': conf.erase++; break; + default: err = options.errmsg; + } + } + + if (conf.amend != t[i].conf.amend) { + nfails++; + printf("FAIL (%2d): expected amend %d, got %d\n", + i, t[i].conf.amend, conf.amend); + } + + if (conf.brief != t[i].conf.brief) { + nfails++; + printf("FAIL (%2d): expected brief %d, got %d\n", + i, t[i].conf.brief, conf.brief); + } + + if (t[i].conf.color) { + if (!conf.color || strcmp(conf.color, t[i].conf.color)) { + nfails++; + printf("FAIL (%2d): expected color %s, got %s\n", + i, t[i].conf.color, conf.color ? conf.color : "(nil)"); + } + } else { + if (conf.color) { + nfails++; + printf("FAIL (%2d): expected no color, got %s\n", + i, conf.color); + } + } + + if (conf.delay != t[i].conf.delay) { + nfails++; + printf("FAIL (%2d): expected delay %d, got %d\n", + i, t[i].conf.delay, conf.delay); + } + + if (conf.erase != t[i].conf.erase) { + nfails++; + printf("FAIL (%2d): expected erase %d, got %d\n", + i, t[i].conf.erase, conf.erase); + } + + if (t[i].err) { + if (!err || strncmp(err, t[i].err, strlen(t[i].err))) { + nfails++; + printf("FAIL (%2d): expected error '%s', got %s\n", + i, t[i].err, err && err[0] ? err : "(nil)"); + } + + } else { + if (err) { + nfails++; + printf("FAIL (%2d): expected no error, got %s\n", + i, err); + } + + for (j = 0; t[i].args[j]; j++) { + arg = optparse_arg(&options); + if (!arg || strcmp(arg, t[i].args[j])) { + nfails++; + printf("FAIL (%2d): expected arg %s, got %s\n", + i, t[i].args[j], arg ? arg : "(nil)"); + } + } + if ((arg = optparse_arg(&options))) { + nfails++; + printf("FAIL (%2d): expected no more args, got %s\n", + i, arg); + } + } + } + + if (nfails == 0) { + puts("All tests pass."); + } + return nfails != 0; +} + +int +main(int argc, char **argv) +{ + if (argc > 1) { + return manual_test(argc, argv); + } else { + return testsuite(); + } +} -- cgit v1.2.3