commit 0d60fb03774bdf0e0a0bfa8d96a73d5edfd61812 Author: anon <anon@anon.anon> Date: Mon Jun 12 15:38:07 2023 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5411a7d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +**/*.out +out.d/* +tests/*.cpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e801bdf --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +main: + @-echo pass + +test: test_template test_compile + +run_test: + @-for i in out.d/test*; do\ + ./$$i; \ + done + +test_template: + cd tests/; \ + for i in *.cpp.part; do \ + m4 -Dtest=$$i test_template.cpp.m4 > $$(basename --suffix=".part" $$i); \ + done + +test_compile: + for i in tests/*.cpp; do \ + g++ -I $$(realpath .) $$i -o out.d/$$(basename $$i); \ + done + +clean: + rm tests/*.cpp + rm out.d/* diff --git a/README.md b/README.md new file mode 100644 index 0000000..747abd1 --- /dev/null +++ b/README.md @@ -0,0 +1,34 @@ +# Setopt +The reverse of getopt. It is standard to create a command from an internal state (object). It is a replacement for usual string concatenation techniques, providing argument order consistency and less bug-prone error handling. + +## Docs +### C +Implemented as a header only library. + + #include "setopt.h" + + /* Struct to define long option - identifier pairs + flags; + Straight from getopt + */ + struct option { + const char *name; + int has_arg; + int *flag; + int val; + }; + + int optc, // number of arguments successfuly added + reset on each invocation + optred, /* not implemented */ + opterrno; // non zero if setopt encountered an error + must be reset manually + + char* setopt(const char* optstring, std::unordered_map<char, std::string> argv); + char* setopt_long(char* optstring, map<string, string> argv, map<string, string> argv_long); /* Not implemented */ + char* setopt_long_only(char* optstring, map<string, string> argv_long); /* Not implemented */ + + + +**optstring:** Complies with GNU getopt's optstring. That is: + +optstring is a string containing the legitimate option characters. A legitimate option character is any visible one byte ascii(7) character (for which isgraph(3) would return nonzero) that is not '-', ':', or ';'. If such a character is followed by a colon, the option requires an argument [...] Two colons mean an option takes an optional arg [...] diff --git a/out.d/.placeholder b/out.d/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/setopt.hpp b/setopt.hpp new file mode 100644 index 0000000..bbd5104 --- /dev/null +++ b/setopt.hpp @@ -0,0 +1,105 @@ +#pragma once + +/* + TODO: + - implement setopt_long and setopt_long_only + - add support repeated options + - add support for embeding format strings +*/ + +#include <unordered_map> +#include <string> +#include <string.h> +#include <ctype.h> + + +#define OPTPREFIX "-" +#define OPTLONGPREFIX "--" +#define OPTGAP " " + +enum { + SETOPTINVALIDOPTSTRING = 1, + SETOPTMISSINGREQUIRED, + SETOPTUNKNOWNOPT +}; + +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +enum { + no_argument = 0, + required_argument = 1, + optional_argument = 2 +}; + +int optc, optred, opterrno = 0; + +static inline void setopt_init(){ + optc = 0; +} + +char* setopt(const char* optstring, std::unordered_map<char, std::string> argv){ + setopt_init(); + + std::string rs; + + for(const char* i = optstring; *i; i++){ + if(not isgraph(*i) or + *i == '-' or + *i == ':' or + *i == ';' + ){ + opterrno = SETOPTINVALIDOPTSTRING; + continue; + } + + auto h = argv.find(*i); + if(h != argv.end()){ + const char &opt = h->first; + const std::string &optarg = h->second; + rs += OPTGAP; + rs += OPTPREFIX; + rs += opt; + ++optc; + if(*(i+1) == ':'){ + if(optarg != ""){ + rs += OPTGAP; + rs += optarg; + }else if(*(i+2) == ':'){ + ++i; + }else{ + opterrno = SETOPTMISSINGREQUIRED; + } + ++i; + } + argv.erase(h); + }else{ + for(int j = 0; j < 2; j++){ + if(*(i+1) == ':'){ + ++i; + } + } + } + } + + if(argv.size()){ + opterrno = SETOPTUNKNOWNOPT; + } + + char* r; + if(rs.size()){ + r = new char[rs.size()-1]; + strcpy(r, rs.c_str()+sizeof(OPTGAP)-1); + }else{ + r = new char[1]; + r[0] = '\00'; + } + return r; +} + +char* setopt_long(const char* optstring, std::unordered_map<std::string, std::string> argv, const struct option* longopts){ return NULL; } +char* setopt_long_only(const char* optstring, std::unordered_map<std::string, std::string> argv, const struct option* longopts){ return NULL; } diff --git a/tests/test1.cpp.part b/tests/test1.cpp.part new file mode 100644 index 0000000..24497bb --- /dev/null +++ b/tests/test1.cpp.part @@ -0,0 +1,4 @@ + argv['d'] = ""; + argv['i'] = "input.txt"; + argv['o'] = "output.txt"; + setopt("i:do:", argv); diff --git a/tests/test2.cpp.part b/tests/test2.cpp.part new file mode 100644 index 0000000..c110387 --- /dev/null +++ b/tests/test2.cpp.part @@ -0,0 +1,2 @@ + argv['d'] = ""; + setopt("i:do:", argv); diff --git a/tests/test3.cpp.part b/tests/test3.cpp.part new file mode 100644 index 0000000..725b07a --- /dev/null +++ b/tests/test3.cpp.part @@ -0,0 +1,2 @@ + argv['d'] = ""; + setopt(":-d", argv); diff --git a/tests/test4.cpp.part b/tests/test4.cpp.part new file mode 100644 index 0000000..1184104 --- /dev/null +++ b/tests/test4.cpp.part @@ -0,0 +1,2 @@ + argv['d'] = ""; + setopt("d:", argv); diff --git a/tests/test5.cpp.part b/tests/test5.cpp.part new file mode 100644 index 0000000..124d497 --- /dev/null +++ b/tests/test5.cpp.part @@ -0,0 +1,3 @@ + argv['d'] = ""; + argv['k'] = "unexpected_arg"; + setopt("d", argv); diff --git a/tests/test_template.cpp.m4 b/tests/test_template.cpp.m4 new file mode 100644 index 0000000..e2edb40 --- /dev/null +++ b/tests/test_template.cpp.m4 @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <unordered_map> +#include "setopt.hpp" + +using namespace std; + +signed main(){ + opterrno = 0; + std::unordered_map<char, std::string> argv; + include(test) + printf("%d %d\n", optc, opterrno); + return opterrno; +}