From c5a4f5951700225bdb87b2cbd1a625d3a8da3217 Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 20 Sep 2016 09:03:38 +0200 Subject: [PATCH] sys: getopt wrapper and reimplementation (first iteration). --- build/autotools/m4/lol-conf.m4 | 12 ++ src/Makefile.am | 6 +- src/lol-core.vcxproj | 2 + src/lol-core.vcxproj.filter | 6 + src/lol/sys/all.h | 15 ++- src/lol/sys/getopt.h | 41 +++++++ src/sys/getopt.cpp | 195 +++++++++++++++++++++++++++++++++ tools/lolremez/lolremez.cpp | 110 +++++++++++++------ 8 files changed, 342 insertions(+), 45 deletions(-) create mode 100644 src/lol/sys/getopt.h create mode 100644 src/sys/getopt.cpp diff --git a/build/autotools/m4/lol-conf.m4 b/build/autotools/m4/lol-conf.m4 index 4d03df52..c95a3d1b 100644 --- a/build/autotools/m4/lol-conf.m4 +++ b/build/autotools/m4/lol-conf.m4 @@ -68,6 +68,18 @@ LOL_CFLAGS="$LOL_CFLAGS -I\$(lol_srcdir)/src/3rdparty/mingw-std-threads" LOL_CFLAGS="$LOL_CFLAGS -I\$(lol_srcdir)/src/3rdparty/pegtl" LOL_CFLAGS="$LOL_CFLAGS -I\$(lol_srcdir)/src/3rdparty/imgui" +dnl Use system-provided getopt_long? +ac_cv_have_getopt_long="no" +AC_CHECK_HEADERS(getopt.h unistd.h) +AC_CHECK_FUNCS(getopt_long, + [ac_cv_have_getopt_long="yes"], + [AC_CHECK_LIB(gnugetopt, getopt_long, + [ac_cv_have_getopt_long="yes" + LIBS="${LIBS} -lgnugetopt"])]) +if test "$ac_cv_have_getopt_long" != "no"; then + AC_DEFINE(HAVE_GETOPT_LONG, 1, Define to 1 if you have the ‘getopt_long’ function.) +fi + dnl Use NativeClient? ac_cv_my_have_nacl="no" diff --git a/src/Makefile.am b/src/Makefile.am index b3300841..c302ea2d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -47,8 +47,8 @@ liblol_core_headers = \ lol/algorithm/sort.h lol/algorithm/portal.h lol/algorithm/aabb_tree.h \ \ lol/sys/all.h \ - lol/sys/init.h lol/sys/file.h lol/sys/thread.h lol/sys/threadtypes.h \ - lol/sys/timer.h \ + lol/sys/init.h lol/sys/file.h lol/sys/getopt.h lol/sys/thread.h \ + lol/sys/threadtypes.h lol/sys/timer.h \ \ lol/image/all.h \ lol/image/pixel.h lol/image/color.h lol/image/image.h lol/image/movie.h \ @@ -108,7 +108,7 @@ liblol_core_sources = \ mesh/primitivemesh.cpp mesh/primitivemesh.h \ \ sys/init.cpp sys/timer.cpp sys/file.cpp sys/hacks.cpp \ - sys/thread.cpp sys/threadtypes.cpp \ + sys/thread.cpp sys/threadtypes.cpp sys/getopt.cpp \ \ image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \ image/crop.cpp image/resample.cpp image/noise.cpp image/combine.cpp \ diff --git a/src/lol-core.vcxproj b/src/lol-core.vcxproj index 5b0cbbcf..ea18527c 100644 --- a/src/lol-core.vcxproj +++ b/src/lol-core.vcxproj @@ -202,6 +202,7 @@ + @@ -301,6 +302,7 @@ + diff --git a/src/lol-core.vcxproj.filter b/src/lol-core.vcxproj.filter index 6366e918..1e757fd0 100644 --- a/src/lol-core.vcxproj.filter +++ b/src/lol-core.vcxproj.filter @@ -291,6 +291,9 @@ sys + + sys + sys @@ -652,6 +655,9 @@ lol\sys + + lol\sys + lol\gpu diff --git a/src/lol/sys/all.h b/src/lol/sys/all.h index 9601cdac..bf18efbe 100644 --- a/src/lol/sys/all.h +++ b/src/lol/sys/all.h @@ -1,11 +1,13 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2013 Sam Hocevar -// This program is free software; you can redistribute it and/or -// modify it under the terms of the Do What The Fuck You Want To -// Public License, Version 2, as published by Sam Hocevar. See -// http://www.wtfpl.net/ for more details. +// Copyright © 2010—2016 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. // #pragma once @@ -13,6 +15,7 @@ #include #include #include +#include #include #include diff --git a/src/lol/sys/getopt.h b/src/lol/sys/getopt.h new file mode 100644 index 00000000..03fceaf8 --- /dev/null +++ b/src/lol/sys/getopt.h @@ -0,0 +1,41 @@ +// +// Lol Engine +// +// Copyright © 2002—2016 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. +// + +#pragma once + +// +// The getopt functions +// -------------------- +// + +namespace lol +{ + +class getopt +{ +public: + getopt(int argc, char ** _argv); + getopt(int argc, char * const * _argv); + ~getopt(); + + void add_arg(int short_opt, char const *long_opt, bool has_arg); + int parse(); + + int index; + char *arg; + +private: + std::unique_ptr m_private; +}; + +} + diff --git a/src/sys/getopt.cpp b/src/sys/getopt.cpp new file mode 100644 index 00000000..a4fb28b1 --- /dev/null +++ b/src/sys/getopt.cpp @@ -0,0 +1,195 @@ +// +// Lol Engine +// +// Copyright © 2002—2016 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. +// + +#include + +#if HAVE_GETOPT_H +# include +#endif +#if HAVE_UNISTD_H +# include +#endif + +namespace lol +{ + +struct getopt_private +{ + getopt_private(int argc, char * const * _argv) + : m_argc(argc), + m_argv(_argv) + {} + +#if HAVE_GETOPT_LONG + typedef option optdesc; +#else + struct optdesc + { + char const *name; + int has_arg; + int *flag; + int val; + }; +#endif + + int m_argc; + char * const *m_argv; + + String m_optstring; + array m_opts; +}; + +getopt::getopt(int argc, char ** _argv) + : index(1), + arg(nullptr), + m_private(new getopt_private(argc, _argv)) +{ +} + +getopt::getopt(int argc, char * const * _argv) + : index(1), + arg(nullptr), + m_private(new getopt_private(argc, _argv)) +{ +} + +getopt::~getopt() +{ +} + +void getopt::add_arg(int short_opt, char const *long_opt, bool has_arg) +{ + getopt_private::optdesc o = { long_opt, has_arg, nullptr, short_opt }; + m_private->m_opts.push(o); + + if (isalnum(short_opt)) + { + m_private->m_optstring += (char)short_opt; + if (has_arg) + m_private->m_optstring += ':'; + } +} + +int getopt::parse() +{ + int longindex = 0; // FIXME: what is this? + +#if HAVE_GETOPT_LONG + int ret; + optind = this->index; + optarg = this->arg; + m_private->m_opts.push(getopt_private::optdesc { nullptr, 0, nullptr, 0 }); + ret = getopt_long(m_private->m_argc, m_private->m_argv, m_private->m_optstring.C(), + (option const *)m_private->m_opts.data(), &longindex); + this->index = optind; + this->arg = optarg; + return ret; + +#else + /* XXX: this getopt_long implementation should not be trusted for other + * applications without any serious peer reviewing. It “just works” with + * zzuf and a few libcaca programs but may fail miserably in other + * programs. */ + char **argv = (char **)(uintptr_t)m_private->m_argv; + char *flag; + + if (this->index >= m_private->m_argc) + return -1; + + flag = argv[this->index]; + + if (flag[0] == '-' && flag[1] != '-') + { + char const *tmp; + int ret = flag[1]; + + if (ret == '\0') + return -1; + + tmp = strchr(m_private->m_optstring.C(), ret); + if (!tmp || ret == ':') + return '?'; + + this->index++; + if (tmp[1] == ':') + { + if (flag[2] != '\0') + this->arg = flag + 2; + else if (this->index < m_private->m_argc) + this->arg = argv[this->index++]; + else + goto too_few; + return ret; + } + + if (flag[2] != '\0') + { + flag[1] = '-'; + this->index--; + argv[this->index]++; + } + + return ret; + } + + if (flag[0] == '-' && flag[1] == '-') + { + if (flag[2] == '\0') + return -1; + + for (int i = 0; m_private->m_opts[i].name; i++) + { + size_t l = strlen(m_private->m_opts[i].name); + + if (strncmp(flag + 2, m_private->m_opts[i].name, l)) + continue; + + switch (flag[2 + l]) + { + case '=': + if (!m_private->m_opts[i].has_arg) + goto bad_opt; + longindex = i; + this->index++; + this->arg = flag + 2 + l + 1; + return m_private->m_opts[i].val; + case '\0': + longindex = i; + this->index++; + if (m_private->m_opts[i].has_arg) + { + if (this->index < m_private->argc) + this->arg = argv[this->index++]; + else + goto too_few; + } + return m_private->m_opts[i].val; + default: + break; + } + } + bad_opt: + fprintf(stderr, "%s: unrecognized option `%s'\n", argv[0], flag); + return '?'; + + too_few: + fprintf(stderr, "%s: option `%s' requires an argument\n", + argv[0], flag); + return '?'; + } + + return -1; +#endif +} + +} // namespace lol + diff --git a/tools/lolremez/lolremez.cpp b/tools/lolremez/lolremez.cpp index de005f6e..2bf626ae 100644 --- a/tools/lolremez/lolremez.cpp +++ b/tools/lolremez/lolremez.cpp @@ -1,7 +1,7 @@ // // LolRemez - Remez algorithm implementation // -// Copyright © 2005—2015 Sam Hocevar +// Copyright © 2005—2016 Sam Hocevar // // This program is free software. It comes without any warranty, to // the extent permitted by applicable law. You can redistribute it @@ -21,70 +21,108 @@ #include "solver.h" #include "expression.h" +using lol::array; using lol::real; using lol::String; -void FAIL(char const *message) +static void version(void) { - printf("Error: %s\n", message); + printf("lolremez %s\n", PACKAGE_VERSION); + printf("Copyright © 2005—2016 Sam Hocevar \n"); + printf("This program is free software. It comes without any warranty, to the extent\n"); + printf("permitted by applicable law. You can redistribute it and/or modify it under\n"); + printf("the terms of the Do What the Fuck You Want to Public License, Version 2, as\n"); + printf("published by the WTFPL Task Force. See http://www.wtfpl.net/ for more details.\n"); + printf("\n"); + printf("Written by Sam Hocevar. Report bugs to .\n"); +} + +static void usage() +{ + printf("Usage: lolremez [-d degree] [-r xmin:xmax] x-expression [x-error]\n"); + printf(" lolremez -h | --help\n"); + printf(" lolremez -V | --version\n"); + printf("Find a polynomial approximation for x-expression.\n"); printf("\n"); - printf("Usage:\n"); - printf(" lolremez [-d degree] [-i xmin xmax] x-expression [x-error]\n"); + printf("Mandatory arguments to long options are mandatory for short options too.\n"); + printf(" -d, --degree degree of final polynomial\n"); + printf(" -r, --range : range over which to approximate\n"); + printf(" -h, --help display this help and exit\n"); + printf(" -V, --version output version information and exit\n"); printf("\n"); - printf("Example:\n"); - printf(" lolremez -d 4 -i -1 1 \"atan(exp(1+x))\"\n"); - printf(" lolremez -d 4 -i -1 1 \"atan(exp(1+x))\" \"exp(1+x)\"\n"); + printf("Examples:\n"); + printf(" lolremez -d 4 -r -1:1 \"atan(exp(1+x))\"\n"); + printf(" lolremez -d 4 -r -1:1 \"atan(exp(1+x))\" \"exp(1+x)\"\n"); printf("\n"); + printf("Written by Sam Hocevar. Report bugs to .\n"); +} +static void FAIL(char const *message) +{ + printf("Error: %s\n", message); + printf("\n"); + usage(); exit(EXIT_FAILURE); } /* See the tutorial at http://lolengine.net/wiki/doc/maths/remez */ int main(int argc, char **argv) { - char const *xmin = "-1", *xmax = "1"; + String xmin("-1"), xmax("1"); char const *f = nullptr, *g = nullptr; int degree = 4; - for (int i = 1; i < argc; ++i) - { - if (argv[i] == String("-d")) - { - if (i + 1 >= argc) - FAIL("not enough arguments for -d"); + lol::getopt opt(argc, argv); + opt.add_arg('d', "degree", true); + opt.add_arg('r', "range", true); + opt.add_arg('h', "help", false); + opt.add_arg('v', "version", false); - degree = atoi(argv[++i]); - } - else if (argv[i] == String("-i")) - { - if (i + 2 >= argc) - FAIL("not enough arguments for -i"); + for (;;) + { + int c = opt.parse(); + if (c == -1) + break; - xmin = argv[++i]; - xmax = argv[++i]; - } - else if (g) - { - FAIL("unknown argument"); - } - else if (f) - { - g = argv[i]; - } - else + switch (c) { - f = argv[i]; + case 'd': /* --degree */ + degree = atoi(opt.arg); + break; + case 'r': { /* --range */ + array arg = String(opt.arg).split(':'); + if (arg.count() != 2) + FAIL("invalid range"); + xmin = arg[0]; + xmax = arg[1]; + } break; + case 'h': /* --help */ + usage(); + return EXIT_SUCCESS; + case 'v': /* --version */ + version(); + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; } } + if (opt.index < argc) + f = argv[opt.index++]; + + if (opt.index < argc) + g = argv[opt.index++]; + if (!f) FAIL("no function specified"); + else if (opt.index < argc) + FAIL("too many arguments"); - if (real(xmin) >= real(xmax)) + if (real(xmin.C()) >= real(xmax.C())) FAIL("invalid range"); remez_solver solver(degree, 20); - solver.run(xmin, xmax, f, g); + solver.run(xmin.C(), xmax.C(), f, g); return 0; }