diff options
author | Vincent Torri <vincent.torri@gmail.com> | 2012-09-11 16:13:11 +0000 |
---|---|---|
committer | Vincent Torri <vincent.torri@gmail.com> | 2012-09-11 16:13:11 +0000 |
commit | cd69ef4c8a66e7155967a8b661a014856979cf31 (patch) | |
tree | 4a351ae4a4ca91abf29c85254b85ea8da71f74b0 /src | |
parent | 59a9dfd11860888a35e96dfe51af63cea5cecfe1 (diff) | |
download | efl-cd69ef4c8a66e7155967a8b661a014856979cf31.tar.gz |
merge: add evil files
SVN revision: 76464
Diffstat (limited to 'src')
103 files changed, 19456 insertions, 0 deletions
diff --git a/src/bin/evil/Makefile.am b/src/bin/evil/Makefile.am new file mode 100644 index 0000000000..538bbdcc69 --- /dev/null +++ b/src/bin/evil/Makefile.am @@ -0,0 +1,53 @@ + +MAINTAINERCLEANFILES = Makefile.in + +AM_CPPFLAGS = \ +-I$(top_srcdir) \ +-I$(top_srcdir)/bin \ +-I$(top_srcdir)/src/lib \ +@EVIL_CPPFLAGS@ + +AM_CFLAGS = @EVIL_CFLAGS@ + +bin_PROGRAMS = evil_suite test_evil + +evil_suite_SOURCES = \ +evil_suite.c \ +evil_test_dlfcn.c \ +evil_test_environment.c \ +evil_test_gettimeofday.c \ +evil_test_link.c \ +evil_test_memcpy.c \ +evil_test_mkstemp.c \ +evil_test_pipe.c \ +evil_test_print.c \ +evil_test_realpath.c \ +evil_test_util.c \ +evil_suite.h \ +evil_test_dlfcn.h \ +evil_test_environment.h \ +evil_test_gettimeofday.h \ +evil_test_link.h \ +evil_test_memcpy.h \ +evil_test_mkstemp.h \ +evil_test_pipe.h \ +evil_test_print.h \ +evil_test_realpath.h \ +evil_test_util.h + +if EVIL_HAVE_WINCE + +evil_suite_SOURCES += memcpy_glibc_arm.S + +#else + +#suite_SOURCES += memcpy_glibc_i686.S + +endif + +evil_suite_LDADD = $(top_builddir)/src/lib/libdl.la $(top_builddir)/src/lib/libevil.la -lm +evil_suite_LDFLAGS = -Wl,--enable-auto-import + +test_evil_SOURCES = test_evil.c +test_evil_LDADD = $(top_builddir)/src/lib/libevil.la +test_evil_LDFLAGS = -Wl,--enable-auto-import diff --git a/src/bin/evil/evil_suite.c b/src/bin/evil/evil_suite.c new file mode 100644 index 0000000000..ba928f0682 --- /dev/null +++ b/src/bin/evil/evil_suite.c @@ -0,0 +1,221 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include "Evil.h" +#include "evil_suite.h" +#include "evil_test_dlfcn.h" +#include "evil_test_environment.h" +#include "evil_test_gettimeofday.h" +#include "evil_test_link.h" +#include "evil_test_memcpy.h" +#include "evil_test_mkstemp.h" +#include "evil_test_pipe.h" +#include "evil_test_print.h" +#include "evil_test_realpath.h" +#include "evil_test_util.h" + + +typedef int(*function)(suite *s); + +struct test +{ + const char *name; + function fct; +}; + +struct list +{ + void *data; + int succeed; + list *next; +}; + +struct suite +{ + LARGE_INTEGER freq; + LARGE_INTEGER start; + LARGE_INTEGER end; + + list *first; + list *l; + + int tests_count; + int tests_success; +}; + + +static suite * +suite_new(void) +{ + suite *s; + + s = (suite *)malloc(sizeof(suite)); + if (!s) return NULL; + + if (!QueryPerformanceFrequency(&s->freq)) + { + free(s); + return NULL; + } + + s->first = NULL; + s->l = NULL; + + s->tests_count = 0; + s->tests_success = 0; + + return s; +} + +static void +suite_del(suite *s) +{ + list *l; + list *tmp; + + if (!s) return; + + l = s->first; + while (l) + { + tmp = l->next; + free(l->data); + free(l); + l = tmp; + } + + free(s); +} + +void +suite_time_start(suite *s) +{ + QueryPerformanceCounter(&s->start); +} + +void +suite_time_stop(suite *s) +{ + QueryPerformanceCounter(&s->end); +} + +double +suite_time_get(suite *s) +{ + return (double)(s->end.QuadPart - s->start.QuadPart) / (double)s->freq.QuadPart; +} + +static void +suite_test_add(suite *s, const char *name, function fct) +{ + test *t; + list *l; + + t = (test *)malloc(sizeof(test)); + if (!t) return; + + l = (list *)malloc(sizeof(list)); + if (!l) + { + free(t); + return; + } + + t->name = name; + t->fct = fct; + + l->data = t; + l->succeed = 0; + l->next = NULL; + + if (!s->first) s->first = l; + + if (!s->l) + s->l = l; + else + { + s->l->next = l; + s->l =l; + } +} + +static void +suite_run(suite *s) +{ + list *l; + + l = s->first; + while (l) + { + test *t; + + t = (test *)l->data; + l->succeed = t->fct(s); + printf("%s test: %s\n", t->name, l->succeed ? "success" : "failure"); + s->tests_count++; + if (l->succeed) + s->tests_success++; + l = l->next; + } +} + +static void +suite_show(suite *s) +{ + printf ("\n%d/%d tests passed (%d%%)\n", + s->tests_success, + s->tests_count, + (100 * s->tests_success) / s->tests_count); +} + +int +main(void) +{ + test tests[] = { + { "dlfcn ", test_dlfcn }, + { "environment ", test_environment }, + { "gettimeofday", test_gettimeofday }, + { "link ", test_link }, + { "mkstemp ", test_mkstemp }, + { "pipe ", test_pipe }, + { "print ", test_print }, + { "realpath ", test_realpath }, + { "util ", test_util }, +/* { "memcpy ", test_memcpy }, */ + { NULL, NULL }, + }; + suite *s; + int i; + + if (!evil_init()) + return EXIT_FAILURE; + + s = suite_new(); + if (!s) + { + evil_shutdown(); + return EXIT_FAILURE; + } + + for (i = 0; tests[i].name; ++i) + { + suite_test_add(s, tests[i].name, tests[i].fct); + } + + suite_run(s); + + suite_show(s); + + suite_del(s); + evil_shutdown(); + + return EXIT_SUCCESS; +} diff --git a/src/bin/evil/evil_suite.h b/src/bin/evil/evil_suite.h new file mode 100644 index 0000000000..d3284b4fbb --- /dev/null +++ b/src/bin/evil/evil_suite.h @@ -0,0 +1,14 @@ +#ifndef __EVIL_SUITE_H__ +#define __EVIL_SUITE_H__ + + +typedef struct test test; +typedef struct list list; +typedef struct suite suite; + +void suite_time_start(suite *s); +void suite_time_stop(suite *s); +double suite_time_get(suite *s); + + +#endif /* __EVIL_SUITE_H__ */ diff --git a/src/bin/evil/evil_test_dlfcn.c b/src/bin/evil/evil_test_dlfcn.c new file mode 100644 index 0000000000..fb32dd1540 --- /dev/null +++ b/src/bin/evil/evil_test_dlfcn.c @@ -0,0 +1,80 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <dlfcn.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_dlfcn.h" + + +typedef int (*_evil_init)(void); +typedef int (*_evil_shutdwon)(void); + + +static int +test_dlfcn_test_dlopen(void) +{ + void *handle; + + handle = dlopen("libevil-1.dll", 0); + if (!handle) + return 0; + + if (dlclose(handle)) + return 0; + + return 1; +} + +static int +test_dlfcn_test_dlsym(void) +{ + void *handle; + _evil_init sym_init; + _evil_shutdwon sym_shutdown; + + handle = dlopen("libevil-1.dll", 0); + if (!handle) + return 0; + + sym_init = dlsym(handle, "evil_init"); + if (!sym_init) + { + dlclose(handle); + return 0; + } + + sym_shutdown = dlsym(handle, "evil_shutdown"); + if (!sym_shutdown) + { + dlclose(handle); + return 0; + } + + if (dlclose(handle)) + return 0; + + return 1; +} + +static int +test_dlfcn_tests_run(suite *s __UNUSED__) +{ + int res; + + res = test_dlfcn_test_dlopen(); + res &= test_dlfcn_test_dlsym(); + + return res; +} + +int +test_dlfcn(suite *s) +{ + + return test_dlfcn_tests_run(s); +} diff --git a/src/bin/evil/evil_test_dlfcn.h b/src/bin/evil/evil_test_dlfcn.h new file mode 100644 index 0000000000..0c9bce689e --- /dev/null +++ b/src/bin/evil/evil_test_dlfcn.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_DLFCN_H__ +#define __EVIL_TEST_DLFCN_H__ + + +int test_dlfcn(suite *s); + + +#endif /* __EVIL_TEST_DLFCN_H__ */ diff --git a/src/bin/evil/evil_test_environment.c b/src/bin/evil/evil_test_environment.c new file mode 100644 index 0000000000..3048a40124 --- /dev/null +++ b/src/bin/evil/evil_test_environment.c @@ -0,0 +1,178 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_environment.h" + + +static int +test_env_test_setenv_NULL(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV", NULL, 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + + return val ? 0 : 1; +} + +static int +test_env_test_setenv_NULL_after_set(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV", "val", 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + if (!val) + return 0; + + if (strcmp(val, "val")) + return 0; + + res = setenv("EVIL_TEST_ENV", NULL, 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + + return val ? 0 : 1; +} + +static int +test_env_test_getenv_one(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV", "val", 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + if (!val) + return 0; + + if (strcmp(val, "val")) + return 0; + + return 1; +} + +static int +test_env_test_getenv_two(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV1", "val1", 1); + if (res < 0) + return 0; + + res = setenv("EVIL_TEST_ENV2", "val2", 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV1"); + if (!val) + return 0; + if (strcmp(val, "val1")) + return 0; + + val = getenv("EVIL_TEST_ENV2"); + if (!val) + return 0; + + if (strcmp(val, "val2")) + return 0; + + return 1; +} + +static int +test_env_test_getenv_two_swapped(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV1", "val1", 1); + if (res < 0) + return 0; + + res = setenv("EVIL_TEST_ENV2", "val2", 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV2"); + if (!val) + return 0; + if (strcmp(val, "val2")) + return 0; + + val = getenv("EVIL_TEST_ENV1"); + if (!val) + return 0; + + if (strcmp(val, "val1")) + return 0; + + return 1; +} + +static int +test_env_test_unsetenv(void) +{ + char *val; + int res; + + res = setenv("EVIL_TEST_ENV", "val", 1); + if (res < 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + if (!val) + return 0; + + if (unsetenv("EVIL_TEST_ENV") != 0) + return 0; + + val = getenv("EVIL_TEST_ENV"); + if (val) + return 0; + + return 1; +} + +static int +test_env_tests_run(suite *s __UNUSED__) +{ + int res; + + res = test_env_test_setenv_NULL(); + res &= test_env_test_setenv_NULL_after_set(); + res &= test_env_test_getenv_one(); + res &= test_env_test_getenv_two(); + res &= test_env_test_getenv_two_swapped(); + res &= test_env_test_unsetenv(); + + return res; +} + +int +test_environment(suite *s) +{ + + return test_env_tests_run(s); +} diff --git a/src/bin/evil/evil_test_environment.h b/src/bin/evil/evil_test_environment.h new file mode 100644 index 0000000000..763ee401b1 --- /dev/null +++ b/src/bin/evil/evil_test_environment.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_ENVIRONMENT_H__ +#define __EVIL_TEST_ENVIRONMENT_H__ + + +int test_environment(suite *s); + + +#endif /* __EVIL_TEST_ENVIRONMENT_H__ */ diff --git a/src/bin/evil/evil_test_gettimeofday.c b/src/bin/evil/evil_test_gettimeofday.c new file mode 100644 index 0000000000..49742eb5d3 --- /dev/null +++ b/src/bin/evil/evil_test_gettimeofday.c @@ -0,0 +1,51 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> +#include <math.h> +#include <sys/time.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_gettimeofday.h" + +static int +test_time_test_gettimeofday(void) +{ + struct timeval tp1; + struct timeval tp2; + double delta; + + gettimeofday (&tp1, NULL); + + Sleep(1000); + + gettimeofday (&tp2, NULL); + + delta = (double)(tp2.tv_sec - tp1.tv_sec) + (double)(tp2.tv_usec - tp1.tv_usec) / 1000000.0; + if (fabs(delta - 1) > 0.005) + { + return 0; + } + + return 1; +} + +static int +test_time_tests_run(suite *s __UNUSED__) +{ + int res; + + res = test_time_test_gettimeofday(); + + return res; +} + +int +test_gettimeofday(suite *s) +{ + + return test_time_tests_run(s); +} diff --git a/src/bin/evil/evil_test_gettimeofday.h b/src/bin/evil/evil_test_gettimeofday.h new file mode 100644 index 0000000000..ad3155b146 --- /dev/null +++ b/src/bin/evil/evil_test_gettimeofday.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_GETTIMEOFDAY_H__ +#define __EVIL_TEST_GETTIMEOFDAY_H__ + + +int test_gettimeofday(suite *s); + + +#endif /* __EVIL_TEST_GETTIMEOFDAY_H__ */ diff --git a/src/bin/evil/evil_test_link.c b/src/bin/evil/evil_test_link.c new file mode 100644 index 0000000000..cf4cd931ae --- /dev/null +++ b/src/bin/evil/evil_test_link.c @@ -0,0 +1,158 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_link.h" + +static int +test_link_test_file_create(const char *name, const char *data) +{ + FILE *f; + size_t length; + size_t res; + + f = fopen(name, "wb"); + if (!f) + return 0; + + length = strlen(data) + 1; + res = fwrite(data, 1, length, f); + if (res < length) + { + fclose(f); + return 0; + } + + fclose(f); + + return 1; +} + +static int +test_link_test_symlink(void) +{ +#ifdef _WIN32_WCE + const char *old_name = "\\efl\\evil_test_link.dat"; + const char *new_name = "\\efl\\evil_test_link.lnk"; +#else + const char *old_name = "evil_test_link.dat"; + const char *new_name = "evil_test_link.lnk"; +#endif + + if (!test_link_test_file_create(old_name, + "evil_test_link symlink data\n")) + return 0; + + if (symlink(old_name, new_name) < 0) + { + unlink(old_name); + return 0; + } + + if (unlink(new_name) < 0) + { + unlink(old_name); + return 0; + } + + if (unlink(old_name) < 0) + return 0; + + return 1; +} + +static int +test_link_test_readlink(void) +{ + char buf[1024]; +#ifdef _WIN32_WCE + const char *old_name = "\\efl\\evil_test_link.dat"; + const char *new_name = "\\efl\\evil_test_link.lnk"; +#else + const char *old_name = "evil_test_link.dat"; + const char *new_name = "evil_test_link.lnk"; +#endif + const char *data = "evil_test_link symlink data\n"; + FILE *f; + ssize_t s1; + size_t s2; + int l; + + if (!test_link_test_file_create(old_name, data)) + return 0; + + if (symlink(old_name, new_name) < 0) + return 0; + + if ((s1 = readlink(new_name, buf, 1023)) < 0) + { + unlink(old_name); + unlink(new_name); + return 0; + } + + buf[s1] = '\0'; + + f = fopen(buf, "rb"); + if (!f) + { + unlink(old_name); + unlink(new_name); + return 0; + } + + l = strlen(data); + s2 = fread(buf, 1, l + 1, f); + + if ((int)s2 != (l + 1)) + { + fclose(f); + unlink(old_name); + unlink(new_name); + return 0; + } + + if (strcmp(buf, data)) + { + fclose(f); + unlink(old_name); + unlink(new_name); + return 0; + } + + fclose(f); + + if (unlink(new_name) < 0) + { + unlink(old_name); + return 0; + } + + if (unlink(old_name) < 0) + return 0; + + return 1; +} + +static int +test_link_tests_run(suite *s __UNUSED__) +{ + int res; + + res = test_link_test_symlink(); + res &= test_link_test_readlink(); + + return res; +} + +int +test_link(suite *s) +{ + + return test_link_tests_run(s); +} diff --git a/src/bin/evil/evil_test_link.h b/src/bin/evil/evil_test_link.h new file mode 100644 index 0000000000..6f8bfa2328 --- /dev/null +++ b/src/bin/evil/evil_test_link.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_LINK_H__ +#define __EVIL_TEST_LINK_H__ + + +int test_link(suite *s); + + +#endif /* __EVIL_TEST_LINK_H__ */ diff --git a/src/bin/evil/evil_test_memcpy.c b/src/bin/evil/evil_test_memcpy.c new file mode 100644 index 0000000000..0adfe373f0 --- /dev/null +++ b/src/bin/evil/evil_test_memcpy.c @@ -0,0 +1,145 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include "evil_suite.h" +#include "evil_test_memcpy.h" + + +typedef void *(*memcpy_decl)(void *dest, const void *src, size_t n); + +void *memcpy_glibc(void *dest, const void *src, size_t n); + + +static unsigned char *buf1 = NULL; +static unsigned char *buf2 = NULL; +static size_t page_size = 0; + + +#if defined (__MINGW32CE__) || defined (_MSC_VER) +static int +getpagesize() +{ + return 1024; +} +#endif /* __MINGW32CE__ || _MSC_VER */ + + +static void +test_memcpy_test_run(suite *s, memcpy_decl fct, char *dst, const char *src, size_t len) +{ + double best; + int i; + + best = 1000000000.0; + + for (i = 0; i < 128; ++i) + { + double time; + + suite_time_start(s); + fct(dst, src, len); + suite_time_stop(s); + time = suite_time_get(s); + if (time < best) best = time; + } + + printf (" %e", best); +} + +static void +test_memcpy_tests_run(suite *s, size_t align1, size_t align2, size_t len) +{ + size_t i, j; + char *s1, *s2; + + printf ("running test..\n"); + +/* align1 &= 63; */ +/* if (align1 + len >= page_size) */ +/* return; */ + +/* align2 &= 63; */ +/* if (align2 + len >= page_size) */ +/* return; */ + + s1 = (char *) (buf1 + align1); + s2 = (char *) (buf2 + align2); + + for (i = 0, j = 1; i < len; i++, j += 23) + s1[i] = j; + + printf ("length: %6d, align %2d/%2d:", (int)len, align1, align2); + + test_memcpy_test_run(s, memcpy, s2, s1, len); +#ifdef _WIN32_WCE + test_memcpy_test_run(s, memcpy_glibc, s2, s1, len); +#endif /* _WIN32_WCE */ + + printf ("\n"); +} + +int +test_memcpy(suite *s) +{ + size_t i; + + page_size = 2 * 1024; + + buf1 = (unsigned char *)malloc(16 * getpagesize()); + if (!buf1) return 0; + + buf2 = (unsigned char *)malloc(16 * getpagesize()); + if (!buf2) + { + free(buf1); + return 0; + } + + memset (buf1, 0xa5, page_size); + memset (buf2, 0x5a, page_size); + + for (i = 0; i < 5; ++i) + { + test_memcpy_tests_run(s, 0, 0, 1 << i); + test_memcpy_tests_run(s, i, 0, 1 << i); + test_memcpy_tests_run(s, 0, i, 1 << i); + test_memcpy_tests_run(s, i, i, 1 << i); + } + + for (i = 0; i < 32; ++i) + { + test_memcpy_tests_run(s, 0, 0, i); + test_memcpy_tests_run(s, i, 0, i); + test_memcpy_tests_run(s, 0, i, i); + test_memcpy_tests_run(s, i, i, i); + } + + for (i = 3; i < 32; ++i) + { + if ((i & (i - 1)) == 0) + continue; + test_memcpy_tests_run(s, 0, 0, 16 * i); + test_memcpy_tests_run(s, i, 0, 16 * i); + test_memcpy_tests_run(s, 0, i, 16 * i); + test_memcpy_tests_run(s, i, i, 16 * i); + } + + test_memcpy_tests_run(s, 0, 0, getpagesize ()); + test_memcpy_tests_run(s, 0, 0, 2 * getpagesize ()); + test_memcpy_tests_run(s, 0, 0, 4 * getpagesize ()); + test_memcpy_tests_run(s, 0, 0, 8 * getpagesize ()); + test_memcpy_tests_run(s, 0, 0, 16 * getpagesize ()); + + free(buf2); + free(buf1); + + return 1; +} diff --git a/src/bin/evil/evil_test_memcpy.h b/src/bin/evil/evil_test_memcpy.h new file mode 100644 index 0000000000..808dd09f37 --- /dev/null +++ b/src/bin/evil/evil_test_memcpy.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_MEMCPY__ +#define __EVIL_TEST_MEMCPY__ + + +int test_memcpy(suite *s); + + +#endif /* __EVIL_TEST_MEMCPY__ */ diff --git a/src/bin/evil/evil_test_mkstemp.c b/src/bin/evil/evil_test_mkstemp.c new file mode 100644 index 0000000000..dc8c4e64f5 --- /dev/null +++ b/src/bin/evil/evil_test_mkstemp.c @@ -0,0 +1,53 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_mkstemp.h" + + +static int +test_mkstemp_test(void) +{ + char _template[PATH_MAX]; +#ifdef _WIN32_WCE + char cwd[PATH_MAX]; +#endif + int fd; + +#ifdef _WIN32_WCE + if (!getcwd(cwd, PATH_MAX)) + return 0; + _snprintf(_template, PATH_MAX, "%s\\%s", cwd, "file_XXXXXX"); +#else + _snprintf(_template, PATH_MAX, "%s", "file_XXXXXX"); +#endif + + fd = mkstemp(_template); + + if (fd < 0) + return 0; + + return 1; +} + +static int +test_mkstemp_run(suite *s __UNUSED__) +{ + int res; + + res = test_mkstemp_test(); + + return res; +} + +int +test_mkstemp(suite *s) +{ + + return test_mkstemp_run(s); +} diff --git a/src/bin/evil/evil_test_mkstemp.h b/src/bin/evil/evil_test_mkstemp.h new file mode 100644 index 0000000000..f5bb0c4270 --- /dev/null +++ b/src/bin/evil/evil_test_mkstemp.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_MKSTEMP_H__ +#define __EVIL_TEST_MKSTEMP_H__ + + +int test_mkstemp(suite *s); + + +#endif /* __EVIL_TEST_MKSTEMP_H__ */ diff --git a/src/bin/evil/evil_test_pipe.c b/src/bin/evil/evil_test_pipe.c new file mode 100644 index 0000000000..2fc530d545 --- /dev/null +++ b/src/bin/evil/evil_test_pipe.c @@ -0,0 +1,126 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> + +# define WIN32_LEAN_AND_MEAN +# include <winsock2.h> +# undef WIN32_LEAN_AND_MEAN + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_pipe.h" + + +#define FDREAD 0 +#define FDWRITE 1 + +typedef struct +{ + int val; + int fd_write; +} data; + + +static DWORD WINAPI +thread (void *param) +{ + data *d; + void *buf[1]; + + Sleep (2 * 1000); + d = (data *)param; + buf[0] = d; + send(d->fd_write, (char *)buf, sizeof(buf), 0); + + return 0; +} + +static int +test_pipe_test(void) +{ + int sockets[2]; + struct timeval t; + fd_set rfds; + int ret; + data *d; + DWORD thread_id; + HANDLE h; + + FD_ZERO(&rfds); + + t.tv_sec = 5; + t.tv_usec = 0; + + if (pipe(sockets) < 0) + return 0; + + FD_SET(sockets[FDREAD], &rfds); + fcntl(sockets[FDREAD], F_SETFL, O_NONBLOCK); + + d = (data *)malloc(sizeof (data)); + if (!d) + return 0; + + d->val = 14; + d->fd_write = sockets[FDWRITE]; + + h = CreateThread(NULL, 0, thread, d, 0, &thread_id); + if (!h) + + ret = select(sockets[FDREAD] + 1, &rfds, NULL, NULL, &t); + + if (ret < 0) + goto free_d; + else if (ret == 0) + goto close_h; + else /* ret > 0 */ + { + void *buf[1]; + data *d2 = NULL; + int len; + + while ((len = recv(sockets[FDREAD], (char *)buf, sizeof(buf), 0)) > 0) + { + if (len == sizeof(buf)) + { + d2 = (data *)buf[0]; + break; + } + } + if (d2 && (d2->val == d->val)) + ret = 1; + else + ret = 0; + } + + CloseHandle(h); + free(d); + + return ret; + + close_h: + CloseHandle(h); + free_d: + free(d); + return 0; +} + +static int +test_pipe_run(suite *s __UNUSED__) +{ + int res; + + res = test_pipe_test(); + + return res; +} + +int +test_pipe(suite *s) +{ + return test_pipe_run(s); +} diff --git a/src/bin/evil/evil_test_pipe.h b/src/bin/evil/evil_test_pipe.h new file mode 100644 index 0000000000..ff8041f716 --- /dev/null +++ b/src/bin/evil/evil_test_pipe.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_PIPE_H__ +#define __EVIL_TEST_PIPE_H__ + + +int test_pipe(suite *s); + + +#endif /* __EVIL_TEST_PIPE_H__ */ diff --git a/src/bin/evil/evil_test_print.c b/src/bin/evil/evil_test_print.c new file mode 100644 index 0000000000..b930a538f4 --- /dev/null +++ b/src/bin/evil/evil_test_print.c @@ -0,0 +1,46 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_print.h" + +static int +test_print_test(void) +{ + char buf[16]; + int i1 = 1; + size_t i2 = 123456; + int res; + + res = printf("%02hhd\n", i1); + if (res != 3) + return 0; + + res = snprintf(buf, sizeof(buf), "%zu", i2); + if (res != 6) + return 0; + + return 1; +} + +static int +test_print_run(suite *s __UNUSED__) +{ + int res; + + res = test_print_test(); + + return res; +} + +int +test_print(suite *s) +{ + return test_print_run(s); +} diff --git a/src/bin/evil/evil_test_print.h b/src/bin/evil/evil_test_print.h new file mode 100644 index 0000000000..2bbf43904f --- /dev/null +++ b/src/bin/evil/evil_test_print.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_PRINT_H__ +#define __EVIL_TEST_PRINT_H__ + + +int test_print(suite *s); + + +#endif /* __EVIL_TEST_PRINT_H__ */ diff --git a/src/bin/evil/evil_test_realpath.c b/src/bin/evil/evil_test_realpath.c new file mode 100644 index 0000000000..f9a48cb977 --- /dev/null +++ b/src/bin/evil/evil_test_realpath.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_realpath.h" + + +static int +test_realpath_test(void) +{ + char buf[PATH_MAX]; + char *filename; + char *result; + + filename = "evil_suite.exe"; + + if (!(result = realpath(filename, buf))) + return 0; + + printf ("res : %s\n", buf); + + return 1; +} + +static int +test_realpath_run(suite *s __UNUSED__) +{ + int res; + + res = test_realpath_test(); + + return res; +} + +int +test_realpath(suite *s) +{ + return test_realpath_run(s); +} diff --git a/src/bin/evil/evil_test_realpath.h b/src/bin/evil/evil_test_realpath.h new file mode 100644 index 0000000000..0205aad149 --- /dev/null +++ b/src/bin/evil/evil_test_realpath.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_REALPATH_H__ +#define __EVIL_TEST_REALPATH_H__ + + +int test_realpath(suite *s); + + +#endif /* __EVIL_TEST_REALPATH_H__ */ diff --git a/src/bin/evil/evil_test_util.c b/src/bin/evil/evil_test_util.c new file mode 100644 index 0000000000..6226ceb040 --- /dev/null +++ b/src/bin/evil/evil_test_util.c @@ -0,0 +1,110 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include <Evil.h> + +#include "evil_suite.h" +#include "evil_test_util.h" + + +static int test_path_absolute_test_1(void) +{ + char *path; + int result; + + path = NULL; + result = evil_path_is_absolute(path); + if (result != 0) + return 0; + + return 1; +} + +static int test_path_absolute_test_2(void) +{ + char *path; + int result; + + path = "1"; + result = evil_path_is_absolute(path); + if (result != 0) + return 0; + + return 1; +} + +static int test_path_absolute_test_3(void) +{ + char *path; + int result; + + path = "1:\\"; + result = evil_path_is_absolute(path); + if (result != 0) + return 0; + + return 1; +} + +static int test_path_absolute_test_4(void) +{ + char *path; + int result; + + path = "1/\\"; + result = evil_path_is_absolute(path); + if (result != 0) + return 0; + + return 1; +} + +static int test_path_absolute_test_5(void) +{ + char *path; + int result; + + path = "F:/foo"; + result = evil_path_is_absolute(path); + if (result == 0) + return 0; + + return 1; +} + +static int test_path_absolute_test_6(void) +{ + char *path; + int result; + + path = "C:\\foo"; + result = evil_path_is_absolute(path); + if (result == 0) + return 0; + + return 1; +} + +static int +test_path_absolute_run(suite *s __UNUSED__) +{ + int res; + + res = test_path_absolute_test_1(); + res &= test_path_absolute_test_2(); + res &= test_path_absolute_test_3(); + res &= test_path_absolute_test_4(); + res &= test_path_absolute_test_5(); + res &= test_path_absolute_test_6(); + + return res; +} + +int +test_util(suite *s) +{ + return test_path_absolute_run(s); +} diff --git a/src/bin/evil/evil_test_util.h b/src/bin/evil/evil_test_util.h new file mode 100644 index 0000000000..bee5c7a164 --- /dev/null +++ b/src/bin/evil/evil_test_util.h @@ -0,0 +1,8 @@ +#ifndef __EVIL_TEST_UTIL_H__ +#define __EVIL_TEST_UTIL_H__ + + +int test_util(suite *s); + + +#endif /* __EVIL_TEST_UTIL_H__ */ diff --git a/src/bin/evil/memcpy_glibc_arm.S b/src/bin/evil/memcpy_glibc_arm.S new file mode 100644 index 0000000000..7c42898647 --- /dev/null +++ b/src/bin/evil/memcpy_glibc_arm.S @@ -0,0 +1,231 @@ +/* Copyright (C) 2006 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + Contributed by MontaVista Software, Inc. (written by Nicolas Pitre) + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Copyright (C) 2008 Vincent Torri + modification of the name and of the entry / end declaration + */ + +/* + * Data preload for architectures that support it (ARM V5TE and above) + */ +#if (!defined (__ARM_ARCH_2__) && !defined (__ARM_ARCH_3__) \ + && !defined (__ARM_ARCH_3M__) && !defined (__ARM_ARCH_4__) \ + && !defined (__ARM_ARCH_4T__) && !defined (__ARM_ARCH_5__) \ + && !defined (__ARM_ARCH_5T__)) +#define PLD(code...) code +#else +#define PLD(code...) +#endif + +/* + * This can be used to enable code to cacheline align the source pointer. + * Experiments on tested architectures (StrongARM and XScale) didn't show + * this a worthwhile thing to do. That might be different in the future. + */ +//#define CALGN(code...) code +#define CALGN(code...) + +/* + * Endian independent macros for shifting bytes within registers. + */ +#ifndef __ARMEB__ +#define pull lsr +#define push lsl +#else +#define pull lsl +#define push lsr +#endif + + .text + +/* Prototype: void *memcpy_glibc(void *dest, const void *src, size_t n); */ + + .align + .global memcpy_glibc + .func memcpy_glibc +memcpy_glibc: + + stmfd sp!, {r0, r4, lr} + + subs r2, r2, #4 + blt 8f + ands ip, r0, #3 + PLD( pld [r1, #0] ) + bne 9f + ands ip, r1, #3 + bne 10f + +1: subs r2, r2, #(28) + stmfd sp!, {r5 - r8} + blt 5f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb r3, ip, #32 ) + CALGN( sbcnes r4, r3, r2 ) @ C is always set here + CALGN( bcs 2f ) + CALGN( adr r4, 6f ) + CALGN( subs r2, r2, r3 ) @ C gets set + CALGN( add pc, r4, ip ) + + PLD( pld [r1, #0] ) +2: PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 4f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +3: PLD( pld [r1, #124] ) +4: ldmia r1!, {r3, r4, r5, r6, r7, r8, ip, lr} + subs r2, r2, #32 + stmia r0!, {r3, r4, r5, r6, r7, r8, ip, lr} + bge 3b + PLD( cmn r2, #96 ) + PLD( bge 4b ) + +5: ands ip, r2, #28 + rsb ip, ip, #32 + addne pc, pc, ip @ C is always clear here + b 7f +6: nop + ldr r3, [r1], #4 + ldr r4, [r1], #4 + ldr r5, [r1], #4 + ldr r6, [r1], #4 + ldr r7, [r1], #4 + ldr r8, [r1], #4 + ldr lr, [r1], #4 + + add pc, pc, ip + nop + nop + str r3, [r0], #4 + str r4, [r0], #4 + str r5, [r0], #4 + str r6, [r0], #4 + str r7, [r0], #4 + str r8, [r0], #4 + str lr, [r0], #4 + + CALGN( bcs 2b ) + +7: ldmfd sp!, {r5 - r8} + +8: movs r2, r2, lsl #31 + ldrneb r3, [r1], #1 + ldrcsb r4, [r1], #1 + ldrcsb ip, [r1] + strneb r3, [r0], #1 + strcsb r4, [r0], #1 + strcsb ip, [r0] + + ldmfd sp!, {r0, r4, pc} + +9: rsb ip, ip, #4 + cmp ip, #2 + ldrgtb r3, [r1], #1 + ldrgeb r4, [r1], #1 + ldrb lr, [r1], #1 + strgtb r3, [r0], #1 + strgeb r4, [r0], #1 + subs r2, r2, ip + strb lr, [r0], #1 + blt 8b + ands ip, r1, #3 + beq 1b + +10: bic r1, r1, #3 + cmp ip, #2 + ldr lr, [r1], #4 + beq 17f + bgt 18f + + + .macro forward_copy_shift pull push + + subs r2, r2, #28 + blt 14f + + CALGN( ands ip, r1, #31 ) + CALGN( rsb ip, ip, #32 ) + CALGN( sbcnes r4, ip, r2 ) @ C is always set here + CALGN( subcc r2, r2, ip ) + CALGN( bcc 15f ) + +11: stmfd sp!, {r5 - r9} + + PLD( pld [r1, #0] ) + PLD( subs r2, r2, #96 ) + PLD( pld [r1, #28] ) + PLD( blt 13f ) + PLD( pld [r1, #60] ) + PLD( pld [r1, #92] ) + +12: PLD( pld [r1, #124] ) +13: ldmia r1!, {r4, r5, r6, r7} + mov r3, lr, pull #\pull + subs r2, r2, #32 + ldmia r1!, {r8, r9, ip, lr} + orr r3, r3, r4, push #\push + mov r4, r4, pull #\pull + orr r4, r4, r5, push #\push + mov r5, r5, pull #\pull + orr r5, r5, r6, push #\push + mov r6, r6, pull #\pull + orr r6, r6, r7, push #\push + mov r7, r7, pull #\pull + orr r7, r7, r8, push #\push + mov r8, r8, pull #\pull + orr r8, r8, r9, push #\push + mov r9, r9, pull #\pull + orr r9, r9, ip, push #\push + mov ip, ip, pull #\pull + orr ip, ip, lr, push #\push + stmia r0!, {r3, r4, r5, r6, r7, r8, r9, ip} + bge 12b + PLD( cmn r2, #96 ) + PLD( bge 13b ) + + ldmfd sp!, {r5 - r9} + +14: ands ip, r2, #28 + beq 16f + +15: mov r3, lr, pull #\pull + ldr lr, [r1], #4 + subs ip, ip, #4 + orr r3, r3, lr, push #\push + str r3, [r0], #4 + bgt 15b + CALGN( cmp r2, #0 ) + CALGN( bge 11b ) + +16: sub r1, r1, #(\push / 8) + b 8b + + .endm + + + forward_copy_shift pull=8 push=24 + +17: forward_copy_shift pull=16 push=16 + +18: forward_copy_shift pull=24 push=8 + +.endfunc diff --git a/src/bin/evil/memcpy_glibc_i686.S b/src/bin/evil/memcpy_glibc_i686.S new file mode 100755 index 0000000000..72da118cf0 --- /dev/null +++ b/src/bin/evil/memcpy_glibc_i686.S @@ -0,0 +1,81 @@ +/* Copy memory block and return pointer to beginning of destination block + For Intel 80x86, x>=6. + This file is part of the GNU C Library. + Copyright (C) 1999, 2000, 2003, 2004 Free Software Foundation, Inc. + Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +# define CHECK_BOUNDS_BOTH_WIDE(VAL_REG, BP_MEM, LENGTH) \ + CHECK_BOUNDS_LOW(VAL_REG, BP_MEM); \ + addl LENGTH, VAL_REG; \ + cmpl 8+BP_MEM, VAL_REG; \ + jbe 0f; /* continue if value <= high */ \ + BOUNDS_VIOLATED; \ + 0: subl LENGTH, VAL_REG /* restore value */ + +# define RETURN_BOUNDED_POINTER(BP_MEM) \ + movl RTN(%esp), %edx; \ + movl %eax, 0(%edx); \ + movl 4+BP_MEM, %eax; \ + movl %eax, 4(%edx); \ + movl 8+BP_MEM, %eax; \ + movl %eax, 8(%edx) + +#define PTR_SIZE 12 +#define RTN_SIZE 4 +#define LINKAGE 8 + +#define PARMS LINKAGE /* no space for saved regs */ +#define RTN PARMS +#define DEST RTN+RTN_SIZE +#define SRC DEST+PTR_SIZE +#define LEN SRC+PTR_SIZE + + .text + + .align + .global memcpy_glibc + .func memcpy_glibc +memcpy_glibc: + + pushl %ebp + movl %esp, %ebp + + movl LEN(%esp), %ecx + movl %edi, %eax + movl DEST(%esp), %edi + movl %esi, %edx + movl SRC(%esp), %esi + + cld + shrl $1, %ecx + jnc 1f + movsb +1: shrl $1, %ecx + jnc 2f + movsw +2: rep + movsl + movl %eax, %edi + movl %edx, %esi + movl DEST(%esp), %eax + RETURN_BOUNDED_POINTER (DEST(%esp)) + + movl %ebp, %esp + popl %ebp + +.endfunc diff --git a/src/bin/evil/test_evil.c b/src/bin/evil/test_evil.c new file mode 100644 index 0000000000..5b91172cb3 --- /dev/null +++ b/src/bin/evil/test_evil.c @@ -0,0 +1,27 @@ +#include <stdlib.h> +#include <stdio.h> +#include <sys/time.h> + +#include <windows.h> + + + +int +main() +{ + struct timeval tv; + double t1 = 0.0; + double t2 = 0.0; + + if (gettimeofday(&tv, NULL) == 0) + t1 = tv.tv_sec + tv.tv_usec / 1000000.0; + + Sleep(3000); + + if (gettimeofday(&tv, NULL) == 0) + t2 = tv.tv_sec + tv.tv_usec / 1000000.0; + + printf ("3 seconds ? %f\n", t2 - t1); + + return EXIT_SUCCESS; +} diff --git a/src/lib/evil/Evil.h b/src/lib/evil/Evil.h new file mode 100644 index 0000000000..e807dffb61 --- /dev/null +++ b/src/lib/evil/Evil.h @@ -0,0 +1,186 @@ +#ifndef __EVIL_H__ +#define __EVIL_H__ + +/** + * @mainpage Evil + * @image html e_big.png + * @author Vincent Torri + * @version 1.7.0 + * @date 2008-2012 + * + * @section intro_sec Introduction + * + * The Evil library is an evil library that ports some evil Unix + * functions to the Windows (XP or above, or Mobile) platform. The + * evilness is so huge that the most of the functions are not POSIX or + * BSD compliant. + * + * These functions are intended to be used in the Enlightenment + * Foundation Libraries only and can be compiled only on Windows, + * using MSYS/MinGW on Windows, and cross-compilation on Unix. This + * library is minimal in the sense that only the functions needed to + * compile the EFL are available. The purpose of this library is NOT + * to have a full POSIX emulation et it is NOT a replacement of + * cygwin. To compare the size of the DLL themselves, Evil is around + * 33 KB and cygwin DLL is around 800 KB. + * + * @section acknowledgments_sec Acknowledgments + * + * This library has receive some from people interested in the EFL or + * not. Among them, evil thanks to Lars Munch, Raoul Hecky, Nicolas + * Aguirre, Tor Lillqvist, Lance Fetters, Vincent Richomme, Paul + * Vixie, Daniel Stenberg, who helped the author of the library in + * different fields (code and tests). + * + * @section license_sec license + * + * The Evil library is distributes under a modified BSD license. See + * the files COPYING and COPYING-PLAIN in the top level directory for + * the full license text. + * + * @section reference_sec Reference API + * + * Use the horizontal menu above to navigate into the reference API + */ + +/** + * @file Evil.h + * @brief The file that provides miscellaneous functions ported from Unix. + * @defgroup Evil Miscellaneous functions ported from Unix. + * + * This header provides miscellaneous functions that exist on Unix + * but not on Windows platform. They try to follow the conformance of + * the Unix versions. + */ + +/** + * @cond LOCAL + */ + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <limits.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <math.h> +#include <direct.h> + + +#ifdef _MSC_VER + +# include <io.h> + +# define F_OK 0 /* Check for file existence */ +# define X_OK 1 /* MS access() doesn't check for execute permission. */ +# define W_OK 2 /* Check for write permission */ +# define R_OK 4 /* Check for read permission */ + +typedef DWORD pid_t; +typedef unsigned short mode_t; + +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +typedef SSIZE_T ssize_t; + +# define strdup(s) _strdup(s) +# define unlink(filename) _unlink(filename) +# define fileno(f) _fileno(f) +# define fdopen(fd,m) _fdopen((fd),(m)) +# define access(p,m) _access((p),(m)) +# define hypot(x,y) _hypot((x),(y)) +# define tzset _tzset + +#endif /* _MSC_VER */ + +#ifdef _WIN32_WCE +# ifndef offsetof +# define offsetof(type, ident) ((size_t)&(((type*)0)->ident)) +# endif +#endif + +typedef unsigned long uid_t; +typedef unsigned long gid_t; + + +#include "evil_macro.h" +#include "evil_fcntl.h" +#include "evil_inet.h" +#include "evil_langinfo.h" +#include "evil_libgen.h" +#include "evil_main.h" +#include "evil_print.h" +#include "evil_stdlib.h" +#include "evil_stdio.h" +#include "evil_string.h" +#include "evil_time.h" +#include "evil_unistd.h" +#include "evil_util.h" +#include "evil_macro_pop.h" + + +#if (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) +# if defined(_MSC_VER) || defined(__MINGW32__) + +# ifdef S_ISDIR +# undef S_ISDIR +# endif +# ifdef S_ISREG +# undef S_ISREG +# endif +# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) + +# define S_ISLNK(m) 0 + +# define S_IRUSR _S_IRUSR +# define S_IWUSR _S_IWUSR +# define S_IXUSR _S_IXUSR +# define S_IRGRP S_IRUSR +# define S_IROTH S_IRUSR +# define S_IWGRP S_IWUSR +# define S_IWOTH S_IWUSR +# define S_IXGRP S_IXUSR +# define S_IXOTH S_IXUSR + +# define _S_IRWXU (_S_IREAD | _S_IWRITE | _S_IEXEC) +# define _S_IXUSR _S_IEXEC +# define _S_IWUSR _S_IWRITE +# define _S_IRUSR _S_IREAD + +# define mkdir(p,m) _mkdir(p) + /* +# define close(fd) _close(fd) +# define read(fd,buffer,count) _read((fd),(buffer),(count)) +# define write(fd,buffer,count) _write((fd),(buffer),(count)) +# define unlink(filename) _unlink((filename)) +# define lstat(f,s) _stat((f),(s)) + */ + +# endif +#endif + + +#ifdef __cplusplus +} +#endif + +/** + * @endcond + */ + +#endif /* __EVIL_H__ */ diff --git a/src/lib/evil/Makefile.am b/src/lib/evil/Makefile.am new file mode 100644 index 0000000000..62d0d56bf3 --- /dev/null +++ b/src/lib/evil/Makefile.am @@ -0,0 +1,132 @@ + +MAINTAINERCLEANFILES = Makefile.in + +lib_LTLIBRARIES = libevil.la libdl.la + +install_evilheadersdir = $(includedir)/evil-@VMAJ@ +dist_install_evilheaders_DATA = \ +Evil.h \ +evil_fcntl.h \ +evil_inet.h \ +evil_langinfo.h \ +evil_libgen.h \ +evil_macro.h \ +evil_macro_pop.h \ +evil_main.h \ +evil_print.h \ +evil_stdlib.h \ +evil_stdio.h \ +evil_string.h \ +evil_time.h \ +evil_unistd.h \ +evil_util.h + +stdheadersdir = $(includedir)/evil-@VMAJ@ +nobase_dist_stdheaders_DATA = pwd.h sys/mman.h fnmatch.h dirent.h dlfcn.h + +if EVIL_HAVE_WINCE + +nobase_dist_stdheaders_DATA += mingw32ce/errno.h + +endif + +# gdtoa +libevil_la_SOURCES = \ +gdtoa/arithchk.c \ +gdtoa/dmisc.c \ +gdtoa/dtoa.c \ +gdtoa/gd_arith.h \ +gdtoa/g_dfmt.c \ +gdtoa/gd_qnan.h \ +gdtoa/gdtoa.c \ +gdtoa/gdtoa_fltrnds.h \ +gdtoa/gdtoa.h \ +gdtoa/gdtoaimp.h \ +gdtoa/gethex.c \ +gdtoa/g_ffmt.c \ +gdtoa/g__fmt.c \ +gdtoa/gmisc.c \ +gdtoa/g_xfmt.c \ +gdtoa/hd_init.c \ +gdtoa/hexnan.c \ +gdtoa/misc.c \ +gdtoa/qnan.c \ +gdtoa/smisc.c \ +gdtoa/strtodg.c \ +gdtoa/strtof.c \ +gdtoa/strtopx.c \ +gdtoa/sum.c \ +gdtoa/ulp.c + +#evil +libevil_la_SOURCES += \ +evil_dirent.c \ +evil_fcntl.c \ +evil_fnmatch.c \ +evil_fnmatch_list_of_states.c \ +evil_inet.c \ +evil_langinfo.c \ +evil_libgen.c \ +evil_main.c \ +evil_mman.c \ +evil_pformata.c \ +evil_pformatw.c \ +evil_printa.c \ +evil_printw.c \ +evil_pwd.c \ +evil_stdlib.c \ +evil_stdio.c \ +evil_string.c \ +evil_time.c \ +evil_unistd.c \ +evil_util.c \ +evil_uuid.c \ +evil_pformat.h \ +evil_private.h \ +evil_fnmatch_private.h + +if EVIL_HAVE_WINCE + +libevil_la_SOURCES += evil_errno.c evil_link_ce.c + +else + +libevil_la_SOURCES += evil_link_xp.cpp + +endif + +libevil_la_CPPFLAGS = @EVIL_CPPFLAGS@ +libevil_la_CFLAGS = @EVIL_CFLAGS@ +libevil_la_CXXFLAGS = @EVIL_CXXFLAGS@ +libevil_la_LIBADD = @EVIL_LIBS@ +libevil_la_LDFLAGS = -no-undefined -Wl,--enable-auto-import -version-info @version_info@ + +if EVIL_HAVE_WINCE + +libevil_la_LINK = $(LINK) $(libevil_la_LDFLAGS) + +else + +libevil_la_LINK = $(CXXLINK) $(libevil_la_LDFLAGS) + +endif + +libdl_la_SOURCES = dlfcn.c + +libdl_la_CPPFLAGS = @EVIL_DLFCN_CPPFLAGS@ +libdl_la_CFLAGS = @EVIL_CFLAGS@ +libdl_la_LIBADD = $(top_builddir)/src/lib/libevil.la @EVIL_DLFCN_LIBS@ +libdl_la_LDFLAGS = -no-undefined -Wl,--enable-auto-import -version-info @version_info@ + +EXTRA_DIST = gdtoa/README gdtoa/README.mingw + +install-data-hook: + rm -f $(libdir)/libevil.la $(libdir)/libdl.la + +uninstall-local: + rm -f $(DESTDIR)$(bindir)/libevil-@VMAJ@.dll + rm -f $(DESTDIR)$(bindir)/libdl-@VMAJ@.dll + rm -f $(DESTDIR)$(libdir)/libevil.dll.a + rm -f $(DESTDIR)$(libdir)/libevil.a + rm -f $(DESTDIR)$(libdir)/libdl.dll.a + rm -f $(DESTDIR)$(libdir)/libdl.a diff --git a/src/lib/evil/dirent.h b/src/lib/evil/dirent.h new file mode 100644 index 0000000000..3fe9d12866 --- /dev/null +++ b/src/lib/evil/dirent.h @@ -0,0 +1,141 @@ +#ifndef __EVIL_DIRENT_H__ +#define __EVIL_DIRENT_H__ + +#ifdef EAPI +# undef EAPI +#endif /* EAPI */ + +#ifdef _WIN32 +# ifdef EFL_EVIL_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVIL_BUILD */ +#endif /* _WIN32 */ + + +/** + * @file dirent.h + * @brief The file that provides functions ported from Unix in dirent.h. + * @defgroup Evil_Dirent_Group Dirent.h functions + * + * This header provides functions ported from Unix in dirent.h. + * + * @{ + */ + + +#ifdef UNICODE +# include <wchar.h> +#endif + +/** + * @def DT_UNKNOWN + * Specifies that the file type is unknown. + */ +#define DT_UNKNOWN 0 + +/** + * @def DT_DIR + * Specifies that the file type is a directory. + */ +#define DT_DIR 4 + +/** + * @typedef DIR + * @brief A structure that describes a directory stream. + */ +typedef struct DIR DIR; + +/** + * @struct dirent + * @brief A structure that describes a directory stream. + */ +struct dirent +{ + char d_name[260 + 1]; /**< The filename. */ + int d_mode; /**< The mode */ + unsigned char d_type; /**< The type */ +}; + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/** + * @brief Open the given directory. + * + * @param name The directory to open. + * @return A pointer to the directory stream. + * + * This function opens the directory @p name and return the directory + * stream. On error or if @p dir is NULL, -1 is returned, and errno is + * set appropriately (on Windows XP only). On success, 0 is returned. + * + * @see closedir() + * @see readdir() + * + * Conformity: None. + * + * Supported OS: Windows XP, CE. + */ +EAPI DIR *opendir(char const *name); + +/** + * @brief Close the given directory. + * + * @param dir The directory stream to close. + * @return A pointer to the directory stream. + * + * This function closes the stream directory @p dir. On error or is + * @p path is NULL or an empty string, NULL is returned, and errno is set + * appropriately (on Windows XP only). + * + * @see opendir() + * @see readdir() + * + * Conformity: None. + * + * Supported OS: Windows XP, CE. + */ +EAPI int closedir(DIR *dir); + +/** + * @brief Read the given directory. + * + * @param dir The directory stream to read. + * @return A pointer to a dirent structure, @c NULL oterhwise. + * + * This function returns a pointer to a dirent structure representing + * the next directory entry in the directory stream pointed to by + * @p dir. It returns NULL on reaching the end of the directory stream + * or if an error occurred and errno is set appropriately (on Windows XP only). + * + * @see opendir() + * @see readdir() + * + * Conformity: None. + * + * Supported OS: Windows XP, CE. + */ +EAPI struct dirent *readdir(DIR *dir); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + + +/** + * @} + */ + + +#endif /* __EVIL_DIRENT_H__ */ diff --git a/src/lib/evil/dlfcn.c b/src/lib/evil/dlfcn.c new file mode 100644 index 0000000000..818cabf014 --- /dev/null +++ b/src/lib/evil/dlfcn.c @@ -0,0 +1,272 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> + +#if defined(__MINGW32CE__) || defined(_MSC_VER) +# include <limits.h> +#endif /* __MINGW32CE__ || _MSC_VER */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#ifdef _WIN32_WCE +# include <tlhelp32.h> /* CreateToolhelp32Snapshot */ +#else +# include <psapi.h> /* EnumProcessModules(Ex) */ +#endif + +#include "Evil.h" + +#include "dlfcn.h" + + +static char *dl_err = NULL; +static int dl_err_viewed = 0; + +static void +get_last_error(char *desc) +{ + char *str; + size_t l1; + size_t l2; + + str = evil_last_error_get(); + + l1 = strlen(desc); + l2 = strlen(str); + + if (dl_err) + free(dl_err); + + dl_err = (char *)malloc(sizeof(char) * (l1 + l2 + 1)); + if (!dl_err) + dl_err = strdup("not enough resource"); + else + { + memcpy(dl_err, desc, l1); + memcpy(dl_err + l1, str, l2); + dl_err[l1 + l2] = '\0'; + } + free(str); + dl_err_viewed = 0; +} + +void * +dlopen(const char* path, int mode __UNUSED__) +{ + HMODULE module = NULL; + + if (!path) + { + module = GetModuleHandle(NULL); + if (!module) + get_last_error("GetModuleHandle returned: "); + } + else + { + char *new_path; + size_t l; + unsigned int i; + + /* according to MSDN, we must change the slash to backslash */ + l = strlen(path); + new_path = (char *)malloc(sizeof(char) * (l + 1)); + if (!new_path) + { + if (dl_err) + free(dl_err); + dl_err = strdup("not enough resource"); + dl_err_viewed = 0; + return NULL; + } + for (i = 0; i <= l; i++) + { + if (path[i] == '/') + new_path[i] = '\\'; + else + new_path[i] = path[i]; + } +#ifdef UNICODE + { + wchar_t *wpath; + + wpath = evil_char_to_wchar(new_path); + module = LoadLibrary(wpath); + free(wpath); + } +#else + module = LoadLibraryEx(new_path, NULL, + LOAD_WITH_ALTERED_SEARCH_PATH); +#endif /* ! UNICODE */ + if (!module) + get_last_error("LoadLibraryEx returned: "); + + free(new_path); + } + + return module; +} + +int +dlclose(void* handle) +{ + if (FreeLibrary(handle)) + return 0; + else + { + get_last_error("FreeLibrary returned: "); + return -1; + } +} + +void * +dlsym(void *handle, const char *symbol) +{ + FARPROC fp = NULL; + LPCTSTR new_symbol; + + if (!symbol || !*symbol) return NULL; + +#ifdef UNICODE + new_symbol = evil_char_to_wchar(symbol); +#else + new_symbol = symbol; +#endif /* UNICODE */ + + if (handle == RTLD_DEFAULT) + { +#ifdef _WIN32_WCE + HANDLE snapshot; + MODULEENTRY32 module; + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | + TH32CS_SNAPMODULE | + TH32CS_GETALLMODS, + 0); + if (!snapshot) + return NULL; + + module.dwSize = sizeof(module); + if (Module32First(snapshot, &module)) + do { + fp = GetProcAddress(module.hModule, new_symbol); + if (fp) break; + } while (Module32Next(snapshot, &module)); + + CloseToolhelp32Snapshot(snapshot); +#else + HMODULE modules[1024]; + DWORD needed; + DWORD i; + + /* TODO: use EnumProcessModulesEx() on Windows >= Vista */ + if (!EnumProcessModules(GetCurrentProcess(), + modules, sizeof(modules), &needed)) + return NULL; + + for (i = 0; i < (needed / sizeof(HMODULE)); i++) + { + fp = GetProcAddress(modules[i], new_symbol); + if (fp) break; + } +#endif + } + else + fp = GetProcAddress(handle, new_symbol); + +#ifdef UNICODE + free((void *)new_symbol); +#endif /* UNICODE */ + + if (!fp) + get_last_error("GetProcAddress returned: "); + + return fp; +} + +int +dladdr (const void *addr __UNUSED__, Dl_info *info) +{ + TCHAR tpath[PATH_MAX]; + MEMORY_BASIC_INFORMATION mbi; + char *path; + char *tmp; + size_t length; + int ret = 0; + + if (!info) + return 0; + +#ifdef _WIN32_WINNT + length = VirtualQuery(addr, &mbi, sizeof(mbi)); + if (!length) + return 0; + + if (mbi.State != MEM_COMMIT) + return 0; + + if (!mbi.AllocationBase) + return 0; + + ret = GetModuleFileName((HMODULE)mbi.AllocationBase, (LPTSTR)&tpath, PATH_MAX); + if (!ret) + return 0; +#else + ret = GetModuleFileName(NULL, (LPTSTR)&tpath, PATH_MAX); + if (!ret) + return 0; +#endif + +#ifdef UNICODE + path = evil_wchar_to_char(tpath); +#else + path = tpath; +#endif /* ! UNICODE */ + + length = strlen (path); + if (length >= PATH_MAX) + { + length = PATH_MAX - 1; + path[PATH_MAX - 1] = '\0'; + } + + /* replace '/' by '\' */ + tmp = path; + while (*tmp) + { + if (*tmp == '/') *tmp = '\\'; + tmp++; + } + + memcpy (info->dli_fname, path, length + 1); + info->dli_fbase = NULL; + info->dli_sname = NULL; + info->dli_saddr = NULL; + +#ifdef UNICODE + free (path); +#endif /* ! UNICODE */ + + return 1; +} + +char * +dlerror (void) +{ + if (!dl_err_viewed) + { + dl_err_viewed = 1; + return dl_err; + } + else + { + if (dl_err) + free(dl_err); + return NULL; + } +} diff --git a/src/lib/evil/dlfcn.h b/src/lib/evil/dlfcn.h new file mode 100644 index 0000000000..610331b702 --- /dev/null +++ b/src/lib/evil/dlfcn.h @@ -0,0 +1,258 @@ +#ifndef __EVIL_DLFCN_H__ +#define __EVIL_DLFCN_H__ + + +#include <limits.h> + + +#ifdef EAPI +# undef EAPI +#endif /* EAPI */ + +#ifdef _WIN32 +# ifdef EFL_EVIL_DLFCN_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVIL_DLFCN_BUILD */ +#endif /* _WIN32 */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32_WCE +# ifndef PATH_MAX +# define PATH_MAX 260 +# endif +#endif + + +/** + * @file dlfcn.h + * @brief The file that provides functions to manage dynamic-link libraries + * @defgroup Dlfcn Functions that manage dynamic-link libraries. + * + * This header provides functions to load and unload dynamic-link + * libaries, to get the address of a symbol, and to get diagnostic + * information. + */ + + +/** + * @def RTLD_LAZY + * Lazy function call binding. + */ +# define RTLD_LAZY 0x00001 /* lazy function call binding */ + +/** + * @def RTLD_NOW + * Immediate function call binding. + */ +# define RTLD_NOW 0x00002 /* immediate function call binding */ + +/** + * @def RTLD_GLOBAL + * Symbols in this dlopen'ed obj are visible to other dlopen'ed objs. + */ +# define RTLD_GLOBAL 0x00100 /* symbols in this dlopen'ed obj are visible + to other dlopen'ed objs */ + +/** + * @def RTLD_NODELETE + * Symbols are not deleted when closed. + */ +#define RTLD_NODELETE 0x01000 /* do not delete object when closed. */ + +/** + * @def RTLD_DEFAULT + * Symbols are searched in all the DLL opened by the current process. + */ +#define RTLD_DEFAULT ((void*)1) /* search the symbol on all the DLL of the current process */ + +/** + * @typedef Dl_info + * @brief A structure that stores infomation of a calling process. + */ +typedef struct Dl_info Dl_info; + +/** + * @struct Dl_info + * @brief A structure that stores infomation of a calling process. + */ +struct Dl_info +{ + char dli_fname[PATH_MAX]; /**< Filename of defining object */ + void *dli_fbase; /**< Load address of that object */ + const char *dli_sname; /**< Name of nearest lower symbol */ + void *dli_saddr; /**< Exact value of nearest symbol */ +}; + +/** + * @brief Map a specified executable module (either a .dll or .exe file) + * into the address space of the user process. + * + * @param path Name of the module. + * @param mode Unused. + * @return A pointer that represent the module, or @c NULL on failure. + * + * Map a specified executable module (either a .dll or .exe file) + * into the address space of the user process. If @p path is @c NULL, + * then the module corresponding to the current process is returned. + * Otherwise the module specified by @p path is loaded if it exists. + * If not, @c NULL is returned. The directory separators can be forward + * slash, or backward ones. Mapping a module can map other modules. + * @p mode is unused. + * + * If an error occurred, an error string can be retrived with dlerror(). + * + * According to the OS, the search order of the module can change, + * according to the value of SafeDllSearchMode. + * + * - For Windows Vista, Windows Server 2003, and Windows XP SP2: + * SafeDLLSearchMode is enabled by default. + * - For Windows XP and Windows 2000 SP4: SafeDLLSearchMode is disabled + * by default. + * + * If SafeDllSearchMode is enabled + * - The directory from which the application loaded. + * - The system directory. Use the GetSystemDirectory() function + * to get the path of this directory. + * - The 16-bit system directory. There is no function that obtains + * the path of this directory, but it is searched. + * - The Windows directory. Use the GetWindowsDirectory() function + * to get the path of this directory. + * - The current directory. + * - The directories that are listed in the PATH environment variable. + * Note that this does not include the per-application path specified + * by the App Paths registry key. + * + * If SafeDllSearchMode is disabled + * - The directory from which the application loaded. + * - The current directory. + * - The system directory. Use the GetSystemDirectory() function + * to get the path of this directory. + * - The 16-bit system directory. There is no function that obtains + * the path of this directory, but it is searched. + * - The Windows directory. Use the GetWindowsDirectory() function + * to get the path of this directory. + * - The directories that are listed in the PATH environment variable. + * Note that this does not include the per-application path specified + * by the App Paths registry key. + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Dlfcn + */ +EAPI void *dlopen(const char* path, int mode); + +/** + * @brief Close a dynamic-link library. + * + * @param handle Handle that references a dynamic-link library. + * @return O on sucess, -1 otherwise. + * + * Release a reference to the dynamic-link library referenced + * by @p handle. If the reference count drops to 0, the handle is + * removed from the address space and is rendered invalid. @p handle + * is the value returned by a previous call to dlopen(). + * + * If no error occurred, the returned value is 0, otherwise the + * returned value is -1 and an error string can be retrived with + * dlerror(). + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Dlfcn + */ +EAPI int dlclose(void* handle); + +/** + * @brief Get the address of a symbol. + * + * @param handle Handle that references a dynamic-link library. + * @param symbol @c NULL-terminated string. + * @return O on sucess, NULL otherwise. + * + * Return the address of the code or data location specified by the + * string @p symbol. @p handle references a library that contains + * the function or variable @p symbol. + * + * If no error occurred, the returned value is the code or data + * location, otherwise the returned value is NULL and an error + * string can be retrived with dlerror(). + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Dlfcn + */ +EAPI void *dlsym(void* handle, const char* symbol); + +/** + * @brief Get the location of the current process (.exe) + * + * @param addr Unused. + * @param info Pointer to the Dl_info to fill. + * @return 1 on success, 0 otherwise. + * + * Fill the dli_fname member of @p info with the absolute name + * of the current calling process (.exe file that is executed). + * All other members are set to @c NULL. + * + * Contrary to the unix function, the full name of the shared + * library is not returned, but insted the full name of the current + * calling process (.exe file). + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Dlfcn + */ +EAPI int dladdr (const void *addr, Dl_info *info); + +/** + * @brief Get diagnostic information + * + * @return A @c NULL-terminated string if an error occured, @c NULL + * otherwise. + * + * Return a @c NULL-terminated character string describing the last + * error that occurred on this thread during a call to dlopen(), + * dlsym(), or dlclose(). If no such error has occurred, dlerror() + * returns a null pointer. At each call to dlerror(), the error + * indication is reset. Thus in the case of two calls to dlerror(), + * where the second call follows the first immediately, the second + * call will always return a null pointer. + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Dlfcn + */ +EAPI char *dlerror (void); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __EVIL_DLFCN_H__ */ diff --git a/src/lib/evil/evil_dirent.c b/src/lib/evil/evil_dirent.c new file mode 100644 index 0000000000..e02b95fe27 --- /dev/null +++ b/src/lib/evil/evil_dirent.c @@ -0,0 +1,197 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <dirent.h> + +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif + +#include "Evil.h" + + +struct DIR +{ + struct dirent dirent; + WIN32_FIND_DATA data; + HANDLE handle; +}; + + +DIR *opendir(char const *name) +{ + DIR *dir; + char *tmp1; + char *tmp2; + DWORD attr; + size_t l; +#ifdef UNICODE + wchar_t *wname; + char *d_name; +#endif + + /* valid name */ + if (!name || !*name) + { +#ifdef HAVE_ERRNO_H + errno = ENOENT; +#endif + return NULL; + } + +#ifdef UNICODE + wname = evil_char_to_wchar(name); + if (!wname) + { +# ifdef HAVE_ERRNO_H + errno = ENOMEM; +# endif + return NULL; + } + + if((attr = GetFileAttributes(wname)) == 0xFFFFFFFF) +#else + if((attr = GetFileAttributes(name)) == 0xFFFFFFFF) +#endif + { +#ifdef HAVE_ERRNO_H + errno = ENOENT; +#endif + return NULL; + } + +#ifdef UNICODE + free(wname); +#endif + + /* directory */ + if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) + { +#ifdef HAVE_ERRNO_H + errno = ENOTDIR; +#endif + return NULL; + } + + dir = (DIR *)malloc(sizeof(DIR)); + if (!dir) + { +#ifdef HAVE_ERRNO_H + errno = ENOMEM; +#endif + return NULL; + } + + l = strlen(name); + tmp1 = (char *)malloc(sizeof(char) * l + 5); + if (!tmp1) + { +#ifdef HAVE_ERRNO_H + errno = ENOMEM; +#endif + return NULL; + } + + memcpy(tmp1, name, l); + memcpy(tmp1 + l, "\\*.*", 5); + + tmp2 = tmp1; + while (*tmp2) + { + if (*tmp2 == '/') *tmp2 = '\\'; + tmp2++; + } + +#ifdef UNICODE + wname = evil_char_to_wchar(tmp1); + if (!wname) + { +#ifdef HAVE_ERRNO_H + errno = ENOMEM; +#endif + free(tmp1); + + return NULL; + } + dir->handle = FindFirstFile(wname, &dir->data); + free(wname); +#else + dir->handle = FindFirstFile(tmp1, &dir->data); +#endif + + free(tmp1); + + if (dir->handle == INVALID_HANDLE_VALUE) + { + free(dir); + return NULL; + } + +#ifdef UNICODE + d_name = evil_wchar_to_char(dir->data.cFileName); + strcpy(dir->dirent.d_name, d_name); + free(d_name); +#else + strcpy(dir->dirent.d_name, dir->data.cFileName); +#endif + dir->dirent.d_mode = (int)dir->data.dwFileAttributes; + + if (dir->data.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) + dir->dirent.d_type = DT_DIR; + else + dir->dirent.d_type = DT_UNKNOWN; + + return dir; +} + +int closedir(DIR *dir) +{ + if (!dir) + { +#ifdef HAVE_ERRNO_H + errno = EBADF; +#endif + return -1; + } + + if (dir->handle != INVALID_HANDLE_VALUE) + FindClose(dir->handle); + free(dir); + + return 0; +} + +struct dirent *readdir(DIR *dir) +{ +#ifdef UNICODE + char *d_name; +#endif + + if (!dir) + { +#ifdef HAVE_ERRNO_H + errno = EBADF; +#endif + return NULL; + } + + if (dir->handle == INVALID_HANDLE_VALUE) + return NULL; + +#ifdef UNICODE + d_name = evil_wchar_to_char(dir->data.cFileName); + strcpy(dir->dirent.d_name, d_name); + free(d_name); +#else + strcpy(dir->dirent.d_name, dir->data.cFileName); +#endif + + if (!FindNextFile(dir->handle, &dir->data)) + { + FindClose(dir->handle); + dir->handle = INVALID_HANDLE_VALUE; + } + + return &dir->dirent; +} diff --git a/src/lib/evil/evil_errno.c b/src/lib/evil/evil_errno.c new file mode 100644 index 0000000000..37cac611f0 --- /dev/null +++ b/src/lib/evil/evil_errno.c @@ -0,0 +1,9 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "Evil.h" +#include "mingw32ce/errno.h" + + +int errno = 0; diff --git a/src/lib/evil/evil_fcntl.c b/src/lib/evil/evil_fcntl.c new file mode 100644 index 0000000000..7c62c2a310 --- /dev/null +++ b/src/lib/evil/evil_fcntl.c @@ -0,0 +1,124 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> + +#ifdef _MSC_VER +# include <io.h> /* for _get_osfhandle _lseek and _locking */ +#endif + +#include <sys/locking.h> + +#include <winsock2.h> /* for ioctlsocket */ + +#include "Evil.h" + + +#ifdef __MINGW32CE__ +# define _get_osfhandle(FILEDES) ((HANDLE)FILEDES) +#endif /* __MINGW32CE__ */ + + +/* + * port of fcntl function + * + */ + +int fcntl(int fd, int cmd, ...) +{ + va_list va; + HANDLE h; + int res = -1; + + va_start (va, cmd); + + h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) + return -1; + + if (cmd == F_GETFD) + { +#ifndef __MINGW32CE__ + DWORD flag; + + if (!GetHandleInformation(h, &flag)) + return -1; + + res = 0; +#endif /* ! __MINGW32CE__ */ + } + + if (cmd == F_SETFD) + { + long flag; + + flag = va_arg(va, long); + if (flag == FD_CLOEXEC) + { +#ifndef __MINGW32CE__ + if (SetHandleInformation(h, HANDLE_FLAG_INHERIT, 0)) + res = 0; +#endif /* ! __MINGW32CE__ */ + } + } + else if (cmd == F_SETFL) + { + long flag; + + flag = va_arg(va, long); + if (flag == O_NONBLOCK) + { + u_long arg = 1; + int type; + int len; + int ret; + + len = (int)sizeof(int); + ret = getsockopt((SOCKET)fd, SOL_SOCKET, SO_TYPE, (char *)&type, &len); + if (!ret && (type == SOCK_STREAM)) + { + if (!ioctlsocket((SOCKET)fd, FIONBIO, &arg) == SOCKET_ERROR) + res = 0; + } + } + } +#ifndef __MINGW32CE__ + else if ((cmd == F_SETLK) || (cmd == F_SETLKW)) + { + struct flock *fl; + off_t length = 0; + long pos; + + fl = va_arg(va, struct flock *); + + if (fl->l_len == 0) + { + length = _lseek(fd, 0L, SEEK_END); + if (length != -1L) + res = 0; + } + fl->l_len = length - fl->l_start - 1; + + pos = _lseek(fd, fl->l_start, fl->l_whence); + if (pos != -1L) + res = 0; + + if ((fl->l_type == F_RDLCK) || (fl->l_type == F_WRLCK)) + { + if (cmd == F_SETLK) + res = _locking(fd, _LK_NBLCK, fl->l_len); /* if cannot be locked, we return immediatly */ + else /* F_SETLKW */ + res = _locking(fd, _LK_LOCK, fl->l_len); /* otherwise, we try several times */ + } + + if (fl->l_type == F_UNLCK) + res = _locking(fd, _LK_UNLCK, fl->l_len); + } + +#endif /* ! __MINGW32CE__ */ + + va_end(va); + + return res; +} diff --git a/src/lib/evil/evil_fcntl.h b/src/lib/evil/evil_fcntl.h new file mode 100644 index 0000000000..194341b369 --- /dev/null +++ b/src/lib/evil/evil_fcntl.h @@ -0,0 +1,110 @@ +#ifndef __EVIL_FCNTL_H__ +#define __EVIL_FCNTL_H__ + + +# include <sys/types.h> + + +/** + * @def FD_CLOEXEC + * Specifies that the file descriptor should be closed when an exec() + * function is invoked. + */ +# define FD_CLOEXEC 1 + +/** + * @def O_NONBLOCK + * Specifies that the socket is in non-blocking mode. + */ +# define O_NONBLOCK 04000 + +/** + * @def F_SETFD + * Specifies that fcntl() should set the file descriptor flags + * associated with the filedes argument. + */ + +/** + * @def F_SETLK + * Specifies that fcntl() should set or clear a file segment lock + * according to the lock description pointed to by the third argument. + */ + +/** + * @def F_SETLKW + * Equivalent to F_SETLK except that if a shared or exclusive lock + * is blocked by other locks, the thread shall wait until the request + * can be satisfied. + */ + +# define F_GETFD 1 +# define F_SETFD 2 +# define F_SETFL 4 +# define F_SETLK 6 +# define F_SETLKW 7 + +/** + * @def F_RDLCK + * Read (or shared) lock + */ + +/** + * @def F_WRLCK + * Write (or exclusive) lock + */ + +/** + * @def F_UNLCK + * Remove lock + */ + +# ifndef F_RDLCK +# define F_RDLCK 0 +# define F_WRLCK 1 +# define F_UNLCK 2 +# endif /* ! F_RDLCK */ + +/** + * @struct flock + * @brief A structure that controls the lock of a file descriptor. + */ +struct flock +{ + short int l_type; /**< lock type: read, write, ... */ + short int l_whence; /**< type of l_start */ + off_t l_start; /**< starting offset */ + off_t l_len; /**< 0 means end of the file */ + pid_t l_pid; /**< lock owner */ +}; + + +/** + * @brief Provide control over file descriptors. + * + * @param fd The file descriptor. + * @param cmd The type of control. + * @return 0 on success, -1 otherwise. + * + * Performs one of various miscellaneous operations on @p fd. + * The operation in question is determined by @p cmd: + * + * - F_SETFD: Set the close-on-exec flag to the value specified + * by the argument after command (only the least significant + * bit is used). + * - F_SETLK and F_SETLKW: used to manage discretionary file locks. + * The third argument must be a pointer to a struct flock (that + * may be overwritten by this call). + * + * This function returns 0 on success, -1 otherwise. + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Evil + */ +EAPI int fcntl(int fd, int cmd, ...); + + +#endif /* __EVIL_FCNTL_H__ */ diff --git a/src/lib/evil/evil_fnmatch.c b/src/lib/evil/evil_fnmatch.c new file mode 100644 index 0000000000..0b4af7bfb0 --- /dev/null +++ b/src/lib/evil/evil_fnmatch.c @@ -0,0 +1,231 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <assert.h> +#include <string.h> + +#include "fnmatch.h" +#include "evil_fnmatch_private.h" + +enum fnmatch_status +{ + fnmatch_not_found = 0, + fnmatch_found = 1, + fnmatch_syntax_error = 2 +}; + +static +size_t +fnmatch_match_class_token(enum fnmatch_status *status, + const char *class_token, + char c) +{ + if (! *class_token) + { + *status = fnmatch_syntax_error; + return 0; + } + else if (class_token[1] == '-' && class_token[2] != ']') + { + if (class_token[0] <= c && c <= class_token[2]) + *status = fnmatch_found; + return 3; + } + else + { + if (c == *class_token) + *status = fnmatch_found; + return 1; + } +} + +static +size_t +fnmatch_complement_class(const char *class_token) +{ + switch (*class_token) + { + case 0: + return FNM_SYNTAXERR; + + case '!': + return 1; + + default: + return 0; + } +} + +static +size_t +fnmatch_match_class(const char *class, + char c) +{ + const size_t complement = fnmatch_complement_class(class + 1); + enum fnmatch_status status; + size_t pos; + + if (complement == FNM_SYNTAXERR) + return FNM_SYNTAXERR; + + status = fnmatch_not_found; + pos = 1 + complement; + + do + pos += fnmatch_match_class_token(&status, class + pos, c); + while (class[pos] && class[pos] != ']'); + + if (status == fnmatch_syntax_error || ! class[pos]) + return FNM_SYNTAXERR; + + if (status == fnmatch_found) + return complement ? 0 : pos + 1; + else + return complement ? pos + 1 : 0; +} + +static +size_t +fnmatch_chrcasecmp(char a, + char b) +{ + if ('A' <= a && a <= 'Z') + a += 'a' - 'A'; + if ('A' <= b && b <= 'Z') + b += 'a' - 'A'; + return a == b; +} + +static +size_t +fnmatch_match_token(const char *token, + char c, + e_bool leading, + int flags) +{ + if (*token == '\\' && !(flags & FNM_NOESCAPE)) + return token[1] ? (token[1] == c ? 2 : 0) : FNM_SYNTAXERR; + + if (c == '/' && (flags & FNM_PATHNAME)) + return *token == '/'; + + if (c == '.' && leading && (flags & FNM_PERIOD)) + return *token == '.'; + + switch (*token) + { + case '?': + return 1; + + case '[': + return fnmatch_match_class(token, c); + + default: + if (flags & FNM_CASEFOLD) + return fnmatch_chrcasecmp(*token, c); + return *token == c ? 1 : 0; + } +} + +static +void +fnmatch_init_states(struct list_of_states *states) +{ + states->size = 1; + states->states[0] = 0; + memset(states->has, 0, states->reserved * sizeof (*states->has)); + states->has[0] = 1; +} + +static +size_t +fnmatch_check_finals(const char *pattern, + const struct list_of_states *states) +{ + size_t i, j; + for (i = 0; i < states->size; ++i) + { + e_bool match = 1; + + for (j = states->states[i]; pattern[j]; ++j) + if (pattern[j] != '*') + { + match = 0; + break; + } + + if (match) + return 0; + } + return FNM_NOMATCH; +} + +int +fnmatch(const char *pattern, + const char *string, + int flags) +{ + struct list_of_states *states; + struct list_of_states *new_states; + e_bool leading = 1; + char *c; + size_t r; + + assert(pattern); + assert(string); + + states = fnmatch_list_of_states_alloc(2, strlen(pattern)); + new_states = states + 1; + + if (! states) + return FNM_NOMEM; + fnmatch_init_states(states); + + + for (c = (char *)string; *c && states->size; ++c) + { + size_t i; + fnmatch_list_of_states_clear(new_states); + + for (i = 0; i < states->size; ++i) + { + const size_t pos = states->states[i]; + + if (! pattern[pos]) + { + if (*c == '/' && (flags & FNM_LEADING_DIR)) + return 0; + continue; + } + else if (pattern[pos] == '*') + { + fnmatch_list_of_states_insert(states, pos + 1); + if ((*c != '/' || !(flags & FNM_PATHNAME)) && + (*c != '.' || !leading || !(flags & FNM_PERIOD))) + fnmatch_list_of_states_insert(new_states, pos); + } + else + { + const size_t m = fnmatch_match_token(pattern + pos, *c, + leading, flags); + + if (m == FNM_SYNTAXERR) + return FNM_SYNTAXERR; + else if (m) + fnmatch_list_of_states_insert(new_states, pos + m); + } + } + { + struct list_of_states *tmp = states; + + states = new_states; + new_states = tmp; + } + leading = *c == '/' && (flags & FNM_PATHNAME); + } + + r = fnmatch_check_finals(pattern, states); + fnmatch_list_of_states_free(states < new_states ? states : new_states, 2); + return (int)r; +} diff --git a/src/lib/evil/evil_fnmatch_list_of_states.c b/src/lib/evil/evil_fnmatch_list_of_states.c new file mode 100644 index 0000000000..c6cfb7fe81 --- /dev/null +++ b/src/lib/evil/evil_fnmatch_list_of_states.c @@ -0,0 +1,77 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "evil_fnmatch_private.h" + +struct list_of_states* +fnmatch_list_of_states_alloc(size_t n, + size_t pattern_len) +{ + struct list_of_states *l; + + const size_t reserved = pattern_len + 1; + const size_t states_size = sizeof (*l->states) * reserved; + const size_t has_size = sizeof (*l->has) * reserved; + const size_t states_has_size = states_size + has_size; + const size_t struct_size = sizeof (*l) + states_has_size; + + unsigned char *states; + unsigned char *has; + size_t i; + + l = malloc(n * struct_size); + if (!l) + return 0; + + states = (unsigned char *) (l + n); + has = states + states_size; + + for (i = 0; i < n; ++i) + { + l[i].reserved = reserved; + l[i].states = (size_t *) states; + l[i].has = (e_bool *) has; + states += states_has_size; + has += states_has_size; + } + + return l; +} + +void +fnmatch_list_of_states_free(struct list_of_states *lists, + size_t n) +{ + assert(lists); + + (void) n; + free(lists); +} + +void +fnmatch_list_of_states_insert(struct list_of_states *list, + size_t state) +{ + assert(list); + assert(state < list->reserved); + + if (list->has[state]) + return; + + list->states[list->size++] = state; + list->has[state] = 1; +} + +void +fnmatch_list_of_states_clear(struct list_of_states *list) +{ + assert(list); + + list->size = 0; + memset(list->has, 0, list->reserved * sizeof (*list->has)); +} diff --git a/src/lib/evil/evil_fnmatch_private.h b/src/lib/evil/evil_fnmatch_private.h new file mode 100644 index 0000000000..f5ced5d68f --- /dev/null +++ b/src/lib/evil/evil_fnmatch_private.h @@ -0,0 +1,24 @@ +#ifndef __EVIL_FNMATCH_PRIVATE_H__ +#define __EVIL_FNMATCH_PRIVATE_H__ + + +typedef int e_bool; + +struct list_of_states +{ + size_t reserved; + size_t size; + size_t *states; + e_bool *has; +}; + +struct list_of_states *fnmatch_list_of_states_alloc(size_t n, size_t pattern_len); + +void fnmatch_list_of_states_free(struct list_of_states *lists, size_t n); + +void fnmatch_list_of_states_insert(struct list_of_states *list, size_t state); + +void fnmatch_list_of_states_clear(struct list_of_states *list); + + +#endif /* __EVIL_FNMATCH_PRIVATE_H__ */ diff --git a/src/lib/evil/evil_inet.c b/src/lib/evil/evil_inet.c new file mode 100644 index 0000000000..99ce14edc2 --- /dev/null +++ b/src/lib/evil/evil_inet.c @@ -0,0 +1,648 @@ + +/* + * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") + * Copyright (c) 1996,1999 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * Modifications: Vincent Torri, for the integration in Evil + * - modification of the name of some functions + * * modification of the management of the error + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> +#include <ctype.h> + +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif /* HAVE_ERRNO_H */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <winsock2.h> +#undef WIN32_LEAN_AND_MEAN + +#include "evil_macro.h" +#include "evil_inet.h" +#include "evil_private.h" +#define APICHAR char +#include "evil_print.h" + +#ifndef EMSGSIZE +# define EMSGSIZE WSAEMSGSIZE +#endif + +#ifndef EAFNOSUPPORT +# define EAFNOSUPPORT WSAEAFNOSUPPORT +#endif + +#define SPRINTF(x) ((size_t)sprintf x) + +#define ERRNO ((int)GetLastError()) +#define SET_ERRNO(x) (SetLastError((DWORD)(x))) + +#define ISDIGIT(x) (isdigit((int) ((unsigned char)x))) +#define ISXDIGIT(x) (isxdigit((int) ((unsigned char)x))) +#define ISUPPER(x) (isupper((int) ((unsigned char)x))) + +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 +#define NS_INADDRSZ sizeof(IN_ADDR) + + +struct ares_in6_addr { + union { + unsigned char _S6_u8[16]; + } _S6_un; +}; + +const struct ares_in6_addr ares_in6addr_any = { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; + + +/* + * static int + * inet_net_pton_ipv4(src, dst, size) + * convert IPv4 network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv4 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0b11110000 in its fourth octet. + * note: + * On Windows we store the error in the thread errno, not + * in the winsock error code. This is to avoid loosing the + * actual last winsock error. So use macro ERRNO to fetch the + * errno this funtion sets when returning (-1), not SOCKERRNO. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +inet_net_pton_ipv4(const char *src, unsigned char *dst, size_t size) +{ + static const char xdigits[] = "0123456789abcdef"; + static const char digits[] = "0123456789"; + int n, ch, tmp = 0, dirty, bits; + const unsigned char *odst = dst; + + ch = *src++; + if (ch == '0' && (src[0] == 'x' || src[0] == 'X') + && ISXDIGIT(src[1])) { + /* Hexadecimal: Eat nybble string. */ + if (!size) + goto emsgsize; + dirty = 0; + src++; /* skip x or X. */ + while ((ch = *src++) != '\0' && ISXDIGIT(ch)) { + if (ISUPPER(ch)) + ch = tolower(ch); + n = (int)(strchr(xdigits, ch) - xdigits); + if (dirty == 0) + tmp = n; + else + tmp = (tmp << 4) | n; + if (++dirty == 2) { + if (!size--) + goto emsgsize; + *dst++ = (unsigned char) tmp; + dirty = 0; + } + } + if (dirty) { /* Odd trailing nybble? */ + if (!size--) + goto emsgsize; + *dst++ = (unsigned char) (tmp << 4); + } + } else if (ISDIGIT(ch)) { + /* Decimal: eat dotted digit string. */ + for (;;) { + tmp = 0; + do { + n = (int)(strchr(digits, ch) - digits); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = *src++) != '\0' && + ISDIGIT(ch)); + if (!size--) + goto emsgsize; + *dst++ = (unsigned char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + ch = *src++; + if (!ISDIGIT(ch)) + goto enoent; + } + } else + goto enoent; + + bits = -1; + if (ch == '/' && + ISDIGIT(src[0]) && dst > odst) { + /* CIDR width specifier. Nothing can follow it. */ + ch = *src++; /* Skip over the /. */ + bits = 0; + do { + n = (int)(strchr(digits, ch) - digits); + bits *= 10; + bits += n; + } while ((ch = *src++) != '\0' && ISDIGIT(ch)); + if (ch != '\0') + goto enoent; + if (bits > 32) + goto emsgsize; + } + + /* Firey death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + /* If no CIDR spec was given, infer width from net class. */ + if (bits == -1) { + if (*odst >= 240) /* Class E */ + bits = 32; + else if (*odst >= 224) /* Class D */ + bits = 8; + else if (*odst >= 192) /* Class C */ + bits = 24; + else if (*odst >= 128) /* Class B */ + bits = 16; + else /* Class A */ + bits = 8; + /* If imputed mask is narrower than specified octets, widen. */ + if (bits < ((dst - odst) * 8)) + bits = (int)(dst - odst) * 8; + /* + * If there are no additional bits specified for a class D + * address adjust bits to 4. + */ + if (bits == 8 && *odst == 224) + bits = 4; + } + /* Extend network to cover the actual mask. */ + while (bits > ((dst - odst) * 8)) { + if (!size--) + goto emsgsize; + *dst++ = '\0'; + } + return (bits); + + enoent: + SET_ERRNO(ENOENT); + return (-1); + + emsgsize: + SET_ERRNO(EMSGSIZE); + return (-1); +} + +static int +getbits(const char *src, int *bitsp) +{ + static const char digits[] = "0123456789"; + int n; + int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 128) /* range */ + return (0); + continue; + } + return (0); + } + if (n == 0) + return (0); + *bitsp = val; + return (1); +} + +static int +getv4(const char *src, unsigned char *dst, int *bitsp) +{ + static const char digits[] = "0123456789"; + unsigned char *odst = dst; + int n; + unsigned int val; + char ch; + + val = 0; + n = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + pch = strchr(digits, ch); + if (pch != NULL) { + if (n++ != 0 && val == 0) /* no leading zeros */ + return (0); + val *= 10; + val += (pch - digits); + if (val > 255) /* range */ + return (0); + continue; + } + if (ch == '.' || ch == '/') { + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = (unsigned char)val; + if (ch == '/') + return (getbits(src, bitsp)); + val = 0; + n = 0; + continue; + } + return (0); + } + if (n == 0) + return (0); + if (dst - odst > 3) /* too many octets? */ + return (0); + *dst++ = (unsigned char)val; + return (1); +} + +static int +inet_net_pton_ipv6(const char *src, unsigned char *dst, size_t size) +{ + static const char xdigits_l[] = "0123456789abcdef", + xdigits_u[] = "0123456789ABCDEF"; + unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; + const char *xdigits, *curtok; + int ch, saw_xdigit; + unsigned int val; + int digits; + int bits; + size_t bytes; + int words; + int ipv4; + + memset((tp = tmp), '\0', NS_IN6ADDRSZ); + endp = tp + NS_IN6ADDRSZ; + colonp = NULL; + /* Leading :: requires some special handling. */ + if (*src == ':') + if (*++src != ':') + goto enoent; + curtok = src; + saw_xdigit = 0; + val = 0; + digits = 0; + bits = -1; + ipv4 = 0; + while ((ch = *src++) != '\0') { + const char *pch; + + if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) + pch = strchr((xdigits = xdigits_u), ch); + if (pch != NULL) { + val <<= 4; + val |= (pch - xdigits); + if (++digits > 4) + goto enoent; + saw_xdigit = 1; + continue; + } + if (ch == ':') { + curtok = src; + if (!saw_xdigit) { + if (colonp) + goto enoent; + colonp = tp; + continue; + } else if (*src == '\0') + goto enoent; + if (tp + NS_INT16SZ > endp) + return (0); + *tp++ = (unsigned char)((val >> 8) & 0xff); + *tp++ = (unsigned char)(val & 0xff); + saw_xdigit = 0; + digits = 0; + val = 0; + continue; + } + if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && + getv4(curtok, tp, &bits) > 0) { + tp += NS_INADDRSZ; + saw_xdigit = 0; + ipv4 = 1; + break; /* '\0' was seen by inet_pton4(). */ + } + if (ch == '/' && getbits(src, &bits) > 0) + break; + goto enoent; + } + if (saw_xdigit) { + if (tp + NS_INT16SZ > endp) + goto enoent; + *tp++ = (unsigned char)((val >> 8) & 0xff); + *tp++ = (unsigned char)(val & 0xff); + } + if (bits == -1) + bits = 128; + + words = (bits + 15) / 16; + if (words < 2) + words = 2; + if (ipv4) + words = 8; + endp = tmp + 2 * words; + + if (colonp != NULL) { + /* + * Since some memmove()'s erroneously fail to handle + * overlapping regions, we'll do the shift by hand. + */ + const ssize_t n = tp - colonp; + ssize_t i; + + if (tp == endp) + goto enoent; + for (i = 1; i <= n; i++) { + *(endp - i) = *(colonp + n - i); + *(colonp + n - i) = 0; + } + tp = endp; + } + if (tp != endp) + goto enoent; + + bytes = (bits + 7) / 8; + if (bytes > size) + goto emsgsize; + memcpy(dst, tmp, bytes); + return (bits); + + enoent: + SET_ERRNO(ENOENT); + return (-1); + + emsgsize: + SET_ERRNO(EMSGSIZE); + return (-1); +} + +/* + * int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * note: + * On Windows we store the error in the thread errno, not + * in the winsock error code. This is to avoid loosing the + * actual last winsock error. So use macro ERRNO to fetch the + * errno this funtion sets when returning (-1), not SOCKERRNO. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +ares_inet_net_pton(int af, const char *src, void *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + SET_ERRNO(EAFNOSUPPORT); + return (-1); + } +} + +int +evil_inet_pton(int af, const char *src, void *dst) +{ + int result; + size_t size; + + if (af == AF_INET) + size = sizeof(struct in_addr); + else if (af == AF_INET6) + size = sizeof(struct ares_in6_addr); + else + { + SET_ERRNO(EAFNOSUPPORT); + return -1; + } + result = ares_inet_net_pton(af, src, dst, size); + if ((result == -1) && (ERRNO == ENOENT)) + return 0; + return (result > -1 ? 1 : -1); +} + + +/* const char * + * inet_ntop4(src, dst, size) + * format an IPv4 address, more or less like inet_ntoa() + * return: + * `dst' (as a const) + * notes: + * (1) uses no statics + * (2) takes a unsigned char* not an in_addr as input + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop4(const unsigned char *src, char *dst, size_t size) +{ + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + + if (SPRINTF((tmp, fmt, src[0], src[1], src[2], src[3])) > size) + { + SET_ERRNO(ENOSPC); + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +/* const char * + * inet_ntop6(src, dst, size) + * convert IPv6 binary address into presentation (printable) format + * author: + * Paul Vixie, 1996. + */ +static const char * +inet_ntop6(const unsigned char *src, char *dst, size_t size) +{ + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char *tp; + struct { + long base; + long len; + } best, cur; + unsigned long words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof(words)); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + + best.base = -1; + cur.base = -1; + best.len = 0; + cur.len = 0; + + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + if (words[i] == 0) + { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } + else + { + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) + { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) + { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && + i < (best.base + best.len)) + { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) + { + if (!inet_ntop4(src+12, tp, sizeof(tmp) - (tp - tmp))) + return (NULL); + tp += strlen(tp); + break; + } + tp += SPRINTF((tp, "%lx", words[i])); + } + + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((size_t)(tp - tmp) > size) + { + SET_ERRNO(ENOSPC); + return (NULL); + } + strcpy(dst, tmp); + return (dst); +} + +/* char * + * inet_ntop(af, src, dst, size) + * convert a network format address to presentation format. + * return: + * pointer to presentation format address (`dst'), or NULL (see errno). + * note: + * On Windows we store the error in the thread errno, not + * in the winsock error code. This is to avoid loosing the + * actual last winsock error. So use macro ERRNO to fetch the + * errno this funtion sets when returning NULL, not SOCKERRNO. + * author: + * Paul Vixie, 1996. + */ +static const char * +ares_inet_ntop(int af, const void *src, char *dst, size_t size) +{ + switch (af) + { + case AF_INET: + return (inet_ntop4(src, dst, size)); + case AF_INET6: + return (inet_ntop6(src, dst, size)); + default: + SET_ERRNO(EAFNOSUPPORT); + return (NULL); + } + /* NOTREACHED */ +} + +const char *evil_inet_ntop(int af, const char *src, void *dst, size_t size) +{ + const char *result; + result = ares_inet_ntop(af, src, dst, size); + if ((result == NULL) && (ERRNO == ENOSPC)) + return NULL; + return result; +} diff --git a/src/lib/evil/evil_inet.h b/src/lib/evil/evil_inet.h new file mode 100644 index 0000000000..b81b8ff715 --- /dev/null +++ b/src/lib/evil/evil_inet.h @@ -0,0 +1,149 @@ + +/* Copyright (C) 2005 by Daniel Stenberg + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +#ifndef __EVIL_INET_H__ +#define __EVIL_INET_H__ + + +/** + * @file evil_inet.h + * @brief The file that provides functions ported from Unix in arpa/inet.h. + * @defgroup Evil_Inet_Group Inet.h functions + * + * This header provides functions ported from Unix in arpa/inet.h. + * + * @{ + */ + +/** + * @brief Convert IPv4 and IPv6 addresses from text to binary form. + * + * @param af The address family. + * @param src The address to convert. + * @param dst The converted address structure. + * @return 1 on success, 0 or -1 otherwise. + * + * This function converts IPv4 and IPv6 addresses from @p src to the + * binary form @p dst. The following address families to pass to @p af + * are currently supported: + * + * <ul> + * <li>i AF_INET: @p src points to a character string containing an IPv4 + * network address in dotted-decimal format, "ddd.ddd.ddd.ddd", where + * ddd is a decimal number of up to three digits in the range 0 to + * 255. The address is converted to a struct in_addr and copied to + * dst, which must be sizeof(struct in_addr) (4) bytes (32 bits) long. + * <li> AF_INET6: @p src points to a character string containing an + * IPv6 network address. The address is converted to a struct in6_addr + * and copied to dst, which must be sizeof(struct in6_addr) (16) bytes + * (128 bits) long. The allowed formats for IPv6 addresses follow + * these rules: + * <ol> + * <li>The preferred format is x:x:x:x:x:x:x:x. This form consists of + * eight hexadecimal numbers, each of which expresses a 16-bit value + * (i.e., each x can be up to 4 hex digits). + * <li>A series of contiguous zero values in the preferred format can + * be abbreviated to ::. Only one instance of :: can occur in an + * address. For example, the loopback address 0:0:0:0:0:0:0:1 can be + * abbreviated as ::1. The wildcard address, consisting of all zeros, + * can be written as ::. + * <li>An alternate format is useful for expressing IPv4-mapped IPv6 + * addresses. This form is written as x:x:x:x:x:x:d.d.d.d, where the + * six leading xs are hexadecimal values that define the six + * most-significant 16-bit pieces of the address (i.e., 96 bits), and + * the ds express a value in dotted-decimal notation that defines the + * least significant 32 bits of the address. An example of such an + * address is :: FFFF:204.152.189.116. + * </ul> + * </ul> + * On success this function returns 1 (network address was successfully + * converted). 0 is returned if @p src does not contain a character + * string representing a valid network address in the specified + * address family. If af does not contain a valid address family, -1 + * is returned and errno is set to EAFNOSUPPORT. + * + * @see evil_inet_ntop() + * @see inet_ntop() + * + * Conformity: POSIX.1-2001. + * + * Supported OS: Windows XP, CE. + * + */ +EAPI int evil_inet_pton(int af, const char *src, void *dst); + +/** + * @def inet_pton(x,y,z) + * + * Wrapper around evil_inet_pton(). + */ +#define inet_pton(x,y,z) evil_inet_pton(x,y,z) + +/** + * @brief Convert IPv4 and IPv6 addresses from binary to text form. + * + * @param af The address family. + * @param src The address structure to convert. + * @param dst A buffer containing the converted string. + * @param size The size of the buffer. + * @return 1 on success, 0 otherwise. + * + * This function converts the network address structure @p src in the + * @p af address family into a character string. The resulting string + * is copied to the buffer pointed to by @p dst, which must be a + * non-NULL pointer. The caller specifies the number of bytes + * available in this buffer in the argument @p size. The following + * address families to pass to @p af are currently supported: + * + * @li AF_INET: @p src points to a struct in_addr (in network byte + * order) which is converted to an IPv4 network address in the + * dotted-decimal format, "ddd.ddd.ddd.ddd". The buffer @p dst must be + * at least INET_ADDRSTRLEN bytes long. + * @li AF_INET6: @p src points to a struct in6_addr (in network byte + * order) which is converted to a representation of this address in + * the most appropriate IPv6 network address format for this + * address. The buffer @p dst must be at least INET6_ADDRSTRLEN bytes + * long. + * + * On success, this function returns a non-NULL pointer to @p dst. NULL is + * returned if there was an error, with errno set to indicate the + * error. + * + * @see evil_inet_pton() + * @see inet_pton() + * + * Conformity: POSIX.1-2001. + * + * Supported OS: Windows XP, CE. + * + */ +EAPI const char *evil_inet_ntop(int af, const char *src, void *dst, size_t size); + +/** + * @def inet_ntop(x,y,z,s) + * + * Wrapper around evil_inet_ntop(). + */ +#define inet_ntop(x,y,z,s) evil_inet_ntop(x,y,z,s) + + + +/** + * @} + */ + + +#endif /* __EVIL_INET_H__ */ diff --git a/src/lib/evil/evil_langinfo.c b/src/lib/evil/evil_langinfo.c new file mode 100644 index 0000000000..32b1f41386 --- /dev/null +++ b/src/lib/evil/evil_langinfo.c @@ -0,0 +1,53 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "Evil.h" + + +#ifndef __MINGW32CE__ + +static char * +replace(char *prev, char *value) +{ + if (!value) + return prev; + + if (prev) + free (prev); + return strdup (value); +} + +char * +nl_langinfo(nl_item index) +{ + static char *result = NULL; + static char *nothing = ""; + + switch (index) + { + case CODESET: + { + char *p; + result = replace(result, setlocale(LC_CTYPE, NULL)); + if (!(p = strrchr(result, '.'))) + return nothing; + + if ((++p - result) > 2) + strcpy(result, "cp"); + else + *result = '\0'; + strcat(result, p); + + return result; + } + case RADIXCHAR: + { + return localeconv()->decimal_point; + } + } + + return nothing; +} + +#endif /* ! __MINGW32CE__ */ diff --git a/src/lib/evil/evil_langinfo.h b/src/lib/evil/evil_langinfo.h new file mode 100644 index 0000000000..0e9485a5c1 --- /dev/null +++ b/src/lib/evil/evil_langinfo.h @@ -0,0 +1,41 @@ +#ifndef __EVIL_LANGINFO_H__ +#define __EVIL_LANGINFO_H__ + + +#ifndef __MINGW32CE__ + +#include <locale.h> + + +typedef int nl_item; + +#define __NL_ITEM( CATEGORY, INDEX ) ((CATEGORY << 16) | INDEX) +#define __NL_ITEM_CATEGORY( ITEM ) (ITEM >> 16) +#define __NL_ITEM_INDEX( ITEM ) (ITEM & 0xffff) + +enum { + /* + * LC_CTYPE category... + * Character set classification items. + */ + _NL_CTYPE_CODESET = __NL_ITEM( LC_CTYPE, 0 ), + _NL_NUMERIC_RADIXCHAR = __NL_ITEM( LC_NUMERIC, 0 ), + + /* + * Dummy entry, to terminate the list. + */ + _NL_ITEM_CLASSIFICATION_END +}; + +/* + * Define the public aliases for the enumerated classification indices... + */ +# define CODESET _NL_CTYPE_CODESET +# define RADIXCHAR _NL_NUMERIC_RADIXCHAR + +EAPI char *nl_langinfo(nl_item index); + +#endif /* __MINGW32CE__ */ + + +#endif /*__EVIL_LANGINFO_H__ */ diff --git a/src/lib/evil/evil_libgen.c b/src/lib/evil/evil_libgen.c new file mode 100644 index 0000000000..7e98f74386 --- /dev/null +++ b/src/lib/evil/evil_libgen.c @@ -0,0 +1,103 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <string.h> + +#include "Evil.h" + +char _evil_basename_buf[PATH_MAX]; +char _evil_dirname_buf[PATH_MAX]; + +char * +evil_basename(char *path) +{ + char *p1; + char *p2; + size_t length; + + /* path must begin by "?:\" or "?:/" */ + if ((!path) || !evil_path_is_absolute(path)) + { + memcpy(_evil_basename_buf, "C:\\", 4); + return _evil_basename_buf; + } + + /* '/' --> '\\' */ + length = strlen(path); + p1 = strdup(path); + if (!p1) + { + memcpy(_evil_basename_buf, "C:\\", 4); + return _evil_basename_buf; + } + p2 = p1; + while (*p2) + { + if (*p2 == '/') *p2 = '\\'; + p2++; + } + + /* remove trailing backslashes */ + p2 = p1 + (length - 1); + if (*p2 == '\\') + { + while (*p2 == '\\') + p2--; + } + *(p2 + 1) = '\0'; + + p2 = strrchr(p1, '\\'); + memcpy(_evil_basename_buf, p2 + 1, (p1 + length + 1) - p2); + + free(p1); + + return _evil_basename_buf; +} + +char * +evil_dirname(char *path) +{ + char *p1; + char *p2; + size_t length; + + /* path must begin by "?:\" or "?:/" */ + if ((!path) || !evil_path_is_absolute(path)) + { + memcpy(_evil_dirname_buf, "C:\\", 4); + return _evil_dirname_buf; + } + + /* '/' --> '\\' */ + length = strlen(path); + p1 = strdup(path); + if (!p1) + { + memcpy(_evil_dirname_buf, "C:\\", 4); + return _evil_dirname_buf; + } + p2 = p1; + while (*p2) + { + if (*p2 == '/') *p2 = '\\'; + p2++; + } + + /* remove trailing backslashes */ + p2 = p1 + (length - 1); + if (*p2 == '\\') + { + while (*p2 == '\\') + p2--; + } + *(p2 + 1) = '\0'; + + p2 = strrchr(p1, '\\'); + *p2 = '\0'; + memcpy(_evil_dirname_buf, p1, strlen(p1) + 1); + + free(p1); + + return _evil_dirname_buf; +} diff --git a/src/lib/evil/evil_libgen.h b/src/lib/evil/evil_libgen.h new file mode 100644 index 0000000000..4ca977ce86 --- /dev/null +++ b/src/lib/evil/evil_libgen.h @@ -0,0 +1,88 @@ +#ifndef __EVIL_LIBGEN_H__ +#define __EVIL_LIBGEN_H__ + + +/** + * @file evil_libgen.h + * @brief The file that provides functions ported from Unix in libgen.h. + * @defgroup Evil_Libgen_Group Libgen.h functions. + * + * This header provides functions ported from Unix in libgen.h. + * + * @{ + */ + +/** + * @brief Parse the base name component of a path. + * + * @param path The path to parse. + * @return The component following the final '/'. + * + * This function parses @p path and returns its component following + * the final '\'. Trailing '\' are not taken into account. On Windows + * XP, @p path must beginning by a drive letter followed by ':/' or + * ':\', otherwise "C:\" is returned. All characters '/' are replaced by '\'. On + * error (memory allocation failure), "C:\" is returned, otherwise the + * component following the final '\' is returned as a statically + * allocated memory. Hence the returns value must not be freed. + * + * Concatenating the string returned by dirname(), a "\", and the + * string returned by basename() yields a complete pathname. + * + * @see evil_dirname() + * @see dirname() + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP. + */ +EAPI char *evil_basename(char *path); + +/** + * @def basename(p) + * + * Wrapper around evil_basename(). + */ +#define basename(p) evil_basename(p) + +/** + * @brief Parse the dir name component of a path. + * + * @param path The path to parse. + * @return The component up to, but not including, the final '/'. + * + * This function parses @p path and returns its component up to, but + * not including, the final '/'. Trailing '\' are not taken into + * account. On Windows XP, @p path must beginning by a drive letter + * followed by ':/' or ':\', otherwise "C:\" is returned. All + * characters '/' are replaced by '\'. On error (memory allocation + * failure), "C:\" is returned, otherwise, the component up to, but + * not including, the final '/' is returned as a statically allocated + * memory. Hence the returns value must not be freed. + * + * Concatenating the string returned by dirname(), a "\", and the + * string returned by basename() yields a complete pathname. + * + * @see evil_basename() + * @see basename() + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP. + */ +EAPI char *evil_dirname(char *path); + +/** + * @def dirname(p) + * + * Wrapper around evil_dirname(). + */ +#define dirname(p) evil_dirname(p) + + +/** + * @} + */ + + +#endif /* __EVIL_LIBGEN_H__ */ diff --git a/src/lib/evil/evil_link_ce.c b/src/lib/evil/evil_link_ce.c new file mode 100644 index 0000000000..17532cc4d6 --- /dev/null +++ b/src/lib/evil/evil_link_ce.c @@ -0,0 +1,90 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include <shellapi.h> + +#include "Evil.h" +#include "evil_private.h" + + +/* + * Symbolic links and directory related functions + * + */ + + +/* REMARK: Windows has no symbolic link. */ +/* Nevertheless, it can create and read .lnk files */ + +int +symlink(const char *oldpath, const char *newpath) +{ + wchar_t *w_oldpath; + wchar_t *w_newpath; + BOOL res; + + w_oldpath = evil_char_to_wchar(oldpath); + if (!w_oldpath) + return -1; + + w_newpath = evil_char_to_wchar(newpath); + if (!w_newpath) + { + free(w_oldpath); + return -1; + } + + res = SHCreateShortcut(w_newpath, w_oldpath); + if (!res) + _evil_last_error_display(__FUNCTION__); + + free(w_oldpath); + free(w_newpath); + + return res ? 0 : -1; +} + +ssize_t +readlink(const char *path, char *buf, size_t bufsiz) +{ + wchar_t *w_path; + wchar_t w_newpath[1024]; + char *newpath; + size_t length; + BOOL res; + + w_path = evil_char_to_wchar(path); + if (!w_path) + return -1; + + res = SHGetShortcutTarget(w_path, w_newpath, 1024); + if (!res) + _evil_last_error_display(__FUNCTION__); + + free(w_path); + + if (!res) + return -1; + + newpath = evil_wchar_to_char(w_newpath); + if (!newpath) + return -1; + + /* That stupid SHGetShortcutTarget add " around the file name... */ + length = strlen(newpath) - 2; + if (length > bufsiz) + length = bufsiz; + + memcpy(buf, newpath + 1, length); + + free(newpath); + + return length; +} diff --git a/src/lib/evil/evil_link_xp.cpp b/src/lib/evil/evil_link_xp.cpp new file mode 100644 index 0000000000..675908f75b --- /dev/null +++ b/src/lib/evil/evil_link_xp.cpp @@ -0,0 +1,151 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif /* HAVE_ERRNO_H */ + +#include <shlobj.h> +#include <objidl.h> +#include <cstdio> + +#include "Evil.h" + + +/* + * Symbolic links and directory related functions + * + */ + + +/* REMARK: Windows has no symbolic link. */ +/* Nevertheless, it can create and read .lnk files */ +int +symlink(const char *oldpath, const char *newpath) +{ + char fullname[PATH_MAX]; + wchar_t *wnewpath; + IShellLink *pISL; + IPersistFile *pIPF; + HRESULT res; + size_t size; + + realpath(oldpath, fullname); + + res = CoInitialize(NULL); + if (FAILED(res)) + { + if (res == E_OUTOFMEMORY) + errno = ENOMEM; + return -1; + } + + if (FAILED(CoCreateInstance(CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + IID_IShellLink, + (void **)&pISL))) + goto no_instance; + + if (FAILED(pISL->SetPath(fullname))) + goto no_setpath; + + if (FAILED(pISL->QueryInterface(IID_IPersistFile, (void **)&pIPF))) + goto no_queryinterface; + + size = mbstowcs(NULL, newpath, 0); + wnewpath = (wchar_t *)malloc((size + 1) * sizeof(wchar_t)); + if (!wnewpath) + goto malloc_failure; + if (mbstowcs(wnewpath, newpath, size + 1) == (size_t)(-1)) + goto translation_failure; + if (FAILED(pIPF->Save(wnewpath, FALSE))) + goto no_save; + + free(wnewpath); + pIPF->Release(); + pISL->Release(); + CoUninitialize(); + + return 0; + + no_save: + translation_failure: + malloc_failure: + pIPF->Release(); + no_queryinterface: + no_setpath: + pISL->Release(); + no_instance: + CoUninitialize(); + return -1; +} + +ssize_t +readlink(const char *path, char *buf, size_t bufsiz) +{ + wchar_t *wpath; + char new_path[PATH_MAX]; + IShellLink *pISL; + IPersistFile *pIPF; + size_t length; + HRESULT res; + size_t size; + + res = CoInitialize(NULL); + if (FAILED(res)) + { + if (res == E_OUTOFMEMORY) + errno = ENOMEM; + return -1; + } + + if (FAILED(CoCreateInstance(CLSID_ShellLink, + NULL, + CLSCTX_INPROC_SERVER, + IID_IShellLink, + (void **)&pISL))) + goto couninitialize; + + if (FAILED(pISL->QueryInterface(IID_IPersistFile, (void **)&pIPF))) + goto release_shell_link; + + size = mbstowcs(NULL, path, 0); + wpath = (wchar_t *)malloc((size + 1) * sizeof(wchar_t)); + if (!wpath) + goto release_persist_file; + + mbstowcs(wpath, path, size + 1); + if (FAILED(pIPF->Load(wpath, STGM_READ))) + goto free_wpath; + + if (FAILED(pISL->Resolve(NULL, SLR_UPDATE | SLR_NO_UI))) + goto free_wpath; + + if (FAILED(pISL->GetPath(new_path, PATH_MAX, NULL, 0))) + goto free_wpath; + + length = strlen(new_path); + if (length > bufsiz) + length = bufsiz; + + memcpy(buf, new_path, length); + + free(wpath); + pISL->Release(); + pIPF->Release(); + CoUninitialize(); + + return length; + + free_wpath: + free(wpath); + release_persist_file: + pIPF->Release(); + release_shell_link: + pISL->Release(); + couninitialize: + CoUninitialize(); + return -1; +} diff --git a/src/lib/evil/evil_macro.h b/src/lib/evil/evil_macro.h new file mode 100644 index 0000000000..b2500d33c7 --- /dev/null +++ b/src/lib/evil/evil_macro.h @@ -0,0 +1,193 @@ +#ifndef __EVIL_MACRO_H__ +#define __EVIL_MACRO_H__ + + +#ifndef __cdecl +# define EVIL_CDECL_IS_DEFINED +# ifdef __GNUC__ +# define __cdecl __attribute__((__cdecl__)) +# else +# define __cdecl +# endif +#endif /* __cdecl */ + + +#ifdef EAPI +# undef EAPI +#endif /* EAPI */ + +#ifdef _WIN32 +# ifdef EFL_EVIL_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVIL_BUILD */ +#endif /* _WIN32 */ + + +#ifndef __EVIL_GNUC_PREREQ +# if defined __GNUC__ && defined __GNUC_MINOR__ +# define __EVIL_GNUC_PREREQ( major, minor )\ + (__GNUC__ > (major) || (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))) +# else +# define __EVIL_GNUC_PREREQ( major, minor ) +# endif +#endif /* __EVIL_GNUC_PREREQ */ + + +#ifndef __EVIL_NOTHROW +# if __EVIL_GNUC_PREREQ( 3, 3 ) +# define __EVIL_NOTHROW __attribute__((__nothrow__)) +# else +# define __EVIL_NOTHROW +# endif +#endif /* __EVIL_NOTHROW */ + + +#ifndef __EVIL_PRINTF +# if __EVIL_GNUC_PREREQ( 2, 4 ) +# define __EVIL_PRINTF(fmt, arg) __attribute__((__format__ (__gnu_printf__, fmt, arg))) +# else +# define __EVIL_PRINTF(fmt, arg) +# endif +#endif /* __EVIL_PRINTF */ + + +#ifndef PATH_MAX +# define PATH_MAX MAX_PATH +#endif /* PATH_MAX */ +#ifdef fprintf +# undef fprintf +#endif + +#ifdef printf +# undef printf +#endif + +#ifdef sprintf +# undef sprintf +#endif + +#ifdef snprintf +# undef snprintf +#endif + +#ifdef vfprintf +# undef vfprintf +#endif + +#ifdef vprintf +# undef vprintf +#endif + +#ifdef vsprintf +# undef vsprintf +#endif + +#ifdef vsnprintf +# undef vsnprintf +#endif + +#ifdef fscanf +# undef fscanf +#endif + +#ifdef scanf +# undef scanf +#endif + +#ifdef sscanf +# undef sscanf +#endif + +#ifdef vfscanf +# undef vfscanf +#endif + +#ifdef vscanf +# undef vscanf +#endif + +#ifdef vsscanf +# undef vsscanf +#endif + +#ifdef asprintf +# undef asprintf +#endif + +#ifdef vasprintf +# undef vasprintf +#endif + + +#if defined(_INTTYPES_H_) && defined(PRId64) + +#undef PRId64 +#undef PRIdLEAST64 +#undef PRIdFAST64 +#undef PRIdMAX +#undef PRIi64 +#undef PRIiLEAST64 +#undef PRIiFAST64 +#undef PRIiMAX +#undef PRIo64 +#undef PRIoLEAST64 +#undef PRIoFAST64 +#undef PRIoMAX +#undef PRIu64 +#undef PRIuLEAST64 +#undef PRIuFAST64 +#undef PRIuMAX +#undef PRIx64 +#undef PRIxLEAST64 +#undef PRIxFAST64 +#undef PRIxMAX +#undef PRIX64 +#undef PRIXLEAST64 +#undef PRIXFAST64 +#undef PRIXMAX + +#undef SCNd64 +#undef SCNdLEAST64 +#undef SCNdFAST64 +#undef SCNdMAX +#undef SCNi64 +#undef SCNiLEAST64 +#undef SCNiFAST64 +#undef SCNiMAX +#undef SCNo64 +#undef SCNoLEAST64 +#undef SCNoFAST64 +#undef SCNoMAX +#undef SCNx64 +#undef SCNxLEAST64 +#undef SCNxFAST64 +#undef SCNxMAX +#undef SCNu64 +#undef SCNuLEAST64 +#undef SCNuFAST64 +#undef SCNuMAX + +#ifdef _WIN64 +#undef PRIdPTR +#undef PRIiPTR +#undef PRIoPTR +#undef PRIuPTR +#undef PRIxPTR +#undef PRIXPTR + +#undef SCNdPTR +#undef SCNiPTR +#undef SCNoPTR +#undef SCNxPTR +#undef SCNuPTR +#endif /* _WIN64 */ + +#endif /* defined(_INTTYPES_H_) && defined(PRId64) */ + +#endif /* __EVIL_MACRO_H__ */ diff --git a/src/lib/evil/evil_macro_pop.h b/src/lib/evil/evil_macro_pop.h new file mode 100644 index 0000000000..a82818da9b --- /dev/null +++ b/src/lib/evil/evil_macro_pop.h @@ -0,0 +1,93 @@ +#ifndef __EVIL_MACRO_POP_H__ +#define __EVIL_MACRO_POP_H__ + + +#ifdef EVIL_CDECL_IS_DEFINED +# undef __cdecl +# undef EVIL_CDECL_IS_DEFINED +#endif + +#define fprintf _evil_fprintfa +#define printf _evil_printfa +#define snprintf _evil_snprintfa +#define sprintf _evil_sprintfa +#define vfprintf _evil_vfprintfa +#define vprintf _evil_vprintfa +#define vsnprintf _evil_vsnprintfa +#define vsprintf _evil_vsprintfa + +#define fscanf _evil_fscanf +#define scanf _evil_scanf +#define sscanf _evil_sscanf +#define vfscanf _evil_vfscanf +#define vscanf _evil_vscanf +#define vsscanf _evil_vsscanf + +#define asprintf _evil_asprintf +#define vasprintf _evil_vasprintf + +/* Redefine to GNU specific PRI... and SCN... macros. */ + +#define PRId64 "lld" +#define PRIdLEAST64 "lld" +#define PRIdFAST64 "lld" +#define PRIdMAX "lld" +#define PRIi64 "lli" +#define PRIiLEAST64 "lli" +#define PRIiFAST64 "lli" +#define PRIiMAX "lli" +#define PRIo64 "llo" +#define PRIoLEAST64 "llo" +#define PRIoFAST64 "llo" +#define PRIoMAX "llo" +#define PRIu64 "llu" +#define PRIuLEAST64 "llu" +#define PRIuFAST64 "llu" +#define PRIuMAX "llu" +#define PRIx64 "llx" +#define PRIxLEAST64 "llx" +#define PRIxFAST64 "llx" +#define PRIxMAX "llx" +#define PRIX64 "llX" +#define PRIXLEAST64 "llX" +#define PRIXFAST64 "llX" +#define PRIXMAX "llX" + +#define SCNd64 "lld" +#define SCNdLEAST64 "lld" +#define SCNdFAST64 "lld" +#define SCNdMAX "lld" +#define SCNi64 "lli" +#define SCNiLEAST64 "lli" +#define SCNiFAST64 "lli" +#define SCNiMAX "lli" +#define SCNo64 "llo" +#define SCNoLEAST64 "llo" +#define SCNoFAST64 "llo" +#define SCNoMAX "llo" +#define SCNx64 "llx" +#define SCNxLEAST64 "llx" +#define SCNxFAST64 "llx" +#define SCNxMAX "llx" +#define SCNu64 "llu" +#define SCNuLEAST64 "llu" +#define SCNuFAST64 "llu" +#define SCNuMAX "llu" + +#ifdef _WIN64 +#define PRIdPTR "lld" +#define PRIiPTR "lli" +#define PRIoPTR "llo" +#define PRIuPTR "llu" +#define PRIxPTR "llx" +#define PRIXPTR "llX" + +#define SCNdPTR "lld" +#define SCNiPTR "lli" +#define SCNoPTR "llo" +#define SCNxPTR "llx" +#define SCNuPTR "llu" +#endif /* _WIN64 */ + + +#endif /* __EVIL_MACRO_POP_H__ */ diff --git a/src/lib/evil/evil_main.c b/src/lib/evil/evil_main.c new file mode 100644 index 0000000000..ee1eb1d113 --- /dev/null +++ b/src/lib/evil/evil_main.c @@ -0,0 +1,74 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <winsock2.h> +#undef WIN32_LEAN_AND_MEAN + +#include "Evil.h" +#include "evil_private.h" + + +static int _evil_init_count = 0; + +extern LONGLONG _evil_time_freq; +extern LONGLONG _evil_time_count; +extern long _evil_time_second; + +int +evil_init(void) +{ + SYSTEMTIME st; + LARGE_INTEGER freq; + LARGE_INTEGER count; + WORD second = 59; + + if (++_evil_init_count != 1) + return _evil_init_count; + + if (!QueryPerformanceFrequency(&freq)) + return 0; + + _evil_time_freq = freq.QuadPart; + + /* be sure that second + 1 != 0 */ + while (second == 59) + { + GetSystemTime(&st); + second = st.wSecond; + } + + /* retrieve the tick corresponding to the time we retrieve above */ + while (1) + { + GetSystemTime(&st); + QueryPerformanceCounter(&count); + if (st.wSecond == second + 1) + break; + } + + _evil_time_second = _evil_systemtime_to_time(st); + if (_evil_time_second < 0) + return --_evil_init_count; + + _evil_time_count = count.QuadPart; + + if (!evil_sockets_init()) + return --_evil_init_count; + + return _evil_init_count; +} + +int +evil_shutdown(void) +{ + if (--_evil_init_count != 0) + return _evil_init_count; + + evil_sockets_shutdown(); + + return _evil_init_count; +} diff --git a/src/lib/evil/evil_main.h b/src/lib/evil/evil_main.h new file mode 100644 index 0000000000..157e6028b7 --- /dev/null +++ b/src/lib/evil/evil_main.h @@ -0,0 +1,52 @@ +#ifndef __EVIL_MAIN_H__ +#define __EVIL_MAIN_H__ + + +/** + * @file evil_main.h + * @brief The file that provides functions to initialize and shut down Evil. + * @defgroup Evil_Main_Group Main + * + * This header provides functions to initialize and shut down the Evil + * library. + * + * @{ + */ + + +/** + * @brief Initialize the Evil library. + * + * This function initializes the Evil library. It must be called before + * using evil_time_get(), gettimeofday() or pipe(). It returns 0 on + * failure, otherwise it returns the number of times it has already been + * called. + * + * When Evil is not used anymore, call evil_shutdown() to shut down + * the Evil library. + */ +EAPI int evil_init(void); + +/** + * @brief Shut down the Evil library. + * + * @return 0 when the Evil library is completely shut down, 1 or + * greater otherwise. + * + * This function shuts down the Evil library. It returns 0 when it has + * been called the same number of times than evil_init(). + * + * Once this function succeeds (that is, @c 0 is returned), you must + * not call any of the Evil function listed in evil_init() + * documentation anymore . You must call evil_init() again to use these + * functions again. + */ +EAPI int evil_shutdown(void); + + +/** + * @} + */ + + +#endif /* __EVIL_MAIN_H__ */ diff --git a/src/lib/evil/evil_mman.c b/src/lib/evil/evil_mman.c new file mode 100644 index 0000000000..8898357cfc --- /dev/null +++ b/src/lib/evil/evil_mman.c @@ -0,0 +1,234 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> +#include <sys/types.h> + +#ifndef _MSC_VER +# include <unistd.h> +#endif + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +# include <io.h> + +#include "evil_macro.h" +#include "sys/mman.h" +#include "evil_util.h" +#include "evil_private.h" +#define APICHAR char +#include "evil_print.h" + +#ifdef __MINGW32CE__ +# define _get_osfhandle(FILEDES) ((long)FILEDES) +#endif /* __MINGW32CE__ */ + + +/***** API *****/ + + +void * +mmap(void *addr __UNUSED__, + size_t len, + int prot, + int flags, + int fd, + off_t offset) +{ + OSVERSIONINFO os_version; + + os_version.dwOSVersionInfoSize = sizeof(os_version); + if (!GetVersionEx(&os_version)) + { + char *str; + + str = evil_last_error_get(); + fprintf(stderr, "[Evil] [mmap] GetVersionEx failed: %s\n", str); + free(str); + + return MAP_FAILED; + } + +#ifdef _WIN32_WCE + if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_CE) && + (os_version.dwMajorVersion < 5)) + { + void *data; + size_t size; + + data = malloc(len); + if (!data) + { + fprintf (stderr, "[Evil] [mmap] malloc failed\n"); + return MAP_FAILED; + } + + size = read(fd, data, len); + if (size != len) + { + fprintf (stderr, "[Evil] [mmap] read failed\n"); + free(data); + return MAP_FAILED; + } + + if (lseek(fd, 0, SEEK_SET) == -1) + { + fprintf (stderr, "[Evil] [mmap] lseek failed\n"); + free(data); + return MAP_FAILED; + } + + return data; + } + else +#endif /* ! _WIN32_WCE */ + { + HANDLE fm; + DWORD protect = PAGE_NOACCESS; + DWORD acs = 0; + HANDLE handle; + void *data; + + /* support only MAP_SHARED */ + if (!(flags & MAP_SHARED)) + return MAP_FAILED; + + if (prot & PROT_EXEC) + { + if (prot & PROT_READ) + { + if (prot & PROT_WRITE) + protect = PAGE_EXECUTE_READWRITE; + else + protect = PAGE_EXECUTE_READ; + } + else + { + if (prot & PROT_WRITE) + protect = PAGE_EXECUTE_WRITECOPY; + else + protect = PAGE_EXECUTE; + } + } + else + { + if (prot & PROT_READ) + { + if (prot & PROT_WRITE) + protect = PAGE_READWRITE; + else + protect = PAGE_READONLY; + } + else if (prot & PROT_WRITE) + protect = PAGE_WRITECOPY; + } + + handle = (HANDLE)_get_osfhandle(fd); + if (handle == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "[Evil] [mmap] _get_osfhandle failed\n"); + + return MAP_FAILED; + } + + fm = CreateFileMapping(handle, NULL, protect, 0, 0, NULL); + if (!fm) + { + char *str; + + str = evil_last_error_get(); + fprintf(stderr, "[Evil] [mmap] CreateFileMapping failed: %s\n", str); + free(str); + + return MAP_FAILED; + } + + if (protect & PAGE_READWRITE) + acs = FILE_MAP_ALL_ACCESS; + if (protect & PAGE_WRITECOPY) + acs = FILE_MAP_COPY; +#if 0 + if (protect & (PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_READ)) + acs = FILE_MAP_EXECUTE; +#endif + if (protect & (PAGE_READWRITE | PAGE_READONLY)) + acs = FILE_MAP_READ; + else + { + if (protect & PAGE_READWRITE) + acs = FILE_MAP_WRITE; + } + + data = MapViewOfFile(fm, + acs, + offset & 0xffff0000, + offset & 0x0000ffff, + len); + CloseHandle(fm); + + if (!data) + { + char *str; + + str = evil_last_error_get(); + fprintf(stderr, "[Evil] [mmap] MapViewOfFile failed: %s\n", str); + free(str); + + return MAP_FAILED; + } + + return data; + } +} + +int +munmap(void *addr, + size_t len __UNUSED__) +{ +#ifdef _WIN32_WCE + OSVERSIONINFO os_version; + + os_version.dwOSVersionInfoSize = sizeof(os_version); + if (!GetVersionEx(&os_version)) + { + char *str; + + str = evil_last_error_get(); + fprintf(stderr, "[Evil] [munmap] GetVersionEx failed: %s\n", str); + free(str); + + return -1; + } + + if ((os_version.dwPlatformId == VER_PLATFORM_WIN32_CE) && + (os_version.dwMajorVersion < 5)) + { + if (addr && (addr != MAP_FAILED)) + free(addr); + + return 0; + } + else +#endif /* ! _WIN32_WCE */ + { + BOOL res; + + res = UnmapViewOfFile(addr); + if (!res) + { + char *str; + + str = evil_last_error_get(); + fprintf(stderr, "[Evil] [munmap] UnmapViewOfFile failed: %s\n", str); + free(str); + } + + return (res == 0) ? -1 : 0; + } +} diff --git a/src/lib/evil/evil_pformat.h b/src/lib/evil/evil_pformat.h new file mode 100644 index 0000000000..ad9b61e024 --- /dev/null +++ b/src/lib/evil/evil_pformat.h @@ -0,0 +1,61 @@ +#ifndef __EVIL_PRIVATE_H__ +#define __EVIL_PRIVATE_H__ + + +#include "evil_macro.h" + + +#ifdef __BUILD_WIDEAPI +#define APICHAR wchar_t +#else +#define APICHAR char +#endif + +/* The following are the declarations specific to the `pformat' API... + */ +#define PFORMAT_TO_FILE 0x1000 +#define PFORMAT_NOLIMIT 0x2000 + + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __BUILD_WIDEAPI +# define __fputc(X,STR) fputwc((wchar_t) (X), (STR)) +# define _evil_pformat _evil_pformatw + +# define _evil_fprintf _evil_fprintfw +# define _evil_printf _evil_printfw +# define _evil_snprintf _evil_snprintfw +# define _evil_sprintf _evil_sprintfw + +# define _evil_vfprintf _evil_vfprintfw +# define _evil_vprintf _evil_vprintfw +# define _evil_vsnprintf _evil_vsnprintfw +# define _evil_vsprintf _evil_vsprintfw +#else +# define __fputc(X,STR) fputc((X), (STR)) +# define _evil_pformat _evil_pformata + +# define _evil_fprintf _evil_fprintfa +# define _evil_printf _evil_printfa +# define _evil_snprintf _evil_snprintfa +# define _evil_sprintf _evil_sprintfa + +# define _evil_vfprintf _evil_vfprintfa +# define _evil_vprintf _evil_vprintfa +# define _evil_vsnprintf _evil_vsnprintfa +# define _evil_vsprintf _evil_vsprintfa +#endif + +int __cdecl _evil_pformat(int, void *, int, const APICHAR *, va_list) __EVIL_NOTHROW; + + +#ifdef __cplusplus +} +#endif + + +#endif /* __EVIL_PRIVATE_H__ */ diff --git a/src/lib/evil/evil_pformata.c b/src/lib/evil/evil_pformata.c new file mode 100644 index 0000000000..ee1d68e772 --- /dev/null +++ b/src/lib/evil/evil_pformata.c @@ -0,0 +1,2493 @@ +/* pformat.c + * + * $Id: pformat.c,v 1.9 2011/01/07 22:57:00 keithmarshall Exp $ + * + * Provides a core implementation of the formatting capabilities + * common to the entire `printf()' family of functions; it conforms + * generally to C99 and SUSv3/POSIX specifications, with extensions + * to support Microsoft's non-standard format specifications. + * + * Written by Keith Marshall <keithmarshall@users.sourceforge.net> + * + * This is free software. You may redistribute and/or modify it as you + * see fit, without restriction of copyright. + * + * This software is provided "as is", in the hope that it may be useful, + * but WITHOUT WARRANTY OF ANY KIND, not even any implied warranty of + * MERCHANTABILITY, nor of FITNESS FOR ANY PARTICULAR PURPOSE. At no + * time will the author accept any form of liability for any damages, + * however caused, resulting from the use of this software. + * + * The elements of this implementation which deal with the formatting + * of floating point numbers, (i.e. the `%e', `%E', `%f', `%F', `%g' + * and `%G' format specifiers, but excluding the hexadecimal floating + * point `%a' and `%A' specifiers), make use of the `__gdtoa' function + * written by David M. Gay, and are modelled on his sample code, which + * has been deployed under its accompanying terms of use:-- + * + ****************************************************************** + * Copyright (C) 1997, 1999, 2001 Lucent Technologies + * All Rights Reserved + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of Lucent or any of its entities + * not be used in advertising or publicity pertaining to + * distribution of the software without specific, written prior + * permission. + * + * LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. + * IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + ****************************************************************** + * + */ +#include <stdio.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <locale.h> +#include <wchar.h> +#include <math.h> + +/* FIXME: The following belongs in values.h, but current MinGW + * has nothing useful there! OTOH, values.h is not a standard + * header, and it's use may be considered obsolete; perhaps it + * is better to just keep these definitions here. + */ +#ifndef _VALUES_H +/* + * values.h + * + */ +#define _VALUES_H + +#include <limits.h> + +#define _TYPEBITS(type) (sizeof(type) * CHAR_BIT) + +#define LLONGBITS _TYPEBITS(long long) + +#endif /* !defined _VALUES_H -- end of file */ + +#include "evil_pformat.h" +#include "gdtoa/gdtoa.h" + +/* Bit-map constants, defining the internal format control + * states, which propagate through the flags. + */ +#define PFORMAT_GROUPED 0x1000 +#define PFORMAT_HASHED 0x0800 +#define PFORMAT_LJUSTIFY 0x0400 +#define PFORMAT_ZEROFILL 0x0200 + +#define PFORMAT_JUSTIFY (PFORMAT_LJUSTIFY | PFORMAT_ZEROFILL) +#define PFORMAT_IGNORE -1 + +#define PFORMAT_SIGNED 0x01C0 +#define PFORMAT_POSITIVE 0x0100 +#define PFORMAT_NEGATIVE 0x0080 +#define PFORMAT_ADDSPACE 0x0040 + +#define PFORMAT_XCASE 0x0020 + +#define PFORMAT_LDOUBLE 0x0004 + +/* `%o' format digit extraction mask, and shift count... + * (These are constant, and do not propagate through the flags). + */ +#define PFORMAT_OMASK 0x0007 +#define PFORMAT_OSHIFT 0x0003 + +/* `%x' and `%X' format digit extraction mask, and shift count... + * (These are constant, and do not propagate through the flags). + */ +#define PFORMAT_XMASK 0x000F +#define PFORMAT_XSHIFT 0x0004 + +/* The radix point character, used in floating point formats, is + * localised on the basis of the active LC_NUMERIC locale category. + * It is stored locally, as a `wchar_t' entity, which is converted + * to a (possibly multibyte) character on output. Initialisation + * of the stored `wchar_t' entity, together with a record of its + * effective multibyte character length, is required each time + * `__pformat()' is entered, (static storage would not be thread + * safe), but this initialisation is deferred until it is actually + * needed; on entry, the effective character length is first set to + * the following value, (and the `wchar_t' entity is zeroed), to + * indicate that a call of `localeconv()' is needed, to complete + * the initialisation. + */ +#define PFORMAT_RPINIT -3 + +/* The floating point format handlers return the following value + * for the radix point position index, when the argument value is + * infinite, or not a number. + */ +#define PFORMAT_INFNAN -32768 + +#ifdef _WIN32 +/* + * The Microsoft standard for printing `%e' format exponents is + * with a minimum of three digits, unless explicitly set otherwise, + * by a prior invocation of the `_set_output_format()' function. + * + * The following macro allows us to replicate this behaviour. + */ +# define PFORMAT_MINEXP __pformat_exponent_digits() + /* + * However, this feature is unsupported for versions of the + * MSVC runtime library prior to msvcr80.dll, and by default, + * MinGW uses an earlier version, (equivalent to msvcr60.dll), + * for which `_TWO_DIGIT_EXPONENT' will be undefined. + */ +# ifndef _TWO_DIGIT_EXPONENT + /* + * This hack works around the lack of the `_set_output_format()' + * feature, when supporting versions of the MSVC runtime library + * prior to msvcr80.dll; it simply enforces Microsoft's original + * convention, for all cases where the feature is unsupported. + */ +# define _get_output_format() 0 +# define _TWO_DIGIT_EXPONENT 1 +# endif +/* + * Irrespective of the MSVCRT version supported, *we* will add + * an additional capability, through the following inline function, + * which will allow the user to choose his own preferred default + * for `PRINTF_EXPONENT_DIGITS', through the simple expedient + * of defining it as an environment variable. + */ +static +int __pformat_exponent_digits( void ) +{ + char *exponent_digits = getenv( "PRINTF_EXPONENT_DIGITS" ); + return ((exponent_digits != NULL) && ((unsigned)(*exponent_digits - '0') < 3)) + || (_get_output_format() & _TWO_DIGIT_EXPONENT) + ? 2 + : 3 + ; +} +#else +/* + * When we don't care to mimic Microsoft's standard behaviour, + * we adopt the C99/POSIX standard of two digit exponents. + */ +# define PFORMAT_MINEXP 2 +#endif + +typedef union +{ + /* A data type agnostic representation, + * for printf arguments of any integral data type... + */ + signed long __pformat_long_t; + signed long long __pformat_llong_t; + unsigned long __pformat_ulong_t; + unsigned long long __pformat_ullong_t; + unsigned short __pformat_ushort_t; + unsigned char __pformat_uchar_t; + signed short __pformat_short_t; + signed char __pformat_char_t; + void * __pformat_ptr_t; +} __pformat_intarg_t; + +typedef enum +{ + /* Format interpreter state indices... + * (used to identify the active phase of format string parsing). + */ + PFORMAT_INIT = 0, + PFORMAT_SET_WIDTH, + PFORMAT_GET_PRECISION, + PFORMAT_SET_PRECISION, + PFORMAT_END +} __pformat_state_t; + +typedef enum +{ + /* Argument length classification indices... + * (used for arguments representing integer data types). + */ + PFORMAT_LENGTH_INT = 0, + PFORMAT_LENGTH_SHORT, + PFORMAT_LENGTH_LONG, + PFORMAT_LENGTH_LLONG, + PFORMAT_LENGTH_CHAR +} __pformat_length_t; +/* + * And a macro to map any arbitrary data type to an appropriate + * matching index, selected from those above; the compiler should + * collapse this to a simple assignment. + */ +#define __pformat_arg_length( type ) \ + sizeof( type ) == sizeof( long long ) ? PFORMAT_LENGTH_LLONG : \ + sizeof( type ) == sizeof( long ) ? PFORMAT_LENGTH_LONG : \ + sizeof( type ) == sizeof( short ) ? PFORMAT_LENGTH_SHORT : \ + sizeof( type ) == sizeof( char ) ? PFORMAT_LENGTH_CHAR : \ + /* should never need this default */ PFORMAT_LENGTH_INT + +typedef struct +{ + /* Formatting and output control data... + * An instance of this control block is created, (on the stack), + * for each call to `__pformat()', and is passed by reference to + * each of the output handlers, as required. + */ + void * dest; + int flags; + int width; + int precision; + int rplen; + wchar_t rpchr; + int thousands_chr_len; + wchar_t thousands_chr; + int count; + int quota; + int expmin; +} __pformat_t; + +static +void __pformat_putc( int c, __pformat_t *stream ) +{ + /* Place a single character into the `__pformat()' output queue, + * provided any specified output quota has not been exceeded. + */ + if( (stream->flags & PFORMAT_NOLIMIT) || (stream->quota > stream->count) ) + { + /* Either there was no quota specified, + * or the active quota has not yet been reached. + */ + if( stream->flags & PFORMAT_TO_FILE ) + /* + * This is single character output to a FILE stream... + */ + __fputc(c, (FILE *)(stream->dest)); + + else + /* Whereas, this is to an internal memory buffer... + */ + ((APICHAR *)(stream->dest))[stream->count] = c; + } + ++stream->count; +} + +static +void __pformat_putchars( const char *s, int count, __pformat_t *stream ) +{ + /* Handler for `%c' and (indirectly) `%s' conversion specifications. + * + * Transfer characters from the string buffer at `s', character by + * character, up to the number of characters specified by `count', or + * if `precision' has been explicitly set to a value less than `count', + * stopping after the number of characters specified for `precision', + * to the `__pformat()' output stream. + * + * Characters to be emitted are passed through `__pformat_putc()', to + * ensure that any specified output quota is honoured. + */ + if( (stream->precision >= 0) && (count > stream->precision) ) + /* + * Ensure that the maximum number of characters transferred doesn't + * exceed any explicitly set `precision' specification. + */ + count = stream->precision; + + /* Establish the width of any field padding required... + */ + if( stream->width > count ) + /* + * as the number of spaces equivalent to the number of characters + * by which those to be emitted is fewer than the field width... + */ + stream->width -= count; + + else + /* ignoring any width specification which is insufficient. + */ + stream->width = PFORMAT_IGNORE; + + if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) + /* + * When not doing flush left justification, (i.e. the `-' flag + * is not set), any residual unreserved field width must appear + * as blank padding, to the left of the output string. + */ + while( stream->width-- ) + __pformat_putc( '\x20', stream ); + + /* Emit the data... + */ +#ifdef __BUILD_WIDEAPI + { + /* mbrtowc */ + size_t l; + wchar_t w[12], *p; + while( count > 0 ) + { + mbstate_t ps; + memset(&ps, 0, sizeof(ps) ); + --count; + p = &w[0]; + l = mbrtowc (p, s, strlen (s), &ps); + if (!l) + break; + if ((ssize_t)l < 0) + { + l = 1; + w[0] = (wchar_t) *s; + } + s += l; + __pformat_putc((int)w[0], stream); + } + } +#else + while( count-- ) + /* + * copying the requisite number of characters from the input. + */ + __pformat_putc( *s++, stream ); +#endif + + /* If we still haven't consumed the entire specified field width, + * we must be doing flush left justification; any residual width + * must be filled with blanks, to the right of the output value. + */ + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); +} + +static +void __pformat_puts( const char *s, __pformat_t *stream ) +{ + /* Handler for `%s' conversion specifications. + * + * Transfer a NUL terminated character string, character by character, + * stopping when the end of the string is encountered, or if `precision' + * has been explicitly set, when the specified number of characters has + * been emitted, if that is less than the length of the input string, + * to the `__pformat()' output stream. + * + * This is implemented as a trivial call to `__pformat_putchars()', + * passing the length of the input string as the character count, + * (after first verifying that the input pointer is not NULL). + */ + if( s == NULL ) s = "(null)"; + __pformat_putchars( s, strlen( s ), stream ); +} + +static +void __pformat_wputchars( const wchar_t *s, int count, __pformat_t *stream ) +{ + /* Handler for `%C'(`%lc') and `%S'(`%ls') conversion specifications; + * (this is a wide character variant of `__pformat_putchars()'). + * + * Each multibyte character sequence to be emitted is passed, byte + * by byte, through `__pformat_putc()', to ensure that any specified + * output quota is honoured. + */ + char buf[16]; + mbstate_t state; + int len = wcrtomb(buf, L'\0', &state); + + if( (stream->precision >= 0) && (count > stream->precision) ) + /* + * Ensure that the maximum number of characters transferred doesn't + * exceed any explicitly set `precision' specification. + */ + count = stream->precision; + + /* Establish the width of any field padding required... + */ + if( stream->width > count ) + /* + * as the number of spaces equivalent to the number of characters + * by which those to be emitted is fewer than the field width... + */ + stream->width -= count; + + else + /* ignoring any width specification which is insufficient. + */ + stream->width = PFORMAT_IGNORE; + + if( (stream->width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) + /* + * When not doing flush left justification, (i.e. the `-' flag + * is not set), any residual unreserved field width must appear + * as blank padding, to the left of the output string. + */ + while( stream->width-- ) + __pformat_putc( '\x20', stream ); + + /* Emit the data, converting each character from the wide + * to the multibyte domain as we go... + */ +#ifdef __BUILD_WIDEAPI + len = count; + while(len-- > 0 && *s != 0) + { + __pformat_putc(*s++, stream); + } + count = len; +#else + while( (count-- > 0) && ((len = wcrtomb( buf, *s++, &state )) > 0) ) + { + char *p = buf; + while( len-- > 0 ) + __pformat_putc( *p++, stream ); + } +#endif + /* If we still haven't consumed the entire specified field width, + * we must be doing flush left justification; any residual width + * must be filled with blanks, to the right of the output value. + */ + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); +} + +static +void __pformat_wcputs( const wchar_t *s, __pformat_t *stream ) +{ + /* Handler for `%S' (`%ls') conversion specifications. + * + * Transfer a NUL terminated wide character string, character by + * character, converting to its equivalent multibyte representation + * on output, and stopping when the end of the string is encountered, + * or if `precision' has been explicitly set, when the specified number + * of characters has been emitted, if that is less than the length of + * the input string, to the `__pformat()' output stream. + * + * This is implemented as a trivial call to `__pformat_wputchars()', + * passing the length of the input string as the character count, + * (after first verifying that the input pointer is not NULL). + */ + if( s == NULL ) s = L"(null)"; + __pformat_wputchars( s, wcslen( s ), stream ); +} + +static +int __pformat_int_bufsiz( int bias, int size, __pformat_t *stream ) +{ + /* Helper to establish the size of the internal buffer, which + * is required to queue the ASCII decomposition of an integral + * data value, prior to transfer to the output stream. + */ + size = ((size - 1 + LLONGBITS) / size) + bias; + size += (stream->precision > 0) ? stream->precision : 0; + if ((stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0) + size += (size / 3); + return (size > stream->width) ? size : stream->width; +} + +static +void __pformat_int( __pformat_intarg_t value, __pformat_t *stream ) +{ + /* Handler for `%d', `%i' and `%u' conversion specifications. + * + * Transfer the ASCII representation of an integer value parameter, + * formatted as a decimal number, to the `__pformat()' output queue; + * output will be truncated, if any specified quota is exceeded. + */ + char buf[__pformat_int_bufsiz(1, PFORMAT_OSHIFT, stream)]; + char *p = buf; int precision; + + if( stream->flags & PFORMAT_NEGATIVE ) + { + /* The input value might be negative, (i.e. it is a signed value)... + */ + if( value.__pformat_llong_t < 0LL ) + /* + * It IS negative, but we want to encode it as unsigned, + * displayed with a leading minus sign, so convert it... + */ + value.__pformat_llong_t = -value.__pformat_llong_t; + + else + /* It is unequivocally a POSITIVE value, so turn off the + * request to prefix it with a minus sign... + */ + stream->flags &= ~PFORMAT_NEGATIVE; + } + + /* Encode the input value for display... + */ + while( value.__pformat_ullong_t ) + { + /* decomposing it into its constituent decimal digits, + * in order from least significant to most significant, using + * the local buffer as a LIFO queue in which to store them. + */ + if (p != buf && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0 + && ((p - buf) % 4) == 3) + { + *p++ = ','; + } + *p++ = '0' + (unsigned char)(value.__pformat_ullong_t % 10LL); + value.__pformat_ullong_t /= 10LL; + } + + if( (stream->precision > 0) + && ((precision = stream->precision - (p - buf)) > 0) ) + /* + * We have not yet queued sufficient digits to fill the field width + * specified for minimum `precision'; pad with zeros to achieve this. + */ + while( precision-- > 0 ) + *p++ = '0'; + + if( (p == buf) && (stream->precision != 0) ) + /* + * Input value was zero; make sure we print at least one digit, + * unless the precision is also explicitly zero. + */ + *p++ = '0'; + + if( (stream->width > 0) && ((stream->width -= p - buf) > 0) ) + { + /* We have now queued sufficient characters to display the input value, + * at the desired precision, but this will not fill the output field... + */ + if( stream->flags & PFORMAT_SIGNED ) + /* + * We will fill one additional space with a sign... + */ + stream->width--; + + if( (stream->precision < 0) + && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) + /* + * and the `0' flag is in effect, so we pad the remaining spaces, + * to the left of the displayed value, with zeros. + */ + while( stream->width-- > 0 ) + *p++ = '0'; + + else if( (stream->flags & PFORMAT_LJUSTIFY) == 0 ) + /* + * the `0' flag is not in effect, and neither is the `-' flag, + * so we pad to the left of the displayed value with spaces, so that + * the value appears right justified within the output field. + */ + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); + } + + if( stream->flags & PFORMAT_NEGATIVE ) + /* + * A negative value needs a sign... + */ + *p++ = '-'; + + else if( stream->flags & PFORMAT_POSITIVE ) + /* + * A positive value may have an optionally displayed sign... + */ + *p++ = '+'; + + else if( stream->flags & PFORMAT_ADDSPACE ) + /* + * Space was reserved for displaying a sign, but none was emitted... + */ + *p++ = '\x20'; + + while( p > buf ) + /* + * Emit the accumulated constituent digits, + * in order from most significant to least significant... + */ + __pformat_putc( *--p, stream ); + + while( stream->width-- > 0 ) + /* + * The specified output field has not yet been completely filled; + * the `-' flag must be in effect, resulting in a displayed value which + * appears left justified within the output field; we must pad the field + * to the right of the displayed value, by emitting additional spaces, + * until we reach the rightmost field boundary. + */ + __pformat_putc( '\x20', stream ); +} + +static +void __pformat_xint( int fmt, __pformat_intarg_t value, __pformat_t *stream ) +{ + /* Handler for `%o', `%p', `%x' and `%X' conversions. + * + * These can be implemented using a simple `mask and shift' strategy; + * set up the mask and shift values appropriate to the conversion format, + * and allocate a suitably sized local buffer, in which to queue encoded + * digits of the formatted value, in preparation for output. + */ + int width; + int mask = (fmt == 'o') ? PFORMAT_OMASK : PFORMAT_XMASK; + int shift = (fmt == 'o') ? PFORMAT_OSHIFT : PFORMAT_XSHIFT; + char buf[__pformat_int_bufsiz(2, shift, stream)]; + char *p = buf; + + while( value.__pformat_ullong_t ) + { + /* Encode the specified non-zero input value as a sequence of digits, + * in the appropriate `base' encoding and in reverse digit order, each + * encoded in its printable ASCII form, with no leading zeros, using + * the local buffer as a LIFO queue in which to store them. + */ + char *q; + if( (*(q = p++) = '0' + (value.__pformat_ullong_t & mask)) > '9' ) + *q = (*q + 'A' - '9' - 1) | (fmt & PFORMAT_XCASE); + value.__pformat_ullong_t >>= shift; + } + + if( p == buf ) + /* + * Nothing was queued; input value must be zero, which should never be + * emitted in the `alternative' PFORMAT_HASHED style. + */ + stream->flags &= ~PFORMAT_HASHED; + + if( ((width = stream->precision) > 0) && ((width -= p - buf) > 0) ) + /* + * We have not yet queued sufficient digits to fill the field width + * specified for minimum `precision'; pad with zeros to achieve this. + */ + while( width-- > 0 ) + *p++ = '0'; + + else if( (fmt == 'o') && (stream->flags & PFORMAT_HASHED) ) + /* + * The field width specified for minimum `precision' has already + * been filled, but the `alternative' PFORMAT_HASHED style for octal + * output requires at least one initial zero; that will not have + * been queued, so add it now. + */ + *p++ = '0'; + + if( (p == buf) && (stream->precision != 0) ) + /* + * Still nothing queued for output, but the `precision' has not been + * explicitly specified as zero, (which is necessary if no output for + * an input value of zero is desired); queue exactly one zero digit. + */ + *p++ = '0'; + + if( stream->width > (width = p - buf) ) + /* + * Specified field width exceeds the minimum required... + * Adjust so that we retain only the additional padding width. + */ + stream->width -= width; + + else + /* Ignore any width specification which is insufficient. + */ + stream->width = PFORMAT_IGNORE; + + if( ((width = stream->width) > 0) + && (fmt != 'o') && (stream->flags & PFORMAT_HASHED) ) + /* + * For `%#x' or `%#X' formats, (which have the `#' flag set), + * further reduce the padding width to accommodate the radix + * indicating prefix. + */ + width -= 2; + + if( (width > 0) && (stream->precision < 0) + && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) + /* + * When the `0' flag is set, and not overridden by the `-' flag, + * or by a specified precision, add sufficient leading zeros to + * consume the remaining field width. + */ + while( width-- > 0 ) + *p++ = '0'; + + if( (fmt != 'o') && (stream->flags & PFORMAT_HASHED) ) + { + /* For formats other than octal, the PFORMAT_HASHED output style + * requires the addition of a two character radix indicator, as a + * prefix to the actual encoded numeric value. + */ + *p++ = fmt; + *p++ = '0'; + } + + if( (width > 0) && ((stream->flags & PFORMAT_LJUSTIFY) == 0) ) + /* + * When not doing flush left justification, (i.e. the `-' flag + * is not set), any residual unreserved field width must appear + * as blank padding, to the left of the output value. + */ + while( width-- > 0 ) + __pformat_putc( '\x20', stream ); + + while( p > buf ) + /* + * Move the queued output from the local buffer to the ultimate + * destination, in LIFO order. + */ + __pformat_putc( *--p, stream ); + + /* If we still haven't consumed the entire specified field width, + * we must be doing flush left justification; any residual width + * must be filled with blanks, to the right of the output value. + */ + while( width-- > 0 ) + __pformat_putc( '\x20', stream ); +} + +typedef union +{ + /* A multifaceted representation of an IEEE extended precision, + * (80-bit), floating point number, facilitating access to its + * component parts. + */ + double __pformat_fpreg_double_t; + long double __pformat_fpreg_ldouble_t; + struct + { unsigned long long __pformat_fpreg_mantissa; + signed short __pformat_fpreg_exponent; + }; + unsigned short __pformat_fpreg_bitmap[5]; + unsigned long __pformat_fpreg_bits; +} __pformat_fpreg_t; + + +static +char *__pformat_cvt( int mode, __pformat_fpreg_t x, int nd, int *dp, int *sign ) +{ + /* Helper function, derived from David M. Gay's `g_xfmt()', calling + * his `__gdtoa()' function in a manner to provide extended precision + * replacements for `ecvt()' and `fcvt()'. + */ + int k; unsigned int e = 0; char *ep; + static FPI fpi = { 64, 1-16383-64+1, 32766-16383-64+1, FPI_Round_near, 0, 14 /* Int_max */ }; + + /* Classify the argument into an appropriate `__gdtoa()' category... + */ + if( (k = __fpclassifyl( x.__pformat_fpreg_ldouble_t )) & FP_NAN ) + /* + * identifying infinities or not-a-number... + */ + k = (k & FP_NORMAL) ? STRTOG_Infinite : STRTOG_NaN; + + else if( k & FP_NORMAL ) + { + /* normal and near-zero `denormals'... + */ + if( k & FP_ZERO ) + { + /* with appropriate exponent adjustment for a `denormal'... + */ + k = STRTOG_Denormal; + e = 1 - 0x3FFF - 63; + } + else + { + /* or with `normal' exponent adjustment... + */ + k = STRTOG_Normal; + e = (x.__pformat_fpreg_exponent & 0x7FFF) - 0x3FFF - 63; + } + } + + else + /* or, if none of the above, it's a zero, (positive or negative). + */ + k = STRTOG_Zero; + + /* Check for negative values, always treating NaN as unsigned... + * (return value is zero for positive/unsigned; non-zero for negative). + */ + *sign = (k == STRTOG_NaN) ? 0 : x.__pformat_fpreg_exponent & 0x8000; + + /* Finally, get the raw digit string, and radix point position index. + */ + return __gdtoa( &fpi, e, &x.__pformat_fpreg_bits, &k, mode, nd, dp, &ep ); +} + +static +char *__pformat_ecvt( long double x, int precision, int *dp, int *sign ) +{ + /* A convenience wrapper for the above... + * it emulates `ecvt()', but takes a `long double' argument. + */ + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + return __pformat_cvt( 2, z, precision, dp, sign ); +} + +static +char *__pformat_fcvt( long double x, int precision, int *dp, int *sign ) +{ + /* A convenience wrapper for the above... + * it emulates `fcvt()', but takes a `long double' argument. + */ + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + return __pformat_cvt( 3, z, precision, dp, sign ); +} + +/* The following are required, to clean up the `__gdtoa()' memory pool, + * after processing the data returned by the above. + */ +#define __pformat_ecvt_release( value ) __freedtoa( value ) +#define __pformat_fcvt_release( value ) __freedtoa( value ) + +static +void __pformat_emit_radix_point( __pformat_t *stream ) +{ + /* Helper to place a localised representation of the radix point + * character at the ultimate destination, when formatting fixed or + * floating point numbers. + */ + if( stream->rplen == PFORMAT_RPINIT ) + { + /* Radix point initialisation not yet completed; + * establish a multibyte to `wchar_t' converter... + */ + int len; wchar_t rpchr; mbstate_t state; + + /* Initialise the conversion state... + */ + memset( &state, 0, sizeof( state ) ); + + /* Fetch and convert the localised radix point representation... + */ + if( (len = mbrtowc( &rpchr, localeconv()->decimal_point, 16, &state )) > 0 ) + /* + * and store it, if valid. + */ + stream->rpchr = rpchr; + + /* In any case, store the reported effective multibyte length, + * (or the error flag), marking initialisation as `done'. + */ + stream->rplen = len; + } + + if( stream->rpchr != (wchar_t)(0) ) + { + /* We have a localised radix point mark; + * establish a converter to make it a multibyte character... + */ +#ifdef __BUILD_WIDEAPI + __pformat_putc (stream->rpchr, stream); +#else + int len; char buf[len = stream->rplen]; mbstate_t state; + + /* Initialise the conversion state... + */ + memset( &state, 0, sizeof( state ) ); + + /* Convert the `wchar_t' representation to multibyte... + */ + if( (len = wcrtomb( buf, stream->rpchr, &state )) > 0 ) + { + /* and copy to the output destination, when valid... + */ + char *p = buf; + while( len-- > 0 ) + __pformat_putc( *p++, stream ); + } + + else + /* otherwise fall back to plain ASCII '.'... + */ + __pformat_putc( '.', stream ); +#endif + } + else + /* No localisation: just use ASCII '.'... + */ + __pformat_putc( '.', stream ); +} + +static +void __pformat_emit_numeric_value( int c, __pformat_t *stream ) +{ + /* Convenience helper to transfer numeric data from an internal + * formatting buffer to the ultimate destination... + */ + if( c == '.' ) + /* + * converting this internal representation of the the radix + * point to the appropriately localised representation... + */ + __pformat_emit_radix_point( stream ); + else if (c == ',') + { + wchar_t wcs; + if ((wcs = stream->thousands_chr) != 0) + __pformat_wputchars (&wcs, 1, stream); + } + else + /* and passing all other characters through, unmodified. + */ + __pformat_putc( c, stream ); +} + +static +void __pformat_emit_inf_or_nan( int sign, char *value, __pformat_t *stream ) +{ + /* Helper to emit INF or NAN where a floating point value + * resolves to one of these special states. + */ + int i; + char buf[4]; + char *p = buf; + + /* We use the string formatting helper to display INF/NAN, + * but we don't want truncation if the precision set for the + * original floating point output request was insufficient; + * ignore it! + */ + stream->precision = PFORMAT_IGNORE; + + if( sign ) + /* + * Negative infinity: emit the sign... + */ + *p++ = '-'; + + else if( stream->flags & PFORMAT_POSITIVE ) + /* + * Not negative infinity, but '+' flag is in effect; + * thus, we emit a positive sign... + */ + *p++ = '+'; + + else if( stream->flags & PFORMAT_ADDSPACE ) + /* + * No sign required, but space was reserved for it... + */ + *p++ = '\x20'; + + /* Copy the appropriate status indicator, up to a maximum of + * three characters, transforming to the case corresponding to + * the format specification... + */ + for( i = 3; i > 0; --i ) + *p++ = (*value++ & ~PFORMAT_XCASE) | (stream->flags & PFORMAT_XCASE); + + /* and emit the result. + */ + __pformat_putchars( buf, p - buf, stream ); +} + +static +void __pformat_emit_float( int sign, char *value, int len, __pformat_t *stream ) +{ + /* Helper to emit a fixed point representation of numeric data, + * as encoded by a prior call to `ecvt()' or `fcvt()'; (this does + * NOT include the exponent, for floating point format). + */ + if( len > 0 ) + { + /* The magnitude of `x' is greater than or equal to 1.0... + * reserve space in the output field, for the required number of + * decimal digits to be placed before the decimal point... + */ + if( stream->width > len ) + /* + * adjusting as appropriate, when width is sufficient... + */ + stream->width -= len; + + else + /* or simply ignoring the width specification, if not. + */ + stream->width = PFORMAT_IGNORE; + } + + else if( stream->width > 0 ) + /* + * The magnitude of `x' is less than 1.0... + * reserve space for exactly one zero before the decimal point. + */ + stream->width--; + + /* Reserve additional space for the digits which will follow the + * decimal point... + */ + if( (stream->width >= 0) && (stream->width > stream->precision) ) + /* + * adjusting appropriately, when sufficient width remains... + * (note that we must check both of these conditions, because + * precision may be more negative than width, as a result of + * adjustment to provide extra padding when trailing zeros + * are to be discarded from "%g" format conversion with a + * specified field width, but if width itself is negative, + * then there is explicitly to be no padding anyway). + */ + stream->width -= stream->precision; + + else + /* or again, ignoring the width specification, if not. + */ + stream->width = PFORMAT_IGNORE; + + /* Reserve space in the output field, for display of the decimal point, + * unless the precision is explicity zero, with the `#' flag not set. + */ + if ((stream->width > 0) + && ((stream->precision > 0) || (stream->flags & PFORMAT_HASHED))) + stream->width--; + + if (len > 0 && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0) + { + int cths = ((len + 2) / 3) - 1; + while (cths > 0 && stream->width > 0) + { + --cths; stream->width--; + } + } + + /* Reserve space in the output field, for display of the sign of the + * formatted value, if required; (i.e. if the value is negative, or if + * either the `space' or `+' formatting flags are set). + */ + if( (stream->width > 0) && (sign || (stream->flags & PFORMAT_SIGNED)) ) + stream->width--; + + /* Emit any padding space, as required to correctly right justify + * the output within the alloted field width. + */ + if( (stream->width > 0) && ((stream->flags & PFORMAT_JUSTIFY) == 0) ) + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); + + /* Emit the sign indicator, as appropriate... + */ + if( sign ) + /* + * mandatory, for negative values... + */ + __pformat_putc( '-', stream ); + + else if( stream->flags & PFORMAT_POSITIVE ) + /* + * optional, for positive values... + */ + __pformat_putc( '+', stream ); + + else if( stream->flags & PFORMAT_ADDSPACE ) + /* + * or just fill reserved space, when the space flag is in effect. + */ + __pformat_putc( '\x20', stream ); + + /* If the `0' flag is in effect, and not overridden by the `-' flag, + * then zero padding, to fill out the field, goes here... + */ + if( (stream->width > 0) + && ((stream->flags & PFORMAT_JUSTIFY) == PFORMAT_ZEROFILL) ) + while( stream->width-- > 0 ) + __pformat_putc( '0', stream ); + + /* Emit the digits of the encoded numeric value... + */ + if( len > 0 ) + { + /* + * ...beginning with those which precede the radix point, + * and appending any necessary significant trailing zeros. + */ + do { + __pformat_putc( *value ? *value++ : '0', stream); + --len; + if (len != 0 && (stream->flags & PFORMAT_GROUPED) != 0 && stream->thousands_chr != 0 + && (len % 3) == 0) + __pformat_wputchars (&stream->thousands_chr, 1, stream); + } + while (len > 0); + } + else + /* The magnitude of the encoded value is less than 1.0, so no + * digits precede the radix point; we emit a mandatory initial + * zero, followed immediately by the radix point. + */ + __pformat_putc( '0', stream ); + + /* Unless the encoded value is integral, AND the radix point + * is not expressly demanded by the `#' flag, we must insert + * the appropriately localised radix point mark here... + */ + if( (stream->precision > 0) || (stream->flags & PFORMAT_HASHED) ) + __pformat_emit_radix_point( stream ); + + /* When the radix point offset, `len', is negative, this implies + * that additional zeros must appear, following the radix point, + * and preceding the first significant digit... + */ + if( len < 0 ) + { + /* To accommodate these, we adjust the precision, (reducing it + * by adding a negative value), and then we emit as many zeros + * as are required. + */ + stream->precision += len; + do __pformat_putc( '0', stream ); + while( ++len < 0 ); + } + + /* Now we emit any remaining significant digits, or trailing zeros, + * until the required precision has been achieved. + */ + while( stream->precision-- > 0 ) + __pformat_putc( *value ? *value++ : '0', stream ); +} + +static +void __pformat_emit_efloat( int sign, char *value, int e, __pformat_t *stream ) +{ + /* Helper to emit a floating point representation of numeric data, + * as encoded by a prior call to `ecvt()' or `fcvt()'; (this DOES + * include the following exponent). + */ + int exp_width = 1; + __pformat_intarg_t exponent; exponent.__pformat_llong_t = e -= 1; + + /* Determine how many digit positions are required for the exponent. + */ + while( (e /= 10) != 0 ) + exp_width++; + + /* Ensure that this is at least as many as the standard requirement. + */ + if( exp_width < stream->expmin ) + exp_width = stream->expmin; + + /* Adjust the residual field width allocation, to allow for the + * number of exponent digits to be emitted, together with a sign + * and exponent separator... + */ + if( stream->width > (exp_width += 2) ) + stream->width -= exp_width; + + else + /* ignoring the field width specification, if insufficient. + */ + stream->width = PFORMAT_IGNORE; + + /* Emit the significand, as a fixed point value with one digit + * preceding the radix point. + */ + __pformat_emit_float( sign, value, 1, stream ); + + /* Reset precision, to ensure the mandatory minimum number of + * exponent digits will be emitted, and set the flags to ensure + * the sign is displayed. + */ + stream->precision = stream->expmin; + stream->flags |= PFORMAT_SIGNED; + + /* Emit the exponent separator. + */ + __pformat_putc( ('E' | (stream->flags & PFORMAT_XCASE)), stream ); + + /* Readjust the field width setting, such that it again allows + * for the digits of the exponent, (which had been discounted when + * computing any left side padding requirement), so that they are + * correctly included in the computation of any right side padding + * requirement, (but here we exclude the exponent separator, which + * has been emitted, and so counted already). + */ + stream->width += exp_width - 1; + + /* And finally, emit the exponent itself, as a signed integer, + * with any padding required to achieve flush left justification, + * (which will be added automatically, by `__pformat_int()'). + */ + __pformat_int( exponent, stream ); +} + +static +void __pformat_float( long double x, __pformat_t *stream ) +{ + /* Handler for `%f' and `%F' format specifiers. + * + * This wraps calls to `__pformat_cvt()', `__pformat_emit_float()' + * and `__pformat_emit_inf_or_nan()', as appropriate, to achieve + * output in fixed point format. + */ + int sign, intlen; char *value; + + /* Establish the precision for the displayed value, defaulting to six + * digits following the decimal point, if not explicitly specified. + */ + if( stream->precision < 0 ) + stream->precision = 6; + + /* Encode the input value as ASCII, for display... + */ + value = __pformat_fcvt( x, stream->precision, &intlen, &sign ); + + if( intlen == PFORMAT_INFNAN ) + /* + * handle cases of `infinity' or `not-a-number'... + */ + __pformat_emit_inf_or_nan( sign, value, stream ); + + else + { /* or otherwise, emit the formatted result. + */ + __pformat_emit_float( sign, value, intlen, stream ); + + /* and, if there is any residual field width as yet unfilled, + * then we must be doing flush left justification, so pad out to + * the right hand field boundary. + */ + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); + } + + /* Clean up `__pformat_fcvt()' memory allocation for `value'... + */ + __pformat_fcvt_release( value ); +} + +static +void __pformat_efloat( long double x, __pformat_t *stream ) +{ + /* Handler for `%e' and `%E' format specifiers. + * + * This wraps calls to `__pformat_cvt()', `__pformat_emit_efloat()' + * and `__pformat_emit_inf_or_nan()', as appropriate, to achieve + * output in floating point format. + */ + int sign, intlen; char *value; + + /* Establish the precision for the displayed value, defaulting to six + * digits following the decimal point, if not explicitly specified. + */ + if( stream->precision < 0 ) + stream->precision = 6; + + /* Encode the input value as ASCII, for display... + */ + value = __pformat_ecvt( x, stream->precision + 1, &intlen, &sign ); + + if( intlen == PFORMAT_INFNAN ) + /* + * handle cases of `infinity' or `not-a-number'... + */ + __pformat_emit_inf_or_nan( sign, value, stream ); + + else + /* or otherwise, emit the formatted result. + */ + __pformat_emit_efloat( sign, value, intlen, stream ); + + /* Clean up `__pformat_ecvt()' memory allocation for `value'... + */ + __pformat_ecvt_release( value ); +} + +static +void __pformat_gfloat( long double x, __pformat_t *stream ) +{ + /* Handler for `%g' and `%G' format specifiers. + * + * This wraps calls to `__pformat_cvt()', `__pformat_emit_float()', + * `__pformat_emit_efloat()' and `__pformat_emit_inf_or_nan()', as + * appropriate, to achieve output in the more suitable of either + * fixed or floating point format. + */ + int sign, intlen; char *value; + + /* Establish the precision for the displayed value, defaulting to + * six significant digits, if not explicitly specified... + */ + if( stream->precision < 0 ) + stream->precision = 6; + + /* or to a minimum of one digit, otherwise... + */ + else if( stream->precision == 0 ) + stream->precision = 1; + + /* Encode the input value as ASCII, for display. + */ + value = __pformat_ecvt( x, stream->precision, &intlen, &sign ); + + if( intlen == PFORMAT_INFNAN ) + /* + * Handle cases of `infinity' or `not-a-number'. + */ + __pformat_emit_inf_or_nan( sign, value, stream ); + + else if( (-4 < intlen) && (intlen <= stream->precision) ) + { + /* Value lies in the acceptable range for fixed point output, + * (i.e. the exponent is no less than minus four, and the number + * of significant digits which precede the radix point is fewer + * than the least number which would overflow the field width, + * specified or implied by the established precision). + */ + if( (stream->flags & PFORMAT_HASHED) == PFORMAT_HASHED ) + /* + * The `#' flag is in effect... + * Adjust precision to retain the specified number of significant + * digits, with the proper number preceding the radix point, and + * the balance following it... + */ + stream->precision -= intlen; + + else + /* The `#' flag is not in effect... + * Here we adjust the precision to accommodate all digits which + * precede the radix point, but we truncate any balance following + * it, to suppress output of non-significant trailing zeros... + */ + if( ((stream->precision = strlen( value ) - intlen) < 0) + /* + * This may require a compensating adjustment to the field + * width, to accommodate significant trailing zeros, which + * precede the radix point... + */ + && (stream->width > 0) ) + stream->width += stream->precision; + + /* Now, we format the result as any other fixed point value. + */ + __pformat_emit_float( sign, value, intlen, stream ); + + /* If there is any residual field width as yet unfilled, then + * we must be doing flush left justification, so pad out to the + * right hand field boundary. + */ + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); + } + + else + { /* Value lies outside the acceptable range for fixed point; + * one significant digit will precede the radix point, so we + * decrement the precision to retain only the appropriate number + * of additional digits following it, when we emit the result + * in floating point format. + */ + if( (stream->flags & PFORMAT_HASHED) == PFORMAT_HASHED ) + /* + * The `#' flag is in effect... + * Adjust precision to emit the specified number of significant + * digits, with one preceding the radix point, and the balance + * following it, retaining any non-significant trailing zeros + * which are required to exactly match the requested precision... + */ + stream->precision--; + + else + /* The `#' flag is not in effect... + * Adjust precision to emit only significant digits, with one + * preceding the radix point, and any others following it, but + * suppressing non-significant trailing zeros... + */ + stream->precision = strlen( value ) - 1; + + /* Now, we format the result as any other floating point value. + */ + __pformat_emit_efloat( sign, value, intlen, stream ); + } + + /* Clean up `__pformat_ecvt()' memory allocation for `value'. + */ + __pformat_ecvt_release( value ); +} + +static +void __pformat_emit_xfloat( __pformat_fpreg_t value, __pformat_t *stream ) +{ + /* Helper for emitting floating point data, originating as + * either `double' or `long double' type, as a hexadecimal + * representation of the argument value. + */ + char buf[18 + 6], *p = buf; + __pformat_intarg_t exponent; short exp_width = 2; + + /* The mantissa field of the argument value representation can + * accommodate at most 16 hexadecimal digits, of which one will + * be placed before the radix point, leaving at most 15 digits + * to satisfy any requested precision; thus... + */ + if( (stream->precision >= 0) && (stream->precision < 15) ) + { + /* When the user specifies a precision within this range, + * we want to adjust the mantissa, to retain just the number + * of digits required, rounding up when the high bit of the + * leftmost discarded digit is set; (mask of 0x08 accounts + * for exactly one digit discarded, shifting 4 bits per + * digit, with up to 14 additional digits, to consume the + * full availability of 15 precision digits). + * + * However, before we perform the rounding operation, we + * normalise the mantissa, shifting it to the left by as many + * bit positions may be necessary, until its highest order bit + * is set, thus preserving the maximum number of bits in the + * rounded result as possible. + */ + while( value.__pformat_fpreg_mantissa < (LLONG_MAX + 1ULL) ) + value.__pformat_fpreg_mantissa <<= 1; + + /* We then shift the mantissa one bit position back to the + * right, to guard against possible overflow when the rounding + * adjustment is added. + */ + value.__pformat_fpreg_mantissa >>= 1; + + /* We now add the rounding adjustment, noting that to keep the + * 0x08 mask aligned with the shifted mantissa, we also need to + * shift it right by one bit initially, changing its starting + * value to 0x04... + */ + value.__pformat_fpreg_mantissa += 0x04LL << (4 * (14 - stream->precision)); + if( (value.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0ULL ) + /* + * When the rounding adjustment would not have overflowed, + * then we shift back to the left again, to fill the vacated + * bit we reserved to accommodate the carry. + */ + value.__pformat_fpreg_mantissa <<= 1; + + else + /* Otherwise the rounding adjustment would have overflowed, + * so the carry has already filled the vacated bit; the effect + * of this is equivalent to an increment of the exponent. + */ + value.__pformat_fpreg_exponent++; + + /* We now complete the rounding to the required precision, by + * shifting the unwanted digits out, from the right hand end of + * the mantissa. + */ + value.__pformat_fpreg_mantissa >>= 4 * (15 - stream->precision); + } + + /* Encode the significant digits of the mantissa in hexadecimal + * ASCII notation, ready for transfer to the output stream... + */ + while( value.__pformat_fpreg_mantissa ) + { + /* taking the rightmost digit in each pass... + */ + int c = value.__pformat_fpreg_mantissa & 0xF; + if( c == (int) value.__pformat_fpreg_mantissa) + { + /* inserting the radix point, when we reach the last, + * (i.e. the most significant digit), unless we found no + * less significant digits, with no mandatory radix point + * inclusion, and no additional required precision... + */ + if( (p > buf) + || (stream->flags & PFORMAT_HASHED) || (stream->precision > 0) ) + { + /* + * Internally, we represent the radix point as an ASCII '.'; + * we will replace it with any locale specific alternative, + * at the time of transfer to the ultimate destination. + */ + *p++ = '.'; + } + + /* If the most significant hexadecimal digit of the encoded + * output value is greater than one, then the indicated value + * will appear too large, by an additional binary exponent + * corresponding to the number of higher order bit positions + * which it occupies... + */ + while( value.__pformat_fpreg_mantissa > 1 ) + { + /* so reduce the exponent value to compensate... + */ + value.__pformat_fpreg_exponent--; + value.__pformat_fpreg_mantissa >>= 1; + } + } + + else if( stream->precision > 0 ) + /* + * we have not yet fulfilled the desired precision, + * and we have not yet found the most significant digit, + * so account for the current digit, within the field + * width required to meet the specified precision. + */ + stream->precision--; + + if( (c > 0) || (p > buf) || (stream->precision >= 0) ) + { + /* + * Ignoring insignificant trailing zeros, (unless required to + * satisfy specified precision), store the current encoded digit + * into the pending output buffer, in LIFO order, and using the + * appropriate case for digits in the `A'..`F' range. + */ + *p++ = c > 9 ? (c - 10 + 'A') | (stream->flags & PFORMAT_XCASE) : c + '0'; + } + /* Shift out the current digit, (4-bit logical shift right), + * to align the next more significant digit to be extracted, + * and encoded in the next pass. + */ + value.__pformat_fpreg_mantissa >>= 4; + } + + if( p == buf ) + { + /* Nothing has been queued for output... + * We need at least one zero, and possibly a radix point. + */ + if( (stream->precision > 0) || (stream->flags & PFORMAT_HASHED) ) + *p++ = '.'; + + *p++ = '0'; + } + + if( stream->width > 0 ) + { + /* Adjust the user specified field width, to account for the + * number of digits minimally required, to display the encoded + * value, at the requested precision. + * + * FIXME: this uses the minimum number of digits possible for + * representation of the binary exponent, in strict conformance + * with C99 and POSIX specifications. Although there appears to + * be no Microsoft precedent for doing otherwise, we may wish to + * relate this to the `_get_output_format()' result, to maintain + * consistency with `%e', `%f' and `%g' styles. + */ + int min_width = p - buf; + int exponent2 = value.__pformat_fpreg_exponent; + + /* If we have not yet queued sufficient digits to fulfil the + * requested precision, then we must adjust the minimum width + * specification, to accommodate the additional digits which + * are required to do so. + */ + if( stream->precision > 0 ) + min_width += stream->precision; + + /* Adjust the minimum width requirement, to accomodate the + * sign, radix indicator and at least one exponent digit... + */ + min_width += stream->flags & PFORMAT_SIGNED ? 6 : 5; + while( (exponent2 = exponent2 / 10) != 0 ) + { + /* and increase as required, if additional exponent digits + * are needed, also saving the exponent field width adjustment, + * for later use when that is emitted. + */ + min_width++; + exp_width++; + } + + if( stream->width > min_width ) + { + /* When specified field width exceeds the minimum required, + * adjust to retain only the excess... + */ + stream->width -= min_width; + + /* and then emit any required left side padding spaces. + */ + if( (stream->flags & PFORMAT_JUSTIFY) == 0 ) + while( stream->width-- > 0 ) + __pformat_putc( '\x20', stream ); + } + + else + /* Specified field width is insufficient; just ignore it! + */ + stream->width = PFORMAT_IGNORE; + } + + /* Emit the sign of the encoded value, as required... + */ + if( stream->flags & PFORMAT_NEGATIVE ) + /* + * this is mandatory, to indicate a negative value... + */ + __pformat_putc( '-', stream ); + + else if( stream->flags & PFORMAT_POSITIVE ) + /* + * but this is optional, for a positive value... + */ + __pformat_putc( '+', stream ); + + else if( stream->flags & PFORMAT_ADDSPACE ) + /* + * with this optional alternative. + */ + __pformat_putc( '\x20', stream ); + + /* Prefix a `0x' or `0X' radix indicator to the encoded value, + * with case appropriate to the format specification. + */ + __pformat_putc( '0', stream ); + __pformat_putc( 'X' | (stream->flags & PFORMAT_XCASE), stream ); + + /* If the `0' flag is in effect... + * Zero padding, to fill out the field, goes here... + */ + if( (stream->width > 0) && (stream->flags & PFORMAT_ZEROFILL) ) + while( stream->width-- > 0 ) + __pformat_putc( '0', stream ); + + /* Next, we emit the encoded value, without its exponent... + */ + while( p > buf ) + __pformat_emit_numeric_value( *--p, stream ); + + /* followed by any additional zeros needed to satisfy the + * precision specification... + */ + while( stream->precision-- > 0 ) + __pformat_putc( '0', stream ); + + /* then the exponent prefix, (C99 and POSIX specify `p'), + * in the case appropriate to the format specification... + */ + __pformat_putc( 'P' | (stream->flags & PFORMAT_XCASE), stream ); + + /* and finally, the decimal representation of the binary exponent, + * as a signed value with mandatory sign displayed, in a field width + * adjusted to accommodate it, LEFT justified, with any additional + * right side padding remaining from the original field width. + */ + stream->width += exp_width; + stream->flags |= PFORMAT_SIGNED; + exponent.__pformat_llong_t = value.__pformat_fpreg_exponent; + __pformat_int( exponent, stream ); +} + +static +void __pformat_xldouble( long double x, __pformat_t *stream ) +{ + /* Handler for `%La' and `%LA' format specifiers, (with argument + * value specified as `long double' type). + */ + unsigned sign_bit = 0; + __pformat_fpreg_t z; z.__pformat_fpreg_ldouble_t = x; + + /* First check for NaN; it is emitted unsigned... + */ + if( isnan( x ) ) + __pformat_emit_inf_or_nan( sign_bit, "NaN", stream ); + + else + { /* Capture the sign bit up-front, so we can show it correctly + * even when the argument value is zero or infinite. + */ + if( (sign_bit = (z.__pformat_fpreg_exponent & 0x8000)) != 0 ) + stream->flags |= PFORMAT_NEGATIVE; + + /* Check for infinity, (positive or negative)... + */ + if( isinf( x ) ) + /* + * displaying the appropriately signed indicator, + * when appropriate. + */ + __pformat_emit_inf_or_nan( sign_bit, "Inf", stream ); + + else + { /* The argument value is a representable number... + * extract the effective value of the biased exponent... + */ + z.__pformat_fpreg_exponent &= 0x7FFF; + if( z.__pformat_fpreg_exponent == 0 ) + { + /* A biased exponent value of zero means either a + * true zero value, if the mantissa field also has + * a zero value, otherwise... + */ + if( z.__pformat_fpreg_mantissa != 0 ) + { + /* ...this mantissa represents a subnormal value; + * adjust the exponent, while shifting the mantissa + * to the left, until its leading bit is 1. + */ + z.__pformat_fpreg_exponent = 1-0x3FFF; + while( (z.__pformat_fpreg_mantissa & (LLONG_MAX + 1ULL)) == 0 ) + { + z.__pformat_fpreg_mantissa <<= 1; + --z.__pformat_fpreg_exponent; + } + } + } + else + /* This argument represents a non-zero normal number; + * eliminate the bias from the exponent... + */ + z.__pformat_fpreg_exponent -= 0x3FFF; + + /* Finally, hand the adjusted representation off to the + * generalised hexadecimal floating point format handler... + */ + __pformat_emit_xfloat( z, stream ); + } + } +} + +int __cdecl +_evil_pformat (int flags, void *dest, int max, const APICHAR *fmt, va_list argv) +{ + int c; + int saved_errno = errno; + + __pformat_t stream = + { + /* Create and initialise a format control block + * for this output request. + */ + dest, /* output goes to here */ + flags &= PFORMAT_TO_FILE | PFORMAT_NOLIMIT, /* only these valid initially */ + PFORMAT_IGNORE, /* no field width yet */ + PFORMAT_IGNORE, /* nor any precision spec */ + PFORMAT_RPINIT, /* radix point uninitialised */ + (wchar_t)(0), /* leave it unspecified */ + 0, + (wchar_t)(0), /* leave it unspecified */ + 0, /* zero output char count */ + max, /* establish output limit */ + PFORMAT_MINEXP /* exponent chars preferred */ + }; + + format_scan: while( (c = *fmt++) != 0 ) + { + /* Format string parsing loop... + * The entry point is labelled, so that we can return to the start state + * from within the inner `conversion specification' interpretation loop, + * as soon as a conversion specification has been resolved. + */ + if( c == '%' ) + { + /* Initiate parsing of a `conversion specification'... + */ + __pformat_intarg_t argval; + __pformat_state_t state = PFORMAT_INIT; + __pformat_length_t length = PFORMAT_LENGTH_INT; + + /* Save the current format scan position, so that we can backtrack + * in the event of encountering an invalid format specification... + */ + const APICHAR *backtrack = fmt; + + /* Restart capture for dynamic field width and precision specs... + */ + int *width_spec = &stream.width; + + /* Reset initial state for flags, width and precision specs... + */ + stream.flags = flags; + stream.width = stream.precision = PFORMAT_IGNORE; + + while( *fmt ) + { + switch( c = *fmt++ ) + { + /* Data type specifiers... + * All are terminal, so exit the conversion spec parsing loop + * with a `goto format_scan', thus resuming at the outer level + * in the regular format string parser. + */ + case '%': + /* + * Not strictly a data type specifier... + * it simply converts as a literal `%' character. + * + * FIXME: should we require this to IMMEDIATELY follow the + * initial `%' of the "conversion spec"? (glibc `printf()' + * on GNU/Linux does NOT appear to require this, but POSIX + * and SUSv3 do seem to demand it). + */ + __pformat_putc( c, &stream ); + goto format_scan; + + case 'C': + /* + * Equivalent to `%lc'; set `length' accordingly, + * and simply fall through. + */ + length = PFORMAT_LENGTH_LONG; + + case 'c': + /* + * Single, (or single multibyte), character output... + * + * We handle these by copying the argument into our local + * `argval' buffer, and then we pass the address of that to + * either `__pformat_putchars()' or `__pformat_wputchars()', + * as appropriate, effectively formatting it as a string of + * the appropriate type, with a length of one. + * + * A side effect of this method of handling character data + * is that, if the user sets a precision of zero, then no + * character is actually emitted; we don't want that, so we + * forcibly override any user specified precision. + */ + stream.precision = PFORMAT_IGNORE; + + /* Now we invoke the appropriate format handler... + */ + if( (length == PFORMAT_LENGTH_LONG) + || (length == PFORMAT_LENGTH_LLONG) ) + { + /* considering any `long' type modifier as a reference to + * `wchar_t' data, (which is promoted to an `int' argument)... + */ + wchar_t iargval = (wchar_t)(va_arg( argv, int )); + __pformat_wputchars( &iargval, 1, &stream ); + } + else + { /* while anything else is simply taken as `char', (which + * is also promoted to an `int' argument)... + */ + argval.__pformat_uchar_t = (unsigned char)(va_arg( argv, int )); + __pformat_putchars( (char *)(&argval), 1, &stream ); + } + goto format_scan; + + case 'S': + /* + * Equivalent to `%ls'; set `length' accordingly, + * and simply fall through. + */ + length = PFORMAT_LENGTH_LONG; + + case 's': + if( (length == PFORMAT_LENGTH_LONG) + || (length == PFORMAT_LENGTH_LLONG)) + { + /* considering any `long' type modifier as a reference to + * a `wchar_t' string... + */ + __pformat_wcputs( va_arg( argv, wchar_t * ), &stream ); + } + else + /* This is normal string output; + * we simply invoke the appropriate handler... + */ + __pformat_puts( va_arg( argv, char * ), &stream ); + goto format_scan; + case 'm': /* strerror (errno) */ + __pformat_puts (strerror (saved_errno), &stream); + goto format_scan; + + case 'o': + case 'u': + case 'x': + case 'X': + /* + * Unsigned integer values; octal, decimal or hexadecimal format... + */ + if( length == PFORMAT_LENGTH_LLONG ) + /* + * with an `unsigned long long' argument, which we + * process `as is'... + */ + argval.__pformat_ullong_t = va_arg( argv, unsigned long long ); + + else if( length == PFORMAT_LENGTH_LONG ) + /* + * or with an `unsigned long', which we promote to + * `unsigned long long'... + */ + argval.__pformat_ullong_t = va_arg( argv, unsigned long ); + + else + { /* or for any other size, which will have been promoted + * to `unsigned int', we select only the appropriately sized + * least significant segment, and again promote to the same + * size as `unsigned long long'... + */ + argval.__pformat_ullong_t = va_arg( argv, unsigned int ); + if( length == PFORMAT_LENGTH_SHORT ) + /* + * from `unsigned short'... + */ + argval.__pformat_ullong_t = argval.__pformat_ushort_t; + + else if( length == PFORMAT_LENGTH_CHAR ) + /* + * or even from `unsigned char'... + */ + argval.__pformat_ullong_t = argval.__pformat_uchar_t; + } + + /* so we can pass any size of argument to either of two + * common format handlers... + */ + if( c == 'u' ) + /* + * depending on whether output is to be encoded in + * decimal format... + */ + __pformat_int( argval, &stream ); + + else + /* or in octal or hexadecimal format... + */ + __pformat_xint( c, argval, &stream ); + + goto format_scan; + + case 'd': + case 'i': + /* + * Signed integer values; decimal format... + * This is similar to `u', but must process `argval' as signed, + * and be prepared to handle negative numbers. + */ + stream.flags |= PFORMAT_NEGATIVE; + + if( length == PFORMAT_LENGTH_LLONG ) + /* + * The argument is a `long long' type... + */ + argval.__pformat_llong_t = va_arg( argv, long long ); + + else if( length == PFORMAT_LENGTH_LONG ) + /* + * or here, a `long' type... + */ + argval.__pformat_llong_t = va_arg( argv, long ); + + else + { /* otherwise, it's an `int' type... + */ + argval.__pformat_llong_t = va_arg( argv, int ); + if( length == PFORMAT_LENGTH_SHORT ) + /* + * but it was promoted from a `short' type... + */ + argval.__pformat_llong_t = argval.__pformat_short_t; + else if( length == PFORMAT_LENGTH_CHAR ) + /* + * or even from a `char' type... + */ + argval.__pformat_llong_t = argval.__pformat_char_t; + } + + /* In any case, all share a common handler... + */ + __pformat_int( argval, &stream ); + goto format_scan; + + case 'p': + /* + * Pointer argument; format as hexadecimal, subject to... + */ + if( (state == PFORMAT_INIT) && (stream.flags == flags) ) + { + /* Here, the user didn't specify any particular + * formatting attributes. We must choose a default + * which will be compatible with Microsoft's (broken) + * scanf() implementation, (i.e. matching the default + * used by MSVCRT's printf(), which appears to resemble + * "%0.8X" for 32-bit pointers); in particular, we MUST + * NOT adopt a GNU-like format resembling "%#x", because + * Microsoft's scanf() will choke on the "0x" prefix. + */ + stream.flags |= PFORMAT_ZEROFILL; + stream.precision = 2 * sizeof( uintptr_t ); + } + argval.__pformat_ullong_t = va_arg( argv, uintptr_t ); + __pformat_xint( 'x', argval, &stream ); + goto format_scan; + + case 'e': + /* + * Floating point format, with lower case exponent indicator + * and lower case `inf' or `nan' representation when required; + * select lower case mode, and simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'E': + /* + * Floating point format, with upper case exponent indicator + * and upper case `INF' or `NAN' representation when required, + * (or lower case for all of these, on fall through from above); + * select lower case mode, and simply fall through... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_efloat( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_efloat( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'f': + /* + * Fixed point format, using lower case for `inf' and + * `nan', when appropriate; select lower case mode, and + * simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'F': + /* + * Fixed case format using upper case, or lower case on + * fall through from above, for `INF' and `NAN'... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_float( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_float( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'g': + /* + * Generalised floating point format, with lower case + * exponent indicator when required; select lower case + * mode, and simply fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'G': + /* + * Generalised floating point format, with upper case, + * or on fall through from above, with lower case exponent + * indicator when required... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * for a `long double' argument... + */ + __pformat_gfloat( va_arg( argv, long double ), &stream ); + + else + /* or just a `double', which we promote to `long double', + * so the two may share a common format handler. + */ + __pformat_gfloat( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'a': + /* + * Hexadecimal floating point format, with lower case radix + * and exponent indicators; select the lower case mode, and + * fall through... + */ + stream.flags |= PFORMAT_XCASE; + + case 'A': + /* + * Hexadecimal floating point format; handles radix and + * exponent indicators in either upper or lower case... + */ + if( stream.flags & PFORMAT_LDOUBLE ) + /* + * with a `long double' argument... + */ + __pformat_xldouble( va_arg( argv, long double ), &stream ); + + else + /* or just a `double'. + */ + __pformat_xldouble( (long double)(va_arg( argv, double )), &stream ); + + goto format_scan; + + case 'n': + /* + * Save current output character count... + */ + if( length == PFORMAT_LENGTH_CHAR ) + /* + * to a signed `char' destination... + */ + *va_arg( argv, char * ) = stream.count; + + else if( length == PFORMAT_LENGTH_SHORT ) + /* + * or to a signed `short'... + */ + *va_arg( argv, short * ) = stream.count; + + else if( length == PFORMAT_LENGTH_LONG ) + /* + * or to a signed `long'... + */ + *va_arg( argv, long * ) = stream.count; + + else if( length == PFORMAT_LENGTH_LLONG ) + /* + * or to a signed `long long'... + */ + *va_arg( argv, long long * ) = stream.count; + + else + /* + * or, by default, to a signed `int'. + */ + *va_arg( argv, int * ) = stream.count; + + goto format_scan; + + /* Argument length modifiers... + * These are non-terminal; each sets the format parser + * into the PFORMAT_END state, and ends with a `break'. + */ + case 'h': + /* + * Interpret the argument as explicitly of a `short' + * or `char' data type, truncated from the standard + * length defined for integer promotion. + */ + if( *fmt == 'h' ) + { + /* Modifier is `hh'; data type is `char' sized... + * Skip the second `h', and set length accordingly. + */ + ++fmt; + length = PFORMAT_LENGTH_CHAR; + } + + else + /* Modifier is `h'; data type is `short' sized... + */ + length = PFORMAT_LENGTH_SHORT; + + state = PFORMAT_END; + break; + + case 'j': + /* + * Interpret the argument as being of the same size as + * a `intmax_t' entity... + */ + length = __pformat_arg_length( intmax_t ); + state = PFORMAT_END; + break; + +# ifdef _WIN32 + + case 'I': + /* + * The MSVCRT implementation of the printf() family of + * functions explicitly uses... + */ + if( (fmt[0] == '6') && (fmt[1] == '4') ) + { + /* I64' instead of `ll', + * when referring to `long long' integer types... + */ + length = PFORMAT_LENGTH_LLONG; + fmt += 2; + } + + else if( (fmt[0] == '3') && (fmt[1] == '2') ) + { + /* and `I32' instead of `l', + * when referring to `long' integer types... + */ + length = PFORMAT_LENGTH_LONG; + fmt += 2; + } + + else + /* or unqualified `I' instead of `t' or `z', + * when referring to `ptrdiff_t' or `size_t' entities; + * (we will choose to map it to `ptrdiff_t'). + */ + length = __pformat_arg_length( ptrdiff_t ); + + state = PFORMAT_END; + break; + +# endif + + case 'l': + /* + * Interpret the argument as explicitly of a + * `long' or `long long' data type. + */ + if( *fmt == 'l' ) + { + /* Modifier is `ll'; data type is `long long' sized... + * Skip the second `l', and set length accordingly. + */ + ++fmt; + length = PFORMAT_LENGTH_LLONG; + } + + else + /* Modifier is `l'; data type is `long' sized... + */ + length = PFORMAT_LENGTH_LONG; + + state = PFORMAT_END; + break; + + case 'L': + /* + * Identify the appropriate argument as a `long double', + * when associated with `%a', `%A', `%e', `%E', `%f', `%F', + * `%g' or `%G' format specifications. + */ + stream.flags |= PFORMAT_LDOUBLE; + state = PFORMAT_END; + break; + + case 't': + /* + * Interpret the argument as being of the same size as + * a `ptrdiff_t' entity... + */ + length = __pformat_arg_length( ptrdiff_t ); + state = PFORMAT_END; + break; + + case 'z': + /* + * Interpret the argument as being of the same size as + * a `size_t' entity... + */ + length = __pformat_arg_length( size_t ); + state = PFORMAT_END; + break; + + /* Precision indicator... + * May appear once only; it must precede any modifier + * for argument length, or any data type specifier. + */ + case '.': + if( state < PFORMAT_GET_PRECISION ) + { + /* We haven't seen a precision specification yet, + * so initialise it to zero, (in case no digits follow), + * and accept any following digits as the precision. + */ + stream.precision = 0; + width_spec = &stream.precision; + state = PFORMAT_GET_PRECISION; + } + + else + /* We've already seen a precision specification, + * so this is just junk; proceed to end game. + */ + state = PFORMAT_END; + + /* Either way, we must not fall through here. + */ + break; + + /* Variable field width, or precision specification, + * derived from the argument list... + */ + case '*': + /* + * When this appears... + */ + if( width_spec + && ((state == PFORMAT_INIT) || (state == PFORMAT_GET_PRECISION)) ) + { + /* in proper context; assign to field width + * or precision, as appropriate. + */ + if( (*width_spec = va_arg( argv, int )) < 0 ) + { + /* Assigned value was negative... + */ + if( state == PFORMAT_INIT ) + { + /* For field width, this is equivalent to + * a positive value with the `-' flag... + */ + stream.flags |= PFORMAT_LJUSTIFY; + stream.width = -stream.width; + } + + else + /* while as a precision specification, + * it should simply be ignored. + */ + stream.precision = PFORMAT_IGNORE; + } + } + + else + /* out of context; give up on width and precision + * specifications for this conversion. + */ + state = PFORMAT_END; + + /* Mark as processed... + * we must not see `*' again, in this context. + */ + width_spec = NULL; + break; + + /* Formatting flags... + * Must appear while in the PFORMAT_INIT state, + * and are non-terminal, so again, end with `break'. + */ + case '#': + /* + * Select alternate PFORMAT_HASHED output style. + */ + if( state == PFORMAT_INIT ) + stream.flags |= PFORMAT_HASHED; + break; + + case '+': + /* + * Print a leading sign with numeric output, + * for both positive and negative values. + */ + if( state == PFORMAT_INIT ) + stream.flags |= PFORMAT_POSITIVE; + break; + + case '-': + /* + * Select left justification of displayed output + * data, within the output field width, instead of + * the default flush right justification. + */ + if( state == PFORMAT_INIT ) + stream.flags |= PFORMAT_LJUSTIFY; + break; + + case '\'': + /* + * This is an XSI extension to the POSIX standard, + * which we do not support, at present. + */ + if (state == PFORMAT_INIT) + { + int len; wchar_t rpchr; mbstate_t cstate; + stream.flags |= PFORMAT_GROUPED; /* $$$$ */ + memset (&cstate, 0, sizeof(state)); + if ((len = mbrtowc( &rpchr, localeconv()->thousands_sep, 16, &cstate)) > 0) + stream.thousands_chr = rpchr; + stream.thousands_chr_len = len; + } + break; + + case '\x20': + /* + * Reserve a single space, within the output field, + * for display of the sign of signed data; this will + * be occupied by the minus sign, if the data value + * is negative, or by a plus sign if the data value + * is positive AND the `+' flag is also present, or + * by a space otherwise. (Technically, this flag + * is redundant, if the `+' flag is present). + */ + if( state == PFORMAT_INIT ) + stream.flags |= PFORMAT_ADDSPACE; + break; + + case '0': + /* + * May represent a flag, to activate the `pad with zeros' + * option, or it may simply be a digit in a width or in a + * precision specification... + */ + if( state == PFORMAT_INIT ) + { + /* This is the flag usage... + */ + stream.flags |= PFORMAT_ZEROFILL; + break; + } + + default: + /* + * If we didn't match anything above, then we will check + * for digits, which we may accumulate to generate field + * width or precision specifications... + */ + if( (state < PFORMAT_END) && ('9' >= c) && (c >= '0') ) + { + if( state == PFORMAT_INIT ) + /* + * Initial digits explicitly relate to field width... + */ + state = PFORMAT_SET_WIDTH; + + else if( state == PFORMAT_GET_PRECISION ) + /* + * while those following a precision indicator + * explicitly relate to precision. + */ + state = PFORMAT_SET_PRECISION; + + if( width_spec ) + { + /* We are accepting a width or precision specification... + */ + if( *width_spec < 0 ) + /* + * and accumulation hasn't started yet; we simply + * initialise the accumulator with the current digit + * value, converting from ASCII to decimal. + */ + *width_spec = c - '0'; + + else + /* Accumulation has already started; we perform a + * `leftwise decimal digit shift' on the accumulator, + * (i.e. multiply it by ten), then add the decimal + * equivalent value of the current digit. + */ + *width_spec = *width_spec * 10 + c - '0'; + } + } + + else + { + /* We found a digit out of context, or some other character + * with no designated meaning; reject this format specification, + * backtrack, and emit it as literal text... + */ + fmt = backtrack; + __pformat_putc( '%', &stream ); + goto format_scan; + } + } + } + } + + else + /* We just parsed a character which is not included within any format + * specification; we simply emit it as a literal. + */ + __pformat_putc( c, &stream ); + } + + /* When we have fully dispatched the format string, the return value is the + * total number of bytes we transferred to the output destination. + */ + return stream.count; +} + +/* $RCSfile: pformat.c,v $Revision: 1.9 $: end of file */ diff --git a/src/lib/evil/evil_pformatw.c b/src/lib/evil/evil_pformatw.c new file mode 100644 index 0000000000..592b56881c --- /dev/null +++ b/src/lib/evil/evil_pformatw.c @@ -0,0 +1,9 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the w64 mingw-runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ +#define __BUILD_WIDEAPI 1 + +#include "evil_pformata.c" + diff --git a/src/lib/evil/evil_print.h b/src/lib/evil/evil_print.h new file mode 100644 index 0000000000..511247bcbd --- /dev/null +++ b/src/lib/evil/evil_print.h @@ -0,0 +1,37 @@ +#ifndef __EVIL_PRINT_H__ +#define __EVIL_PRINT_H__ + + +#include "evil_macro.h" + + +EAPI int __cdecl _evil_fprintfa(FILE *, const char *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_printfa(const char *, ...) __EVIL_PRINTF(1, 2) __EVIL_NOTHROW; +EAPI int __cdecl _evil_sprintfa(char *, const char *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_snprintfa(char *, size_t, const char *, ...) __EVIL_PRINTF(3, 4) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vfprintfa(FILE *, const char *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vprintfa(const char *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vsprintfa(char *, const char *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vsnprintfa(char *, size_t, const char *, va_list) __EVIL_NOTHROW; + +EAPI int __cdecl _evil_fscanf (FILE *, const char *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_scanf (const char *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_sscanf (const char *, const char *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vfscanf(FILE *, const char *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vscanf (const char *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vsscanf(const char *, const char *, va_list) __EVIL_NOTHROW; + +EAPI int __cdecl _evil_asprintf(char ** __restrict__, const char * __restrict__, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vasprintf(char ** __restrict__, const char * __restrict__, va_list) __EVIL_NOTHROW; + +EAPI int __cdecl _evil_fprintfw(FILE *, const wchar_t *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_printfw(const wchar_t *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_sprintfw(wchar_t *, const wchar_t *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_snprintfw(wchar_t *, size_t, const wchar_t *, ...) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vfprintfw(FILE *, const wchar_t *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vprintfw(const wchar_t *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vsprintfw(wchar_t *, const wchar_t *, va_list) __EVIL_NOTHROW; +EAPI int __cdecl _evil_vsnprintfw(wchar_t *, size_t, const wchar_t *, va_list) __EVIL_NOTHROW; + + +#endif /* __EVIL_PRINT_H__ */ diff --git a/src/lib/evil/evil_printa.c b/src/lib/evil/evil_printa.c new file mode 100644 index 0000000000..e6f6eb8227 --- /dev/null +++ b/src/lib/evil/evil_printa.c @@ -0,0 +1,1786 @@ +#define __CRT__NO_INLINE + +#include <limits.h> +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <ctype.h> +#include <wctype.h> +#include <locale.h> + +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif /* HAVE_ERRNO_H */ + +#include "evil_pformat.h" +#include "evil_print.h" +#include "gdtoa/gdtoa.h" + + +/*============================================================================* + * Local * + *============================================================================*/ + + +/* Helper flags for conversion. */ +#define IS_C 0x0001 +#define IS_S 0x0002 +#define IS_L 0x0004 +#define IS_LL 0x0008 +#define IS_SIGNED_NUM 0x0010 +#define IS_POINTER 0x0020 +#define IS_HEX_FLOAT 0x0040 +#define IS_SUPPRESSED 0x0080 +#define USE_GROUP 0x0100 +#define USE_GNU_ALLOC 0x0200 +#define USE_POSIX_ALLOC 0x0400 + +#define IS_ALLOC_USED (USE_GNU_ALLOC | USE_POSIX_ALLOC) + +#ifndef __BUILD_WIDEAPI + +/* internal stream structure with back-buffer. */ +typedef struct _IFP +{ + __extension__ union { + void *fp; + const char *str; + }; + int bch[1024]; + int is_string : 1; + int back_top; + int seen_eof : 1; +} _IFP; + +static void * +get_va_nth (va_list argp, unsigned int n) +{ + va_list ap; + if (!n) abort (); + va_copy (ap, argp); + while (--n > 0) + (void) va_arg(ap, void *); + return va_arg (ap, void *); +} + +static void +optimize_alloc (char **p, char *end, size_t alloc_sz) +{ + size_t need_sz; + char *h; + + if (!p || !*p) + return; + + need_sz = end - *p; + if (need_sz == alloc_sz) + return; + + if ((h = (char *) realloc (*p, need_sz)) != NULL) + *p = h; +} + +static void +back_ch (int c, _IFP *s, size_t *rin, int not_eof) +{ + if (!not_eof && c == EOF) + return; + if (s->is_string == 0) + { + FILE *fp = s->fp; + ungetc (c, fp); + rin[0] -= 1; + return; + } + rin[0] -= 1; + s->bch[s->back_top] = c; + s->back_top += 1; +} + +static int +in_ch (_IFP *s, size_t *rin) +{ + int r; + if (s->back_top) + { + s->back_top -= 1; + r = s->bch[s->back_top]; + rin[0] += 1; + } + else if (s->seen_eof) + { + return EOF; + } + else if (s->is_string) + { + const char *ps = s->str; + r = ((int) *ps) & 0xff; + ps++; + if (r != 0) + { + rin[0] += 1; + s->str = ps; + return r; + } + s->seen_eof = 1; + return EOF; + } + else + { + FILE *fp = (FILE *) s->fp; + r = getc (fp); + if (r != EOF) + rin[0] += 1; + else s->seen_eof = 1; + } + return r; +} + +static int +match_string (_IFP *s, size_t *rin, int *c, const char *str) +{ + int ch = *c; + + if (*str == 0) + return 1; + + if (*str != (char) tolower (ch)) + return 0; + ++str; + while (*str != 0) + { + if ((ch = in_ch (s, rin)) == EOF) + { + c[0] = ch; + return 0; + } + + if (*str != (char) tolower (ch)) + { + c[0] = ch; + return 0; + } + ++str; + } + c[0] = ch; + return 1; +} + +struct gcollect +{ + size_t count; + struct gcollect *next; + char **ptrs[32]; +}; + +static void +release_ptrs (struct gcollect **pt, char **wbuf) +{ + struct gcollect *pf; + size_t cnt; + + if (!pt || (pf = *pt) == NULL) + return; + while (pf != NULL) + { + struct gcollect *pf_sv = pf; + for (cnt = 0; cnt < pf->count; ++cnt) + { + free (*pf->ptrs[cnt]); + *pf->ptrs[cnt] = NULL; + } + pf = pf->next; + free (pf_sv); + } + *pt = NULL; + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } +} + +static int +cleanup_return (int rval, struct gcollect **pfree, char **strp, char **wbuf) +{ + if (rval == EOF) + release_ptrs (pfree, wbuf); + else + { + if (pfree) + { + struct gcollect *pf = *pfree, *pf_sv; + while (pf != NULL) + { + pf_sv = pf; + pf = pf->next; + free (pf_sv); + } + *pfree = NULL; + } + if (strp != NULL) + { + free (*strp); + *strp = NULL; + } + if (wbuf) + { + free (*wbuf); + *wbuf = NULL; + } + } + return rval; +} + +static struct gcollect * +resize_gcollect (struct gcollect *pf) +{ + struct gcollect *np; + if (pf && pf->count < 32) + return pf; + np = malloc (sizeof (struct gcollect)); + np->count = 0; + np->next = pf; + return np; +} + +static char * +resize_wbuf (size_t wpsz, size_t *wbuf_max_sz, char *old) +{ + char *wbuf; + size_t nsz; + if (*wbuf_max_sz != wpsz) + return old; + nsz = (256 > (2 * wbuf_max_sz[0]) ? 256 : (2 * wbuf_max_sz[0])); + if (!old) + wbuf = (char *) malloc (nsz); + else + wbuf = (char *) realloc (old, nsz); + if (!wbuf) + { + if (old) + free (old); + } + else + *wbuf_max_sz = nsz; + return wbuf; +} + +static int +_evil_sformat (_IFP *s, const char *format, va_list argp) +{ + const char *f = format; + struct gcollect *gcollect = NULL; + size_t read_in = 0, wbuf_max_sz = 0, cnt; + ssize_t str_sz = 0; + char *str = NULL, **pstr = NULL, *wbuf = NULL; + wchar_t *wstr = NULL; + int rval = 0, c = 0, ignore_ws = 0; + va_list arg; + unsigned char fc; + unsigned int npos; + int width, flags, base = 0, errno_sv; + size_t wbuf_cur_sz, read_in_sv, new_sz, n; + char seen_dot, seen_exp, is_neg, not_in; + char *tmp_wbuf_ptr, buf[MB_LEN_MAX]; + const char *lc_decimal_point, *lc_thousands_sep; + mbstate_t state, cstate; + union { + unsigned long long ull; + unsigned long ul; + long long ll; + long l; + } cv_val; + + arg = argp; + + if (!s || s->fp == NULL || !format) + { +#ifdef HAVE_ERRNO_H + errno = EINVAL; +#endif /* HAVE_ERRNO_H */ + return EOF; + } + + memset (&state, 0, sizeof (state)); + + lc_decimal_point = localeconv()->decimal_point; + lc_thousands_sep = localeconv()->thousands_sep; + if (lc_thousands_sep != NULL && *lc_thousands_sep == 0) + lc_thousands_sep = NULL; + + while (*f != 0) + { + if (!isascii ((unsigned char) *f)) + { + int len; + + if ((len = mbrlen (f, strlen (f), &state)) > 0) + { + do + { + if ((c = in_ch (s, &read_in)) == EOF || c != (unsigned char) *f++) + { + back_ch (c, s, &read_in, 1); + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + } + while (--len > 0); + + continue; + } + } + + fc = *f++; + if (fc != '%') + { + if (isspace (fc)) + ignore_ws = 1; + else + { + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if (ignore_ws) + { + ignore_ws = 0; + if (isspace (c)) + { + do + { + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (isspace (c)); + } + } + + if (c != fc) + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + continue; + } + + width = flags = 0; + npos = 0; + wbuf_cur_sz = 0; + + if (isdigit ((unsigned char) *f)) + { + const char *svf = f; + npos = (unsigned char) *f++ - '0'; + while (isdigit ((unsigned char) *f)) + npos = npos * 10 + ((unsigned char) *f++ - '0'); + if (*f != '$') + { + npos = 0; + f = svf; + } + else + f++; + } + + do + { + if (*f == '*') + flags |= IS_SUPPRESSED; + else if (*f == '\'') + { + if (lc_thousands_sep) + flags |= USE_GROUP; + } + else if (*f == 'I') + /* we don't support locale's digits (i18N), but ignore it for now silently. */ + ; + else + break; + ++f; + } + while (1); + + while (isdigit ((unsigned char) *f)) + width = width * 10 + ((unsigned char) *f++ - '0'); + + if (!width) + width = -1; + + switch (*f) + { + case 'h': + ++f; + flags |= (*f == 'h' ? IS_C : IS_S); + if (*f == 'h') + ++f; + break; + case 'l': + ++f; + flags |= (*f == 'l' ? IS_LL : 0) | IS_L; + if (*f == 'l') + ++f; + break; + case 'q': case 'L': + ++f; + flags |= IS_LL | IS_L; + break; + case 'a': + if (f[1] != 's' && f[1] != 'S' && f[1] != '[') + break; + ++f; + flags |= USE_GNU_ALLOC; + break; + case 'm': + flags |= USE_POSIX_ALLOC; + ++f; + if (*f == 'l') + { + flags |= IS_L; + f++; + } + break; + case 'z': +#ifdef _WIN64 + flags |= IS_LL; +#else + flags |= IS_L; +#endif + ++f; + break; + case 'j': + if (sizeof (uintmax_t) > sizeof (unsigned long)) + flags |= IS_LL; + else if (sizeof (uintmax_t) > sizeof (unsigned int)) + flags |= IS_L; + ++f; + break; + case 't': +#ifdef _WIN64 + flags |= IS_LL; +#else + flags |= IS_L; +#endif + ++f; + break; + case 0: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + default: + break; + } + + if (*f == 0) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + fc = *f++; + if (ignore_ws || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n')) + { + errno_sv = errno; + errno = 0; + do + { + if ((c == EOF || (c = in_ch (s, &read_in)) == EOF) +#ifdef HAVE_ERRNO_H + && errno == EINTR +#endif /* HAVE_ERRNO_H */ + ) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + } + while (isspace (c)); + + ignore_ws = 0; + errno = errno_sv; + back_ch (c, s, &read_in, 0); + } + + switch (fc) + { + case 'c': + if ((flags & IS_L) != 0) + fc = 'C'; + break; + case 's': + if ((flags & IS_L) != 0) + fc = 'S'; + break; + } + + switch (fc) + { + case '%': + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + if (c != fc) + { + back_ch (c, s, &read_in, 1); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + break; + + case 'n': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = read_in; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = read_in; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = read_in; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (char *) get_va_nth (argp, npos) : va_arg (arg, char *)) = read_in; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = read_in; + } + break; + + case 'c': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = (width > 1024 ? 1024 : width); + if ((str = *pstr = (char *) malloc (str_sz)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + do + { + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz + (str_sz >= width ? width - 1 : str_sz); + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + *str++ = c; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + } + else + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + + break; + + case 'C': + if (width == -1) + width = 1; + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + str_sz = (width > 1024 ? 1024 : width); + *pstr = (char *) malloc (str_sz * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + buf[0] = c; + + if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz + (str_sz > width ? width - 1 : str_sz); + + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + release_ptrs (&gcollect, &wbuf); + return EOF; + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + + while (1) + { + n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); + + if (n == (size_t) -2) + { + if ((c = in_ch (s, &read_in)) == EOF) + { +#ifdef HAVE_ERRNO_H + errno = EILSEQ; +#endif /* HAVE_ERRNO_H */ + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + buf[0] = c; + continue; + } + + if (n != 1) + { +#ifdef HAVE_ERRNO_H + errno = EILSEQ; +#endif /* HAVE_ERRNO_H */ + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + break; + } + + ++wstr; + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 's': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + if (isspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = c; + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz * 2; + + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = 0; + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + break; + + case 'S': + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + if (isspace (c)) + { + back_ch (c, s, &read_in, 1); + break; + } + + buf[0] = c; + + while (1) + { + n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); + + if (n == (size_t) -2) + { + if ((c = in_ch (s, &read_in)) == EOF) + { +#ifdef HAVE_ERRNO_H + errno = EILSEQ; +#endif /* HAVE_ERRNO_H */ + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + buf[0] = c; + continue; + } + + if (n != 1) + { +#ifdef HAVE_ERRNO_H + errno = EILSEQ; +#endif /* HAVE_ERRNO_H */ + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + ++wstr; + break; + } + + if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 + && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + break; + + case 'd': case 'i': + case 'o': case 'p': + case 'u': + case 'x': case 'X': + switch (fc) + { + case 'd': + flags |= IS_SIGNED_NUM; + base = 10; + break; + case 'i': + flags |= IS_SIGNED_NUM; + base = 0; + break; + case 'o': + base = 8; + break; + case 'p': + base = 16; + flags &= ~(IS_S | IS_LL | IS_L); + #ifdef _WIN64 + flags |= IS_LL; + #endif + flags |= IS_L | IS_POINTER; + break; + case 'u': + base = 10; + break; + case 'x': case 'X': + base = 16; + break; + } + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + if (c == '+' || c == '-') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + if (width != 0 && c == '0') + { + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + c = in_ch (s, &read_in); + + if (width != 0 && tolower (c) == 'x') + { + if (!base) + base = 16; + if (base == 16) + { + if (width > 0) + --width; + c = in_ch (s, &read_in); + } + } + else if (!base) + base = 8; + } + + if (!base) + base = 10; + + while (c != EOF && width != 0) + { + if (base == 16) + { + if (!isxdigit (c)) + break; + } + else if (!isdigit (c) || (int) (c - '0') >= base) + { + const char *p = lc_thousands_sep; + int remain; + + if (base != 10 || (flags & USE_GROUP) == 0) + break; + remain = width > 0 ? width : INT_MAX; + while ((unsigned char) *p == c && remain >= 0) + { + /* As our conversion routines aren't supporting thousands + separators, we are filtering them here. */ + + ++p; + if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + + if (*p != 0) + { + if (p > lc_thousands_sep) + { + back_ch (c, s, &read_in, 0); + while (--p > lc_thousands_sep) + back_ch ((unsigned char) *p, s, &read_in, 1); + c = (unsigned char) *p; + } + break; + } + + if (width > 0) + width = remain; + --wbuf_cur_sz; + } + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + if (width > 0) + --width; + + c = in_ch (s, &read_in); + } + + if (!wbuf_cur_sz || (wbuf_cur_sz == 1 && (wbuf[0] == '+' || wbuf[0] == '-'))) + { + if (!wbuf_cur_sz && (flags & IS_POINTER) != 0 + && match_string (s, &read_in, &c, "(nil)")) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = '0'; + } + else + { + back_ch (c, s, &read_in, 0); + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + else + back_ch (c, s, &read_in, 0); + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL)) + { + if (flags & IS_SIGNED_NUM) + cv_val.ll = strtoll (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ull = strtoull (wbuf, &tmp_wbuf_ptr, base); + } + else + { + if (flags & IS_SIGNED_NUM) + cv_val.l = strtol (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); + else + cv_val.ul = strtoul (wbuf, &tmp_wbuf_ptr, base); + } + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_SIGNED_NUM) != 0) + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = cv_val.ll; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = cv_val.l; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = (short) cv_val.l; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (signed char *) get_va_nth (argp, npos) : va_arg (arg, signed char *)) = (signed char) cv_val.ul; + else + *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = (int) cv_val.l; + } + else + { + if ((flags & IS_LL) != 0) + *(npos != 0 ? (unsigned long long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long long *)) = cv_val.ull; + else if ((flags & IS_L) != 0) + *(npos != 0 ? (unsigned long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long *)) = cv_val.ul; + else if ((flags & IS_S) != 0) + *(npos != 0 ? (unsigned short *) get_va_nth (argp, npos) : va_arg (arg, unsigned short *)) + = (unsigned short) cv_val.ul; + else if ((flags & IS_C) != 0) + *(npos != 0 ? (unsigned char *) get_va_nth (argp, npos) : va_arg (arg, unsigned char *)) = (unsigned char) cv_val.ul; + else + *(npos != 0 ? (unsigned int *) get_va_nth (argp, npos) : va_arg (arg, unsigned int *)) = (unsigned int) cv_val.ul; + } + ++rval; + } + break; + + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'a': case 'A': + if (width > 0) + --width; + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + seen_dot = seen_exp = 0; + is_neg = (c == '-' ? 1 : 0); + + if (c == '-' || c == '+') + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + } + + if (tolower (c) == 'n') + { + const char *match_txt = "nan"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (tolower (c) == 'i') + { + const char *match_txt = "inf"; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + ++match_txt; + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + + if (width != 0 && (c = in_ch (s, &read_in)) != EOF && tolower (c) == 'i') + { + match_txt = "inity"; + + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + + do + { + if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + if (width > 0) + --width; + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + ++match_txt; + } + while (*match_txt != 0); + } + else if (width != 0 && c != EOF) + back_ch (c, s, &read_in, 0); + } + else + { + not_in = 'e'; + if (width != 0 && c == '0') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + c = in_ch (s, &read_in); + if (width > 0) + --width; + if (width != 0 && tolower (c) == 'x') + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + + flags |= IS_HEX_FLOAT; + not_in = 'p'; + + flags &= ~USE_GROUP; + c = in_ch (s, &read_in); + if (width > 0) + --width; + } + } + + while (1) + { + if (isdigit (c) || (!seen_exp && (flags & IS_HEX_FLOAT) != 0 && isxdigit (c)) + || (seen_exp && wbuf[wbuf_cur_sz - 1] == not_in && (c == '-' || c == '+'))) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = c; + } + else if (wbuf_cur_sz > 0 && !seen_exp && (char) tolower (c) == not_in) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = not_in; + seen_exp = seen_dot = 1; + } + else + { + const char *p = lc_decimal_point; + int remain = width > 0 ? width : INT_MAX; + + if (! seen_dot) + { + while ((unsigned char) *p == c && remain >= 0) + { + ++p; + if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + } + + if (*p == 0) + { + for (p = lc_decimal_point; *p != 0; ++p) + { + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = (unsigned char) *p; + } + if (width > 0) + width = remain; + seen_dot = 1; + } + else + { + const char *pp = lc_thousands_sep; + + if (!seen_dot && (flags & USE_GROUP) != 0) + { + while ((pp - lc_thousands_sep) < (p - lc_decimal_point) + && *pp == lc_decimal_point[(pp - lc_thousands_sep)]) + ++pp; + if ((pp - lc_thousands_sep) == (p - lc_decimal_point)) + { + while ((unsigned char) *pp == c && remain >= 0) + { + ++pp; + if (*pp == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) + break; + --remain; + } + } + } + + if (pp != NULL && *pp == 0) + { + /* As our conversion routines aren't supporting thousands + separators, we are filtering them here. */ + if (width > 0) + width = remain; + } + else + { + back_ch (c, s, &read_in, 0); + break; + } + } + } + + if (width == 0 || (c = in_ch (s, &read_in)) == EOF) + break; + + if (width > 0) + --width; + } + + if (!wbuf_cur_sz || ((flags & IS_HEX_FLOAT) != 0 && wbuf_cur_sz == 2)) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); + wbuf[wbuf_cur_sz++] = 0; + + if ((flags & IS_LL) != 0) + { + long double ld; + ld = __evil_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (long double *) get_va_nth (argp, npos) : va_arg (arg, long double *)) = is_neg ? -ld : ld; + } + else if ((flags & IS_L) != 0) + { + double d; + d = (double) __evil_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (double *) get_va_nth (argp, npos) : va_arg (arg, double *)) = is_neg ? -d : d; + } + else + { + float d = __evil_strtof (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); + if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) + *(npos != 0 ? (float *) get_va_nth (argp, npos) : va_arg (arg, float *)) = is_neg ? -d : d; + } + + if (wbuf == tmp_wbuf_ptr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + ++rval; + break; + + case '[': + if ((flags & IS_L) != 0) + { + if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + *pstr = (char *) malloc (100 * sizeof (wchar_t)); + + if ((wstr = (wchar_t *) *pstr) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + wstr = (wchar_t *) get_va_nth (argp, npos); + else + wstr = va_arg (arg, wchar_t *); + if (!wstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + } + else if ((flags & IS_SUPPRESSED) == 0) + { + if ((flags & IS_ALLOC_USED) != 0) + { + if (npos != 0) + pstr = (char **) get_va_nth (argp, npos); + else + pstr = va_arg (arg, char **); + + if (!pstr) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + str_sz = 100; + if ((str = *pstr = (char *) malloc (100)) == NULL) + return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); + + gcollect = resize_gcollect (gcollect); + gcollect->ptrs[gcollect->count++] = pstr; + } + else + { + if (npos != 0) + str = (char *) get_va_nth (argp, npos); + else + str = va_arg (arg, char *); + if (!str) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + not_in = (*f == '^' ? 1 : 0); + if (*f == '^') + f++; + + if (width < 0) + width = INT_MAX; + + if (wbuf_max_sz < 256) + { + wbuf_max_sz = 256; + if (wbuf) + free (wbuf); + wbuf = (char *) malloc (wbuf_max_sz); + } + memset (wbuf, 0, 256); + + fc = *f; + if (fc == ']' || fc == '-') + { + wbuf[fc] = 1; + ++f; + } + + while ((fc = *f++) != 0 && fc != ']') + { + if (fc == '-' && *f != 0 && *f != ']' && (unsigned char) f[-2] <= (unsigned char) *f) + { + for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc) + wbuf[fc] = 1; + } + else + wbuf[fc] = 1; + } + + if (!fc) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_L) != 0) + { + read_in_sv = read_in; + cnt = 0; + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + memset (&cstate, 0, sizeof (cstate)); + + do + { + if (wbuf[c] == not_in) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + buf[0] = c; + n = mbrtowc (wstr, buf, 1, &cstate); + + if (n == (size_t) -2) + { + ++cnt; + continue; + } + cnt = 0; + + ++wstr; + if ((flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) + { + new_sz = str_sz * 2; + while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!wstr) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + ((wchar_t *) (*pstr))[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + else + rval = EOF; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + *pstr = (char *) wstr; + wstr += str_sz; + str_sz = new_sz; + } + } + + if (--width <= 0) + break; + } + while ((c = in_ch (s, &read_in)) != EOF); + + if (cnt != 0) + { +#ifdef HAVE_ERRNO_H + errno = EILSEQ; +#endif /* HAVE_ERRNO_H */ + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + + if ((flags & IS_SUPPRESSED) == 0) + { + *wstr++ = 0; + optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); + pstr = NULL; + ++rval; + } + } + else + { + read_in_sv = read_in; + + if ((c = in_ch (s, &read_in)) == EOF) + return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); + + do + { + if (wbuf[c] == not_in) + { + back_ch (c, s, &read_in, 1); + break; + } + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = c; + if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) + { + new_sz = str_sz * 2; + + while ((str = (char *) realloc (*pstr, new_sz)) == NULL + && new_sz > (size_t) (str_sz + 1)) + new_sz = str_sz + 1; + if (!str) + { + if ((flags & USE_POSIX_ALLOC) == 0) + { + (*pstr)[str_sz - 1] = 0; + pstr = NULL; + ++rval; + } + else + rval = EOF; + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + *pstr = str; + str += str_sz; + str_sz = new_sz; + } + } + } + while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); + + if (read_in_sv == read_in) + return cleanup_return (rval, &gcollect, pstr, &wbuf); + + if ((flags & IS_SUPPRESSED) == 0) + { + *str++ = 0; + optimize_alloc (pstr, str, str_sz); + pstr = NULL; + ++rval; + } + } + break; + + default: + return cleanup_return (rval, &gcollect, pstr, &wbuf); + } + } + + if (ignore_ws) + { + while (isspace ((c = in_ch (s, &read_in)))); + back_ch (c, s, &read_in, 0); + } + + return cleanup_return (rval, &gcollect, pstr, &wbuf); +} + +#endif + +/*============================================================================* + * API * + *============================================================================*/ + +/**** printf family ****/ + +int __cdecl _evil_fprintf(FILE *stream, const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; + + va_start( argv, fmt ); + retval = _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); + va_end( argv ); + + return retval; +} + +int __cdecl _evil_printf(const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; + + va_start( argv, fmt ); + retval = _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); + va_end( argv ); + + return retval; +} + +int __cdecl _evil_sprintf(APICHAR *buf, const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; + + va_start( argv, fmt ); + buf[retval = _evil_pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; + va_end( argv ); + + return retval; +} + +int __cdecl _evil_snprintf(APICHAR *buf, size_t length, const APICHAR *fmt, ...) +{ + register int retval; + va_list argv; + + va_start( argv, fmt ); + retval = _evil_vsnprintf( buf, length, fmt, argv ); + va_end( argv ); + + return retval; +} + +int __cdecl _evil_vfprintf(FILE *stream, const APICHAR *fmt, va_list argv) +{ + return _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); +} + +int __cdecl _evil_vprintf(const APICHAR *fmt, va_list argv) +{ + return _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); +} + +int __cdecl _evil_vsprintf(APICHAR *buf, const APICHAR *fmt, va_list argv) +{ + register int retval; + buf[retval = _evil_pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; + return retval; +} + +int __cdecl _evil_vsnprintf(APICHAR *buf, size_t length, const APICHAR *fmt, va_list argv ) +{ + register int retval; + + if( length == (size_t)(0) ) + /* + * No buffer; simply compute and return the size required, + * without actually emitting any data. + */ + return _evil_pformat( 0, buf, 0, fmt, argv); + + /* If we get to here, then we have a buffer... + * Emit data up to the limit of buffer length less one, + * then add the requisite NUL terminator. + */ + retval = _evil_pformat( 0, buf, --length, fmt, argv ); + buf[retval < (int) length ? retval : (int)length] = '\0'; + + return retval; +} + + +/**** scanf family ****/ + +#ifndef __BUILD_WIDEAPI + +int __cdecl _evil_fscanf (FILE *stream, const APICHAR *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = _evil_vfscanf (stream, format, argp); + va_end (argp); + + return r; +} + +int __cdecl _evil_scanf (const APICHAR *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = _evil_vfscanf (stdin, format, argp); + va_end (argp); + + return r; +} + +int __cdecl _evil_sscanf (const APICHAR *buf, const APICHAR *format, ...) +{ + va_list argp; + int r; + + va_start (argp, format); + r = _evil_vsscanf (buf, format, argp); + va_end (argp); + + return r; +} + +int __cdecl _evil_vfscanf (FILE *s, const APICHAR *format, va_list argp) +{ + _IFP ifp; + + memset (&ifp, 0, sizeof (_IFP)); + ifp.fp = s; + + return _evil_sformat (&ifp, format, argp); +} + +int __cdecl _evil_vscanf (const APICHAR *format, va_list argp) +{ + return _evil_vfscanf (stdin, format, argp); +} + +int __cdecl _evil_vsscanf (const APICHAR *s, const APICHAR *format, va_list argp) +{ + _IFP ifp; + + memset (&ifp, 0, sizeof (_IFP)); + ifp.str = s; + ifp.is_string = 1; + + return _evil_sformat (&ifp, format, argp); +} + +#endif + +/**** asprintf family ****/ + +#ifndef __BUILD_WIDEAPI + +int _evil_asprintf(char ** __restrict__ ret, const char * __restrict__ format, ...) +{ + va_list ap; + int len; + va_start(ap,format); + /* Get Length */ + len = _evil_vsnprintf(NULL,0,format,ap); + if (len < 0) goto _end; + /* +1 for \0 terminator. */ + *ret = malloc(len + 1); + /* Check malloc fail*/ + if (!*ret) { + len = -1; + goto _end; + } + /* Write String */ + _evil_vsnprintf(*ret,len+1,format,ap); + /* Terminate explicitly */ + (*ret)[len] = '\0'; + _end: + va_end(ap); + return len; +} + +int __cdecl _evil_vasprintf(char ** __restrict__ ret, const char * __restrict__ format, va_list ap) +{ + int len; + /* Get Length */ + len = _evil_vsnprintf(NULL,0,format,ap); + if (len < 0) return -1; + /* +1 for \0 terminator. */ + *ret = malloc(len + 1); + /* Check malloc fail*/ + if (!*ret) return -1; + /* Write String */ + _evil_vsnprintf(*ret,len+1,format,ap); + /* Terminate explicitly */ + (*ret)[len] = '\0'; + return len; +} + +#endif diff --git a/src/lib/evil/evil_printw.c b/src/lib/evil/evil_printw.c new file mode 100644 index 0000000000..8e8fa182dd --- /dev/null +++ b/src/lib/evil/evil_printw.c @@ -0,0 +1,4 @@ + +#define __BUILD_WIDEAPI 1 + +#include "evil_printa.c" diff --git a/src/lib/evil/evil_private.h b/src/lib/evil/evil_private.h new file mode 100644 index 0000000000..d46a379d01 --- /dev/null +++ b/src/lib/evil/evil_private.h @@ -0,0 +1,20 @@ +#ifndef __EVIL_PRIVATE_H__ +#define __EVIL_PRIVATE_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +long _evil_systemtime_to_time(SYSTEMTIME st); + +void _evil_error_display(const char *fct, LONG res); + +void _evil_last_error_display(const char *fct); + +#ifdef __cplusplus +} +#endif + + +#endif /* __EVIL_PRIVATE_H__ */ diff --git a/src/lib/evil/evil_pwd.c b/src/lib/evil/evil_pwd.c new file mode 100644 index 0000000000..0830b5b26a --- /dev/null +++ b/src/lib/evil/evil_pwd.c @@ -0,0 +1,65 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <windows.h> +#include <security.h> + +#include "Evil.h" +#include "pwd.h" + + +static struct passwd pw; + +struct passwd * +getpwuid (uid_t uid) +{ + static char user_name[PATH_MAX]; + TCHAR name[PATH_MAX]; + ULONG length; + BOOLEAN res; +#ifdef UNICODE + char *a_name; +# endif /* UNICODE */ + + length = PATH_MAX; +#ifdef _WIN32_WINNT + res = GetUserNameEx(NameDisplay, name, &length); +#else + res = GetUserNameEx(NameWindowsCeLocal, name, &length); +#endif +#ifdef UNICODE + if (res) + { + a_name = evil_wchar_to_char(name); + if (a_name) + { + int l; + + l = strlen(a_name); + if (l >= PATH_MAX) + l = PATH_MAX; + memcpy(user_name, a_name, l); + user_name[l] = '\0'; + free(a_name); + } + else + res = 0; + } +#endif /* UNICODE */ + pw.pw_name = (res ? user_name : NULL); + pw.pw_passwd = NULL; + pw.pw_uid = uid; + pw.pw_gid = 0; + pw.pw_change = 0; + pw.pw_class = NULL; + pw.pw_gecos = (res ? user_name : NULL); + pw.pw_dir = (char *)evil_homedir_get(); + pw.pw_shell = getenv("SHELL"); + if (!pw.pw_shell) + pw.pw_shell = "sh"; + pw.pw_expire = 0; + pw.pw_fields = 0; + + return &pw; +} diff --git a/src/lib/evil/evil_stdio.c b/src/lib/evil/evil_stdio.c new file mode 100644 index 0000000000..594d281159 --- /dev/null +++ b/src/lib/evil/evil_stdio.c @@ -0,0 +1,215 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "Evil.h" +#include "evil_private.h" + +#undef fopen + + +#ifdef _WIN32_WCE + +/* + * Error related functions + * + */ + +void evil_perror (const char *s __UNUSED__) +{ + fprintf(stderr, "[Windows CE] error\n"); +} + +/* + * Stream related functions + * + */ + +FILE *evil_fopen(const char *path, const char *mode) +{ + FILE *f; + char *filename; + char *tmp; + + if (!path || !*path) + return NULL; + + if ((*path != '\\') && (*path != '/')) + { + char buf[PATH_MAX]; + int l1; + int l2; + + if (!evil_getcwd(buf, PATH_MAX)) + return NULL; + + l1 = strlen(buf); + l2 = strlen(path); + filename = (char *)malloc(l1 + 1 + l2 + 1); + if (!filename) + return NULL; + memcpy(filename, buf, l1); + filename[l1] = '\\'; + memcpy(filename + l1 + 1, path, l2); + filename[l1 + 1 + l2] = '\0'; + } + else + filename = _strdup(path); + + tmp = filename; + while (*tmp) + { + if (*tmp == '/') + *tmp = '\\'; + tmp++; + } + + printf ("fopen : %s\n", filename); + + f = fopen(filename, mode); + free(filename); + + return f; +} + +void evil_rewind(FILE *stream) +{ + fseek(stream, 0, SEEK_SET); +} + +int evil_remove(const char *path) +{ + struct stat st; + + if (stat(path, &st) < 0) return -1; + + if (S_ISDIR(st.st_mode)) + { + if (rmdir(path) < 0) return -1; + return 0; + } + + if (S_ISREG(st.st_mode)) + { + if (unlink(path) < 0) return -1; + return 0; + } + + return -1; +} + + +#endif /* _WIN32_WCE */ + + +#ifdef _WIN32_WCE + +FILE *evil_fopen_native(const char *path, const char *mode) +{ + HANDLE handle; + char *filename; + char *tmp; + wchar_t *wfilename; + DWORD acs = GENERIC_READ; + DWORD creation; + + if (!path || !*path || !mode || !*mode) + return NULL; + + if ((*path != '\\') && (*path != '/')) + { + char buf[PATH_MAX]; + int l1; + int l2; + + if (!evil_getcwd(buf, PATH_MAX)) + return NULL; + + l1 = strlen(buf); + l2 = strlen(path); + filename = (char *)malloc(l1 + 1 + l2 + 1); + if (!filename) + return NULL; + memcpy(filename, buf, l1); + filename[l1] = '\\'; + memcpy(filename + l1 + 1, path, l2); + filename[l1 + 1 + l2] = '\0'; + } + else + filename = _strdup(path); + + tmp = filename; + while (*tmp) + { + if (*tmp == '/') + *tmp = '\\'; + tmp++; + } + printf ("fopen native : %s\n", filename); + + wfilename = evil_char_to_wchar(filename); + free(filename); + + if (!wfilename) + return NULL; + + if (*mode == 'r') + { + acs = GENERIC_READ; + creation = OPEN_EXISTING; + } + if (*mode == 'w') + { + acs = GENERIC_WRITE; + creation = CREATE_ALWAYS; + } + + handle = CreateFile(wfilename, + acs, + 0, NULL, + creation, + FILE_ATTRIBUTE_NORMAL, + NULL); + free(wfilename); + + if (handle == INVALID_HANDLE_VALUE) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + return (FILE *)handle; +} + +size_t evil_fread_native(void* buffer, size_t size, size_t count, FILE* stream) +{ + HANDLE handle; + DWORD bytes_read; + BOOL res; + + if ((size == 0) || (count == 0)) + return 0; + + handle = (HANDLE)stream; + res = ReadFile(handle, buffer, size * count, &bytes_read, NULL); + if (!res) + { + _evil_last_error_display(__FUNCTION__); + return 0; + } + + return (bytes_read != size * count) ? 0 : bytes_read; +} + +int evil_fclose_native(FILE *stream) +{ + if (!CloseHandle((HANDLE)stream)) + { + _evil_last_error_display(__FUNCTION__); + return -1; + } + + return 0; +} + +#endif /* _WIN32_WCE */ diff --git a/src/lib/evil/evil_stdio.h b/src/lib/evil/evil_stdio.h new file mode 100644 index 0000000000..533d31e4fe --- /dev/null +++ b/src/lib/evil/evil_stdio.h @@ -0,0 +1,196 @@ +#ifndef __EVIL_STDIO_H__ +#define __EVIL_STDIO_H__ + + +/** + * @file evil_stdio.h + * @brief The file that provides functions ported from Unix in stdio.h. + * @defgroup Evil_Stdio_Group Stdio.h functions + * + * This header provides functions ported from Unix in stdio.h. + * + * @{ + */ + + +#ifdef _WIN32_WCE + +/* + * Error related functions + * + */ + +/** + * @brief Print the given string to stderr. + * + * @param s The string to print. + * + * This function just printf the string @p s to stderr. + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI void evil_perror (const char *s); + +/** + * @def perror(s) + * + * Wrapper around evil_perror(). + */ +# define perror(s) evil_perror(s) + +/* + * Stream related functions + * + */ + +/** + * @brief Emulate the fopen() function on Windows CE. + * + * @param path The file to open. + * @param mode The mode. + * @return A FILE pointer on success, @c NULL otherwise. + * + * This function emulates the fopen() function on Windows CE using the + * Windows libc. The main problem is that the filesytem on Windows CE + * is like on Sys V : the top level directory is beginning by '/' or + * '\' and the full path must be calculated.. This function takes care + * of this feature, and replace all the '/' by '/'. Otherwise, it + * calls the Windows CE fopen() function once the filename has been + * correctly set. If @p path is @c NULL or empty, this function + * returns @c NULL. On success, this function returns a FILE stream, + * @c NULL otherwise. + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI FILE *evil_fopen(const char *path, const char *mode); + +/** + * @def fopen(path, mode) + * + * Wrapper around evil_fopen(). + */ +# define fopen(path, mode) evil_fopen(path, mode) + +/** + * @brief Emulate the rewind() function on Windows CE. + * + * @param stream The FILE stream. + * + * This function emulates the rewind() function on Windows CE by just + * calling fseek(). + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI void evil_rewind(FILE *stream); + +/** + * @def rewind(f) + * + * Wrapper around evil_rewind(). + */ +# define rewind(f) evil_rewind(f) + +/** + * @brief Emulate the remove() function on Windows CE. + * + * @param path The path to remove. + * @return 0 on success, -1 otherwise. + * + * This function emulates the remove() function on Windows CE. If + * @p path is an empty directory, it removes it. If @p path is a file, + * it deletes it. On success, 0 is returns, -1 otherwise. + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI int evil_remove(const char *path); + +/** + * @def remove(p) + * + * Wrapper around evil_remove(). + */ +# define remove(p) evil_remove(p) + +#endif /* _WIN32_WCE */ + + +#ifdef _WIN32_WCE + +/** + * @brief Emulate the fopen() function on Windows CE using Windows API. + * + * @param path The file to open. + * @param mode The mode. + * @return A FILE pointer on success, @c NULL otherwise. + * + * This function emulates the fopen() function on Windows CE using the + * Windows API and not the libc. It is similar to evil_fopen(). + * + * @see evil_fopen() + * @see evil_fread_native() + * @see evil_fclose_native() + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI FILE *evil_fopen_native(const char *path, const char *mode); + +/** + * @brief Read the given stream on Windows CE using Windows API. + * + * @param buffer The buffer to store the data. + * @param size The size of each element of data to read. + * @param count The number of elements of data to read. + * @param stream The stream to read. + * @return The size read on success, 0 otherwise. + * + * This function read @p stream using the Windows API. It reads + * @p size elements of data, each @ size bytes long. The data is + * stored in the buffer @p buffer. On success, the size read is + * returned, 0 otherwise. + * + * @see evil_fopen_native() + * @see evil_fclose_native() + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI size_t evil_fread_native(void* buffer, size_t size, size_t count, FILE* stream); + +/** + * @brief Close the given stream on Windows CE using Windows API. + * + * @param stream The stream to close. + * @return 0 on success, -1 otherwise. + * + * This function closes @p stream using the Windows API. On success, 0 + * is returned, -1 otherwise. + * + * @see evil_fopen_native() + * @see evil_fread_native() + * + * Conformity: None. + * + * Supported OS: Windows CE. + */ +EAPI int evil_fclose_native(FILE *stream); + +#endif /* _WIN32_WCE */ + + +/** + * @} + */ + + +#endif /* __EVIL_STDIO_H__ */ diff --git a/src/lib/evil/evil_stdlib.c b/src/lib/evil/evil_stdlib.c new file mode 100644 index 0000000000..d22a7dc71e --- /dev/null +++ b/src/lib/evil/evil_stdlib.c @@ -0,0 +1,453 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> +#include <io.h> +#include <fcntl.h> +#include <sys/stat.h> + +#ifdef HAVE_ERRNO_H +# include <errno.h> +#endif /* HAVE_ERRNO_H */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include "evil_macro.h" +#include "evil_stdlib.h" +#include "evil_private.h" +#define APICHAR char +#include "evil_print.h" + +/* + * Environment variable related functions + * + * char *getenv (const char *name); + * int putenv (const char *string); + * int setenv (const char *name, const char *value, int overwrite); + * void unsetenv (const char *name); + * + */ + +#ifdef _WIN32_WCE + +static char _evil_stdlib_getenv_buffer[PATH_MAX]; + +char * +getenv(const char *name) +{ + HKEY key; + wchar_t *wname; + LONG res; + DWORD type; + DWORD disposition; + DWORD size = PATH_MAX; + + if (!name || !*name) + return NULL; + + if ((res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, + TEXT("Software\\Efl\\Environment"), + 0, NULL, + REG_OPTION_VOLATILE, + 0, NULL, + &key, &disposition)) != ERROR_SUCCESS) + { + _evil_error_display(__FUNCTION__, res); + return NULL; + } + + wname = evil_char_to_wchar(name); + if (!wname) + { + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + _evil_error_display(__FUNCTION__, res); + return NULL; + } + + if ((res = RegQueryValueEx(key, wname, + NULL, &type, + (LPBYTE)&_evil_stdlib_getenv_buffer, + &size)) != ERROR_SUCCESS) + { + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + _evil_error_display(__FUNCTION__, res); + free(wname); + return NULL; + } + + free(wname); + + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + { + _evil_error_display(__FUNCTION__, res); + return NULL; + } + + if (_evil_stdlib_getenv_buffer[0] == '\0') + return NULL; + else + { + return _evil_stdlib_getenv_buffer; + } +} + +#endif /* _WIN32_WCE */ + +#ifdef __MINGW32CE__ + +int +putenv(const char *string) +{ + char *str; + char *egal; + char *name; + char *value; + + str = strdup(string); + if (!str) + return -1; + egal = strchr(str, '='); + if (!egal) + return -1; + + value = egal + 1; + *egal = '\0'; + name = str; + setenv(name, value, 1); + free(str); + + return 0; +} + +#endif /* __MINGW32CE__ */ + + + +int +setenv(const char *name, + const char *value, + int overwrite) +{ +#ifndef __MINGW32CE__ + + char *old_name; + char *str; + size_t length; + int res; + + if (!name || !*name) + return -1; + + /* if '=' is found, return EINVAL */ + if (strchr (name, '=')) + { +#ifdef HAVE_ERRNO_H + errno = EINVAL; +#endif /* HAVE_ERRNO_H */ + return -1; + } + + /* if name is already set and overwrite is 0, we exit with success */ + old_name = getenv(name); + if (!overwrite && old_name) + return 0; + + length = value ? strlen(value) : 0; + length += strlen(name) + 2; + str = (char *)malloc(length); + if (!str) + { +#ifdef HAVE_ERRNO_H + errno = ENOMEM; +#endif /* HAVE_ERRNO_H */ + return -1; + } + if (!value) + sprintf(str, "%s=", name); + else + sprintf(str, "%s=%s", name, value); + res = _putenv(str); + free(str); + + return res; + +#else /* __MINGW32CE__ */ + + HKEY key; + LONG res; + DWORD disposition; + wchar_t *wname; + char *data; + DWORD size; + + if (!name || !*name) + return -1; + + /* if '=' is found, return an error */ + if (strchr (name, '=')) + return -1; + + if ((res = RegCreateKeyEx(HKEY_LOCAL_MACHINE, + TEXT("Software\\Efl\\Environment"), + 0, NULL, + REG_OPTION_VOLATILE, + 0, NULL, + &key, + &disposition)) != ERROR_SUCCESS) + { + _evil_error_display(__FUNCTION__, res); + return -1; + } + + /* if name is already set and overwrite is 0, we exit with success */ + if (!overwrite && (disposition == REG_OPENED_EXISTING_KEY)) + return 0; + + wname = evil_char_to_wchar(name); + if (!wname) + { + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + _evil_error_display(__FUNCTION__, res); + return -1; + } + + if (value) + { + size = strlen(value); + data = malloc(sizeof(char) * (size + 1)); + if (!data) + return -1; + memcpy((void *)data, value, size); + data[size] = '\0'; + } + else + { + size = 0; + data = malloc(sizeof(char)); + if (!data) + return -1; + data[0] = '\0'; + } + if (!data) + return -1; + + if ((res = RegSetValueEx(key, + (LPCWSTR)wname, + 0, REG_SZ, + (const BYTE *)data, + size + 1)) != ERROR_SUCCESS) + { + free(wname); + _evil_error_display(__FUNCTION__, res); + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + _evil_error_display(__FUNCTION__, res); + return -1; + } + + free(data); + free(wname); + + if ((res = RegCloseKey (key)) != ERROR_SUCCESS) + { + _evil_error_display(__FUNCTION__, res); + return -1; + } + + return 0; + +#endif /* ! __MINGW32CE__ */ +} + +int +unsetenv(const char *name) +{ + return setenv(name, NULL, 1); +} + + +/* + * Files related functions + * + */ + +int +mkstemp(char *__template) +{ + const char lookup[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + char *suffix; + DWORD val; + size_t length; + int i; + + if (!__template) + return 0; + + length = strlen(__template); + if ((length < 6) || + (strncmp (__template + length - 6, "XXXXXX", 6))) + { +#ifdef HAVE_ERRNO_H + errno = EINVAL; +#endif /* HAVE_ERRNO_H */ + return -1; + } + + suffix = __template + length - 6; + + val = GetTickCount(); + val += GetCurrentProcessId(); + + for (i = 0; i < 32768; i++) + { + DWORD v; + int fd; + + v = val; + + suffix[0] = lookup[v % 62]; + v /= 62; + suffix[1] = lookup[v % 62]; + v /= 62; + suffix[2] = lookup[v % 62]; + v /= 62; + suffix[3] = lookup[v % 62]; + v /= 62; + suffix[4] = lookup[v % 62]; + v /= 62; + suffix[5] = lookup[v % 62]; + v /= 62; + +#ifndef __MINGW32CE__ + fd = _open(__template, _O_RDWR | _O_BINARY | _O_CREAT | _O_EXCL, _S_IREAD | _S_IWRITE); +#else /* ! __MINGW32CE__ */ + { + FILE *f; + wchar_t *wtemplate; + + wtemplate = evil_char_to_wchar(__template); + if (!wtemplate) + return -1; + f = _wfopen(wtemplate, L"rwb"); + free(wtemplate); + if (!f) + { +# ifdef HAVE_ERRNO_H + errno = EEXIST; +# endif /* HAVE_ERRNO_H */ + return -1; + } + fd = (int)_fileno(f); + } +#endif /* __MINGW32CE__ */ + if (fd >= 0) + return fd; + + val += 7777; + } + +#ifdef HAVE_ERRNO_H + errno = EEXIST; +#endif /* HAVE_ERRNO_H */ + return -1; +} + + +char * +realpath(const char *file_name, char *resolved_name) +{ +#ifndef __MINGW32CE__ + char *retname = NULL; /* we will return this, if we fail */ + + /* SUSv3 says we must set `errno = EINVAL', and return NULL, + * if `name' is passed as a NULL pointer. + */ + + if (file_name == NULL) + errno = EINVAL; + + /* Otherwise, `name' must refer to a readable filesystem object, + * if we are going to resolve its absolute path name. + */ + + else if (access(file_name, 4) == 0) + { + /* If `name' didn't point to an existing entity, + * then we don't get to here; we simply fall past this block, + * returning NULL, with `errno' appropriately set by `access'. + * + * When we _do_ get to here, then we can use `_fullpath' to + * resolve the full path for `name' into `resolved', but first, + * check that we have a suitable buffer, in which to return it. + */ + + if ((retname = resolved_name) == NULL) + { + /* Caller didn't give us a buffer, so we'll exercise the + * option granted by SUSv3, and allocate one. + * + * `_fullpath' would do this for us, but it uses `malloc', and + * Microsoft's implementation doesn't set `errno' on failure. + * If we don't do this explicitly ourselves, then we will not + * know if `_fullpath' fails on `malloc' failure, or for some + * other reason, and we want to set `errno = ENOMEM' for the + * `malloc' failure case. + */ + + retname = malloc(_MAX_PATH); + } + + /* By now, we should have a valid buffer. + * If we don't, then we know that `malloc' failed, + * so we can set `errno = ENOMEM' appropriately. + */ + + if (retname == NULL) + errno = ENOMEM; + + /* Otherwise, when we do have a valid buffer, + * `_fullpath' should only fail if the path name is too long. + */ + + else if ((retname = _fullpath(retname, file_name, _MAX_PATH)) == NULL) + errno = ENAMETOOLONG; + } + + /* By the time we get to here, + * `retname' either points to the required resolved path name, + * or it is NULL, with `errno' set appropriately, either of which + * is our required return condition. + */ + + return retname; +#else + char cwd[PATH_MAX]; + size_t l1; + size_t l2; + size_t l; + + if (!file_name || !resolved_name) + return NULL; + + if (!getcwd(cwd, PATH_MAX)) + return NULL; + + l1 = strlen(cwd); + l2 = strlen(file_name); + l = l1 + l2 + 2; + + if (l > PATH_MAX) + l = PATH_MAX - 1; + memcpy(resolved_name, cwd, l1); + resolved_name[l1] = '\\'; + memcpy(resolved_name + l1 + 1, file_name, l2); + resolved_name[l] = '\0'; + + return resolved_name; +#endif /* __MINGW32CE__ */ +} diff --git a/src/lib/evil/evil_stdlib.h b/src/lib/evil/evil_stdlib.h new file mode 100644 index 0000000000..81d564f86f --- /dev/null +++ b/src/lib/evil/evil_stdlib.h @@ -0,0 +1,199 @@ +#ifndef __EVIL_STDLIB_H__ +#define __EVIL_STDLIB_H__ + + +/** + * @file evil_stdlib.h + * @brief The file that provides functions ported from Unix in stdlib.h. + * @defgroup Evil_Stdlib_Group Stdlib.h functions. + * + * This header provides functions ported from Unix in stdlib.h. + * + * @{ + */ + + +/* + * Environment variable related functions + * + */ + +#ifdef _WIN32_WCE + +/** + * @brief Retrieve the value of environment variables. + * + * @param name The name of the environment variable. + * @return The value of the environment variable. + * + * This function searches the environment variable @p name if it + * exists and return a pointer to the value of the environment + * variable. If the specified environment variable cannot be found, + * @c NULL is returned. + * + * The returned value may be overwritten by a subsequent call to + * getenv(), setenv(), or unsetenv(). + * + * Conformity: Non applicable. + * + * Supported OS: Windows CE. + * + * @note On Windows CE, there is no environment variable. This is + * faked by storing a value in a key in the base registry. + */ +EAPI char *getenv(const char *name); + +#endif /* _WIN32_WCE */ + + +#ifdef __MINGW32CE__ + +/** + * @brief Set the value of environment variables. + * + * @param string A formatted string. + * @return 0 in success, non-zero otherwise. + * + * This function uses @p string to set environment variable values. + * @p string should point to a string of the form "name=value". This + * function makes the value of the environment variable name equal to + * value by altering an existing variable or creating a new one. In + * either case, the string pointed to by @p string becomes part of the + * environment, so altering @p string shall change the environment. + * + * Conformity: Non applicable. + * + * Supported OS: Windows CE. + * + * @note On Windows CE, there is no environment variable. This is + * faked by storing a value in a key in the base registry. + */ +EAPI int putenv(const char *string); + +#endif /* __MINGW32CE__ */ + +/** + * @brief Create, modify, or remove environment variables. + * + * @param name The name of the environment variable. + * @param value The value of the environment variable to set. + * @param overwrite 0 to let the environment variable unchanged, 1 otherwise. + * @return 0 on success, -1 otherwise. + * + * Add the new environment variable @p name or modify its value if it + * exists, and set it to @p value. Environment variables define the + * environment in which a process executes. If @p value is @c NULL, the + * variable is removed (unset) and that call is equivalent to + * unsetenv().If the environment variable named by @p name already + * exists and the value of @p overwrite is 0, the function shall + * return success and the environment shall remain unchanged. + * If the function succeeds, it returns 0, otherwise it returns -1. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE. + * + * @note On Windows CE, there is no environment variable. This is + * faked by storing a value in a key in the base registry. + */ +EAPI int setenv(const char *name, + const char *value, + int overwrite); + +/** + * @brief Remove environment variables. + * + * @param name The name of the environment variable. + * @return 0 on success, -1 otherwise. + * + * Remove the new environment variable @p name if it exists. That + * function is equivalent to setenv() with its second parameter to + * @c NULL and the third to 1. If the function succeeds, it returns 0, + * otherwise it returns -1. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE (not cegcc). + * + * @note On Windows CE, there is no environment variable. This is + * faked by storing a value in a key in the base registry. + */ +EAPI int unsetenv(const char *name); + + +/* + * Files related functions + * + */ + +/** + * @brief Make temporay unique file name. + * + * @param __template Template of the file to create. + * @return A file descriptor on success, -1 otherwise. + * + * Take the given file name @p template and overwrite a portion of it + * to create a file name. This file is guaranted not to exist at the + * time invocation and is suitable for use by the function. + * + * The @p template parameter can be any file name with some number of + * 'Xs' appended to it, for example @em baseXXXXXX, where @em base is + * the part of the new file that you supply and eacg 'X' is a placeholder + * for a character supplied by mkstemp(). The trailing 'Xs' are replaced + * with a five-digit value; this value is a unique number. Each successful + * call to mkstemp() modifes @p template. + * + * When mkstemp() succeeds, it creates and opens the template file for + * reading and writing. + * + * On success, the function returns the file descriptor of the + * temporary file. Otherwise, it returns -1 and errno is set to the + * following values: + * - EINVAL: @p template has an invalid format. + * - EEXISTS: File name already exists. + * + * Conformity: Should follow BSD conformity. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int mkstemp(char *__template); + +/** + * @brief Return an absolute or full path name for a specified relative path name. + * + * @param file_name The absolute path name. + * @param resolved_name The relative path name. + * @return @c NULL on failure, a pointer to the absolute path name otherwise. + * + * The function expands the relative path name @p file_name to its + * fully qualified or absolute path and store it in the buffer pointed + * by @p resolved_name. The buffer is at most @c PATH_MAX bytes long. + * If @p resolved_name is @c NULL, malloc() is used to allocate a + * buffer of sufficient length to hold the path name. In that case, it + * is the responsability of the caller to free this buffer with free(). + * + * That function can be used to obtain the absolute path name for + * relative paths (relPath) that include "./" or "../" in their names. + * + * On Windows XP, errno is set in the following cases: + * + * @li EACCESS: if @p file_name can not be accessed. + * @li EINVAL: if @p file_name is @c NULL. + * @li ENAMETOOLONG: if the path name is too long. + * @li ENOENT: @p file_name does not exist + * @li ENOMEM: if memory allocation fails. + * + * Conformity: None. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI char *realpath(const char *file_name, char *resolved_name); + + +/** + * @} + */ + + +#endif /* __EVIL_STDLIB_H__ */ + diff --git a/src/lib/evil/evil_string.c b/src/lib/evil/evil_string.c new file mode 100644 index 0000000000..ffbe308b67 --- /dev/null +++ b/src/lib/evil/evil_string.c @@ -0,0 +1,128 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "Evil.h" + + +#ifdef _WIN32_WCE + +/* + * Error related functions + * + */ + +char *strerror (int errnum __UNUSED__) +{ + return "[Windows CE] error\n"; +} + +#endif /* _WIN32_WCE */ + + +/* + * bit related functions + * + */ + +int ffs(int i) +{ + int size; + int x; + + if (!i) return 1; + + /* remove the sign bit */ + x = i & -i; + size = sizeof(int) << 3; + for (i = size; i > 0; --i, x <<= 1) + if (x & (1 << (size - 1))) return i; + + return x; +} + + +#ifdef _WIN32_WCE + +/* + * String manipulation related functions + * + */ + +int +strcoll (const char *s1, const char *s2) +{ +#ifdef UNICODE + wchar_t *ws1; + wchar_t *ws2; + int res; + + ws1 = evil_char_to_wchar(s1); + ws2 = evil_char_to_wchar(s2); + res = wcscmp(ws1, ws2); + if (ws1) free(ws1); + if (ws2) free(ws2); + + return res; +#else + return strcmp(s1, s2); +#endif /* ! UNICODE */ +} + + +#endif /* _WIN32_WCE */ + +char * +strrstr (const char *str, const char *substr) +{ + char *it; + char *ret = NULL; + + while ((it = strstr(str, substr))) + ret = it; + + return ret; +} + +#ifdef _MSC_VER + +int strcasecmp(const char *s1, const char *s2) +{ + return lstrcmpi(s1, s2); +} + +#endif /* _MSC_VER */ + +char *strcasestr(const char *haystack, const char *needle) +{ + size_t length_needle; + size_t length_haystack; + size_t i; + + if (!haystack || !needle) + return NULL; + + length_needle = strlen(needle); + length_haystack = strlen(haystack) - length_needle + 1; + + for (i = 0; i < length_haystack; i++) + { + size_t j; + + for (j = 0; j < length_needle; j++) + { + unsigned char c1; + unsigned char c2; + + c1 = haystack[i+j]; + c2 = needle[j]; + if (toupper(c1) != toupper(c2)) + goto next; + } + return (char *) haystack + i; + next: + ; + } + + return NULL; +} diff --git a/src/lib/evil/evil_string.h b/src/lib/evil/evil_string.h new file mode 100644 index 0000000000..9141eba762 --- /dev/null +++ b/src/lib/evil/evil_string.h @@ -0,0 +1,152 @@ +#ifndef __EVIL_STRING_H__ +#define __EVIL_STRING_H__ + + +/** + * @file evil_string.h + * @brief The file that provides functions ported from Unix in string.h. + * @defgroup Evil_String_Group String.h functions. + * + * This header provides functions ported from Unix in string.h. + * + * @{ + */ + + +#ifdef _WIN32_WCE + +/* + * Environment variable related functions + * + */ + +/** + * @brief Return the static string "[Windows CE] error\n". + * + * @param errnum Unused parameter. + * @return The static string "[Windows CE] error\n". + * + * This function just returns the static string "[Windows CE] + * error\n". + * + * Conformity: Non applicable. + * + * Supported OS: Windows CE (not cegcc). + */ +EAPI char *strerror (int errnum); + +#endif /* _WIN32_WCE */ + +/* + * bit related functions + * + */ + +/** + * @brief Return the position of the first (least significant) bit set in a word + * + * @param i Word to take the first bit. + * @return The position of the first bit set, or 0 if no bits are set. + * + * This function returns the position of the first (least significant) + * bit set in @p i. The least significant bit is position 1 and the + * most significant position e.g. 32 or 64. The function returns 0 if + * no bits are set in @p i, or the position of the first bit set + * otherwise. + * + * Conformity: BSD + * + * Supported OS: Windows XP, Windows CE (not cegcc). + */ +EAPI int ffs(int i); + + +#ifdef _WIN32_WCE + +/* + * String manipulation related functions + * + */ + +/** + * @brief Compare two strings. + * + * @param s1 The first string to compare. + * @param s2 The second string to compare. + * @return < 0 if s1 < s2, >0 if s1 > s2, 0 otherwise. + * + * This function is exactly the same as strcmp(). No possible way to + * achieve the behavior of strcoll() on Windows CE. + * + * Conformity: Non applicable. + * + * Supported OS: Windows CE. + */ +EAPI int strcoll (const char *s1, const char *s2); + +#endif /* _WIN32_WCE */ + +/** + * @brief Get the last substring occurence. + * + * @param str The string to search from. + * @param substr The substring to search. + * @return The last occurrence of the substring if found, @c NULL otherwise. + * + * This function retrieves the last occurrence of @p substring in the + * string @p str. If @p str or @p substr are @c NULL, of if @p substr + * is not found in @p str, @c NULL is returned. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI char *strrstr (const char *str, const char *substr); + +#ifdef _MSC_VER + +/** + * @brief Compare two string, ignoring case. + * + * @param s1 The first string to compare. + * @param s2 The first string to compare. + * @return + * + * This function compares the two strings @p s1 and @p s2, ignoring + * the case of the characters. It returns an integer less than, equal + * to, or greater than zero if s1 is found, respectively, to be less + * than, to match, or be greater than s2. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP (vc++ only) + */ +EAPI int strcasecmp(const char *s1, const char *s2); + +#endif /* _MSC_VER */ + +/** + * @brief Locatea substring into a string, ignoring case. + * + * @param haystack The string to search in. + * @param needle The substring to find. + * @return + * + * This function locates the string @p needle into the string @p haystack, + * ignoring the case of the characters. It returns apointer to the + * beginning of the substring, or NULL if the substring is not found. + * If @p haystack or @p needle are @c NULL, this function returns @c NULL. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE + */ +EAPI char *strcasestr(const char *haystack, const char *needle); + + +/** + * @} + */ + + +#endif /* __EVIL_STRING_H__ */ diff --git a/src/lib/evil/evil_time.c b/src/lib/evil/evil_time.c new file mode 100644 index 0000000000..bcff29eb05 --- /dev/null +++ b/src/lib/evil/evil_time.c @@ -0,0 +1,45 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include "Evil.h" +#include "evil_private.h" + +#ifndef localtime_r + +struct tm * +localtime_r(const time_t *timep, struct tm *result) +{ +# ifndef _MSC_VER + struct tm *tmp; +# endif /* ! _MSC_VER */ + + if (!timep || !result) + return NULL; + +# ifdef _MSC_VER + if (localtime_s(result, timep) != 0) + return NULL; +# else + tmp = localtime(timep); + if (!tmp) + return NULL; + + memcpy(result, tmp, sizeof(struct tm)); + +# endif /* ! _MSC_VER */ + + return result; +} + +#endif /* localtime_r */ + +#ifdef UNDER_CE + +void +tzset(void) +{ + /* does nothing... */ +} + +#endif /* UNDER_CE */ diff --git a/src/lib/evil/evil_time.h b/src/lib/evil/evil_time.h new file mode 100644 index 0000000000..13fc909024 --- /dev/null +++ b/src/lib/evil/evil_time.h @@ -0,0 +1,61 @@ +#ifndef __EVIL_TIME_H__ +#define __EVIL_TIME_H__ + + +/** + * @file evil_time.h + * @brief The file that provides functions ported from Unix in time.h. + * @defgroup Evil_Time_Group Time.h functions + * + * This header provides functions ported from Unix in time.h. + * + * @{ + */ + + +#ifndef localtime_r + +/** + * @brief Convert the calendar time to broken-time representation in a + * user supplied data. + * + * @param timep The calender time. + * @param result The broken-down time representation. + * @return The broken-down time representation. + * + * This function converts the calendar time @p timep to a broken-time + * representation. The result is stored in the buffer @p result + * supplied by the user. If @p timep or @p result are @c NULL, or if + * an error occurred, this function returns @c NULL and the values in + * @p result might be undefined. Otherwise it returns @p result. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP. + */ +EAPI struct tm *localtime_r(const time_t *timep, struct tm *result); + +#endif /* localtime_r */ + +#ifdef UNDER_CE + +/** + * @brief Stub implementation of tzset(). + * + * This function does nothing. + * + * Conformity: Non applicable. + * + * Supported OS: Windows CE. + */ +EAPI void tzset(void); + +#endif + + +/** + * @} + */ + + +#endif /* __EVIL_TIME_H__ */ diff --git a/src/lib/evil/evil_unistd.c b/src/lib/evil/evil_unistd.c new file mode 100644 index 0000000000..e1498509df --- /dev/null +++ b/src/lib/evil/evil_unistd.c @@ -0,0 +1,431 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif /* HAVE_ERRNO_H */ + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <winsock2.h> +#undef WIN32_LEAN_AND_MEAN + +# include <sys/time.h> + +#ifdef _MSC_VER +# include <direct.h> /* for _getcwd */ +#endif + +#include "Evil.h" +#include "evil_private.h" + + +LONGLONG _evil_time_freq; +LONGLONG _evil_time_count; +long _evil_time_second; + + +long +_evil_systemtime_to_time(SYSTEMTIME st) +{ + int days[] = { + -1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364 + }; + int day; + time_t t; + + st.wYear -= 1900; + if ((st.wYear < 70) || (st.wYear > 138)) + return -1; + + day = st.wDay + days[st.wMonth - 1]; + + if (!(st.wYear & 3) && (st.wMonth > 2) ) + day++; + + t = ((st.wYear - 70) * 365 + ((st.wYear - 1) >> 2) - 17 + day) * 24 + st.wHour; + t = (t * 60 + st.wMinute) * 60 + st.wSecond; + + return (long)t; +} + +/* + * Time related functions + * + */ + +double +evil_time_get(void) +{ + LARGE_INTEGER count; + + QueryPerformanceCounter(&count); + + return (double)_evil_time_second + (double)(count.QuadPart - _evil_time_count)/ (double)_evil_time_freq; +} + +#ifdef _MSC_VER +int +evil_gettimeofday(struct timeval *tp, void *tzp __UNUSED__) +{ + LARGE_INTEGER count; + LONGLONG diff; + + QueryPerformanceCounter(&count); + diff = count.QuadPart - _evil_time_count; + tp->tv_sec = _evil_time_second + (long)(diff / _evil_time_freq); + tp->tv_usec = (long)(((diff % _evil_time_freq) * 1000000ll) / _evil_time_freq); + + return 1; +} + +int +evil_usleep(unsigned long usec) +{ + Sleep(usec / 1000); + return 0; +} +#endif + + +/* + * Process identifer related functions + * + */ + +#if defined (_MSC_VER) || defined (_WIN32_WCE) +pid_t +getpid(void) +{ + return (pid_t)GetCurrentProcessId(); +} +#endif + +/* + * File related functions + * + */ + +char * +evil_getcwd(char *buffer, size_t size) +{ +#ifdef _WIN32_WCE + wchar_t wpath[PATH_MAX]; + char *cpath; + char *delim; + DWORD ret = 0; + + if (size <= 0) + return NULL; + + ret = GetModuleFileName(GetModuleHandle(NULL), (LPWSTR)&wpath, PATH_MAX); + + if (!ret) + { + _evil_error_display(__FUNCTION__, ret); + return NULL; + } + + cpath = evil_wchar_to_char(wpath); + if (!cpath) + return NULL; + + if (strlen(cpath) >= (size - 1)) + { + free(cpath); + return NULL; + } + + delim = strrchr(cpath, '\\'); + if (delim) + *delim = '\0'; + + if (!buffer) + { + buffer = (char *)malloc(sizeof(char) * size); + if (!buffer) + { + free(cpath); + return NULL; + } + } + + strcpy(buffer, cpath); + free(cpath); + + return buffer; +#else + return _getcwd(buffer, (int)size); +#endif /* ! _WIN32_WCE */ +} + +#ifdef _WIN32_WCE + +int +evil_stat(const char *file_name, struct stat *st) +{ + SYSTEMTIME system_time; + FILETIME local_time; + WIN32_FIND_DATA data; + HANDLE handle; + char *f; + char *tmp; + wchar_t *file; + int permission = 0; + + if (!file_name || !*file_name) + return -1; + + f = strdup(file_name); + if (!f) + return -1; + + tmp = f; + while (*tmp) + { + if (*tmp == '/') *tmp = '\\'; + tmp++; + } + + if (!strcmp(file_name, "\\")) + { + st->st_size = 1024; + st->st_mode = S_IFDIR; + permission = S_IREAD|S_IWRITE|S_IEXEC; + + st->st_mode |= permission | (permission >> 3) | (permission >> 6); + return 0; + } + + if (*f != '\\') + { + char buf[PATH_MAX]; + int l1; + int l2; + + evil_getcwd(buf, PATH_MAX); + l1 = strlen(buf); + l2 = strlen(file_name); + tmp = (char *)malloc(l1 + 1 + l2 + 1); + if (!tmp) + return -1; + memcpy(tmp, buf, l1); + tmp[l1] = '\\'; + memcpy(tmp + l1 + 1, file_name, l2); + tmp[l1 + 1 + l2] = '\0'; + file = evil_char_to_wchar(tmp); + free(tmp); + if (!file) + return -1; + } + else + { + file = evil_char_to_wchar(f); + if (!file) + return -1; + } + + free(f); + + handle = FindFirstFile(file, &data); + if (handle == INVALID_HANDLE_VALUE) + { + free(file); + return -1; + } + + free(file); + + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + st->st_size = 1024; + st->st_mode = S_IFDIR; + st->st_nlink = 2; + } + else + { + st->st_size = data.nFileSizeLow; + st->st_mode = S_IFREG; + st->st_nlink = 1; + } + + permission |= S_IREAD; + + if (!(data.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) + permission |= S_IWRITE; + + if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + permission |= S_IEXEC; + + st->st_mode |= permission | (permission >> 3) | (permission >> 6); + + FileTimeToLocalFileTime(&data.ftLastWriteTime, &local_time); + FileTimeToSystemTime(&local_time, &system_time); + + st->st_mtime = _evil_systemtime_to_time(system_time); + + FileTimeToLocalFileTime(&data.ftCreationTime, &local_time); + FileTimeToSystemTime(&local_time, &system_time); + + st->st_ctime = _evil_systemtime_to_time(system_time); + + FileTimeToLocalFileTime(&data.ftLastAccessTime, &local_time); + FileTimeToSystemTime(&local_time, &system_time); + + st->st_atime = _evil_systemtime_to_time(system_time); + + if(st->st_atime == 0) + st->st_atime = st->st_mtime; + if (st->st_ctime == 0) + st->st_ctime = st->st_mtime; + + st->st_rdev = 1; + st->st_ino = 0; + + FindClose(handle); + + return 0; +} + +#endif /* _WIN32_WCE */ + + + +/* + * Sockets and pipe related functions + * + */ + +int +evil_sockets_init(void) +{ + WSADATA wsa_data; + + return (WSAStartup(MAKEWORD(2, 2), &wsa_data) == 0) ? 1 : 0; +} + +void +evil_sockets_shutdown(void) +{ + WSACleanup(); +} + +/* + * The code of the following functions has been kindly offered + * by Tor Lillqvist. + */ +int +evil_pipe(int *fds) +{ + struct sockaddr_in saddr; + SOCKET temp; + SOCKET socket1 = INVALID_SOCKET; + SOCKET socket2 = INVALID_SOCKET; + u_long arg; + fd_set read_set; + fd_set write_set; + int len; + + temp = socket (AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) + goto out0; + + arg = 1; + if (ioctlsocket (temp, FIONBIO, &arg) == SOCKET_ERROR) + goto out0; + + memset (&saddr, 0, sizeof (saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + if (bind (temp, (struct sockaddr *)&saddr, sizeof (saddr))) + goto out0; + + if (listen (temp, 1) == SOCKET_ERROR) + goto out0; + + len = sizeof (saddr); + if (getsockname (temp, (struct sockaddr *)&saddr, &len)) + goto out0; + + socket1 = socket (AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) + goto out0; + + arg = 1; + if (ioctlsocket (socket1, FIONBIO, &arg) == SOCKET_ERROR) + goto out1; + + if ((connect (socket1, (struct sockaddr *)&saddr, len) == SOCKET_ERROR) && + (WSAGetLastError () != WSAEWOULDBLOCK)) + goto out1; + + FD_ZERO (&read_set); + FD_SET (temp, &read_set); + + if (select (0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) + goto out1; + + if (!FD_ISSET (temp, &read_set)) + goto out1; + + socket2 = accept (temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) + goto out1; + + FD_ZERO (&write_set); + FD_SET (socket1, &write_set); + + if (select (0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) + goto out2; + + if (!FD_ISSET (socket1, &write_set)) + goto out2; + + arg = 0; + if (ioctlsocket (socket1, FIONBIO, &arg) == SOCKET_ERROR) + goto out2; + + arg = 0; + if (ioctlsocket (socket2, FIONBIO, &arg) == SOCKET_ERROR) + goto out2; + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + + out2: + closesocket (socket2); + out1: + closesocket (socket1); + out0: + closesocket (temp); + + fds[0] = INVALID_SOCKET; + fds[1] = INVALID_SOCKET; + + return -1; +} + + +/* + * Exec related functions + * + */ + +#ifdef _WIN32_WCE + +int execvp (const char *file __UNUSED__, char *const argv[] __UNUSED__) +{ + return 1; +} + +#endif /* _WIN32_WCE */ diff --git a/src/lib/evil/evil_unistd.h b/src/lib/evil/evil_unistd.h new file mode 100644 index 0000000000..db6cd022fc --- /dev/null +++ b/src/lib/evil/evil_unistd.h @@ -0,0 +1,326 @@ +#ifndef __EVIL_UNISTD_H__ +#define __EVIL_UNISTD_H__ + + +/** + * @file evil_unistd.h + * @brief The file that provides functions ported from Unix in unistd.h. + * @defgroup Evil_Unistd_Group Unistd.h functions + * + * This header provides functions ported from Unix in unistd.h. + * + * @{ + */ + + +/* + * Time related functions + * + */ + +/** + * @brief Return the time spent since the Evil library has been initialized. + * + * @return The time spent since the Evil library has been initialized. + * + * This function returns the time spent since the Evil library has + * been initialized. It uses a high-resolution timer and then can have + * a precision up to the nano-second. The precision is processor + * dependant. This function can be used to benchmark parts of code in + * with high precision. + * + * Conformity: Not appliclable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI double evil_time_get(void); + +#ifdef _MSC_VER + +/** + * @brief Retrieve the time since the Evil library has been + * initialized. + * + * @param tp Structure to fill. + * @param tzp Unused. + * @return Always 1. + * + * The function fills @p tp with the time spent since the Evil library + * has been initialized. It uses a high-resolution timer and then can + * have a precision up to the nano-second. The precision is processor + * dependant. This function can be used to benchmark parts of code in + * with high precision. This function always returns 1. + * + * Conformity: Not appliclable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int evil_gettimeofday(struct timeval * tp, void * tzp); + +/** + * @def gettimeofday(tp,tzp) + * + * Wrapper around evil_gettimeofday(). + */ +#define gettimeofday(tp,tzp) evil_gettimeofday((tp),(tzp)) + +/** + * @brief Suspend the execution of the calling process for microsecond + * intervals. + * + * @param usec The interval in microseconds. + * @return Always 0 (success). + * + * This function suspends the execution of the calling process for (at + * least) @p usec microseconds. + * + * Conformity: Not appliclable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int evil_usleep(unsigned long usec); + +/** + * @def usleep(usec) + * + * Wrapper around evil_usleep(). + */ +#define usleep(usec) evil_usleep(usec); + +#endif /* _MSC_VER */ + +/* + * Process identifer related functions + * + */ + +#if defined (_MSC_VER) || defined (_WIN32_WCE) +/** + * @brief Return the process identifier of the calling process. + * + * @return The process ID. + * + * Return the process identifier of the calling process. Until + * the process terminates, the process identifier uniquely + * identifies the process throughout the system. + * + * Conformity: Not appliclable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI pid_t getpid(void); +#endif + + +/* + * Symbolic links and directory related functions + * + */ + +/** + * @brief Create a shell link. + * + * @param oldpath The file name to be linked. + * @param newpath The file name to create. + * @return 0 on success, -1 otherwise. + * + * Create a shell link @p newpath to @p oldpath (@p newpath is the + * name of the file created, @p oldpath is the string used in + * creating the shell link). + * + * On success, this function returns 0. Otherwise, it returns -1 and + * errno may be set to the following value: + * - ENOMEM: Not enough memory. + * + * On Windows, the symbolic links do not exist. Nevertheless + * shell links can be created. This function is named like the Unix + * function for portability reasons. + * + * Conformity: None. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int symlink(const char *oldpath, const char *newpath); + +/** + * @brief Read value of a shell link. + * + * @param path The file name to be linked. + * @param buf The file name to create. + * @param bufsiz The size of the buffer. + * @return 0 on success, -1 otherwise. + * + * Place the content of the shell link @p path in the buffer + * @p buf, which has size @p bufzsiz. + * + * On success, this function returns 0. Otherwise, it returns -1 and + * errno may be set to the following value: + * - ENOMEM: Not enough memory. + * + * On Windows, the symbolic links do not exist. Nevertheless + * shell links can be managed. This function is named like the Unix + * function for portability reasons. + * + * Conformity: None. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI ssize_t readlink(const char *path, char *buf, size_t bufsiz); + + +/* + * file related functions + * + */ + + +#ifdef _WIN32_WCE + +/** + * @brief Return information about a file. + * + * @param file_name The file to retrieve information from. + * @param st Buffer to fill + * @return 0 on success, -1 otherwise. + * + * This function retrieves information about the file named + * @p file_name and fill the structure @p st. If the function + * succeeds, 0 is returned, otherwise -1 is returned. + * + * Conformity: None. + * + * Supported OS: Windows CE (not cegcc). + */ +EAPI int evil_stat(const char *file_name, struct stat *st); + +/** + * @def stat(f,st) + * + * Wrapper around evil_stat(). + */ +# define stat(f,st) evil_stat((f),(st)) + +#endif /* _WIN32_WCE */ + +/** + * @brief Get the current directory. + * + * @param buffer Buffer to store the current directory. + * @param size Size of the buffer. + * @return The current directory. + * + * On Windows desktop, use the _getcwd function in MSVCRT. + * + * On Windows CE, get the current directory by extracting the path + * from the executable that is running and put the result in @p buffer + * of length @p size. If @p size is less or equal than 0, return NULL. + * If the current absolute path would require a buffer longer than + * @p size elements, NULL is returned. If @p buffer is NULL, a buffer + * of length @p size is allocated and is returned. If the allocation + * fails, NULL is returned. On success, @p buffer is returned and + * contains the current directory. The last '\' is not included. + * If @p buffer is NULL, the returned value must be freed if not NULL. + * + * Specially usefull on WinCE where the current directory functionality + * is not supported. + * + * Conformity: Almost POSIX.1 (no errno set) + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI char *evil_getcwd(char *buffer, size_t size); + +/** + * @def getcwd(b,s) + * + * Wrapper around evil_getcwd(). + */ +#define getcwd(b,s) evil_getcwd((b),(s)) + +/* + * Sockets and pipe related functions + * + */ + +/** + * @brief Initiates the use of Windows sockets. + * + * @return 1 on success, 0 otherwise. + * + * Initiates the use of Windows sockets. If the function succeeds, + * it returns 1, otherwise it returns 0. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int evil_sockets_init(void); + +/** + * @brief Shutdown the Windows socket system. + * + * Shutdown the Windows socket system. + * + * Conformity: Non applicable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI void evil_sockets_shutdown(void); + +/** + * @brief Create a pair of sockets. + * + * @param fds A pointer that contains two sockets. + * + * Create a pair of sockets that can be use with select(). + * Hence, evil_sockets_init() must have been caled at least + * once before. Contrary to Unix, that functions does not + * create a pair of file descriptors. + * + * Conformity: Not applicable. + * + * Supported OS: Windows XP, Windows CE. + */ +EAPI int evil_pipe(int *fds); + +/** + * @def pipe(fds) + * + * Wrapper around evil_pipe(). + */ +#define pipe(fds) evil_pipe(fds) + + +/* + * Exec related functions + * + */ + + +#ifdef _WIN32_WCE + +/** + * @brief Replace the current process image with a new process image. + * + * @param file The file name of the file being executed. + * @param argv A @c NULL terminated array of strings. + * @return Always 1. + * + * This function does nothing and returns always 1. It is defined for + * ecore_app only for native Windows CE code. + * + * Conformity: Not appliclable. + * + * Supported OS: Windows CE (not cegcc). + */ +EAPI int execvp( const char *file, char *const argv[]); + +#endif /* _WIN32_WCE */ + + +/** + * @} + */ + + +#endif /* __EVIL_UNISTD_H__ */ diff --git a/src/lib/evil/evil_util.c b/src/lib/evil/evil_util.c new file mode 100644 index 0000000000..ad42559a71 --- /dev/null +++ b/src/lib/evil/evil_util.c @@ -0,0 +1,247 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <limits.h> +#include <wchar.h> + +#ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +#include "evil_macro.h" +#include "evil_util.h" +#include "evil_private.h" +#define APICHAR char +#include "evil_print.h" + + +wchar_t * +evil_char_to_wchar(const char *text) +{ + wchar_t *wtext; + int wsize; + + if (!text) + return NULL; + + wsize = MultiByteToWideChar(CP_ACP, 0, text, (int)strlen(text) + 1, NULL, 0); + if ((wsize == 0) || + (wsize > (int)(ULONG_MAX / sizeof(wchar_t)))) + { + if (wsize == 0) + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + wtext = malloc(wsize * sizeof(wchar_t)); + if (wtext) + if (!MultiByteToWideChar(CP_ACP, 0, text, (int)strlen(text) + 1, wtext, wsize)) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + return wtext; +} + +char * +evil_wchar_to_char(const wchar_t *text) +{ + char *atext; + int asize; + + if (!text) + return NULL; + + asize = WideCharToMultiByte(CP_ACP, 0, text, -1, NULL, 0, NULL, NULL); + if (asize == 0) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + atext = (char*)malloc(asize * sizeof(char)); + if (!atext) + return NULL; + + asize = WideCharToMultiByte(CP_ACP, 0, text, -1, atext, asize, NULL, NULL); + if (asize == 0) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + return atext; +} + +char * +evil_utf16_to_utf8(const wchar_t *text16) +{ + char *text8; + DWORD flag = 0; + int size8; + + if (!text16) + return NULL; + +#if _WIN32_WINNT >= 0x0600 + flag = WC_ERR_INVALID_CHARS;; +#endif + + size8 = WideCharToMultiByte(CP_UTF8, flag, text16, -1, NULL, 0, NULL, NULL); + if (size8 == 0) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + text8 = (char*)malloc(size8 * sizeof(char)); + if (!text8) + return NULL; + + size8 = WideCharToMultiByte(CP_UTF8, flag, text16, -1, text8, size8, NULL, NULL); + if (size8 == 0) + { + _evil_last_error_display(__FUNCTION__); + return NULL; + } + + return text8; +} + +char * +evil_format_message(long err) +{ + LPTSTR msg; + char *str; + char *disp; + + if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + 0, /* Default language */ + (LPTSTR)&msg, + 0, + NULL)) + { + char buf[4096]; + + snprintf(buf, 4096, "FormatMessage failed with error %ld\n", GetLastError()); + return strdup(buf); + } + +#ifdef UNICODE + str = evil_wchar_to_char(msg); +#else + str = msg; +#endif /* UNICODE */ + + disp = (char *)malloc((strlen(str) + strlen("(00000) ") + 1) * sizeof(char)); + if (!disp) + { + LocalFree(msg); + return NULL; + } + + snprintf(disp, strlen(str) + strlen("(00000) ") + 1, + "(%5ld) %s", err, str); + +#ifdef UNICODE + free(str); +#endif /* UNICODE */ + + LocalFree(msg); + + return disp; +} + +void +_evil_error_display(const char *fct, LONG res) +{ + char *error; + + error = evil_format_message(res); + fprintf(stderr, "[Evil] [%s] ERROR (%ld): %s\n", fct, res, error); + free(error); +} + +char * +evil_last_error_get(void) +{ + DWORD err; + + err = GetLastError(); + return evil_format_message(err); +} + +void +_evil_last_error_display(const char *fct) +{ + char *error; + + error = evil_last_error_get(); + fprintf(stderr, "[Evil] [%s] ERROR: %s\n", fct, error); + free(error); +} + + +const char * +evil_tmpdir_get(void) +{ +#ifdef _WIN32_WCE + return "\\temp"; +#else + char *tmpdir; + + tmpdir = getenv("TMP"); + if (!tmpdir) tmpdir = getenv("TEMP"); + if (!tmpdir) tmpdir = getenv("USERPROFILE"); + if (!tmpdir) tmpdir = getenv("WINDIR"); + if (!tmpdir) tmpdir="C:\\"; + + return tmpdir; +#endif /* ! _WIN32_WCE */ +} + +const char * +evil_homedir_get(void) +{ +#ifdef _WIN32_WCE + return "\\my documents"; +#else + char *homedir; + + homedir = getenv("HOME"); + if (!homedir) homedir = getenv("USERPROFILE"); + if (!homedir) homedir = getenv("WINDIR"); + if (!homedir) homedir="C:\\"; + + return homedir; +#endif /* ! _WIN32_WCE */ +} + +int +evil_path_is_absolute(const char *path) +{ + size_t length; + + if (!path) + return 0; + + length = strlen(path); + if (length < 3) return 0; + + if ((((*path >= 'a') && (*path <= 'z')) || + ((*path >= 'A') && (*path <= 'Z'))) && + (path[1] == ':') && + ((path[2] == '/') || (path[2] == '\\'))) + return 1; + + return 0; +} diff --git a/src/lib/evil/evil_util.h b/src/lib/evil/evil_util.h new file mode 100644 index 0000000000..7d488a9a5c --- /dev/null +++ b/src/lib/evil/evil_util.h @@ -0,0 +1,134 @@ +#ifndef __EVIL_UTIL_H__ +#define __EVIL_UTIL_H__ + + +/** + * @brief Convert a string from char * to wchar_t *. + * + * @param text The string to convert. + * @return The converted string. + * + * Convert a string from char * to wchar_t * and return it. If the + * allocation or conversion fails, NULL is returned. On success, the + * returned value must be freed when it is not used anymore. + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP. + * + * @ingroup Evil + */ +EAPI wchar_t *evil_char_to_wchar(const char *text); + +/** + * @brief Convert a string from wchar_t * to char *. + * + * @param text The string to convert. + * @return The converted string. + * + * Convert a string from wchar_t * to char * and return it. If the + * allocation or conversion fails, NULL is returned. On success, the + * returned value must be freed when it is not used anymore. + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP. + * + * @ingroup Evil + */ +EAPI char *evil_wchar_to_char(const wchar_t *text); + +/** + * @brief Convert a string from UTF-16 to UTF-8. + * + * @param text The string to convert in UTF-16. + * @return The converted string in UTF-8. + * + * Convert a string from UTF-16 to UTF-8 and return it. If the + * allocation or conversion fails, NULL is returned. On success, the + * returned value must be freed when it is not used anymore. + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP. + * + * @ingroup Evil + */ +EAPI char *evil_utf16_to_utf8(const wchar_t *text); + +EAPI char *evil_format_message(long err); + +EAPI char *evil_last_error_get(void); + +/** + * @brief Return a dir to store temporary files. + * + * @return The directory to store temporary files. + * + * Return a directory to store temporary files. The function gets + * the value of the following environment variables, and in that order: + * - TMP + * - TEMP + * - USERPROFILE + * - WINDIR + * and returns its value if it exists. If none exists, the function + * returns "C:\". + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP. + * + * @ingroup Evil + */ +EAPI const char *evil_tmpdir_get(void); + +/** + * @brief Return a dir to store personal files. + * + * @return The directory to store personal files. + * + * Return a directory to store personal files. The function gets + * the value of the following environment variables, and in that order: + * - HOME + * - USERPROFILE + * - WINDIR + * and returns its value if it exists. If none exists, the function + * returns "C:\". + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP. + * + * @ingroup Evil + */ +EAPI const char *evil_homedir_get(void); + +/** + * @brief check if the given path is absolute. + * + * @param path The path to check. + * @return 1 if the given path is absolute, 0 otherwise. + * + * Check if the path @p path is absolute or not. An absolute path must + * begin with a letter (upper or lower case), followed by by the char + * ':', followed by the char '/' or '\'. If @p path is absolute this + * function returns 1, otherwise it returns 0. If @p path is @c NULL, + * it returns 0. + * + * Conformity: Non applicable. + * + * Supported OS: Windows 95, Windows 98, Windows Me, Windows NT, Windows 2000, + * Windows XP, Windows CE. + * + * @since 1.7 + * + * @ingroup Evil + */ +EAPI int evil_path_is_absolute(const char *path); + +#endif /* __EVIL_UTIL_H__ */ diff --git a/src/lib/evil/evil_uuid.c b/src/lib/evil/evil_uuid.c new file mode 100644 index 0000000000..cfdb4302bb --- /dev/null +++ b/src/lib/evil/evil_uuid.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32CE__ +/* + * Defines the windows UUID IID_IPersistFile used for links in + * evil. This is here since uuid.lib is a static only library and + * libtool does not allow you to link a DLL against a static library. + */ + +# define INITGUID +# include <basetyps.h> +DEFINE_OLEGUID(IID_IPersistFile, 0x0000010BL, 0, 0); + +#endif /* ! __MINGW32CE__ */ diff --git a/src/lib/evil/fnmatch.h b/src/lib/evil/fnmatch.h new file mode 100644 index 0000000000..f2a9266e53 --- /dev/null +++ b/src/lib/evil/fnmatch.h @@ -0,0 +1,54 @@ +#ifndef __EVIL_FNMATCH_H__ +#define __EVIL_FNMATCH_H__ + +#ifdef EAPI +# undef EAPI +#endif /* EAPI */ + +#ifdef _WIN32 +# ifdef EFL_EVIL_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVIL_BUILD */ +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* We #undef these before defining them because some losing systems + (HP-UX A.08.07 for example) define these in <unistd.h>. */ +#undef FNM_PATHNAME +#undef FNM_NOESCAPE +#undef FNM_PERIOD + +/* Bits set in the FLAGS argument to `fnmatch'. */ +#define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ +#define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ +#define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ + +#if !defined (_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 2 || defined (_GNU_SOURCE) +#define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ +#define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ +#define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ +#endif + +/* Value returned by `fnmatch' if STRING does not match PATTERN. */ +#define FNM_NOMATCH 1 +#define FNM_SYNTAXERR 2 +#define FNM_NOMEM 3 + +/* Match STRING against the filename pattern PATTERN, + returning zero if it matches, FNM_NOMATCH if not. */ +EAPI int fnmatch(const char *__pattern, const char *__string, int __flags); + +#ifdef __cplusplus +} +#endif + +#endif /* __EVIL_FNMATCH_H__ */ diff --git a/src/lib/evil/gdtoa/README b/src/lib/evil/gdtoa/README new file mode 100644 index 0000000000..ce8be55888 --- /dev/null +++ b/src/lib/evil/gdtoa/README @@ -0,0 +1,357 @@ +This directory contains source for a library of binary -> decimal +and decimal -> binary conversion routines, for single-, double-, +and extended-precision IEEE binary floating-point arithmetic, and +other IEEE-like binary floating-point, including "double double", +as in + + T. J. Dekker, "A Floating-Point Technique for Extending the + Available Precision", Numer. Math. 18 (1971), pp. 224-242 + +and + + "Inside Macintosh: PowerPC Numerics", Addison-Wesley, 1994 + +The conversion routines use double-precision floating-point arithmetic +and, where necessary, high precision integer arithmetic. The routines +are generalizations of the strtod and dtoa routines described in + + David M. Gay, "Correctly Rounded Binary-Decimal and + Decimal-Binary Conversions", Numerical Analysis Manuscript + No. 90-10, Bell Labs, Murray Hill, 1990; + http://cm.bell-labs.com/cm/cs/what/ampl/REFS/rounding.ps.gz + +(based in part on papers by Clinger and Steele & White: see the +references in the above paper). + +The present conversion routines should be able to use any of IEEE binary, +VAX, or IBM-mainframe double-precision arithmetic internally, but I (dmg) +have so far only had a chance to test them with IEEE double precision +arithmetic. + +The core conversion routines are strtodg for decimal -> binary conversions +and gdtoa for binary -> decimal conversions. These routines operate +on arrays of unsigned 32-bit integers of type ULong, a signed 32-bit +exponent of type Long, and arithmetic characteristics described in +struct FPI; FPI, Long, and ULong are defined in gdtoa.h. File arith.h +is supposed to provide #defines that cause gdtoa.h to define its +types correctly. File arithchk.c is source for a program that +generates a suitable arith.h on all systems where I've been able to +test it. + +The core conversion routines are meant to be called by helper routines +that know details of the particular binary arithmetic of interest and +convert. The present directory provides helper routines for 5 variants +of IEEE binary floating-point arithmetic, each indicated by one or +two letters: + + f IEEE single precision + d IEEE double precision + x IEEE extended precision, as on Intel 80x87 + and software emulations of Motorola 68xxx chips + that do not pad the way the 68xxx does, but + only store 80 bits + xL IEEE extended precision, as on Motorola 68xxx chips + Q quad precision, as on Sun Sparc chips + dd double double, pairs of IEEE double numbers + whose sum is the desired value + +For decimal -> binary conversions, there are three families of +helper routines: one for round-nearest (or the current rounding +mode on IEEE-arithmetic systems that provide the C99 fegetround() +function, if compiled with -DHonor_FLT_ROUNDS): + + strtof + strtod + strtodd + strtopd + strtopf + strtopx + strtopxL + strtopQ + +one with rounding direction specified: + + strtorf + strtord + strtordd + strtorx + strtorxL + strtorQ + +and one for computing an interval (at most one bit wide) that contains +the decimal number: + + strtoIf + strtoId + strtoIdd + strtoIx + strtoIxL + strtoIQ + +The latter call strtoIg, which makes one call on strtodg and adjusts +the result to provide the desired interval. On systems where native +arithmetic can easily make one-ulp adjustments on values in the +desired floating-point format, it might be more efficient to use the +native arithmetic. Routine strtodI is a variant of strtoId that +illustrates one way to do this for IEEE binary double-precision +arithmetic -- but whether this is more efficient remains to be seen. + +Functions strtod and strtof have "natural" return types, float and +double -- strtod is specified by the C standard, and strtof appears +in the stdlib.h of some systems, such as (at least some) Linux systems. +The other functions write their results to their final argument(s): +to the final two argument for the strtoI... (interval) functions, +and to the final argument for the others (strtop... and strtor...). +Where possible, these arguments have "natural" return types (double* +or float*), to permit at least some type checking. In reality, they +are viewed as arrays of ULong (or, for the "x" functions, UShort) +values. On systems where long double is the appropriate type, one can +pass long double* final argument(s) to these routines. The int value +that these routines return is the return value from the call they make +on strtodg; see the enum of possible return values in gdtoa.h. + +Source files g_ddfmt.c, misc.c, smisc.c, strtod.c, strtodg.c, and ulp.c +should use true IEEE double arithmetic (not, e.g., double extended), +at least for storing (and viewing the bits of) the variables declared +"double" within them. + +One detail indicated in struct FPI is whether the target binary +arithmetic departs from the IEEE standard by flushing denormalized +numbers to 0. On systems that do this, the helper routines for +conversion to double-double format (when compiled with +Sudden_Underflow #defined) penalize the bottom of the exponent +range so that they return a nonzero result only when the least +significant bit of the less significant member of the pair of +double values returned can be expressed as a normalized double +value. An alternative would be to drop to 53-bit precision near +the bottom of the exponent range. To get correct rounding, this +would (in general) require two calls on strtodg (one specifying +126-bit arithmetic, then, if necessary, one specifying 53-bit +arithmetic). + +By default, the core routine strtodg and strtod set errno to ERANGE +if the result overflows to +Infinity or underflows to 0. Compile +these routines with NO_ERRNO #defined to inhibit errno assignments. + +Routine strtod is based on netlib's "dtoa.c from fp", and +(f = strtod(s,se)) is more efficient for some conversions than, say, +strtord(s,se,1,&f). Parts of strtod require true IEEE double +arithmetic with the default rounding mode (round-to-nearest) and, on +systems with IEEE extended-precision registers, double-precision +(53-bit) rounding precision. If the machine uses (the equivalent of) +Intel 80x87 arithmetic, the call + _control87(PC_53, MCW_PC); +does this with many compilers. Whether this or another call is +appropriate depends on the compiler; for this to work, it may be +necessary to #include "float.h" or another system-dependent header +file. + +Source file strtodnrp.c gives a strtod that does not require 53-bit +rounding precision on systems (such as Intel IA32 systems) that may +suffer double rounding due to use of extended-precision registers. +For some conversions this variant of strtod is less efficient than the +one in strtod.c when the latter is run with 53-bit rounding precision. + +The values that the strto* routines return for NaNs are determined by +gd_qnan.h, which the makefile generates by running the program whose +source is qnan.c. Note that the rules for distinguishing signaling +from quiet NaNs are system-dependent. For cross-compilation, you need +to determine arith.h and gd_qnan.h suitably, e.g., using the +arithmetic of the target machine. + +C99's hexadecimal floating-point constants are recognized by the +strto* routines (but this feature has not yet been heavily tested). +Compiling with NO_HEX_FP #defined disables this feature. + +When compiled with -DINFNAN_CHECK, the strto* routines recognize C99's +NaN and Infinity syntax. Moreover, unless No_Hex_NaN is #defined, the +strto* routines also recognize C99's NaN(...) syntax: they accept +(case insensitively) strings of the form NaN(x), where x is a string +of hexadecimal digits and spaces; if there is only one string of +hexadecimal digits, it is taken for the fraction bits of the resulting +NaN; if there are two or more strings of hexadecimal digits, each +string is assigned to the next available sequence of 32-bit words of +fractions bits (starting with the most significant), right-aligned in +each sequence. + +For binary -> decimal conversions, I've provided just one family +of helper routines: + + g_ffmt + g_dfmt + g_ddfmt + g_xfmt + g_xLfmt + g_Qfmt + +which do a "%g" style conversion either to a specified number of decimal +places (if their ndig argument is positive), or to the shortest +decimal string that rounds to the given binary floating-point value +(if ndig <= 0). They write into a buffer supplied as an argument +and return either a pointer to the end of the string (a null character) +in the buffer, if the buffer was long enough, or 0. Other forms of +conversion are easily done with the help of gdtoa(), such as %e or %f +style and conversions with direction of rounding specified (so that, if +desired, the decimal value is either >= or <= the binary value). +On IEEE-arithmetic systems that provide the C99 fegetround() function, +if compiled with -DHonor_FLT_ROUNDS, these routines honor the current +rounding mode. + +For an example of more general conversions based on dtoa(), see +netlib's "printf.c from ampl/solvers". + +For double-double -> decimal, g_ddfmt() assumes IEEE-like arithmetic +of precision max(126, #bits(input)) bits, where #bits(input) is the +number of mantissa bits needed to represent the sum of the two double +values in the input. + +The makefile creates a library, gdtoa.a. To use the helper +routines, a program only needs to include gdtoa.h. All the +source files for gdtoa.a include a more extensive gdtoaimp.h; +among other things, gdtoaimp.h has #defines that make "internal" +names end in _D2A. To make a "system" library, one could modify +these #defines to make the names start with __. + +Various comments about possible #defines appear in gdtoaimp.h, +but for most purposes, arith.h should set suitable #defines. + +Systems with preemptive scheduling of multiple threads require some +manual intervention. On such systems, it's necessary to compile +dmisc.c, dtoa.c gdota.c, and misc.c with MULTIPLE_THREADS #defined, +and to provide (or suitably #define) two locks, acquired by +ACQUIRE_DTOA_LOCK(n) and freed by FREE_DTOA_LOCK(n) for n = 0 or 1. +(The second lock, accessed in pow5mult, ensures lazy evaluation of +only one copy of high powers of 5; omitting this lock would introduce +a small probability of wasting memory, but would otherwise be harmless.) +Routines that call dtoa or gdtoa directly must also invoke freedtoa(s) +to free the value s returned by dtoa or gdtoa. It's OK to do so whether +or not MULTIPLE_THREADS is #defined, and the helper g_*fmt routines +listed above all do this indirectly (in gfmt_D2A(), which they all call). + +By default, there is a private pool of memory of length 2000 bytes +for intermediate quantities, and MALLOC (see gdtoaimp.h) is called only +if the private pool does not suffice. 2000 is large enough that MALLOC +is called only under very unusual circumstances (decimal -> binary +conversion of very long strings) for conversions to and from double +precision. For systems with preemptively scheduled multiple threads +or for conversions to extended or quad, it may be appropriate to +#define PRIVATE_MEM nnnn, where nnnn is a suitable value > 2000. +For extended and quad precisions, -DPRIVATE_MEM=20000 is probably +plenty even for many digits at the ends of the exponent range. +Use of the private pool avoids some overhead. + +Directory test provides some test routines. See its README. +I've also tested this stuff (except double double conversions) +with Vern Paxson's testbase program: see + + V. Paxson and W. Kahan, "A Program for Testing IEEE Binary-Decimal + Conversion", manuscript, May 1991, + ftp://ftp.ee.lbl.gov/testbase-report.ps.Z . + +(The same ftp directory has source for testbase.) + +Some system-dependent additions to CFLAGS in the makefile: + + HU-UX: -Aa -Ae + OSF (DEC Unix): -ieee_with_no_inexact + SunOS 4.1x: -DKR_headers -DBad_float_h + +If you want to put this stuff into a shared library and your +operating system requires export lists for shared libraries, +the following would be an appropriate export list: + + dtoa + freedtoa + g_Qfmt + g_ddfmt + g_dfmt + g_ffmt + g_xLfmt + g_xfmt + gdtoa + strtoIQ + strtoId + strtoIdd + strtoIf + strtoIx + strtoIxL + strtod + strtodI + strtodg + strtof + strtopQ + strtopd + strtopdd + strtopf + strtopx + strtopxL + strtorQ + strtord + strtordd + strtorf + strtorx + strtorxL + +When time permits, I (dmg) hope to write in more detail about the +present conversion routines; for now, this README file must suffice. +Meanwhile, if you wish to write helper functions for other kinds of +IEEE-like arithmetic, some explanation of struct FPI and the bits +array may be helpful. Both gdtoa and strtodg operate on a bits array +described by FPI *fpi. The bits array is of type ULong, a 32-bit +unsigned integer type. Floating-point numbers have fpi->nbits bits, +with the least significant 32 bits in bits[0], the next 32 bits in +bits[1], etc. These numbers are regarded as integers multiplied by +2^e (i.e., 2 to the power of the exponent e), where e is the second +argument (be) to gdtoa and is stored in *exp by strtodg. The minimum +and maximum exponent values fpi->emin and fpi->emax for normalized +floating-point numbers reflect this arrangement. For example, the +P754 standard for binary IEEE arithmetic specifies doubles as having +53 bits, with normalized values of the form 1.xxxxx... times 2^(b-1023), +with 52 bits (the x's) and the biased exponent b represented explicitly; +b is an unsigned integer in the range 1 <= b <= 2046 for normalized +finite doubles, b = 0 for denormals, and b = 2047 for Infinities and NaNs. +To turn an IEEE double into the representation used by strtodg and gdtoa, +we multiply 1.xxxx... by 2^52 (to make it an integer) and reduce the +exponent e = (b-1023) by 52: + + fpi->emin = 1 - 1023 - 52 + fpi->emax = 1046 - 1023 - 52 + +In various wrappers for IEEE double, we actually write -53 + 1 rather +than -52, to emphasize that there are 53 bits including one implicit bit. +Field fpi->rounding indicates the desired rounding direction, with +possible values + FPI_Round_zero = toward 0, + FPI_Round_near = unbiased rounding -- the IEEE default, + FPI_Round_up = toward +Infinity, and + FPI_Round_down = toward -Infinity +given in gdtoa.h. + +Field fpi->sudden_underflow indicates whether strtodg should return +denormals or flush them to zero. Normal floating-point numbers have +bit fpi->nbits in the bits array on. Denormals have it off, with +exponent = fpi->emin. Strtodg provides distinct return values for normals +and denormals; see gdtoa.h. + +Compiling g__fmt.c, strtod.c, and strtodg.c with -DUSE_LOCALE causes +the decimal-point character to be taken from the current locale; otherwise +it is '.'. + +Source files dtoa.c and strtod.c in this directory are derived from +netlib's "dtoa.c from fp" and are meant to function equivalently. +When compiled with Honor_FLT_ROUNDS #defined (on systems that provide +FLT_ROUNDS and fegetround() as specified in the C99 standard), they +honor the current rounding mode. Because FLT_ROUNDS is buggy on some +(Linux) systems -- not reflecting calls on fesetround(), as the C99 +standard says it should -- when Honor_FLT_ROUNDS is #defined, the +current rounding mode is obtained from fegetround() rather than from +FLT_ROUNDS, unless Trust_FLT_ROUNDS is also #defined. + +Compile with -DUSE_LOCALE to use the current locale; otherwise +decimal points are assumed to be '.'. With -DUSE_LOCALE, unless +you also compile with -DNO_LOCALE_CACHE, the details about the +current "decimal point" character string are cached and assumed not +to change during the program's execution. + +Please send comments to David M. Gay (dmg at acm dot org, with " at " +changed at "@" and " dot " changed to "."). diff --git a/src/lib/evil/gdtoa/README.mingw b/src/lib/evil/gdtoa/README.mingw new file mode 100644 index 0000000000..b41d1fdae0 --- /dev/null +++ b/src/lib/evil/gdtoa/README.mingw @@ -0,0 +1,20 @@ +The gdtoa code here is based on David M. Gay's original +gdtoa source at http://www.netlib.org/fp/ from Sep. 27, +2010. The major changes between the original source and +the mingw port here include: + +* IBM, CRAY and VAX code removed. +* KR_headers, ANSI, Void and Char ifdefs are removed. +* gdtoa symbols are prepended with "__". +* g_xfmt() uses __fpclassifyl() instead of interpreting + the flags bit-wise. +* lo0bits() and hi0bits() of misc.c replaced by wrappers + to gcc's __builtin_clz() +* The double/ulong union renamed from U to dbl_union + (grep'ped better..) +* A few compiler warning fixes here and there. +* A few other insignificant changes (if any..) + +MinGW specific compile-time definitions are at the top of +gdtoaimp.h and gdtoa.h headers. + diff --git a/src/lib/evil/gdtoa/arithchk.c b/src/lib/evil/gdtoa/arithchk.c new file mode 100644 index 0000000000..901bab1fb3 --- /dev/null +++ b/src/lib/evil/gdtoa/arithchk.c @@ -0,0 +1,192 @@ +/**************************************************************** +Copyright (C) 1997, 1998 Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. +****************************************************************/ + +/* Try to deduce arith.h from arithmetic properties. */ +#ifdef MINGW_BUILD_GEN + +#include <stdio.h> + + static int dalign; + typedef struct +Akind { + char *name; + int kind; + } Akind; + + static Akind +IEEE_8087 = { "IEEE_8087", 1 }, +IEEE_MC68k = { "IEEE_MC68k", 2 }, +IBM = { "IBM", 3 }, +VAX = { "VAX", 4 }, +CRAY = { "CRAY", 5}; + + static Akind * +Lcheck() +{ + union { + double d; + long L[2]; + } u; + struct { + double d; + long L; + } x[2]; + + if (sizeof(x) > 2*(sizeof(double) + sizeof(long))) + dalign = 1; + u.L[0] = u.L[1] = 0; + u.d = 1e13; + if (u.L[0] == 1117925532 && u.L[1] == -448790528) + return &IEEE_MC68k; + if (u.L[1] == 1117925532 && u.L[0] == -448790528) + return &IEEE_8087; + if (u.L[0] == -2065213935 && u.L[1] == 10752) + return &VAX; + if (u.L[0] == 1267827943 && u.L[1] == 704643072) + return &IBM; + return 0; + } + + static Akind * +icheck() +{ + union { + double d; + int L[2]; + } u; + struct { + double d; + int L; + } x[2]; + + if (sizeof(x) > 2*(sizeof(double) + sizeof(int))) + dalign = 1; + u.L[0] = u.L[1] = 0; + u.d = 1e13; + if (u.L[0] == 1117925532 && u.L[1] == -448790528) + return &IEEE_MC68k; + if (u.L[1] == 1117925532 && u.L[0] == -448790528) + return &IEEE_8087; + if (u.L[0] == -2065213935 && u.L[1] == 10752) + return &VAX; + if (u.L[0] == 1267827943 && u.L[1] == 704643072) + return &IBM; + return 0; + } + +char *emptyfmt = ""; /* avoid possible warning message with printf("") */ + + static Akind * +ccheck() +{ + union { + double d; + long L; + } u; + long Cray1; + + /* Cray1 = 4617762693716115456 -- without overflow on non-Crays */ + Cray1 = printf(emptyfmt) < 0 ? 0 : 4617762; + if (printf(emptyfmt, Cray1) >= 0) + Cray1 = 1000000*Cray1 + 693716; + if (printf(emptyfmt, Cray1) >= 0) + Cray1 = 1000000*Cray1 + 115456; + u.d = 1e13; + if (u.L == Cray1) + return &CRAY; + return 0; + } + + static int +fzcheck() +{ + double a, b; + int i; + + a = 1.; + b = .1; + for(i = 155;; b *= b, i >>= 1) { + if (i & 1) { + a *= b; + if (i == 1) + break; + } + } + b = a * a; + return b == 0.; + } + + int +main() +{ + Akind *a = 0; + int Ldef = 0; + FILE *f; + +#ifdef WRITE_ARITH_H /* for Symantec's buggy "make" */ + f = fopen("arith.h", "w"); + if (!f) { + printf("Cannot open arith.h\n"); + return 1; + } +#else + f = stdout; +#endif + + if (sizeof(double) == 2*sizeof(long)) + a = Lcheck(); + else if (sizeof(double) == 2*sizeof(int)) { + Ldef = 1; + a = icheck(); + } + else if (sizeof(double) == sizeof(long)) + a = ccheck(); + if (a) { + fprintf(f, "#define %s\n#define Arith_Kind_ASL %d\n", + a->name, a->kind); + if (Ldef) + fprintf(f, "#define Long int\n#define Intcast (int)(long)\n"); + if (dalign) + fprintf(f, "#define Double_Align\n"); + if (sizeof(char*) == 8) + fprintf(f, "#define X64_bit_pointers\n"); +#ifndef NO_LONG_LONG + if (sizeof(long long) < 8) +#endif + fprintf(f, "#define NO_LONG_LONG\n"); + if (a->kind <= 2 && fzcheck()) + fprintf(f, "#define Sudden_Underflow\n"); +#ifdef WRITE_ARITH_H /* for Symantec's buggy "make" */ + fclose(f); +#endif + return 0; + } + fprintf(f, "/* Unknown arithmetic */\n"); +#ifdef WRITE_ARITH_H /* for Symantec's buggy "make" */ + fclose(f); +#endif + return 1; + } +#endif /* MINGW_BUILD_GEN */ + diff --git a/src/lib/evil/gdtoa/dmisc.c b/src/lib/evil/gdtoa/dmisc.c new file mode 100644 index 0000000000..c61e9fa8a5 --- /dev/null +++ b/src/lib/evil/gdtoa/dmisc.c @@ -0,0 +1,196 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#ifndef MULTIPLE_THREADS +char *dtoa_result; +#endif + +char *rv_alloc (int i) +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + (int) (sizeof(Bigint) - sizeof(ULong) - sizeof(int)) + j <= i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); +} + +char *nrv_alloc (char *s, char **rve, int n) +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++) !=0) + t++; + if (rve) + *rve = t; + return rv; +} + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + +void __freedtoa (char *s) +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif +} + +int quorem (Bigint *b, Bigint *S) +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } while(sx <= sxe); + + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & 0xffffffffUL) - borrow; + borrow = y >> 32 & 1UL; + *bx++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } while(sx <= sxe); + + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; +} diff --git a/src/lib/evil/gdtoa/dtoa.c b/src/lib/evil/gdtoa/dtoa.c new file mode 100644 index 0000000000..2906bd99f6 --- /dev/null +++ b/src/lib/evil/gdtoa/dtoa.c @@ -0,0 +1,750 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +char *__dtoa (double d0, int mode, int ndigits, int *decpt, int *sign, char **rve) +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j2, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + union _dbl_union d, d2, eps; + double ds; + char *s, *s0; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + __freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + d.d = d0; + if (word0(&d) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&d) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + + if ((word0(&d) & Exp_mask) == Exp_mask) + { + /* Infinity or NaN */ + *decpt = 9999; + if (!word1(&d) && !(word0(&d) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); + return nrv_alloc("NaN", rve, 3); + } + if (!dval(&d)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif + + b = d2b(dval(&d), &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(&d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if (( i = (int)(word0(&d) >> Exp_shift1 & (Exp_mask>>Exp_shift1)) )!=0) { +#endif + dval(&d2) = dval(&d); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(&d) = (i-Bias)*log(2)/log(10) + log10(&d2) + * + * This suggests computing an approximation k to log10(&d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&d) << (64 - i) | word1(&d) >> (i - 32) + : word1(&d) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&d) /= ds; + } + else if (( j2 = -k )!=0) { + dval(&d) *= tens[j2 & 0xf]; + for(j = j2 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&d) *= bigtens[i]; + } + } + if (k_check && dval(&d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&d) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&d) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&d) -= 5.; + if (dval(&d) > dval(&eps)) + goto one_digit; + if (dval(&d) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); + for(i = 0;;) { + L = dval(&d); + dval(&d) -= L; + *s++ = '0' + (int)L; + if (dval(&d) < dval(&eps)) + goto ret1; + if (1. - dval(&d) < dval(&eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&d) *= 10.) { + L = (Long)(dval(&d)); + if (!(dval(&d) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&d) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&d) < 0.5 - dval(&eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&d) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&d) *= 10.) { + L = (Long)(dval(&d) / ds); + dval(&d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&d) < 0) { + L--; + dval(&d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&d)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(&d) += dval(&d); +#ifdef ROUND_BIASED + if (dval(&d) >= ds) +#else + if (dval(&d) > ds || (dval(&d) == ds && L & 1)) +#endif + { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif + 1 + P - bbits; + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if (( j = b5 - m5 )!=0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&d) && !(word0(&d) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&d) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if (( i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f )!=0) + i = 32 - i; +#else + if (( i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf )!=0) + i = 16 - i; +#endif + if (i > 4) { + i -= 4; + b2 += i; + m2 += i; + s2 += i; + } + else if (i < 4) { + i += 28; + b2 += i; + m2 += i; + s2 += i; + } + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j2 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j2 == 0 && mode != 1 && !(word1(&d) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&d) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j2 > 0) { + b = lshift(b, 1); + j2 = cmp(b, S); +#ifdef ROUND_BIASED + if (j2 >= 0 /*)*/ +#else + if ((j2 > 0 || (j2 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j2 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&d) = Exp_1 + (70 << Exp_shift); + word1(&d) = 0; + dval(&d) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; +} diff --git a/src/lib/evil/gdtoa/g__fmt.c b/src/lib/evil/gdtoa/g__fmt.c new file mode 100644 index 0000000000..49bd95a845 --- /dev/null +++ b/src/lib/evil/gdtoa/g__fmt.c @@ -0,0 +1,142 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +char *__g__fmt (char *b, char *s, char *se, int decpt, ULong sign, size_t blen) +{ + int i, j, k; + char *be, *s0; + size_t len; +#ifdef USE_LOCALE +#ifdef NO_LOCALE_CACHE + char *decimalpoint = localeconv()->decimal_point; + size_t dlen = strlen(decimalpoint); +#else + char *decimalpoint; + static char *decimalpoint_cache; + static size_t dlen; + if (!(s0 = decimalpoint_cache)) { + s0 = localeconv()->decimal_point; + dlen = strlen(s0); + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#else +#define dlen 0 +#endif + s0 = s; + len = (se-s) + dlen + 6; /* 6 = sign + e+dd + trailing null */ + if (blen < len) + goto ret0; + be = b + blen - 1; + if (sign) + *b++ = '-'; + if (decpt <= -4 || decpt > se - s + 5) { + *b++ = *s++; + if (*s) { +#ifdef USE_LOCALE + while((*b = *decimalpoint++)) + ++b; +#else + *b++ = '.'; +#endif + while((*b = *s++) !=0) + b++; + } + *b++ = 'e'; + /* sprintf(b, "%+.2d", decpt - 1); */ + if (--decpt < 0) { + *b++ = '-'; + decpt = -decpt; + } + else + *b++ = '+'; + for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10){} + for(;;) { + i = decpt / k; + if (b >= be) + goto ret0; + *b++ = i + '0'; + if (--j <= 0) + break; + decpt -= i*k; + decpt *= 10; + } + *b = 0; + } + else if (decpt <= 0) { +#ifdef USE_LOCALE + while((*b = *decimalpoint++)) + ++b; +#else + *b++ = '.'; +#endif + if (be < b - decpt + (se - s)) + goto ret0; + for(; decpt < 0; decpt++) + *b++ = '0'; + while((*b = *s++) != 0) + b++; + } + else { + while((*b = *s++) != 0) { + b++; + if (--decpt == 0 && *s) { +#ifdef USE_LOCALE + while((*b = *decimalpoint++)) + ++b; +#else + *b++ = '.'; +#endif + } + } + if (b + decpt > be) { + ret0: + b = 0; + goto ret; + } + for(; decpt > 0; decpt--) + *b++ = '0'; + *b = 0; + } + ret: + __freedtoa(s0); + return b; +} diff --git a/src/lib/evil/gdtoa/g_dfmt.c b/src/lib/evil/gdtoa/g_dfmt.c new file mode 100644 index 0000000000..50ed708a6c --- /dev/null +++ b/src/lib/evil/gdtoa/g_dfmt.c @@ -0,0 +1,90 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +char *__g_dfmt (char *buf, double *d, int ndig, size_t bufsize) +{ + static FPI fpi0 = { 53, 1-1023-53+1, 2046-1023-53+1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "gdtoa_fltrnds.h" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if ((int) bufsize < ndig + 10) + return 0; + + L = (ULong*)d; + sign = L[_0] & 0x80000000L; + if ((L[_0] & 0x7ff00000) == 0x7ff00000) { + /* Infinity or NaN */ + if (bufsize < 10) + return 0; + if (L[_0] & 0xfffff || L[_1]) { + return strcp(buf, "NaN"); + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, "Infinity"); + } + if (L[_1] == 0 && (L[_0] ^ sign) == 0 /*d == 0.*/) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[_0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[_1]; + bits[1] = L[_0] & 0xfffff; + if ( (ex = (L[_0] >> 20) & 0x7ff) !=0) + bits[1] |= 0x100000; + else + ex = 1; + ex -= 0x3ff + 52; + mode = 2; + if (ndig <= 0) + mode = 0; + i = STRTOG_Normal; + if (sign) + i = STRTOG_Normal | STRTOG_Neg; + s = __gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return __g__fmt(buf, s, se, decpt, sign, bufsize); +} diff --git a/src/lib/evil/gdtoa/g_ffmt.c b/src/lib/evil/gdtoa/g_ffmt.c new file mode 100644 index 0000000000..f3f7c2419c --- /dev/null +++ b/src/lib/evil/gdtoa/g_ffmt.c @@ -0,0 +1,88 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +char *__g_ffmt (char *buf, float *f, int ndig, size_t bufsize) +{ + static FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, 0, 6 }; + char *b, *s, *se; + ULong bits[1], *L, sign; + int decpt, ex, i, mode; +#ifdef Honor_FLT_ROUNDS +#include "gdtoa_fltrnds.h" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if ((int) bufsize < ndig + 10) + return 0; + + L = (ULong*)f; + sign = L[0] & 0x80000000L; + if ((L[0] & 0x7f800000) == 0x7f800000) { + /* Infinity or NaN */ + if (L[0] & 0x7fffff) { + return strcp(buf, "NaN"); + } + b = buf; + if (sign) + *b++ = '-'; + return strcp(b, "Infinity"); + } + if (*f == 0.) { + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (L[0] & 0x80000000L) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + bits[0] = L[0] & 0x7fffff; + if ( (ex = (L[0] >> 23) & 0xff) !=0) + bits[0] |= 0x800000; + else + ex = 1; + ex -= 0x7f + 23; + mode = 2; + if (ndig <= 0) { + if (bufsize < 16) + return 0; + mode = 0; + } + i = STRTOG_Normal; + s = __gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return __g__fmt(buf, s, se, decpt, sign, bufsize); +} diff --git a/src/lib/evil/gdtoa/g_xfmt.c b/src/lib/evil/gdtoa/g_xfmt.c new file mode 100644 index 0000000000..da11a5881b --- /dev/null +++ b/src/lib/evil/gdtoa/g_xfmt.c @@ -0,0 +1,143 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + +char *__g_xfmt (char *buf, void *V, int ndig, size_t bufsize) +{ + static FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, 0, Int_max }; + char *b, *s, *se; + ULong bits[2], sign; + UShort *L; + int decpt, ex, i, mode; +#if defined(__MINGW32__) || defined(__MINGW64__) + int fptype = __fpclassifyl (*(long double*) V); +#endif /* MinGW */ +#ifdef Honor_FLT_ROUNDS +#include "gdtoa_fltrnds.h" +#else +#define fpi &fpi0 +#endif + + if (ndig < 0) + ndig = 0; + if ((int) bufsize < ndig + 10) + return 0; + + L = (UShort *)V; + sign = L[_0] & 0x8000; + ex = L[_0] & 0x7fff; + bits[1] = (L[_1] << 16) | L[_2]; + bits[0] = (L[_3] << 16) | L[_4]; + +#if defined(__MINGW32__) || defined(__MINGW64__) + if (fptype & FP_NAN) { + /* NaN or Inf */ + if (fptype & FP_NORMAL) { + b = buf; + *b++ = sign ? '-': '+'; + strncpy (b, "Infinity", ndig ? ndig : 8); + return (buf + strlen (buf)); + } + strncpy (buf, "NaN", ndig ? ndig : 3); + return (buf + strlen (buf)); + } + else if (fptype & FP_NORMAL) { + /* Normal or subnormal */ + if (fptype & FP_ZERO) { + i = STRTOG_Denormal; + ex = 1; + } + else + i = STRTOG_Normal; + } +#else + if (ex != 0) { + if (ex == 0x7fff) { + /* Infinity or NaN */ + if (bits[0] | bits[1]) + b = strcp(buf, "NaN"); + else { + b = buf; + if (sign) + *b++ = '-'; + b = strcp(b, "Infinity"); + } + return b; + } + i = STRTOG_Normal; + } + else if (bits[0] | bits[1]) { + i = STRTOG_Denormal; + ex = 1; + } +#endif + else { + /* i = STRTOG_Zero; */ + b = buf; +#ifndef IGNORE_ZERO_SIGN + if (sign) + *b++ = '-'; +#endif + *b++ = '0'; + *b = 0; + return b; + } + ex -= 0x3fff + 63; + mode = 2; + if (ndig <= 0) { + if (bufsize < 32) + return 0; + mode = 0; + } + s = __gdtoa(fpi, ex, bits, &i, mode, ndig, &decpt, &se); + return __g__fmt(buf, s, se, decpt, sign, bufsize); +} diff --git a/src/lib/evil/gdtoa/gd_arith.h b/src/lib/evil/gdtoa/gd_arith.h new file mode 100644 index 0000000000..f86e633b37 --- /dev/null +++ b/src/lib/evil/gdtoa/gd_arith.h @@ -0,0 +1,6 @@ +#define IEEE_8087 +#define Arith_Kind_ASL 1 +#define Double_Align +#ifdef _WIN64 +#define X64_bit_pointers +#endif /* w64 */ diff --git a/src/lib/evil/gdtoa/gd_qnan.h b/src/lib/evil/gdtoa/gd_qnan.h new file mode 100644 index 0000000000..87eba8fb31 --- /dev/null +++ b/src/lib/evil/gdtoa/gd_qnan.h @@ -0,0 +1,12 @@ +#define f_QNAN 0xffc00000 +#define d_QNAN0 0x0 +#define d_QNAN1 0xfff80000 +#define ld_QNAN0 0x0 +#define ld_QNAN1 0xc0000000 +#define ld_QNAN2 0xffff +#define ld_QNAN3 0x0 +#define ldus_QNAN0 0x0 +#define ldus_QNAN1 0x0 +#define ldus_QNAN2 0x0 +#define ldus_QNAN3 0xc000 +#define ldus_QNAN4 0xffff diff --git a/src/lib/evil/gdtoa/gdtoa.c b/src/lib/evil/gdtoa/gdtoa.c new file mode 100644 index 0000000000..cf9c290fb4 --- /dev/null +++ b/src/lib/evil/gdtoa/gdtoa.c @@ -0,0 +1,733 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +static Bigint *bitstob (ULong *bits, int nbits, int *bbits) +{ + int i, k; + Bigint *b; + ULong *be, *x, *x0; + + i = ULbits; + k = 0; + while(i < nbits) { + i <<= 1; + k++; + } +#ifndef Pack_32 + if (!k) + k = 1; +#endif + b = Balloc(k); + be = bits + ((nbits - 1) >> kshift); + x = x0 = b->x; + do { + *x++ = *bits & ALL_ON; +#ifdef Pack_16 + *x++ = (*bits >> 16) & ALL_ON; +#endif + } while(++bits <= be); + i = x - x0; + while(!x0[--i]) + if (!i) { + b->wds = 0; + *bbits = 0; + goto ret; + } + b->wds = i + 1; + *bbits = i*ULbits + 32 - hi0bits(b->x[i]); + ret: + return b; +} + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + +char *__gdtoa (FPI *fpi, int be, ULong *bits, int *kindp, int mode, int ndigits, + int *decpt, char **rve) +{ + /* Arguments ndigits and decpt are similar to the second and third + arguments of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + be = exponent: value = (integer represented by bits) * (2 to the power of be). + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4-9 should give the same return values as 2-3, i.e., + 4 <= mode <= 9 ==> same return as mode + 2 + (mode & 1). These modes are mainly for + debugging; often they run slower but sometimes + faster than modes 2-3. + 4,5,8,9 ==> left-to-right digit generation. + 6-9 ==> don't try fast floating-point estimate + (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be0, dig, i, ieps, ilim, ilim0, ilim1, inex; + int j, j2, k, k0, k_check, kind, leftright, m2, m5, nbits; + int rdir, s2, s5, spec_case, try_quick; + Long L; + Bigint *b, *b1, *delta, *mlo, *mhi, *mhi1, *S; + double d2, ds; + char *s, *s0; + union _dbl_union d, eps; + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + __freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + inex = 0; + kind = *kindp &= ~STRTOG_Inexact; + switch(kind & STRTOG_Retmask) { + case STRTOG_Zero: + goto ret_zero; + case STRTOG_Normal: + case STRTOG_Denormal: + break; + case STRTOG_Infinite: + *decpt = -32768; + return nrv_alloc("Infinity", rve, 8); + case STRTOG_NaN: + *decpt = -32768; + return nrv_alloc("NaN", rve, 3); + default: + return 0; + } + b = bitstob(bits, nbits = fpi->nbits, &bbits); + be0 = be; + if ( (i = trailz(b)) !=0) { + rshift(b, i); + be += i; + bbits -= i; + } + if (!b->wds) { + Bfree(b); + ret_zero: + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + + dval(&d) = b2d(b, &i); + i = be + bbits - 1; + word0(&d) &= Frac_mask1; + word0(&d) |= Exp_11; + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(&d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(&d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + ds = (dval(&d)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + + /* correct assumption about exponent range */ + if ((j = i) < 0) + j = -j; + if ((j -= 1077) > 0) + ds += j * 7e-17; + + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + word0(&d) += (be + bbits - 1) << Exp_shift; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&d) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + try_quick = 1; + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + else if (i >= -4 - Emin || i < Emin) + try_quick = 0; + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = (int)(nbits * .30103) + 3; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + + if ( (rdir = fpi->rounding - 1) !=0) { + if (rdir < 0) + rdir = 2; + if (kind & STRTOG_Neg) + rdir = 3 - rdir; + } + + /* Now rdir = 0 ==> round near, 1 ==> round up, 2 ==> round down. */ + + if (ilim >= 0 && ilim <= Quick_max && try_quick && !rdir +#ifndef IMPRECISE_INEXACT + && k == 0 +#endif + ) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + d2 = dval(&d); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&d) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + } + else { + ds = 1.; + if ( (j2 = -k) !=0) { + dval(&d) *= tens[j2 & 0xf]; + for(j = j2 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&d) *= bigtens[i]; + } + } + } + if (k_check && dval(&d) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&d) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&d) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&d) -= 5.; + if (dval(&d) > dval(&eps)) + goto one_digit; + if (dval(&d) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = ds*0.5/tens[ilim-1] - dval(&eps); + for(i = 0;;) { + L = (Long)(dval(&d)/ds); + dval(&d) -= L*ds; + *s++ = '0' + (int)L; + if (dval(&d) < dval(&eps)) { + if (dval(&d)) + inex = STRTOG_Inexlo; + goto ret1; + } + if (ds - dval(&d) < dval(&eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&d) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&d) *= 10.) { + if ( (L = (Long)(dval(&d)/ds)) !=0) + dval(&d) -= L*ds; + *s++ = '0' + (int)L; + if (i == ilim) { + ds *= 0.5; + if (dval(&d) > ds + dval(&eps)) + goto bump_up; + else if (dval(&d) < ds - dval(&eps)) { + if (dval(&d)) + inex = STRTOG_Inexlo; + goto clear_trailing0; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&d) = d2; + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= fpi->int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&d) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&d) *= 10.) { + L = dval(&d) / ds; + dval(&d) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&d) < 0) { + L--; + dval(&d) += ds; + } +#endif + *s++ = '0' + (int)L; + if (dval(&d) == 0.) + break; + if (i == ilim) { + if (rdir) { + if (rdir == 1) + goto bump_up; + inex = STRTOG_Inexlo; + goto ret1; + } + dval(&d) += dval(&d); +#ifdef ROUND_BIASED + if (dval(&d) >= ds) +#else + if (dval(&d) > ds || (dval(&d) == ds && L & 1)) +#endif + { + bump_up: + inex = STRTOG_Inexhi; + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + else { + inex = STRTOG_Inexlo; + clear_trailing0: + while(*--s == '0'){} + ++s; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = nbits - bbits; + if (be - i++ < fpi->emin && mode != 3 && mode != 5) { + /* denormal */ + i = be - fpi->emin + 1; + if (mode >= 2 && ilim > 0 && ilim < i) + goto small_ilim; + } + else if (mode >= 2) { + small_ilim: + j = ilim - 1; + if (m5 >= j) + m5 -= j; + else { + s5 += j -= m5; + b5 += j; + m5 = 0; + } + if ((i = ilim) < 0) { + m2 -= i; + i = 0; + } + } + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ( (j = b5 - m5) !=0) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if (mode < 2) { + if (bbits == 1 && be0 > fpi->emin + 1) { + /* The special case */ + b2++; + s2++; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ + i = ((s5 ? hi0bits(S->x[S->wds-1]) : ULbits - 1) - s2 - 4) & kmask; + m2 += i; + if ((b2 += i) > 0) + b = lshift(b, b2); + if ((s2 += i) > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && mode > 2) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + inex = STRTOG_Inexlo; + goto ret; + } + one_digit: + inex = STRTOG_Inexhi; + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, 1); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j2 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j2 == 0 && !mode && !(bits[0] & 1) && !rdir) { + if (dig == '9') + goto round_9_up; + if (j <= 0) { + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + } + else { + dig++; + inex = STRTOG_Inexhi; + } + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && !mode +#ifndef ROUND_BIASED + && !(bits[0] & 1) +#endif + )) { + if (rdir && (b->wds > 1 || b->x[0])) { + if (rdir == 2) { + inex = STRTOG_Inexlo; + goto accept; + } + while (cmp(S,mhi) > 0) { + *s++ = dig; + mhi1 = multadd(mhi, 10, 0); + if (mlo == mhi) + mlo = mhi1; + mhi = mhi1; + b = multadd(b, 10, 0); + dig = quorem(b,S) + '0'; + } + if (dig++ == '9') + goto round_9_up; + inex = STRTOG_Inexhi; + goto accept; + } + if (j2 > 0) { + b = lshift(b, 1); + j2 = cmp(b, S); +#ifdef ROUND_BIASED + if (j2 >= 0 /*)*/ +#else + if ((j2 > 0 || (j2 == 0 && dig & 1)) +#endif + && dig++ == '9') + goto round_9_up; + inex = STRTOG_Inexhi; + } + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + accept: + *s++ = dig; + goto ret; + } + if (j2 > 0 && rdir != 2) { + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + inex = STRTOG_Inexhi; + goto roundoff; + } + inex = STRTOG_Inexhi; + *s++ = dig + 1; + goto ret; + } + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + + if (rdir) { + if (rdir == 2 || (b->wds <= 1 && !b->x[0])) + goto chopzeros; + goto roundoff; + } + b = lshift(b, 1); + j = cmp(b, S); +#ifdef ROUND_BIASED + if (j >= 0) +#else + if (j > 0 || (j == 0 && dig & 1)) +#endif + { + roundoff: + inex = STRTOG_Inexhi; + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { + chopzeros: + if (b->wds > 1 || b->x[0]) + inex = STRTOG_Inexlo; + while(*--s == '0'){} + ++s; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + *kindp |= inex; + return s0; +} diff --git a/src/lib/evil/gdtoa/gdtoa.h b/src/lib/evil/gdtoa/gdtoa.h new file mode 100644 index 0000000000..ef72bf99ed --- /dev/null +++ b/src/lib/evil/gdtoa/gdtoa.h @@ -0,0 +1,121 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* Modified by Danny Smith for inclusion in libmingwex.a + Aug 2006 */ + +/* Modified by Vincent Torri for inclusion in Evil + February 2012 */ + +#ifndef GDTOA_H_INCLUDED +#define GDTOA_H_INCLUDED + +#include "gd_arith.h" +#include <stddef.h> /* for size_t */ + +#if defined(__MINGW32__) || defined(__MINGW64__) +/* keep the 'Long' definition as 'long' for compatibility + * with older/other software. long in w64 is 32 bits anyway.. + */ +#define Long long /* Windows long is 32 bit */ +#undef NO_LONG_LONG /* we have long long type */ +#endif /* MinGW */ + +#ifndef Long +#define Long int +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif +#ifndef UShort +typedef unsigned short UShort; +#endif + +enum { /* return values from strtodg */ + STRTOG_Zero = 0, + STRTOG_Normal = 1, + STRTOG_Denormal = 2, + STRTOG_Infinite = 3, + STRTOG_NaN = 4, + STRTOG_NaNbits = 5, + STRTOG_NoNumber = 6, + STRTOG_Retmask = 7, + + /* The following may be or-ed into one of the above values. */ + + STRTOG_Neg = 0x08, /* does not affect STRTOG_Inexlo or STRTOG_Inexhi */ + STRTOG_Inexlo = 0x10, /* returned result rounded toward zero */ + STRTOG_Inexhi = 0x20, /* returned result rounded away from zero */ + STRTOG_Inexact = 0x30, + STRTOG_Underflow= 0x40, + STRTOG_Overflow = 0x80 +}; + +typedef struct +FPI { + int nbits; + int emin; + int emax; + int rounding; + int sudden_underflow; + int int_max; +} FPI; + +enum { /* FPI.rounding values: same as FLT_ROUNDS */ + FPI_Round_zero = 0, + FPI_Round_near = 1, + FPI_Round_up = 2, + FPI_Round_down = 3 +}; + +#ifdef __cplusplus +extern "C" { +#endif + +extern char* __dtoa (double d, int mode, int ndigits, int *decpt, + int *sign, char **rve); +extern char* __gdtoa (FPI *fpi, int be, ULong *bits, int *kindp, + int mode, int ndigits, int *decpt, char **rve); +extern void __freedtoa (char *); + +extern float __evil_strtof (const char *, char **); +extern long double __evil_strtold (const char *, char **); +extern int __strtodg (const char *, char **, FPI *, Long *, ULong *); + +extern char* __g__fmt (char*, char*, char*, int, ULong, size_t); +extern char* __g_dfmt (char*, double*, int, size_t); +extern char* __g_ffmt (char*, float*, int, size_t); +extern char* __g_xfmt (char*, void*, int, size_t); + +#ifdef __cplusplus +} +#endif +#endif /* GDTOA_H_INCLUDED */ diff --git a/src/lib/evil/gdtoa/gdtoa_fltrnds.h b/src/lib/evil/gdtoa/gdtoa_fltrnds.h new file mode 100644 index 0000000000..28c474e2c2 --- /dev/null +++ b/src/lib/evil/gdtoa/gdtoa_fltrnds.h @@ -0,0 +1,18 @@ + FPI *fpi, fpi1; + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ + fpi = &fpi0; + if (Rounding != 1) { + fpi1 = fpi0; + fpi = &fpi1; + fpi1.rounding = Rounding; + } diff --git a/src/lib/evil/gdtoa/gdtoaimp.h b/src/lib/evil/gdtoa/gdtoaimp.h new file mode 100644 index 0000000000..3a06c2072f --- /dev/null +++ b/src/lib/evil/gdtoa/gdtoaimp.h @@ -0,0 +1,645 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998-2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* This is a variation on dtoa.c that converts arbitary binary + floating-point formats to and from decimal notation. It uses + double-precision arithmetic internally, so there are still + various #ifdefs that adapt the calculations to the native + double-precision arithmetic (any of IEEE, VAX D_floating, + or IBM mainframe arithmetic). + + Please send bug reports to David M. Gay (dmg at acm dot org, + with " at " changed at "@" and " dot " changed to "."). + */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define Sudden_Underflow for IEEE-format machines without gradual + * underflow (i.e., that flush to zero on underflow). + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa and gdtoa. This will cause modes 4 and 5 to be + * treated the same as modes 2 and 3 for some inputs. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding and arithmetic + * that rounds toward +Infinity. + * #define ROUND_BIASED_without_Round_Up for IEEE-format with biased + * rounding when the underlying floating-point arithmetic uses + * unbiased rounding. This prevent using ordinary floating-point + * arithmetic when the result could be computed with one rounding error. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. When converting IEEE double precision values, the + * longest string gdtoa can return is about 751 bytes long. For + * conversions by strtod of strings of 800 digits and all gdtoa + * conversions of IEEE doubles in single-threaded executions with + * 8-byte pointers, PRIVATE_MEM >= 7400 appears to suffice; with + * 4-byte pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtodg also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits (optionally + * preceded by 0x or 0X) and spaces; if there is only one string + * of hexadecimal digits, it is taken for the fraction bits of the + * resulting NaN; if there are two or more strings of hexadecimal + * digits, each string is assigned to the next available sequence + * of 32-bit words of fractions bits (starting with the most + * significant), right-aligned in each sequence. + * Unless GDTOA_NON_PEDANTIC_NANCHECK is #defined, input "NaN(...)" + * is consumed even when ... has the wrong form (in which case the + * "(...)" is consumed but ignored). + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define IMPRECISE_INEXACT if you do not care about the setting of + * the STRTOG_Inexact bits in the special case of doing IEEE double + * precision conversions (which could also be done by the strtog in + * dtoa.c). + * #define NO_HEX_FP to disable recognition of C9x's hexadecimal + * floating-point constants. + * #define -DNO_ERRNO to suppress setting errno (in strtod.c and + * strtodg.c). + * #define NO_STRING_H to use private versions of memcpy. + * On some K&R systems, it may also be necessary to + * #define DECLARE_SIZE_T in this case. + * #define USE_LOCALE to use the current locale's decimal_point value. + */ + +#ifndef GDTOAIMP_H_INCLUDED +#define GDTOAIMP_H_INCLUDED +#include "gdtoa.h" +#include "gd_qnan.h" + +#if defined(__MINGW32__) || defined(__MINGW64__) +#define MULTIPLE_THREADS 1 +#define USE_LOCALE 1 +#define NO_LOCALE_CACHE 1 +#endif /* MinGW */ + +#ifdef Honor_FLT_ROUNDS +#include <fenv.h> +#endif + +#ifdef DEBUG +#include <stdio.h> +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include <stdlib.h> +#include <string.h> + +#ifdef MALLOC +extern void *MALLOC (size_t); +#else +#define MALLOC malloc +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#include <errno.h> + +#ifdef NO_ERRNO +#define SET_ERRNO(x) +#else +#define SET_ERRNO(x) \ + errno = (x) +#endif + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#define DBL_MAX 1.7976931348623157e+308 +#endif + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#define n_bigtens 2 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include <float.h> +#endif /* Bad_float_h */ + +#ifdef IEEE_Arith +#define Scale_Bit 0x10 +#define n_bigtens 5 +#endif + +#ifdef IBM +#define n_bigtens 3 +#endif + +#ifdef VAX +#define n_bigtens 2 +#endif + +#ifndef __MATH_H__ +#include <math.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union _dbl_union { double d; ULong L[2]; } dbl_union; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Bias 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#else /* ifndef IEEE_Arith */ +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Bias 65 +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Bias 129 +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#else +#ifdef ROUND_BIASED_without_Round_Up +#undef ROUND_BIASED +#define ROUND_BIASED +#endif +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +extern double rnd_prod(double, double), rnd_quot(double, double); +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#undef Pack_16 +#ifndef Pack_32 +#define Pack_32 +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +#define Pack_16 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#define ALL_ON 0xffffffff +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#define ALL_ON 0xffff +#endif + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 9 + +#define Bigint __Bigint +struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; +}; +typedef struct Bigint Bigint; + +#ifdef NO_STRING_H +#ifdef DECLARE_SIZE_T +typedef unsigned int size_t; +#endif +extern void memcpy_D2A (void*, const void*, size_t); +#define Bcopy(x,y) memcpy_D2A(&x->sign,&y->sign,y->wds*sizeof(ULong) + 2*sizeof(int)) +#else /* !NO_STRING_H */ +#define Bcopy(x,y) memcpy(&x->sign,&y->sign,y->wds*sizeof(ULong) + 2*sizeof(int)) +#endif /* NO_STRING_H */ + +#ifdef __GNUC__ +static inline int +__lo0bits_D2A (ULong *y) +{ + int ret = __builtin_ctz(*y); + *y = *y >> ret; + return ret; +} + +static inline int +__hi0bits_D2A (ULong y) +{ + return __builtin_clz(y); +} +#endif + +#define Balloc __Balloc_D2A +#define Bfree __Bfree_D2A +#define ULtoQ __ULtoQ_D2A +#define ULtof __ULtof_D2A +#define ULtod __ULtod_D2A +#define ULtodd __ULtodd_D2A +#define ULtox __ULtox_D2A +#define ULtoxL __ULtoxL_D2A +#define any_on __any_on_D2A +#define b2d __b2d_D2A +#define bigtens __bigtens_D2A +#define cmp __cmp_D2A +#define copybits __copybits_D2A +#define d2b __d2b_D2A +#define decrement __decrement_D2A +#define diff __diff_D2A +#define dtoa_result __dtoa_result_D2A +#define gethex __gethex_D2A +#define hexdig __hexdig_D2A +#define hexnan __hexnan_D2A +#define hi0bits_D2A __hi0bits_D2A +#define hi0bits(x) __hi0bits_D2A((ULong)(x)) +#define i2b __i2b_D2A +#define increment __increment_D2A +#define lo0bits __lo0bits_D2A +#define lshift __lshift_D2A +#define match __match_D2A +#define mult __mult_D2A +#define multadd __multadd_D2A +#define nrv_alloc __nrv_alloc_D2A +#define pow5mult __pow5mult_D2A +#define quorem __quorem_D2A +#define ratio __ratio_D2A +#define rshift __rshift_D2A +#define rv_alloc __rv_alloc_D2A +#define s2b __s2b_D2A +#define set_ones __set_ones_D2A +#define strcp_D2A __strcp_D2A +#define strcp __strcp_D2A +#define strtoIg __strtoIg_D2A +#define sum __sum_D2A +#define tens __tens_D2A +#define tinytens __tinytens_D2A +#define tinytens __tinytens_D2A +#define trailz __trailz_D2A +#define ulp __ulp_D2A + +#define hexdig_init_D2A __mingw_hexdig_init_D2A + +extern char *dtoa_result; +extern const double bigtens[], tens[], tinytens[]; +extern unsigned char hexdig[]; + +extern Bigint *Balloc (int); +extern void Bfree (Bigint*); +extern void ULtof (ULong*, ULong*, Long, int); +extern void ULtod (ULong*, ULong*, Long, int); +extern void ULtodd (ULong*, ULong*, Long, int); +extern void ULtoQ (ULong*, ULong*, Long, int); +extern void ULtox (UShort*, ULong*, Long, int); +extern void ULtoxL (ULong*, ULong*, Long, int); +extern ULong any_on (Bigint*, int); +extern double b2d (Bigint*, int*); +extern int cmp (Bigint*, Bigint*); +extern void copybits (ULong*, int, Bigint*); +extern Bigint *d2b (double, int*, int*); +extern void decrement (Bigint*); +extern Bigint *diff (Bigint*, Bigint*); +extern int gethex (const char**, FPI*, Long*, Bigint**, int); +extern void hexdig_init_D2A(void); +extern int hexnan (const char**, FPI*, ULong*); +#ifndef __GNUC__ +extern int hi0bits_D2A (ULong); +#endif +extern Bigint *i2b (int); +extern Bigint *increment (Bigint*); +#ifndef __GNUC__ +extern int lo0bits (ULong*); +#endif +extern Bigint *lshift (Bigint*, int); +extern int match (const char**, char*); +extern Bigint *mult (Bigint*, Bigint*); +extern Bigint *multadd (Bigint*, int, int); +extern char *nrv_alloc (char*, char **, int); +extern Bigint *pow5mult (Bigint*, int); +extern int quorem (Bigint*, Bigint*); +extern double ratio (Bigint*, Bigint*); +extern void rshift (Bigint*, int); +extern char *rv_alloc (int); +extern Bigint *s2b (const char*, int, int, ULong, int); +extern Bigint *set_ones (Bigint*, int); +extern char *strcp (char*, const char*); +extern Bigint *sum (Bigint*, Bigint*); +extern int trailz (Bigint*); +extern double ulp (dbl_union *); + +#ifdef __cplusplus +} +#endif +/* + * NAN_WORD0 and NAN_WORD1 are only referenced in strtod.c. Prior to + * 20050115, they used to be hard-wired here (to 0x7ff80000 and 0, + * respectively), but now are determined by compiling and running + * qnan.c to generate gd_qnan.h, which specifies d_QNAN0 and d_QNAN1. + * Formerly gdtoaimp.h recommended supplying suitable -DNAN_WORD0=... + * and -DNAN_WORD1=... values if necessary. This should still work. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + */ +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#ifndef NAN_WORD0 +#define NAN_WORD0 d_QNAN0 +#endif +#ifndef NAN_WORD1 +#define NAN_WORD1 d_QNAN1 +#endif +#else +#define _0 1 +#define _1 0 +#ifndef NAN_WORD0 +#define NAN_WORD0 d_QNAN1 +#endif +#ifndef NAN_WORD1 +#define NAN_WORD1 d_QNAN0 +#endif +#endif +#else +#undef INFNAN_CHECK +#endif + +#undef SI +#ifdef Sudden_Underflow +#define SI 1 +#else +#define SI 0 +#endif + +#endif /* GDTOAIMP_H_INCLUDED */ diff --git a/src/lib/evil/gdtoa/gethex.c b/src/lib/evil/gdtoa/gethex.c new file mode 100644 index 0000000000..276175453e --- /dev/null +++ b/src/lib/evil/gdtoa/gethex.c @@ -0,0 +1,340 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +int gethex (const char **sp, FPI *fpi, Long *expo, Bigint **bp, int sign) +{ + Bigint *b; + const unsigned char *decpt, *s0, *s, *s1; + int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret; + ULong L, lostbits, *x; + Long e, e1; +#ifdef USE_LOCALE + int i; + const unsigned char *decimalpoint; +#ifdef NO_LOCALE_CACHE + decimalpoint = (unsigned char *)localeconv()->decimal_point; +#else + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char *)localeconv()->decimal_point; + decimalpoint_cache = (unsigned char *) + MALLOC(strlen((char *)s0) + 1); + if (decimalpoint_cache) { + strcpy((char *)decimalpoint_cache, (char *)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + if (!hexdig['0']) + hexdig_init_D2A(); + *bp = 0; + havedig = 0; + s0 = *(const unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + return STRTOG_Zero; + if (big) { + if (esign) { + switch(fpi->rounding) { + case FPI_Round_up: + if (sign) + break; + goto ret_tiny; + case FPI_Round_down: + if (!sign) + break; + goto ret_tiny; + } + goto retz; + ret_tiny: + b = Balloc(0); + b->wds = 1; + b->x[0] = 1; + goto dret; + } + switch(fpi->rounding) { + case FPI_Round_near: + goto ovfl1; + case FPI_Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case FPI_Round_down: + if (sign) + goto ovfl1; + goto ret_big; + } + ret_big: + nbits = fpi->nbits; + n0 = n = nbits >> kshift; + if (nbits & kmask) + ++n; + for(j = n, k = 0; j >>= 1; ++k); + *bp = b = Balloc(k); + b->wds = n; + for(j = 0; j < n0; ++j) + b->x[j] = ALL_ON; + if (n > n0) + b->x[j] = ULbits >> (ULbits - (nbits & kmask)); + *expo = fpi->emin; + return STRTOG_Normal | STRTOG_Inexlo; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = fpi->nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n); + e -= n; + x = b->x; + } + if (e > fpi->emax) { + ovfl: + Bfree(b); + ovfl1: + SET_ERRNO(ERANGE); + return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; + } + irv = STRTOG_Normal; + if (e < fpi->emin) { + irv = STRTOG_Denormal; + n = fpi->emin - e; + if (n >= nbits) { + switch (fpi->rounding) { + case FPI_Round_near: + if (n == nbits && (n < 2 || any_on(b,n-1))) + goto one_bit; + break; + case FPI_Round_up: + if (!sign) + goto one_bit; + break; + case FPI_Round_down: + if (sign) { + one_bit: + x[0] = b->wds = 1; + dret: + *bp = b; + *expo = fpi->emin; + SET_ERRNO(ERANGE); + return STRTOG_Denormal | STRTOG_Inexhi + | STRTOG_Underflow; + } + } + Bfree(b); + retz: + SET_ERRNO(ERANGE); + return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow; + } + k = n - 1; + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; + nbits -= n; + rshift(b,n); + e = fpi->emin; + } + if (lostbits) { + up = 0; + switch(fpi->rounding) { + case FPI_Round_zero: + break; + case FPI_Round_near: + if (lostbits & 2 + && (lostbits | x[0]) & 1) + up = 1; + break; + case FPI_Round_up: + up = 1 - sign; + break; + case FPI_Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b); + x = b->x; + if (irv == STRTOG_Denormal) { + if (nbits == fpi->nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) + irv = STRTOG_Normal; + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > fpi->emax) + goto ovfl; + } + irv |= STRTOG_Inexhi; + } + else + irv |= STRTOG_Inexlo; + } + *bp = b; + *expo = e; + return irv; +} diff --git a/src/lib/evil/gdtoa/gmisc.c b/src/lib/evil/gdtoa/gmisc.c new file mode 100644 index 0000000000..157e872319 --- /dev/null +++ b/src/lib/evil/gdtoa/gmisc.c @@ -0,0 +1,76 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +void rshift (Bigint *b, int k) +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = ULbits - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & ALL_ON; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; +} + +int trailz (Bigint *b) +{ + ULong L, *x, *xe; + int n = 0; + + x = b->x; + xe = x + b->wds; + for(n = 0; x < xe && !*x; x++) + n += ULbits; + if (x < xe) { + L = *x; + n += lo0bits(&L); + } + return n; +} diff --git a/src/lib/evil/gdtoa/hd_init.c b/src/lib/evil/gdtoa/hd_init.c new file mode 100644 index 0000000000..5ee0caa73d --- /dev/null +++ b/src/lib/evil/gdtoa/hd_init.c @@ -0,0 +1,49 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +unsigned char hexdig[256]; + +static void htinit (unsigned char *h, unsigned char *s, int inc) +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; +} + +void hexdig_init_D2A (void) +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); +} diff --git a/src/lib/evil/gdtoa/hexnan.c b/src/lib/evil/gdtoa/hexnan.c new file mode 100644 index 0000000000..4fa4c77458 --- /dev/null +++ b/src/lib/evil/gdtoa/hexnan.c @@ -0,0 +1,139 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +static void L_shift (ULong *x, ULong *x1, int i) +{ + int j; + + i = 8 - i; + i <<= 2; + j = ULbits - i; + do { + *x |= x[1] << j; + x[1] >>= i; + } while(++x < x1); +} + +int hexnan (const char **sp, FPI *fpi, ULong *x0) +{ + ULong c, h, *x, *x1, *xe; + const char *s; + int havedig, hd0, i, nbits; + + if (!hexdig['0']) + hexdig_init_D2A(); + nbits = fpi->nbits; + x = x0 + (nbits >> kshift); + if (nbits & kmask) + x++; + *--x = 0; + x1 = xe = x; + havedig = hd0 = i = 0; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(const unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X') + && *(const unsigned char*)(s+3) > ' ') + s += 2; + while((c = *(const unsigned char*)++s)) { + if (!(h = hexdig[c])) { + if (c <= ' ') { + if (hd0 < havedig) { + if (x < x1 && i < 8) + L_shift(x, x1, i); + if (x <= x0) { + i = 8; + continue; + } + hd0 = havedig; + *--x = 0; + x1 = x; + i = 0; + } + while(*(const unsigned char*)(s+1) <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X') + && *(const unsigned char*)(s+3) > ' ') + s += 2; + continue; + } + if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } +#ifndef GDTOA_NON_PEDANTIC_NANCHECK + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); +#endif + return STRTOG_NaN; + } + havedig++; + if (++i > 8) { + if (x <= x0) + continue; + i = 1; + *--x = 0; + } + *x = (*x << 4) | (h & 0xf); + } + if (!havedig) + return STRTOG_NaN; + if (x < x1 && i < 8) + L_shift(x, x1, i); + if (x > x0) { + x1 = x0; + do *x1++ = *x++; + while(x <= xe); + do *x1++ = 0; + while(x1 <= xe); + } + else { + /* truncate high-order word if necessary */ + if ( (i = nbits & (ULbits-1)) !=0) + *xe &= ((ULong)0xffffffff) >> (ULbits - i); + } + for(x1 = xe;; --x1) { + if (*x1 != 0) + break; + if (x1 == x0) { + *x1 = 1; + break; + } + } + return STRTOG_NaNbits; +} diff --git a/src/lib/evil/gdtoa/misc.c b/src/lib/evil/gdtoa/misc.c new file mode 100644 index 0000000000..43f31cea55 --- /dev/null +++ b/src/lib/evil/gdtoa/misc.c @@ -0,0 +1,860 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + + +#if defined(__MINGW32__) || defined(__MINGW64__) +/* we have to include windows.h before gdtoa + headers, otherwise defines cause conflicts. */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> + +#define NLOCKS 2 + +#ifdef USE_WIN32_SL +/* Use spin locks. */ +static long dtoa_sl[NLOCKS]; + +#define ACQUIRE_DTOA_LOCK(n) \ + while (InterlockedCompareExchange (&dtoa_sl[n], 1, 0) != 0) \ + Sleep (0); +#define FREE_DTOA_LOCK(n) InterlockedExchange (&dtoa_sl[n], 0); + +#else /* USE_WIN32_SL */ + +#include <stdlib.h> +static CRITICAL_SECTION dtoa_CritSec[NLOCKS]; +static long dtoa_CS_init = 0; +/* + 1 = initializing + 2 = initialized + 3 = deleted +*/ +static void dtoa_lock_cleanup (void) +{ + long last_CS_init = InterlockedExchange (&dtoa_CS_init,3); + if (2 == last_CS_init) { + int i; + for (i = 0; i < NLOCKS; i++) + DeleteCriticalSection (&dtoa_CritSec[i]); + } +} + +static void dtoa_lock (int n) +{ + if (2 == dtoa_CS_init) { + EnterCriticalSection (&dtoa_CritSec[n]); + return; + } + else if (0 == dtoa_CS_init) { + long last_CS_init = InterlockedExchange (&dtoa_CS_init, 1); + if (0 == last_CS_init) { + int i; + for (i = 0; i < NLOCKS; i++) + InitializeCriticalSection (&dtoa_CritSec[i]); + atexit (dtoa_lock_cleanup); + dtoa_CS_init = 2; + } + else if (2 == last_CS_init) + dtoa_CS_init = 2; + } + /* Another thread is initializing. Wait. */ + while (1 == dtoa_CS_init) + Sleep (1); + + /* It had better be initialized now. */ + if (2 == dtoa_CS_init) + EnterCriticalSection(&dtoa_CritSec[n]); +} + +static void dtoa_unlock (int n) +{ + if (2 == dtoa_CS_init) + LeaveCriticalSection (&dtoa_CritSec[n]); +} + +#define ACQUIRE_DTOA_LOCK(n) dtoa_lock(n) +#define FREE_DTOA_LOCK(n) dtoa_unlock(n) +#endif /* USE_WIN32_SL */ + +#endif /* __MINGW32__ / __MINGW64__ */ + +#include "gdtoaimp.h" + +static Bigint *freelist[Kmax+1]; +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +Bigint *Balloc (int k) +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k]) !=0) { + freelist[k] = rv->next; + } + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); + if (rv == NULL) + return NULL; +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax + && (size_t) (pmem_next - private_mem + len) <= PRIVATE_mem) + { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + { + rv = (Bigint*)MALLOC(len*sizeof(double)); + if (rv == NULL) + return NULL; + } +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; +} + +void Bfree (Bigint *v) +{ + if (v) { + if (v->k > Kmax) + free((void*)v); + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } +} + +/* lo0bits(): Shift y so lowest bit is 1 and return the + * number of bits y was shifted. + * With GCC, we use an inline wrapper for __builtin_clz() + */ +#ifndef __GNUC__ +int lo0bits (ULong *y) +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; +} +#endif /* __GNUC__ */ + +Bigint *multadd (Bigint *b, int m, int a) /* multiply by m and add a */ +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & 0xffffffffUL; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + if (b1 == NULL) + return NULL; + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; +} + +/* hi0bits(); + * With GCC, we use an inline wrapper for __builtin_clz() + */ +#ifndef __GNUC__ +int hi0bits_D2A (ULong x) +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; +} +#endif /* __GNUC__ */ + +Bigint *i2b (int i) +{ + Bigint *b; + + b = Balloc(1); + if (b == NULL) + return NULL; + b->x[0] = i; + b->wds = 1; + return b; +} + +Bigint *mult (Bigint *a, Bigint *b) +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + if (c == NULL) + return NULL; + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ( (y = *xb++) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & 0xffffffffUL; + } while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if ( (y = *xb & 0xffff) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } while(x < xae); + *xc = carry; + } + if ( (y = *xb >> 16) !=0) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if ( (y = *xb++) !=0) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; +} + +static Bigint *p5s; + +Bigint *pow5mult (Bigint *b, int k) +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ( (i = k & 3) !=0) + { + b = multadd(b, p05[i-1], 0); + if (b == NULL) + return NULL; + } + if (!(k >>= 2)) + return b; + if ((p5 = p5s) == 0) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + if (p5 == NULL) + return NULL; + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + if (p5 == NULL) + return NULL; + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + if (b1 == NULL) + return NULL; + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if ((p51 = p5->next) == 0) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + if (p51 == NULL) + return NULL; + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + if (p51 == NULL) + return NULL; + p51->next = 0; +#endif + } + p5 = p51; + } + return b; +} + +Bigint *lshift (Bigint *b, int k) +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + + n = k >> kshift; + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + if (b1 == NULL) + return NULL; + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; + if (k &= kmask) { +#ifdef Pack_32 + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } while(x < xe); + if ((*x1 = z) !=0) + ++n1; +#else + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } while(x < xe); + if (*x1 = z) + ++n1; +#endif + } + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; +} + +int cmp (Bigint *a, Bigint *b) +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; +} + +Bigint *diff (Bigint *a, Bigint *b) +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + if (c == NULL) + return NULL; + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + if (c == NULL) + return NULL; + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = y & 0xffffffffUL; + } while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & 1UL; + *xc++ = y & 0xffffffffUL; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; +} + +double b2d (Bigint *a, int *e) +{ + ULong *xa, *xa0, w, y, z; + int k; + union _dbl_union d; +#define d0 word0(&d) +#define d1 word1(&d) + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: + return dval(&d); +#undef d0 +#undef d1 +} + +Bigint *d2b (double dd, int *e, int *bits) +{ + Bigint *b; + union _dbl_union d; +#ifndef Sudden_Underflow + int i; +#endif + int de, k; + ULong *x, y, z; +#define d0 word0(&d) +#define d1 word1(&d) + d.d = dd; + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + if (b == NULL) + return NULL; + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); + z |= Exp_msk11; +#else + if ( (de = (int)(d0 >> Exp_shift)) !=0) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ( (y = d1) !=0) { + if ( (k = lo0bits(&y)) !=0) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) !=0 ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if ( (y = d1) !=0) { + if ( (k = lo0bits(&y)) !=0) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif + *e = de - Bias - (P-1) + k; + *bits = P - k; +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; +#undef d0 +#undef d1 +} + +const double +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; + +const double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +}; + +char *strcp_D2A (char *a, const char *b) +{ + while((*a = *b++)) + a++; + return a; +} + +#ifdef NO_STRING_H +void *memcpy_D2A (void *a1, void *b1, size_t len) +{ + char *a = (char*)a1, *ae = a + len; + char *b = (char*)b1, *a0 = a; + while(a < ae) + *a++ = *b++; + return a0; +} +#endif /* NO_STRING_H */ + diff --git a/src/lib/evil/gdtoa/qnan.c b/src/lib/evil/gdtoa/qnan.c new file mode 100644 index 0000000000..c6bc3fe49b --- /dev/null +++ b/src/lib/evil/gdtoa/qnan.c @@ -0,0 +1,116 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 2005 by David M. Gay +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that the copyright notice and this permission notice and warranty +disclaimer appear in supporting documentation, and that the name of +the author or any of his current or former employers not be used in +advertising or publicity pertaining to distribution of the software +without specific, written prior permission. + +THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN +NO EVENT SHALL THE AUTHOR OR ANY OF HIS CURRENT OR FORMER EMPLOYERS BE +LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* Program to compute quiet NaNs of various precisions (float, */ +/* double, and perhaps long double) on the current system, */ +/* provided the system uses binary IEEE (P754) arithmetic. */ +/* Note that one system's quiet NaN may be a signaling NaN on */ +/* another system. The IEEE arithmetic standards (P754, P854) */ +/* do not specify how to distinguish signaling NaNs from quiet */ +/* ones, and this detail varies across systems. The computed */ +/* NaN values are encoded in #defines for values for an */ +/* unsigned 32-bit integer type, called Ulong below, and */ +/* (for long double) perhaps as unsigned short values. Once */ +/* upon a time, there were PC compilers for Intel CPUs that */ +/* had sizeof(long double) = 10. Are such compilers still */ +/* distributed? */ + +#include <stdio.h> +#include "gd_arith.h" + +#ifndef Long +#define Long long +#endif + +typedef unsigned Long Ulong; + +#undef HAVE_IEEE +#ifdef IEEE_8087 +#define _0 1 +#define _1 0 +#define HAVE_IEEE +#endif +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define HAVE_IEEE +#endif + +#define UL (unsigned long) + +#ifdef MINGW_BUILD_GEN + int +main(void) +{ +#ifdef HAVE_IEEE + typedef union { + float f; + double d; + Ulong L[4]; +#ifndef NO_LONG_LONG +/* need u[8] instead of u[5] for 64 bit */ + unsigned short u[8]; + long double D; +#endif + } U; + U a, b, c; + int i; + a.L[0]=a.L[1]=a.L[2]=a.L[3]=0; + b.L[0]=b.L[1]=b.L[2]=b.L[3]=0; + c.L[0]=c.L[1]=c.L[2]=c.L[3]=0; + + a.L[0] = b.L[0] = 0x7f800000; + c.f = a.f - b.f; + printf("#define f_QNAN 0x%lx\n", UL c.L[0]); + a.L[_0] = b.L[_0] = 0x7ff00000; + a.L[_1] = b.L[_1] = 0; + c.d = a.d - b.d; /* quiet NaN */ + printf("#define d_QNAN0 0x%lx\n", UL c.L[0]); + printf("#define d_QNAN1 0x%lx\n", UL c.L[1]); +#ifdef NO_LONG_LONG + for(i = 0; i < 4; i++) + printf("#define ld_QNAN%d 0xffffffff\n", i); + for(i = 0; i < 5; i++) + printf("#define ldus_QNAN%d 0xffff\n", i); +#else + b.D = c.D = a.d; + if (printf("") < 0) + c.D = 37; /* never executed; just defeat optimization */ + a.L[2] = a.L[3] = 0; + a.D = b.D - c.D; + for(i = 0; i < 4; i++) + printf("#define ld_QNAN%d 0x%lx\n", i, UL a.L[i]); + for(i = 0; i < 5; i++) + printf("#define ldus_QNAN%d 0x%x\n", i, a.u[i]); +#endif +#endif /* HAVE_IEEE */ + return 0; + } +#endif diff --git a/src/lib/evil/gdtoa/smisc.c b/src/lib/evil/gdtoa/smisc.c new file mode 100644 index 0000000000..1d09b44ea1 --- /dev/null +++ b/src/lib/evil/gdtoa/smisc.c @@ -0,0 +1,149 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +Bigint *s2b (const char *s, int nd0, int nd, ULong y9, int dplen) +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; +} + +double ratio (Bigint *a, Bigint *b) +{ + union _dbl_union da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); + k = ka - kb + ULbits*(a->wds - b->wds); + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } + return dval(&da) / dval(&db); +} + +#ifdef INFNAN_CHECK + +int match (const char **sp, char *t) +{ + int c, d; + const char *s = *sp; + + while( (d = *t++) !=0) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; +} +#endif /* INFNAN_CHECK */ + +void copybits (ULong *c, int n, Bigint *b) +{ + ULong *ce, *x, *xe; +#ifdef Pack_16 + int nw, nw1; +#endif + + ce = c + ((n-1) >> kshift) + 1; + x = b->x; +#ifdef Pack_32 + xe = x + b->wds; + while(x < xe) + *c++ = *x++; +#else + nw = b->wds; + nw1 = nw & 1; + for(xe = x + (nw - nw1); x < xe; x += 2) + Storeinc(c, x[1], x[0]); + if (nw1) + *c++ = *x; +#endif + while(c < ce) + *c++ = 0; +} + +ULong any_on (Bigint *b, int k) +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; +} diff --git a/src/lib/evil/gdtoa/strtodg.c b/src/lib/evil/gdtoa/strtodg.c new file mode 100644 index 0000000000..42ab9df3a2 --- /dev/null +++ b/src/lib/evil/gdtoa/strtodg.c @@ -0,0 +1,979 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998-2001 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +static const int +fivesbits[] = { 0, 3, 5, 7, 10, 12, 14, 17, 19, 21, + 24, 26, 28, 31, 33, 35, 38, 40, 42, 45, + 47, 49, 52 +}; + +Bigint *increment (Bigint *b) +{ + ULong *x, *xe; + Bigint *b1; +#ifdef Pack_16 + ULong carry = 1, y; +#endif + + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); +#else + do { + y = *x + carry; + carry = y >> 16; + *x++ = y & 0xffff; + if (!carry) + return b; + } while(x < xe); + if (carry) +#endif + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1,b); + Bfree(b); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; +} + +void decrement (Bigint *b) +{ + ULong *x, *xe; +#ifdef Pack_16 + ULong borrow = 1, y; +#endif + + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + do { + if (*x) { + --*x; + break; + } + *x++ = 0xffffffffL; + } while(x < xe); +#else + do { + y = *x - borrow; + borrow = (y & 0x10000) >> 16; + *x++ = y & 0xffff; + } while(borrow && x < xe); +#endif +} + +static int all_on (Bigint *b, int n) +{ + ULong *x, *xe; + + x = b->x; + xe = x + (n >> kshift); + while(x < xe) + if ((*x++ & ALL_ON) != ALL_ON) + return 0; + if (n &= kmask) + return ((*x | (ALL_ON << n)) & ALL_ON) == ALL_ON; + return 1; +} + +Bigint *set_ones (Bigint *b, int n) +{ + int k; + ULong *x, *xe; + + k = (n + ((1 << kshift) - 1)) >> kshift; + if (b->k < k) { + Bfree(b); + b = Balloc(k); + } + k = n >> kshift; + if (n &= kmask) + k++; + b->wds = k; + x = b->x; + xe = x + k; + while(x < xe) + *x++ = ALL_ON; + if (n) + x[-1] >>= ULbits - n; + return b; +} + +static int rvOK (dbl_union *d, FPI *fpi, Long *expo, ULong *bits, + int exact, int rd, int *irv) +{ + Bigint *b; + ULong carry, inex, lostbits; + int bdif, e, j, k, k1, nb, rv; + + carry = rv = 0; + b = d2b(dval(d), &e, &bdif); + bdif -= nb = fpi->nbits; + e += bdif; + if (bdif <= 0) { + if (exact) + goto trunc; + goto ret; + } + if (P == nb) { + if ( +#ifndef IMPRECISE_INEXACT + exact && +#endif + fpi->rounding == +#ifdef RND_PRODQUOT + FPI_Round_near +#else + Flt_Rounds +#endif + ) goto trunc; + goto ret; + } + switch(rd) { + case 1: /* round down (toward -Infinity) */ + goto trunc; + case 2: /* round up (toward +Infinity) */ + break; + default: /* round near */ + k = bdif - 1; + if (k < 0) + goto trunc; + if (!k) { + if (!exact) + goto ret; + if (b->x[0] & 2) + break; + goto trunc; + } + if (b->x[k>>kshift] & ((ULong)1 << (k & kmask))) + break; + goto trunc; + } + /* "break" cases: round up 1 bit, then truncate; bdif > 0 */ + carry = 1; + trunc: + inex = lostbits = 0; + if (bdif > 0) { + if ( (lostbits = any_on(b, bdif)) !=0) + inex = STRTOG_Inexlo; + rshift(b, bdif); + if (carry) { + inex = STRTOG_Inexhi; + b = increment(b); + if ( (j = nb & kmask) !=0) + j = ULbits - j; + if (hi0bits(b->x[b->wds - 1]) != j) { + if (!lostbits) + lostbits = b->x[0] & 1; + rshift(b, 1); + e++; + } + } + } + else if (bdif < 0) + b = lshift(b, -bdif); + if (e < fpi->emin) { + k = fpi->emin - e; + e = fpi->emin; + if (k > nb || fpi->sudden_underflow) { + b->wds = inex = 0; + *irv = STRTOG_Underflow | STRTOG_Inexlo; + } + else { + k1 = k - 1; + if (k1 > 0 && !lostbits) + lostbits = any_on(b, k1); + if (!lostbits && !exact) + goto ret; + lostbits |= + carry = b->x[k1>>kshift] & (1 << (k1 & kmask)); + rshift(b, k); + *irv = STRTOG_Denormal; + if (carry) { + b = increment(b); + inex = STRTOG_Inexhi | STRTOG_Underflow; + } + else if (lostbits) + inex = STRTOG_Inexlo | STRTOG_Underflow; + } + } + else if (e > fpi->emax) { + e = fpi->emax + 1; + *irv = STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; + SET_ERRNO(ERANGE); + b->wds = inex = 0; + } + *expo = e; + copybits(bits, nb, b); + *irv |= inex; + rv = 1; + ret: + Bfree(b); + return rv; +} + +static int mantbits (dbl_union *d) +{ + ULong L; + if ( (L = word1(d)) !=0) + return P - lo0bits(&L); + L = word0(d) | Exp_msk1; + return P - 32 - lo0bits(&L); +} + +int __strtodg (const char *s00, char **se, FPI *fpi, Long *expo, ULong *bits) +{ + int abe, abits, asub; + int bb0, bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, decpt, denorm; + int dsign, e, e1, e2, emin, esign, finished, i, inex, irv; + int j, k, nbits, nd, nd0, nf, nz, nz0, rd, rvbits, rve, rve1, sign; + int sudden_underflow; + const char *s, *s0, *s1; + double adj0, tol; + Long L; + union _dbl_union adj, rv; + ULong *b, *be, y, z; + Bigint *ab, *bb, *bb1, *bd, *bd0, *bs, *delta, *rvb, *rvb0; +#ifdef USE_LOCALE /*{{*/ +#ifdef NO_LOCALE_CACHE + char *decimalpoint = localeconv()->decimal_point; + int dplen = strlen(decimalpoint); +#else + char *decimalpoint; + static char *decimalpoint_cache; + static int dplen; + if (!(s0 = decimalpoint_cache)) { + s0 = localeconv()->decimal_point; + if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) { + strcpy(decimalpoint_cache, s0); + s0 = decimalpoint_cache; + } + dplen = strlen(s0); + } + decimalpoint = (char*)s0; +#endif /*NO_LOCALE_CACHE*/ +#else /*USE_LOCALE}{*/ +#define dplen 1 +#endif /*USE_LOCALE}}*/ + + irv = STRTOG_Zero; + denorm = sign = nz0 = nz = 0; + dval(&rv) = 0.; + rvb = 0; + nbits = fpi->nbits; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + sign = 0; + irv = STRTOG_NoNumber; + s = s00; + goto ret; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP + switch(s[1]) { + case 'x': + case 'X': + irv = gethex(&s, fpi, expo, &rvb, sign); + if (irv == STRTOG_NoNumber) { + s = s00; + sign = 0; + } + goto ret; + } +#endif + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + sudden_underflow = fpi->sudden_underflow; + s0 = s; + y = z = 0; + for(decpt = nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; +#ifdef USE_LOCALE + if (c == *decimalpoint) { + for(i = 1; decimalpoint[i]; ++i) + if (s[i] != decimalpoint[i]) + goto dig_done; + s += i; + c = *s; +#else + if (c == '.') { + c = *++s; +#endif + decpt = 1; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + }/*}*/ + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + irv = STRTOG_NoNumber; + s = s00; + goto ret; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!decpt) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + irv = STRTOG_Infinite; + goto infnanexp; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + irv = STRTOG_NaN; + *expo = fpi->emax + 1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + irv = hexnan(&s, fpi, bits); +#endif + goto infnanexp; + } + } +#endif /* INFNAN_CHECK */ + irv = STRTOG_NoNumber; + s = s00; + } + goto ret; + } + + irv = STRTOG_Normal; + e1 = e -= nf; + rd = 0; + switch(fpi->rounding & 3) { + case FPI_Round_up: + rd = 2 - sign; + break; + case FPI_Round_zero: + rd = 1; + break; + case FPI_Round_down: + rd = 1 + sign; + } + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) + dval(&rv) = tens[k - 9] * dval(&rv) + z; + bd0 = 0; + if (nbits <= P && nd <= DBL_DIG) { + if (!e) { + if (rvOK(&rv, fpi, expo, bits, 1, rd, &irv)) + goto ret; + } + else if (e > 0) { + if (e <= Ten_pmax) { + i = fivesbits[e] + mantbits(&rv) <= P; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if (rvOK(&rv, fpi, expo, bits, i, rd, &irv)) + goto ret; + e1 -= e; + goto rv_notOK; + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ + e2 = e - i; + e1 -= i; + dval(&rv) *= tens[i]; + /* rv = */ rounded_product(dval(&rv), tens[e2]); + if (rvOK(&rv, fpi, expo, bits, 0, rd, &irv)) + goto ret; + e1 -= e2; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + if (rvOK(&rv, fpi, expo, bits, 0, rd, &irv)) + goto ret; + e1 -= e; + } +#endif + } + rv_notOK: + e1 += nd - k; + + /* Get starting approximation = rv * 10**e1 */ + + e2 = 0; + if (e1 > 0) { + if ( (i = e1 & 15) !=0) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + while(e1 >= (1 << (n_bigtens-1))) { + e2 += ((word0(&rv) & Exp_mask) + >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + dval(&rv) *= bigtens[n_bigtens-1]; + e1 -= 1 << (n_bigtens-1); + } + e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + } + } + else if (e1 < 0) { + e1 = -e1; + if ( (i = e1 & 15) !=0) + dval(&rv) /= tens[i]; + if (e1 &= ~15) { + e1 >>= 4; + while(e1 >= (1 << (n_bigtens-1))) { + e2 += ((word0(&rv) & Exp_mask) + >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + dval(&rv) *= tinytens[n_bigtens-1]; + e1 -= 1 << (n_bigtens-1); + } + e2 += ((word0(&rv) & Exp_mask) >> Exp_shift1) - Bias; + word0(&rv) &= ~Exp_mask; + word0(&rv) |= Bias << Exp_shift1; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + } + } + rvb = d2b(dval(&rv), &rve, &rvbits); /* rv = rvb * 2^rve */ + rve += e2; + if ((j = rvbits - nbits) > 0) { + rshift(rvb, j); + rvbits = nbits; + rve += j; + } + bb0 = 0; /* trailing zero bits in rvb */ + e2 = rve + rvbits - nbits; + if (e2 > fpi->emax + 1) + goto huge; + rve1 = rve + rvbits - nbits; + if (e2 < (emin = fpi->emin)) { + denorm = 1; + j = rve - emin; + if (j > 0) { + rvb = lshift(rvb, j); + rvbits += j; + } + else if (j < 0) { + rvbits += j; + if (rvbits <= 0) { + if (rvbits < -1) { + ufl: + rvb->wds = 0; + rvb->x[0] = 0; + *expo = emin; + irv = STRTOG_Underflow | STRTOG_Inexlo; + goto ret; + } + rvb->x[0] = rvb->wds = rvbits = 1; + } + else + rshift(rvb, -j); + } + rve = rve1 = emin; + if (sudden_underflow && e2 + 1 < emin) + goto ufl; + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bd0 = s2b(s0, nd0, nd, y, dplen); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = Balloc(rvb->k); + Bcopy(bb, rvb); + bbbits = rvbits - bb0; + bbe = rve + bb0; + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; + j = nbits + 1 - bbbits; + i = bbe + bbbits - nbits; + if (i < emin) /* denormal */ + j += i - emin; + bb2 += j; + bd2 += j; + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + bb2 -= bb0; + if (bb2 > 0) + bb = lshift(bb, bb2); + else if (bb2 < 0) + rshift(bb, -bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + asub = 1; + inex = STRTOG_Inexhi; + delta = diff(bb, bd); + if (delta->wds <= 1 && !delta->x[0]) + break; + dsign = delta->sign; + delta->sign = finished = 0; + L = 0; + i = cmp(delta, bs); + if (rd && i <= 0) { + irv = STRTOG_Normal; + if ( (finished = dsign ^ (rd&1)) !=0) { + if (dsign != 0) { + irv |= STRTOG_Inexhi; + goto adj1; + } + irv |= STRTOG_Inexlo; + if (rve1 == emin) + goto adj1; + for(i = 0, j = nbits; j >= ULbits; + i++, j -= ULbits) { + if (rvb->x[i] & ALL_ON) + goto adj1; + } + if (j > 1 && lo0bits(rvb->x + i) < j - 1) + goto adj1; + rve = rve1 - 1; + rvb = set_ones(rvb, rvbits = nbits); + break; + } + irv |= dsign ? STRTOG_Inexlo : STRTOG_Inexhi; + break; + } + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + irv = dsign + ? STRTOG_Normal | STRTOG_Inexlo + : STRTOG_Normal | STRTOG_Inexhi; + if (dsign || bbbits > 1 || denorm || rve1 == emin) + break; + delta = lshift(delta,1); + if (cmp(delta, bs) > 0) { + irv = STRTOG_Normal | STRTOG_Inexlo; + goto drop_down; + } + break; + } + if (i == 0) { + /* exactly half-way between */ + if (dsign) { + if (denorm && all_on(rvb, rvbits)) { + /*boundary case -- increment exponent*/ + rvb->wds = 1; + rvb->x[0] = 1; + rve = emin + nbits - (rvbits = 1); + irv = STRTOG_Normal | STRTOG_Inexhi; + denorm = 0; + break; + } + irv = STRTOG_Normal | STRTOG_Inexlo; + } + else if (bbbits == 1) { + irv = STRTOG_Normal; + drop_down: + /* boundary case -- decrement exponent */ + if (rve1 == emin) { + irv = STRTOG_Normal | STRTOG_Inexhi; + if (rvb->wds == 1 && rvb->x[0] == 1) + sudden_underflow = 1; + break; + } + rve -= nbits; + rvb = set_ones(rvb, rvbits = nbits); + break; + } + else + irv = STRTOG_Normal | STRTOG_Inexhi; + if ((bbbits < nbits && !denorm) || !(rvb->x[0] & 1)) + break; + if (dsign) { + rvb = increment(rvb); + j = kmask & (ULbits - (rvbits & kmask)); + if (hi0bits(rvb->x[rvb->wds - 1]) != j) + rvbits++; + irv = STRTOG_Normal | STRTOG_Inexhi; + } + else { + if (bbbits == 1) + goto undfl; + decrement(rvb); + irv = STRTOG_Normal | STRTOG_Inexlo; + } + break; + } + if ((dval(&adj) = ratio(delta, bs)) <= 2.) { + adj1: + inex = STRTOG_Inexlo; + if (dsign) { + asub = 0; + inex = STRTOG_Inexhi; + } + else if (denorm && bbbits <= 1) { + undfl: + rvb->wds = 0; + rve = emin; + irv = STRTOG_Underflow | STRTOG_Inexlo; + break; + } + adj0 = dval(&adj) = 1.; + } + else { + adj0 = dval(&adj) *= 0.5; + if (dsign) { + asub = 0; + inex = STRTOG_Inexlo; + } + if (dval(&adj) < 2147483647.) { + L = adj0; + adj0 -= L; + switch(rd) { + case 0: + if (adj0 >= .5) + goto inc_L; + break; + case 1: + if (asub && adj0 > 0.) + goto inc_L; + break; + case 2: + if (!asub && adj0 > 0.) { + inc_L: + L++; + inex = STRTOG_Inexact - inex; + } + } + dval(&adj) = L; + } + } + y = rve + rvbits; + + /* adj *= ulp(&rv); */ + /* if (asub) rv -= adj; else rv += adj; */ + + if (!denorm && rvbits < nbits) { + rvb = lshift(rvb, j = nbits - rvbits); + rve -= j; + rvbits = nbits; + } + ab = d2b(dval(&adj), &abe, &abits); + if (abe < 0) + rshift(ab, -abe); + else if (abe > 0) + ab = lshift(ab, abe); + rvb0 = rvb; + if (asub) { + /* rv -= adj; */ + j = hi0bits(rvb->x[rvb->wds-1]); + rvb = diff(rvb, ab); + k = rvb0->wds - 1; + if (denorm) + /* do nothing */; + else if (rvb->wds <= k + || hi0bits( rvb->x[k]) > + hi0bits(rvb0->x[k])) { + /* unlikely; can only have lost 1 high bit */ + if (rve1 == emin) { + --rvbits; + denorm = 1; + } + else { + rvb = lshift(rvb, 1); + --rve; + --rve1; + L = finished = 0; + } + } + } + else { + rvb = sum(rvb, ab); + k = rvb->wds - 1; + if (k >= rvb0->wds + || hi0bits(rvb->x[k]) < hi0bits(rvb0->x[k])) { + if (denorm) { + if (++rvbits == nbits) + denorm = 0; + } + else { + rshift(rvb, 1); + rve++; + rve1++; + L = 0; + } + } + } + Bfree(ab); + Bfree(rvb0); + if (finished) + break; + + z = rve + rvbits; + if (y == z && L) { + /* Can we stop now? */ + tol = dval(&adj) * 5e-16; /* > max rel error */ + dval(&adj) = adj0 - .5; + if (dval(&adj) < -tol) { + if (adj0 > tol) { + irv |= inex; + break; + } + } + else if (dval(&adj) > tol && adj0 < 1. - tol) { + irv |= inex; + break; + } + } + bb0 = denorm ? 0 : trailz(rvb); + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + if (!denorm && (j = nbits - rvbits)) { + if (j > 0) + rvb = lshift(rvb, j); + else + rshift(rvb, -j); + rve -= j; + } + *expo = rve; + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); + if (rve > fpi->emax) { + switch(fpi->rounding & 3) { + case FPI_Round_near: + goto huge; + case FPI_Round_up: + if (!sign) + goto huge; + break; + case FPI_Round_down: + if (sign) + goto huge; + } + /* Round to largest representable magnitude */ + Bfree(rvb); + rvb = 0; + irv = STRTOG_Normal | STRTOG_Inexlo; + *expo = fpi->emax; + b = bits; + be = b + ((fpi->nbits + 31) >> 5); + while(b < be) + *b++ = -1; + if ((j = fpi->nbits & 0x1f)) + *--be >>= (32 - j); + goto ret; + huge: + rvb->wds = 0; + irv = STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi; + SET_ERRNO(ERANGE); + infnanexp: + *expo = fpi->emax + 1; + } + ret: + if (denorm) { + if (sudden_underflow) { + rvb->wds = 0; + irv = STRTOG_Underflow | STRTOG_Inexlo; + SET_ERRNO(ERANGE); + } + else { + irv = (irv & ~STRTOG_Retmask) | + (rvb->wds > 0 ? STRTOG_Denormal : STRTOG_Zero); + if (irv & STRTOG_Inexact) { + irv |= STRTOG_Underflow; + SET_ERRNO(ERANGE); + } + } + } + if (se) + *se = (char *)s; + if (sign) + irv |= STRTOG_Neg; + if (rvb) { + copybits(bits, nbits, rvb); + Bfree(rvb); + } + return irv; +} diff --git a/src/lib/evil/gdtoa/strtof.c b/src/lib/evil/gdtoa/strtof.c new file mode 100644 index 0000000000..71be2225f5 --- /dev/null +++ b/src/lib/evil/gdtoa/strtof.c @@ -0,0 +1,77 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +float __evil_strtof (const char *s, char **sp) +{ + static FPI fpi0 = { 24, 1-127-24+1, 254-127-24+1, 1, SI, Int_max }; + ULong bits[1]; + Long expo; + int k; + union { ULong L[1]; float f; } u = { { 0 } }; +#ifdef Honor_FLT_ROUNDS +#include "gdtoa_fltrnds.h" +#else +#define fpi &fpi0 +#endif + + k = __strtodg(s, sp, fpi, &expo, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + u.L[0] = 0; + break; + + case STRTOG_Normal: + case STRTOG_NaNbits: + u.L[0] = (bits[0] & 0x7fffff) | ((expo + 0x7f + 23) << 23); + break; + + case STRTOG_Denormal: + u.L[0] = bits[0]; + break; + + case STRTOG_Infinite: + u.L[0] = 0x7f800000; + break; + + case STRTOG_NaN: + u.L[0] = f_QNAN; + } + if (k & STRTOG_Neg) + u.L[0] |= 0x80000000L; + return u.f; +} + +/* float __cdecl */ +/* __evil_strtof (const char * __restrict__ src, char ** __restrict__ endptr) */ +/* __attribute__((alias("__strtof"))); */ diff --git a/src/lib/evil/gdtoa/strtopx.c b/src/lib/evil/gdtoa/strtopx.c new file mode 100644 index 0000000000..68b3ab3121 --- /dev/null +++ b/src/lib/evil/gdtoa/strtopx.c @@ -0,0 +1,119 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 2000 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +#undef _0 +#undef _1 + +/* one or the other of IEEE_MC68k or IEEE_8087 should be #defined */ + +#ifdef IEEE_MC68k +#define _0 0 +#define _1 1 +#define _2 2 +#define _3 3 +#define _4 4 +#endif +#ifdef IEEE_8087 +#define _0 4 +#define _1 3 +#define _2 2 +#define _3 1 +#define _4 0 +#endif + +typedef union lD { + UShort L[5]; + long double D; +} lD; + +static int __strtopx (const char *s, char **sp, lD *V) +{ + static FPI fpi0 = { 64, 1-16383-64+1, 32766 - 16383 - 64 + 1, 1, SI, Int_max }; + ULong bits[2]; + Long expo; + int k; + UShort *L = & (V->L[0]); +#ifdef Honor_FLT_ROUNDS +#include "gdtoa_fltrnds.h" +#else +#define fpi &fpi0 +#endif + V->D = 0.0L; + + k = __strtodg(s, sp, fpi, &expo, bits); + switch(k & STRTOG_Retmask) { + case STRTOG_NoNumber: + case STRTOG_Zero: + L[0] = L[1] = L[2] = L[3] = L[4] = 0; + break; + + case STRTOG_Denormal: + L[_0] = 0; + goto normal_bits; + + case STRTOG_Normal: + case STRTOG_NaNbits: + L[_0] = expo + 0x3fff + 63; + normal_bits: + L[_4] = (UShort)bits[0]; + L[_3] = (UShort)(bits[0] >> 16); + L[_2] = (UShort)bits[1]; + L[_1] = (UShort)(bits[1] >> 16); + break; + + case STRTOG_Infinite: + L[_0] = 0x7fff; + L[_1] = 0x8000; + L[_2] = L[_3] = L[_4] = 0; + break; + + case STRTOG_NaN: + L[0] = ldus_QNAN0; + L[1] = ldus_QNAN1; + L[2] = ldus_QNAN2; + L[3] = ldus_QNAN3; + L[4] = ldus_QNAN4; + } + if (k & STRTOG_Neg) + L[_0] |= 0x8000; + return k; +} + +long double __cdecl +__evil_strtold (const char * __restrict__ src, char ** __restrict__ endptr) +{ + lD ret; + ret.D = 0.0L; + __strtopx(src, endptr, &ret); + return ret.D; +} diff --git a/src/lib/evil/gdtoa/sum.c b/src/lib/evil/gdtoa/sum.c new file mode 100644 index 0000000000..8823541ea6 --- /dev/null +++ b/src/lib/evil/gdtoa/sum.c @@ -0,0 +1,91 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +Bigint *sum (Bigint *a, Bigint *b) +{ + Bigint *c; + ULong carry, *xc, *xa, *xb, *xe, y; +#ifdef Pack_32 + ULong z; +#endif + + if (a->wds < b->wds) { + c = b; b = a; a = c; + } + c = Balloc(a->k); + c->wds = a->wds; + carry = 0; + xa = a->x; + xb = b->x; + xc = c->x; + xe = xc + b->wds; +#ifdef Pack_32 + do { + y = (*xa & 0xffff) + (*xb & 0xffff) + carry; + carry = (y & 0x10000) >> 16; + z = (*xa++ >> 16) + (*xb++ >> 16) + carry; + carry = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } while(xc < xe); + xe += a->wds - b->wds; + while(xc < xe) { + y = (*xa & 0xffff) + carry; + carry = (y & 0x10000) >> 16; + z = (*xa++ >> 16) + carry; + carry = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ + *xb++ + carry; + carry = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } while(xc < xe); + xe += a->wds - b->wds; + while(xc < xe) { + y = *xa++ + carry; + carry = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif + if (carry) { + if (c->wds == c->maxwds) { + b = Balloc(c->k + 1); + Bcopy(b, c); + Bfree(c); + c = b; + } + c->x[c->wds++] = 1; + } + return c; +} diff --git a/src/lib/evil/gdtoa/ulp.c b/src/lib/evil/gdtoa/ulp.c new file mode 100644 index 0000000000..bfa9bdeeaf --- /dev/null +++ b/src/lib/evil/gdtoa/ulp.c @@ -0,0 +1,61 @@ +/**************************************************************** + +The author of this software is David M. Gay. + +Copyright (C) 1998, 1999 by Lucent Technologies +All Rights Reserved + +Permission to use, copy, modify, and distribute this software and +its documentation for any purpose and without fee is hereby +granted, provided that the above copyright notice appear in all +copies and that both that the copyright notice and this +permission notice and warranty disclaimer appear in supporting +documentation, and that the name of Lucent or any of its entities +not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, +INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. +IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER +IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. + +****************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +#include "gdtoaimp.h" + +double ulp (dbl_union *x) +{ + Long L; + union _dbl_union a; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Sudden_Underflow + if (L > 0) { +#endif + word0(&a) = L; + word1(&a) = 0; +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&a) = 0x80000 >> L; + word1(&a) = 0; + } + else { + word0(&a) = 0; + L -= Exp_shift; + word1(&a) = L >= 31 ? 1 : 1 << (31 - L); + } + } +#endif + return dval(&a); +} diff --git a/src/lib/evil/mingw32ce/errno.h b/src/lib/evil/mingw32ce/errno.h new file mode 100644 index 0000000000..5e8f3d7cbd --- /dev/null +++ b/src/lib/evil/mingw32ce/errno.h @@ -0,0 +1,111 @@ +#ifndef __EVIL_ERRNO_H__ +#define __EVIL_ERRNO_H__ + +#ifdef EAPI +# undef EAPI +#endif /* EAPI */ + +#ifdef _WIN32 +# ifdef EFL_EVIL_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_EVIL_BUILD */ +#endif /* _WIN32 */ + +#ifdef __cplusplus +extern "C" { +#endif + +extern EAPI int errno; + +/* Fake values */ +#define E2BIG 1 +#define EACCES 2 +#define EAGAIN 3 +#define EBADF 4 +#define EBADMSG 5 +#define EBUSY 6 +#define ECANCELED 7 +#define ECHILD 9 +#define EDEADLK 10 +#define EDOM 11 +#define EEXIST 12 +#define EFAULT 13 +#define EFBIG 14 +#define EINPROGRESS 15 +#define EINTR 16 +#define EINVAL 17 +#define EIO 18 +#define EISDIR 19 +#define EMFILE 20 +#define EMLINK 21 +#define EMSGSIZE 22 +#define ENAMETOOLONG 23 +#define ENFILE 24 +#define ENODEV 25 +#define ENOENT 26 +#define ENOEXEC 27 +#define ENOLCK 28 +#define ENOMEM 29 +#define ENOSPC 30 +#define ENOSYS 31 +#define ENOTDIR 32 +#define ENOTEMPTY 33 +#define ENOTSUP 34 +#define ENOTTY 35 +#define ENXIO 36 +#define EPERM 37 +#define EPIPE 38 +#define ERANGE 39 +#define EROFS 40 +#define ESPIPE 41 +#define ESRCH 42 +#define ETIMEDOUT 43 +#define EXDEV 44 +#define EADDRINUSE 45 +#define EADDRNOTAVAIL 46 +#define EAFNOSUPPORT 47 +#define EALREADY 48 +#define ECONNABORTED 49 +#define ECONNREFUSED 50 +#define ECONNRESET 51 +#define EDESTADDRREQ 52 +#define EDQUOT 53 +#define EHOSTUNREACH 54 +#define EIDRM 55 +#define EILSEQ 56 +#define EISCONN 57 +#define ELOOP 58 +#define EMULTIHOP 59 +#define ENETDOWN 60 +#define ENETRESET 61 +#define ENETUNREACH 62 +#define ENOBUFS 63 +#define ENODATA 64 +#define ENOLINK 65 +#define ENOMSG 66 +#define ENOPROTOOPT 67 +#define ENOSR 68 +#define ENOSTR 69 +#define ENOTCONN 70 +#define ENOTSOCK 71 +#define EOPNOTSUPP 72 +#define EOVERFLOW 73 +#define EPROTO 74 +#define EPROTONOSUPPORT 75 +#define EPROTOTYPE 76 +#define ESTALE 77 +#define ETIME 78 +#define ETXTBSY 79 +#define EWOULDBLOCK 80 + +#ifdef __cplusplus +} +#endif + +#endif /* __EVIL_ERRNO_H__ */ diff --git a/src/lib/evil/pwd.h b/src/lib/evil/pwd.h new file mode 100644 index 0000000000..e7907cacbd --- /dev/null +++ b/src/lib/evil/pwd.h @@ -0,0 +1,71 @@ +#ifndef __EVIL_PWD_H__ +#define __EVIL_PWD_H__ + + +/** + * @file pwd.h + * @brief The file that provides functions ported from Unix in pwd.h. + * @defgroup Evil_Pwd_Group Pwd.h functions + * + * This header provides functions ported from Unix in dirent.h. + * + * @{ + */ + + +#include <time.h> + +#include <Evil.h> + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @struct passwd + * @brief A structure that describes a password. + */ +struct passwd { + char *pw_name; /**< user name */ + char *pw_passwd; /**< encrypted password (always @c NULL) */ + uid_t pw_uid; /**< user uid */ + gid_t pw_gid; /**< user gid (always O) */ + time_t pw_change; /**< password change time (always 0) */ + char *pw_class; /**< user access class (always @c NULL) */ + char *pw_gecos; /**< Honeywell login info */ + char *pw_dir; /**< home directory */ + char *pw_shell; /**< default shell */ + time_t pw_expire; /**< account expiration (always O) */ + int pw_fields; /**< internal: fields filled in (always O) */ +}; + +/** + * @brief Return a passwd structure. + * + * @param uid The User ID + * @return A stacally allocated passwd structure. + * + * This function fills a static buffer @ref passwd with @p uid and the + * user name. + * + * Conformity: None. + * + * Supported OS: Windows XP, CE. + */ +EAPI struct passwd *getpwuid (uid_t uid); + + +#ifdef __cplusplus +} +#endif + + + +/** + * @} + */ + + +#endif /* __EVIL_PWD_H__ */ diff --git a/src/lib/evil/sys/mman.h b/src/lib/evil/sys/mman.h new file mode 100644 index 0000000000..c53437f001 --- /dev/null +++ b/src/lib/evil/sys/mman.h @@ -0,0 +1,149 @@ +#ifndef __EVIL_SYS_MMAN_H__ +#define __EVIL_SYS_MMAN_H__ + +#include <evil_macro.h> + + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @def PROT_NONE + * Data can not be accessed. + */ + +/** + * @def PROT_READ + * Data can be read. + */ + +/** + * @def PROT_WRITE + * Data can be written. + */ + +/** + * @def PROT_EXEC + * Data can be executed. + */ + +#define PROT_NONE 0x00 +#define PROT_READ 0x01 +#define PROT_WRITE 0x02 +#define PROT_EXEC 0x04 + +/** + * @def MAP_SHARED + * Changes are shared. + */ + +/** + * @def MAP_PRIVATE + * Changes are private. + */ + +/** + * @def MAP_FIXED + * Interpret the address (addr) exactly. + */ + +/** + * @def MAP_FAILED + * Error return from mmap(). + */ + +#define MAP_SHARED 0x0001 +#define MAP_PRIVATE 0x0002 +#define MAP_FIXED 0x0010 + +#define MAP_FAILED ((void *)-1) + + +/** + * @file mman.h + * @brief The file that provides the memory map functions + * @defgroup Mman Functions that manage memory mappping. + * + * This header provides the meomry map functions mmap and munmap. + * + */ + +/** + * Creates or opens a named or unnamed file mapping object for a + * specified file and maps a view of a file mapping into the + * address space of a calling process. + * + * @param addr Unused + * @param len Number of bytes to be mapped. + * @param prot Protections. + * @param flags Type of the mapped object. + * @param fd File descriptor that describes the object to map. + * @param offset Number of bytes from which to start the mapping. + * @return The starting address of the mapped view on success, -1 otherwise. + * + * Create or open an unnamed file mapping object for a specified + * file described by the file descriptor @p fd. The number of + * bytes that are mapped is given by @p len and start after + * @p offset bytes. The parameter @p addr is unused. + * + * The only type of the mapped object that is supported is + * @c MAP_SHARED. If another value if given, -1 is returned. + * + * @p prot specifies the protection of the mapped region. If + * PROT_EXEC is used, it set the execute access. If PROT_READ + * is used, it sets the read access. If PROT_WRITE is used, it + * sets the write access. + * + * If the map view of file can not be created, -1 is returned. + * If the mappping can not be done, -1 is returned. + * + * If no error occured, the starting address of the mapped view + * is returned. + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Mman + */ +EAPI void *mmap(void *addr, + size_t len, + int prot, + int flags, + int fd, + off_t offset); + +/** + * Unmaps a mapped view of a file from the calling process's + * address space. + * + * @param addr Pointer to the base address. + * @param len Unused. + * @return 0 on success, -1 otherwise. + * + * Unmaps a mapped view of a file from the calling process's + * address space. @p addr is the pointer to the base address. + * This value must be identical to the value returned by a + * previous call to mmap(). The parameter @p len is unsed. + * + * Conformity: None. + * + * Supported OS: Windows Vista, Windows XP or Windows 2000 + * Professional. + * + * @ingroup Mman + */ +EAPI int munmap(void *addr, + size_t len); + + +#ifdef __cplusplus +} +#endif + + +#endif /* __EVIL_SYS_MMAN_H__ */ + |