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;
}