diff options
Diffstat (limited to 'src')
856 files changed, 33417 insertions, 0 deletions
diff --git a/src/conf/fpathconf.c b/src/conf/fpathconf.c new file mode 100644 index 00000000..af7e4d3b --- /dev/null +++ b/src/conf/fpathconf.c @@ -0,0 +1,35 @@ +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +long fpathconf(int fd, int name) +{ + static const short values[] = { + [_PC_LINK_MAX] = _POSIX_LINK_MAX, + [_PC_MAX_CANON] = _POSIX_MAX_CANON, + [_PC_MAX_INPUT] = _POSIX_MAX_INPUT, + [_PC_NAME_MAX] = NAME_MAX, + [_PC_PATH_MAX] = PATH_MAX, + [_PC_PIPE_BUF] = PIPE_BUF, + [_PC_CHOWN_RESTRICTED] = 1, + [_PC_NO_TRUNC] = 1, + [_PC_VDISABLE] = 0, + [_PC_SYNC_IO] = 0, + [_PC_ASYNC_IO] = 0, + [_PC_PRIO_IO] = 0, + [_PC_SOCK_MAXBUF] = -1, + [_PC_FILESIZEBITS] = sizeof(off_t), + [_PC_REC_INCR_XFER_SIZE] = PAGE_SIZE, + [_PC_REC_MAX_XFER_SIZE] = PAGE_SIZE, + [_PC_REC_MIN_XFER_SIZE] = PAGE_SIZE, + [_PC_REC_XFER_ALIGN] = PAGE_SIZE, + [_PC_ALLOC_SIZE_MIN] = PAGE_SIZE, + [_PC_SYMLINK_MAX] = SYMLINK_MAX, + [_PC_2_SYMLINKS] = 1 + }; + if (name > sizeof(values)/sizeof(values[0])) { + errno = EINVAL; + return -1; + } + return values[name]; +} diff --git a/src/conf/pathconf.c b/src/conf/pathconf.c new file mode 100644 index 00000000..01e19c59 --- /dev/null +++ b/src/conf/pathconf.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +long pathconf(const char *path, int name) +{ + return fpathconf(-1, name); +} diff --git a/src/conf/sysconf.c b/src/conf/sysconf.c new file mode 100644 index 00000000..cdaeb2a6 --- /dev/null +++ b/src/conf/sysconf.c @@ -0,0 +1,222 @@ +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +#define VER (-2) +#define OFLOW (-3) + +long sysconf(int name) +{ + static const short values[] = { + [_SC_ARG_MAX] = OFLOW, + [_SC_CHILD_MAX] = -1, + [_SC_CLK_TCK] = 100, + [_SC_NGROUPS_MAX] = 32, + [_SC_OPEN_MAX] = 1024, + [_SC_STREAM_MAX] = -1, + [_SC_TZNAME_MAX] = TZNAME_MAX, + [_SC_JOB_CONTROL] = 1, + [_SC_SAVED_IDS] = 1, + [_SC_REALTIME_SIGNALS] = 1, + [_SC_PRIORITY_SCHEDULING] = -1, + [_SC_TIMERS] = VER, + [_SC_ASYNCHRONOUS_IO] = VER, + [_SC_PRIORITIZED_IO] = -1, + [_SC_SYNCHRONIZED_IO] = -1, + [_SC_FSYNC] = -1, + [_SC_MAPPED_FILES] = VER, + [_SC_MEMLOCK] = VER, + [_SC_MEMLOCK_RANGE] = VER, + [_SC_MEMORY_PROTECTION] = VER, + [_SC_MESSAGE_PASSING] = -1, + [_SC_SEMAPHORES] = VER, + [_SC_SHARED_MEMORY_OBJECTS] = -1, + [_SC_AIO_LISTIO_MAX] = -1, + [_SC_AIO_MAX] = -1, + [_SC_AIO_PRIO_DELTA_MAX] = 0, /* ?? */ + [_SC_DELAYTIMER_MAX] = _POSIX_DELAYTIMER_MAX, + [_SC_MQ_OPEN_MAX] = -1, + [_SC_MQ_PRIO_MAX] = _POSIX_MQ_PRIO_MAX, + [_SC_VERSION] = VER, + [_SC_PAGE_SIZE] = PAGE_SIZE, + [_SC_RTSIG_MAX] = 63, /* ?? */ + [_SC_SEM_NSEMS_MAX] = _POSIX_SEM_NSEMS_MAX, + [_SC_SEM_VALUE_MAX] = _POSIX_SEM_VALUE_MAX, + [_SC_SIGQUEUE_MAX] = -1, + [_SC_TIMER_MAX] = -1, + [_SC_BC_BASE_MAX] = _POSIX2_BC_BASE_MAX, + [_SC_BC_DIM_MAX] = _POSIX2_BC_DIM_MAX, + [_SC_BC_SCALE_MAX] = _POSIX2_BC_SCALE_MAX, + [_SC_BC_STRING_MAX] = _POSIX2_BC_STRING_MAX, + [_SC_COLL_WEIGHTS_MAX] = COLL_WEIGHTS_MAX, + [_SC_EQUIV_CLASS_MAX] = -1, /* ?? */ + [_SC_EXPR_NEST_MAX] = -1, + [_SC_LINE_MAX] = -1, + [_SC_RE_DUP_MAX] = RE_DUP_MAX, + [_SC_CHARCLASS_NAME_MAX] = -1, /* ?? */ + [_SC_2_VERSION] = VER, + [_SC_2_C_BIND] = VER, + [_SC_2_C_DEV] = -1, + [_SC_2_FORT_DEV] = -1, + [_SC_2_FORT_RUN] = -1, + [_SC_2_SW_DEV] = -1, + [_SC_2_LOCALEDEF] = -1, + [_SC_PII] = -1, /* ????????? */ + [_SC_PII_XTI] = -1, + [_SC_PII_SOCKET] = -1, + [_SC_PII_INTERNET] = -1, + [_SC_PII_OSI] = -1, + [_SC_POLL] = 1, + [_SC_SELECT] = 1, + [_SC_IOV_MAX] = IOV_MAX, + [_SC_PII_INTERNET_STREAM] = -1, + [_SC_PII_INTERNET_DGRAM] = -1, + [_SC_PII_OSI_COTS] = -1, + [_SC_PII_OSI_CLTS] = -1, + [_SC_PII_OSI_M] = -1, + [_SC_T_IOV_MAX] = -1, + [_SC_THREADS] = VER, + [_SC_THREAD_SAFE_FUNCTIONS] = VER, + [_SC_GETGR_R_SIZE_MAX] = -1, + [_SC_GETPW_R_SIZE_MAX] = -1, + [_SC_LOGIN_NAME_MAX] = 256, + [_SC_TTY_NAME_MAX] = TTY_NAME_MAX, + [_SC_THREAD_DESTRUCTOR_ITERATIONS] = _POSIX_THREAD_DESTRUCTOR_ITERATIONS, + [_SC_THREAD_KEYS_MAX] = -1, + [_SC_THREAD_STACK_MIN] = 2*PAGE_SIZE, + [_SC_THREAD_THREADS_MAX] = -1, + [_SC_THREAD_ATTR_STACKADDR] = -1, + [_SC_THREAD_ATTR_STACKSIZE] = VER, + [_SC_THREAD_PRIORITY_SCHEDULING] = -1, + [_SC_THREAD_PRIO_INHERIT] = -1, + [_SC_THREAD_PRIO_PROTECT] = -1, + [_SC_THREAD_PROCESS_SHARED] = VER, + [_SC_NPROCESSORS_CONF] = -1, + [_SC_NPROCESSORS_ONLN] = -1, + [_SC_PHYS_PAGES] = -1, + [_SC_AVPHYS_PAGES] = -1, + [_SC_ATEXIT_MAX] = -1, + [_SC_PASS_MAX] = -1, + [_SC_XOPEN_VERSION] = _XOPEN_VERSION, + [_SC_XOPEN_XCU_VERSION] = _XOPEN_VERSION, + [_SC_XOPEN_UNIX] = -1, + [_SC_XOPEN_CRYPT] = -1, + [_SC_XOPEN_ENH_I18N] = 1, + [_SC_XOPEN_SHM] = 1, + [_SC_2_CHAR_TERM] = -1, + [_SC_2_C_VERSION] = -1, + [_SC_2_UPE] = -1, + [_SC_XOPEN_XPG2] = -1, + [_SC_XOPEN_XPG3] = -1, + [_SC_XOPEN_XPG4] = -1, + [_SC_CHAR_BIT] = -1, + [_SC_CHAR_MAX] = -1, + [_SC_CHAR_MIN] = -1, + [_SC_INT_MAX] = -1, + [_SC_INT_MIN] = -1, + [_SC_LONG_BIT] = -1, + [_SC_WORD_BIT] = -1, + [_SC_MB_LEN_MAX] = -1, + [_SC_NZERO] = NZERO, + [_SC_SSIZE_MAX] = -1, + [_SC_SCHAR_MAX] = -1, + [_SC_SCHAR_MIN] = -1, + [_SC_SHRT_MAX] = -1, + [_SC_SHRT_MIN] = -1, + [_SC_UCHAR_MAX] = -1, + [_SC_UINT_MAX] = -1, + [_SC_ULONG_MAX] = -1, + [_SC_USHRT_MAX] = -1, + [_SC_NL_ARGMAX] = -1, + [_SC_NL_LANGMAX] = -1, + [_SC_NL_MSGMAX] = -1, + [_SC_NL_NMAX] = -1, + [_SC_NL_SETMAX] = -1, + [_SC_NL_TEXTMAX] = -1, + [_SC_XBS5_ILP32_OFF32] = -1, + [_SC_XBS5_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1, + [_SC_XBS5_LP64_OFF64] = 2*(sizeof(long)==8)-1, + [_SC_XBS5_LPBIG_OFFBIG] = 2*(sizeof(long)>=8)-1, + [_SC_XOPEN_LEGACY] = -1, + [_SC_XOPEN_REALTIME] = -1, + [_SC_XOPEN_REALTIME_THREADS] = -1, + [_SC_ADVISORY_INFO] = -1, + [_SC_BARRIERS] = VER, + [_SC_BASE] = -1, + [_SC_C_LANG_SUPPORT] = -1, + [_SC_C_LANG_SUPPORT_R] = -1, + [_SC_CLOCK_SELECTION] = VER, + [_SC_CPUTIME] = VER, + [_SC_THREAD_CPUTIME] = -1, + [_SC_DEVICE_IO] = -1, + [_SC_DEVICE_SPECIFIC] = -1, + [_SC_DEVICE_SPECIFIC_R] = -1, + [_SC_FD_MGMT] = -1, + [_SC_FIFO] = -1, + [_SC_PIPE] = -1, + [_SC_FILE_ATTRIBUTES] = -1, + [_SC_FILE_LOCKING] = -1, + [_SC_FILE_SYSTEM] = -1, + [_SC_MONOTONIC_CLOCK] = VER, + [_SC_MULTI_PROCESS] = -1, + [_SC_SINGLE_PROCESS] = -1, + [_SC_NETWORKING] = -1, + [_SC_READER_WRITER_LOCKS] = VER, + [_SC_SPIN_LOCKS] = VER, + [_SC_REGEXP] = 1, + [_SC_REGEX_VERSION] = -1, + [_SC_SHELL] = 1, + [_SC_SIGNALS] = -1, + [_SC_SPAWN] = -1, + [_SC_SPORADIC_SERVER] = -1, + [_SC_THREAD_SPORADIC_SERVER] = -1, + [_SC_SYSTEM_DATABASE] = -1, + [_SC_SYSTEM_DATABASE_R] = -1, + [_SC_TIMEOUTS] = VER, + [_SC_TYPED_MEMORY_OBJECTS] = -1, + [_SC_USER_GROUPS] = -1, + [_SC_USER_GROUPS_R] = -1, + [_SC_2_PBS] = -1, + [_SC_2_PBS_ACCOUNTING] = -1, + [_SC_2_PBS_LOCATE] = -1, + [_SC_2_PBS_MESSAGE] = -1, + [_SC_2_PBS_TRACK] = -1, + [_SC_SYMLOOP_MAX] = SYMLOOP_MAX, + [_SC_STREAMS] = 0, + [_SC_2_PBS_CHECKPOINT] = -1, + [_SC_V6_ILP32_OFF32] = -1, + [_SC_V6_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1, + [_SC_V6_LP64_OFF64] = 2*(sizeof(long)==8)-1, + [_SC_V6_LPBIG_OFFBIG] = 2*(sizeof(long)>=8)-1, + [_SC_HOST_NAME_MAX] = HOST_NAME_MAX, + [_SC_TRACE] = -1, + [_SC_TRACE_EVENT_FILTER] = -1, + [_SC_TRACE_INHERIT] = -1, + [_SC_TRACE_LOG] = -1, + + [_SC_IPV6] = VER, + [_SC_RAW_SOCKETS] = VER, + [_SC_V7_ILP32_OFF32] = -1, + [_SC_V7_ILP32_OFFBIG] = 2*(sizeof(long)==4)-1, + [_SC_V7_LP64_OFF64] = 2*(sizeof(long)==8)-1, + [_SC_V7_LPBIG_OFFBIG] = 2*(sizeof(long)>=8)-1, + [_SC_SS_REPL_MAX] = -1, + [_SC_TRACE_EVENT_NAME_MAX] = -1, + [_SC_TRACE_NAME_MAX] = -1, + [_SC_TRACE_SYS_MAX] = -1, + [_SC_TRACE_USER_EVENT_MAX] = -1, + [_SC_XOPEN_STREAMS] = 0, + [_SC_THREAD_ROBUST_PRIO_INHERIT] = -1, + [_SC_THREAD_ROBUST_PRIO_PROTECT] = -1, + }; + if (name > sizeof(values)/sizeof(values[0])) { + errno = EINVAL; + return -1; + } else if (values[name] == VER) { + return _POSIX_VERSION; + } else if (values[name] == OFLOW) { + return ARG_MAX; + } else { + return values[name]; + } +} diff --git a/src/ctype/__ctype_get_mb_cur_max.c b/src/ctype/__ctype_get_mb_cur_max.c new file mode 100644 index 00000000..42e4ee71 --- /dev/null +++ b/src/ctype/__ctype_get_mb_cur_max.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +size_t __ctype_get_mb_cur_max() +{ + return 4; +} diff --git a/src/ctype/isalnum.c b/src/ctype/isalnum.c new file mode 100644 index 00000000..e3d2cf0b --- /dev/null +++ b/src/ctype/isalnum.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isalnum(int c) +{ + return isalpha(c) || isdigit(c); +} diff --git a/src/ctype/isalpha.c b/src/ctype/isalpha.c new file mode 100644 index 00000000..53e115c2 --- /dev/null +++ b/src/ctype/isalpha.c @@ -0,0 +1,7 @@ +#include <ctype.h> +#undef isalpha + +int isalpha(int c) +{ + return ((unsigned)c|32)-'a' < 26; +} diff --git a/src/ctype/isascii.c b/src/ctype/isascii.c new file mode 100644 index 00000000..3af0a10d --- /dev/null +++ b/src/ctype/isascii.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isascii(int c) +{ + return !(c&~0x7f); +} diff --git a/src/ctype/isblank.c b/src/ctype/isblank.c new file mode 100644 index 00000000..957400b2 --- /dev/null +++ b/src/ctype/isblank.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isblank(int c) +{ + return (c == ' ' || c == '\t'); +} diff --git a/src/ctype/iscntrl.c b/src/ctype/iscntrl.c new file mode 100644 index 00000000..92ed7f0e --- /dev/null +++ b/src/ctype/iscntrl.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int iscntrl(int c) +{ + return (unsigned)c < 0x20 || c == 0x7f; +} diff --git a/src/ctype/isdigit.c b/src/ctype/isdigit.c new file mode 100644 index 00000000..0bc82a6d --- /dev/null +++ b/src/ctype/isdigit.c @@ -0,0 +1,7 @@ +#include <ctype.h> +#undef isdigit + +int isdigit(int c) +{ + return (unsigned)c-'0' < 10; +} diff --git a/src/ctype/isgraph.c b/src/ctype/isgraph.c new file mode 100644 index 00000000..98979d1e --- /dev/null +++ b/src/ctype/isgraph.c @@ -0,0 +1,4 @@ +int isgraph(int c) +{ + return (unsigned)c-0x21 < 0x5e; +} diff --git a/src/ctype/islower.c b/src/ctype/islower.c new file mode 100644 index 00000000..d72fb212 --- /dev/null +++ b/src/ctype/islower.c @@ -0,0 +1,7 @@ +#include <ctype.h> +#undef islower + +int islower(int c) +{ + return (unsigned)c-'a' < 26; +} diff --git a/src/ctype/isprint.c b/src/ctype/isprint.c new file mode 100644 index 00000000..504e66ed --- /dev/null +++ b/src/ctype/isprint.c @@ -0,0 +1,4 @@ +int isprint(int c) +{ + return (unsigned)c-0x20 < 0x5f; +} diff --git a/src/ctype/ispunct.c b/src/ctype/ispunct.c new file mode 100644 index 00000000..fc455352 --- /dev/null +++ b/src/ctype/ispunct.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int ispunct(int c) +{ + return isgraph(c) && !isalnum(c); +} diff --git a/src/ctype/isspace.c b/src/ctype/isspace.c new file mode 100644 index 00000000..8e535aa1 --- /dev/null +++ b/src/ctype/isspace.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isspace(int c) +{ + return c == ' ' || (unsigned)c-'\t' < 5; +} diff --git a/src/ctype/isupper.c b/src/ctype/isupper.c new file mode 100644 index 00000000..f09d88c5 --- /dev/null +++ b/src/ctype/isupper.c @@ -0,0 +1,7 @@ +#include <ctype.h> +#undef isupper + +int isupper(int c) +{ + return (unsigned)c-'A' < 26; +} diff --git a/src/ctype/iswalnum.c b/src/ctype/iswalnum.c new file mode 100644 index 00000000..d3b56674 --- /dev/null +++ b/src/ctype/iswalnum.c @@ -0,0 +1,9 @@ +#include <wchar.h> +#include <wctype.h> + +#undef iswalnum + +int iswalnum(wint_t wc) +{ + return (unsigned)wc-'0' < 10 || iswalpha(wc); +} diff --git a/src/ctype/iswalpha.c b/src/ctype/iswalpha.c new file mode 100644 index 00000000..0f031eac --- /dev/null +++ b/src/ctype/iswalpha.c @@ -0,0 +1,6 @@ +#include <wctype.h> + +int iswalpha(wint_t wc) +{ + return (32U|wc)-'a'<26; +} diff --git a/src/ctype/iswblank.c b/src/ctype/iswblank.c new file mode 100644 index 00000000..bc6196f2 --- /dev/null +++ b/src/ctype/iswblank.c @@ -0,0 +1,8 @@ +#include <wchar.h> +#include <wctype.h> +#include <ctype.h> + +int iswblank(wint_t wc) +{ + return isblank(wc); +} diff --git a/src/ctype/iswcntrl.c b/src/ctype/iswcntrl.c new file mode 100644 index 00000000..93942b08 --- /dev/null +++ b/src/ctype/iswcntrl.c @@ -0,0 +1,10 @@ +#include <wchar.h> +#include <wctype.h> + +int iswcntrl(wint_t wc) +{ + return (unsigned)wc < 32 + || (unsigned)(wc-0x7f) < 33 + || (unsigned)(wc-0x2028) < 2 + || (unsigned)(wc-0xfff9) < 3; +} diff --git a/src/ctype/iswctype.c b/src/ctype/iswctype.c new file mode 100644 index 00000000..d917975b --- /dev/null +++ b/src/ctype/iswctype.c @@ -0,0 +1,63 @@ +#include <wchar.h> +#include <wctype.h> +#include <string.h> + +#define WCTYPE_ALNUM 1 +#define WCTYPE_ALPHA 2 +#define WCTYPE_BLANK 3 +#define WCTYPE_CNTRL 4 +#define WCTYPE_DIGIT 5 +#define WCTYPE_GRAPH 6 +#define WCTYPE_LOWER 7 +#define WCTYPE_PRINT 8 +#define WCTYPE_PUNCT 9 +#define WCTYPE_SPACE 10 +#define WCTYPE_UPPER 11 +#define WCTYPE_XDIGIT 12 + +int iswctype(wint_t wc, wctype_t type) +{ + switch (type) { + case WCTYPE_ALNUM: + return iswalnum(wc); + case WCTYPE_ALPHA: + return iswalpha(wc); + case WCTYPE_BLANK: + return iswblank(wc); + case WCTYPE_CNTRL: + return iswcntrl(wc); + case WCTYPE_DIGIT: + return iswdigit(wc); + case WCTYPE_GRAPH: + return iswgraph(wc); + case WCTYPE_LOWER: + return iswlower(wc); + case WCTYPE_PRINT: + return iswprint(wc); + case WCTYPE_PUNCT: + return iswpunct(wc); + case WCTYPE_SPACE: + return iswspace(wc); + case WCTYPE_UPPER: + return iswupper(wc); + case WCTYPE_XDIGIT: + return iswxdigit(wc); + } + return 0; +} + +wctype_t wctype(const char *s) +{ + int i; + const char *p; + /* order must match! */ + static const char names[] = + "alnum\0" "alpha\0" "blank\0" + "cntrl\0" "digit\0" "graph\0" + "lower\0" "print\0" "punct\0" + "space\0" "upper\0" "xdigit"; + for (i=1, p=names; *p; i++, p+=6) + if (*s == *p && !strcmp(s, p)) + return i; + return 0; +} diff --git a/src/ctype/iswdigit.c b/src/ctype/iswdigit.c new file mode 100644 index 00000000..0487145f --- /dev/null +++ b/src/ctype/iswdigit.c @@ -0,0 +1,9 @@ +#include <wchar.h> +#include <wctype.h> + +#undef iswdigit + +int iswdigit(wint_t wc) +{ + return (unsigned)wc-'0' < 10; +} diff --git a/src/ctype/iswgraph.c b/src/ctype/iswgraph.c new file mode 100644 index 00000000..fdc97853 --- /dev/null +++ b/src/ctype/iswgraph.c @@ -0,0 +1,7 @@ +#include <wctype.h> + +int iswgraph(wint_t wc) +{ + /* ISO C defines this function as: */ + return !iswspace(wc) && iswprint(wc); +} diff --git a/src/ctype/iswlower.c b/src/ctype/iswlower.c new file mode 100644 index 00000000..0a568e77 --- /dev/null +++ b/src/ctype/iswlower.c @@ -0,0 +1,6 @@ +#include <wctype.h> + +int iswlower(wint_t wc) +{ + return towupper(wc) != wc; +} diff --git a/src/ctype/iswprint.c b/src/ctype/iswprint.c new file mode 100644 index 00000000..7717671a --- /dev/null +++ b/src/ctype/iswprint.c @@ -0,0 +1,10 @@ +#include <wctype.h> + +int iswprint(wint_t wc) +{ + unsigned c = wc; + /* assume any non-control, non-illegal codepoint is printable */ + if (c>0x10ffff || c-0xd800<0x800 || (c&0xfffe)==0xfffe || iswcntrl(c)) + return 0; + return 1; +} diff --git a/src/ctype/iswpunct.c b/src/ctype/iswpunct.c new file mode 100644 index 00000000..1414c30c --- /dev/null +++ b/src/ctype/iswpunct.c @@ -0,0 +1,138 @@ +#include <wctype.h> +#include <inttypes.h> + +/* The below data is derived from classes (P.|Sm) plus Pattern_Syntax */ + +#define R(a,b) { (b), (b)-(a) } + +static const struct range { + uint32_t base:20; + uint32_t len:12; +} ranges[] = { +R(0x21, 0x2f), +R(0x3a, 0x40), +R(0x5b, 0x60), +R(0x7b, 0x7e), +R(0xa1, 0xa7), +R(0xa9, 0xa9), +R(0xab, 0xac), +R(0xae, 0xae), +R(0xb0, 0xb1), +R(0xb6, 0xb7), +R(0xbb, 0xbb), +R(0xbf, 0xbf), +R(0xd7, 0xd7), +R(0xf7, 0xf7), +R(0x37e, 0x37e), +R(0x387, 0x387), +R(0x3f6, 0x3f6), +R(0x55a, 0x55f), +R(0x589, 0x58a), +R(0x5be, 0x5be), +R(0x5c0, 0x5c0), +R(0x5c3, 0x5c3), +R(0x5c6, 0x5c6), +R(0x5f3, 0x5f4), +R(0x606, 0x60a), +R(0x60c, 0x60d), +R(0x61b, 0x61b), +R(0x61e, 0x61f), +R(0x66a, 0x66d), +R(0x6d4, 0x6d4), +R(0x700, 0x70d), +R(0x7f7, 0x7f9), +R(0x964, 0x965), +R(0x970, 0x970), +R(0xdf4, 0xdf4), +R(0xe4f, 0xe4f), +R(0xe5a, 0xe5b), +R(0xf04, 0xf12), +R(0xf3a, 0xf3d), +R(0xf85, 0xf85), +R(0xfd0, 0xfd4), +R(0x104a, 0x104f), +R(0x10fb, 0x10fb), +R(0x1361, 0x1368), +R(0x166d, 0x166e), +R(0x1680, 0x1680), +R(0x169b, 0x169c), +R(0x16eb, 0x16ed), +R(0x1735, 0x1736), +R(0x17d4, 0x17d6), +R(0x17d8, 0x17da), +R(0x1800, 0x180a), +R(0x180e, 0x180e), +R(0x1944, 0x1945), +R(0x19de, 0x19df), +R(0x1a1e, 0x1a1f), +R(0x1b5a, 0x1b60), +R(0x1c3b, 0x1c3f), +R(0x1c7e, 0x1c7f), +R(0x2010, 0x2027), +R(0x2030, 0x205e), +R(0x207a, 0x207e), +R(0x208a, 0x208e), +R(0x2140, 0x2144), +R(0x214b, 0x214b), +R(0x2190, 0x245f), +R(0x2500, 0x2775), +R(0x2794, 0x2bff), +R(0x2cf9, 0x2cfc), +R(0x2cfe, 0x2cff), +R(0x2e00, 0x2e7f), +R(0x3001, 0x3003), +R(0x3008, 0x3020), +R(0x3030, 0x3030), +R(0x303d, 0x303d), +R(0x30a0, 0x30a0), +R(0x30fb, 0x30fb), +R(0xa60d, 0xa60f), +R(0xa874, 0xa877), +R(0xa8ce, 0xa8cf), +R(0xa92e, 0xa92f), +R(0xa95f, 0xa95f), +R(0xfb29, 0xfb29), +R(0xfd3e, 0xfd3f), +R(0xfe10, 0xfe19), +R(0xfe30, 0xfe52), +R(0xfe54, 0xfe66), +R(0xfe68, 0xfe68), +R(0xfe6a, 0xfe6b), +R(0xff01, 0xff03), +R(0xff05, 0xff0f), +R(0xff1a, 0xff20), +R(0xff3b, 0xff3d), +R(0xff3f, 0xff3f), +R(0xff5b, 0xff65), +R(0xffe2, 0xffe2), +R(0xffe9, 0xffec), +R(0x10100, 0x10101), +R(0x1039f, 0x1039f), +R(0x103d0, 0x103d0), +R(0x1091f, 0x1091f), +R(0x1093f, 0x1093f), +R(0x10a50, 0x10a58), +R(0x12470, 0x12473), +R(0x1d6c1, 0x1d6c1), +R(0x1d6db, 0x1d6db), +R(0x1d6fb, 0x1d6fb), +R(0x1d715, 0x1d715), +R(0x1d735, 0x1d735), +R(0x1d74f, 0x1d74f), +R(0x1d76f, 0x1d76f), +R(0x1d789, 0x1d789), +R(0x1d7a9, 0x1d7a9), +R(0x1d7c3, 0x1d7c3), +}; + +int iswpunct(wint_t wc) +{ + unsigned c = wc; + int a = 0; + int n = sizeof ranges / sizeof ranges[0]; + do { + n >>= 1; + a += n+1 & (signed)(ranges[a+n].base-c)>>31; + } while (n); + return ranges[a].base-c <= ranges[a].len; +} diff --git a/src/ctype/iswspace.c b/src/ctype/iswspace.c new file mode 100644 index 00000000..68a17437 --- /dev/null +++ b/src/ctype/iswspace.c @@ -0,0 +1,15 @@ +#include <wchar.h> +#include <wctype.h> +#include <ctype.h> + +int iswspace(wint_t wc) +{ + static const wchar_t spaces[] = { + ' ', '\t', '\n', '\r', 11, 12, 0x0085, + 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, + 0x2006, 0x2008, 0x2009, 0x200a, 0x200b, + 0x2028, 0x2029, 0x2050, 0x3000, 0 + }; + if (wcschr(spaces, wc)) return 1; + return 0; +} diff --git a/src/ctype/iswupper.c b/src/ctype/iswupper.c new file mode 100644 index 00000000..eae59a75 --- /dev/null +++ b/src/ctype/iswupper.c @@ -0,0 +1,6 @@ +#include <wctype.h> + +int iswupper(wint_t wc) +{ + return towlower(wc) != wc; +} diff --git a/src/ctype/iswxdigit.c b/src/ctype/iswxdigit.c new file mode 100644 index 00000000..229a469f --- /dev/null +++ b/src/ctype/iswxdigit.c @@ -0,0 +1,7 @@ +#include <wchar.h> +#include <wctype.h> + +int iswxdigit(wint_t wc) +{ + return (unsigned)(wc-'0') < 10 || (unsigned)((wc|32)-'a') < 6; +} diff --git a/src/ctype/isxdigit.c b/src/ctype/isxdigit.c new file mode 100644 index 00000000..ae68a3dc --- /dev/null +++ b/src/ctype/isxdigit.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isxdigit(int c) +{ + return isdigit(c) || ((unsigned)c|32)-'a' < 6; +} diff --git a/src/ctype/toascii.c b/src/ctype/toascii.c new file mode 100644 index 00000000..f0e48e8e --- /dev/null +++ b/src/ctype/toascii.c @@ -0,0 +1,7 @@ +#include <ctype.h> + +/* nonsense function that should NEVER be used! */ +int toascii(int c) +{ + return c & 0x7f; +} diff --git a/src/ctype/tolower.c b/src/ctype/tolower.c new file mode 100644 index 00000000..b56f3c50 --- /dev/null +++ b/src/ctype/tolower.c @@ -0,0 +1,7 @@ +#include <ctype.h> + +int tolower(int c) +{ + if (isupper(c)) return c | 32; + return c; +} diff --git a/src/ctype/toupper.c b/src/ctype/toupper.c new file mode 100644 index 00000000..1799f030 --- /dev/null +++ b/src/ctype/toupper.c @@ -0,0 +1,7 @@ +#include <ctype.h> + +int toupper(int c) +{ + if (islower(c)) return c & 0x5f; + return c; +} diff --git a/src/ctype/towctrans.c b/src/ctype/towctrans.c new file mode 100644 index 00000000..0b1eed04 --- /dev/null +++ b/src/ctype/towctrans.c @@ -0,0 +1,246 @@ +#include <wchar.h> +#include <wctype.h> +#include <stdio.h> + +#define CASEMAP(u1,u2,l) { (u1), (l)-(u1), (u2)-(u1)+1 } +#define CASELACE(u1,u2) CASEMAP((u1),(u2),(u1)+1) + +static const struct { + unsigned short upper; + signed char lower; + unsigned char len; +} casemaps[] = { + CASEMAP('A','Z','a'), + CASEMAP(0xc0,0xde,0xe0), + + CASELACE(0x0100,0x012e), + CASELACE(0x0132,0x0136), + CASELACE(0x0139,0x0147), + CASELACE(0x014a,0x0176), + CASELACE(0x0179,0x017d), + + CASELACE(0x370,0x372), + CASEMAP(0x391,0x3a1,0x3b1), + CASEMAP(0x3a3,0x3ab,0x3c3), + CASEMAP(0x400,0x40f,0x450), + CASEMAP(0x410,0x42f,0x430), + + CASELACE(0x460,0x480), + CASELACE(0x48a,0x4be), + CASELACE(0x4c1,0x4cd), + CASELACE(0x4d0,0x50e), + + CASEMAP(0x531,0x556,0x561), + + CASELACE(0x01a0,0x01a4), + CASELACE(0x01b3,0x01b5), + CASELACE(0x01cd,0x01db), + CASELACE(0x01de,0x01ee), + CASELACE(0x01f8,0x021e), + CASELACE(0x0222,0x0232), + CASELACE(0x03d8,0x03ee), + + CASELACE(0x1e00,0x1e94), + CASELACE(0x1ea0,0x1efe), + + CASEMAP(0x1f08,0x1f0f,0x1f00), + CASEMAP(0x1f18,0x1f1d,0x1f10), + CASEMAP(0x1f28,0x1f2f,0x1f20), + CASEMAP(0x1f38,0x1f3f,0x1f30), + CASEMAP(0x1f48,0x1f4d,0x1f40), + + CASEMAP(0x1f68,0x1f6f,0x1f60), + CASEMAP(0x1f88,0x1f8f,0x1f80), + CASEMAP(0x1f98,0x1f9f,0x1f90), + CASEMAP(0x1fa8,0x1faf,0x1fa0), + CASEMAP(0x1fb8,0x1fb9,0x1fb0), + CASEMAP(0x1fba,0x1fbb,0x1f70), + CASEMAP(0x1fc8,0x1fcb,0x1f72), + CASEMAP(0x1fd8,0x1fd9,0x1fd0), + CASEMAP(0x1fda,0x1fdb,0x1f76), + CASEMAP(0x1fe8,0x1fe9,0x1fe0), + CASEMAP(0x1fea,0x1feb,0x1f7a), + CASEMAP(0x1ff8,0x1ff9,0x1f78), + CASEMAP(0x1ffa,0x1ffb,0x1f7c), + + CASELACE(0x246,0x24e), + CASELACE(0x510,0x512), + CASEMAP(0x2160,0x216f,0x2170), + CASEMAP(0x2c00,0x2c2e,0x2c30), + CASELACE(0x2c67,0x2c6b), + CASELACE(0x2c80,0x2ce2), + + CASELACE(0xa722,0xa72e), + CASELACE(0xa732,0xa76e), + CASELACE(0xa779,0xa77b), + CASELACE(0xa77e,0xa786), + + CASEMAP(0xff21,0xff3a,0xff41), + { 0,0,0 } +}; + +static const unsigned short pairs[][2] = { + { 'I', 0x0131 }, + { 'S', 0x017f }, + { 0x0130, 'i' }, + { 0x0178, 0x00ff }, + { 0x0181, 0x0253 }, + { 0x0182, 0x0183 }, + { 0x0184, 0x0185 }, + { 0x0186, 0x0254 }, + { 0x0187, 0x0188 }, + { 0x0189, 0x0256 }, + { 0x018a, 0x0257 }, + { 0x018b, 0x018c }, + { 0x018e, 0x01dd }, + { 0x018f, 0x0259 }, + { 0x0190, 0x025b }, + { 0x0191, 0x0192 }, + { 0x0193, 0x0260 }, + { 0x0194, 0x0263 }, + { 0x0196, 0x0269 }, + { 0x0197, 0x0268 }, + { 0x0198, 0x0199 }, + { 0x019c, 0x026f }, + { 0x019d, 0x0272 }, + { 0x019f, 0x0275 }, + { 0x01a6, 0x0280 }, + { 0x01a7, 0x01a8 }, + { 0x01a9, 0x0283 }, + { 0x01ac, 0x01ad }, + { 0x01ae, 0x0288 }, + { 0x01af, 0x01b0 }, + { 0x01b1, 0x028a }, + { 0x01b2, 0x028b }, + { 0x01b7, 0x0292 }, + { 0x01b8, 0x01b9 }, + { 0x01bc, 0x01bd }, + { 0x01c4, 0x01c6 }, + { 0x01c4, 0x01c5 }, + { 0x01c5, 0x01c6 }, + { 0x01c7, 0x01c9 }, + { 0x01c7, 0x01c8 }, + { 0x01c8, 0x01c9 }, + { 0x01ca, 0x01cc }, + { 0x01ca, 0x01cb }, + { 0x01cb, 0x01cc }, + { 0x01f1, 0x01f3 }, + { 0x01f1, 0x01f2 }, + { 0x01f2, 0x01f3 }, + { 0x01f4, 0x01f5 }, + { 0x01f6, 0x0195 }, + { 0x01f7, 0x01bf }, + { 0x0220, 0x019e }, + { 0x0386, 0x03ac }, + { 0x0388, 0x03ad }, + { 0x0389, 0x03ae }, + { 0x038a, 0x03af }, + { 0x038c, 0x03cc }, + { 0x038e, 0x03cd }, + { 0x038f, 0x03ce }, + { 0x0399, 0x0345 }, + { 0x0399, 0x1fbe }, + { 0x03a3, 0x03c2 }, + { 0x03f7, 0x03f8 }, + { 0x03fa, 0x03fb }, + { 0x1e60, 0x1e9b }, + + { 0x1f59, 0x1f51 }, + { 0x1f5b, 0x1f53 }, + { 0x1f5d, 0x1f55 }, + { 0x1f5f, 0x1f57 }, + { 0x1fbc, 0x1fb3 }, + { 0x1fcc, 0x1fc3 }, + { 0x1fec, 0x1fe5 }, + { 0x1ffc, 0x1ff3 }, + + { 0x23a, 0x2c65 }, + { 0x23b, 0x23c }, + { 0x23d, 0x19a }, + { 0x23e, 0x2c66 }, + { 0x241, 0x242 }, + { 0x243, 0x180 }, + { 0x244, 0x289 }, + { 0x245, 0x28c }, + { 0x3f4, 0x3b8 }, + { 0x3f9, 0x3f2 }, + { 0x3fd, 0x37b }, + { 0x3fe, 0x37c }, + { 0x3ff, 0x37d }, + { 0x4c0, 0x4cf }, + + { 0x2126, 0x3c9 }, + { 0x212a, 'k' }, + { 0x212b, 0xe5 }, + { 0x2132, 0x214e }, + { 0x2183, 0x2184 }, + { 0x2c60, 0x2c61 }, + { 0x2c62, 0x26b }, + { 0x2c63, 0x1d7d }, + { 0x2c64, 0x27d }, + { 0x2c6d, 0x251 }, + { 0x2c6e, 0x271 }, + { 0x2c6f, 0x250 }, + { 0x2c72, 0x2c73 }, + { 0x2c75, 0x2c76 }, + + { 0xa77d, 0x1d79 }, + + /* bogus greek 'symbol' letters */ + { 0x376, 0x377 }, + { 0x39c, 0xb5 }, + { 0x392, 0x3d0 }, + { 0x398, 0x3d1 }, + { 0x3a6, 0x3d5 }, + { 0x3a0, 0x3d6 }, + { 0x39a, 0x3f0 }, + { 0x3a1, 0x3f1 }, + { 0x395, 0x3f5 }, + { 0x3cf, 0x3d7 }, + + { 0,0 } +}; + + +static wchar_t __towcase(wchar_t wc, int lower) +{ + int i; + int lmul = 2*lower-1; + int lmask = lower-1; + if ((unsigned)wc - 0x10400 < 0x50) + return wc + lmul*0x28; + /* no letters with case in these large ranges */ + if (!iswalpha(wc) + || (unsigned)wc - 0x0600 <= 0x0fff-0x0600 + || (unsigned)wc - 0x2e00 <= 0xa6ff-0x2e00 + || (unsigned)wc - 0xa800 <= 0xfeff-0xa800) + return wc; + /* special case because the diff between upper/lower is too big */ + if ((unsigned)wc - 0x10a0 < 0x26 || (unsigned)wc - 0x2d00 < 0x26) + return wc + lmul*(0x2d00-0x10a0); + for (i=0; casemaps[i].len; i++) { + int base = casemaps[i].upper + (lmask & casemaps[i].lower); + if ((unsigned)wc-base < casemaps[i].len) { + if (casemaps[i].lower == 1) + return wc + lower - ((wc-casemaps[i].upper)&1); + return wc + lmul*casemaps[i].lower; + } + } + for (i=0; pairs[i][1-lower]; i++) { + if (pairs[i][1-lower] == wc) + return pairs[i][lower]; + } + if ((unsigned)wc - 0x10428 + (lower<<5) + (lower<<3) < 0x28) + return wc - 0x28 + (lower<<10) + (lower<<6); + return wc; +} + +wint_t towupper(wint_t wc) +{ + return __towcase(wc, 0); +} + +wint_t towlower(wint_t wc) +{ + return __towcase(wc, 1); +} diff --git a/src/ctype/wcswidth.c b/src/ctype/wcswidth.c new file mode 100644 index 00000000..5c8a5a4d --- /dev/null +++ b/src/ctype/wcswidth.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +int wcswidth(const wchar_t *wcs, size_t n) +{ + int l=0, k=0; + for (; n-- && *wcs && (k = wcwidth(*wcs)) >= 0; l+=k, wcs++); + return (k < 0) ? k : l; +} diff --git a/src/ctype/wctrans.c b/src/ctype/wctrans.c new file mode 100644 index 00000000..03e9fd6a --- /dev/null +++ b/src/ctype/wctrans.c @@ -0,0 +1,16 @@ +#include <wctype.h> +#include <string.h> + +wctrans_t wctrans(const char *class) +{ + if (!strcmp(class, "toupper")) return 1; + if (!strcmp(class, "tolower")) return 2; + return 0; +} + +wint_t towctrans(wint_t wc, wctrans_t trans) +{ + if (trans == 1) return towupper(wc); + if (trans == 2) return towlower(wc); + return wc; +} diff --git a/src/ctype/wcwidth.c b/src/ctype/wcwidth.c new file mode 100644 index 00000000..ebc560a5 --- /dev/null +++ b/src/ctype/wcwidth.c @@ -0,0 +1,185 @@ +#include <inttypes.h> +#include <wchar.h> + +#define R(a,b,w) { (b), (w)/2, (b)-(a) } + +static const struct range { + uint32_t base:20; + uint32_t width:1; + uint32_t len:11; +} ranges[] = { + R(0x0300, 0x036F, 0), + R(0x0483, 0x0486, 0), + R(0x0488, 0x0489, 0), + R(0x0591, 0x05BD, 0), + R(0x05BF, 0x05BF, 0), + R(0x05C1, 0x05C2, 0), + R(0x05C4, 0x05C5, 0), + R(0x05C7, 0x05C7, 0), + R(0x0600, 0x0603, 0), + R(0x0610, 0x0615, 0), + R(0x064B, 0x065E, 0), + R(0x0670, 0x0670, 0), + R(0x06D6, 0x06E4, 0), + R(0x06E7, 0x06E8, 0), + R(0x06EA, 0x06ED, 0), + R(0x070F, 0x070F, 0), + R(0x0711, 0x0711, 0), + R(0x0730, 0x074A, 0), + R(0x07A6, 0x07B0, 0), + R(0x07EB, 0x07F3, 0), + R(0x0901, 0x0902, 0), + R(0x093C, 0x093C, 0), + R(0x0941, 0x0948, 0), + R(0x094D, 0x094D, 0), + R(0x0951, 0x0954, 0), + R(0x0962, 0x0963, 0), + R(0x0981, 0x0981, 0), + R(0x09BC, 0x09BC, 0), + R(0x09C1, 0x09C4, 0), + R(0x09CD, 0x09CD, 0), + R(0x09E2, 0x09E3, 0), + R(0x0A01, 0x0A02, 0), + R(0x0A3C, 0x0A3C, 0), + R(0x0A41, 0x0A42, 0), + R(0x0A47, 0x0A48, 0), + R(0x0A4B, 0x0A4D, 0), + R(0x0A70, 0x0A71, 0), + R(0x0A81, 0x0A82, 0), + R(0x0ABC, 0x0ABC, 0), + R(0x0AC1, 0x0AC5, 0), + R(0x0AC7, 0x0AC8, 0), + R(0x0ACD, 0x0ACD, 0), + R(0x0AE2, 0x0AE3, 0), + R(0x0B01, 0x0B01, 0), + R(0x0B3C, 0x0B3C, 0), + R(0x0B3F, 0x0B3F, 0), + R(0x0B41, 0x0B43, 0), + R(0x0B4D, 0x0B4D, 0), + R(0x0B56, 0x0B56, 0), + R(0x0B82, 0x0B82, 0), + R(0x0BC0, 0x0BC0, 0), + R(0x0BCD, 0x0BCD, 0), + R(0x0C3E, 0x0C40, 0), + R(0x0C46, 0x0C48, 0), + R(0x0C4A, 0x0C4D, 0), + R(0x0C55, 0x0C56, 0), + R(0x0CBC, 0x0CBC, 0), + R(0x0CBF, 0x0CBF, 0), + R(0x0CC6, 0x0CC6, 0), + R(0x0CCC, 0x0CCD, 0), + R(0x0CE2, 0x0CE3, 0), + R(0x0D41, 0x0D43, 0), + R(0x0D4D, 0x0D4D, 0), + R(0x0DCA, 0x0DCA, 0), + R(0x0DD2, 0x0DD4, 0), + R(0x0DD6, 0x0DD6, 0), + R(0x0E31, 0x0E31, 0), + R(0x0E34, 0x0E3A, 0), + R(0x0E47, 0x0E4E, 0), + R(0x0EB1, 0x0EB1, 0), + R(0x0EB4, 0x0EB9, 0), + R(0x0EBB, 0x0EBC, 0), + R(0x0EC8, 0x0ECD, 0), + R(0x0F18, 0x0F19, 0), + R(0x0F35, 0x0F35, 0), + R(0x0F37, 0x0F37, 0), + R(0x0F39, 0x0F39, 0), + R(0x0F71, 0x0F7E, 0), + R(0x0F80, 0x0F84, 0), + R(0x0F86, 0x0F87, 0), + R(0x0F90, 0x0F97, 0), + R(0x0F99, 0x0FBC, 0), + R(0x0FC6, 0x0FC6, 0), + R(0x102D, 0x1030, 0), + R(0x1032, 0x1032, 0), + R(0x1036, 0x1037, 0), + R(0x1039, 0x1039, 0), + R(0x1058, 0x1059, 0), + R(0x1100, 0x115F, 2), + R(0x1160, 0x11FF, 0), + R(0x135F, 0x135F, 0), + R(0x1712, 0x1714, 0), + R(0x1732, 0x1734, 0), + R(0x1752, 0x1753, 0), + R(0x1772, 0x1773, 0), + R(0x17B4, 0x17B5, 0), + R(0x17B7, 0x17BD, 0), + R(0x17C6, 0x17C6, 0), + R(0x17C9, 0x17D3, 0), + R(0x17DD, 0x17DD, 0), + R(0x180B, 0x180D, 0), + R(0x18A9, 0x18A9, 0), + R(0x1920, 0x1922, 0), + R(0x1927, 0x1928, 0), + R(0x1932, 0x1932, 0), + R(0x1939, 0x193B, 0), + R(0x1A17, 0x1A18, 0), + R(0x1B00, 0x1B03, 0), + R(0x1B34, 0x1B34, 0), + R(0x1B36, 0x1B3A, 0), + R(0x1B3C, 0x1B3C, 0), + R(0x1B42, 0x1B42, 0), + R(0x1B6B, 0x1B73, 0), + R(0x1DC0, 0x1DCA, 0), + R(0x1DFE, 0x1DFF, 0), + R(0x200B, 0x200F, 0), + R(0x202A, 0x202E, 0), + R(0x2060, 0x2063, 0), + R(0x206A, 0x206F, 0), + R(0x20D0, 0x20EF, 0), + R(0x2329, 0x232A, 2), + R(0x2E80, 0x3029, 2), + R(0x302A, 0x302F, 0), + R(0x3030, 0x303E, 2), + R(0x3099, 0x309A, 0), + R(0xA806, 0xA806, 0), + R(0xA80B, 0xA80B, 0), + R(0xA825, 0xA826, 0), + R(0xF900, 0xFAFF, 2), + R(0xFB1E, 0xFB1E, 0), + R(0xFE00, 0xFE0F, 0), + R(0xFE20, 0xFE23, 0), + R(0xFE30, 0xFE6F, 2), + R(0xFEFF, 0xFEFF, 0), + R(0xFF00, 0xFF60, 2), + R(0xFFE0, 0xFFE6, 2), + R(0x10A01, 0x10A03, 0), + R(0x10A05, 0x10A06, 0), + R(0x10A0C, 0x10A0F, 0), + R(0x10A38, 0x10A3A, 0), + R(0x10A3F, 0x10A3F, 0), + R(0x1D167, 0x1D169, 0), + R(0x1D173, 0x1D182, 0), + R(0x1D185, 0x1D18B, 0), + R(0x1D1AA, 0x1D1AD, 0), + R(0x1D242, 0x1D244, 0), + R(0xE0001, 0xE0001, 0), + R(0xE0020, 0xE007F, 0), + R(0xE0100, 0xE01EF, 0), +}; + +/* Note: because the len field is only 10 bits, we must special-case + * the two huge ranges of full width characters and exclude them + * from the binary search table. */ + +int wcwidth(wchar_t wc) +{ + int a, n; + uint32_t c = wc; + + if (c-0x20 < 0x5f) return 1; + if (!iswprint(c)) return wc ? -1 : 0; + if (c-0x20000 < 0x20000) return 2; + + /* The following code is a branchless binary search. */ + a = 0; + n = sizeof ranges / sizeof ranges[0]; + do { + n >>= 1; + a += n+1 & (signed)(ranges[a+n].base-c)>>31; + } while (n); + if (ranges[a].base-c <= ranges[a].len) + return 2*ranges[a].width; + return 1 + (c-0x3040 < 0xd800-0x3040); +} diff --git a/src/dirent/__dirent.h b/src/dirent/__dirent.h new file mode 100644 index 00000000..07b3ee68 --- /dev/null +++ b/src/dirent/__dirent.h @@ -0,0 +1,9 @@ +struct __DIR_s +{ + int lock; + int fd; + off_t tell; + int buf_pos; + int buf_end; + char buf[2048]; +}; diff --git a/src/dirent/__getdents.c b/src/dirent/__getdents.c new file mode 100644 index 00000000..4195430b --- /dev/null +++ b/src/dirent/__getdents.c @@ -0,0 +1,12 @@ +#include <dirent.h> +#include "syscall.h" +#include "libc.h" + +int __getdents(int fd, struct dirent *buf, size_t len) +{ + return syscall3(__NR_getdents64, fd, (long)buf, len); +} + +weak_alias(__getdents, getdents); + +LFS64(getdents); diff --git a/src/dirent/alphasort.c b/src/dirent/alphasort.c new file mode 100644 index 00000000..42050fb7 --- /dev/null +++ b/src/dirent/alphasort.c @@ -0,0 +1,10 @@ +#include <string.h> +#include <dirent.h> +#include "libc.h" + +int alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +LFS64(alphasort); diff --git a/src/dirent/closedir.c b/src/dirent/closedir.c new file mode 100644 index 00000000..81e9591c --- /dev/null +++ b/src/dirent/closedir.c @@ -0,0 +1,11 @@ +#include <dirent.h> +#include <unistd.h> +#include "__dirent.h" +#include "libc.h" + +int closedir(DIR *dir) +{ + int ret = close(dir->fd); + free(dir); + return ret; +} diff --git a/src/dirent/dirfd.c b/src/dirent/dirfd.c new file mode 100644 index 00000000..6c860073 --- /dev/null +++ b/src/dirent/dirfd.c @@ -0,0 +1,7 @@ +#include <dirent.h> +#include "__dirent.h" + +int dirfd(DIR *d) +{ + return d->fd; +} diff --git a/src/dirent/fdopendir.c b/src/dirent/fdopendir.c new file mode 100644 index 00000000..c4b8e61d --- /dev/null +++ b/src/dirent/fdopendir.c @@ -0,0 +1,26 @@ +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include "__dirent.h" + +DIR *fdopendir(int fd) +{ + DIR *dir; + struct stat st; + + if (fstat(fd, &st) < 0 || !S_ISDIR(st.st_mode)) { + errno = ENOTDIR; + return 0; + } + if (!(dir = calloc(1, sizeof *dir))) { + return 0; + } + + fcntl(fd, F_SETFD, FD_CLOEXEC); + dir->fd = fd; + return dir; +} diff --git a/src/dirent/opendir.c b/src/dirent/opendir.c new file mode 100644 index 00000000..cefe6ce7 --- /dev/null +++ b/src/dirent/opendir.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include "__dirent.h" + +DIR *opendir(const char *name) +{ + int fd; + DIR *dir; + + if ((fd = open(name, O_RDONLY|O_DIRECTORY)) < 0) + return 0; + fcntl(fd, F_SETFD, FD_CLOEXEC); + if (!(dir = calloc(1, sizeof *dir))) { + close(fd); + return 0; + } + dir->fd = fd; + return dir; +} diff --git a/src/dirent/readdir.c b/src/dirent/readdir.c new file mode 100644 index 00000000..1aeb25a5 --- /dev/null +++ b/src/dirent/readdir.c @@ -0,0 +1,32 @@ +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <limits.h> +#include "__dirent.h" +#include "syscall.h" +#include "libc.h" + +int __getdents(int, struct dirent *, size_t); + +struct dirent *readdir(DIR *dir) +{ + struct dirent *de; + + if (dir->buf_pos >= dir->buf_end) { + int len = __getdents(dir->fd, (void *)dir->buf, sizeof dir->buf); + if (len < 0) { + dir->lock = 0; + return NULL; + } else if (len == 0) return 0; + dir->buf_end = len; + dir->buf_pos = 0; + } + de = (void *)(dir->buf + dir->buf_pos); + dir->buf_pos += de->d_reclen; + dir->tell = de->d_off; + return de; +} + +LFS64(readdir); diff --git a/src/dirent/readdir_r.c b/src/dirent/readdir_r.c new file mode 100644 index 00000000..58f60325 --- /dev/null +++ b/src/dirent/readdir_r.c @@ -0,0 +1,30 @@ +#include <dirent.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include "__dirent.h" +#include "libc.h" + +int readdir_r(DIR *dir, struct dirent *buf, struct dirent **result) +{ + struct dirent *de; + int errno_save = errno; + int ret; + + LOCK(&dir->lock); + errno = 0; + de = readdir(dir); + if ((ret = errno)) { + UNLOCK(&dir->lock); + return ret; + } + errno = errno_save; + if (de) memcpy(buf, de, de->d_reclen); + else buf = NULL; + + UNLOCK(&dir->lock); + *result = buf; + return 0; +} + +LFS64_2(readdir_r, readdir64_r); diff --git a/src/dirent/rewinddir.c b/src/dirent/rewinddir.c new file mode 100644 index 00000000..c6138f7c --- /dev/null +++ b/src/dirent/rewinddir.c @@ -0,0 +1,13 @@ +#include <dirent.h> +#include <unistd.h> +#include "__dirent.h" +#include "libc.h" + +void rewinddir(DIR *dir) +{ + LOCK(&dir->lock); + lseek(dir->fd, 0, SEEK_SET); + dir->buf_pos = dir->buf_end = 0; + dir->tell = 0; + UNLOCK(&dir->lock); +} diff --git a/src/dirent/scandir.c b/src/dirent/scandir.c new file mode 100644 index 00000000..6a0a9993 --- /dev/null +++ b/src/dirent/scandir.c @@ -0,0 +1,50 @@ +#include <dirent.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <stddef.h> +#include <libc.h> + +int scandir(const char *path, struct dirent ***res, + int (*sel)(const struct dirent *), + int (*cmp)(const struct dirent **, const struct dirent **)) +{ + DIR *d = opendir(path); + struct dirent *de, **names=0, **tmp; + size_t cnt=0, len=0, size; + int old_errno = errno; + + if (!d) return -1; + + while ((errno=0), (de = readdir(d))) { + if (sel && !sel(de)) continue; + if (cnt >= len) { + len = 2*len+1; + if (len > SIZE_MAX/sizeof *names) break; + tmp = realloc(names, len * sizeof *names); + if (!tmp) break; + names = tmp; + } + size = offsetof(struct dirent,d_name) + strlen(de->d_name) + 1; + names[cnt] = malloc(size); + if (!names[cnt]) break; + memcpy(names[cnt++], de, size); + } + + closedir(d); + + if (errno) { + old_errno = errno; + if (names) while (cnt-->0) free(names[cnt]); + free(names); + errno = old_errno; + return -1; + } + + if (cmp) qsort(names, cnt, sizeof *names, (int (*)(const void *, const void *))cmp); + *res = names; + return cnt; +} + +LFS64(scandir); diff --git a/src/dirent/seekdir.c b/src/dirent/seekdir.c new file mode 100644 index 00000000..81a0e331 --- /dev/null +++ b/src/dirent/seekdir.c @@ -0,0 +1,12 @@ +#include <dirent.h> +#include <unistd.h> +#include "__dirent.h" +#include "libc.h" + +void seekdir(DIR *dir, long off) +{ + LOCK(&dir->lock); + dir->tell = lseek(dir->fd, off, SEEK_SET); + dir->buf_pos = dir->buf_end = 0; + UNLOCK(&dir->lock); +} diff --git a/src/dirent/telldir.c b/src/dirent/telldir.c new file mode 100644 index 00000000..cf25acff --- /dev/null +++ b/src/dirent/telldir.c @@ -0,0 +1,7 @@ +#include <dirent.h> +#include "__dirent.h" + +long telldir(DIR *dir) +{ + return dir->tell; +} diff --git a/src/env/__environ.c b/src/env/__environ.c new file mode 100644 index 00000000..d7bd5e50 --- /dev/null +++ b/src/env/__environ.c @@ -0,0 +1,7 @@ +#include "libc.h" + +#undef environ +char **___environ = 0; +weak_alias(___environ, __environ); +weak_alias(___environ, _environ); +weak_alias(___environ, environ); diff --git a/src/env/__libc_start_main.c b/src/env/__libc_start_main.c new file mode 100644 index 00000000..70af77b5 --- /dev/null +++ b/src/env/__libc_start_main.c @@ -0,0 +1,26 @@ +#include "libc.h" + +/* Any use of __environ/environ will override this symbol. */ +char **__dummy_environ = (void *)-1; +weak_alias(__dummy_environ, ___environ); + +int __libc_start_main( + int (*main)(int, char **, char **), int argc, char **argv, + int (*init)(int, char **, char **), void (*fini)(void), + void (*ldso_fini)(void)) +{ + /* Save the environment if it may be used by libc/application */ + char **envp = argv+argc+1; + if (___environ != (void *)-1) ___environ = envp; + + /* Avoid writing 0 and triggering unnecessary COW */ + if (ldso_fini) libc.ldso_fini = ldso_fini; + if (fini) libc.fini = fini; + + /* Execute constructors (static) linked into the application */ + if (init) init(argc, argv, envp); + + /* Pass control to to application */ + exit(main(argc, argv, envp)); + return 0; +} diff --git a/src/env/clearenv.c b/src/env/clearenv.c new file mode 100644 index 00000000..a2475ce7 --- /dev/null +++ b/src/env/clearenv.c @@ -0,0 +1,9 @@ +#include <stdlib.h> + +extern char **__environ; + +int clearenv() +{ + __environ[0] = 0; + return 0; +} diff --git a/src/env/getenv.c b/src/env/getenv.c new file mode 100644 index 00000000..00c1bce0 --- /dev/null +++ b/src/env/getenv.c @@ -0,0 +1,14 @@ +#include <stdlib.h> +#include <string.h> +#include "libc.h" + +char *getenv(const char *name) +{ + int i; + size_t l = strlen(name); + if (!__environ || !*name || strchr(name, '=')) return NULL; + for (i=0; __environ[i] && (strncmp(name, __environ[i], l) + || __environ[i][l] != '='); i++); + if (__environ[i]) return __environ[i] + l+1; + return NULL; +} diff --git a/src/env/putenv.c b/src/env/putenv.c new file mode 100644 index 00000000..181a4181 --- /dev/null +++ b/src/env/putenv.c @@ -0,0 +1,59 @@ +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdio.h> + +extern char **__environ; +char **__env_map; + +int __putenv(char *s, int a) +{ + int i=0, j=0; + char *end = strchr(s, '='); + size_t l = end-s+1; + char **newenv = 0; + char **newmap = 0; + static char **oldenv; + + if (!end || l == 1) return -1; + for (; __environ[i] && memcmp(s, __environ[i], l); i++); + if (a) { + if (!__env_map) { + __env_map = calloc(2, sizeof(char *)); + if (__env_map) __env_map[0] = s; + } else { + for (; __env_map[j] && __env_map[j] != __environ[i]; j++); + if (!__env_map[j]) { + newmap = realloc(__env_map, sizeof(char *)*(j+2)); + if (newmap) { + __env_map = newmap; + __env_map[j] = s; + __env_map[j+1] = NULL; + } + } else { + free(__env_map[j]); + } + } + } + if (!__environ[i]) { + newenv = malloc(sizeof(char *)*(i+2)); + if (!newenv) { + if (a && __env_map) __env_map[j] = 0; + return -1; + } + memcpy(newenv, __environ, sizeof(char *)*i); + newenv[i] = s; + newenv[i+1] = 0; + __environ = newenv; + free(oldenv); + oldenv = __environ; + } + + __environ[i] = s; + return 0; +} + +int putenv(char *s) +{ + return __putenv(s, 0); +} diff --git a/src/env/setenv.c b/src/env/setenv.c new file mode 100644 index 00000000..03e165c8 --- /dev/null +++ b/src/env/setenv.c @@ -0,0 +1,31 @@ +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +int __putenv(char *s, int a); + +int setenv(const char *var, const char *value, int overwrite) +{ + char *s; + int l1, l2; + + if (strchr(var, '=')) { + errno = EINVAL; + return -1; + } + if (!overwrite && getenv(var)) return 0; + + l1 = strlen(var); + l2 = strlen(value); + s = malloc(l1+l2+2); + memcpy(s, var, l1); + s[l1] = '='; + memcpy(s+l1+1, value, l2); + s[l1+l2+1] = 0; + if (__putenv(s, 1)) { + free(s); + errno = ENOMEM; + return -1; + } + return 0; +} diff --git a/src/env/unsetenv.c b/src/env/unsetenv.c new file mode 100644 index 00000000..7493d970 --- /dev/null +++ b/src/env/unsetenv.c @@ -0,0 +1,32 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +extern char **__environ; +extern char **__env_map; + +int unsetenv(const char *name) +{ + int i, j; + size_t l = strlen(name); + + if (!*name || strchr(name, '=')) { + errno = EINVAL; + return -1; + } +again: + for (i=0; __environ[i] && (memcmp(name, __environ[i], l) || __environ[i][l] != '='); i++); + if (__environ[i]) { + if (__env_map) { + for (j=0; __env_map[j] && __env_map[j] != __environ[i]; j++); + free (__env_map[j]); + for (; __env_map[j]; j++) + __env_map[j] = __env_map[j+1]; + } + for (; __environ[i]; i++) + __environ[i] = __environ[i+1]; + goto again; + } + return 0; +} diff --git a/src/errno/__errno_location.c b/src/errno/__errno_location.c new file mode 100644 index 00000000..0a220b63 --- /dev/null +++ b/src/errno/__errno_location.c @@ -0,0 +1,11 @@ +#include <errno.h> +#include "libc.h" + +#undef errno +int errno; + +int *__errno_location(void) +{ + if (libc.errno_location) return libc.errno_location(); + return &errno; +} diff --git a/src/errno/__strerror.h b/src/errno/__strerror.h new file mode 100644 index 00000000..00eaf938 --- /dev/null +++ b/src/errno/__strerror.h @@ -0,0 +1,101 @@ +/* This file is sorted such that 'errors' which represent exceptional + * conditions under which a correct program may fail come first, followed + * by messages that indicate an incorrect program or system failure. The + * macro E() along with double-inclusion is used to ensure that ordering + * of the strings remains synchronized. */ + +E(EILSEQ, "Illegal byte sequence") +E(EDOM, "Argument outside domain") +E(ERANGE, "Result not representable") + +E(ENOTTY, "Not a tty") +E(EACCES, "Permission denied") +E(EPERM, "Operation not permitted") +E(ENOENT, "No such file or directory") +E(ESRCH, "No such process") +E(EEXIST, "File exists") + +E(EOVERFLOW, "Value too large for defined data type") +E(ENOSPC, "No space left on device") +E(ENOMEM, "Out of memory") + +E(EBUSY, "Device or resource busy") +E(EINTR, "Interrupted system call") +E(EAGAIN, "Operation would block") +E(ESPIPE, "Illegal seek") + +E(EXDEV, "Cross-device link") +E(EROFS, "Read-only file system") +E(ENOTEMPTY, "Directory not empty") + +E(ECONNRESET, "Connection reset by peer") +E(ETIMEDOUT, "Connection timed out") +E(ECONNREFUSED, "Connection refused") +E(EHOSTDOWN, "Host is down") +E(EHOSTUNREACH, "No route to host") +E(EADDRINUSE, "Address already in use") + +E(EPIPE, "Broken pipe") +E(EIO, "I/O error") +E(ENXIO, "No such device or address") +E(ENOTBLK, "Block device required") +E(ENODEV, "No such device") +E(ENOTDIR, "Not a directory") +E(EISDIR, "Is a directory") +E(ETXTBSY, "Text file busy") +E(ENOEXEC, "Exec format error") + +E(EINVAL, "Invalid argument") + +E(E2BIG, "Argument list too long") +E(ELOOP, "Too many levels of symbolic links") +E(ENAMETOOLONG, "Filename too long") +E(ENFILE, "File table overflow") +E(EMFILE, "Too many open files") +E(EBADF, "Bad file number") +E(ECHILD, "No child processes") +E(EFAULT, "Bad address") +E(EFBIG, "File too large") +E(EMLINK, "Too many links") +E(ENOLCK, "No record locks available") + +E(EDEADLK, "Resource deadlock would occur") +E(ENOSYS, "Function not supported") +E(ENOMSG, "No message of desired type") +E(EIDRM, "Identifier removed") +E(ENOSTR, "Device not a stream") +E(ENODATA, "No data available") +E(ETIME, "Timer expired") +E(ENOSR, "Out of streams resources") +E(ENOLINK, "Link has been severed") +E(EPROTO, "Protocol error") +E(EBADMSG, "Not a data message") +E(EBADFD, "File descriptor in bad state") +E(ENOTSOCK, "Socket operation on non-socket") +E(EDESTADDRREQ, "Destination address required") +E(EMSGSIZE, "Message too long") +E(EPROTOTYPE, "Protocol wrong type for socket") +E(ENOPROTOOPT, "Protocol not available") +E(EPROTONOSUPPORT,"Protocol not supported") +E(ESOCKTNOSUPPORT,"Socket type not supported") +E(EOPNOTSUPP, "Operation not supported on socket") +E(EPFNOSUPPORT, "Protocol family not supported") +E(EAFNOSUPPORT, "Address family not supported by protocol") +E(EADDRNOTAVAIL,"Cannot assign requested address") +E(ENETDOWN, "Network is down") +E(ENETUNREACH, "Network is unreachable") +E(ENETRESET, "Network dropped connection because of reset") +E(ECONNABORTED, "Software caused connection abort") +E(ENOBUFS, "No buffer space available") +E(EISCONN, "Socket is connected") +E(ENOTCONN, "Socket is not connected") +E(ESHUTDOWN, "Cannot send after socket shutdown") +E(EALREADY, "Operation already in progress") +E(EINPROGRESS, "Operation now in progress") +E(ESTALE, "Stale NFS file handle") +E(EREMOTEIO, "Remote I/O error") +E(EDQUOT, "Quota exceeded") +E(ENOMEDIUM, "No medium found") +E(EMEDIUMTYPE, "Wrong medium type") + +E(0, "Invalid error number") diff --git a/src/errno/strerror.c b/src/errno/strerror.c new file mode 100644 index 00000000..b8fbc6db --- /dev/null +++ b/src/errno/strerror.c @@ -0,0 +1,22 @@ +#include <errno.h> +#include <string.h> + +#define E(a,b) a, +static const unsigned char errid[] = { +#include "__strerror.h" +}; + +#undef E +#define E(a,b) b "\0" +static const char errmsg[] = +#include "__strerror.h" +; + +char *strerror(int e) +{ + const char *s; + int i; + for (i=0; errid[i] && errid[i] != e; i++); + for (s=errmsg; i; s++, i--) for (; *s; s++); + return (char *)s; +} diff --git a/src/exit/_Exit.c b/src/exit/_Exit.c new file mode 100644 index 00000000..8ef85a8f --- /dev/null +++ b/src/exit/_Exit.c @@ -0,0 +1,9 @@ +#include <stdlib.h> +#define SYSCALL_NORETURN +#include "syscall.h" + +void _Exit(int ec) +{ + syscall1(__NR_exit_group, ec); + syscall1(__NR_exit, ec); +} diff --git a/src/exit/abort.c b/src/exit/abort.c new file mode 100644 index 00000000..9a1c3d40 --- /dev/null +++ b/src/exit/abort.c @@ -0,0 +1,8 @@ +#include <stdlib.h> +#include <signal.h> + +void abort(void) +{ + raise(SIGABRT); + for (;;); +} diff --git a/src/exit/assert.c b/src/exit/assert.c new file mode 100644 index 00000000..e87442a7 --- /dev/null +++ b/src/exit/assert.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <stdlib.h> + +void __assert_fail(const char *expr, const char *file, int line, const char *func) +{ + fprintf(stderr, "Assertion failed: %s (%s: %s: %d)\n", expr, file, func, line); + fflush(NULL); + abort(); +} diff --git a/src/exit/atexit.c b/src/exit/atexit.c new file mode 100644 index 00000000..49c060e6 --- /dev/null +++ b/src/exit/atexit.c @@ -0,0 +1,57 @@ +#include <stddef.h> +#include <stdlib.h> +#include <limits.h> +#include "libc.h" + +/* Ensure that at least 32 atexit handlers can be registered without malloc */ +#define COUNT 32 + +static struct fl +{ + struct fl *next; + void (*f[COUNT])(void); +} builtin, *head; + +static int run_atexit_functions(void) +{ + int i; + for (; head; head=head->next) { + for (i=COUNT-1; i>=0 && !head->f[i]; i--); + for (; i>=0; i--) head->f[i](); + } + return 0; +} + +int (*const __funcs_on_exit)(void) = run_atexit_functions; + +int atexit(void (*func)(void)) +{ + static int lock; + int i; + + /* Hook for atexit extensions */ + if (libc.atexit) return libc.atexit(func); + + LOCK(&lock); + + /* Defer initialization of head so it can be in BSS */ + if (!head) head = &builtin; + + /* If the current function list is full, add a new one */ + if (head->f[COUNT-1]) { + struct fl *new_fl = calloc(sizeof(struct fl), 1); + if (!new_fl) { + UNLOCK(&lock); + return -1; + } + new_fl->next = head; + head = new_fl; + } + + /* Append function to the list. */ + for (i=0; i<COUNT && head->f[i]; i++); + head->f[i] = func; + + UNLOCK(&lock); + return 0; +} diff --git a/src/exit/exit.c b/src/exit/exit.c new file mode 100644 index 00000000..d0c1bfc1 --- /dev/null +++ b/src/exit/exit.c @@ -0,0 +1,28 @@ +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include "libc.h" + +/* __overflow.c and atexit.c override these */ +static int (*const dummy)() = 0; +weak_alias(dummy, __funcs_on_exit); +weak_alias(dummy, __fflush_on_exit); + +void exit(int code) +{ + static int lock; + + /* If more than one thread calls exit, hang until _Exit ends it all */ + LOCK(&lock); + + /* Only do atexit & stdio flush if they were actually used */ + if (__funcs_on_exit) __funcs_on_exit(); + if (__fflush_on_exit) __fflush_on_exit(0); + + /* Destructor s**t is kept separate from atexit to avoid bloat */ + if (libc.fini) libc.fini(); + if (libc.ldso_fini) libc.ldso_fini(); + + _Exit(code); + for(;;); +} diff --git a/src/fcntl/creat.c b/src/fcntl/creat.c new file mode 100644 index 00000000..be05faae --- /dev/null +++ b/src/fcntl/creat.c @@ -0,0 +1,9 @@ +#include <fcntl.h> +#include "libc.h" + +int creat(const char *filename, mode_t mode) +{ + return open(filename, O_CREAT|O_WRONLY|O_TRUNC, mode); +} + +LFS64(creat); diff --git a/src/fcntl/fcntl.c b/src/fcntl/fcntl.c new file mode 100644 index 00000000..464dbf00 --- /dev/null +++ b/src/fcntl/fcntl.c @@ -0,0 +1,22 @@ +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> +#include "syscall.h" +#include "libc.h" + +int fcntl(int fd, int cmd, ...) +{ + int r; + long arg; + va_list ap; + va_start(ap, cmd); + arg = va_arg(ap, long); + va_end(ap); + if (cmd == F_SETFL) arg |= O_LARGEFILE; + if (cmd == F_SETLKW) CANCELPT_BEGIN; + r = __syscall_fcntl(fd, cmd, arg); + if (cmd == F_SETLKW) CANCELPT_END; + return r; +} + +LFS64(fcntl); diff --git a/src/fcntl/open.c b/src/fcntl/open.c new file mode 100644 index 00000000..4c1a591d --- /dev/null +++ b/src/fcntl/open.c @@ -0,0 +1,21 @@ +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> +#include "syscall.h" +#include "libc.h" + +int open(const char *filename, int flags, ...) +{ + int r; + mode_t mode; + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + CANCELPT_BEGIN; + r = __syscall_open(filename, flags, mode); + CANCELPT_END; + return r; +} + +LFS64(open); diff --git a/src/fcntl/openat.c b/src/fcntl/openat.c new file mode 100644 index 00000000..eefa0901 --- /dev/null +++ b/src/fcntl/openat.c @@ -0,0 +1,21 @@ +#include <fcntl.h> +#include <unistd.h> +#include <stdarg.h> +#include "syscall.h" +#include "libc.h" + +int openat(int fd, const char *filename, int flags, ...) +{ + int r; + mode_t mode; + va_list ap; + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); + CANCELPT_BEGIN; + r = syscall4(__NR_openat, fd, (long)filename, flags|O_LARGEFILE, mode); + CANCELPT_END; + return r; +} + +LFS64(openat); diff --git a/src/internal/atomic.h b/src/internal/atomic.h new file mode 100644 index 00000000..a15f8c2a --- /dev/null +++ b/src/internal/atomic.h @@ -0,0 +1,110 @@ +#ifndef _INTERNAA_ATOMIC_H +#define _INTERNAA_ATOMIC_H + +#include <stdint.h> + +static inline int a_ctz_64(uint64_t x) +{ + int r; + __asm__( "bsf %1,%0 ; jnz 1f ; bsf %2,%0 ; addl $32,%0\n1:" + : "=r"(r) : "r"((unsigned)x), "r"((unsigned)(x>>32)) ); + return r; +} + + +static inline void a_and_64(volatile uint64_t *p, uint64_t v) +{ + __asm__( "lock ; andl %1, (%0) ; lock ; andl %2, 4(%0)" + : : "r"((long *)p), "r"((unsigned)v), "r"((unsigned)(v>>32)) ); +} + +static inline void a_or_64(volatile uint64_t *p, uint64_t v) +{ + __asm__( "lock ; orl %1, (%0) ; lock ; orl %2, 4(%0)" + : : "r"((long *)p), "r"((unsigned)v), "r"((unsigned)(v>>32)) ); +} + +static inline void a_store_l(volatile void *p, long x) +{ + __asm__( "movl %1, %0" : "=m"(*(long *)p) : "r"(x) : "memory" ); +} + +static inline void a_or_l(volatile void *p, long v) +{ + __asm__( "lock ; orl %1, %0" + : "=m"(*(long *)p) : "r"(v) ); +} + +static inline void *a_cas_p(volatile void *p, void *t, void *s) +{ + __asm__( "lock ; cmpxchg %3, %1" + : "=a"(t), "=m"(*(long *)p) : "a"(t), "r"(s) ); + return t; +} + +static inline long a_cas_l(volatile void *p, long t, long s) +{ + __asm__( "lock ; cmpxchg %3, %1" + : "=a"(t), "=m"(*(long *)p) : "a"(t), "r"(s) ); + return t; +} + +static inline void *a_swap_p(void *volatile *x, void *v) +{ + __asm__( "xchg %0, %1" : "=r"(v), "=m"(*(void **)x) : "0"(v) ); + return v; +} +static inline long a_swap_l(volatile void *x, long v) +{ + __asm__( "xchg %0, %1" : "=r"(v), "=m"(*(long *)x) : "0"(v) ); + return v; +} + +static inline void a_or(volatile void *p, int v) +{ + __asm__( "lock ; orl %1, %0" + : "=m"(*(int *)p) : "r"(v) ); +} + +static inline void a_and(volatile void *p, int v) +{ + __asm__( "lock ; andl %1, %0" + : "=m"(*(int *)p) : "r"(v) ); +} + +static inline int a_swap(volatile int *x, int v) +{ + __asm__( "xchg %0, %1" : "=r"(v), "=m"(*x) : "0"(v) ); + return v; +} + +#define a_xchg a_swap + +static inline int a_fetch_add(volatile int *x, int v) +{ + __asm__( "lock ; xadd %0, %1" : "=r"(v), "=m"(*x) : "0"(v) ); + return v; +} + +static inline void a_inc(volatile int *x) +{ + __asm__( "lock ; incl %0" : "=m"(*x) : "m"(*x) ); +} + +static inline void a_dec(volatile int *x) +{ + __asm__( "lock ; decl %0" : "=m"(*x) : "m"(*x) ); +} + +static inline void a_store(volatile int *p, int x) +{ + __asm__( "movl %1, %0" : "=m"(*p) : "r"(x) : "memory" ); +} + +static inline void a_spin() +{ + __asm__ __volatile__( "pause" : : : "memory" ); +} + + +#endif diff --git a/src/internal/clone.h b/src/internal/clone.h new file mode 100644 index 00000000..cc84aeb4 --- /dev/null +++ b/src/internal/clone.h @@ -0,0 +1,22 @@ +#define CLONE_VM 0x00000100 +#define CLONE_FS 0x00000200 +#define CLONE_FILES 0x00000400 +#define CLONE_SIGHAND 0x00000800 +#define CLONE_PTRACE 0x00002000 +#define CLONE_VFORK 0x00004000 +#define CLONE_PARENT 0x00008000 +#define CLONE_THREAD 0x00010000 +#define CLONE_NEWNS 0x00020000 +#define CLONE_SYSVSEM 0x00040000 +#define CLONE_SETTLS 0x00080000 +#define CLONE_PARENT_SETTID 0x00100000 +#define CLONE_CHILD_CLEARTID 0x00200000 +#define CLONE_DETACHED 0x00400000 +#define CLONE_UNTRACED 0x00800000 +#define CLONE_CHILD_SETTID 0x01000000 +#define CLONE_NEWUTS 0x04000000 +#define CLONE_NEWIPC 0x08000000 +#define CLONE_NEWUSER 0x10000000 +#define CLONE_NEWPID 0x20000000 +#define CLONE_NEWNET 0x40000000 +#define CLONE_IO 0x80000000 diff --git a/src/internal/futex.h b/src/internal/futex.h new file mode 100644 index 00000000..c0453822 --- /dev/null +++ b/src/internal/futex.h @@ -0,0 +1,16 @@ +#ifndef _INTERNAL_FUTEX_H +#define _INTERNAL_FUTEX_H + +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 +#define FUTEX_FD 2 +#define FUTEX_REQUEUE 3 +#define FUTEX_CMP_REQUEUE 4 +#define FUTEX_WAKE_OP 5 +#define FUTEX_LOCK_PI 6 +#define FUTEX_UNLOCK_PI 7 +#define FUTEX_TRYLOCK_PI 8 + +int __futex(volatile int *, int, int, void *); + +#endif diff --git a/src/internal/libc.c b/src/internal/libc.c new file mode 100644 index 00000000..5e8e9d95 --- /dev/null +++ b/src/internal/libc.c @@ -0,0 +1,3 @@ +#include "libc.h" + +struct libc libc; diff --git a/src/internal/libc.h b/src/internal/libc.h new file mode 100644 index 00000000..e353f363 --- /dev/null +++ b/src/internal/libc.h @@ -0,0 +1,43 @@ +#ifndef LIBC_H +#define LIBC_H + +#include <stdlib.h> +#include <stdio.h> + +#define libc __libc +extern struct libc { + void (*lock)(volatile int *); + void (*cancelpt)(int); + int (*atexit)(void (*)(void)); + void (*fini)(void); + void (*ldso_fini)(void); + int *(*errno_location)(void); + volatile int threads_minus_1; + int (*rsyscall)(int, long, long, long, long, long, long); + void (**tsd_keys)(void *); +} libc; + + +/* Designed to avoid any overhead in non-threaded processes */ +void __lock(volatile int *); +#define LOCK(x) (libc.threads_minus_1 ? (__lock(x),1) : ((void)(x),1)) +#define UNLOCK(x) (*(x)=0) +#define CANCELPT(x) (libc.cancelpt ? libc.cancelpt((x)),0 : (void)(x),0) +#define CANCELPT_BEGIN CANCELPT(1) +#define CANCELPT_END CANCELPT(0) + +extern char **__environ; +#define environ __environ + +#undef weak_alias +#define weak_alias(old, new) \ + extern __typeof(old) new __attribute__((weak, alias(#old))) + +#undef LFS64_2 +//#define LFS64_2(x, y) weak_alias(x, y) +#define LFS64_2(x, y) extern __typeof(x) y + +#undef LFS64 +#define LFS64(x) LFS64_2(x, x##64) + +#endif diff --git a/src/internal/locale_impl.h b/src/internal/locale_impl.h new file mode 100644 index 00000000..c268124f --- /dev/null +++ b/src/internal/locale_impl.h @@ -0,0 +1,5 @@ +#include <locale.h> + +struct __locale { + int dummy; +}; diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h new file mode 100644 index 00000000..b7892103 --- /dev/null +++ b/src/internal/pthread_impl.h @@ -0,0 +1,68 @@ +#ifndef _PTHREAD_IMPL_H +#define _PTHREAD_IMPL_H + +#include <pthread.h> +#include <sched.h> +#include <signal.h> +#include <unistd.h> +#include <sys/mman.h> +#include <errno.h> +#include <limits.h> +#include <inttypes.h> +#include <setjmp.h> +#include <string.h> +#include <time.h> +#include "libc.h" +#include "syscall.h" +#include "atomic.h" +#include "futex.h" + +#define pthread __pthread + +struct pthread { + struct pthread *self, *join; + int errno_val; + pid_t tid, pid; + volatile int canceldisable, cancelasync, cancelpoint, cancel; + unsigned char *map_base; + size_t map_size; + void *start_arg; + void *(*start)(void *); + void *result; + jmp_buf exit_jmp_buf; + int detached; + int exitlock; + unsigned long tlsdesc[4]; + struct __ptcb *cancelbuf; + void **tsd; + int tsd_used; + pthread_attr_t attr; + int *errno_ptr; +}; + +static inline struct pthread *__pthread_self() +{ + struct pthread *self; + __asm__ ("movl %%gs:0,%0" : "=r" (self) ); + return self; +} + +#define SIGCANCEL 32 +#define SIGSYSCALL 33 +#define SIGTIMER 32 /* ?? */ + +int __set_thread_area(unsigned long *); +int __set_pthread_self(void *); +int __libc_sigaction(int, const struct sigaction *, struct sigaction *); +int __libc_sigprocmask(int, const sigset_t *, sigset_t *); +void __lock(volatile int *); +void __unmapself(void *, size_t); + +int __timedwait(volatile int *, int, clockid_t, const struct timespec *, int); +void __wait(volatile int *, volatile int *, int, int); +void __wake(volatile int *, int, int); + +#define DEFAULT_STACK_SIZE (16384-PAGE_SIZE) +#define DEFAULT_GUARD_SIZE PAGE_SIZE + +#endif diff --git a/src/internal/stdio_impl.h b/src/internal/stdio_impl.h new file mode 100644 index 00000000..1e9159f6 --- /dev/null +++ b/src/internal/stdio_impl.h @@ -0,0 +1,100 @@ +#ifndef _STDIO_IMPL_H +#define _STDIO_IMPL_H + +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include <wchar.h> +#include <unistd.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <ctype.h> +#include <sys/wait.h> +#include <math.h> +#include <float.h> +#include "syscall.h" +#include "libc.h" + +#define UNGET 4 + +#define FLOCK(f) LOCK(&f->lock) +#define FUNLOCK(f) UNLOCK(&f->lock) + +#define F_PERM 1 +#define F_NORD 4 +#define F_NOWR 8 +#define F_EOF 16 +#define F_ERR 32 + +struct __FILE_s { + unsigned flags; + unsigned char *rpos, *rstop; + unsigned char *rend, *wend; + unsigned char *wpos, *wstop; + unsigned char *wbase; + unsigned char *dummy01[3]; + unsigned char *buf; + size_t buf_size; + FILE *prev, *next; + int fd; + int pipe_pid; + long dummy2; + short dummy3; + char dummy4; + signed char lbf; + int lock; + int lockcount; + void *owner; + off_t off; + int (*flush)(FILE *); + void **wide_data; /* must be NULL */ + size_t (*read)(FILE *, unsigned char *, size_t); + size_t (*write)(FILE *, const unsigned char *, size_t); + off_t (*seek)(FILE *, off_t, int); + int mode; + int (*close)(FILE *); +}; + +size_t __stdio_read(FILE *, unsigned char *, size_t); +size_t __stdio_write(FILE *, const unsigned char *, size_t); +off_t __stdio_seek(FILE *, off_t, int); +int __stdio_close(FILE *); + +int __overflow(FILE *, int); +int __oflow(FILE *); +int __uflow(FILE *); +int __underflow(FILE *); + +int __fseeko(FILE *, off_t, int); +int __fseeko_unlocked(FILE *, off_t, int); +off_t __ftello(FILE *); +off_t __ftello_unlocked(FILE *); +size_t __fwritex(const unsigned char *, size_t, FILE *); +int __putc_unlocked(int, FILE *); + +FILE *__fdopen(int, const char *); + +extern struct ofl +{ + FILE *head; + int lock; +} __ofl; + +#define OFLLOCK() LOCK(&__ofl.lock) +#define OFLUNLOCK() UNLOCK(&__ofl.lock) +#define ofl_head (__ofl.head) + +#define feof(f) ((f)->flags & F_EOF) +#define ferror(f) ((f)->flags & F_ERR) + +/* Caller-allocated FILE * operations */ +FILE *__fopen_rb_ca(const char *, FILE *, unsigned char *, size_t); +int __fclose_ca(FILE *); + +#endif diff --git a/src/internal/syscall.c b/src/internal/syscall.c new file mode 100644 index 00000000..4f159e0b --- /dev/null +++ b/src/internal/syscall.c @@ -0,0 +1,11 @@ +#include <errno.h> +#include <unistd.h> + +long __syscall_ret(unsigned long r) +{ + if (r >= (unsigned long)-1 - 4096) { + errno = -(long)r; + return -1; + } + return (long)r; +} diff --git a/src/internal/syscall.h b/src/internal/syscall.h new file mode 100644 index 00000000..4b3c0a73 --- /dev/null +++ b/src/internal/syscall.h @@ -0,0 +1,469 @@ +#ifndef _SYSCALL_H +#define _SYSCALL_H + +#define SYSCALL_LL(x) \ +((union { long long ll; long l[2]; }){ .ll = x }).l[0], \ +((union { long long ll; long l[2]; }){ .ll = x }).l[1] + +#define SYSCALL_SIGSET_SIZE 8 + +#if defined(SYSCALL_STANDALONE) +#include <errno.h> +static inline long __syscall_ret(unsigned long r) +{ + if (r >= (unsigned long)-1 - 4096) { + errno = -(long)r; + return -1; + } + return (long)r; +} +#elif defined(SYSCALL_NORETURN) +static inline long __syscall_ret(unsigned long r) +{ + for(;;); + return 0; +} +#elif defined(SYSCALL_RETURN_ERRNO) +static inline long __syscall_ret(unsigned long r) +{ + return -r; +} +#else +extern long __syscall_ret(unsigned long); +#endif + +#define SYSCALL0 "int $128" + +#ifdef __PIC__ +#define SYSCALL "xchgl %%ebx,%2\n\t" SYSCALL0 "\n\txchgl %%ebx,%2" +#define EBX "m" +#else +#define SYSCALL SYSCALL0 +#define EBX "b" +#endif + +static inline long syscall0(long n) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL0 : "=a"(ret) : "a"(n) : "memory"); + return __syscall_ret(ret); +} + +static inline long syscall1(long n, long a1) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL : "=a"(ret) : "a"(n), EBX(a1) : "memory"); + return __syscall_ret(ret); +} + +static inline long syscall2(long n, long a1, long a2) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL : "=a"(ret) : "a"(n), EBX(a1), "c"(a2) : "memory"); + return __syscall_ret(ret); +} + +static inline long syscall3(long n, long a1, long a2, long a3) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL : "=a"(ret) : "a"(n), EBX(a1), "c"(a2), "d"(a3) : "memory"); + return __syscall_ret(ret); +} + +static inline long syscall4(long n, long a1, long a2, long a3, long a4) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL : "=a"(ret) : "a"(n), EBX(a1), "c"(a2), "d"(a3), "S"(a4) : "memory"); + return __syscall_ret(ret); +} + +static inline long syscall5(long n, long a1, long a2, long a3, long a4, long a5) +{ + unsigned long ret; + __asm__ __volatile__ (SYSCALL : "=a"(ret) : "a"(n), EBX(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5) : "memory"); + return __syscall_ret(ret); +} + +#ifdef __PIC__ +/* note: it's probably only safe to use this when a6 is on the stack */ +static inline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) +{ + unsigned long ret; + __asm__ __volatile__ ("xchgl %%ebx,%2 ; pushl %1 ; pushl %%ebp ; movl %%eax,%%ebp ; movl 4(%%esp),%%eax ; int $128 ; popl %%ebp ; popl %%ecx ; xchgl %%ebx,%2" + : "=a"(ret) : "g"(n), EBX(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5), "a"(a6) : "memory"); + return __syscall_ret(ret); +} +#else +static inline long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6) +{ + unsigned long ret; + __asm__ __volatile__ ("pushl %%ebp ; mov %1, %%ebp ; xchg %%ebp, %7 ; int $128 ; popl %%ebp" + : "=a"(ret) : "g"(n), EBX(a1), "c"(a2), "d"(a3), "S"(a4), "D"(a5), "a"(a6) : "memory"); + return __syscall_ret(ret); +} +#endif + +#define __NR_restart_syscall 0 +#define __NR_exit 1 +#define __NR_fork 2 +#define __NR_read 3 +#define __NR_write 4 +#define __NR_open 5 +#define __NR_close 6 +#define __NR_waitpid 7 +#define __NR_creat 8 +#define __NR_link 9 +#define __NR_unlink 10 +#define __NR_execve 11 +#define __NR_chdir 12 +#define __NR_time 13 +#define __NR_mknod 14 +#define __NR_chmod 15 +#define __NR_lchown 16 +#define __NR_break 17 +#define __NR_oldstat 18 +#define __NR_lseek 19 +#define __NR_getpid 20 +#define __NR_mount 21 +#define __NR_umount 22 +#define __NR_setuid 23 +#define __NR_getuid 24 +#define __NR_stime 25 +#define __NR_ptrace 26 +#define __NR_alarm 27 +#define __NR_oldfstat 28 +#define __NR_pause 29 +#define __NR_utime 30 +#define __NR_stty 31 +#define __NR_gtty 32 +#define __NR_access 33 +#define __NR_nice 34 +#define __NR_ftime 35 +#define __NR_sync 36 +#define __NR_kill 37 +#define __NR_rename 38 +#define __NR_mkdir 39 +#define __NR_rmdir 40 +#define __NR_dup 41 +#define __NR_pipe 42 +#define __NR_times 43 +#define __NR_prof 44 +#define __NR_brk 45 +#define __NR_setgid 46 +#define __NR_getgid 47 +#define __NR_signal 48 +#define __NR_geteuid 49 +#define __NR_getegid 50 +#define __NR_acct 51 +#define __NR_umount2 52 +#define __NR_lock 53 +#define __NR_ioctl 54 +#define __NR_fcntl 55 +#define __NR_mpx 56 +#define __NR_setpgid 57 +#define __NR_ulimit 58 +#define __NR_oldolduname 59 +#define __NR_umask 60 +#define __NR_chroot 61 +#define __NR_ustat 62 +#define __NR_dup2 63 +#define __NR_getppid 64 +#define __NR_getpgrp 65 +#define __NR_setsid 66 +#define __NR_sigaction 67 +#define __NR_sgetmask 68 +#define __NR_ssetmask 69 +#define __NR_setreuid 70 +#define __NR_setregid 71 +#define __NR_sigsuspend 72 +#define __NR_sigpending 73 +#define __NR_sethostname 74 +#define __NR_setrlimit 75 +#define __NR_getrlimit 76 /* Back compatible 2Gig limited rlimit */ +#define __NR_getrusage 77 +#define __NR_gettimeofday 78 +#define __NR_settimeofday 79 +#define __NR_getgroups 80 +#define __NR_setgroups 81 +#define __NR_select 82 +#define __NR_symlink 83 +#define __NR_oldlstat 84 +#define __NR_readlink 85 +#define __NR_uselib 86 +#define __NR_swapon 87 +#define __NR_reboot 88 +#define __NR_readdir 89 +#define __NR_mmap 90 +#define __NR_munmap 91 +#define __NR_truncate 92 +#define __NR_ftruncate 93 +#define __NR_fchmod 94 +#define __NR_fchown 95 +#define __NR_getpriority 96 +#define __NR_setpriority 97 +#define __NR_profil 98 +#define __NR_statfs 99 +#define __NR_fstatfs 100 +#define __NR_ioperm 101 +#define __NR_socketcall 102 +#define __NR_syslog 103 +#define __NR_setitimer 104 +#define __NR_getitimer 105 +#define __NR_stat 106 +#define __NR_lstat 107 +#define __NR_fstat 108 +#define __NR_olduname 109 +#define __NR_iopl 110 +#define __NR_vhangup 111 +#define __NR_idle 112 +#define __NR_vm86old 113 +#define __NR_wait4 114 +#define __NR_swapoff 115 +#define __NR_sysinfo 116 +#define __NR_ipc 117 +#define __NR_fsync 118 +#define __NR_sigreturn 119 +#define __NR_clone 120 +#define __NR_setdomainname 121 +#define __NR_uname 122 +#define __NR_modify_ldt 123 +#define __NR_adjtimex 124 +#define __NR_mprotect 125 +#define __NR_sigprocmask 126 +#define __NR_create_module 127 +#define __NR_init_module 128 +#define __NR_delete_module 129 +#define __NR_get_kernel_syms 130 +#define __NR_quotactl 131 +#define __NR_getpgid 132 +#define __NR_fchdir 133 +#define __NR_bdflush 134 +#define __NR_sysfs 135 +#define __NR_personality 136 +#define __NR_afs_syscall 137 +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#define __NR__llseek 140 +#define __NR_getdents 141 +#define __NR__newselect 142 +#define __NR_flock 143 +#define __NR_msync 144 +#define __NR_readv 145 +#define __NR_writev 146 +#define __NR_getsid 147 +#define __NR_fdatasync 148 +#define __NR__sysctl 149 +#define __NR_mlock 150 +#define __NR_munlock 151 +#define __NR_mlockall 152 +#define __NR_munlockall 153 +#define __NR_sched_setparam 154 +#define __NR_sched_getparam 155 +#define __NR_sched_setscheduler 156 +#define __NR_sched_getscheduler 157 +#define __NR_sched_yield 158 +#define __NR_sched_get_priority_max 159 +#define __NR_sched_get_priority_min 160 +#define __NR_sched_rr_get_interval 161 +#define __NR_nanosleep 162 +#define __NR_mremap 163 +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_vm86 166 +#define __NR_query_module 167 +#define __NR_poll 168 +#define __NR_nfsservctl 169 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#define __NR_prctl 172 +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigtimedwait 177 +#define __NR_rt_sigqueueinfo 178 +#define __NR_rt_sigsuspend 179 +#define __NR_pread64 180 +#define __NR_pwrite64 181 +#define __NR_chown 182 +#define __NR_getcwd 183 +#define __NR_capget 184 +#define __NR_capset 185 +#define __NR_sigaltstack 186 +#define __NR_sendfile 187 +#define __NR_getpmsg 188 +#define __NR_putpmsg 189 +#define __NR_vfork 190 +#define __NR_ugetrlimit 191 +#define __NR_mmap2 192 +#define __NR_truncate64 193 +#define __NR_ftruncate64 194 +#define __NR_stat64 195 +#define __NR_lstat64 196 +#define __NR_fstat64 197 +#define __NR_lchown32 198 +#define __NR_getuid32 199 +#define __NR_getgid32 200 +#define __NR_geteuid32 201 +#define __NR_getegid32 202 +#define __NR_setreuid32 203 +#define __NR_setregid32 204 +#define __NR_getgroups32 205 +#define __NR_setgroups32 206 +#define __NR_fchown32 207 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#define __NR_chown32 212 +#define __NR_setuid32 213 +#define __NR_setgid32 214 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#define __NR_pivot_root 217 +#define __NR_mincore 218 +#define __NR_madvise 219 +#define __NR_madvise1 219 +#define __NR_getdents64 220 +#define __NR_fcntl64 221 +/* 223 is unused */ +#define __NR_gettid 224 +#define __NR_readahead 225 +#define __NR_setxattr 226 +#define __NR_lsetxattr 227 +#define __NR_fsetxattr 228 +#define __NR_getxattr 229 +#define __NR_lgetxattr 230 +#define __NR_fgetxattr 231 +#define __NR_listxattr 232 +#define __NR_llistxattr 233 +#define __NR_flistxattr 234 +#define __NR_removexattr 235 +#define __NR_lremovexattr 236 +#define __NR_fremovexattr 237 +#define __NR_tkill 238 +#define __NR_sendfile64 239 +#define __NR_futex 240 +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#define __NR_set_thread_area 243 +#define __NR_get_thread_area 244 +#define __NR_io_setup 245 +#define __NR_io_destroy 246 +#define __NR_io_getevents 247 +#define __NR_io_submit 248 +#define __NR_io_cancel 249 +#define __NR_fadvise64 250 +/* 251 is available for reuse (was briefly sys_set_zone_reclaim) */ +#define __NR_exit_group 252 +#define __NR_lookup_dcookie 253 +#define __NR_epoll_create 254 +#define __NR_epoll_ctl 255 +#define __NR_epoll_wait 256 +#define __NR_remap_file_pages 257 +#define __NR_set_tid_address 258 +#define __NR_timer_create 259 +#define __NR_timer_settime (__NR_timer_create+1) +#define __NR_timer_gettime (__NR_timer_create+2) +#define __NR_timer_getoverrun (__NR_timer_create+3) +#define __NR_timer_delete (__NR_timer_create+4) +#define __NR_clock_settime (__NR_timer_create+5) +#define __NR_clock_gettime (__NR_timer_create+6) +#define __NR_clock_getres (__NR_timer_create+7) +#define __NR_clock_nanosleep (__NR_timer_create+8) +#define __NR_statfs64 268 +#define __NR_fstatfs64 269 +#define __NR_tgkill 270 +#define __NR_utimes 271 +#define __NR_fadvise64_64 272 +#define __NR_vserver 273 +#define __NR_mbind 274 +#define __NR_get_mempolicy 275 +#define __NR_set_mempolicy 276 +#define __NR_mq_open 277 +#define __NR_mq_unlink (__NR_mq_open+1) +#define __NR_mq_timedsend (__NR_mq_open+2) +#define __NR_mq_timedreceive (__NR_mq_open+3) +#define __NR_mq_notify (__NR_mq_open+4) +#define __NR_mq_getsetattr (__NR_mq_open+5) +#define __NR_kexec_load 283 +#define __NR_waitid 284 +/* #define __NR_sys_setaltroot 285 */ +#define __NR_add_key 286 +#define __NR_request_key 287 +#define __NR_keyctl 288 +#define __NR_ioprio_set 289 +#define __NR_ioprio_get 290 +#define __NR_inotify_init 291 +#define __NR_inotify_add_watch 292 +#define __NR_inotify_rm_watch 293 +#define __NR_migrate_pages 294 +#define __NR_openat 295 +#define __NR_mkdirat 296 +#define __NR_mknodat 297 +#define __NR_fchownat 298 +#define __NR_futimesat 299 +#define __NR_fstatat64 300 +#define __NR_unlinkat 301 +#define __NR_renameat 302 +#define __NR_linkat 303 +#define __NR_symlinkat 304 +#define __NR_readlinkat 305 +#define __NR_fchmodat 306 +#define __NR_faccessat 307 +#define __NR_pselect6 308 +#define __NR_ppoll 309 +#define __NR_unshare 310 +#define __NR_set_robust_list 311 +#define __NR_get_robust_list 312 +#define __NR_splice 313 +#define __NR_sync_file_range 314 +#define __NR_tee 315 +#define __NR_vmsplice 316 +#define __NR_move_pages 317 +#define __NR_getcpu 318 +#define __NR_epoll_pwait 319 +#define __NR_utimensat 320 +#define __NR_signalfd 321 +#define __NR_timerfd_create 322 +#define __NR_eventfd 323 +#define __NR_fallocate 324 +#define __NR_timerfd_settime 325 +#define __NR_timerfd_gettime 326 +#define __NR_signalfd4 327 +#define __NR_eventfd2 328 +#define __NR_epoll_create1 329 +#define __NR_dup3 330 +#define __NR_pipe2 331 +#define __NR_inotify_init1 332 +#define __NR_preadv 333 +#define __NR_pwritev 334 + + +#undef O_LARGEFILE +#define O_LARGEFILE 0100000 + +/* the following are needed for iso c functions to use */ +#define __syscall_open(filename, flags, mode) syscall3(__NR_open, (long)(filename), (flags)|O_LARGEFILE, (mode)) +#define __syscall_read(fd, buf, len) syscall3(__NR_read, (fd), (long)(buf), (len)) +#define __syscall_write(fd, buf, len) syscall3(__NR_write, (fd), (long)(buf), (len)) +#define __syscall_close(fd) syscall1(__NR_close, (fd)) +#define __syscall_fcntl(fd, cmd, arg) syscall3(__NR_fcntl64, (fd), (cmd), (long)(arg)) +#define __syscall_dup2(old, new) syscall2(__NR_dup2, (old), (new)) +#define __syscall_unlink(path) syscall1(__NR_unlink, (long)(path)) +#define __syscall_getpid() syscall0(__NR_getpid) +#define __syscall_kill(pid,sig) syscall2(__NR_kill, (pid), (sig)) +#define __syscall_sigaction(sig,new,old) syscall4(__NR_rt_sigaction, (sig), (long)(new), (long)(old), SYSCALL_SIGSET_SIZE) +#define __syscall_ioctl(fd,ioc,arg) syscall3(__NR_ioctl, (fd), (ioc), (long)(arg)) +#define __syscall_exit(code) syscall1(__NR_exit, code) + +#define __NEED_off_t +#include <bits/alltypes.h> + +static inline off_t __syscall_lseek(int fd, off_t offset, int whence) +{ + off_t result; + return syscall5(__NR__llseek, fd, offset>>32, offset, (long)&result, whence) ? -1 : result; +} + +#endif diff --git a/src/internal/util.h b/src/internal/util.h new file mode 100644 index 00000000..7c7c3a17 --- /dev/null +++ b/src/internal/util.h @@ -0,0 +1,5 @@ +#ifndef _INTERNAL_UTIL_H +#define _INTERNAL_UTIL_H + + +#endif diff --git a/src/ipc/ftok.c b/src/ipc/ftok.c new file mode 100644 index 00000000..cd6002ed --- /dev/null +++ b/src/ipc/ftok.c @@ -0,0 +1,10 @@ +#include <sys/ipc.h> +#include <sys/stat.h> + +key_t ftok(const char *path, int id) +{ + struct stat st; + if (stat(path, &st) < 0) return -1; + + return ((st.st_ino & 0xffff) | ((st.st_dev & 0xff) << 16) | ((id & 0xff) << 24)); +} diff --git a/src/ipc/ipc.h b/src/ipc/ipc.h new file mode 100644 index 00000000..9edd5ecf --- /dev/null +++ b/src/ipc/ipc.h @@ -0,0 +1,13 @@ +#define IPCOP_semop 1 +#define IPCOP_semget 2 +#define IPCOP_semctl 3 +#define IPCOP_msgsnd 11 +#define IPCOP_msgrcv 12 +#define IPCOP_msgget 13 +#define IPCOP_msgctl 14 +#define IPCOP_shmat 21 +#define IPCOP_shmdt 22 +#define IPCOP_shmget 23 +#define IPCOP_shmctl 24 + +#define IPC_MODERN 0x100 diff --git a/src/ipc/semctl.c b/src/ipc/semctl.c new file mode 100644 index 00000000..7ada116b --- /dev/null +++ b/src/ipc/semctl.c @@ -0,0 +1,18 @@ +#include <sys/sem.h> +#include <stdarg.h> +#include "syscall.h" +#include "ipc.h" + +int semctl(int id, int num, int cmd, ...) +{ + long arg; + va_list ap; + va_start(ap, cmd); + arg = va_arg(ap, long); + va_end(ap); +#ifdef __NR_semctl + return syscall4(__NR_semctl, id, num, cmd, arg); +#else + return syscall5(__NR_ipc, IPCOP_semctl, id, num, cmd | 0x100, (long)&arg); +#endif +} diff --git a/src/ipc/semget.c b/src/ipc/semget.c new file mode 100644 index 00000000..2dcf6eac --- /dev/null +++ b/src/ipc/semget.c @@ -0,0 +1,12 @@ +#include <sys/sem.h> +#include "syscall.h" +#include "ipc.h" + +int semget(key_t key, int n, int fl) +{ +#ifdef __NR_semget + return syscall3(__NR_semget, key, n, fl); +#else + return syscall4(__NR_ipc, IPCOP_semget, key, n, fl); +#endif +} diff --git a/src/ipc/semop.c b/src/ipc/semop.c new file mode 100644 index 00000000..48d8a654 --- /dev/null +++ b/src/ipc/semop.c @@ -0,0 +1,12 @@ +#include <sys/sem.h> +#include "syscall.h" +#include "ipc.h" + +int semop(int id, struct sembuf *buf, size_t n) +{ +#ifdef __NR_semop + return syscall3(__NR_semop, id, (long)buf, n); +#else + return syscall5(__NR_ipc, IPCOP_semop, id, n, 0, (long)buf); +#endif +} diff --git a/src/ipc/shmat.c b/src/ipc/shmat.c new file mode 100644 index 00000000..ff65b6a4 --- /dev/null +++ b/src/ipc/shmat.c @@ -0,0 +1,17 @@ +#include <sys/shm.h> +#include "syscall.h" +#include "ipc.h" + +#ifdef __NR_shmat +void *shmat(int id, const void *addr, int flag) +{ + return syscall3(__NR_shmat, id, (long)addr, flag); +} +#else +void *shmat(int id, const void *addr, int flag) +{ + unsigned long ret; + ret = syscall5(__NR_ipc, IPCOP_shmat, id, flag, (long)&addr, (long)addr); + return (ret > -(unsigned long)SHMLBA) ? (void *)ret : (void *)addr; +} +#endif diff --git a/src/ipc/shmctl.c b/src/ipc/shmctl.c new file mode 100644 index 00000000..da357fa8 --- /dev/null +++ b/src/ipc/shmctl.c @@ -0,0 +1,12 @@ +#include <sys/shm.h> +#include "syscall.h" +#include "ipc.h" + +int shmctl(int id, int cmd, struct shmid_ds *buf) +{ +#ifdef __NR_shmctl + return syscall3(__NR_shmctl, id, cmd, (long)buf); +#else + return syscall4(__NR_ipc, IPCOP_shmctl, id, cmd | IPC_MODERN, (long)buf); +#endif +} diff --git a/src/ipc/shmdt.c b/src/ipc/shmdt.c new file mode 100644 index 00000000..e04188f9 --- /dev/null +++ b/src/ipc/shmdt.c @@ -0,0 +1,12 @@ +#include <sys/shm.h> +#include "syscall.h" +#include "ipc.h" + +int shmdt(const void *addr) +{ +#ifdef __NR_shmdt + return syscall1(__NR_shmdt, (long)addr); +#else + return syscall2(__NR_ipc, IPCOP_shmdt, (long)addr); +#endif +} diff --git a/src/ipc/shmget.c b/src/ipc/shmget.c new file mode 100644 index 00000000..86e254af --- /dev/null +++ b/src/ipc/shmget.c @@ -0,0 +1,12 @@ +#include <sys/shm.h> +#include "syscall.h" +#include "ipc.h" + +int shmget(key_t key, size_t size, int flag) +{ +#ifdef __NR_shmget + return syscall3(__NR_shmget, key, size, flag); +#else + return syscall4(__NR_ipc, IPCOP_shmget, key, size, flag); +#endif +} diff --git a/src/linux/brk.c b/src/linux/brk.c new file mode 100644 index 00000000..3c2982c6 --- /dev/null +++ b/src/linux/brk.c @@ -0,0 +1,6 @@ +#include "syscall.h" + +int brk(void *end) +{ + return -(syscall1(__NR_brk, (long)end) == -1); +} diff --git a/src/linux/chroot.c b/src/linux/chroot.c new file mode 100644 index 00000000..b5af62dc --- /dev/null +++ b/src/linux/chroot.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int chroot(const char *path) +{ + return syscall1(__NR_chroot, (long)path); +} diff --git a/src/linux/daemon.c b/src/linux/daemon.c new file mode 100644 index 00000000..632d1203 --- /dev/null +++ b/src/linux/daemon.c @@ -0,0 +1,31 @@ +#include <fcntl.h> +#include <unistd.h> + +int daemon(int nochdir, int noclose) +{ + int fd; + + switch(fork()) { + case 0: break; + case -1: return -1; + default: _exit(0); + } + + if (setsid() < 0) return -1; + + switch(fork()) { + case 0: break; + case -1: return -1; + default: _exit(0); + } + + if (!nochdir) chdir("/"); + if (!noclose && (fd = open("/dev/null", O_RDWR)) >= 0) { + dup2(fd, 0); + dup2(fd, 1); + dup2(fd, 2); + if (fd > 2) close(fd); + } + + return 0; +} diff --git a/src/linux/epoll_create.c b/src/linux/epoll_create.c new file mode 100644 index 00000000..c9dea8ce --- /dev/null +++ b/src/linux/epoll_create.c @@ -0,0 +1,7 @@ +#include <sys/epoll.h> +#include "syscall.h" + +int epoll_create(int size) +{ + return syscall1(__NR_epoll_create, size); +} diff --git a/src/linux/epoll_create1.c b/src/linux/epoll_create1.c new file mode 100644 index 00000000..2e82e995 --- /dev/null +++ b/src/linux/epoll_create1.c @@ -0,0 +1,7 @@ +#include <sys/epoll.h> +#include "syscall.h" + +int epoll_create1(int flags) +{ + return syscall1(__NR_epoll_create1, flags); +} diff --git a/src/linux/epoll_ctl.c b/src/linux/epoll_ctl.c new file mode 100644 index 00000000..4214f407 --- /dev/null +++ b/src/linux/epoll_ctl.c @@ -0,0 +1,7 @@ +#include <sys/epoll.h> +#include "syscall.h" + +int epoll_ctl(int fd, int op, int fd2, struct epoll_event *ev) +{ + return syscall4(__NR_epoll_ctl, fd, op, fd2, (long)ev); +} diff --git a/src/linux/epoll_pwait.c b/src/linux/epoll_pwait.c new file mode 100644 index 00000000..5aaacba6 --- /dev/null +++ b/src/linux/epoll_pwait.c @@ -0,0 +1,7 @@ +#include <sys/epoll.h> +#include "syscall.h" + +int epoll_pwait(int fd, struct epoll_event *ev, int cnt, int to, const sigset_t *sigs) +{ + return syscall6(__NR_epoll_pwait, fd, (long)ev, cnt, to, (long)sigs, 8); +} diff --git a/src/linux/epoll_wait.c b/src/linux/epoll_wait.c new file mode 100644 index 00000000..8a68ebdd --- /dev/null +++ b/src/linux/epoll_wait.c @@ -0,0 +1,7 @@ +#include <sys/epoll.h> +#include "syscall.h" + +int epoll_wait(int fd, struct epoll_event *ev, int cnt, int to) +{ + return syscall4(__NR_epoll_wait, fd, (long)ev, cnt, to); +} diff --git a/src/linux/getdtablesize.c b/src/linux/getdtablesize.c new file mode 100644 index 00000000..623a6af3 --- /dev/null +++ b/src/linux/getdtablesize.c @@ -0,0 +1,9 @@ +#include <limits.h> +#include <sys/resource.h> + +int getdtablesize(void) +{ + struct rlimit rl; + getrlimit(RLIMIT_NOFILE, &rl); + return rl.rlim_max < INT_MAX ? rl.rlim_max : INT_MAX; +} diff --git a/src/linux/gethostid.c b/src/linux/gethostid.c new file mode 100644 index 00000000..ea65611a --- /dev/null +++ b/src/linux/gethostid.c @@ -0,0 +1,4 @@ +long gethostid() +{ + return 0; +} diff --git a/src/linux/getopt_long.c b/src/linux/getopt_long.c new file mode 100644 index 00000000..d80cd1b6 --- /dev/null +++ b/src/linux/getopt_long.c @@ -0,0 +1,52 @@ +#define _GNU_SOURCE +#include <stddef.h> +#include <getopt.h> +#include <stdio.h> + +static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + if (optind >= argc || !argv[optind] || argv[optind][0] != '-') return -1; + if ((longonly && argv[optind][1]) || + (argv[optind][1] == '-' && argv[optind][2])) + { + int i; + for (i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + char *opt = argv[optind]+1; + if (*opt == '-') opt++; + while (*name && *name++ == *opt++); + if (*name || (*opt && *opt != '=')) continue; + if (*opt == '=') { + if (!longopts[i].has_arg) continue; + optarg = opt+1; + } else { + if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[++optind])) + return ':'; + } else optarg = NULL; + } + optind++; + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} + +int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + return __getopt_long(argc, argv, optstring, longopts, idx, 0); +} + +int getopt_long_only(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + return __getopt_long(argc, argv, optstring, longopts, idx, 1); +} diff --git a/src/linux/getpagesize.c b/src/linux/getpagesize.c new file mode 100644 index 00000000..5ede652b --- /dev/null +++ b/src/linux/getpagesize.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include <limits.h> + +int getpagesize(void) +{ + return PAGE_SIZE; +} diff --git a/src/linux/getpass.c b/src/linux/getpass.c new file mode 100644 index 00000000..d439a2a5 --- /dev/null +++ b/src/linux/getpass.c @@ -0,0 +1,39 @@ +#include <stdio.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> +#include <fcntl.h> + +char *getpass(const char *prompt) +{ + int fd; + struct termios s, t; + ssize_t l; + static char password[128]; + + if ((fd = open("/dev/tty", O_RDONLY|O_NOCTTY)) < 0) fd = 0; + + tcgetattr(fd, &t); + s = t; + t.c_lflag &= ~(ECHO|ISIG); + t.c_lflag |= ICANON; + t.c_iflag &= ~(INLCR|IGNCR); + t.c_iflag |= ICRNL; + tcsetattr(fd, TCSAFLUSH, &t); + tcdrain(fd); + + fputs(prompt, stderr); + fflush(stderr); + + l = read(fd, password, sizeof password); + if (l >= 0) { + if (l > 0 && password[l-1] == '\n') l--; + password[l] = 0; + } + + tcsetattr(fd, TCSAFLUSH, &s); + + if (fd > 2) close(fd); + + return password; +} diff --git a/src/linux/initgroups.c b/src/linux/initgroups.c new file mode 100644 index 00000000..ef9bc10a --- /dev/null +++ b/src/linux/initgroups.c @@ -0,0 +1,15 @@ +#include <sys/types.h> +#include <unistd.h> +#include <grp.h> +#include <limits.h> + +int getgrouplist(const char *, gid_t, gid_t *, int *); +int setgroups(size_t, const gid_t *); + +int initgroups(const char *user, gid_t gid) +{ + gid_t groups[NGROUPS_MAX]; + int count; + if (getgrouplist(user, gid, groups, &count) < 0) return -1; + return setgroups(count, groups); +} diff --git a/src/linux/klogctl.c b/src/linux/klogctl.c new file mode 100644 index 00000000..6c288aff --- /dev/null +++ b/src/linux/klogctl.c @@ -0,0 +1,7 @@ +#define SYSCALL_STANDALONE +#include "syscall.h" + +int klogctl (int type, char *buf, int len) +{ + return syscall3(__NR_syslog, type, (long)buf, len); +} diff --git a/src/linux/mntent.c b/src/linux/mntent.c new file mode 100644 index 00000000..e3735666 --- /dev/null +++ b/src/linux/mntent.c @@ -0,0 +1,57 @@ +#include <stdio.h> +#include <string.h> +#include <mntent.h> + +FILE *setmntent(const char *name, const char *mode) +{ + return fopen(name, mode); +} + +int endmntent(FILE *f) +{ + fclose(f); + return 1; +} + +struct mntent *getmntent(FILE *f) +{ + static char linebuf[256]; + static struct mntent mnt; + int cnt, n[8]; + + mnt.mnt_freq = 0; + mnt.mnt_passno = 0; + + do { + fgets(linebuf, sizeof linebuf, f); + if (feof(f)) return NULL; + cnt = sscanf(linebuf, " %n%*s%n %n%*s%n %n%*s%n %n%*s%n %d %d", + n, n+1, n+2, n+3, n+4, n+5, n+6, n+7, + &mnt.mnt_freq, &mnt.mnt_passno); + } while (cnt >= 8 && linebuf[n[0]] != '#'); + + linebuf[n[1]] = 0; + linebuf[n[3]] = 0; + linebuf[n[5]] = 0; + linebuf[n[7]] = 0; + + mnt.mnt_fsname = linebuf+n[0]; + mnt.mnt_dir = linebuf+n[2]; + mnt.mnt_type = linebuf+n[4]; + mnt.mnt_opts = linebuf+n[6]; + + return &mnt; +} + +int addmntent(FILE *f, const struct mntent *mnt) +{ + fseek(f, 0, SEEK_END); + return fprintf(f, "%s\t%s\t%s\t%s\t%d\t%d\n", + mnt->mnt_fsname, mnt->mnt_dir, mnt->mnt_type, mnt->mnt_opts, + mnt->mnt_freq, mnt->mnt_passno) < 0; +} + +char *hasmntopt(const struct mntent *mnt, const char *opt) +{ + return strstr(mnt->mnt_opts, opt); +} diff --git a/src/linux/mount.c b/src/linux/mount.c new file mode 100644 index 00000000..61299d48 --- /dev/null +++ b/src/linux/mount.c @@ -0,0 +1,8 @@ +#include <sys/mount.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int mount(const char *special, const char *dir, const char *fstype, unsigned long flags, const void *data) +{ + return syscall5(__NR_mount, (long)special, (long)dir, (long)fstype, flags, (long)data); +} diff --git a/src/linux/prctl.c b/src/linux/prctl.c new file mode 100644 index 00000000..d5516830 --- /dev/null +++ b/src/linux/prctl.c @@ -0,0 +1,13 @@ +#include <sys/prctl.h> +#include <stdarg.h> +#include "syscall.h" + +int prctl(int op, ...) +{ + unsigned long x[4]; + int i; + va_list ap; + va_start(ap, op); + for (i=0; i<4; i++) x[i] = va_arg(ap, unsigned long); + return syscall5(__NR_prctl, op, x[0], x[1], x[2], x[3]); +} diff --git a/src/linux/reboot.c b/src/linux/reboot.c new file mode 100644 index 00000000..68830d8e --- /dev/null +++ b/src/linux/reboot.c @@ -0,0 +1,8 @@ +#include <sys/reboot.h> +#include <errno.h> + +int reboot(int type) +{ + errno = ENOSYS; + return -1; +} diff --git a/src/linux/sbrk.c b/src/linux/sbrk.c new file mode 100644 index 00000000..56f60d1b --- /dev/null +++ b/src/linux/sbrk.c @@ -0,0 +1,7 @@ +#include <stddef.h> +#include "syscall.h" + +void *sbrk(ptrdiff_t inc) +{ + return (void *)syscall1(__NR_brk, syscall1(__NR_brk, 0)+inc); +} diff --git a/src/linux/sendfile.c b/src/linux/sendfile.c new file mode 100644 index 00000000..bfbc40ae --- /dev/null +++ b/src/linux/sendfile.c @@ -0,0 +1,10 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +ssize_t sendfile(int out_fd, int in_fd, off_t *ofs, size_t count) +{ + return syscall4(__NR_sendfile, out_fd, in_fd, (long)ofs, count); +} + +LFS64(sendfile); diff --git a/src/linux/setgroups.c b/src/linux/setgroups.c new file mode 100644 index 00000000..2368aa0d --- /dev/null +++ b/src/linux/setgroups.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int setgroups(int count, const gid_t list[]) +{ + /* this depends on our gid_t being 32bit */ + return syscall2(__NR_setgroups32, count, (long)list); +} diff --git a/src/linux/sethostname.c b/src/linux/sethostname.c new file mode 100644 index 00000000..f61e0cb4 --- /dev/null +++ b/src/linux/sethostname.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int sethostname(const char *name, size_t len) +{ + return syscall2(__NR_sethostname, (long)name, len); +} diff --git a/src/linux/settimeofday.c b/src/linux/settimeofday.c new file mode 100644 index 00000000..bd7e4104 --- /dev/null +++ b/src/linux/settimeofday.c @@ -0,0 +1,7 @@ +#include <sys/time.h> +#include "syscall.h" + +int settimeofday(const struct timeval *tv, void *tz) +{ + return syscall2(__NR_settimeofday, (long)tv, 0); +} diff --git a/src/linux/signalfd.c b/src/linux/signalfd.c new file mode 100644 index 00000000..ecda263e --- /dev/null +++ b/src/linux/signalfd.c @@ -0,0 +1,7 @@ +#include <sys/signalfd.h> +#include "syscall.h" + +int signalfd(int fd, const sigset_t *sigs, int flags) +{ + return syscall3(__NR_signalfd, fd, (long)sigs, 8); +} diff --git a/src/linux/stime.c b/src/linux/stime.c new file mode 100644 index 00000000..ec3ba821 --- /dev/null +++ b/src/linux/stime.c @@ -0,0 +1,7 @@ +#include <sys/time.h> + +int stime(time_t *t) +{ + struct timeval tv = { .tv_sec = *t, .tv_usec = 0 }; + return settimeofday(&tv, (void *)0); +} diff --git a/src/linux/swapoff.c b/src/linux/swapoff.c new file mode 100644 index 00000000..f6fa794e --- /dev/null +++ b/src/linux/swapoff.c @@ -0,0 +1,8 @@ +#include <sys/swap.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int swapoff(const char *path) +{ + return syscall1(__NR_swapoff, (long)path); +} diff --git a/src/linux/swapon.c b/src/linux/swapon.c new file mode 100644 index 00000000..13d2876b --- /dev/null +++ b/src/linux/swapon.c @@ -0,0 +1,8 @@ +#include <sys/swap.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int swapon(const char *path, int flags) +{ + return syscall2(__NR_swapon, (long)path, flags); +} diff --git a/src/linux/sysinfo.c b/src/linux/sysinfo.c new file mode 100644 index 00000000..98669472 --- /dev/null +++ b/src/linux/sysinfo.c @@ -0,0 +1,9 @@ +#define SYSCALL_STANDALONE +#include "syscall.h" + +struct sysinfo; + +int sysinfo(struct sysinfo *info) +{ + return syscall1(__NR_sysinfo, (long)info); +} diff --git a/src/linux/umount.c b/src/linux/umount.c new file mode 100644 index 00000000..c35f994d --- /dev/null +++ b/src/linux/umount.c @@ -0,0 +1,8 @@ +#include <sys/mount.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int umount(const char *special) +{ + return syscall1(__NR_umount, (long)special); +} diff --git a/src/linux/umount2.c b/src/linux/umount2.c new file mode 100644 index 00000000..cab93fd0 --- /dev/null +++ b/src/linux/umount2.c @@ -0,0 +1,8 @@ +#include <sys/mount.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int umount2(const char *special, int flags) +{ + return syscall2(__NR_umount2, (long)special, flags); +} diff --git a/src/linux/utimes.c b/src/linux/utimes.c new file mode 100644 index 00000000..99a3b2b8 --- /dev/null +++ b/src/linux/utimes.c @@ -0,0 +1,13 @@ +#include <sys/time.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +int utimes(const char *path, const struct timeval times[2]) +{ + long ktimes[2]; + if (times) { + ktimes[0] = times[0].tv_sec; + ktimes[1] = times[1].tv_sec; + } + return syscall2(__NR_utime, (long)path, times ? (long)ktimes : 0); +} diff --git a/src/linux/wait3.c b/src/linux/wait3.c new file mode 100644 index 00000000..dd63707c --- /dev/null +++ b/src/linux/wait3.c @@ -0,0 +1,11 @@ +#include <sys/wait.h> +#include <sys/resource.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +pid_t wait4(pid_t, int *, int, struct rusage *); + +pid_t wait3(int *status, int options, struct rusage *usage) +{ + return wait4(-1, status, options, usage); +} diff --git a/src/linux/wait4.c b/src/linux/wait4.c new file mode 100644 index 00000000..dda942d3 --- /dev/null +++ b/src/linux/wait4.c @@ -0,0 +1,19 @@ +#include <sys/wait.h> +#include <sys/resource.h> +#include <string.h> +#define SYSCALL_STANDALONE +#include "syscall.h" + +pid_t wait4(pid_t pid, int *status, int options, struct rusage *usage) +{ + pid_t ret = syscall4(__NR_wait4, pid, (long)status, options, (long)usage); + /* Fixup kernel time_t... */ + if (usage) { + long kusage[4]; + memcpy(kusage, usage, sizeof kusage); + memmove((struct timeval *)usage + 2, (long *)usage + 4, sizeof *usage - 2*sizeof(struct timeval)); + usage->ru_utime = (struct timeval){ kusage[0], kusage[1] }; + usage->ru_stime = (struct timeval){ kusage[2], kusage[3] }; + } + return ret; +} diff --git a/src/locale/catclose.c b/src/locale/catclose.c new file mode 100644 index 00000000..02cd3e5c --- /dev/null +++ b/src/locale/catclose.c @@ -0,0 +1,6 @@ +#include <nl_types.h> + +int catclose (nl_catd catd) +{ + return 0; +} diff --git a/src/locale/catgets.c b/src/locale/catgets.c new file mode 100644 index 00000000..bbee8986 --- /dev/null +++ b/src/locale/catgets.c @@ -0,0 +1,6 @@ +#include <nl_types.h> + +char *catgets (nl_catd catd, int set_id, int msg_id, const char *s) +{ + return (char *)s; +} diff --git a/src/locale/catopen.c b/src/locale/catopen.c new file mode 100644 index 00000000..4423c4d9 --- /dev/null +++ b/src/locale/catopen.c @@ -0,0 +1,6 @@ +#include <nl_types.h> + +nl_catd catopen (const char *name, int oflag) +{ + return (nl_catd)-1; +} diff --git a/src/locale/duplocale.c b/src/locale/duplocale.c new file mode 100644 index 00000000..dd445d46 --- /dev/null +++ b/src/locale/duplocale.c @@ -0,0 +1,11 @@ +#include <stdlib.h> +#include <string.h> +#include "locale_impl.h" + +locale_t duplocale(locale_t old) +{ + locale_t new; + new = calloc(1, sizeof *new); + if (new) memcpy(new, old, sizeof *new); + return new; +} diff --git a/src/locale/freelocale.c b/src/locale/freelocale.c new file mode 100644 index 00000000..4e089f22 --- /dev/null +++ b/src/locale/freelocale.c @@ -0,0 +1,7 @@ +#include <stdlib.h> +#include "locale_impl.h" + +void freelocale(locale_t l) +{ + free(l); +} diff --git a/src/locale/iconv.c b/src/locale/iconv.c new file mode 100644 index 00000000..4e46c7e4 --- /dev/null +++ b/src/locale/iconv.c @@ -0,0 +1,568 @@ +#include <iconv.h> +#include <errno.h> +#include <wchar.h> +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <stdint.h> + +#define UTF_32BE 000 +#define UTF_16LE 001 +#define UTF_16BE 002 +#define UTF_32LE 003 +#define UCS2BE 004 +#define UCS2LE 005 +#define WCHAR_T 007 + +#define US_ASCII 021 +#define UTF_8 022 +#define LATIN_9 024 +#define TIS_620 025 +#define JIS_0201 026 + +#define EUC 031 +#define EUC_TW 032 +#define SHIFT_JIS 033 +#define BIG5 034 +#define GBK 035 + +/* FIXME: these are not implemented yet + * EUC: A1-FE A1-FE + * GBK: 81-FE 40-7E,80-FE + * Big5: A1-FE 40-7E,A1-FE + */ + +/* Definitions of charmaps. Each charmap consists of: + * 1. Empty-string-terminated list of null-terminated aliases. + * 2. Special type code or bits per character. + * 3. Number of elided entries (128 for specials). + * 4. Character table (size determined by fields 2 and 3). */ + +static const unsigned char charmaps[] = +"utf8\0\0\022\x80" +"wchart\0\0\007\x80" + +"ucs2\0ucs2be\0\0\004\x80" +"ucs2le\0\0\005\x80" + +"utf16\0utf16be\0\0\002\x80" +"utf16le\0\0\001\x80" + +"ucs4\0ucs4be\0utf32\0utf32be\0\0\000\x80" +"ucs4le\0utf32le\0\0\003\x80" + +"ascii\0iso646\0usascii\0\0\021\x80" +"latin1\0iso88591\0\0\x09\x80" +"latin9\0iso885915\0\0\024\x80" +"tis620\0iso885911\0\0\025\x80" +"jis0201\0\0\026\x80" + +"iso88592\0\0\x0a\x21" +"\x04\x61\x1b\x14\x29\x3d\x69\x75\x0a\x2a" +"\x60\x79\x45\x56\x5e\xad\xf4\xb5\x17\x2c" +"\x05\x6d\x2b\x14\x2d\x3e\x6d\x75\x2c\x2e" +"\x61\x7d\x55\x96\x5e\xdd\xfa\xc5\x17\x55" +"\xc1\x08\x23\x10\x31\x39\x19\x74\x0c\x43" +"\xc9\x60\xb4\x8c\x46\xcd\x38\xe3\x10\x44" +"\x43\x1d\x35\x0d\x35\x50\x59\x73\x0d\x56" +"\x6e\x69\x03\x17\x37\xdd\x88\xf5\x4d\x55" +"\xe1\x88\x33\x10\x39\x3a\x1d\x74\x4e\x43" +"\xe9\x64\xb4\xce\x46\xed\xb8\xf3\x50\x44" +"\x44\x21\x35\x0f\x3d\x51\xd9\x73\x4f\x56" +"\x6f\xe9\x13\x17\x3f\xfd\x8c\x95\x2d" + +"iso88593\0\0\x0a\x21" +"\x26\x61\x3b\x0a\x29\x00\x90\x74\x0a\x2a" +"\x30\x79\xe5\x11\x4d\xad\x00\xb0\x17\x2c" +"\x27\xc9\x32\x0b\x2d\xb5\x94\x74\x0b\x2e" +"\x31\x7d\xf5\x51\x4d\xbd\x00\xc0\x17\x30" +"\xc1\x08\x03\x00\x31\x0a\x21\x74\x0c\x32" +"\xc9\x28\xb3\x0c\x33\xcd\x38\xf3\x0c\x00" +"\xd1\x48\x33\x0d\x35\x20\x59\x73\x0d\x47" +"\xd9\x68\xb3\x0d\x37\x6c\x71\xf5\x0d\x38" +"\xe1\x88\x03\x00\x39\x0b\x25\x74\x0e\x3a" +"\xe9\xa8\xb3\x0e\x3b\xed\xb8\xf3\x0e\x00" +"\xf1\xc8\x33\x0f\x3d\x21\xd9\x73\x4f\x47" +"\xf9\xe8\xb3\x0f\x3f\x6d\x75\x95\x2d" + +"iso88594\0\0\x0a\x21" +"\x04\xe1\x64\x15\x29\x28\xed\x74\x0a\x2a" +"\x60\x49\x24\x92\x59\xad\xf4\xf5\x0a\x2c" +"\x05\x6d\x7b\x15\x2d\x29\xf1\x74\x2c\x2e" +"\x61\x4d\x34\xd2\x59\x4a\xf9\xb5\x14\x40" +"\xc1\x08\x33\x0c\x31\xc5\x18\xe3\x12\x43" +"\xc9\x60\xb4\x8c\x45\xcd\x38\xa3\x12\x44" +"\x45\x31\x65\x13\x35\xd5\x58\x73\x0d\x36" +"\x72\x69\xb3\x0d\x37\x68\xa9\xf5\x4d\x40" +"\xe1\x88\x33\x0e\x39\xe5\x98\xf3\x52\x43" +"\xe9\x64\xb4\xce\x45\xed\xb8\xb3\x52\x44" +"\x46\x35\x75\x13\x3d\xf5\xd8\x73\x0f\x3e" +"\x73\xe9\xb3\x0f\x3f\x69\xad\x95\x2d" + +"iso88595\0\0\x0e\x21" +"\x01\x84\x00\x31\x40\x10\x10\x05\x84\x01" +"\x71\x40\x20\x10\x09\x84\x02\xb1\x40\x30" +"\x10\xad\x80\x03\xf1\x40\x40\x10\x11\x84" +"\x04\x31\x41\x50\x10\x15\x84\x05\x71\x41" +"\x60\x10\x19\x84\x06\xb1\x41\x70\x10\x1d" +"\x84\x07\xf1\x41\x80\x10\x21\x84\x08\x31" +"\x42\x90\x10\x25\x84\x09\x71\x42\xa0\x10" +"\x29\x84\x0a\xb1\x42\xb0\x10\x2d\x84\x0b" +"\xf1\x42\xc0\x10\x31\x84\x0c\x31\x43\xd0" +"\x10\x35\x84\x0d\x71\x43\xe0\x10\x39\x84" +"\x0e\xb1\x43\xf0\x10\x3d\x84\x0f\xf1\x43" +"\x00\x11\x41\x84\x10\x31\x44\x10\x11\x45" +"\x84\x11\x71\x44\x20\x11\x49\x84\x12\xb1" +"\x44\x30\x11\x4d\x84\x13\xf1\x44\x58\x84" +"\x51\x84\x14\x31\x45\x50\x11\x55\x84\x15" +"\x71\x45\x60\x11\x59\x84\x16\xb1\x45\x70" +"\x11\xa7\x80\x17\xf1\x45\x00" + +"iso88596\0\0\x0b\x21" +"\x00\x00\x00\x00\x48\x01\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x18\xdc\x0a\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\xc0\x86\x01\x00\x00" +"\x00\x7c\x18\x00\x21\x16\xf1\x88\x49\x5c" +"\x62\x13\x9f\x18\xc5\x29\x56\xf1\x8a\x59" +"\xdc\x62\x17\xbf\x18\xc6\x31\x96\xf1\x8c" +"\x69\x5c\x63\x1b\xdf\x18\xc7\x39\xd6\x31" +"\x00\x00\x00\x00\x00\x00\x00\xc8\x41\x16" +"\xf2\x90\x89\x5c\x64\x23\x1f\x19\xc9\x49" +"\x56\xf2\x92\x99\xdc\x64\x27\x3f\x19\xca" +"\x51\x96\x32\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00" + +"iso88597\0\0\x0e\x21" +"\x18\x60\x06\x38\x0a\xb0\x82\xaf\xa0\x29" +"\x70\x0a\xa0\x02\xa9\x80\xde\xb0\x0a\xb0" +"\x02\xad\x00\x00\x50\x01\xc2\x02\xb1\x80" +"\x2c\x30\x0b\x10\x0e\x85\x83\xe1\x70\x0b" +"\x20\x0e\x89\x83\xe2\xb0\x0b\x30\x0e\xbd" +"\x80\xe3\xf0\x38\x40\x0e\x91\x83\xe4\x30" +"\x39\x50\x0e\x95\x83\xe5\x70\x39\x60\x0e" +"\x99\x83\xe6\xb0\x39\x70\x0e\x9d\x83\xe7" +"\xf0\x39\x80\x0e\xa1\x03\x00\x30\x3a\x90" +"\x0e\xa5\x83\xe9\x70\x3a\xa0\x0e\xa9\x83" +"\xea\xb0\x3a\xb0\x0e\xad\x83\xeb\xf0\x3a" +"\xc0\x0e\xb1\x83\xec\x30\x3b\xd0\x0e\xb5" +"\x83\xed\x70\x3b\xe0\x0e\xb9\x83\xee\xb0" +"\x3b\xf0\x0e\xbd\x83\xef\xf0\x3b\x00\x0f" +"\xc1\x83\xf0\x30\x3c\x10\x0f\xc5\x83\xf1" +"\x70\x3c\x20\x0f\xc9\x83\xf2\xb0\x3c\x30" +"\x0f\xcd\x83\xf3\x00\x00\x00" + +"iso88598\0\0\x0e\x21" +"\x00\x80\x28\x30\x0a\x90\x02\xa5\x80\x29" +"\x70\x0a\xa0\x02\xa9\xc0\x35\xb0\x0a\xb0" +"\x02\xad\x80\x2b\xf0\x0a\xc0\x02\xb1\x80" +"\x2c\x30\x0b\xd0\x02\xb5\x80\x2d\x70\x0b" +"\xe0\x02\xb9\xc0\x3d\xb0\x0b\xf0\x02\xbd" +"\x80\x2f\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x70\x01" +"\x42\x17\xd1\x85\x74\x31\x5d\x50\x17\xd5" +"\x85\x75\x71\x5d\x60\x17\xd9\x85\x76\xb1" +"\x5d\x70\x17\xdd\x85\x77\xf1\x5d\x80\x17" +"\xe1\x85\x78\x31\x5e\x90\x17\xe5\x85\x79" +"\x71\x5e\xa0\x17\xe9\x85\x7a\x01\x00\x00" +"\x00\x0e\xe0\x03\x08\x00\x00" + +"iso88599\0\0\x09\x50" +"\x1e\xa3\x49\x9b\x46\xad\x9a\xb5\x6b\xd8" +"\xb2\x69\xdb\xc6\x0d\xa6\xd7\x6f\xe0\xc2" +"\x89\x1b\x47\xae\x9c\xb9\x73\xe8\xd2\xa9" +"\x5b\xc7\xae\x9d\xbb\x77\x1f\xe3\xc9\x9b" +"\x47\xaf\x9e\xbd\x7b\xf8\xf2\xe9\xdb\xc7" +"\x2f\xe6\xd7\x7f" + +"iso885910\0\0\x0e\x21" +"\x04\x81\x44\x20\x12\xa8\x04\x28\x81\x4d" +"\x70\x0a\xec\x04\x10\x01\x58\x60\x16\xf4" +"\x05\xad\x80\x5a\xa0\x14\xc0\x02\x05\xc1" +"\x44\x30\x12\xac\x04\x29\xc1\x4d\x70\x0b" +"\xf0\x04\x11\x41\x58\x70\x16\xf8\x05\x15" +"\xe0\x5a\xb0\x14\x00\x04\xc1\x80\x30\x30" +"\x0c\x10\x03\xc5\x80\x31\xe0\x12\x30\x04" +"\xc9\x00\x46\xb0\x0c\x58\x04\xcd\x80\x33" +"\xf0\x0c\x40\x03\x45\x01\x53\x30\x0d\x50" +"\x03\xd5\x80\x35\x80\x16\x60\x03\x72\x81" +"\x36\xb0\x0d\x70\x03\xdd\x80\x37\xf0\x0d" +"\x04\x04\xe1\x80\x38\x30\x0e\x90\x03\xe5" +"\x80\x39\xf0\x12\x34\x04\xe9\x40\x46\xb0" +"\x0e\x5c\x04\xed\x80\x3b\xf0\x0e\xc0\x03" +"\x46\x41\x53\x30\x0f\xd0\x03\xf5\x80\x3d" +"\x90\x16\xe0\x03\x73\x81\x3e\xb0\x0f\xf0" +"\x03\xfd\x80\x3f\x80\x13\x00" + +"iso885913\0\0\x0e\x21" +"\x1d\xa0\x28\x30\x0a\x90\x02\x1e\xa0\x29" +"\x70\x0a\x60\x03\xa9\x80\x55\xb0\x0a\xb0" +"\x02\xad\x80\x2b\x60\x0c\xc0\x02\xb1\x80" +"\x2c\x30\x0b\x70\x80\xb5\x80\x2d\x70\x0b" +"\xe0\x03\xb9\xc0\x55\xb0\x0b\xf0\x02\xbd" +"\x80\x2f\x60\x0e\x10\x04\x2e\x01\x40\x60" +"\x10\x10\x03\xc5\x00\x46\x20\x11\x30\x04" +"\xc9\x40\x5e\x60\x11\x88\x04\x36\x81\x4a" +"\xb0\x13\x80\x05\x43\x41\x51\x30\x0d\x30" +"\x05\xd5\x80\x35\x70\x0d\xc8\x05\x41\x81" +"\x56\xa0\x16\x70\x03\x7b\x41\x5f\xf0\x0d" +"\x14\x04\x2f\x41\x40\x70\x10\x90\x03\xe5" +"\x40\x46\x30\x11\x34\x04\xe9\x80\x5e\x70" +"\x11\x8c\x04\x37\xc1\x4a\xc0\x13\x84\x05" +"\x44\x81\x51\x30\x0f\x34\x05\xf5\x80\x3d" +"\x70\x0f\xcc\x05\x42\xc1\x56\xb0\x16\xf0" +"\x03\x7c\x81\x5f\x90\x01\x02" + +"iso885914\0\0\x0d\x21" +"\x02\x7e\xc0\x8f\x02\x85\xb0\x10\x14\xfc" +"\x29\x00\xf4\xa9\x40\xd0\x2f\x78\x79\xdf" +"\x0a\x5c\x01\x5e\xf0\xf0\x1f\x1e\x24\x84" +"\x04\x20\x1f\xe4\x6d\x81\x95\x0f\xf4\x57" +"\x7e\xd0\x83\xf9\x79\x4f\xe8\x0b\x7d\x98" +"\x07\x06\xc1\x40\x18\x0c\x03\x62\x50\x0c" +"\x8c\xc1\x31\x40\x06\xc9\x40\x19\x2c\x03" +"\x66\xd0\x0c\x9c\xc1\x33\xa0\x0b\xd1\x40" +"\x1a\x4c\x03\x6a\x50\x0d\xac\x81\x9a\xc7" +"\x06\xd9\x40\x1b\x6c\x03\x6e\xd0\x0d\xec" +"\xc2\x37\x00\x07\xe1\x40\x1c\x8c\x03\x72" +"\x50\x0e\xcc\xc1\x39\x40\x07\xe9\x40\x1d" +"\xac\x03\x76\xd0\x0e\xdc\xc1\x3b\xa8\x0b" +"\xf1\x40\x1e\xcc\x03\x7a\x50\x0f\xec\xc1" +"\x9a\xc7\x07\xf9\x40\x1f\xec\x03\x7e\xd0" +"\x0f\xee\xc2\x3f\x00" + +"iso885916\0\0\x0e\x21" +"\x04\x41\x41\x10\x14\xb0\x82\x1e\x20\x58" +"\x70\x0a\x84\x05\xa9\x00\x86\xb0\x0a\xe4" +"\x05\xad\x80\x5e\xb0\x17\xc0\x02\xb1\x00" +"\x43\x20\x14\xf4\x05\x1d\xa0\x2d\x70\x0b" +"\xf8\x05\x0d\x41\x86\xb0\x0b\x48\x05\x53" +"\x01\x5e\xc0\x17\x00\x03\xc1\x80\x30\x20" +"\x10\x10\x03\x06\x81\x31\x70\x0c\x20\x03" +"\xc9\x80\x32\xb0\x0c\x30\x03\xcd\x80\x33" +"\xf0\x0c\x40\x04\x43\x81\x34\x30\x0d\x50" +"\x03\x50\x81\x35\xa0\x15\xc0\x05\xd9\x80" +"\x36\xb0\x0d\x70\x03\x18\x81\x86\xf0\x0d" +"\x80\x03\xe1\x80\x38\x30\x10\x90\x03\x07" +"\x81\x39\x70\x0e\xa0\x03\xe9\x80\x3a\xb0" +"\x0e\xb0\x03\xed\x80\x3b\xf0\x0e\x44\x04" +"\x44\x81\x3c\x30\x0f\xd0\x03\x51\x81\x3d" +"\xb0\x15\xc4\x05\xf9\x80\x3e\xb0\x0f\xf0" +"\x03\x19\xc1\x86\xf0\x0f\x00" + +"windows1252\0\0\x0e\x00" +"\xac\x20\x00\xa0\x01\x4a\x06\x1e\xa0\x09" +"\x08\x02\x86\x80\xc6\x02\x0c\x08\x16\xe4" +"\x80\x52\x01\x00\xd0\x17\x00\x00\x00\x00" +"\x06\x98\x01\x72\x80\x1d\xa0\x08\x38\x01" +"\x52\x80\xdc\x82\x48\x18\x16\xe8\x80\x53" +"\x01\x00\xe0\x17\xe0\x05\xa0\x40\x28\x20" +"\x0a\x8c\x02\xa4\x40\x29\x60\x0a\x9c\x02" +"\xa8\x40\x2a\xa0\x0a\xac\x02\xac\x40\x2b" +"\xe0\x0a\xbc\x02\xb0\x40\x2c\x20\x0b\xcc" +"\x02\xb4\x40\x2d\x60\x0b\xdc\x02\xb8\x40" +"\x2e\xa0\x0b\xec\x02\xbc\x40\x2f\xe0\x0b" +"\xfc\x02\xc0\x40\x30\x20\x0c\x0c\x03\xc4" +"\x40\x31\x60\x0c\x1c\x03\xc8\x40\x32\xa0" +"\x0c\x2c\x03\xcc\x40\x33\xe0\x0c\x3c\x03" +"\xd0\x40\x34\x20\x0d\x4c\x03\xd4\x40\x35" +"\x60\x0d\x5c\x03\xd8\x40\x36\xa0\x0d\x6c" +"\x03\xdc\x40\x37\xe0\x0d\x7c\x03\xe0\x40" +"\x38\x20\x0e\x8c\x03\xe4\x40\x39\x60\x0e" +"\x9c\x03\xe8\x40\x3a\xa0\x0e\xac\x03\xec" +"\x40\x3b\xe0\x0e\xbc\x03\xf0\x40\x3c\x20" +"\x0f\xcc\x03\xf4\x40\x3d\x60\x0f\xdc\x03" +"\xf8\x40\x3e\xa0\x0f\xec\x03\xfc\x40\x3f" +"\xe0\x0f\xfc\x03" +; + + + +static int fuzzycmp(const char *a, const char *b) +{ + for (; *a && *b; a++, b++) { + while (*a && (*a|32U)-'a'>26 && *a-'0'>10U) a++; + if ((*a|32U) != *b) return 1; + } + return *a != *b; +} + +static int find_charmap(const char *name) +{ + const unsigned char *s; + for (s=charmaps; *s; ) { + if (!fuzzycmp(name, s)) { + for (; *s; s+=strlen(s)+1); + return s+1-charmaps; + } + s += strlen(s)+1; + if (!*s) s += ((128-s[2])*s[1]+7)/8 + 3; + } + return -1; +} + +iconv_t iconv_open(const char *to, const char *from) +{ + int f, t; + + if ((t = find_charmap(to)) < 0 || (f = find_charmap(from)) < 0) { + errno = EINVAL; + return (iconv_t)-1; + } + + return (void *)(f<<16 | t); +} + +int iconv_close(iconv_t cd) +{ + return 0; +} + +static unsigned get_16(const unsigned char *s, int e) +{ + e &= 1; + return s[e]<<8 | s[1-e]; +} + +static void put_16(unsigned char *s, unsigned c, int e) +{ + e &= 1; + s[e] = c>>8; + s[1-e] = c; +} + +static unsigned get_32(const unsigned char *s, int e) +{ + return s[e]+0U<<24 | s[e^1]<<16 | s[e^2]<<8 | s[e^3]; +} + +static void put_32(unsigned char *s, unsigned c, int e) +{ + s[e^0] = c>>24; + s[e^1] = c>>16; + s[e^2] = c>>8; + s[e^3] = c; +} + + + +#define GET_MAPPING(m, i, n) ( (1<<(n))-1 & ( \ + (m)[(i)*(n)/8] >> ((n)%8*(i)%8) | \ + (m)[(i)*(n)/8+1] << 8-((n)%8*(i)%8) | \ + (m)[(i)*(n)/8+2] << 16-((n)%8*(i)%8) ) ) + +static unsigned get_mapping(const unsigned char *m, unsigned c, unsigned n) +{ + switch (n) { + default: + case 9: return m[c*9/8]>>c%8 | m[c*9/8+1]<<8-c%8 & (1<<n)-1; + case 10: return m[c*10/8]>>2*c%8 | m[c*10/8+1]<<8-2*c%8 & (1<<n)-1; + case 11: return GET_MAPPING(m, c, 11); + case 13: return GET_MAPPING(m, c, 13); + case 14: return GET_MAPPING(m, c, 14); + } +} + +/* Adapt as needed */ +#define mbrtowc_utf8 mbrtowc +#define wctomb_utf8 wctomb + +#include <stdio.h> +size_t iconv(iconv_t cd0, char **in, size_t *inb, char **out, size_t *outb) +{ + size_t x=0; + unsigned long cd = (unsigned long)cd0; + unsigned to = cd & 0xffff; + unsigned from = cd >> 16; + const unsigned char *map = charmaps+from+2; + const unsigned char *tomap = charmaps+to+2; + mbstate_t st = {0}; + wchar_t wc; + unsigned c, d; + size_t k, l; + int err; + unsigned elide = map[-1] + 128; + unsigned toelide = tomap[-1] + 128; + unsigned char type = map[-2]; + unsigned char totype = tomap[-2]; + + if (!in || !*in || !*inb) return 0; + + for (; *inb; *in+=l, *inb-=l) { + c = *(unsigned char *)*in; + l = 1; + if (type < 8 || c >= 0x80) switch (type) { + case UTF_8: + l = mbrtowc_utf8(&wc, *in, *inb, &st); + if (!l) l++; + else if (l == (size_t)-1) goto ilseq; + else if (l == (size_t)-2) goto starved; + c = wc; + break; + case LATIN_9: + if ((unsigned)c - 0xa4 <= 0xbe - 0xa4) { + static const unsigned char map[] = { + 0, 0x60, 0, 0x61, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0x7d, 0, 0, 0, 0x7e, 0, 0, 0, + 0x52, 0x53, 0x78 + }; + if (c == 0xa4) c = 0x20ac; + else if (map[c-0xa5]) c = 0x100 | map[c-0xa5]; + } + break; + case TIS_620: + if (c >= 0xa1) c += 0x0e01-0xa1; + break; + case JIS_0201: + if (c >= 0xa1) + if (c <= 0xdf) c += 0xff61-0xa1; + else goto ilseq; + break; + case 9: case 10: case 11: case 13: case 14: + if (c < elide) break; + c = get_mapping(map, c-elide, type); + if (!c) { + case US_ASCII: + goto ilseq; + } + break; + case WCHAR_T: + l = sizeof(wchar_t); + if (*inb < l) goto starved; + c = *(wchar_t *)*in; + if (0) { + case UTF_32BE: + case UTF_32LE: + l = 4; + if (*inb < 4) goto starved; + c = get_32(*in, type); + } + if (c-0xd800u < 0x800u || c >= 0x110000u) goto ilseq; + break; + case UCS2BE: + case UCS2LE: + case UTF_16BE: + case UTF_16LE: + l = 2; + if (*inb < 2) goto starved; + c = get_16(*in, type); + if ((unsigned)(c-0xdc00) < 0x400) goto ilseq; + if ((unsigned)(c-0xd800) < 0x400) { + if (type-UCS2BE < 2U) goto ilseq; + l = 4; + if (*inb < 4) goto starved; + d = get_16(*in + 2, from); + if ((unsigned)(c-0xdc00) >= 0x400) goto ilseq; + c = ((c-0xd800)<<10) | (d-0xdc00); + } + break; + } + + switch (totype) { + case WCHAR_T: + if (*outb < sizeof(wchar_t)) goto toobig; + *(wchar_t *)*out = c; + *out += sizeof(wchar_t); + *outb -= sizeof(wchar_t); + break; + case UTF_8: + if (*outb < 4) { + char tmp[4]; + k = wctomb_utf8(tmp, c); + if (*outb < k) goto toobig; + memcpy(*out, tmp, k); + } else k = wctomb_utf8(*out, c); + *out += k; + *outb -= k; + break; + case TIS_620: + if (c-0xe01u <= 0xff-0xa1) + c -= 0xe01-0xa1; + else if (c >= 0xa1) + goto ascii; + goto revout; + case JIS_0201: + if (c-0xff61u <= 0xdf-0xa1) + c -= 0xff61-0xa1; + else if (c >= 0xa1) + goto ascii; + goto revout; + case LATIN_9: + if (c == 0x20ac) { + c=0xa4; + } else if (c-0x150u<=0x12 && (1<<c-0x150 & 0x3000c)) { + static const unsigned char map[] = + { 0xa6,0xa8,0xbc,0xbd }; + c = map[c&3]; + } else if (c-0x178u<=0x7 && (1<<c-0x178 & 0x61)) { + static const unsigned char map[] = + { 0xbe,0,0,0,0,0xb4,0xb8 }; + c = map[c&7]; + } else if (c>0x100 || + c-0xa5u<=0xbeu-0xa5 + && (1<<c-0xa5 & 0x388800a)) + case US_ASCII: ascii: + if (c > 0x7f) x++, c='*'; + case 9: case 10: case 11: case 13: case 14: + if (*outb < 1) goto toobig; + if (c < toelide) { + revout: + *(*out)++ = c; + *outb -= 1; + break; + } + for (d=0; d<256-toelide; d++) { + if (c == get_mapping(tomap, d, totype)) { + c = d + toelide; + goto revout; + } + } + x++; + c = '*'; + goto revout; + case UCS2BE: + case UCS2LE: + case UTF_16BE: + case UTF_16LE: + if (c < 0x10000) { + if (*outb < 2) goto toobig; + put_16(*out, c, totype); + *out += 2; + *outb -= 2; + break; + } + if (type-UCS2BE < 2U) goto ilseq; + if (*outb < 4) goto toobig; + put_16(*out, (c>>10)|0xd800, totype); + put_16(*out + 2, (c&0x3ff)|0xdc00, totype); + *out += 4; + *outb -= 4; + break; + case UTF_32BE: + case UTF_32LE: + if (*outb < 4) goto toobig; + put_32(*out, c, totype); + *out += 4; + *outb -= 4; + break; + } + } + return x; +ilseq: + err = EILSEQ; + x = -1; + goto end; +toobig: + err = E2BIG; + goto end; +starved: + err = EINVAL; +end: + errno = err; + return x; +} diff --git a/src/locale/intl.c b/src/locale/intl.c new file mode 100644 index 00000000..964f7da1 --- /dev/null +++ b/src/locale/intl.c @@ -0,0 +1,67 @@ +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> + +char *gettext(const char *msgid) +{ + return (char *) msgid; +} + +char *dgettext(const char *domainname, const char *msgid) +{ + return (char *) msgid; +} + +char *dcgettext(const char *domainname, const char *msgid, int category) +{ + return (char *) msgid; +} + +char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category) +{ + return (char *) ((n == 1) ? msgid1 : msgid2); +} + +char *textdomain(const char *domainname) +{ + static const char default_str[] = "messages"; + + if (domainname && *domainname && strcmp(domainname, default_str)) { + errno = EINVAL; + return NULL; + } + return (char *) default_str; +} + +char *bindtextdomain(const char *domainname, const char *dirname) +{ + static const char dir[] = "/"; + + if (!domainname || !*domainname + || (dirname && ((dirname[0] != '/') || dirname[1])) + ) { + errno = EINVAL; + return NULL; + } + + return (char *) dir; +} + +char *bind_textdomain_codeset(const char *domainname, const char *codeset) +{ + if (!domainname || !*domainname || (codeset && strcasecmp(codeset, "UTF-8"))) { + errno = EINVAL; + } + return NULL; +} diff --git a/src/locale/isalnum_l.c b/src/locale/isalnum_l.c new file mode 100644 index 00000000..b8a6eef3 --- /dev/null +++ b/src/locale/isalnum_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isalnum_l(int c, locale_t l) +{ + return isalnum(c); +} diff --git a/src/locale/isalpha_l.c b/src/locale/isalpha_l.c new file mode 100644 index 00000000..2e1205c6 --- /dev/null +++ b/src/locale/isalpha_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isalpha_l(int c, locale_t l) +{ + return isalpha(c); +} diff --git a/src/locale/isblank_l.c b/src/locale/isblank_l.c new file mode 100644 index 00000000..27479aa1 --- /dev/null +++ b/src/locale/isblank_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isblank_l(int c, locale_t l) +{ + return isblank(c); +} diff --git a/src/locale/iscntrl_l.c b/src/locale/iscntrl_l.c new file mode 100644 index 00000000..ca596fa9 --- /dev/null +++ b/src/locale/iscntrl_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int iscntrl_l(int c, locale_t l) +{ + return iscntrl(c); +} diff --git a/src/locale/isdigit_l.c b/src/locale/isdigit_l.c new file mode 100644 index 00000000..c8ae7bd3 --- /dev/null +++ b/src/locale/isdigit_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isdigit_l(int c, locale_t l) +{ + return isdigit(c); +} diff --git a/src/locale/isgraph_l.c b/src/locale/isgraph_l.c new file mode 100644 index 00000000..713a86e6 --- /dev/null +++ b/src/locale/isgraph_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isgraph_l(int c, locale_t l) +{ + return isgraph(c); +} diff --git a/src/locale/islower_l.c b/src/locale/islower_l.c new file mode 100644 index 00000000..25ec97a1 --- /dev/null +++ b/src/locale/islower_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int islower_l(int c, locale_t l) +{ + return islower(c); +} diff --git a/src/locale/isprint_l.c b/src/locale/isprint_l.c new file mode 100644 index 00000000..79ef3514 --- /dev/null +++ b/src/locale/isprint_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isprint_l(int c, locale_t l) +{ + return isprint(c); +} diff --git a/src/locale/ispunct_l.c b/src/locale/ispunct_l.c new file mode 100644 index 00000000..1c0bd046 --- /dev/null +++ b/src/locale/ispunct_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int ispunct_l(int c, locale_t l) +{ + return ispunct(c); +} diff --git a/src/locale/isspace_l.c b/src/locale/isspace_l.c new file mode 100644 index 00000000..e1a0efed --- /dev/null +++ b/src/locale/isspace_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isspace_l(int c, locale_t l) +{ + return isspace(c); +} diff --git a/src/locale/isupper_l.c b/src/locale/isupper_l.c new file mode 100644 index 00000000..11ba7036 --- /dev/null +++ b/src/locale/isupper_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isupper_l(int c, locale_t l) +{ + return isupper(c); +} diff --git a/src/locale/isxdigit_l.c b/src/locale/isxdigit_l.c new file mode 100644 index 00000000..68649d09 --- /dev/null +++ b/src/locale/isxdigit_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int isxdigit_l(int c, locale_t l) +{ + return isxdigit(c); +} diff --git a/src/locale/langinfo.c b/src/locale/langinfo.c new file mode 100644 index 00000000..f7f56012 --- /dev/null +++ b/src/locale/langinfo.c @@ -0,0 +1,58 @@ +#include <locale.h> +#include <langinfo.h> + +static const char c_time[] = + "Sun\0" "Mon\0" "Tue\0" "Wed\0" "Thu\0" "Fri\0" "Sat\0" + "Sunday\0" "Monday\0" "Tuesday\0" "Wednesday\0" + "Thursday\0" "Friday\0" "Saturday\0" + "Jan\0" "Feb\0" "Mar\0" "Apr\0" "May\0" "Jun\0" + "Jul\0" "Aug\0" "Sep\0" "Oct\0" "Nov\0" "Dec\0" + "January\0" "February\0" "March\0" "April\0" + "May\0" "June\0" "July\0" "August\0" + "September\0" "October\0" "November\0" "December\0" + "AM\0" "PM\0" + "%a %b %e %T %Y\0" + "%m/%d/%y\0" + "%H:%M:%S\0" + "%I:%M:%S %p\0" + "\0" + "%m/%d/%y\0" + "0123456789" + "%a %b %e %T %Y\0" + "%H:%M:%S"; + +static const char c_messages[] = "^[yY]\0" "^[nN]"; +static const char c_numeric[] = ".\0" ""; + +const char *__langinfo(nl_item item) +{ + int cat = item >> 16; + int idx = item & 65535; + const char *str; + + if (item == CODESET) return "UTF-8"; + + switch (cat) { + case LC_NUMERIC: + if (idx > 1) return NULL; + str = c_numeric; + break; + case LC_TIME: + if (idx > 0x31) return NULL; + str = c_time; + break; + case LC_MONETARY: + if (idx > 0) return NULL; + str = ""; + break; + case LC_MESSAGES: + if (idx > 1) return NULL; + str = c_messages; + break; + default: + return NULL; + } + + for (; idx; idx--, str++) for (; *str; str++); + return str; +} diff --git a/src/locale/localeconv.c b/src/locale/localeconv.c new file mode 100644 index 00000000..d79d1c07 --- /dev/null +++ b/src/locale/localeconv.c @@ -0,0 +1,22 @@ +#include <locale.h> +#include <string.h> +#include <stdlib.h> + +struct lconv *localeconv(void) +{ + static struct lconv *posix_lconv; + if (posix_lconv) return posix_lconv; + posix_lconv = malloc(sizeof *posix_lconv); + memset(posix_lconv, -1, sizeof *posix_lconv); + posix_lconv->decimal_point = "."; + posix_lconv->thousands_sep = ""; + posix_lconv->grouping = "\xff"; + posix_lconv->int_curr_symbol = ""; //"\xc2\xa4"; + posix_lconv->currency_symbol = ""; + posix_lconv->mon_decimal_point = ""; + posix_lconv->mon_thousands_sep = ""; + posix_lconv->mon_grouping = "\xff"; + posix_lconv->positive_sign = ""; // "+"; + posix_lconv->negative_sign = ""; // "-"; + return posix_lconv; +} diff --git a/src/locale/newlocale.c b/src/locale/newlocale.c new file mode 100644 index 00000000..986e796f --- /dev/null +++ b/src/locale/newlocale.c @@ -0,0 +1,11 @@ +#include <stdlib.h> +#include <string.h> +#include "locale_impl.h" + +locale_t newlocale(int mask, const char *name, locale_t base) +{ + if (*name && strcmp(name, "C") && strcmp(name, "POSIX")) + return 0; + if (!base) base = calloc(1, sizeof *base); + return base; +} diff --git a/src/locale/nl_langinfo.c b/src/locale/nl_langinfo.c new file mode 100644 index 00000000..bb3a2c46 --- /dev/null +++ b/src/locale/nl_langinfo.c @@ -0,0 +1,13 @@ +#include <langinfo.h> + +// FIXME: other items + +char *nl_langinfo(nl_item item) +{ + switch (item) { + case CODESET: + return "UTF-8"; + default: + return ""; + } +} diff --git a/src/locale/setlocale.c b/src/locale/setlocale.c new file mode 100644 index 00000000..28f29b80 --- /dev/null +++ b/src/locale/setlocale.c @@ -0,0 +1,9 @@ +#include <locale.h> + +char *setlocale(int category, const char *locale) +{ + /* Note: plain "C" would be better, but puts some broken + * software into legacy 8-bit-codepage mode, ignoring + * the standard library's multibyte encoding */ + return "C.UTF-8"; +} diff --git a/src/locale/strcoll.c b/src/locale/strcoll.c new file mode 100644 index 00000000..30bccd62 --- /dev/null +++ b/src/locale/strcoll.c @@ -0,0 +1,6 @@ +#include <string.h> + +int strcoll(const char *l, const char *r) +{ + return strcmp(l, r); +} diff --git a/src/locale/strxfrm.c b/src/locale/strxfrm.c new file mode 100644 index 00000000..8f123399 --- /dev/null +++ b/src/locale/strxfrm.c @@ -0,0 +1,9 @@ +#include <string.h> + +/* collate only by code points */ +size_t strxfrm(char *dest, const char *src, size_t n) +{ + size_t l = strlen(src); + if (n > l) strcpy(dest, src); + return l; +} diff --git a/src/locale/tmp b/src/locale/tmp new file mode 100644 index 00000000..aa71779f --- /dev/null +++ b/src/locale/tmp @@ -0,0 +1,390 @@ +"iso88591\0\x08\x80" +"iso88592\0\x0a\x21" +"\x04\x61\x1b\x14\x29\x3d\x69\x75\x0a\x2a" +"\x60\x79\x45\x56\x5e\xad\xf4\xb5\x17\x2c" +"\x05\x6d\x2b\x14\x2d\x3e\x6d\x75\x2c\x2e" +"\x61\x7d\x55\x96\x5e\xdd\xfa\xc5\x17\x55" +"\xc1\x08\x23\x10\x31\x39\x19\x74\x0c\x43" +"\xc9\x60\xb4\x8c\x46\xcd\x38\xe3\x10\x44" +"\x43\x1d\x35\x0d\x35\x50\x59\x73\x0d\x56" +"\x6e\x69\x03\x17\x37\xdd\x88\xf5\x4d\x55" +"\xe1\x88\x33\x10\x39\x3a\x1d\x74\x4e\x43" +"\xe9\x64\xb4\xce\x46\xed\xb8\xf3\x50\x44" +"\x44\x21\x35\x0f\x3d\x51\xd9\x73\x4f\x56" +"\x6f\xe9\x13\x17\x3f\xfd\x8c\x95\x2d" +"iso88593\0\x0a0x21" +"\x26\x61\x3b\x0a\x29\x00\x90\x74\x0a\x2a" +"\x30\x79\xe5\x11\x4d\xad\x00\xb0\x17\x2c" +"\x27\xc9\x32\x0b\x2d\xb5\x94\x74\x0b\x2e" +"\x31\x7d\xf5\x51\x4d\xbd\x00\xc0\x17\x30" +"\xc1\x08\x03\x00\x31\x0a\x21\x74\x0c\x32" +"\xc9\x28\xb3\x0c\x33\xcd\x38\xf3\x0c\x00" +"\xd1\x48\x33\x0d\x35\x20\x59\x73\x0d\x47" +"\xd9\x68\xb3\x0d\x37\x6c\x71\xf5\x0d\x38" +"\xe1\x88\x03\x00\x39\x0b\x25\x74\x0e\x3a" +"\xe9\xa8\xb3\x0e\x3b\xed\xb8\xf3\x0e\x00" +"\xf1\xc8\x33\x0f\x3d\x21\xd9\x73\x4f\x47" +"\xf9\xe8\xb3\x0f\x3f\x6d\x75\x95\x2d" +"iso88594\0\x0a\x21" +"\x04\xe1\x64\x15\x29\x28\xed\x74\x0a\x2a" +"\x60\x49\x24\x92\x59\xad\xf4\xf5\x0a\x2c" +"\x05\x6d\x7b\x15\x2d\x29\xf1\x74\x2c\x2e" +"\x61\x4d\x34\xd2\x59\x4a\xf9\xb5\x14\x40" +"\xc1\x08\x33\x0c\x31\xc5\x18\xe3\x12\x43" +"\xc9\x60\xb4\x8c\x45\xcd\x38\xa3\x12\x44" +"\x45\x31\x65\x13\x35\xd5\x58\x73\x0d\x36" +"\x72\x69\xb3\x0d\x37\x68\xa9\xf5\x4d\x40" +"\xe1\x88\x33\x0e\x39\xe5\x98\xf3\x52\x43" +"\xe9\x64\xb4\xce\x45\xed\xb8\xb3\x52\x44" +"\x46\x35\x75\x13\x3d\xf5\xd8\x73\x0f\x3e" +"\x73\xe9\xb3\x0f\x3f\x69\xad\x95\x2d" +"iso88595\0\x0d\x21" +"\x01\x84\x00\x30\x40\x10\x10\x05\x84\x01" +"\x70\x40\x20\x10\x09\x84\x02\xb0\x40\x30" +"\x10\xad\x80\x03\xf0\x40\x40\x10\x11\x84" +"\x04\x30\x41\x50\x10\x15\x84\x05\x70\x41" +"\x60\x10\x19\x84\x06\xb0\x41\x70\x10\x1d" +"\x84\x07\xf0\x41\x80\x10\x21\x84\x08\x30" +"\x42\x90\x10\x25\x84\x09\x70\x42\xa0\x10" +"\x29\x84\x0a\xb0\x42\xb0\x10\x2d\x84\x0b" +"\xf0\x42\xc0\x10\x31\x84\x0c\x30\x43\xd0" +"\x10\x35\x84\x0d\x70\x43\xe0\x10\x39\x84" +"\x0e\xb0\x43\xf0\x10\x3d\x84\x0f\xf0\x43" +"\x00\x11\x41\x84\x10\x30\x44\x10\x11\x45" +"\x84\x11\x70\x44\x20\x11\x49\x84\x12\xb0" +"\x44\x30\x11\x4d\x84\x13\xf0\x44\x58\x84" +"\x51\x84\x14\x30\x45\x50\x11\x55\x84\x15" +"\x70\x45\x60\x11\x59\x84\x16\xb0\x45\x70" +"\x11\xa7\x80\x17\xf0\x45\x00" +"iso88596\0\x0b\x21" +"\x00\x00\x00\x00\x48\x01\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x18\xdc\x0a\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\xc0\x86\x00\x00\x00" +"\x00\x7c\x18\x00\x21\x16\xf1\x88\x48\x5c" +"\x62\x13\x9c\x18\xc5\x29\x56\xf1\x8a\x58" +"\xdc\x62\x17\xbc\x18\xc6\x31\x96\xf1\x8c" +"\x68\x5c\x63\x1b\xdc\x18\xc7\x39\xd6\x31" +"\x00\x00\x00\x00\x00\x00\x00\xc8\x41\x16" +"\xf2\x90\x88\x5c\x64\x23\x1c\x19\xc9\x49" +"\x56\xf2\x92\x98\xdc\x64\x27\x3c\x19\xca" +"\x51\x96\x32\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00" +"iso88597\0\x0d\x21" +"\x18\x60\x06\x30\x0a\xb0\x82\xaf\xa0\x29" +"\x70\x0a\xa0\x02\xa9\x80\xde\xb0\x0a\xb0" +"\x02\xad\x00\x00\x50\x01\xc0\x02\xb1\x80" +"\x2c\x30\x0b\x10\x0e\x85\x83\xe1\x70\x0b" +"\x20\x0e\x89\x83\xe2\xb0\x0b\x30\x0e\xbd" +"\x80\xe3\xf0\x38\x40\x0e\x91\x83\xe4\x30" +"\x39\x50\x0e\x95\x83\xe5\x70\x39\x60\x0e" +"\x99\x83\xe6\xb0\x39\x70\x0e\x9d\x83\xe7" +"\xf0\x39\x80\x0e\xa1\x03\x00\x30\x3a\x90" +"\x0e\xa5\x83\xe9\x70\x3a\xa0\x0e\xa9\x83" +"\xea\xb0\x3a\xb0\x0e\xad\x83\xeb\xf0\x3a" +"\xc0\x0e\xb1\x83\xec\x30\x3b\xd0\x0e\xb5" +"\x83\xed\x70\x3b\xe0\x0e\xb9\x83\xee\xb0" +"\x3b\xf0\x0e\xbd\x83\xef\xf0\x3b\x00\x0f" +"\xc1\x83\xf0\x30\x3c\x10\x0f\xc5\x83\xf1" +"\x70\x3c\x20\x0f\xc9\x83\xf2\xb0\x3c\x30" +"\x0f\xcd\x83\xf3\x00\x00\x00" +"iso88598\0\x0d\x21" +"\x00\x80\x28\x30\x0a\x90\x02\xa5\x80\x29" +"\x70\x0a\xa0\x02\xa9\xc0\x35\xb0\x0a\xb0" +"\x02\xad\x80\x2b\xf0\x0a\xc0\x02\xb1\x80" +"\x2c\x30\x0b\xd0\x02\xb5\x80\x2d\x70\x0b" +"\xe0\x02\xb9\xc0\x3d\xb0\x0b\xf0\x02\xbd" +"\x80\x2f\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +"\x00\x00\x00\x00\x00\x00\x00\x00\x70\x01" +"\x40\x17\xd1\x85\x74\x30\x5d\x50\x17\xd5" +"\x85\x75\x70\x5d\x60\x17\xd9\x85\x76\xb0" +"\x5d\x70\x17\xdd\x85\x77\xf0\x5d\x80\x17" +"\xe1\x85\x78\x30\x5e\x90\x17\xe5\x85\x79" +"\x70\x5e\xa0\x17\xe9\x85\x7a\x00\x00\x00" +"\x00\x0e\xe0\x03\x00\x00\x00" +"iso88599\0\x09\x50" +"\x1e\xa3\x49\x9b\x46\xad\x9a\xb5\x6b\xd8" +"\xb2\x69\xdb\xc6\x0d\xa6\xd7\x6f\xe0\xc2" +"\x89\x1b\x47\xae\x9c\xb9\x73\xe8\xd2\xa9" +"\x5b\xc7\xae\x9d\xbb\x77\x1f\xe3\xc9\x9b" +"\x47\xaf\x9e\xbd\x7b\xf8\xf2\xe9\xdb\xc7" +"\x2f\xe6\xd7\x7f" +"iso885910\0\x0d\x21" +"\x04\x81\x44\x20\x12\xa8\x04\x28\x81\x4d" +"\x70\x0a\xec\x04\x10\x01\x58\x60\x16\xf4" +"\x05\xad\x80\x5a\xa0\x14\xc0\x02\x05\xc1" +"\x44\x30\x12\xac\x04\x29\xc1\x4d\x70\x0b" +"\xf0\x04\x11\x41\x58\x70\x16\xf8\x05\x15" +"\xe0\x5a\xb0\x14\x00\x04\xc1\x80\x30\x30" +"\x0c\x10\x03\xc5\x80\x31\xe0\x12\x30\x04" +"\xc9\x00\x46\xb0\x0c\x58\x04\xcd\x80\x33" +"\xf0\x0c\x40\x03\x45\x01\x53\x30\x0d\x50" +"\x03\xd5\x80\x35\x80\x16\x60\x03\x72\x81" +"\x36\xb0\x0d\x70\x03\xdd\x80\x37\xf0\x0d" +"\x04\x04\xe1\x80\x38\x30\x0e\x90\x03\xe5" +"\x80\x39\xf0\x12\x34\x04\xe9\x40\x46\xb0" +"\x0e\x5c\x04\xed\x80\x3b\xf0\x0e\xc0\x03" +"\x46\x41\x53\x30\x0f\xd0\x03\xf5\x80\x3d" +"\x90\x16\xe0\x03\x73\x81\x3e\xb0\x0f\xf0" +"\x03\xfd\x80\x3f\x80\x13\x00" +"iso885913\0\x0d\x21" +"\x1d\xa0\x28\x30\x0a\x90\x02\x1e\xa0\x29" +"\x70\x0a\x60\x03\xa9\x80\x55\xb0\x0a\xb0" +"\x02\xad\x80\x2b\x60\x0c\xc0\x02\xb1\x80" +"\x2c\x30\x0b\x70\x80\xb5\x80\x2d\x70\x0b" +"\xe0\x03\xb9\xc0\x55\xb0\x0b\xf0\x02\xbd" +"\x80\x2f\x60\x0e\x10\x04\x2e\x01\x40\x60" +"\x10\x10\x03\xc5\x00\x46\x20\x11\x30\x04" +"\xc9\x40\x5e\x60\x11\x88\x04\x36\x81\x4a" +"\xb0\x13\x80\x05\x43\x41\x51\x30\x0d\x30" +"\x05\xd5\x80\x35\x70\x0d\xc8\x05\x41\x81" +"\x56\xa0\x16\x70\x03\x7b\x41\x5f\xf0\x0d" +"\x14\x04\x2f\x41\x40\x70\x10\x90\x03\xe5" +"\x40\x46\x30\x11\x34\x04\xe9\x80\x5e\x70" +"\x11\x8c\x04\x37\xc1\x4a\xc0\x13\x84\x05" +"\x44\x81\x51\x30\x0f\x34\x05\xf5\x80\x3d" +"\x70\x0f\xcc\x05\x42\xc1\x56\xb0\x16\xf0" +"\x03\x7c\x81\x5f\x90\x01\x00" +"iso885914\0\x0c\x21" +"\x02\x7e\xc0\x8c\x02\x85\xb0\x10\x14\xfc" +"\x29\x00\xf4\xa9\x40\xd0\x2c\x78\x79\xd0" +"\x0a\x5c\x01\x5e\xf0\xf0\x1f\x1e\x24\x84" +"\x04\x20\x10\xe4\x6c\x81\x95\x08\xf4\x57" +"\x7e\xd0\x80\xf9\x79\x40\xe8\x0a\x7d\x98" +"\x00\x06\xc1\x40\x18\x0c\x03\x62\x50\x0c" +"\x8c\xc1\x31\x40\x06\xc9\x40\x19\x2c\x03" +"\x66\xd0\x0c\x9c\xc1\x33\xa0\x0b\xd1\x40" +"\x1a\x4c\x03\x6a\x50\x0d\xac\x81\x9a\xc0" +"\x06\xd9\x40\x1b\x6c\x03\x6e\xd0\x0d\xec" +"\xc2\x37\x00\x07\xe1\x40\x1c\x8c\x03\x72" +"\x50\x0e\xcc\xc1\x39\x40\x07\xe9\x40\x1d" +"\xac\x03\x76\xd0\x0e\xdc\xc1\x3b\xa8\x0b" +"\xf1\x40\x1e\xcc\x03\x7a\x50\x0f\xec\xc1" +"\x9a\xc0\x07\xf9\x40\x1f\xec\x03\x7e\xd0" +"\x0f\xee\xc2\x3f\x00" +"iso885916\0\x0d\x21" +"\x04\x41\x41\x10\x14\xb0\x82\x1e\x20\x58" +"\x70\x0a\x84\x05\xa9\x00\x86\xb0\x0a\xe4" +"\x05\xad\x80\x5e\xb0\x17\xc0\x02\xb1\x00" +"\x43\x20\x14\xf4\x05\x1d\xa0\x2d\x70\x0b" +"\xf8\x05\x0d\x41\x86\xb0\x0b\x48\x05\x53" +"\x01\x5e\xc0\x17\x00\x03\xc1\x80\x30\x20" +"\x10\x10\x03\x06\x81\x31\x70\x0c\x20\x03" +"\xc9\x80\x32\xb0\x0c\x30\x03\xcd\x80\x33" +"\xf0\x0c\x40\x04\x43\x81\x34\x30\x0d\x50" +"\x03\x50\x81\x35\xa0\x15\xc0\x05\xd9\x80" +"\x36\xb0\x0d\x70\x03\x18\x81\x86\xf0\x0d" +"\x80\x03\xe1\x80\x38\x30\x10\x90\x03\x07" +"\x81\x39\x70\x0e\xa0\x03\xe9\x80\x3a\xb0" +"\x0e\xb0\x03\xed\x80\x3b\xf0\x0e\x44\x04" +"\x44\x81\x3c\x30\x0f\xd0\x03\x51\x81\x3d" +"\xb0\x15\xc4\x05\xf9\x80\x3e\xb0\x0f\xf0" +"\x03\x19\xc1\x86\xf0\x0f\x00" +; + + +'i','s','o','8','8','5','9','1',0, +8,128, +'i','s','o','8','8','5','9','2',0, +10, 33, +0x04, 0x61, 0x1b, 0x14, 0x29, 0x3d, 0x69, 0x75, 0x0a, 0x2a, +0x60, 0x79, 0x45, 0x56, 0x5e, 0xad, 0xf4, 0xb5, 0x17, 0x2c, +0x05, 0x6d, 0x2b, 0x14, 0x2d, 0x3e, 0x6d, 0x75, 0x2c, 0x2e, +0x61, 0x7d, 0x55, 0x96, 0x5e, 0xdd, 0xfa, 0xc5, 0x17, 0x55, +0xc1, 0x08, 0x23, 0x10, 0x31, 0x39, 0x19, 0x74, 0x0c, 0x43, +0xc9, 0x60, 0xb4, 0x8c, 0x46, 0xcd, 0x38, 0xe3, 0x10, 0x44, +0x43, 0x1d, 0x35, 0x0d, 0x35, 0x50, 0x59, 0x73, 0x0d, 0x56, +0x6e, 0x69, 0x03, 0x17, 0x37, 0xdd, 0x88, 0xf5, 0x4d, 0x55, +0xe1, 0x88, 0x33, 0x10, 0x39, 0x3a, 0x1d, 0x74, 0x4e, 0x43, +0xe9, 0x64, 0xb4, 0xce, 0x46, 0xed, 0xb8, 0xf3, 0x50, 0x44, +0x44, 0x21, 0x35, 0x0f, 0x3d, 0x51, 0xd9, 0x73, 0x4f, 0x56, +0x6f, 0xe9, 0x13, 0x17, 0x3f, 0xfd, 0x8c, 0x95, 0x2d, +'i','s','o','8','8','5','9','3',0, +10, 33, +0x26, 0x61, 0x3b, 0x0a, 0x29, 0x00, 0x90, 0x74, 0x0a, 0x2a, +0x30, 0x79, 0xe5, 0x11, 0x4d, 0xad, 0x00, 0xb0, 0x17, 0x2c, +0x27, 0xc9, 0x32, 0x0b, 0x2d, 0xb5, 0x94, 0x74, 0x0b, 0x2e, +0x31, 0x7d, 0xf5, 0x51, 0x4d, 0xbd, 0x00, 0xc0, 0x17, 0x30, +0xc1, 0x08, 0x03, 0x00, 0x31, 0x0a, 0x21, 0x74, 0x0c, 0x32, +0xc9, 0x28, 0xb3, 0x0c, 0x33, 0xcd, 0x38, 0xf3, 0x0c, 0x00, +0xd1, 0x48, 0x33, 0x0d, 0x35, 0x20, 0x59, 0x73, 0x0d, 0x47, +0xd9, 0x68, 0xb3, 0x0d, 0x37, 0x6c, 0x71, 0xf5, 0x0d, 0x38, +0xe1, 0x88, 0x03, 0x00, 0x39, 0x0b, 0x25, 0x74, 0x0e, 0x3a, +0xe9, 0xa8, 0xb3, 0x0e, 0x3b, 0xed, 0xb8, 0xf3, 0x0e, 0x00, +0xf1, 0xc8, 0x33, 0x0f, 0x3d, 0x21, 0xd9, 0x73, 0x4f, 0x47, +0xf9, 0xe8, 0xb3, 0x0f, 0x3f, 0x6d, 0x75, 0x95, 0x2d, +'i','s','o','8','8','5','9','4',0, +10, 33, +0x04, 0xe1, 0x64, 0x15, 0x29, 0x28, 0xed, 0x74, 0x0a, 0x2a, +0x60, 0x49, 0x24, 0x92, 0x59, 0xad, 0xf4, 0xf5, 0x0a, 0x2c, +0x05, 0x6d, 0x7b, 0x15, 0x2d, 0x29, 0xf1, 0x74, 0x2c, 0x2e, +0x61, 0x4d, 0x34, 0xd2, 0x59, 0x4a, 0xf9, 0xb5, 0x14, 0x40, +0xc1, 0x08, 0x33, 0x0c, 0x31, 0xc5, 0x18, 0xe3, 0x12, 0x43, +0xc9, 0x60, 0xb4, 0x8c, 0x45, 0xcd, 0x38, 0xa3, 0x12, 0x44, +0x45, 0x31, 0x65, 0x13, 0x35, 0xd5, 0x58, 0x73, 0x0d, 0x36, +0x72, 0x69, 0xb3, 0x0d, 0x37, 0x68, 0xa9, 0xf5, 0x4d, 0x40, +0xe1, 0x88, 0x33, 0x0e, 0x39, 0xe5, 0x98, 0xf3, 0x52, 0x43, +0xe9, 0x64, 0xb4, 0xce, 0x45, 0xed, 0xb8, 0xb3, 0x52, 0x44, +0x46, 0x35, 0x75, 0x13, 0x3d, 0xf5, 0xd8, 0x73, 0x0f, 0x3e, +0x73, 0xe9, 0xb3, 0x0f, 0x3f, 0x69, 0xad, 0x95, 0x2d, +'i','s','o','8','8','5','9','5',0, +14, 33, +0x01, 0x84, 0x00, 0x30, 0x40, 0x10, 0x10, 0x05, 0x84, 0x01, +0x70, 0x40, 0x20, 0x10, 0x09, 0x84, 0x02, 0xb0, 0x40, 0x30, +0x10, 0xad, 0x80, 0x03, 0xf0, 0x40, 0x40, 0x10, 0x11, 0x84, +0x04, 0x30, 0x41, 0x50, 0x10, 0x15, 0x84, 0x05, 0x70, 0x41, +0x60, 0x10, 0x19, 0x84, 0x06, 0xb0, 0x41, 0x70, 0x10, 0x1d, +0x84, 0x07, 0xf0, 0x41, 0x80, 0x10, 0x21, 0x84, 0x08, 0x30, +0x42, 0x90, 0x10, 0x25, 0x84, 0x09, 0x70, 0x42, 0xa0, 0x10, +0x29, 0x84, 0x0a, 0xb0, 0x42, 0xb0, 0x10, 0x2d, 0x84, 0x0b, +0xf0, 0x42, 0xc0, 0x10, 0x31, 0x84, 0x0c, 0x30, 0x43, 0xd0, +0x10, 0x35, 0x84, 0x0d, 0x70, 0x43, 0xe0, 0x10, 0x39, 0x84, +0x0e, 0xb0, 0x43, 0xf0, 0x10, 0x3d, 0x84, 0x0f, 0xf0, 0x43, +0x00, 0x11, 0x41, 0x84, 0x10, 0x30, 0x44, 0x10, 0x11, 0x45, +0x84, 0x11, 0x70, 0x44, 0x20, 0x11, 0x49, 0x84, 0x12, 0xb0, +0x44, 0x30, 0x11, 0x4d, 0x84, 0x13, 0xf0, 0x44, 0x58, 0x84, +0x51, 0x84, 0x14, 0x30, 0x45, 0x50, 0x11, 0x55, 0x84, 0x15, +0x70, 0x45, 0x60, 0x11, 0x59, 0x84, 0x16, 0xb0, 0x45, 0x70, +0x11, 0xa7, 0x80, 0x17, 0xf0, 0x45, 0x00, +'i','s','o','8','8','5','9','6',0, +11, 33, +0x00, 0x00, 0x00, 0x00, 0x48, 0x01, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xdc, 0x0a, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x86, 0x00, 0x00, 0x00, +0x00, 0x7c, 0x18, 0x00, 0x21, 0x16, 0xf1, 0x88, 0x48, 0x5c, +0x62, 0x13, 0x9c, 0x18, 0xc5, 0x29, 0x56, 0xf1, 0x8a, 0x58, +0xdc, 0x62, 0x17, 0xbc, 0x18, 0xc6, 0x31, 0x96, 0xf1, 0x8c, +0x68, 0x5c, 0x63, 0x1b, 0xdc, 0x18, 0xc7, 0x39, 0xd6, 0x31, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x41, 0x16, +0xf2, 0x90, 0x88, 0x5c, 0x64, 0x23, 0x1c, 0x19, 0xc9, 0x49, +0x56, 0xf2, 0x92, 0x98, 0xdc, 0x64, 0x27, 0x3c, 0x19, 0xca, +0x51, 0x96, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, +'i','s','o','8','8','5','9','7',0, +14, 33, +0x18, 0x60, 0x06, 0x30, 0x0a, 0xb0, 0x82, 0xaf, 0xa0, 0x29, +0x70, 0x0a, 0xa0, 0x02, 0xa9, 0x80, 0xde, 0xb0, 0x0a, 0xb0, +0x02, 0xad, 0x00, 0x00, 0x50, 0x01, 0xc0, 0x02, 0xb1, 0x80, +0x2c, 0x30, 0x0b, 0x10, 0x0e, 0x85, 0x83, 0xe1, 0x70, 0x0b, +0x20, 0x0e, 0x89, 0x83, 0xe2, 0xb0, 0x0b, 0x30, 0x0e, 0xbd, +0x80, 0xe3, 0xf0, 0x38, 0x40, 0x0e, 0x91, 0x83, 0xe4, 0x30, +0x39, 0x50, 0x0e, 0x95, 0x83, 0xe5, 0x70, 0x39, 0x60, 0x0e, +0x99, 0x83, 0xe6, 0xb0, 0x39, 0x70, 0x0e, 0x9d, 0x83, 0xe7, +0xf0, 0x39, 0x80, 0x0e, 0xa1, 0x03, 0x00, 0x30, 0x3a, 0x90, +0x0e, 0xa5, 0x83, 0xe9, 0x70, 0x3a, 0xa0, 0x0e, 0xa9, 0x83, +0xea, 0xb0, 0x3a, 0xb0, 0x0e, 0xad, 0x83, 0xeb, 0xf0, 0x3a, +0xc0, 0x0e, 0xb1, 0x83, 0xec, 0x30, 0x3b, 0xd0, 0x0e, 0xb5, +0x83, 0xed, 0x70, 0x3b, 0xe0, 0x0e, 0xb9, 0x83, 0xee, 0xb0, +0x3b, 0xf0, 0x0e, 0xbd, 0x83, 0xef, 0xf0, 0x3b, 0x00, 0x0f, +0xc1, 0x83, 0xf0, 0x30, 0x3c, 0x10, 0x0f, 0xc5, 0x83, 0xf1, +0x70, 0x3c, 0x20, 0x0f, 0xc9, 0x83, 0xf2, 0xb0, 0x3c, 0x30, +0x0f, 0xcd, 0x83, 0xf3, 0x00, 0x00, 0x00, +'i','s','o','8','8','5','9','8',0, +14, 33, +0x00, 0x80, 0x28, 0x30, 0x0a, 0x90, 0x02, 0xa5, 0x80, 0x29, +0x70, 0x0a, 0xa0, 0x02, 0xa9, 0xc0, 0x35, 0xb0, 0x0a, 0xb0, +0x02, 0xad, 0x80, 0x2b, 0xf0, 0x0a, 0xc0, 0x02, 0xb1, 0x80, +0x2c, 0x30, 0x0b, 0xd0, 0x02, 0xb5, 0x80, 0x2d, 0x70, 0x0b, +0xe0, 0x02, 0xb9, 0xc0, 0x3d, 0xb0, 0x0b, 0xf0, 0x02, 0xbd, +0x80, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x01, +0x40, 0x17, 0xd1, 0x85, 0x74, 0x30, 0x5d, 0x50, 0x17, 0xd5, +0x85, 0x75, 0x70, 0x5d, 0x60, 0x17, 0xd9, 0x85, 0x76, 0xb0, +0x5d, 0x70, 0x17, 0xdd, 0x85, 0x77, 0xf0, 0x5d, 0x80, 0x17, +0xe1, 0x85, 0x78, 0x30, 0x5e, 0x90, 0x17, 0xe5, 0x85, 0x79, +0x70, 0x5e, 0xa0, 0x17, 0xe9, 0x85, 0x7a, 0x00, 0x00, 0x00, +0x00, 0x0e, 0xe0, 0x03, 0x00, 0x00, 0x00, +'i','s','o','8','8','5','9','9',0, +9, 80, +0x1e, 0xa3, 0x49, 0x9b, 0x46, 0xad, 0x9a, 0xb5, 0x6b, 0xd8, +0xb2, 0x69, 0xdb, 0xc6, 0x0d, 0xa6, 0xd7, 0x6f, 0xe0, 0xc2, +0x89, 0x1b, 0x47, 0xae, 0x9c, 0xb9, 0x73, 0xe8, 0xd2, 0xa9, +0x5b, 0xc7, 0xae, 0x9d, 0xbb, 0x77, 0x1f, 0xe3, 0xc9, 0x9b, +0x47, 0xaf, 0x9e, 0xbd, 0x7b, 0xf8, 0xf2, 0xe9, 0xdb, 0xc7, +0x2f, 0xe6, 0xd7, 0x7f, +'i','s','o','8','8','5','9','1','0',0, +14, 33, +0x04, 0x81, 0x44, 0x20, 0x12, 0xa8, 0x04, 0x28, 0x81, 0x4d, +0x70, 0x0a, 0xec, 0x04, 0x10, 0x01, 0x58, 0x60, 0x16, 0xf4, +0x05, 0xad, 0x80, 0x5a, 0xa0, 0x14, 0xc0, 0x02, 0x05, 0xc1, +0x44, 0x30, 0x12, 0xac, 0x04, 0x29, 0xc1, 0x4d, 0x70, 0x0b, +0xf0, 0x04, 0x11, 0x41, 0x58, 0x70, 0x16, 0xf8, 0x05, 0x15, +0xe0, 0x5a, 0xb0, 0x14, 0x00, 0x04, 0xc1, 0x80, 0x30, 0x30, +0x0c, 0x10, 0x03, 0xc5, 0x80, 0x31, 0xe0, 0x12, 0x30, 0x04, +0xc9, 0x00, 0x46, 0xb0, 0x0c, 0x58, 0x04, 0xcd, 0x80, 0x33, +0xf0, 0x0c, 0x40, 0x03, 0x45, 0x01, 0x53, 0x30, 0x0d, 0x50, +0x03, 0xd5, 0x80, 0x35, 0x80, 0x16, 0x60, 0x03, 0x72, 0x81, +0x36, 0xb0, 0x0d, 0x70, 0x03, 0xdd, 0x80, 0x37, 0xf0, 0x0d, +0x04, 0x04, 0xe1, 0x80, 0x38, 0x30, 0x0e, 0x90, 0x03, 0xe5, +0x80, 0x39, 0xf0, 0x12, 0x34, 0x04, 0xe9, 0x40, 0x46, 0xb0, +0x0e, 0x5c, 0x04, 0xed, 0x80, 0x3b, 0xf0, 0x0e, 0xc0, 0x03, +0x46, 0x41, 0x53, 0x30, 0x0f, 0xd0, 0x03, 0xf5, 0x80, 0x3d, +0x90, 0x16, 0xe0, 0x03, 0x73, 0x81, 0x3e, 0xb0, 0x0f, 0xf0, +0x03, 0xfd, 0x80, 0x3f, 0x80, 0x13, 0x00, +'i','s','o','8','8','5','9','1','3',0, +14, 33, +0x1d, 0xa0, 0x28, 0x30, 0x0a, 0x90, 0x02, 0x1e, 0xa0, 0x29, +0x70, 0x0a, 0x60, 0x03, 0xa9, 0x80, 0x55, 0xb0, 0x0a, 0xb0, +0x02, 0xad, 0x80, 0x2b, 0x60, 0x0c, 0xc0, 0x02, 0xb1, 0x80, +0x2c, 0x30, 0x0b, 0x70, 0x80, 0xb5, 0x80, 0x2d, 0x70, 0x0b, +0xe0, 0x03, 0xb9, 0xc0, 0x55, 0xb0, 0x0b, 0xf0, 0x02, 0xbd, +0x80, 0x2f, 0x60, 0x0e, 0x10, 0x04, 0x2e, 0x01, 0x40, 0x60, +0x10, 0x10, 0x03, 0xc5, 0x00, 0x46, 0x20, 0x11, 0x30, 0x04, +0xc9, 0x40, 0x5e, 0x60, 0x11, 0x88, 0x04, 0x36, 0x81, 0x4a, +0xb0, 0x13, 0x80, 0x05, 0x43, 0x41, 0x51, 0x30, 0x0d, 0x30, +0x05, 0xd5, 0x80, 0x35, 0x70, 0x0d, 0xc8, 0x05, 0x41, 0x81, +0x56, 0xa0, 0x16, 0x70, 0x03, 0x7b, 0x41, 0x5f, 0xf0, 0x0d, +0x14, 0x04, 0x2f, 0x41, 0x40, 0x70, 0x10, 0x90, 0x03, 0xe5, +0x40, 0x46, 0x30, 0x11, 0x34, 0x04, 0xe9, 0x80, 0x5e, 0x70, +0x11, 0x8c, 0x04, 0x37, 0xc1, 0x4a, 0xc0, 0x13, 0x84, 0x05, +0x44, 0x81, 0x51, 0x30, 0x0f, 0x34, 0x05, 0xf5, 0x80, 0x3d, +0x70, 0x0f, 0xcc, 0x05, 0x42, 0xc1, 0x56, 0xb0, 0x16, 0xf0, +0x03, 0x7c, 0x81, 0x5f, 0x90, 0x01, 0x00, +'i','s','o','8','8','5','9','1','4',0, +13, 33, +0x02, 0x7e, 0xc0, 0x8c, 0x02, 0x85, 0xb0, 0x10, 0x14, 0xfc, +0x29, 0x00, 0xf4, 0xa9, 0x40, 0xd0, 0x2c, 0x78, 0x79, 0xd0, +0x0a, 0x5c, 0x01, 0x5e, 0xf0, 0xf0, 0x1f, 0x1e, 0x24, 0x84, +0x04, 0x20, 0x10, 0xe4, 0x6c, 0x81, 0x95, 0x08, 0xf4, 0x57, +0x7e, 0xd0, 0x80, 0xf9, 0x79, 0x40, 0xe8, 0x0a, 0x7d, 0x98, +0x00, 0x06, 0xc1, 0x40, 0x18, 0x0c, 0x03, 0x62, 0x50, 0x0c, +0x8c, 0xc1, 0x31, 0x40, 0x06, 0xc9, 0x40, 0x19, 0x2c, 0x03, +0x66, 0xd0, 0x0c, 0x9c, 0xc1, 0x33, 0xa0, 0x0b, 0xd1, 0x40, +0x1a, 0x4c, 0x03, 0x6a, 0x50, 0x0d, 0xac, 0x81, 0x9a, 0xc0, +0x06, 0xd9, 0x40, 0x1b, 0x6c, 0x03, 0x6e, 0xd0, 0x0d, 0xec, +0xc2, 0x37, 0x00, 0x07, 0xe1, 0x40, 0x1c, 0x8c, 0x03, 0x72, +0x50, 0x0e, 0xcc, 0xc1, 0x39, 0x40, 0x07, 0xe9, 0x40, 0x1d, +0xac, 0x03, 0x76, 0xd0, 0x0e, 0xdc, 0xc1, 0x3b, 0xa8, 0x0b, +0xf1, 0x40, 0x1e, 0xcc, 0x03, 0x7a, 0x50, 0x0f, 0xec, 0xc1, +0x9a, 0xc0, 0x07, 0xf9, 0x40, 0x1f, 0xec, 0x03, 0x7e, 0xd0, +0x0f, 0xee, 0xc2, 0x3f, 0x00, +'i','s','o','8','8','5','9','1','6',0, +14, 33, +0x04, 0x41, 0x41, 0x10, 0x14, 0xb0, 0x82, 0x1e, 0x20, 0x58, +0x70, 0x0a, 0x84, 0x05, 0xa9, 0x00, 0x86, 0xb0, 0x0a, 0xe4, +0x05, 0xad, 0x80, 0x5e, 0xb0, 0x17, 0xc0, 0x02, 0xb1, 0x00, +0x43, 0x20, 0x14, 0xf4, 0x05, 0x1d, 0xa0, 0x2d, 0x70, 0x0b, +0xf8, 0x05, 0x0d, 0x41, 0x86, 0xb0, 0x0b, 0x48, 0x05, 0x53, +0x01, 0x5e, 0xc0, 0x17, 0x00, 0x03, 0xc1, 0x80, 0x30, 0x20, +0x10, 0x10, 0x03, 0x06, 0x81, 0x31, 0x70, 0x0c, 0x20, 0x03, +0xc9, 0x80, 0x32, 0xb0, 0x0c, 0x30, 0x03, 0xcd, 0x80, 0x33, +0xf0, 0x0c, 0x40, 0x04, 0x43, 0x81, 0x34, 0x30, 0x0d, 0x50, +0x03, 0x50, 0x81, 0x35, 0xa0, 0x15, 0xc0, 0x05, 0xd9, 0x80, +0x36, 0xb0, 0x0d, 0x70, 0x03, 0x18, 0x81, 0x86, 0xf0, 0x0d, +0x80, 0x03, 0xe1, 0x80, 0x38, 0x30, 0x10, 0x90, 0x03, 0x07, +0x81, 0x39, 0x70, 0x0e, 0xa0, 0x03, 0xe9, 0x80, 0x3a, 0xb0, +0x0e, 0xb0, 0x03, 0xed, 0x80, 0x3b, 0xf0, 0x0e, 0x44, 0x04, +0x44, 0x81, 0x3c, 0x30, 0x0f, 0xd0, 0x03, 0x51, 0x81, 0x3d, +0xb0, 0x15, 0xc4, 0x05, 0xf9, 0x80, 0x3e, 0xb0, 0x0f, 0xf0, +0x03, 0x19, 0xc1, 0x86, 0xf0, 0x0f, 0x00, diff --git a/src/locale/tolower_l.c b/src/locale/tolower_l.c new file mode 100644 index 00000000..ba277919 --- /dev/null +++ b/src/locale/tolower_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int tolower_l(int c, locale_t l) +{ + return tolower(c); +} diff --git a/src/locale/toupper_l.c b/src/locale/toupper_l.c new file mode 100644 index 00000000..73f2f39b --- /dev/null +++ b/src/locale/toupper_l.c @@ -0,0 +1,6 @@ +#include <ctype.h> + +int toupper_l(int c, locale_t l) +{ + return toupper(c); +} diff --git a/src/locale/wcscoll.c b/src/locale/wcscoll.c new file mode 100644 index 00000000..cdbce1c2 --- /dev/null +++ b/src/locale/wcscoll.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +/* FIXME: stub */ +int wcscoll(const wchar_t *l, const wchar_t *r) +{ + return wcscmp(l, r); +} diff --git a/src/locale/wcsxfrm.c b/src/locale/wcsxfrm.c new file mode 100644 index 00000000..5f76e5a7 --- /dev/null +++ b/src/locale/wcsxfrm.c @@ -0,0 +1,12 @@ +#include <wchar.h> + +/* collate only by code points */ +size_t wcsxfrm(wchar_t *dest, const wchar_t *src, size_t n) +{ + size_t l = wcslen(src); + if (l >= n) { + wmemcpy(dest, src, n-1); + dest[n-1] = 0; + } else wcscpy(dest, src); + return l; +} diff --git a/src/malloc/DESIGN b/src/malloc/DESIGN new file mode 100644 index 00000000..58b0523f --- /dev/null +++ b/src/malloc/DESIGN @@ -0,0 +1,22 @@ + + +In principle, this memory allocator is roughly equivalent to Doug +Lea's dlmalloc with fine-grained locking. + + + +malloc: + +Uses a freelist binned by chunk size, with a bitmap to optimize +searching for the smallest non-empty bin which can satisfy an +allocation. If no free chunks are available, it creates a new chunk of +the requested size and attempts to merge it with any existing free +chunk immediately below the newly created chunk. + +Whether the chunk was obtained from a bin or newly created, it's +likely to be larger than the requested allocation. malloc always +finishes its work by passing the new chunk to realloc, which will +split it into two chunks and free the tail portion. + + + diff --git a/src/malloc/__brk.c b/src/malloc/__brk.c new file mode 100644 index 00000000..e3b3af31 --- /dev/null +++ b/src/malloc/__brk.c @@ -0,0 +1,7 @@ +#include <stdint.h> +#include "syscall.h" + +uintptr_t __brk(uintptr_t newbrk) +{ + return syscall1(__NR_brk, newbrk); +} diff --git a/src/malloc/__simple_malloc.c b/src/malloc/__simple_malloc.c new file mode 100644 index 00000000..49b74c8e --- /dev/null +++ b/src/malloc/__simple_malloc.c @@ -0,0 +1,44 @@ +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include <errno.h> +#include "libc.h" + +uintptr_t __brk(uintptr_t); + +#define ALIGN 16 + +void *__simple_malloc(size_t n) +{ + static uintptr_t cur, brk; + uintptr_t base, new; + static int lock; + size_t align=1; + + if (n < SIZE_MAX - ALIGN) + while (align<n && align<ALIGN) + align += align; + n = n + align - 1 & -align; + + LOCK(&lock); + if (!cur) cur = brk = __brk(0)+16; + if (n > SIZE_MAX - brk) goto fail; + + base = cur + align-1 & -align; + if (base+n > brk) { + new = base+n + PAGE_SIZE-1 & -PAGE_SIZE; + if (__brk(new) != new) goto fail; + brk = new; + } + cur = base+n; + UNLOCK(&lock); + + return (void *)base; + +fail: + UNLOCK(&lock); + errno = ENOMEM; + return 0; +} + +weak_alias(__simple_malloc, malloc); diff --git a/src/malloc/calloc.c b/src/malloc/calloc.c new file mode 100644 index 00000000..9d574562 --- /dev/null +++ b/src/malloc/calloc.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +#include <errno.h> +#include <string.h> + +void *calloc(size_t m, size_t n) +{ + void *p; + size_t *z; + if (n && m > (size_t)-1/n) { + errno = ENOMEM; + return 0; + } + n *= m; + p = malloc(n); + if (!p) return 0; + /* Only do this for non-mmapped chunks */ + if (((size_t *)p)[-1] & 7) { + /* Only write words that are not already zero */ + m = (n + sizeof *z - 1)/sizeof *z; + for (z=p; m; m--, z++) if (*z) *z=0; + } + return p; +} diff --git a/src/malloc/malloc.c b/src/malloc/malloc.c new file mode 100644 index 00000000..d9a30fe4 --- /dev/null +++ b/src/malloc/malloc.c @@ -0,0 +1,515 @@ +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <stdint.h> +#include <errno.h> +#include <sys/mman.h> +#include "libc.h" +#include "atomic.h" +#include "pthread_impl.h" + +uintptr_t __brk(uintptr_t); +void *__mmap(void *, size_t, int, int, int, off_t); +int __munmap(void *, size_t); +void *__mremap(void *, size_t, size_t, int, ...); +int __madvise(void *, size_t, int); + +struct chunk { + size_t data[1]; + struct chunk *next; + struct chunk *prev; +}; + +struct bin { + int lock[2]; + struct chunk *head; + struct chunk *tail; +}; + +static struct { + uintptr_t brk; + size_t *heap; + uint64_t binmap; + struct bin bins[64]; + int brk_lock[2]; + int free_lock[2]; +} mal; + + +#define SIZE_ALIGN (4*sizeof(size_t)) +#define SIZE_MASK (-SIZE_ALIGN) +#define OVERHEAD (2*sizeof(size_t)) +#define MMAP_THRESHOLD (0x1c00*SIZE_ALIGN) +#define DONTCARE 16 +#define RECLAIM 163840 + +#define CHUNK_SIZE(c) ((c)->data[0] & SIZE_MASK) +#define CHUNK_PSIZE(c) ((c)->data[-1] & SIZE_MASK) +#define PREV_CHUNK(c) ((struct chunk *)((char *)(c) - CHUNK_PSIZE(c))) +#define NEXT_CHUNK(c) ((struct chunk *)((char *)(c) + CHUNK_SIZE(c))) +#define MEM_TO_CHUNK(p) (struct chunk *)((size_t *)p - 1) +#define CHUNK_TO_MEM(c) (void *)((c)->data+1) +#define BIN_TO_CHUNK(i) (MEM_TO_CHUNK(&mal.bins[i].head)) + +#define C_INUSE ((size_t)1) +#define C_FLAGS ((size_t)3) +#define C_SIZE SIZE_MASK + +#define IS_MMAPPED(c) !((c)->data[0] & (C_INUSE)) + + +/* Synchronization tools */ + +static void lock(volatile int *lk) +{ + if (!libc.threads_minus_1) return; + while(a_swap(lk, 1)) __wait(lk, lk+1, 1, 1); +} + +static void unlock(volatile int *lk) +{ + if (!libc.threads_minus_1) return; + a_store(lk, 0); + if (lk[1]) __wake(lk, 1, 1); +} + +static void lock_bin(int i) +{ + if (libc.threads_minus_1) + lock(mal.bins[i].lock); + if (!mal.bins[i].head) + mal.bins[i].head = mal.bins[i].tail = BIN_TO_CHUNK(i); +} + +static void unlock_bin(int i) +{ + if (!libc.threads_minus_1) return; + unlock(mal.bins[i].lock); +} + +static int first_set(uint64_t x) +{ +#if 1 + return a_ctz_64(x); +#else + static const char debruijn64[64] = { + 0, 1, 2, 53, 3, 7, 54, 27, 4, 38, 41, 8, 34, 55, 48, 28, + 62, 5, 39, 46, 44, 42, 22, 9, 24, 35, 59, 56, 49, 18, 29, 11, + 63, 52, 6, 26, 37, 40, 33, 47, 61, 45, 43, 21, 23, 58, 17, 10, + 51, 25, 36, 32, 60, 20, 57, 16, 50, 31, 19, 15, 30, 14, 13, 12 + }; + static const char debruijn32[32] = { + 0, 1, 23, 2, 29, 24, 19, 3, 30, 27, 25, 11, 20, 8, 4, 13, + 31, 22, 28, 18, 26, 10, 7, 12, 21, 17, 9, 6, 16, 5, 15, 14 + }; + if (sizeof(long) < 8) { + uint32_t y = x; + if (!y) { + y = x>>32; + return 32 + debruijn32[(y&-y)*0x076be629 >> 27]; + } + return debruijn32[(y&-y)*0x076be629 >> 27]; + } + return debruijn64[(x&-x)*0x022fdd63cc95386dull >> 58]; +#endif +} + +static int bin_index(size_t x) +{ + x = x / SIZE_ALIGN - 1; + if (x <= 32) return x; + if (x > 0x1c00) return 63; + return ((union { float v; uint32_t r; }){ x }.r>>21) - 496; +} + +static int bin_index_up(size_t x) +{ + x = x / SIZE_ALIGN - 1; + if (x <= 32) return x; + return ((union { float v; uint32_t r; }){ x }.r+0x1fffff>>21) - 496; +} + +#if 0 +void __dump_heap(int x) +{ + struct chunk *c; + int i; + for (c = (void *)mal.heap; CHUNK_SIZE(c); c = NEXT_CHUNK(c)) + fprintf(stderr, "base %p size %zu (%d) flags %d/%d\n", + c, CHUNK_SIZE(c), bin_index(CHUNK_SIZE(c)), + c->data[0] & 15, + NEXT_CHUNK(c)->data[-1] & 15); + for (i=0; i<64; i++) { + if (mal.bins[i].head != BIN_TO_CHUNK(i) && mal.bins[i].head) { + fprintf(stderr, "bin %d: %p\n", i, mal.bins[i].head); + if (!(mal.binmap & 1ULL<<i)) + fprintf(stderr, "missing from binmap!\n"); + } else if (mal.binmap & 1ULL<<i) + fprintf(stderr, "binmap wrongly contains %d!\n", i); + } +} +#endif + +static struct chunk *expand_heap(size_t n) +{ + struct chunk *w; + uintptr_t new; + + lock(mal.brk_lock); + + if (n > SIZE_MAX - mal.brk - 2*PAGE_SIZE) goto fail; + new = mal.brk + n + SIZE_ALIGN + PAGE_SIZE - 1 & -PAGE_SIZE; + n = new - mal.brk; + + if (__brk(new) != new) goto fail; + + w = MEM_TO_CHUNK(new); + w->data[-1] = n | C_INUSE; + w->data[0] = 0 | C_INUSE; + + w = MEM_TO_CHUNK(mal.brk); + w->data[0] = n | C_INUSE; + mal.brk = new; + + unlock(mal.brk_lock); + + return w; +fail: + unlock(mal.brk_lock); + return 0; +} + +static int init_malloc() +{ + static int init, waiters; + int state; + struct chunk *c; + + if (init == 2) return 0; + + while ((state=a_swap(&init, 1)) == 1) + __wait(&init, &waiters, 1, 1); + if (state) { + a_store(&init, 2); + return 0; + } + + mal.brk = __brk(0) + 2*SIZE_ALIGN-1 & -SIZE_ALIGN; + + c = expand_heap(1); + + if (!c) { + a_store(&init, 0); + if (waiters) __wake(&init, 1, 1); + return -1; + } + + mal.heap = (void *)c; + c->data[-1] = 0 | C_INUSE; + free(CHUNK_TO_MEM(c)); + + a_store(&init, 2); + if (waiters) __wake(&init, -1, 1); + return 0; +} + +static int adjust_size(size_t *n) +{ + /* Result of pointer difference must fit in ptrdiff_t. */ + if (*n > PTRDIFF_MAX - SIZE_ALIGN - PAGE_SIZE) { + errno = ENOMEM; + return -1; + } + *n = (*n + OVERHEAD + SIZE_ALIGN - 1) & SIZE_MASK; + return 0; +} + +static void unbin(struct chunk *c, int i) +{ + if (c->prev == c->next) + a_and_64(&mal.binmap, ~(1ULL<<i)); + c->prev->next = c->next; + c->next->prev = c->prev; + c->data[0] |= C_INUSE; + NEXT_CHUNK(c)->data[-1] |= C_INUSE; +} + +static int alloc_fwd(struct chunk *c) +{ + int i; + size_t k; + while (!((k=c->data[0]) & C_INUSE)) { + i = bin_index(k); + lock_bin(i); + if (c->data[0] == k) { + unbin(c, i); + unlock_bin(i); + return 1; + } + unlock_bin(i); + } + return 0; +} + +static int alloc_rev(struct chunk *c) +{ + int i; + size_t k; + while (!((k=c->data[-1]) & C_INUSE)) { + i = bin_index(k); + lock_bin(i); + if (c->data[-1] == k) { + unbin(PREV_CHUNK(c), i); + unlock_bin(i); + return 1; + } + unlock_bin(i); + } + return 0; +} + + +/* pretrim - trims a chunk _prior_ to removing it from its bin. + * Must be called with i as the ideal bin for size n, j the bin + * for the _free_ chunk self, and bin j locked. */ +static int pretrim(struct chunk *self, size_t n, int i, int j) +{ + size_t n1; + struct chunk *next, *split; + + /* We cannot pretrim if it would require re-binning. */ + if (j < 40) return 0; + if (j < i+3) { + if (j != 63) return 0; + n1 = CHUNK_SIZE(self); + if (n1-n <= MMAP_THRESHOLD) return 0; + } else { + n1 = CHUNK_SIZE(self); + } + if (bin_index(n1-n) != j) return 0; + + next = NEXT_CHUNK(self); + split = (void *)((char *)self + n); + + split->prev = self->prev; + split->next = self->next; + split->prev->next = split; + split->next->prev = split; + split->data[-1] = n | C_INUSE; + split->data[0] = n1-n; + next->data[-1] = n1-n; + self->data[0] = n | C_INUSE; + return 1; +} + +static void trim(struct chunk *self, size_t n) +{ + size_t n1 = CHUNK_SIZE(self); + struct chunk *next, *split; + + if (n >= n1 - DONTCARE) return; + + next = NEXT_CHUNK(self); + split = (void *)((char *)self + n); + + split->data[-1] = n | C_INUSE; + split->data[0] = n1-n | C_INUSE; + next->data[-1] = n1-n | C_INUSE; + self->data[0] = n | C_INUSE; + + free(CHUNK_TO_MEM(split)); +} + +void *malloc(size_t n) +{ + struct chunk *c; + int i, j; + + if (!n || adjust_size(&n) < 0) return 0; + + if (n > MMAP_THRESHOLD) { + size_t len = n + PAGE_SIZE - 1 & -PAGE_SIZE; + char *base = __mmap(0, len, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + if (base == (void *)-1) return 0; + c = (void *)(base + SIZE_ALIGN - sizeof(size_t)); + c->data[0] = len - (SIZE_ALIGN - sizeof(size_t)); + c->data[-1] = SIZE_ALIGN - sizeof(size_t); + return CHUNK_TO_MEM(c); + } + + i = bin_index_up(n); + for (;;) { + uint64_t mask = mal.binmap & -(1ULL<<i); + if (!mask) { + init_malloc(); + c = expand_heap(n); + if (!c) return 0; + if (alloc_rev(c)) { + struct chunk *x = c; + c = PREV_CHUNK(c); + NEXT_CHUNK(x)->data[-1] = c->data[0] = + x->data[0] + CHUNK_SIZE(c); + } + break; + } + j = first_set(mask); + lock_bin(j); + c = mal.bins[j].head; + if (c != BIN_TO_CHUNK(j) && j == bin_index(c->data[0])) { + if (!pretrim(c, n, i, j)) unbin(c, j); + unlock_bin(j); + break; + } + unlock_bin(j); + } + + /* Now patch up in case we over-allocated */ + trim(c, n); + + return CHUNK_TO_MEM(c); +} + +void *realloc(void *p, size_t n) +{ + struct chunk *self, *next; + size_t n0, n1; + void *new; + + if (!p) return malloc(n); + else if (!n) return free(p), (void *)0; + + if (adjust_size(&n) < 0) return 0; + + self = MEM_TO_CHUNK(p); + n1 = n0 = CHUNK_SIZE(self); + + if (IS_MMAPPED(self)) { + size_t extra = self->data[-1]; + char *base = (char *)self - extra; + size_t oldlen = n0 + extra; + size_t newlen = n + extra; + if (newlen < PAGE_SIZE && (new = malloc(n))) { + memcpy(new, p, n-OVERHEAD); + free(p); + return new; + } + newlen = (newlen + PAGE_SIZE-1) & -PAGE_SIZE; + if (oldlen == newlen) return p; + base = __mremap(base, oldlen, newlen, MREMAP_MAYMOVE); + if (base == (void *)-1) + return newlen < oldlen ? p : 0; + self = (void *)(base + extra); + self->data[0] = newlen - extra; + return CHUNK_TO_MEM(self); + } + + next = NEXT_CHUNK(self); + + /* Merge adjacent chunks if we need more space. This is not + * a waste of time even if we fail to get enough space, because our + * subsequent call to free would otherwise have to do the merge. */ + if (n > n1 && alloc_fwd(next)) { + n1 += CHUNK_SIZE(next); + next = NEXT_CHUNK(next); + } + /* FIXME: find what's wrong here and reenable it..? */ + if (0 && n > n1 && alloc_rev(self)) { + self = PREV_CHUNK(self); + n1 += CHUNK_SIZE(self); + } + self->data[0] = n1 | C_INUSE; + next->data[-1] = n1 | C_INUSE; + + /* If we got enough space, split off the excess and return */ + if (n <= n1) { + //memmove(CHUNK_TO_MEM(self), p, n0-OVERHEAD); + trim(self, n); + return CHUNK_TO_MEM(self); + } + + /* As a last resort, allocate a new chunk and copy to it. */ + new = malloc(n-OVERHEAD); + if (!new) return 0; + memcpy(new, p, n0-OVERHEAD); + free(CHUNK_TO_MEM(self)); + return new; +} + +void free(void *p) +{ + struct chunk *self = MEM_TO_CHUNK(p); + struct chunk *next; + size_t final_size, new_size, size; + int reclaim=0; + int i; + + if (!p) return; + + if (IS_MMAPPED(self)) { + size_t extra = self->data[-1]; + char *base = (char *)self - extra; + size_t len = CHUNK_SIZE(self) + extra; + __munmap(base, len); + return; + } + + final_size = new_size = CHUNK_SIZE(self); + next = NEXT_CHUNK(self); + + for (;;) { + /* Replace middle of large chunks with fresh zero pages */ + if (reclaim && (self->data[-1] & next->data[0] & C_INUSE)) { + uintptr_t a = (uintptr_t)self + SIZE_ALIGN+PAGE_SIZE-1 & -PAGE_SIZE; + uintptr_t b = (uintptr_t)next - SIZE_ALIGN & -PAGE_SIZE; +#if 1 + __madvise((void *)a, b-a, MADV_DONTNEED); +#else + __mmap((void *)a, b-a, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); +#endif + } + + if (self->data[-1] & next->data[0] & C_INUSE) { + self->data[0] = final_size | C_INUSE; + next->data[-1] = final_size | C_INUSE; + i = bin_index(final_size); + lock_bin(i); + lock(mal.free_lock); + if (self->data[-1] & next->data[0] & C_INUSE) + break; + unlock(mal.free_lock); + unlock_bin(i); + } + + if (alloc_rev(self)) { + self = PREV_CHUNK(self); + size = CHUNK_SIZE(self); + final_size += size; + if (new_size+size > RECLAIM && (new_size+size^size) > size) + reclaim = 1; + } + + if (alloc_fwd(next)) { + size = CHUNK_SIZE(next); + final_size += size; + if (new_size+size > RECLAIM && (new_size+size^size) > size) + reclaim = 1; + next = NEXT_CHUNK(next); + } + } + + self->data[0] = final_size; + next->data[-1] = final_size; + unlock(mal.free_lock); + + self->next = BIN_TO_CHUNK(i); + self->prev = mal.bins[i].tail; + self->next->prev = self; + self->prev->next = self; + + if (!(mal.binmap & 1ULL<<i)) + a_or_64(&mal.binmap, 1ULL<<i); + + unlock_bin(i); +} diff --git a/src/malloc/memalign.c b/src/malloc/memalign.c new file mode 100644 index 00000000..61f456e4 --- /dev/null +++ b/src/malloc/memalign.c @@ -0,0 +1,13 @@ +#include <stdlib.h> +#include <errno.h> + +void *memalign(size_t align, size_t len) +{ + void *mem; + int ret; + if ((ret = posix_memalign(&mem, align, len))) { + errno = ret; + return 0; + } + return mem; +} diff --git a/src/malloc/posix_memalign.c b/src/malloc/posix_memalign.c new file mode 100644 index 00000000..ef86260d --- /dev/null +++ b/src/malloc/posix_memalign.c @@ -0,0 +1,47 @@ +#include <stdlib.h> +#include <stdint.h> +#include <errno.h> + +/* This function should work with most dlmalloc-like chunk bookkeeping + * systems, but it's only guaranteed to work with the native implementation + * used in this library. */ + +int posix_memalign(void **res, size_t align, size_t len) +{ + unsigned char *mem, *new, *end; + size_t header, footer; + + if ((align & -align) != align) return EINVAL; + if (len > SIZE_MAX - align) return ENOMEM; + + if (align <= 4*sizeof(size_t)) { + if (!(mem = malloc(len))) + return errno; + *res = mem; + return 0; + } + + if (!(mem = malloc(len + align-1))) + return errno; + + header = ((size_t *)mem)[-1]; + end = mem + (header & -8); + footer = ((size_t *)end)[-2]; + new = (void *)((uintptr_t)mem + align-1 & -align); + + if (!(header & 7)) { + ((size_t *)new)[-2] = ((size_t *)mem)[-2] + (new-mem); + ((size_t *)new)[-1] = ((size_t *)mem)[-1] - (new-mem); + *res = new; + return 0; + } + + ((size_t *)mem)[-1] = header&7 | new-mem; + ((size_t *)new)[-2] = footer&7 | new-mem; + ((size_t *)new)[-1] = header&7 | end-new; + ((size_t *)end)[-2] = footer&7 | end-new; + + if (new != mem) free(mem); + *res = new; + return 0; +} diff --git a/src/math/__fpclassify.c b/src/math/__fpclassify.c new file mode 100644 index 00000000..16051100 --- /dev/null +++ b/src/math/__fpclassify.c @@ -0,0 +1,14 @@ +#include <stdint.h> +#include <math.h> + +int __fpclassify(double __x) +{ + union { + double __d; + __uint64_t __i; + } __y = { __x }; + int __ee = __y.__i>>52 & 0x7ff; + if (!__ee) return __y.__i<<1 ? FP_SUBNORMAL : FP_ZERO; + if (__ee==0x7ff) return __y.__i<<12 ? FP_NAN : FP_INFINITE; + return FP_NORMAL; +} diff --git a/src/math/__fpclassifyf.c b/src/math/__fpclassifyf.c new file mode 100644 index 00000000..bf59d0d4 --- /dev/null +++ b/src/math/__fpclassifyf.c @@ -0,0 +1,14 @@ +#include <stdint.h> +#include <math.h> + +int __fpclassifyf(float __x) +{ + union { + float __f; + __uint32_t __i; + } __y = { __x }; + int __ee = __y.__i>>23 & 0xff; + if (!__ee) return __y.__i<<1 ? FP_SUBNORMAL : FP_ZERO; + if (__ee==0xff) return __y.__i<<9 ? FP_NAN : FP_INFINITE; + return FP_NORMAL; +} diff --git a/src/math/__fpclassifyl.c b/src/math/__fpclassifyl.c new file mode 100644 index 00000000..4f93bef1 --- /dev/null +++ b/src/math/__fpclassifyl.c @@ -0,0 +1,16 @@ +#include <stdint.h> +#include <math.h> + +/* FIXME: move this to arch-specific file */ +int __fpclassifyl(long double __x) +{ + union { + long double __ld; + __uint16_t __hw[5]; + __uint64_t __m; + } __y = { __x }; + int __ee = __y.__hw[4]&0x7fff; + if (!__ee) return __y.__m ? FP_SUBNORMAL : FP_ZERO; + if (__ee==0x7fff) return __y.__m ? FP_NAN : FP_INFINITE; + return FP_NORMAL; +} diff --git a/src/math/e_acos.c b/src/math/e_acos.c new file mode 100644 index 00000000..e0236391 --- /dev/null +++ b/src/math/e_acos.c @@ -0,0 +1,99 @@ +/* @(#)e_acos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* acos(x) + * Method : + * acos(x) = pi/2 - asin(x) + * acos(-x) = pi/2 + asin(x) + * For |x|<=0.5 + * acos(x) = pi/2 - (x + x*x^2*R(x^2)) (see asin.c) + * For x>0.5 + * acos(x) = pi/2 - (pi/2 - 2asin(sqrt((1-x)/2))) + * = 2asin(sqrt((1-x)/2)) + * = 2s + 2s*z*R(z) ...z=(1-x)/2, s=sqrt(z) + * = 2f + (2c + 2s*z*R(z)) + * where f=hi part of s, and c = (z-f*f)/(s+f) is the correction term + * for f so that f+c ~ sqrt(z). + * For x<-0.5 + * acos(x) = pi - 2asin(sqrt((1-|x|)/2)) + * = pi - 0.5*(s+s*z*R(z)), where z=(1-|x|)/2,s=sqrt(z) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + * Function needed: sqrt + */ + +#include <math.h> +#include "math_private.h" + +static const double +one= 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +pi = 3.14159265358979311600e+00, /* 0x400921FB, 0x54442D18 */ +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ +pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +double +acos(double x) +{ + double z,p,q,r,w,s,c,df; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x3ff00000) { /* |x| >= 1 */ + uint32_t lx; + GET_LOW_WORD(lx,x); + if(((ix-0x3ff00000)|lx)==0) { /* |x|==1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+2.0*pio2_lo; /* acos(-1)= pi */ + } + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3fe00000) { /* |x| < 0.5 */ + if(ix<=0x3c600000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ + z = x*x; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + return pio2_hi - (x - (pio2_lo-x*r)); + } else if (hx<0) { /* x < -0.5 */ + z = (one+x)*0.5; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + s = sqrt(z); + r = p/q; + w = r*s-pio2_lo; + return pi - 2.0*(s+w); + } else { /* x > 0.5 */ + z = (one-x)*0.5; + s = sqrt(z); + df = s; + SET_LOW_WORD(df,0); + c = (z-df*df)/(s+df); + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + w = r*s+c; + return 2.0*(df+w); + } +} diff --git a/src/math/e_acosf.c b/src/math/e_acosf.c new file mode 100644 index 00000000..4c59781b --- /dev/null +++ b/src/math/e_acosf.c @@ -0,0 +1,77 @@ +/* e_acosf.c -- float version of e_acos.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +pi = 3.1415925026e+00, /* 0x40490fda */ +pio2_hi = 1.5707962513e+00, /* 0x3fc90fda */ +pio2_lo = 7.5497894159e-08, /* 0x33a22168 */ +pS0 = 1.6666667163e-01, /* 0x3e2aaaab */ +pS1 = -3.2556581497e-01, /* 0xbea6b090 */ +pS2 = 2.0121252537e-01, /* 0x3e4e0aa8 */ +pS3 = -4.0055535734e-02, /* 0xbd241146 */ +pS4 = 7.9153501429e-04, /* 0x3a4f7f04 */ +pS5 = 3.4793309169e-05, /* 0x3811ef08 */ +qS1 = -2.4033949375e+00, /* 0xc019d139 */ +qS2 = 2.0209457874e+00, /* 0x4001572d */ +qS3 = -6.8828397989e-01, /* 0xbf303361 */ +qS4 = 7.7038154006e-02; /* 0x3d9dc62e */ + +float +acosf(float x) +{ + float z,p,q,r,w,s,c,df; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix==0x3f800000) { /* |x|==1 */ + if(hx>0) return 0.0; /* acos(1) = 0 */ + else return pi+(float)2.0*pio2_lo; /* acos(-1)= pi */ + } else if(ix>0x3f800000) { /* |x| >= 1 */ + return (x-x)/(x-x); /* acos(|x|>1) is NaN */ + } + if(ix<0x3f000000) { /* |x| < 0.5 */ + if(ix<=0x23000000) return pio2_hi+pio2_lo;/*if|x|<2**-57*/ + z = x*x; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + return pio2_hi - (x - (pio2_lo-x*r)); + } else if (hx<0) { /* x < -0.5 */ + z = (one+x)*(float)0.5; + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + s = sqrtf(z); + r = p/q; + w = r*s-pio2_lo; + return pi - (float)2.0*(s+w); + } else { /* x > 0.5 */ + int32_t idf; + z = (one-x)*(float)0.5; + s = sqrtf(z); + df = s; + GET_FLOAT_WORD(idf,df); + SET_FLOAT_WORD(df,idf&0xfffff000); + c = (z-df*df)/(s+df); + p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5))))); + q = one+z*(qS1+z*(qS2+z*(qS3+z*qS4))); + r = p/q; + w = r*s+c; + return (float)2.0*(df+w); + } +} diff --git a/src/math/e_acosh.c b/src/math/e_acosh.c new file mode 100644 index 00000000..8b454e75 --- /dev/null +++ b/src/math/e_acosh.c @@ -0,0 +1,59 @@ + +/* @(#)e_acosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* acosh(x) + * Method : + * Based on + * acosh(x) = log [ x + sqrt(x*x-1) ] + * we have + * acosh(x) := log(x)+ln2, if x is large; else + * acosh(x) := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else + * acosh(x) := log1p(t+sqrt(2.0*t+t*t)); where t=x-1. + * + * Special cases: + * acosh(x) is NaN with signal if x<1. + * acosh(NaN) is NaN without signal. + */ + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.0, +ln2 = 6.93147180559945286227e-01; /* 0x3FE62E42, 0xFEFA39EF */ + +double +acosh(double x) +{ + double t; + int32_t hx; + uint32_t lx; + EXTRACT_WORDS(hx,lx,x); + if(hx<0x3ff00000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x41b00000) { /* x > 2**28 */ + if(hx >=0x7ff00000) { /* x is inf of NaN */ + return x+x; + } else + return log(x)+ln2; /* acosh(huge)=log(2x) */ + } else if(((hx-0x3ff00000)|lx)==0) { + return 0.0; /* acosh(1) = 0 */ + } else if (hx > 0x40000000) { /* 2**28 > x > 2 */ + t=x*x; + return log(2.0*x-one/(x+sqrt(t-one))); + } else { /* 1<x<2 */ + t = x-one; + return log1p(t+sqrt(2.0*t+t*t)); + } +} diff --git a/src/math/e_acoshf.c b/src/math/e_acoshf.c new file mode 100644 index 00000000..b7f1df69 --- /dev/null +++ b/src/math/e_acoshf.c @@ -0,0 +1,45 @@ +/* e_acoshf.c -- float version of e_acosh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0, +ln2 = 6.9314718246e-01; /* 0x3f317218 */ + +float +acoshf(float x) +{ + float t; + int32_t hx; + GET_FLOAT_WORD(hx,x); + if(hx<0x3f800000) { /* x < 1 */ + return (x-x)/(x-x); + } else if(hx >=0x4d800000) { /* x > 2**28 */ + if(hx >=0x7f800000) { /* x is inf of NaN */ + return x+x; + } else + return logf(x)+ln2; /* acosh(huge)=log(2x) */ + } else if (hx==0x3f800000) { + return 0.0; /* acosh(1) = 0 */ + } else if (hx > 0x40000000) { /* 2**28 > x > 2 */ + t=x*x; + return logf((float)2.0*x-one/(x+sqrtf(t-one))); + } else { /* 1<x<2 */ + t = x-one; + return log1pf(t+sqrtf((float)2.0*t+t*t)); + } +} diff --git a/src/math/e_asin.c b/src/math/e_asin.c new file mode 100644 index 00000000..4bf162a1 --- /dev/null +++ b/src/math/e_asin.c @@ -0,0 +1,109 @@ + +/* @(#)e_asin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* asin(x) + * Method : + * Since asin(x) = x + x^3/6 + x^5*3/40 + x^7*15/336 + ... + * we approximate asin(x) on [0,0.5] by + * asin(x) = x + x*x^2*R(x^2) + * where + * R(x^2) is a rational approximation of (asin(x)-x)/x^3 + * and its remez error is bounded by + * |(asin(x)-x)/x^3 - R(x^2)| < 2^(-58.75) + * + * For x in [0.5,1] + * asin(x) = pi/2-2*asin(sqrt((1-x)/2)) + * Let y = (1-x), z = y/2, s := sqrt(z), and pio2_hi+pio2_lo=pi/2; + * then for x>0.98 + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio2_hi - (2*(s+s*z*R(z)) - pio2_lo) + * For x<=0.98, let pio4_hi = pio2_hi/2, then + * f = hi part of s; + * c = sqrt(z) - f = (z-f*f)/(s+f) ...f+c=sqrt(z) + * and + * asin(x) = pi/2 - 2*(s+s*z*R(z)) + * = pio4_hi+(pio4-2s)-(2s*z*R(z)-pio2_lo) + * = pio4_hi+(pio4-2f)-(2s*z*R(z)-(pio2_lo+2c)) + * + * Special cases: + * if x is NaN, return x itself; + * if |x|>1, return NaN with invalid signal. + * + */ + + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +huge = 1.000e+300, +pio2_hi = 1.57079632679489655800e+00, /* 0x3FF921FB, 0x54442D18 */ +pio2_lo = 6.12323399573676603587e-17, /* 0x3C91A626, 0x33145C07 */ +pio4_hi = 7.85398163397448278999e-01, /* 0x3FE921FB, 0x54442D18 */ + /* coefficient for R(x^2) */ +pS0 = 1.66666666666666657415e-01, /* 0x3FC55555, 0x55555555 */ +pS1 = -3.25565818622400915405e-01, /* 0xBFD4D612, 0x03EB6F7D */ +pS2 = 2.01212532134862925881e-01, /* 0x3FC9C155, 0x0E884455 */ +pS3 = -4.00555345006794114027e-02, /* 0xBFA48228, 0xB5688F3B */ +pS4 = 7.91534994289814532176e-04, /* 0x3F49EFE0, 0x7501B288 */ +pS5 = 3.47933107596021167570e-05, /* 0x3F023DE1, 0x0DFDF709 */ +qS1 = -2.40339491173441421878e+00, /* 0xC0033A27, 0x1C8A2D4B */ +qS2 = 2.02094576023350569471e+00, /* 0x40002AE5, 0x9C598AC8 */ +qS3 = -6.88283971605453293030e-01, /* 0xBFE6066C, 0x1B8D0159 */ +qS4 = 7.70381505559019352791e-02; /* 0x3FB3B8C5, 0xB12E9282 */ + +double +asin(double x) +{ + double t=0.0,w,p,q,c,r,s; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>= 0x3ff00000) { /* |x|>= 1 */ + uint32_t lx; + GET_LOW_WORD(lx,x); + if(((ix-0x3ff00000)|lx)==0) + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3fe00000) { /* |x|<0.5 */ + if(ix<0x3e400000) { /* if |x| < 2**-27 */ + if(huge+x>one) return x;/* return x with inexact if x!=0*/ + } else + t = x*x; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fabs(x); + t = w*0.5; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + s = sqrt(t); + if(ix>=0x3FEF3333) { /* if |x| > 0.975 */ + w = p/q; + t = pio2_hi-(2.0*(s+s*w)-pio2_lo); + } else { + w = s; + SET_LOW_WORD(w,0); + c = (t-w*w)/(s+w); + r = p/q; + p = 2.0*s*r-(pio2_lo-2.0*c); + q = pio4_hi-2.0*w; + t = pio4_hi-(p-q); + } + if(hx>0) return t; else return -t; +} diff --git a/src/math/e_asinf.c b/src/math/e_asinf.c new file mode 100644 index 00000000..9c693970 --- /dev/null +++ b/src/math/e_asinf.c @@ -0,0 +1,80 @@ +/* e_asinf.c -- float version of e_asin.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +huge = 1.000e+30, +pio2_hi = 1.5707962513e+00, /* 0x3fc90fda */ +pio2_lo = 7.5497894159e-08, /* 0x33a22168 */ +pio4_hi = 7.8539818525e-01, /* 0x3f490fdb */ + /* coefficient for R(x^2) */ +pS0 = 1.6666667163e-01, /* 0x3e2aaaab */ +pS1 = -3.2556581497e-01, /* 0xbea6b090 */ +pS2 = 2.0121252537e-01, /* 0x3e4e0aa8 */ +pS3 = -4.0055535734e-02, /* 0xbd241146 */ +pS4 = 7.9153501429e-04, /* 0x3a4f7f04 */ +pS5 = 3.4793309169e-05, /* 0x3811ef08 */ +qS1 = -2.4033949375e+00, /* 0xc019d139 */ +qS2 = 2.0209457874e+00, /* 0x4001572d */ +qS3 = -6.8828397989e-01, /* 0xbf303361 */ +qS4 = 7.7038154006e-02; /* 0x3d9dc62e */ + +float +asinf(float x) +{ + float t=0.0,w,p,q,c,r,s; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix==0x3f800000) { + /* asin(1)=+-pi/2 with inexact */ + return x*pio2_hi+x*pio2_lo; + } else if(ix> 0x3f800000) { /* |x|>= 1 */ + return (x-x)/(x-x); /* asin(|x|>1) is NaN */ + } else if (ix<0x3f000000) { /* |x|<0.5 */ + if(ix<0x32000000) { /* if |x| < 2**-27 */ + if(huge+x>one) return x;/* return x with inexact if x!=0*/ + } else + t = x*x; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + w = p/q; + return x+x*w; + } + /* 1> |x|>= 0.5 */ + w = one-fabsf(x); + t = w*(float)0.5; + p = t*(pS0+t*(pS1+t*(pS2+t*(pS3+t*(pS4+t*pS5))))); + q = one+t*(qS1+t*(qS2+t*(qS3+t*qS4))); + s = sqrtf(t); + if(ix>=0x3F79999A) { /* if |x| > 0.975 */ + w = p/q; + t = pio2_hi-((float)2.0*(s+s*w)-pio2_lo); + } else { + int32_t iw; + w = s; + GET_FLOAT_WORD(iw,w); + SET_FLOAT_WORD(w,iw&0xfffff000); + c = (t-w*w)/(s+w); + r = p/q; + p = (float)2.0*s*r-(pio2_lo-(float)2.0*c); + q = pio4_hi-(float)2.0*w; + t = pio4_hi-(p-q); + } + if(hx>0) return t; else return -t; +} diff --git a/src/math/e_atan2.c b/src/math/e_atan2.c new file mode 100644 index 00000000..dd021164 --- /dev/null +++ b/src/math/e_atan2.c @@ -0,0 +1,120 @@ + +/* @(#)e_atan2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* atan2(y,x) + * Method : + * 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x). + * 2. Reduce x to positive by (if x and y are unexceptional): + * ARG (x+iy) = arctan(y/x) ... if x > 0, + * ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0, + * + * Special cases: + * + * ATAN2((anything), NaN ) is NaN; + * ATAN2(NAN , (anything) ) is NaN; + * ATAN2(+-0, +(anything but NaN)) is +-0 ; + * ATAN2(+-0, -(anything but NaN)) is +-pi ; + * ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2; + * ATAN2(+-(anything but INF and NaN), +INF) is +-0 ; + * ATAN2(+-(anything but INF and NaN), -INF) is +-pi; + * ATAN2(+-INF,+INF ) is +-pi/4 ; + * ATAN2(+-INF,-INF ) is +-3pi/4; + * ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2; + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +tiny = 1.0e-300, +zero = 0.0, +pi_o_4 = 7.8539816339744827900E-01, /* 0x3FE921FB, 0x54442D18 */ +pi_o_2 = 1.5707963267948965580E+00, /* 0x3FF921FB, 0x54442D18 */ +pi = 3.1415926535897931160E+00, /* 0x400921FB, 0x54442D18 */ +pi_lo = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */ + +double +atan2(double y, double x) +{ + double z; + int32_t k,m,hx,hy,ix,iy; + uint32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + ix = hx&0x7fffffff; + EXTRACT_WORDS(hy,ly,y); + iy = hy&0x7fffffff; + if(((ix|((lx|-lx)>>31))>0x7ff00000)|| + ((iy|((ly|-ly)>>31))>0x7ff00000)) /* x or y is NaN */ + return x+y; + if(((hx-0x3ff00000)|lx)==0) return atan(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if((iy|ly)==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if((ix|lx)==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7ff00000) { + if(iy==0x7ff00000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return 3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return -3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7ff00000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>20; + if(k > 60) z=pi_o_2+0.5*pi_lo; /* |y/x| > 2**60 */ + else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */ + else z=atan(fabs(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: { + uint32_t zh; + GET_HIGH_WORD(zh,z); + SET_HIGH_WORD(z,zh ^ 0x80000000); + } + return z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} diff --git a/src/math/e_atan2f.c b/src/math/e_atan2f.c new file mode 100644 index 00000000..535e10a0 --- /dev/null +++ b/src/math/e_atan2f.c @@ -0,0 +1,93 @@ +/* e_atan2f.c -- float version of e_atan2.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +tiny = 1.0e-30, +zero = 0.0, +pi_o_4 = 7.8539818525e-01, /* 0x3f490fdb */ +pi_o_2 = 1.5707963705e+00, /* 0x3fc90fdb */ +pi = 3.1415927410e+00, /* 0x40490fdb */ +pi_lo = -8.7422776573e-08; /* 0xb3bbbd2e */ + +float +atan2f(float y, float x) +{ + float z; + int32_t k,m,hx,hy,ix,iy; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + GET_FLOAT_WORD(hy,y); + iy = hy&0x7fffffff; + if((ix>0x7f800000)|| + (iy>0x7f800000)) /* x or y is NaN */ + return x+y; + if(hx==0x3f800000) return atanf(y); /* x=1.0 */ + m = ((hy>>31)&1)|((hx>>30)&2); /* 2*sign(x)+sign(y) */ + + /* when y = 0 */ + if(iy==0) { + switch(m) { + case 0: + case 1: return y; /* atan(+-0,+anything)=+-0 */ + case 2: return pi+tiny;/* atan(+0,-anything) = pi */ + case 3: return -pi-tiny;/* atan(-0,-anything) =-pi */ + } + } + /* when x = 0 */ + if(ix==0) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* when x is INF */ + if(ix==0x7f800000) { + if(iy==0x7f800000) { + switch(m) { + case 0: return pi_o_4+tiny;/* atan(+INF,+INF) */ + case 1: return -pi_o_4-tiny;/* atan(-INF,+INF) */ + case 2: return (float)3.0*pi_o_4+tiny;/*atan(+INF,-INF)*/ + case 3: return (float)-3.0*pi_o_4-tiny;/*atan(-INF,-INF)*/ + } + } else { + switch(m) { + case 0: return zero ; /* atan(+...,+INF) */ + case 1: return -zero ; /* atan(-...,+INF) */ + case 2: return pi+tiny ; /* atan(+...,-INF) */ + case 3: return -pi-tiny ; /* atan(-...,-INF) */ + } + } + } + /* when y is INF */ + if(iy==0x7f800000) return (hy<0)? -pi_o_2-tiny: pi_o_2+tiny; + + /* compute y/x */ + k = (iy-ix)>>23; + if(k > 60) z=pi_o_2+(float)0.5*pi_lo; /* |y/x| > 2**60 */ + else if(hx<0&&k<-60) z=0.0; /* |y|/x < -2**60 */ + else z=atanf(fabsf(y/x)); /* safe to do y/x */ + switch (m) { + case 0: return z ; /* atan(+,+) */ + case 1: { + uint32_t zh; + GET_FLOAT_WORD(zh,z); + SET_FLOAT_WORD(z,zh ^ 0x80000000); + } + return z ; /* atan(-,+) */ + case 2: return pi-(z-pi_lo);/* atan(+,-) */ + default: /* case 3 */ + return (z-pi_lo)-pi;/* atan(-,-) */ + } +} diff --git a/src/math/e_atanh.c b/src/math/e_atanh.c new file mode 100644 index 00000000..45f1c966 --- /dev/null +++ b/src/math/e_atanh.c @@ -0,0 +1,59 @@ + +/* @(#)e_atanh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* atanh(x) + * Method : + * 1.Reduced x to positive by atanh(-x) = -atanh(x) + * 2.For x>=0.5 + * 1 2x x + * atanh(x) = --- * log(1 + -------) = 0.5 * log1p(2 * --------) + * 2 1 - x 1 - x + * + * For x<0.5 + * atanh(x) = 0.5*log1p(2x+2x*x/(1-x)) + * + * Special cases: + * atanh(x) is NaN if |x| > 1 with signal; + * atanh(NaN) is that NaN with no signal; + * atanh(+-1) is +-INF with signal. + * + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0, huge = 1e300; +static const double zero = 0.0; + +double +atanh(double x) +{ + double t; + int32_t hx,ix; + uint32_t lx; + EXTRACT_WORDS(hx,lx,x); + ix = hx&0x7fffffff; + if ((ix|((lx|(-lx))>>31))>0x3ff00000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3ff00000) + return x/zero; + if(ix<0x3e300000&&(huge+x)>zero) return x; /* x<2**-28 */ + SET_HIGH_WORD(x,ix); + if(ix<0x3fe00000) { /* x < 0.5 */ + t = x+x; + t = 0.5*log1p(t+t*x/(one-x)); + } else + t = 0.5*log1p((x+x)/(one-x)); + if(hx>=0) return t; else return -t; +} diff --git a/src/math/e_atanhf.c b/src/math/e_atanhf.c new file mode 100644 index 00000000..7356cfc9 --- /dev/null +++ b/src/math/e_atanhf.c @@ -0,0 +1,42 @@ +/* e_atanhf.c -- float version of e_atanh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0, huge = 1e30; + +static const float zero = 0.0; + +float +atanhf(float x) +{ + float t; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if (ix>0x3f800000) /* |x|>1 */ + return (x-x)/(x-x); + if(ix==0x3f800000) + return x/zero; + if(ix<0x31800000&&(huge+x)>zero) return x; /* x<2**-28 */ + SET_FLOAT_WORD(x,ix); + if(ix<0x3f000000) { /* x < 0.5 */ + t = x+x; + t = (float)0.5*log1pf(t+t*x/(one-x)); + } else + t = (float)0.5*log1pf((x+x)/(one-x)); + if(hx>=0) return t; else return -t; +} diff --git a/src/math/e_cosh.c b/src/math/e_cosh.c new file mode 100644 index 00000000..ad425bd3 --- /dev/null +++ b/src/math/e_cosh.c @@ -0,0 +1,82 @@ + +/* @(#)e_cosh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* cosh(x) + * Method : + * mathematically cosh(x) if defined to be (exp(x)+exp(-x))/2 + * 1. Replace x by |x| (cosh(x) = cosh(-x)). + * 2. + * [ exp(x) - 1 ]^2 + * 0 <= x <= ln2/2 : cosh(x) := 1 + ------------------- + * 2*exp(x) + * + * exp(x) + 1/exp(x) + * ln2/2 <= x <= 22 : cosh(x) := ------------------- + * 2 + * 22 <= x <= lnovft : cosh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: cosh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : cosh(x) := huge*huge (overflow) + * + * Special cases: + * cosh(x) is |x| if x is +INF, -INF, or NaN. + * only cosh(0)=1 is exact for finite x. + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0, half=0.5, huge = 1.0e300; + +double +cosh(double x) +{ + double t,w; + int32_t ix; + uint32_t lx; + + /* High word of |x|. */ + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3fd62e43) { + t = expm1(fabs(x)); + w = one+t; + if (ix<0x3c800000) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ix < 0x40360000) { + t = exp(fabs(x)); + return half*t+half/t; + } + + /* |x| in [22, log(maxdouble)] return half*exp(|x|) */ + if (ix < 0x40862E42) return half*exp(fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + GET_LOW_WORD(lx,x); + if (ix<0x408633CE || + ((ix==0x408633ce)&&(lx<=(uint32_t)0x8fb9f87d))) { + w = exp(half*fabs(x)); + t = half*w; + return t*w; + } + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge*huge; +} diff --git a/src/math/e_coshf.c b/src/math/e_coshf.c new file mode 100644 index 00000000..6db10885 --- /dev/null +++ b/src/math/e_coshf.c @@ -0,0 +1,59 @@ +/* e_coshf.c -- float version of e_cosh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0, half=0.5, huge = 1.0e30; + +float +coshf(float x) +{ + float t,w; + int32_t ix; + + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) return x*x; + + /* |x| in [0,0.5*ln2], return 1+expm1(|x|)^2/(2*exp(|x|)) */ + if(ix<0x3eb17218) { + t = expm1f(fabsf(x)); + w = one+t; + if (ix<0x24000000) return w; /* cosh(tiny) = 1 */ + return one+(t*t)/(w+w); + } + + /* |x| in [0.5*ln2,22], return (exp(|x|)+1/exp(|x|)/2; */ + if (ix < 0x41b00000) { + t = expf(fabsf(x)); + return half*t+half/t; + } + + /* |x| in [22, log(maxdouble)] return half*exp(|x|) */ + if (ix < 0x42b17180) return half*expf(fabsf(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + if (ix<=0x42b2d4fc) { + w = expf(half*fabsf(x)); + t = half*w; + return t*w; + } + + /* |x| > overflowthresold, cosh(x) overflow */ + return huge*huge; +} diff --git a/src/math/e_exp.c b/src/math/e_exp.c new file mode 100644 index 00000000..66107b95 --- /dev/null +++ b/src/math/e_exp.c @@ -0,0 +1,155 @@ + +/* @(#)e_exp.c 1.6 04/04/22 */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* exp(x) + * Returns the exponential of x. + * + * Method + * 1. Argument reduction: + * Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658. + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2. + * + * Here r will be represented as r = hi-lo for better + * accuracy. + * + * 2. Approximation of exp(r) by a special rational function on + * the interval [0,0.34658]: + * Write + * R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ... + * We use a special Remes algorithm on [0,0.34658] to generate + * a polynomial of degree 5 to approximate R. The maximum error + * of this polynomial approximation is bounded by 2**-59. In + * other words, + * R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5 + * (where z=r*r, and the values of P1 to P5 are listed below) + * and + * | 5 | -59 + * | 2.0+P1*z+...+P5*z - R(z) | <= 2 + * | | + * The computation of exp(r) thus becomes + * 2*r + * exp(r) = 1 + ------- + * R - r + * r*R1(r) + * = 1 + r + ----------- (for better accuracy) + * 2 - R1(r) + * where + * 2 4 10 + * R1(r) = r - (P1*r + P2*r + ... + P5*r ). + * + * 3. Scale back to obtain exp(x): + * From step 1, we have + * exp(x) = 2^k * exp(r) + * + * Special cases: + * exp(INF) is INF, exp(NaN) is NaN; + * exp(-INF) is 0, and + * for finite argument, only exp(0)=1 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then exp(x) overflow + * if x < -7.45133219101941108420e+02 then exp(x) underflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.0, +halF[2] = {0.5,-0.5,}, +huge = 1.0e+300, +twom1000= 9.33263618503218878990e-302, /* 2**-1000=0x01700000,0*/ +o_threshold= 7.09782712893383973096e+02, /* 0x40862E42, 0xFEFA39EF */ +u_threshold= -7.45133219101941108420e+02, /* 0xc0874910, 0xD52D3051 */ +ln2HI[2] ={ 6.93147180369123816490e-01, /* 0x3fe62e42, 0xfee00000 */ + -6.93147180369123816490e-01,},/* 0xbfe62e42, 0xfee00000 */ +ln2LO[2] ={ 1.90821492927058770002e-10, /* 0x3dea39ef, 0x35793c76 */ + -1.90821492927058770002e-10,},/* 0xbdea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00, /* 0x3ff71547, 0x652b82fe */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08; /* 0x3E663769, 0x72BEA4D0 */ + + +double +exp(double x) /* default IEEE double exp */ +{ + double y,hi=0.0,lo=0.0,c,t; + int32_t k=0,xsb; + uint32_t hx; + + GET_HIGH_WORD(hx,x); + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + uint32_t lx; + GET_LOW_WORD(lx,x); + if(((hx&0xfffff)|lx)!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + } + if(x > o_threshold) return huge*huge; /* overflow */ + if(x < u_threshold) return twom1000*twom1000; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = (int)(invln2*x+halF[xsb]); + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + x = hi - lo; + } + else if(hx < 0x3e300000) { /* when |x|<2**-28 */ + if(huge+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + c = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + if(k==0) return one-((x*c)/(c-2.0)-x); + else y = one-((lo-(x*c)/(2.0-c))-hi); + if(k >= -1021) { + uint32_t hy; + GET_HIGH_WORD(hy,y); + SET_HIGH_WORD(y,hy+(k<<20)); /* add k to y's exponent */ + return y; + } else { + uint32_t hy; + GET_HIGH_WORD(hy,y); + SET_HIGH_WORD(y,hy+((k+1000)<<20)); /* add k to y's exponent */ + return y*twom1000; + } +} diff --git a/src/math/e_expf.c b/src/math/e_expf.c new file mode 100644 index 00000000..99818edc --- /dev/null +++ b/src/math/e_expf.c @@ -0,0 +1,91 @@ +/* e_expf.c -- float version of e_exp.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0, +halF[2] = {0.5,-0.5,}, +huge = 1.0e+30, +twom100 = 7.8886090522e-31, /* 2**-100=0x0d800000 */ +o_threshold= 8.8721679688e+01, /* 0x42b17180 */ +u_threshold= -1.0397208405e+02, /* 0xc2cff1b5 */ +ln2HI[2] ={ 6.9313812256e-01, /* 0x3f317180 */ + -6.9313812256e-01,}, /* 0xbf317180 */ +ln2LO[2] ={ 9.0580006145e-06, /* 0x3717f7d1 */ + -9.0580006145e-06,}, /* 0xb717f7d1 */ +invln2 = 1.4426950216e+00, /* 0x3fb8aa3b */ +P1 = 1.6666667163e-01, /* 0x3e2aaaab */ +P2 = -2.7777778450e-03, /* 0xbb360b61 */ +P3 = 6.6137559770e-05, /* 0x388ab355 */ +P4 = -1.6533901999e-06, /* 0xb5ddea0e */ +P5 = 4.1381369442e-08; /* 0x3331bb4c */ + +float +expf(float x) /* default IEEE double exp */ +{ + float y,hi=0.0,lo=0.0,c,t; + int32_t k=0,xsb; + uint32_t hx; + + GET_FLOAT_WORD(hx,x); + xsb = (hx>>31)&1; /* sign bit of x */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out non-finite argument */ + if(hx >= 0x42b17218) { /* if |x|>=88.721... */ + if(hx>0x7f800000) + return x+x; /* NaN */ + if(hx==0x7f800000) + return (xsb==0)? x:0.0; /* exp(+-inf)={inf,0} */ + if(x > o_threshold) return huge*huge; /* overflow */ + if(x < u_threshold) return twom100*twom100; /* underflow */ + } + + /* argument reduction */ + if(hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3F851592) { /* and |x| < 1.5 ln2 */ + hi = x-ln2HI[xsb]; lo=ln2LO[xsb]; k = 1-xsb-xsb; + } else { + k = invln2*x+halF[xsb]; + t = k; + hi = x - t*ln2HI[0]; /* t*ln2HI is exact here */ + lo = t*ln2LO[0]; + } + x = hi - lo; + } + else if(hx < 0x31800000) { /* when |x|<2**-28 */ + if(huge+x>one) return one+x;/* trigger inexact */ + } + else k = 0; + + /* x is now in primary range */ + t = x*x; + c = x - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + if(k==0) return one-((x*c)/(c-(float)2.0)-x); + else y = one-((lo-(x*c)/((float)2.0-c))-hi); + if(k >= -125) { + uint32_t hy; + GET_FLOAT_WORD(hy,y); + SET_FLOAT_WORD(y,hy+(k<<23)); /* add k to y's exponent */ + return y; + } else { + uint32_t hy; + GET_FLOAT_WORD(hy,y); + SET_FLOAT_WORD(y,hy+((k+100)<<23)); /* add k to y's exponent */ + return y*twom100; + } +} diff --git a/src/math/e_fmod.c b/src/math/e_fmod.c new file mode 100644 index 00000000..99afe489 --- /dev/null +++ b/src/math/e_fmod.c @@ -0,0 +1,129 @@ + +/* @(#)e_fmod.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fmod(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0, Zero[] = {0.0, -0.0,}; + +double +fmod(double x, double y) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + uint32_t lx,ly,lz; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7ff00000)|| /* y=0,or x not finite */ + ((hy|((ly|-ly)>>31))>0x7ff00000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx<hy)||(lx<ly)) return x; /* |x|<|y| return x */ + if(lx==ly) + return Zero[(uint32_t)sx>>31]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x00100000) { /* subnormal x */ + if(hx==0) { + for (ix = -1043, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1; + } + } else ix = (hx>>20)-1023; + + /* determine iy = ilogb(y) */ + if(hy<0x00100000) { /* subnormal y */ + if(hy==0) { + for (iy = -1043, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1; + } + } else iy = (hy>>20)-1023; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -1022) + hx = 0x00100000|(0x000fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -1022-ix; + if(n<=31) { + hx = (hx<<n)|(lx>>(32-n)); + lx <<= n; + } else { + hx = lx<<(n-32); + lx = 0; + } + } + if(iy >= -1022) + hy = 0x00100000|(0x000fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -1022-iy; + if(n<=31) { + hy = (hy<<n)|(ly>>(32-n)); + ly <<= n; + } else { + hy = ly<<(n-32); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1; + if(hz<0){hx = hx+hx+(lx>>31); lx = lx+lx;} + else { + if((hz|lz)==0) /* return sign(x)*0 */ + return Zero[(uint32_t)sx>>31]; + hx = hz+hz+(lz>>31); lx = lz+lz; + } + } + hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1; + if(hz>=0) {hx=hz;lx=lz;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) /* return sign(x)*0 */ + return Zero[(uint32_t)sx>>31]; + while(hx<0x00100000) { /* normalize x */ + hx = hx+hx+(lx>>31); lx = lx+lx; + iy -= 1; + } + if(iy>= -1022) { /* normalize output */ + hx = ((hx-0x00100000)|((iy+1023)<<20)); + INSERT_WORDS(x,hx|sx,lx); + } else { /* subnormal output */ + n = -1022 - iy; + if(n<=20) { + lx = (lx>>n)|((uint32_t)hx<<(32-n)); + hx >>= n; + } else if (n<=31) { + lx = (hx<<(32-n))|(lx>>n); hx = sx; + } else { + lx = hx>>(n-32); hx = sx; + } + INSERT_WORDS(x,hx|sx,lx); + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/src/math/e_fmodf.c b/src/math/e_fmodf.c new file mode 100644 index 00000000..fe86cb04 --- /dev/null +++ b/src/math/e_fmodf.c @@ -0,0 +1,101 @@ +/* e_fmodf.c -- float version of e_fmod.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fmodf(x,y) + * Return x mod y in exact arithmetic + * Method: shift and subtract + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0, Zero[] = {0.0, -0.0,}; + +float +fmodf(float x, float y) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if(hy==0||(hx>=0x7f800000)|| /* y=0,or x not finite */ + (hy>0x7f800000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<hy) return x; /* |x|<|y| return x */ + if(hx==hy) + return Zero[(uint32_t)sx>>31]; /* |x|=|y| return x*0*/ + + /* determine ix = ilogb(x) */ + if(hx<0x00800000) { /* subnormal x */ + for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; + } else ix = (hx>>23)-127; + + /* determine iy = ilogb(y) */ + if(hy<0x00800000) { /* subnormal y */ + for (iy = -126,i=(hy<<8); i>=0; i<<=1) iy -=1; + } else iy = (hy>>23)-127; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -126) + hx = 0x00800000|(0x007fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -126-ix; + hx = hx<<n; + } + if(iy >= -126) + hy = 0x00800000|(0x007fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -126-iy; + hy = hy<<n; + } + + /* fix point fmod */ + n = ix - iy; + while(n--) { + hz=hx-hy; + if(hz<0){hx = hx+hx;} + else { + if(hz==0) /* return sign(x)*0 */ + return Zero[(uint32_t)sx>>31]; + hx = hz+hz; + } + } + hz=hx-hy; + if(hz>=0) {hx=hz;} + + /* convert back to floating value and restore the sign */ + if(hx==0) /* return sign(x)*0 */ + return Zero[(uint32_t)sx>>31]; + while(hx<0x00800000) { /* normalize x */ + hx = hx+hx; + iy -= 1; + } + if(iy>= -126) { /* normalize output */ + hx = ((hx-0x00800000)|((iy+127)<<23)); + SET_FLOAT_WORD(x,hx|sx); + } else { /* subnormal output */ + n = -126 - iy; + hx >>= n; + SET_FLOAT_WORD(x,hx|sx); + x *= one; /* create necessary signal */ + } + return x; /* exact output */ +} diff --git a/src/math/e_hypot.c b/src/math/e_hypot.c new file mode 100644 index 00000000..e925adc3 --- /dev/null +++ b/src/math/e_hypot.c @@ -0,0 +1,121 @@ + +/* @(#)e_hypot.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* hypot(x,y) + * + * Method : + * If (assume round-to-nearest) z=x*x+y*y + * has error less than sqrt(2)/2 ulp, than + * sqrt(z) has error less than 1 ulp (exercise). + * + * So, compute sqrt(x*x+y*y) with some care as + * follows to get the error below 1 ulp: + * + * Assume x>y>0; + * (if possible, set rounding to round-to-nearest) + * 1. if x > 2y use + * x1*x1+(y*y+(x2*(x+x1))) for x*x+y*y + * where x1 = x with lower 32 bits cleared, x2 = x-x1; else + * 2. if x <= 2y use + * t1*y1+((x-y)*(x-y)+(t1*y2+t2*y)) + * where t1 = 2x with lower 32 bits cleared, t2 = 2x-t1, + * y1= y with lower 32 bits chopped, y2 = y-y1. + * + * NOTE: scaling may be necessary if some argument is too + * large or too tiny + * + * Special cases: + * hypot(x,y) is INF if x or y is +INF or -INF; else + * hypot(x,y) is NAN if x or y is NAN. + * + * Accuracy: + * hypot(x,y) returns sqrt(x^2+y^2) with error less + * than 1 ulps (units in the last place) + */ + +#include <math.h> +#include "math_private.h" + +double +hypot(double x, double y) +{ + double a=x,b=y,t1,t2,y1,y2,w; + int32_t j,k,ha,hb; + + GET_HIGH_WORD(ha,x); + ha &= 0x7fffffff; + GET_HIGH_WORD(hb,y); + hb &= 0x7fffffff; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + SET_HIGH_WORD(a,ha); /* a <- |a| */ + SET_HIGH_WORD(b,hb); /* b <- |b| */ + if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ + k=0; + if(ha > 0x5f300000) { /* a>2**500 */ + if(ha >= 0x7ff00000) { /* Inf or NaN */ + uint32_t low; + w = a+b; /* for sNaN */ + GET_LOW_WORD(low,a); + if(((ha&0xfffff)|low)==0) w = a; + GET_LOW_WORD(low,b); + if(((hb^0x7ff00000)|low)==0) w = b; + return w; + } + /* scale a and b by 2**-600 */ + ha -= 0x25800000; hb -= 0x25800000; k += 600; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + if(hb < 0x20b00000) { /* b < 2**-500 */ + if(hb <= 0x000fffff) { /* subnormal b or 0 */ + uint32_t low; + GET_LOW_WORD(low,b); + if((hb|low)==0) return a; + t1=0; + SET_HIGH_WORD(t1,0x7fd00000); /* t1=2^1022 */ + b *= t1; + a *= t1; + k -= 1022; + } else { /* scale a and b by 2^600 */ + ha += 0x25800000; /* a *= 2^600 */ + hb += 0x25800000; /* b *= 2^600 */ + k -= 600; + SET_HIGH_WORD(a,ha); + SET_HIGH_WORD(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + t1 = 0; + SET_HIGH_WORD(t1,ha); + t2 = a-t1; + w = sqrt(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + y1 = 0; + SET_HIGH_WORD(y1,hb); + y2 = b - y1; + t1 = 0; + SET_HIGH_WORD(t1,ha+0x00100000); + t2 = a - t1; + w = sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + uint32_t high; + t1 = 1.0; + GET_HIGH_WORD(high,t1); + SET_HIGH_WORD(t1,high+(k<<20)); + return t1*w; + } else return w; +} diff --git a/src/math/e_hypotf.c b/src/math/e_hypotf.c new file mode 100644 index 00000000..13773554 --- /dev/null +++ b/src/math/e_hypotf.c @@ -0,0 +1,79 @@ +/* e_hypotf.c -- float version of e_hypot.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +hypotf(float x, float y) +{ + float a=x,b=y,t1,t2,y1,y2,w; + int32_t j,k,ha,hb; + + GET_FLOAT_WORD(ha,x); + ha &= 0x7fffffff; + GET_FLOAT_WORD(hb,y); + hb &= 0x7fffffff; + if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} + SET_FLOAT_WORD(a,ha); /* a <- |a| */ + SET_FLOAT_WORD(b,hb); /* b <- |b| */ + if((ha-hb)>0xf000000) {return a+b;} /* x/y > 2**30 */ + k=0; + if(ha > 0x58800000) { /* a>2**50 */ + if(ha >= 0x7f800000) { /* Inf or NaN */ + w = a+b; /* for sNaN */ + if(ha == 0x7f800000) w = a; + if(hb == 0x7f800000) w = b; + return w; + } + /* scale a and b by 2**-68 */ + ha -= 0x22000000; hb -= 0x22000000; k += 68; + SET_FLOAT_WORD(a,ha); + SET_FLOAT_WORD(b,hb); + } + if(hb < 0x26800000) { /* b < 2**-50 */ + if(hb <= 0x007fffff) { /* subnormal b or 0 */ + if(hb==0) return a; + SET_FLOAT_WORD(t1,0x7e800000); /* t1=2^126 */ + b *= t1; + a *= t1; + k -= 126; + } else { /* scale a and b by 2^68 */ + ha += 0x22000000; /* a *= 2^68 */ + hb += 0x22000000; /* b *= 2^68 */ + k -= 68; + SET_FLOAT_WORD(a,ha); + SET_FLOAT_WORD(b,hb); + } + } + /* medium size a and b */ + w = a-b; + if (w>b) { + SET_FLOAT_WORD(t1,ha&0xfffff000); + t2 = a-t1; + w = sqrtf(t1*t1-(b*(-b)-t2*(a+t1))); + } else { + a = a+a; + SET_FLOAT_WORD(y1,hb&0xfffff000); + y2 = b - y1; + SET_FLOAT_WORD(t1,ha+0x00800000); + t2 = a - t1; + w = sqrtf(t1*y1-(w*(-w)-(t1*y2+t2*b))); + } + if(k!=0) { + SET_FLOAT_WORD(t1,0x3f800000+(k<<23)); + return t1*w; + } else return w; +} diff --git a/src/math/e_log.c b/src/math/e_log.c new file mode 100644 index 00000000..9eb0e444 --- /dev/null +++ b/src/math/e_log.c @@ -0,0 +1,131 @@ + +/* @(#)e_log.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* log(x) + * Return the logrithm of x + * + * Method : + * 1. Argument Reduction: find k and f such that + * x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * 2. Approximation of log(1+f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s + * (the values of Lg1 to Lg7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lg1*s +...+Lg7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log(1+f) = f - s*(f - R) (if f is not too large) + * log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy) + * + * 3. Finally, log(x) = k*ln2 + log(1+f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log(x) is NaN with signal if x < 0 (including -INF) ; + * log(+INF) is +INF; log(0) is -INF with signal; + * log(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lg1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lg2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lg3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lg4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lg5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lg6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static const double zero = 0.0; + +double +log(double x) +{ + double hfsq,f,s,z,R,w,t1,t2,dk; + int32_t k,hx,i,j; + uint32_t lx; + + EXTRACT_WORDS(hx,lx,x); + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx,x); + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + hx &= 0x000fffff; + i = (hx+0x95f64)&0x100000; + SET_HIGH_WORD(x,hx|(i^0x3ff00000)); /* normalize x or x/2 */ + k += (i>>20); + f = x-1.0; + if((0x000fffff&(2+hx))<3) { /* |f| < 2**-20 */ + if(f==zero) { if(k==0) return zero; else {dk=(double)k; + return dk*ln2_hi+dk*ln2_lo;} } + R = f*f*(0.5-0.33333333333333333*f); + if(k==0) return f-R; else {dk=(double)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/(2.0+f); + dk = (double)k; + z = s*s; + i = hx-0x6147a; + w = z*z; + j = 0x6b851-hx; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} diff --git a/src/math/e_log10.c b/src/math/e_log10.c new file mode 100644 index 00000000..3be179f7 --- /dev/null +++ b/src/math/e_log10.c @@ -0,0 +1,83 @@ + +/* @(#)e_log10.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* log10(x) + * Return the base 10 logarithm of x + * + * Method : + * Let log10_2hi = leading 40 bits of log10(2) and + * log10_2lo = log10(2) - log10_2hi, + * ivln10 = 1/log(10) rounded. + * Then + * n = ilogb(x), + * if(n<0) n = n+1; + * x = scalbn(x,-n); + * log10(x) := n*log10_2hi + (n*log10_2lo + ivln10*log(x)) + * + * Note 1: + * To guarantee log10(10**n)=n, where 10**n is normal, the rounding + * mode must set to Round-to-Nearest. + * Note 2: + * [1/log(10)] rounded to 53 bits has error .198 ulps; + * log10 is monotonic at all binary break points. + * + * Special cases: + * log10(x) is NaN with signal if x < 0; + * log10(+INF) is +INF with no signal; log10(0) is -INF with signal; + * log10(NaN) is that NaN with no signal; + * log10(10**N) = N for N=0,1,...,22. + * + * Constants: + * The hexadecimal values are the intended ones for the following constants. + * The decimal values may be used, provided that the compiler will convert + * from decimal to binary accurately enough to produce the hexadecimal values + * shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +ivln10 = 4.34294481903251816668e-01, /* 0x3FDBCB7B, 0x1526E50E */ +log10_2hi = 3.01029995663611771306e-01, /* 0x3FD34413, 0x509F6000 */ +log10_2lo = 3.69423907715893078616e-13; /* 0x3D59FEF3, 0x11F12B36 */ + +static const double zero = 0.0; + +double +log10(double x) +{ + double y,z; + int32_t i,k,hx; + uint32_t lx; + + EXTRACT_WORDS(hx,lx,x); + + k=0; + if (hx < 0x00100000) { /* x < 2**-1022 */ + if (((hx&0x7fffffff)|lx)==0) + return -two54/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 54; x *= two54; /* subnormal number, scale up x */ + GET_HIGH_WORD(hx,x); + } + if (hx >= 0x7ff00000) return x+x; + k += (hx>>20)-1023; + i = ((uint32_t)k&0x80000000)>>31; + hx = (hx&0x000fffff)|((0x3ff-i)<<20); + y = (double)(k+i); + SET_HIGH_WORD(x,hx); + z = y*log10_2lo + ivln10*log(x); + return z+y*log10_2hi; +} diff --git a/src/math/e_log10f.c b/src/math/e_log10f.c new file mode 100644 index 00000000..8fc5c5ca --- /dev/null +++ b/src/math/e_log10f.c @@ -0,0 +1,51 @@ +/* e_log10f.c -- float version of e_log10.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +two25 = 3.3554432000e+07, /* 0x4c000000 */ +ivln10 = 4.3429449201e-01, /* 0x3ede5bd9 */ +log10_2hi = 3.0102920532e-01, /* 0x3e9a2080 */ +log10_2lo = 7.9034151668e-07; /* 0x355427db */ + +static const float zero = 0.0; + +float +log10f(float x) +{ + float y,z; + int32_t i,k,hx; + + GET_FLOAT_WORD(hx,x); + + k=0; + if (hx < 0x00800000) { /* x < 2**-126 */ + if ((hx&0x7fffffff)==0) + return -two25/zero; /* log(+-0)=-inf */ + if (hx<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 25; x *= two25; /* subnormal number, scale up x */ + GET_FLOAT_WORD(hx,x); + } + if (hx >= 0x7f800000) return x+x; + k += (hx>>23)-127; + i = ((uint32_t)k&0x80000000)>>31; + hx = (hx&0x007fffff)|((0x7f-i)<<23); + y = (float)(k+i); + SET_FLOAT_WORD(x,hx); + z = y*log10_2lo + ivln10*logf(x); + return z+y*log10_2hi; +} diff --git a/src/math/e_logf.c b/src/math/e_logf.c new file mode 100644 index 00000000..46a8b8ce --- /dev/null +++ b/src/math/e_logf.c @@ -0,0 +1,81 @@ +/* e_logf.c -- float version of e_log.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ +ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ +two25 = 3.355443200e+07, /* 0x4c000000 */ +Lg1 = 6.6666668653e-01, /* 3F2AAAAB */ +Lg2 = 4.0000000596e-01, /* 3ECCCCCD */ +Lg3 = 2.8571429849e-01, /* 3E924925 */ +Lg4 = 2.2222198546e-01, /* 3E638E29 */ +Lg5 = 1.8183572590e-01, /* 3E3A3325 */ +Lg6 = 1.5313838422e-01, /* 3E1CD04F */ +Lg7 = 1.4798198640e-01; /* 3E178897 */ + +static const float zero = 0.0; + +float +logf(float x) +{ + float hfsq,f,s,z,R,w,t1,t2,dk; + int32_t k,ix,i,j; + + GET_FLOAT_WORD(ix,x); + + k=0; + if (ix < 0x00800000) { /* x < 2**-126 */ + if ((ix&0x7fffffff)==0) + return -two25/zero; /* log(+-0)=-inf */ + if (ix<0) return (x-x)/zero; /* log(-#) = NaN */ + k -= 25; x *= two25; /* subnormal number, scale up x */ + GET_FLOAT_WORD(ix,x); + } + if (ix >= 0x7f800000) return x+x; + k += (ix>>23)-127; + ix &= 0x007fffff; + i = (ix+(0x95f64<<3))&0x800000; + SET_FLOAT_WORD(x,ix|(i^0x3f800000)); /* normalize x or x/2 */ + k += (i>>23); + f = x-(float)1.0; + if((0x007fffff&(15+ix))<16) { /* |f| < 2**-20 */ + if(f==zero) { if(k==0) return zero; else {dk=(float)k; + return dk*ln2_hi+dk*ln2_lo;} } + R = f*f*((float)0.5-(float)0.33333333333333333*f); + if(k==0) return f-R; else {dk=(float)k; + return dk*ln2_hi-((R-dk*ln2_lo)-f);} + } + s = f/((float)2.0+f); + dk = (float)k; + z = s*s; + i = ix-(0x6147a<<3); + w = z*z; + j = (0x6b851<<3)-ix; + t1= w*(Lg2+w*(Lg4+w*Lg6)); + t2= z*(Lg1+w*(Lg3+w*(Lg5+w*Lg7))); + i |= j; + R = t2+t1; + if(i>0) { + hfsq=(float)0.5*f*f; + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return dk*ln2_hi-((hfsq-(s*(hfsq+R)+dk*ln2_lo))-f); + } else { + if(k==0) return f-s*(f-R); else + return dk*ln2_hi-((s*(f-R)-dk*ln2_lo)-f); + } +} diff --git a/src/math/e_pow.c b/src/math/e_pow.c new file mode 100644 index 00000000..aad24287 --- /dev/null +++ b/src/math/e_pow.c @@ -0,0 +1,300 @@ +/* @(#)e_pow.c 1.5 04/04/22 SMI */ +/* + * ==================================================== + * Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* pow(x,y) return x**y + * + * n + * Method: Let x = 2 * (1+f) + * 1. Compute and return log2(x) in two pieces: + * log2(x) = w1 + w2, + * where w1 has 53-24 = 29 bit trailing zeros. + * 2. Perform y*log2(x) = n+y' by simulating muti-precision + * arithmetic, where |y'|<=0.5. + * 3. Return x**y = 2**n*exp(y'*log2) + * + * Special cases: + * 1. (anything) ** 0 is 1 + * 2. (anything) ** 1 is itself + * 3. (anything) ** NAN is NAN + * 4. NAN ** (anything except 0) is NAN + * 5. +-(|x| > 1) ** +INF is +INF + * 6. +-(|x| > 1) ** -INF is +0 + * 7. +-(|x| < 1) ** +INF is +0 + * 8. +-(|x| < 1) ** -INF is +INF + * 9. +-1 ** +-INF is NAN + * 10. +0 ** (+anything except 0, NAN) is +0 + * 11. -0 ** (+anything except 0, NAN, odd integer) is +0 + * 12. +0 ** (-anything except 0, NAN) is +INF + * 13. -0 ** (-anything except 0, NAN, odd integer) is +INF + * 14. -0 ** (odd integer) = -( +0 ** (odd integer) ) + * 15. +INF ** (+anything except 0,NAN) is +INF + * 16. +INF ** (-anything except 0,NAN) is +0 + * 17. -INF ** (anything) = -0 ** (-anything) + * 18. (-anything) ** (integer) is (-1)**(integer)*(+anything**integer) + * 19. (-anything except 0 and inf) ** (non-integer) is NAN + * + * Accuracy: + * pow(x,y) returns x**y nearly rounded. In particular + * pow(integer,integer) + * always returns the correct integer provided it is + * representable. + * + * Constants : + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84962487220764160156e-01,}, /* 0x3FE2B803, 0x40000000 */ +dp_l[] = { 0.0, 1.35003920212974897128e-08,}, /* 0x3E4CFDEB, 0x43CFD006 */ +zero = 0.0, +one = 1.0, +two = 2.0, +two53 = 9007199254740992.0, /* 0x43400000, 0x00000000 */ +huge = 1.0e300, +tiny = 1.0e-300, + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 5.99999999999994648725e-01, /* 0x3FE33333, 0x33333303 */ +L2 = 4.28571428578550184252e-01, /* 0x3FDB6DB6, 0xDB6FABFF */ +L3 = 3.33333329818377432918e-01, /* 0x3FD55555, 0x518F264D */ +L4 = 2.72728123808534006489e-01, /* 0x3FD17460, 0xA91D4101 */ +L5 = 2.30660745775561754067e-01, /* 0x3FCD864A, 0x93C9DB65 */ +L6 = 2.06975017800338417784e-01, /* 0x3FCA7E28, 0x4A454EEF */ +P1 = 1.66666666666666019037e-01, /* 0x3FC55555, 0x5555553E */ +P2 = -2.77777777770155933842e-03, /* 0xBF66C16C, 0x16BEBD93 */ +P3 = 6.61375632143793436117e-05, /* 0x3F11566A, 0xAF25DE2C */ +P4 = -1.65339022054652515390e-06, /* 0xBEBBBD41, 0xC5D26BF1 */ +P5 = 4.13813679705723846039e-08, /* 0x3E663769, 0x72BEA4D0 */ +lg2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +lg2_h = 6.93147182464599609375e-01, /* 0x3FE62E43, 0x00000000 */ +lg2_l = -1.90465429995776804525e-09, /* 0xBE205C61, 0x0CA86C39 */ +ovt = 8.0085662595372944372e-0017, /* -(1024-log2(ovfl+.5ulp)) */ +cp = 9.61796693925975554329e-01, /* 0x3FEEC709, 0xDC3A03FD =2/(3ln2) */ +cp_h = 9.61796700954437255859e-01, /* 0x3FEEC709, 0xE0000000 =(float)cp */ +cp_l = -7.02846165095275826516e-09, /* 0xBE3E2FE0, 0x145B01F5 =tail of cp_h*/ +ivln2 = 1.44269504088896338700e+00, /* 0x3FF71547, 0x652B82FE =1/ln2 */ +ivln2_h = 1.44269502162933349609e+00, /* 0x3FF71547, 0x60000000 =24b 1/ln2*/ +ivln2_l = 1.92596299112661746887e-08; /* 0x3E54AE0B, 0xF85DDF44 =1/ln2 tail*/ + +double +pow(double x, double y) +{ + double z,ax,z_h,z_l,p_h,p_l; + double y1,t1,t2,r,s,t,u,v,w; + int32_t i,j,k,yisint,n; + int32_t hx,hy,ix,iy; + uint32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if((iy|ly)==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7ff00000 || ((ix==0x7ff00000)&&(lx!=0)) || + iy > 0x7ff00000 || ((iy==0x7ff00000)&&(ly!=0))) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x43400000) yisint = 2; /* even integer y */ + else if(iy>=0x3ff00000) { + k = (iy>>20)-0x3ff; /* exponent */ + if(k>20) { + j = ly>>(52-k); + if((j<<(52-k))==ly) yisint = 2-(j&1); + } else if(ly==0) { + j = iy>>(20-k); + if((j<<(20-k))==iy) yisint = 2-(j&1); + } + } + } + + /* special value of y */ + if(ly==0) { + if (iy==0x7ff00000) { /* y is +-inf */ + if(((ix-0x3ff00000)|lx)==0) + return y - y; /* inf**+-1 is NaN */ + else if (ix >= 0x3ff00000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3ff00000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3fe00000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return sqrt(x); + } + } + + ax = fabs(x); + /* special value of x */ + if(lx==0) { + if(ix==0x7ff00000||ix==0||ix==0x3ff00000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3ff00000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + } + + /* CYGNUS LOCAL + fdlibm-5.3 fix: This used to be + n = (hx>>31)+1; + but ANSI C says a right shift of a signed negative quantity is + implementation defined. */ + n = ((uint32_t)hx>>31)-1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + s = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) s = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x41e00000) { /* if |y| > 2**31 */ + if(iy>0x43f00000){ /* if |y| > 2**64, must o/uflow */ + if(ix<=0x3fefffff) return (hy<0)? huge*huge:tiny*tiny; + if(ix>=0x3ff00000) return (hy>0)? huge*huge:tiny*tiny; + } + /* over/underflow if x is not close to one */ + if(ix<0x3fefffff) return (hy<0)? s*huge*huge:s*tiny*tiny; + if(ix>0x3ff00000) return (hy>0)? s*huge*huge:s*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-one; /* t has 20 trailing zeros */ + w = (t*t)*(0.5-t*(0.3333333333333333333333-t*0.25)); + u = ivln2_h*t; /* ivln2_h has 21 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + SET_LOW_WORD(t1,0); + t2 = v-(t1-u); + } else { + double ss,s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00100000) + {ax *= two53; n -= 53; GET_HIGH_WORD(ix,ax); } + n += ((ix)>>20)-0x3ff; + j = ix&0x000fffff; + /* determine interval */ + ix = j|0x3ff00000; /* normalize ix */ + if(j<=0x3988E) k=0; /* |x|<sqrt(3/2) */ + else if(j<0xBB67A) k=1; /* |x|<sqrt(3) */ + else {k=0;n+=1;ix -= 0x00100000;} + SET_HIGH_WORD(ax,ix); + + /* compute ss = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */ + u = ax-bp[k]; /* bp[0]=1.0, bp[1]=1.5 */ + v = one/(ax+bp[k]); + ss = u*v; + s_h = ss; + SET_LOW_WORD(s_h,0); + /* t_h=ax+bp[k] High */ + t_h = zero; + SET_HIGH_WORD(t_h,((ix>>1)|0x20000000)+0x00080000+(k<<18)); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = ss*ss; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+ss); + s2 = s_h*s_h; + t_h = 3.0+s2+r; + SET_LOW_WORD(t_h,0); + t_l = r-((t_h-3.0)-s2); + /* u+v = ss*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*ss; + /* 2/(3log2)*(ss+...) */ + p_h = u+v; + SET_LOW_WORD(p_h,0); + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (ss+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (double)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + SET_LOW_WORD(t1,0); + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + y1 = y; + SET_LOW_WORD(y1,0); + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + EXTRACT_WORDS(j,i,z); + if (j>=0x40900000) { /* z >= 1024 */ + if(((j-0x40900000)|i)!=0) /* if z > 1024 */ + return s*huge*huge; /* overflow */ + else { + if(p_l+ovt>z-p_h) return s*huge*huge; /* overflow */ + } + } else if((j&0x7fffffff)>=0x4090cc00 ) { /* z <= -1075 */ + if(((j-0xc090cc00)|i)!=0) /* z < -1075 */ + return s*tiny*tiny; /* underflow */ + else { + if(p_l<=z-p_h) return s*tiny*tiny; /* underflow */ + } + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>20)-0x3ff; + n = 0; + if(i>0x3fe00000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00100000>>(k+1)); + k = ((n&0x7fffffff)>>20)-0x3ff; /* new k for n */ + t = zero; + SET_HIGH_WORD(t,n&~(0x000fffff>>k)); + n = ((n&0x000fffff)|0x00100000)>>(20-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + SET_LOW_WORD(t,0); + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + GET_HIGH_WORD(j,z); + j += (n<<20); + if((j>>20)<=0) z = scalbn(z,n); /* subnormal output */ + else SET_HIGH_WORD(z,j); + return s*z; +} diff --git a/src/math/e_powf.c b/src/math/e_powf.c new file mode 100644 index 00000000..ae61c246 --- /dev/null +++ b/src/math/e_powf.c @@ -0,0 +1,243 @@ +/* e_powf.c -- float version of e_pow.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +bp[] = {1.0, 1.5,}, +dp_h[] = { 0.0, 5.84960938e-01,}, /* 0x3f15c000 */ +dp_l[] = { 0.0, 1.56322085e-06,}, /* 0x35d1cfdc */ +zero = 0.0, +one = 1.0, +two = 2.0, +two24 = 16777216.0, /* 0x4b800000 */ +huge = 1.0e30, +tiny = 1.0e-30, + /* poly coefs for (3/2)*(log(x)-2s-2/3*s**3 */ +L1 = 6.0000002384e-01, /* 0x3f19999a */ +L2 = 4.2857143283e-01, /* 0x3edb6db7 */ +L3 = 3.3333334327e-01, /* 0x3eaaaaab */ +L4 = 2.7272811532e-01, /* 0x3e8ba305 */ +L5 = 2.3066075146e-01, /* 0x3e6c3255 */ +L6 = 2.0697501302e-01, /* 0x3e53f142 */ +P1 = 1.6666667163e-01, /* 0x3e2aaaab */ +P2 = -2.7777778450e-03, /* 0xbb360b61 */ +P3 = 6.6137559770e-05, /* 0x388ab355 */ +P4 = -1.6533901999e-06, /* 0xb5ddea0e */ +P5 = 4.1381369442e-08, /* 0x3331bb4c */ +lg2 = 6.9314718246e-01, /* 0x3f317218 */ +lg2_h = 6.93145752e-01, /* 0x3f317200 */ +lg2_l = 1.42860654e-06, /* 0x35bfbe8c */ +ovt = 4.2995665694e-08, /* -(128-log2(ovfl+.5ulp)) */ +cp = 9.6179670095e-01, /* 0x3f76384f =2/(3ln2) */ +cp_h = 9.6179199219e-01, /* 0x3f763800 =head of cp */ +cp_l = 4.7017383622e-06, /* 0x369dc3a0 =tail of cp_h */ +ivln2 = 1.4426950216e+00, /* 0x3fb8aa3b =1/ln2 */ +ivln2_h = 1.4426879883e+00, /* 0x3fb8aa00 =16b 1/ln2*/ +ivln2_l = 7.0526075433e-06; /* 0x36eca570 =1/ln2 tail*/ + +float +powf(float x, float y) +{ + float z,ax,z_h,z_l,p_h,p_l; + float y1,t1,t2,r,s,sn,t,u,v,w; + int32_t i,j,k,yisint,n; + int32_t hx,hy,ix,iy,is; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + ix = hx&0x7fffffff; iy = hy&0x7fffffff; + + /* y==zero: x**0 = 1 */ + if(iy==0) return one; + + /* +-NaN return x+y */ + if(ix > 0x7f800000 || + iy > 0x7f800000) + return x+y; + + /* determine if y is an odd int when x < 0 + * yisint = 0 ... y is not an integer + * yisint = 1 ... y is an odd int + * yisint = 2 ... y is an even int + */ + yisint = 0; + if(hx<0) { + if(iy>=0x4b800000) yisint = 2; /* even integer y */ + else if(iy>=0x3f800000) { + k = (iy>>23)-0x7f; /* exponent */ + j = iy>>(23-k); + if((j<<(23-k))==iy) yisint = 2-(j&1); + } + } + + /* special value of y */ + if (iy==0x7f800000) { /* y is +-inf */ + if (ix==0x3f800000) + return y - y; /* inf**+-1 is NaN */ + else if (ix > 0x3f800000)/* (|x|>1)**+-inf = inf,0 */ + return (hy>=0)? y: zero; + else /* (|x|<1)**-,+inf = inf,0 */ + return (hy<0)?-y: zero; + } + if(iy==0x3f800000) { /* y is +-1 */ + if(hy<0) return one/x; else return x; + } + if(hy==0x40000000) return x*x; /* y is 2 */ + if(hy==0x3f000000) { /* y is 0.5 */ + if(hx>=0) /* x >= +0 */ + return sqrtf(x); + } + + ax = fabsf(x); + /* special value of x */ + if(ix==0x7f800000||ix==0||ix==0x3f800000){ + z = ax; /*x is +-0,+-inf,+-1*/ + if(hy<0) z = one/z; /* z = (1/|x|) */ + if(hx<0) { + if(((ix-0x3f800000)|yisint)==0) { + z = (z-z)/(z-z); /* (-1)**non-int is NaN */ + } else if(yisint==1) + z = -z; /* (x<0)**odd = -(|x|**odd) */ + } + return z; + } + + n = ((uint32_t)hx>>31)-1; + + /* (x<0)**(non-int) is NaN */ + if((n|yisint)==0) return (x-x)/(x-x); + + sn = one; /* s (sign of result -ve**odd) = -1 else = 1 */ + if((n|(yisint-1))==0) sn = -one;/* (-ve)**(odd int) */ + + /* |y| is huge */ + if(iy>0x4d000000) { /* if |y| > 2**27 */ + /* over/underflow if x is not close to one */ + if(ix<0x3f7ffff8) return (hy<0)? sn*huge*huge:sn*tiny*tiny; + if(ix>0x3f800007) return (hy>0)? sn*huge*huge:sn*tiny*tiny; + /* now |1-x| is tiny <= 2**-20, suffice to compute + log(x) by x-x^2/2+x^3/3-x^4/4 */ + t = ax-1; /* t has 20 trailing zeros */ + w = (t*t)*((float)0.5-t*((float)0.333333333333-t*(float)0.25)); + u = ivln2_h*t; /* ivln2_h has 16 sig. bits */ + v = t*ivln2_l-w*ivln2; + t1 = u+v; + GET_FLOAT_WORD(is,t1); + SET_FLOAT_WORD(t1,is&0xfffff000); + t2 = v-(t1-u); + } else { + float s2,s_h,s_l,t_h,t_l; + n = 0; + /* take care subnormal number */ + if(ix<0x00800000) + {ax *= two24; n -= 24; GET_FLOAT_WORD(ix,ax); } + n += ((ix)>>23)-0x7f; + j = ix&0x007fffff; + /* determine interval */ + ix = j|0x3f800000; /* normalize ix */ + if(j<=0x1cc471) k=0; /* |x|<sqrt(3/2) */ + else if(j<0x5db3d7) k=1; /* |x|<sqrt(3) */ + else {k=0;n+=1;ix -= 0x00800000;} + SET_FLOAT_WORD(ax,ix); + + /* compute s = s_h+s_l = (x-1)/(x+1) or (x-1.5)/(x+1.5) */ + u = ax-bp[k]; /* bp[0]=1.0, bp[1]=1.5 */ + v = one/(ax+bp[k]); + s = u*v; + s_h = s; + GET_FLOAT_WORD(is,s_h); + SET_FLOAT_WORD(s_h,is&0xfffff000); + /* t_h=ax+bp[k] High */ + is = ((ix>>1)&0xfffff000)|0x20000000; + SET_FLOAT_WORD(t_h,is+0x00400000+(k<<21)); + t_l = ax - (t_h-bp[k]); + s_l = v*((u-s_h*t_h)-s_h*t_l); + /* compute log(ax) */ + s2 = s*s; + r = s2*s2*(L1+s2*(L2+s2*(L3+s2*(L4+s2*(L5+s2*L6))))); + r += s_l*(s_h+s); + s2 = s_h*s_h; + t_h = (float)3.0+s2+r; + GET_FLOAT_WORD(is,t_h); + SET_FLOAT_WORD(t_h,is&0xfffff000); + t_l = r-((t_h-(float)3.0)-s2); + /* u+v = s*(1+...) */ + u = s_h*t_h; + v = s_l*t_h+t_l*s; + /* 2/(3log2)*(s+...) */ + p_h = u+v; + GET_FLOAT_WORD(is,p_h); + SET_FLOAT_WORD(p_h,is&0xfffff000); + p_l = v-(p_h-u); + z_h = cp_h*p_h; /* cp_h+cp_l = 2/(3*log2) */ + z_l = cp_l*p_h+p_l*cp+dp_l[k]; + /* log2(ax) = (s+..)*2/(3*log2) = n + dp_h + z_h + z_l */ + t = (float)n; + t1 = (((z_h+z_l)+dp_h[k])+t); + GET_FLOAT_WORD(is,t1); + SET_FLOAT_WORD(t1,is&0xfffff000); + t2 = z_l-(((t1-t)-dp_h[k])-z_h); + } + + /* split up y into y1+y2 and compute (y1+y2)*(t1+t2) */ + GET_FLOAT_WORD(is,y); + SET_FLOAT_WORD(y1,is&0xfffff000); + p_l = (y-y1)*t1+y*t2; + p_h = y1*t1; + z = p_l+p_h; + GET_FLOAT_WORD(j,z); + if (j>0x43000000) /* if z > 128 */ + return sn*huge*huge; /* overflow */ + else if (j==0x43000000) { /* if z == 128 */ + if(p_l+ovt>z-p_h) return sn*huge*huge; /* overflow */ + } + else if ((j&0x7fffffff)>0x43160000) /* z <= -150 */ + return sn*tiny*tiny; /* underflow */ + else if (j==0xc3160000){ /* z == -150 */ + if(p_l<=z-p_h) return sn*tiny*tiny; /* underflow */ + } + /* + * compute 2**(p_h+p_l) + */ + i = j&0x7fffffff; + k = (i>>23)-0x7f; + n = 0; + if(i>0x3f000000) { /* if |z| > 0.5, set n = [z+0.5] */ + n = j+(0x00800000>>(k+1)); + k = ((n&0x7fffffff)>>23)-0x7f; /* new k for n */ + SET_FLOAT_WORD(t,n&~(0x007fffff>>k)); + n = ((n&0x007fffff)|0x00800000)>>(23-k); + if(j<0) n = -n; + p_h -= t; + } + t = p_l+p_h; + GET_FLOAT_WORD(is,t); + SET_FLOAT_WORD(t,is&0xffff8000); + u = t*lg2_h; + v = (p_l-(t-p_h))*lg2+t*lg2_l; + z = u+v; + w = v-(z-u); + t = z*z; + t1 = z - t*(P1+t*(P2+t*(P3+t*(P4+t*P5)))); + r = (z*t1)/(t1-two)-(w+z*w); + z = one-(r-z); + GET_FLOAT_WORD(j,z); + j += (n<<23); + if((j>>23)<=0) z = scalbnf(z,n); /* subnormal output */ + else SET_FLOAT_WORD(z,j); + return sn*z; +} diff --git a/src/math/e_rem_pio2.c b/src/math/e_rem_pio2.c new file mode 100644 index 00000000..9eee36ae --- /dev/null +++ b/src/math/e_rem_pio2.c @@ -0,0 +1,163 @@ + +/* @(#)e_rem_pio2.c 1.4 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + * + */ + +/* __ieee754_rem_pio2(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2() + */ + +#include <math.h> +#include "math_private.h" + +/* + * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi + */ +static const int32_t two_over_pi[] = { +0xA2F983, 0x6E4E44, 0x1529FC, 0x2757D1, 0xF534DD, 0xC0DB62, +0x95993C, 0x439041, 0xFE5163, 0xABDEBB, 0xC561B7, 0x246E3A, +0x424DD2, 0xE00649, 0x2EEA09, 0xD1921C, 0xFE1DEB, 0x1CB129, +0xA73EE8, 0x8235F5, 0x2EBB44, 0x84E99C, 0x7026B4, 0x5F7E41, +0x3991D6, 0x398353, 0x39F49C, 0x845F8B, 0xBDF928, 0x3B1FF8, +0x97FFDE, 0x05980F, 0xEF2F11, 0x8B5A0A, 0x6D1F6D, 0x367ECF, +0x27CB09, 0xB74F46, 0x3F669E, 0x5FEA2D, 0x7527BA, 0xC7EBE5, +0xF17B3D, 0x0739F7, 0x8A5292, 0xEA6BFB, 0x5FB11F, 0x8D5D08, +0x560330, 0x46FC7B, 0x6BABF0, 0xCFBC20, 0x9AF436, 0x1DA9E3, +0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880, +0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, +}; + +static const int32_t npio2_hw[] = { +0x3FF921FB, 0x400921FB, 0x4012D97C, 0x401921FB, 0x401F6A7A, 0x4022D97C, +0x4025FDBB, 0x402921FB, 0x402C463A, 0x402F6A7A, 0x4031475C, 0x4032D97C, +0x40346B9C, 0x4035FDBB, 0x40378FDB, 0x403921FB, 0x403AB41B, 0x403C463A, +0x403DD85A, 0x403F6A7A, 0x40407E4C, 0x4041475C, 0x4042106C, 0x4042D97C, +0x4043A28C, 0x40446B9C, 0x404534AC, 0x4045FDBB, 0x4046C6CB, 0x40478FDB, +0x404858EB, 0x404921FB, +}; + +/* + * invpio2: 53 bits of 2/pi + * pio2_1: first 33 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 33 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 33 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const double +zero = 0.00000000000000000000e+00, /* 0x00000000, 0x00000000 */ +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +invpio2 = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ +pio2_1 = 1.57079632673412561417e+00, /* 0x3FF921FB, 0x54400000 */ +pio2_1t = 6.07710050650619224932e-11, /* 0x3DD0B461, 0x1A626331 */ +pio2_2 = 6.07710050630396597660e-11, /* 0x3DD0B461, 0x1A600000 */ +pio2_2t = 2.02226624879595063154e-21, /* 0x3BA3198A, 0x2E037073 */ +pio2_3 = 2.02226624871116645580e-21, /* 0x3BA3198A, 0x2E000000 */ +pio2_3t = 8.47842766036889956997e-32; /* 0x397B839A, 0x252049C1 */ + +int32_t __ieee754_rem_pio2(double x, double *y) +{ + double z,w,t,r,fn; + double tx[3]; + int32_t e0,i,j,nx,n,ix,hx; + uint32_t low; + + GET_HIGH_WORD(hx,x); /* high word of x */ + ix = hx&0x7fffffff; + if(ix<=0x3fe921fb) /* |x| ~<= pi/4 , no need for reduction */ + {y[0] = x; y[1] = 0; return 0;} + if(ix<0x4002d97c) { /* |x| < 3pi/4, special case with n=+-1 */ + if(hx>0) { + z = x - pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z - pio2_1t; + y[1] = (z-y[0])-pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z -= pio2_2; + y[0] = z - pio2_2t; + y[1] = (z-y[0])-pio2_2t; + } + return 1; + } else { /* negative x */ + z = x + pio2_1; + if(ix!=0x3ff921fb) { /* 33+53 bit pi is good enough */ + y[0] = z + pio2_1t; + y[1] = (z-y[0])+pio2_1t; + } else { /* near pi/2, use 33+33+53 bit pi */ + z += pio2_2; + y[0] = z + pio2_2t; + y[1] = (z-y[0])+pio2_2t; + } + return -1; + } + } + if(ix<=0x413921fb) { /* |x| ~<= 2^19*(pi/2), medium size */ + t = fabs(x); + n = (int32_t) (t*invpio2+half); + fn = (double)n; + r = t-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 85 bit */ + if(n<32&&ix!=npio2_hw[n-1]) { + y[0] = r-w; /* quick check no cancellation */ + } else { + uint32_t high; + j = ix>>20; + y[0] = r-w; + GET_HIGH_WORD(high,y[0]); + i = j-((high>>20)&0x7ff); + if(i>16) { /* 2nd iteration needed, good to 118 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + GET_HIGH_WORD(high,y[0]); + i = j-((high>>20)&0x7ff); + if(i>49) { /* 3rd iteration need, 151 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + else return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7ff00000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-23) */ + GET_LOW_WORD(low,x); + e0 = (ix>>20)-1046; /* e0 = ilogb(z)-23; */ + INSERT_WORDS(z, ix - ((int32_t)(e0<<20)), low); + for(i=0;i<2;i++) { + tx[i] = (double)((int32_t)(z)); + z = (z-tx[i])*two24; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2(tx,y,e0,nx,2,two_over_pi); + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + return n; +} diff --git a/src/math/e_rem_pio2f.c b/src/math/e_rem_pio2f.c new file mode 100644 index 00000000..4992ea0c --- /dev/null +++ b/src/math/e_rem_pio2f.c @@ -0,0 +1,175 @@ +/* e_rem_pio2f.c -- float version of e_rem_pio2.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __ieee754_rem_pio2f(x,y) + * + * return the remainder of x rem pi/2 in y[0]+y[1] + * use __kernel_rem_pio2f() + */ + +#include <math.h> +#include "math_private.h" + +/* + * Table of constants for 2/pi, 396 Hex digits (476 decimal) of 2/pi + */ +static const int32_t two_over_pi[] = { +0xA2, 0xF9, 0x83, 0x6E, 0x4E, 0x44, 0x15, 0x29, 0xFC, +0x27, 0x57, 0xD1, 0xF5, 0x34, 0xDD, 0xC0, 0xDB, 0x62, +0x95, 0x99, 0x3C, 0x43, 0x90, 0x41, 0xFE, 0x51, 0x63, +0xAB, 0xDE, 0xBB, 0xC5, 0x61, 0xB7, 0x24, 0x6E, 0x3A, +0x42, 0x4D, 0xD2, 0xE0, 0x06, 0x49, 0x2E, 0xEA, 0x09, +0xD1, 0x92, 0x1C, 0xFE, 0x1D, 0xEB, 0x1C, 0xB1, 0x29, +0xA7, 0x3E, 0xE8, 0x82, 0x35, 0xF5, 0x2E, 0xBB, 0x44, +0x84, 0xE9, 0x9C, 0x70, 0x26, 0xB4, 0x5F, 0x7E, 0x41, +0x39, 0x91, 0xD6, 0x39, 0x83, 0x53, 0x39, 0xF4, 0x9C, +0x84, 0x5F, 0x8B, 0xBD, 0xF9, 0x28, 0x3B, 0x1F, 0xF8, +0x97, 0xFF, 0xDE, 0x05, 0x98, 0x0F, 0xEF, 0x2F, 0x11, +0x8B, 0x5A, 0x0A, 0x6D, 0x1F, 0x6D, 0x36, 0x7E, 0xCF, +0x27, 0xCB, 0x09, 0xB7, 0x4F, 0x46, 0x3F, 0x66, 0x9E, +0x5F, 0xEA, 0x2D, 0x75, 0x27, 0xBA, 0xC7, 0xEB, 0xE5, +0xF1, 0x7B, 0x3D, 0x07, 0x39, 0xF7, 0x8A, 0x52, 0x92, +0xEA, 0x6B, 0xFB, 0x5F, 0xB1, 0x1F, 0x8D, 0x5D, 0x08, +0x56, 0x03, 0x30, 0x46, 0xFC, 0x7B, 0x6B, 0xAB, 0xF0, +0xCF, 0xBC, 0x20, 0x9A, 0xF4, 0x36, 0x1D, 0xA9, 0xE3, +0x91, 0x61, 0x5E, 0xE6, 0x1B, 0x08, 0x65, 0x99, 0x85, +0x5F, 0x14, 0xA0, 0x68, 0x40, 0x8D, 0xFF, 0xD8, 0x80, +0x4D, 0x73, 0x27, 0x31, 0x06, 0x06, 0x15, 0x56, 0xCA, +0x73, 0xA8, 0xC9, 0x60, 0xE2, 0x7B, 0xC0, 0x8C, 0x6B, +}; + +/* This array is like the one in e_rem_pio2.c, but the numbers are + single precision and the last 8 bits are forced to 0. */ +static const int32_t npio2_hw[] = { +0x3fc90f00, 0x40490f00, 0x4096cb00, 0x40c90f00, 0x40fb5300, 0x4116cb00, +0x412fed00, 0x41490f00, 0x41623100, 0x417b5300, 0x418a3a00, 0x4196cb00, +0x41a35c00, 0x41afed00, 0x41bc7e00, 0x41c90f00, 0x41d5a000, 0x41e23100, +0x41eec200, 0x41fb5300, 0x4203f200, 0x420a3a00, 0x42108300, 0x4216cb00, +0x421d1400, 0x42235c00, 0x4229a500, 0x422fed00, 0x42363600, 0x423c7e00, +0x4242c700, 0x42490f00 +}; + +/* + * invpio2: 24 bits of 2/pi + * pio2_1: first 17 bit of pi/2 + * pio2_1t: pi/2 - pio2_1 + * pio2_2: second 17 bit of pi/2 + * pio2_2t: pi/2 - (pio2_1+pio2_2) + * pio2_3: third 17 bit of pi/2 + * pio2_3t: pi/2 - (pio2_1+pio2_2+pio2_3) + */ + +static const float +zero = 0.0000000000e+00, /* 0x00000000 */ +half = 5.0000000000e-01, /* 0x3f000000 */ +two8 = 2.5600000000e+02, /* 0x43800000 */ +invpio2 = 6.3661980629e-01, /* 0x3f22f984 */ +pio2_1 = 1.5707855225e+00, /* 0x3fc90f80 */ +pio2_1t = 1.0804334124e-05, /* 0x37354443 */ +pio2_2 = 1.0804273188e-05, /* 0x37354400 */ +pio2_2t = 6.0770999344e-11, /* 0x2e85a308 */ +pio2_3 = 6.0770943833e-11, /* 0x2e85a300 */ +pio2_3t = 6.1232342629e-17; /* 0x248d3132 */ + +int32_t __ieee754_rem_pio2f(float x, float *y) +{ + float z,w,t,r,fn; + float tx[3]; + int32_t e0,i,j,nx,n,ix,hx; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix<=0x3f490fd8) /* |x| ~<= pi/4 , no need for reduction */ + {y[0] = x; y[1] = 0; return 0;} + if(ix<0x4016cbe4) { /* |x| < 3pi/4, special case with n=+-1 */ + if(hx>0) { + z = x - pio2_1; + if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ + y[0] = z - pio2_1t; + y[1] = (z-y[0])-pio2_1t; + } else { /* near pi/2, use 24+24+24 bit pi */ + z -= pio2_2; + y[0] = z - pio2_2t; + y[1] = (z-y[0])-pio2_2t; + } + return 1; + } else { /* negative x */ + z = x + pio2_1; + if((ix&0xfffffff0)!=0x3fc90fd0) { /* 24+24 bit pi OK */ + y[0] = z + pio2_1t; + y[1] = (z-y[0])+pio2_1t; + } else { /* near pi/2, use 24+24+24 bit pi */ + z += pio2_2; + y[0] = z + pio2_2t; + y[1] = (z-y[0])+pio2_2t; + } + return -1; + } + } + if(ix<=0x43490f80) { /* |x| ~<= 2^7*(pi/2), medium size */ + t = fabsf(x); + n = (int32_t) (t*invpio2+half); + fn = (float)n; + r = t-fn*pio2_1; + w = fn*pio2_1t; /* 1st round good to 40 bit */ + if(n<32&&(ix&0xffffff00)!=npio2_hw[n-1]) { + y[0] = r-w; /* quick check no cancellation */ + } else { + uint32_t high; + j = ix>>23; + y[0] = r-w; + GET_FLOAT_WORD(high,y[0]); + i = j-((high>>23)&0xff); + if(i>8) { /* 2nd iteration needed, good to 57 */ + t = r; + w = fn*pio2_2; + r = t-w; + w = fn*pio2_2t-((t-r)-w); + y[0] = r-w; + GET_FLOAT_WORD(high,y[0]); + i = j-((high>>23)&0xff); + if(i>25) { /* 3rd iteration need, 74 bits acc */ + t = r; /* will cover all possible cases */ + w = fn*pio2_3; + r = t-w; + w = fn*pio2_3t-((t-r)-w); + y[0] = r-w; + } + } + } + y[1] = (r-y[0])-w; + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + else return n; + } + /* + * all other (large) arguments + */ + if(ix>=0x7f800000) { /* x is inf or NaN */ + y[0]=y[1]=x-x; return 0; + } + /* set z = scalbn(|x|,ilogb(x)-7) */ + e0 = (ix>>23)-134; /* e0 = ilogb(z)-7; */ + SET_FLOAT_WORD(z, ix - ((int32_t)(e0<<23))); + for(i=0;i<2;i++) { + tx[i] = (float)((int32_t)(z)); + z = (z-tx[i])*two8; + } + tx[2] = z; + nx = 3; + while(tx[nx-1]==zero) nx--; /* skip zero term */ + n = __kernel_rem_pio2f(tx,y,e0,nx,2,two_over_pi); + if(hx<0) {y[0] = -y[0]; y[1] = -y[1]; return -n;} + return n; +} diff --git a/src/math/e_remainder.c b/src/math/e_remainder.c new file mode 100644 index 00000000..9cb56919 --- /dev/null +++ b/src/math/e_remainder.c @@ -0,0 +1,69 @@ + +/* @(#)e_remainder.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* remainder(x,p) + * Return : + * returns x REM p = x - [x/p]*p as if in infinite + * precise arithmetic, where [x/p] is the (infinite bit) + * integer nearest x/p (in half way case choose the even one). + * Method : + * Based on fmod() return x-[x/p]chopped*p exactlp. + */ + +#include <math.h> +#include "math_private.h" + +static const double zero = 0.0; + + +double +remainder(double x, double p) +{ + int32_t hx,hp; + uint32_t sx,lx,lp; + double p_half; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hp,lp,p); + sx = hx&0x80000000; + hp &= 0x7fffffff; + hx &= 0x7fffffff; + + /* purge off exception values */ + if((hp|lp)==0) return (x*p)/(x*p); /* p = 0 */ + if((hx>=0x7ff00000)|| /* x not finite */ + ((hp>=0x7ff00000)&& /* p is NaN */ + (((hp-0x7ff00000)|lp)!=0))) + return (x*p)/(x*p); + + + if (hp<=0x7fdfffff) x = fmod(x,p+p); /* now x < 2p */ + if (((hx-hp)|(lx-lp))==0) return zero*x; + x = fabs(x); + p = fabs(p); + if (hp<0x00200000) { + if(x+x>p) { + x-=p; + if(x+x>=p) x -= p; + } + } else { + p_half = 0.5*p; + if(x>p_half) { + x-=p; + if(x>=p_half) x -= p; + } + } + GET_HIGH_WORD(hx,x); + SET_HIGH_WORD(x,hx^sx); + return x; +} diff --git a/src/math/e_remainderf.c b/src/math/e_remainderf.c new file mode 100644 index 00000000..c292367d --- /dev/null +++ b/src/math/e_remainderf.c @@ -0,0 +1,61 @@ +/* e_remainderf.c -- float version of e_remainder.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float zero = 0.0; + + +float +remainderf(float x, float p) +{ + int32_t hx,hp; + uint32_t sx; + float p_half; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hp,p); + sx = hx&0x80000000; + hp &= 0x7fffffff; + hx &= 0x7fffffff; + + /* purge off exception values */ + if(hp==0) return (x*p)/(x*p); /* p = 0 */ + if((hx>=0x7f800000)|| /* x not finite */ + ((hp>0x7f800000))) /* p is NaN */ + return (x*p)/(x*p); + + + if (hp<=0x7effffff) x = fmodf(x,p+p); /* now x < 2p */ + if ((hx-hp)==0) return zero*x; + x = fabsf(x); + p = fabsf(p); + if (hp<0x01000000) { + if(x+x>p) { + x-=p; + if(x+x>=p) x -= p; + } + } else { + p_half = (float)0.5*p; + if(x>p_half) { + x-=p; + if(x>=p_half) x -= p; + } + } + GET_FLOAT_WORD(hx,x); + SET_FLOAT_WORD(x,hx^sx); + return x; +} diff --git a/src/math/e_scalb.c b/src/math/e_scalb.c new file mode 100644 index 00000000..cee2b44f --- /dev/null +++ b/src/math/e_scalb.c @@ -0,0 +1,35 @@ + +/* @(#)e_scalb.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalb(x, fn) is provide for + * passing various standard test suite. One + * should use scalbn() instead. + */ + +#include <math.h> +#include "math_private.h" + +double +scalb(double x, double fn) +{ + if (isnan(x)||isnan(fn)) return x*fn; + if (!isfinite(fn)) { + if(fn>0.0) return x*fn; + else return x/(-fn); + } + if (rint(fn)!=fn) return (fn-fn)/(fn-fn); + if ( fn > 65000.0) return scalbn(x, 65000); + if (-fn > 65000.0) return scalbn(x,-65000); + return scalbn(x,(int)fn); +} diff --git a/src/math/e_scalbf.c b/src/math/e_scalbf.c new file mode 100644 index 00000000..de7d7f67 --- /dev/null +++ b/src/math/e_scalbf.c @@ -0,0 +1,31 @@ +/* e_scalbf.c -- float version of e_scalb.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +scalbf(float x, float fn) +{ + if (isnan(x)||isnan(fn)) return x*fn; + if (!isfinite(fn)) { + if(fn>(float)0.0) return x*fn; + else return x/(-fn); + } + if (rintf(fn)!=fn) return (fn-fn)/(fn-fn); + if ( fn > (float)65000.0) return scalbnf(x, 65000); + if (-fn > (float)65000.0) return scalbnf(x,-65000); + return scalbnf(x,(int)fn); +} diff --git a/src/math/e_sinh.c b/src/math/e_sinh.c new file mode 100644 index 00000000..3a574274 --- /dev/null +++ b/src/math/e_sinh.c @@ -0,0 +1,75 @@ + +/* @(#)e_sinh.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sinh(x) + * Method : + * mathematically sinh(x) if defined to be (exp(x)-exp(-x))/2 + * 1. Replace x by |x| (sinh(-x) = -sinh(x)). + * 2. + * E + E/(E+1) + * 0 <= x <= 22 : sinh(x) := --------------, E=expm1(x) + * 2 + * + * 22 <= x <= lnovft : sinh(x) := exp(x)/2 + * lnovft <= x <= ln2ovft: sinh(x) := exp(x/2)/2 * exp(x/2) + * ln2ovft < x : sinh(x) := x*shuge (overflow) + * + * Special cases: + * sinh(x) is |x| if x is +INF, -INF, or NaN. + * only sinh(0)=0 is exact for finite x. + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0, shuge = 1.0e307; + +double +sinh(double x) +{ + double t,w,h; + int32_t ix,jx; + uint32_t lx; + + /* High word of |x|. */ + GET_HIGH_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3e300000) /* |x|<2**-28 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = expm1(fabs(x)); + if(ix<0x3ff00000) return h*(2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x40862E42) return h*exp(fabs(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + GET_LOW_WORD(lx,x); + if (ix<0x408633CE || ((ix==0x408633ce)&&(lx<=(uint32_t)0x8fb9f87d))) { + w = exp(0.5*fabs(x)); + t = h*w; + return t*w; + } + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; +} diff --git a/src/math/e_sinhf.c b/src/math/e_sinhf.c new file mode 100644 index 00000000..fe60608a --- /dev/null +++ b/src/math/e_sinhf.c @@ -0,0 +1,56 @@ +/* e_sinhf.c -- float version of e_sinh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0, shuge = 1.0e37; + +float +sinhf(float x) +{ + float t,w,h; + int32_t ix,jx; + + GET_FLOAT_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) return x+x; + + h = 0.5; + if (jx<0) h = -h; + /* |x| in [0,22], return sign(x)*0.5*(E+E/(E+1))) */ + if (ix < 0x41b00000) { /* |x|<22 */ + if (ix<0x31800000) /* |x|<2**-28 */ + if(shuge+x>one) return x;/* sinh(tiny) = tiny with inexact */ + t = expm1f(fabsf(x)); + if(ix<0x3f800000) return h*((float)2.0*t-t*t/(t+one)); + return h*(t+t/(t+one)); + } + + /* |x| in [22, log(maxdouble)] return 0.5*exp(|x|) */ + if (ix < 0x42b17180) return h*expf(fabsf(x)); + + /* |x| in [log(maxdouble), overflowthresold] */ + if (ix<=0x42b2d4fc) { + w = expf((float)0.5*fabsf(x)); + t = h*w; + return t*w; + } + + /* |x| > overflowthresold, sinh(x) overflow */ + return x*shuge; +} diff --git a/src/math/e_sqrt.c b/src/math/e_sqrt.c new file mode 100644 index 00000000..2bc68747 --- /dev/null +++ b/src/math/e_sqrt.c @@ -0,0 +1,442 @@ + +/* @(#)e_sqrt.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sqrt(x) + * Return correctly rounded sqrt. + * ------------------------------------------ + * | Use the hardware sqrt if you have one | + * ------------------------------------------ + * Method: + * Bit by bit method using integer arithmetic. (Slow, but portable) + * 1. Normalization + * Scale x to y in [1,4) with even powers of 2: + * find an integer k such that 1 <= (y=x*2^(2k)) < 4, then + * sqrt(x) = 2^k * sqrt(y) + * 2. Bit by bit computation + * Let q = sqrt(y) truncated to i bit after binary point (q = 1), + * i 0 + * i+1 2 + * s = 2*q , and y = 2 * ( y - q ). (1) + * i i i i + * + * To compute q from q , one checks whether + * i+1 i + * + * -(i+1) 2 + * (q + 2 ) <= y. (2) + * i + * -(i+1) + * If (2) is false, then q = q ; otherwise q = q + 2 . + * i+1 i i+1 i + * + * With some algebric manipulation, it is not difficult to see + * that (2) is equivalent to + * -(i+1) + * s + 2 <= y (3) + * i i + * + * The advantage of (3) is that s and y can be computed by + * i i + * the following recurrence formula: + * if (3) is false + * + * s = s , y = y ; (4) + * i+1 i i+1 i + * + * otherwise, + * -i -(i+1) + * s = s + 2 , y = y - s - 2 (5) + * i+1 i i+1 i i + * + * One may easily use induction to prove (4) and (5). + * Note. Since the left hand side of (3) contain only i+2 bits, + * it does not necessary to do a full (53-bit) comparison + * in (3). + * 3. Final rounding + * After generating the 53 bits result, we compute one more bit. + * Together with the remainder, we can decide whether the + * result is exact, bigger than 1/2ulp, or less than 1/2ulp + * (it will never equal to 1/2ulp). + * The rounding mode can be detected by checking whether + * huge + tiny is equal to huge, and whether huge - tiny is + * equal to huge for some floating point number "huge" and "tiny". + * + * Special cases: + * sqrt(+-0) = +-0 ... exact + * sqrt(inf) = inf + * sqrt(-ve) = NaN ... with invalid signal + * sqrt(NaN) = NaN ... with invalid signal for signaling NaN + * + * Other methods : see the appended file at the end of the program below. + *--------------- + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0, tiny=1.0e-300; + +double +sqrt(double x) +{ + double z; + int32_t sign = (int)0x80000000; + int32_t ix0,s0,q,m,t,i; + uint32_t r,t1,s1,ix1,q1; + + EXTRACT_WORDS(ix0,ix1,x); + + /* take care of Inf and NaN */ + if((ix0&0x7ff00000)==0x7ff00000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix0<=0) { + if(((ix0&(~sign))|ix1)==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix0<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix0>>20); + if(m==0) { /* subnormal x */ + while(ix0==0) { + m -= 21; + ix0 |= (ix1>>11); ix1 <<= 21; + } + for(i=0;(ix0&0x00100000)==0;i++) ix0<<=1; + m -= i-1; + ix0 |= (ix1>>(32-i)); + ix1 <<= i; + } + m -= 1023; /* unbias exponent */ + ix0 = (ix0&0x000fffff)|0x00100000; + if(m&1){ /* odd m, double x to make it even */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + } + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + q = q1 = s0 = s1 = 0; /* [q,q1] = sqrt(x) */ + r = 0x00200000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s0+r; + if(t<=ix0) { + s0 = t+r; + ix0 -= t; + q += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + r = sign; + while(r!=0) { + t1 = s1+r; + t = s0; + if((t<ix0)||((t==ix0)&&(t1<=ix1))) { + s1 = t1+r; + if(((t1&sign)==sign)&&(s1&sign)==0) s0 += 1; + ix0 -= t; + if (ix1 < t1) ix0 -= 1; + ix1 -= t1; + q1 += r; + } + ix0 += ix0 + ((ix1&sign)>>31); + ix1 += ix1; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if((ix0|ix1)!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (q1==(uint32_t)0xffffffff) { q1=0; q += 1;} + else if (z>one) { + if (q1==(uint32_t)0xfffffffe) q+=1; + q1+=2; + } else + q1 += (q1&1); + } + } + ix0 = (q>>1)+0x3fe00000; + ix1 = q1>>1; + if ((q&1)==1) ix1 |= sign; + ix0 += (m <<20); + INSERT_WORDS(z,ix0,ix1); + return z; +} + +/* +Other methods (use floating-point arithmetic) +------------- +(This is a copy of a drafted paper by Prof W. Kahan +and K.C. Ng, written in May, 1986) + + Two algorithms are given here to implement sqrt(x) + (IEEE double precision arithmetic) in software. + Both supply sqrt(x) correctly rounded. The first algorithm (in + Section A) uses newton iterations and involves four divisions. + The second one uses reciproot iterations to avoid division, but + requires more multiplications. Both algorithms need the ability + to chop results of arithmetic operations instead of round them, + and the INEXACT flag to indicate when an arithmetic operation + is executed exactly with no roundoff error, all part of the + standard (IEEE 754-1985). The ability to perform shift, add, + subtract and logical AND operations upon 32-bit words is needed + too, though not part of the standard. + +A. sqrt(x) by Newton Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + + 1 11 52 ...widths + ------------------------------------------------------ + x: |s| e | f | + ------------------------------------------------------ + msb lsb msb lsb ...order + + + ------------------------ ------------------------ + x0: |s| e | f1 | x1: | f2 | + ------------------------ ------------------------ + + By performing shifts and subtracts on x0 and x1 (both regarded + as integers), we obtain an 8-bit approximation of sqrt(x) as + follows. + + k := (x0>>1) + 0x1ff80000; + y0 := k - T1[31&(k>>15)]. ... y ~ sqrt(x) to 8 bits + Here k is a 32-bit integer and T1[] is an integer array containing + correction terms. Now magically the floating value of y (y's + leading 32-bit word is y0, the value of its trailing word is 0) + approximates sqrt(x) to almost 8-bit. + + Value of T1: + static int T1[32]= { + 0, 1024, 3062, 5746, 9193, 13348, 18162, 23592, + 29598, 36145, 43202, 50740, 58733, 67158, 75992, 85215, + 83599, 71378, 60428, 50647, 41945, 34246, 27478, 21581, + 16499, 12183, 8588, 5674, 3403, 1742, 661, 130,}; + + (2) Iterative refinement + + Apply Heron's rule three times to y, we have y approximates + sqrt(x) to within 1 ulp (Unit in the Last Place): + + y := (y+x/y)/2 ... almost 17 sig. bits + y := (y+x/y)/2 ... almost 35 sig. bits + y := y-(y-x/y)/2 ... within 1 ulp + + + Remark 1. + Another way to improve y to within 1 ulp is: + + y := (y+x/y) ... almost 17 sig. bits to 2*sqrt(x) + y := y - 0x00100006 ... almost 18 sig. bits to sqrt(x) + + 2 + (x-y )*y + y := y + 2* ---------- ...within 1 ulp + 2 + 3y + x + + + This formula has one division fewer than the one above; however, + it requires more multiplications and additions. Also x must be + scaled in advance to avoid spurious overflow in evaluating the + expression 3y*y+x. Hence it is not recommended uless division + is slow. If division is very slow, then one should use the + reciproot algorithm given in section B. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + I := FALSE; ... reset INEXACT flag I + R := RZ; ... set rounding mode to round-toward-zero + z := x/y; ... chopped quotient, possibly inexact + If(not I) then { ... if the quotient is exact + if(z=y) { + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + } else { + z := z - ulp; ... special rounding + } + } + i := TRUE; ... sqrt(x) is inexact + If (r=RN) then z=z+ulp ... rounded-to-nearest + If (r=RP) then { ... round-toward-+inf + y = y+ulp; z=z+ulp; + } + y := y+z; ... chopped sum + y0:=y0-0x00100000; ... y := y/2 is correctly rounded. + I := i; ... restore inexact flag + R := r; ... restore rounded mode + return sqrt(x):=y. + + (4) Special cases + + Square root of +inf, +-0, or NaN is itself; + Square root of a negative number is NaN with invalid signal. + + +B. sqrt(x) by Reciproot Iteration + + (1) Initial approximation + + Let x0 and x1 be the leading and the trailing 32-bit words of + a floating point number x (in IEEE double format) respectively + (see section A). By performing shifs and subtracts on x0 and y0, + we obtain a 7.8-bit approximation of 1/sqrt(x) as follows. + + k := 0x5fe80000 - (x0>>1); + y0:= k - T2[63&(k>>14)]. ... y ~ 1/sqrt(x) to 7.8 bits + + Here k is a 32-bit integer and T2[] is an integer array + containing correction terms. Now magically the floating + value of y (y's leading 32-bit word is y0, the value of + its trailing word y1 is set to zero) approximates 1/sqrt(x) + to almost 7.8-bit. + + Value of T2: + static int T2[64]= { + 0x1500, 0x2ef8, 0x4d67, 0x6b02, 0x87be, 0xa395, 0xbe7a, 0xd866, + 0xf14a, 0x1091b,0x11fcd,0x13552,0x14999,0x15c98,0x16e34,0x17e5f, + 0x18d03,0x19a01,0x1a545,0x1ae8a,0x1b5c4,0x1bb01,0x1bfde,0x1c28d, + 0x1c2de,0x1c0db,0x1ba73,0x1b11c,0x1a4b5,0x1953d,0x18266,0x16be0, + 0x1683e,0x179d8,0x18a4d,0x19992,0x1a789,0x1b445,0x1bf61,0x1c989, + 0x1d16d,0x1d77b,0x1dddf,0x1e2ad,0x1e5bf,0x1e6e8,0x1e654,0x1e3cd, + 0x1df2a,0x1d635,0x1cb16,0x1be2c,0x1ae4e,0x19bde,0x1868e,0x16e2e, + 0x1527f,0x1334a,0x11051,0xe951, 0xbe01, 0x8e0d, 0x5924, 0x1edd,}; + + (2) Iterative refinement + + Apply Reciproot iteration three times to y and multiply the + result by x to get an approximation z that matches sqrt(x) + to about 1 ulp. To be exact, we will have + -1ulp < sqrt(x)-z<1.0625ulp. + + ... set rounding mode to Round-to-nearest + y := y*(1.5-0.5*x*y*y) ... almost 15 sig. bits to 1/sqrt(x) + y := y*((1.5-2^-30)+0.5*x*y*y)... about 29 sig. bits to 1/sqrt(x) + ... special arrangement for better accuracy + z := x*y ... 29 bits to sqrt(x), with z*y<1 + z := z + 0.5*z*(1-z*y) ... about 1 ulp to sqrt(x) + + Remark 2. The constant 1.5-2^-30 is chosen to bias the error so that + (a) the term z*y in the final iteration is always less than 1; + (b) the error in the final result is biased upward so that + -1 ulp < sqrt(x) - z < 1.0625 ulp + instead of |sqrt(x)-z|<1.03125ulp. + + (3) Final adjustment + + By twiddling y's last bit it is possible to force y to be + correctly rounded according to the prevailing rounding mode + as follows. Let r and i be copies of the rounding mode and + inexact flag before entering the square root program. Also we + use the expression y+-ulp for the next representable floating + numbers (up and down) of y. Note that y+-ulp = either fixed + point y+-1, or multiply y by nextafter(1,+-inf) in chopped + mode. + + R := RZ; ... set rounding mode to round-toward-zero + switch(r) { + case RN: ... round-to-nearest + if(x<= z*(z-ulp)...chopped) z = z - ulp; else + if(x<= z*(z+ulp)...chopped) z = z; else z = z+ulp; + break; + case RZ:case RM: ... round-to-zero or round-to--inf + R:=RP; ... reset rounding mod to round-to-+inf + if(x<z*z ... rounded up) z = z - ulp; else + if(x>=(z+ulp)*(z+ulp) ...rounded up) z = z+ulp; + break; + case RP: ... round-to-+inf + if(x>(z+ulp)*(z+ulp)...chopped) z = z+2*ulp; else + if(x>z*z ...chopped) z = z+ulp; + break; + } + + Remark 3. The above comparisons can be done in fixed point. For + example, to compare x and w=z*z chopped, it suffices to compare + x1 and w1 (the trailing parts of x and w), regarding them as + two's complement integers. + + ...Is z an exact square root? + To determine whether z is an exact square root of x, let z1 be the + trailing part of z, and also let x0 and x1 be the leading and + trailing parts of x. + + If ((z1&0x03ffffff)!=0) ... not exact if trailing 26 bits of z!=0 + I := 1; ... Raise Inexact flag: z is not exact + else { + j := 1 - [(x0>>20)&1] ... j = logb(x) mod 2 + k := z1 >> 26; ... get z's 25-th and 26-th + fraction bits + I := i or (k&j) or ((k&(j+j+1))!=(x1&3)); + } + R:= r ... restore rounded mode + return sqrt(x):=z. + + If multiplication is cheaper then the foregoing red tape, the + Inexact flag can be evaluated by + + I := i; + I := (z*z!=x) or I. + + Note that z*z can overwrite I; this value must be sensed if it is + True. + + Remark 4. If z*z = x exactly, then bit 25 to bit 0 of z1 must be + zero. + + -------------------- + z1: | f2 | + -------------------- + bit 31 bit 0 + + Further more, bit 27 and 26 of z1, bit 0 and 1 of x1, and the odd + or even of logb(x) have the following relations: + + ------------------------------------------------- + bit 27,26 of z1 bit 1,0 of x1 logb(x) + ------------------------------------------------- + 00 00 odd and even + 01 01 even + 10 10 odd + 10 00 even + 11 01 even + ------------------------------------------------- + + (4) Special cases (see (4) of Section A). + + */ + diff --git a/src/math/e_sqrtf.c b/src/math/e_sqrtf.c new file mode 100644 index 00000000..03a15beb --- /dev/null +++ b/src/math/e_sqrtf.c @@ -0,0 +1,85 @@ +/* e_sqrtf.c -- float version of e_sqrt.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0, tiny=1.0e-30; + +float +sqrtf(float x) +{ + float z; + int32_t sign = (int)0x80000000; + int32_t ix,s,q,m,t,i; + uint32_t r; + + GET_FLOAT_WORD(ix,x); + + /* take care of Inf and NaN */ + if((ix&0x7f800000)==0x7f800000) { + return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf + sqrt(-inf)=sNaN */ + } + /* take care of zero */ + if(ix<=0) { + if((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */ + else if(ix<0) + return (x-x)/(x-x); /* sqrt(-ve) = sNaN */ + } + /* normalize x */ + m = (ix>>23); + if(m==0) { /* subnormal x */ + for(i=0;(ix&0x00800000)==0;i++) ix<<=1; + m -= i-1; + } + m -= 127; /* unbias exponent */ + ix = (ix&0x007fffff)|0x00800000; + if(m&1) /* odd m, double x to make it even */ + ix += ix; + m >>= 1; /* m = [m/2] */ + + /* generate sqrt(x) bit by bit */ + ix += ix; + q = s = 0; /* q = sqrt(x) */ + r = 0x01000000; /* r = moving bit from right to left */ + + while(r!=0) { + t = s+r; + if(t<=ix) { + s = t+r; + ix -= t; + q += r; + } + ix += ix; + r>>=1; + } + + /* use floating add to find out rounding direction */ + if(ix!=0) { + z = one-tiny; /* trigger inexact flag */ + if (z>=one) { + z = one+tiny; + if (z>one) + q += 2; + else + q += (q&1); + } + } + ix = (q>>1)+0x3f000000; + ix += (m <<23); + SET_FLOAT_WORD(z,ix); + return z; +} diff --git a/src/math/i386/e_exp.s b/src/math/i386/e_exp.s new file mode 100644 index 00000000..d6c54a30 --- /dev/null +++ b/src/math/i386/e_exp.s @@ -0,0 +1,36 @@ +.global expf +expf: + mov 4(%esp),%eax + flds 4(%esp) + shr $23,%eax + inc %al + jz 1f + jmp 0f + +.global exp +exp: + mov 8(%esp),%eax + fldl 4(%esp) + shl %eax + cmp $0xffe00000,%eax + jae 1f + +0: fldl2e + fmulp + fst %st(1) + frndint + fst %st(2) + fsubrp + f2xm1 + fld1 + faddp + fscale + fstp %st(1) + ret + +1: fsts 4(%esp) + cmpl $0xff800000,4(%esp) + jnz 1f + fstp %st(0) + fldz +1: ret diff --git a/src/math/i386/e_expf.s b/src/math/i386/e_expf.s new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/math/i386/e_expf.s @@ -0,0 +1 @@ + diff --git a/src/math/i386/e_log.s b/src/math/i386/e_log.s new file mode 100644 index 00000000..34b8d38d --- /dev/null +++ b/src/math/i386/e_log.s @@ -0,0 +1,6 @@ +.global log +log: + fldln2 + fldl 4(%esp) + fyl2x + ret diff --git a/src/math/i386/e_log10.s b/src/math/i386/e_log10.s new file mode 100644 index 00000000..7f48941b --- /dev/null +++ b/src/math/i386/e_log10.s @@ -0,0 +1,6 @@ +.global log10 +log10: + fldlg2 + fldl 4(%esp) + fyl2x + ret diff --git a/src/math/i386/e_log10f.s b/src/math/i386/e_log10f.s new file mode 100644 index 00000000..311486ea --- /dev/null +++ b/src/math/i386/e_log10f.s @@ -0,0 +1,6 @@ +.global log10f +log10f: + fldlg2 + flds 4(%esp) + fyl2x + ret diff --git a/src/math/i386/e_logf.s b/src/math/i386/e_logf.s new file mode 100644 index 00000000..b8beec0f --- /dev/null +++ b/src/math/i386/e_logf.s @@ -0,0 +1,6 @@ +.global logf +logf: + fldln2 + flds 4(%esp) + fyl2x + ret diff --git a/src/math/i386/e_remainder.s b/src/math/i386/e_remainder.s new file mode 100644 index 00000000..b7ff3ef8 --- /dev/null +++ b/src/math/i386/e_remainder.s @@ -0,0 +1,16 @@ +.global remainderf +remainderf: + flds 8(%esp) + flds 4(%esp) + jmp 1f + +.global remainder +remainder: + fldl 12(%esp) + fldl 4(%esp) +1: fprem1 + fstsw %ax + sahf + jp 1b + fstp %st(1) + ret diff --git a/src/math/i386/e_remainderf.s b/src/math/i386/e_remainderf.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/e_remainderf.s diff --git a/src/math/i386/e_sqrt.s b/src/math/i386/e_sqrt.s new file mode 100644 index 00000000..11314dca --- /dev/null +++ b/src/math/i386/e_sqrt.s @@ -0,0 +1,4 @@ +.global sqrt +sqrt: fldl 4(%esp) + fsqrt + ret diff --git a/src/math/i386/e_sqrtf.s b/src/math/i386/e_sqrtf.s new file mode 100644 index 00000000..015e24cd --- /dev/null +++ b/src/math/i386/e_sqrtf.s @@ -0,0 +1,4 @@ +.global sqrtf +sqrtf: flds 4(%esp) + fsqrt + ret diff --git a/src/math/i386/s_ceil.s b/src/math/i386/s_ceil.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_ceil.s diff --git a/src/math/i386/s_ceilf.s b/src/math/i386/s_ceilf.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_ceilf.s diff --git a/src/math/i386/s_fabs.s b/src/math/i386/s_fabs.s new file mode 100644 index 00000000..10c70f37 --- /dev/null +++ b/src/math/i386/s_fabs.s @@ -0,0 +1,5 @@ +.global fabs +fabs: + fldl 4(%esp) + fabs + ret diff --git a/src/math/i386/s_fabsf.s b/src/math/i386/s_fabsf.s new file mode 100644 index 00000000..45442699 --- /dev/null +++ b/src/math/i386/s_fabsf.s @@ -0,0 +1,5 @@ +.global fabsf +fabsf: + flds 4(%esp) + fabs + ret diff --git a/src/math/i386/s_floor.s b/src/math/i386/s_floor.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_floor.s diff --git a/src/math/i386/s_floorf.s b/src/math/i386/s_floorf.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_floorf.s diff --git a/src/math/i386/s_ldexp.s b/src/math/i386/s_ldexp.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_ldexp.s diff --git a/src/math/i386/s_ldexpf.s b/src/math/i386/s_ldexpf.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_ldexpf.s diff --git a/src/math/i386/s_rint.s b/src/math/i386/s_rint.s new file mode 100644 index 00000000..5ba4ab4a --- /dev/null +++ b/src/math/i386/s_rint.s @@ -0,0 +1,5 @@ +.global rint +rint: + fldl 4(%esp) + frndint + ret diff --git a/src/math/i386/s_rintf.s b/src/math/i386/s_rintf.s new file mode 100644 index 00000000..d7aacd8f --- /dev/null +++ b/src/math/i386/s_rintf.s @@ -0,0 +1,5 @@ +.global rintf +rintf: + flds 4(%esp) + frndint + ret diff --git a/src/math/i386/s_scalbln.s b/src/math/i386/s_scalbln.s new file mode 100644 index 00000000..bd022b46 --- /dev/null +++ b/src/math/i386/s_scalbln.s @@ -0,0 +1,11 @@ +.global ldexp +.global scalbn +.global scalbln +ldexp: +scalbn: +scalbln: + fildl 12(%esp) + fldl 4(%esp) + fscale + fstp %st(1) + ret diff --git a/src/math/i386/s_scalblnf.s b/src/math/i386/s_scalblnf.s new file mode 100644 index 00000000..379ec919 --- /dev/null +++ b/src/math/i386/s_scalblnf.s @@ -0,0 +1,11 @@ +.global ldexpf +.global scalbnf +.global scalblnf +ldexpf: +scalbnf: +scalblnf: + fildl 8(%esp) + flds 4(%esp) + fscale + fstp %st(1) + ret diff --git a/src/math/i386/s_trunc.s b/src/math/i386/s_trunc.s new file mode 100644 index 00000000..0773891a --- /dev/null +++ b/src/math/i386/s_trunc.s @@ -0,0 +1,36 @@ +.global ceilf +ceilf: flds 4(%esp) + jmp 1f + +.global ceil +ceil: fldl 4(%esp) +1: mov $0x08fb,%edx + jmp 0f + +.global floorf +floorf: flds 4(%esp) + jmp 1f + +.global floor +floor: fldl 4(%esp) +1: mov $0x04f7,%edx + jmp 0f + +.global truncf +truncf: flds 4(%esp) + jmp 1f + +.global trunc +trunc: fldl 4(%esp) +1: mov $0x0cff,%edx + +0: fstcw 4(%esp) + mov 5(%esp),%ah + or %dh,%ah + and %dl,%ah + xchg %ah,5(%esp) + fldcw 4(%esp) + frndint + mov %ah,5(%esp) + fldcw 4(%esp) + ret diff --git a/src/math/i386/s_truncf.s b/src/math/i386/s_truncf.s new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/math/i386/s_truncf.s diff --git a/src/math/k_cos.c b/src/math/k_cos.c new file mode 100644 index 00000000..22e9841e --- /dev/null +++ b/src/math/k_cos.c @@ -0,0 +1,85 @@ + +/* @(#)k_cos.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __kernel_cos( x, y ) + * kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * + * Algorithm + * 1. Since cos(-x) = cos(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. + * 3. cos(x) is approximated by a polynomial of degree 14 on + * [0,pi/4] + * 4 14 + * cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x + * where the remez error is + * + * | 2 4 6 8 10 12 14 | -58 + * |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 + * | | + * + * 4 6 8 10 12 14 + * 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then + * cos(x) = 1 - x*x/2 + r + * since cos(x+y) ~ cos(x) - sin(x)*y + * ~ cos(x) - x*y, + * a correction term is necessary in cos(x) and hence + * cos(x+y) = 1 - (x*x/2 - (r - x*y)) + * For better accuracy when x > 0.3, let qx = |x|/4 with + * the last 32 bits mask off, and if x > 0.78125, let qx = 0.28125. + * Then + * cos(x+y) = (1-qx) - ((x*x/2-qx) - (r-x*y)). + * Note that 1-qx and (x*x/2-qx) is EXACT here, and the + * magnitude of the latter is at least a quarter of x*x/2, + * thus, reducing the rounding error in the subtraction. + */ + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +C1 = 4.16666666666666019037e-02, /* 0x3FA55555, 0x5555554C */ +C2 = -1.38888888888741095749e-03, /* 0xBF56C16C, 0x16C15177 */ +C3 = 2.48015872894767294178e-05, /* 0x3EFA01A0, 0x19CB1590 */ +C4 = -2.75573143513906633035e-07, /* 0xBE927E4F, 0x809C52AD */ +C5 = 2.08757232129817482790e-09, /* 0x3E21EE9E, 0xBDB4B1C4 */ +C6 = -1.13596475577881948265e-11; /* 0xBDA8FAE9, 0xBE8838D4 */ + +double +__kernel_cos(double x, double y) +{ + double a,hz,z,r,qx; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; /* ix = |x|'s high word*/ + if(ix<0x3e400000) { /* if x < 2**27 */ + if(((int)x)==0) return one; /* generate inexact */ + } + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); + if(ix < 0x3FD33333) /* if |x| < 0.3 */ + return one - (0.5*z - (z*r - x*y)); + else { + if(ix > 0x3fe90000) { /* x > 0.78125 */ + qx = 0.28125; + } else { + INSERT_WORDS(qx,ix-0x00200000,0); /* x/4 */ + } + hz = 0.5*z-qx; + a = one-qx; + return a - (hz - (z*r-x*y)); + } +} diff --git a/src/math/k_cosf.c b/src/math/k_cosf.c new file mode 100644 index 00000000..61dc3749 --- /dev/null +++ b/src/math/k_cosf.c @@ -0,0 +1,52 @@ +/* k_cosf.c -- float version of k_cos.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3f800000 */ +C1 = 4.1666667908e-02, /* 0x3d2aaaab */ +C2 = -1.3888889225e-03, /* 0xbab60b61 */ +C3 = 2.4801587642e-05, /* 0x37d00d01 */ +C4 = -2.7557314297e-07, /* 0xb493f27c */ +C5 = 2.0875723372e-09, /* 0x310f74f6 */ +C6 = -1.1359647598e-11; /* 0xad47d74e */ + +float +__kernel_cosf(float x, float y) +{ + float a,hz,z,r,qx; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; /* ix = |x|'s high word*/ + if(ix<0x32000000) { /* if x < 2**27 */ + if(((int)x)==0) return one; /* generate inexact */ + } + z = x*x; + r = z*(C1+z*(C2+z*(C3+z*(C4+z*(C5+z*C6))))); + if(ix < 0x3e99999a) /* if |x| < 0.3 */ + return one - ((float)0.5*z - (z*r - x*y)); + else { + if(ix > 0x3f480000) { /* x > 0.78125 */ + qx = (float)0.28125; + } else { + SET_FLOAT_WORD(qx,ix-0x01000000); /* x/4 */ + } + hz = (float)0.5*z-qx; + a = one-qx; + return a - (hz - (z*r-x*y)); + } +} diff --git a/src/math/k_rem_pio2.c b/src/math/k_rem_pio2.c new file mode 100644 index 00000000..d993e4f2 --- /dev/null +++ b/src/math/k_rem_pio2.c @@ -0,0 +1,300 @@ + +/* @(#)k_rem_pio2.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * __kernel_rem_pio2(x,y,e0,nx,prec,ipio2) + * double x[],y[]; int e0,nx,prec; int ipio2[]; + * + * __kernel_rem_pio2 return the last three digits of N with + * y = x - N*pi/2 + * so that |y| < pi/2. + * + * The method is to compute the integer (mod 8) and fraction parts of + * (2/pi)*x without doing the full multiplication. In general we + * skip the part of the product that are known to be a huge integer ( + * more accurately, = 0 mod 8 ). Thus the number of operations are + * independent of the exponent of the input. + * + * (2/pi) is represented by an array of 24-bit integers in ipio2[]. + * + * Input parameters: + * x[] The input value (must be positive) is broken into nx + * pieces of 24-bit integers in double precision format. + * x[i] will be the i-th 24 bit of x. The scaled exponent + * of x[0] is given in input parameter e0 (i.e., x[0]*2^e0 + * match x's up to 24 bits. + * + * Example of breaking a double positive z into x[0]+x[1]+x[2]: + * e0 = ilogb(z)-23 + * z = scalbn(z,-e0) + * for i = 0,1,2 + * x[i] = floor(z) + * z = (z-x[i])*2**24 + * + * + * y[] ouput result in an array of double precision numbers. + * The dimension of y[] is: + * 24-bit precision 1 + * 53-bit precision 2 + * 64-bit precision 2 + * 113-bit precision 3 + * The actual value is the sum of them. Thus for 113-bit + * precison, one may have to do something like: + * + * long double t,w,r_head, r_tail; + * t = (long double)y[2] + (long double)y[1]; + * w = (long double)y[0]; + * r_head = t+w; + * r_tail = w - (r_head - t); + * + * e0 The exponent of x[0] + * + * nx dimension of x[] + * + * prec an integer indicating the precision: + * 0 24 bits (single) + * 1 53 bits (double) + * 2 64 bits (extended) + * 3 113 bits (quad) + * + * ipio2[] + * integer array, contains the (24*i)-th to (24*i+23)-th + * bit of 2/pi after binary point. The corresponding + * floating value is + * + * ipio2[i] * 2^(-24(i+1)). + * + * External function: + * double scalbn(), floor(); + * + * + * Here is the description of some local variables: + * + * jk jk+1 is the initial number of terms of ipio2[] needed + * in the computation. The recommended value is 2,3,4, + * 6 for single, double, extended,and quad. + * + * jz local integer variable indicating the number of + * terms of ipio2[] used. + * + * jx nx - 1 + * + * jv index for pointing to the suitable ipio2[] for the + * computation. In general, we want + * ( 2^e0*x[0] * ipio2[jv-1]*2^(-24jv) )/8 + * is an integer. Thus + * e0-3-24*jv >= 0 or (e0-3)/24 >= jv + * Hence jv = max(0,(e0-3)/24). + * + * jp jp+1 is the number of terms in PIo2[] needed, jp = jk. + * + * q[] double array with integral value, representing the + * 24-bits chunk of the product of x and 2/pi. + * + * q0 the corresponding exponent of q[0]. Note that the + * exponent for q[i] would be q0-24*i. + * + * PIo2[] double precision array, obtained by cutting pi/2 + * into 24 bits chunks. + * + * f[] ipio2[] in floating point + * + * iq[] integer array by breaking up q[] in 24-bits chunk. + * + * fq[] final product of x*(2/pi) in fq[0],..,fq[jk] + * + * ih integer. If >0 it indicates q[] is >= 0.5, hence + * it also indicates the *sign* of the result. + * + */ + + +/* + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const int init_jk[] = {2,3,4,6}; /* initial value for jk */ + +static const double PIo2[] = { + 1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */ + 7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */ + 5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */ + 3.28200341580791294123e-22, /* 0x3B78CC51, 0x60000000 */ + 1.27065575308067607349e-29, /* 0x39F01B83, 0x80000000 */ + 1.22933308981111328932e-36, /* 0x387A2520, 0x40000000 */ + 2.73370053816464559624e-44, /* 0x36E38222, 0x80000000 */ + 2.16741683877804819444e-51, /* 0x3569F31D, 0x00000000 */ +}; + +static const double +zero = 0.0, +one = 1.0, +two24 = 1.67772160000000000000e+07, /* 0x41700000, 0x00000000 */ +twon24 = 5.96046447753906250000e-08; /* 0x3E700000, 0x00000000 */ + + int __kernel_rem_pio2(double *x, double *y, int e0, int nx, int prec, const int32_t *ipio2) +{ + int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; + double z,fw,f[20],fq[20],q[20]; + + /* initialize jk*/ + jk = init_jk[prec]; + jp = jk; + + /* determine jx,jv,q0, note that 3>q0 */ + jx = nx-1; + jv = (e0-3)/24; if(jv<0) jv=0; + q0 = e0-24*(jv+1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + j = jv-jx; m = jx+jk; + for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (double) ipio2[j]; + + /* compute q[0],q[1],...q[jk] */ + for (i=0;i<=jk;i++) { + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw; + } + + jz = jk; +recompute: + /* distill q[] into iq[] reversingly */ + for(i=0,j=jz,z=q[jz];j>0;i++,j--) { + fw = (double)((int32_t)(twon24* z)); + iq[i] = (int32_t)(z-two24*fw); + z = q[j-1]+fw; + } + + /* compute n */ + z = scalbn(z,q0); /* actual value of z */ + z -= 8.0*floor(z*0.125); /* trim off integer >= 8 */ + n = (int32_t) z; + z -= (double)n; + ih = 0; + if(q0>0) { /* need iq[jz-1] to determine n */ + i = (iq[jz-1]>>(24-q0)); n += i; + iq[jz-1] -= i<<(24-q0); + ih = iq[jz-1]>>(23-q0); + } + else if(q0==0) ih = iq[jz-1]>>23; + else if(z>=0.5) ih=2; + + if(ih>0) { /* q > 0.5 */ + n += 1; carry = 0; + for(i=0;i<jz ;i++) { /* compute 1-q */ + j = iq[i]; + if(carry==0) { + if(j!=0) { + carry = 1; iq[i] = 0x1000000- j; + } + } else iq[i] = 0xffffff - j; + } + if(q0>0) { /* rare case: chance is 1 in 12 */ + switch(q0) { + case 1: + iq[jz-1] &= 0x7fffff; break; + case 2: + iq[jz-1] &= 0x3fffff; break; + } + } + if(ih==2) { + z = one - z; + if(carry!=0) z -= scalbn(one,q0); + } + } + + /* check if recomputation is needed */ + if(z==zero) { + j = 0; + for (i=jz-1;i>=jk;i--) j |= iq[i]; + if(j==0) { /* need recomputation */ + for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ + + for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ + f[jx+i] = (double) ipio2[jv+i]; + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + /* chop off zero terms */ + if(z==0.0) { + jz -= 1; q0 -= 24; + while(iq[jz]==0) { jz--; q0-=24;} + } else { /* break z into 24-bit if necessary */ + z = scalbn(z,-q0); + if(z>=two24) { + fw = (double)((int32_t)(twon24*z)); + iq[jz] = (int32_t)(z-two24*fw); + jz += 1; q0 += 24; + iq[jz] = (int32_t) fw; + } else iq[jz] = (int32_t) z ; + } + + /* convert integer "bit" chunk to floating-point value */ + fw = scalbn(one,q0); + for(i=jz;i>=0;i--) { + q[i] = fw*(double)iq[i]; fw*=twon24; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + for(i=jz;i>=0;i--) { + for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; + fq[jz-i] = fw; + } + + /* compress fq[] into y[] */ + switch(prec) { + case 0: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + break; + case 1: + case 2: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + fw = fq[0]-fw; + for (i=1;i<=jz;i++) fw += fq[i]; + y[1] = (ih==0)? fw: -fw; + break; + case 3: /* painful */ + for (i=jz;i>0;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (i=jz;i>1;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; + if(ih==0) { + y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; + } else { + y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; + } + } + return n&7; +} diff --git a/src/math/k_rem_pio2f.c b/src/math/k_rem_pio2f.c new file mode 100644 index 00000000..b543f084 --- /dev/null +++ b/src/math/k_rem_pio2f.c @@ -0,0 +1,192 @@ +/* k_rem_pio2f.c -- float version of k_rem_pio2.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +/* In the float version, the input parameter x contains 8 bit + integers, not 24 bit integers. 113 bit precision is not supported. */ + +static const int init_jk[] = {4,7,9}; /* initial value for jk */ + +static const float PIo2[] = { + 1.5703125000e+00, /* 0x3fc90000 */ + 4.5776367188e-04, /* 0x39f00000 */ + 2.5987625122e-05, /* 0x37da0000 */ + 7.5437128544e-08, /* 0x33a20000 */ + 6.0026650317e-11, /* 0x2e840000 */ + 7.3896444519e-13, /* 0x2b500000 */ + 5.3845816694e-15, /* 0x27c20000 */ + 5.6378512969e-18, /* 0x22d00000 */ + 8.3009228831e-20, /* 0x1fc40000 */ + 3.2756352257e-22, /* 0x1bc60000 */ + 6.3331015649e-25, /* 0x17440000 */ +}; + +static const float +zero = 0.0, +one = 1.0, +two8 = 2.5600000000e+02, /* 0x43800000 */ +twon8 = 3.9062500000e-03; /* 0x3b800000 */ + + int __kernel_rem_pio2f(float *x, float *y, int e0, int nx, int prec, const int32_t *ipio2) +{ + int32_t jz,jx,jv,jp,jk,carry,n,iq[20],i,j,k,m,q0,ih; + float z,fw,f[20],fq[20],q[20]; + + /* initialize jk*/ + jk = init_jk[prec]; + jp = jk; + + /* determine jx,jv,q0, note that 3>q0 */ + jx = nx-1; + jv = (e0-3)/8; if(jv<0) jv=0; + q0 = e0-8*(jv+1); + + /* set up f[0] to f[jx+jk] where f[jx+jk] = ipio2[jv+jk] */ + j = jv-jx; m = jx+jk; + for(i=0;i<=m;i++,j++) f[i] = (j<0)? zero : (float) ipio2[j]; + + /* compute q[0],q[1],...q[jk] */ + for (i=0;i<=jk;i++) { + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; q[i] = fw; + } + + jz = jk; +recompute: + /* distill q[] into iq[] reversingly */ + for(i=0,j=jz,z=q[jz];j>0;i++,j--) { + fw = (float)((int32_t)(twon8* z)); + iq[i] = (int32_t)(z-two8*fw); + z = q[j-1]+fw; + } + + /* compute n */ + z = scalbnf(z,q0); /* actual value of z */ + z -= (float)8.0*floorf(z*(float)0.125); /* trim off integer >= 8 */ + n = (int32_t) z; + z -= (float)n; + ih = 0; + if(q0>0) { /* need iq[jz-1] to determine n */ + i = (iq[jz-1]>>(8-q0)); n += i; + iq[jz-1] -= i<<(8-q0); + ih = iq[jz-1]>>(7-q0); + } + else if(q0==0) ih = iq[jz-1]>>7; + else if(z>=(float)0.5) ih=2; + + if(ih>0) { /* q > 0.5 */ + n += 1; carry = 0; + for(i=0;i<jz ;i++) { /* compute 1-q */ + j = iq[i]; + if(carry==0) { + if(j!=0) { + carry = 1; iq[i] = 0x100- j; + } + } else iq[i] = 0xff - j; + } + if(q0>0) { /* rare case: chance is 1 in 12 */ + switch(q0) { + case 1: + iq[jz-1] &= 0x7f; break; + case 2: + iq[jz-1] &= 0x3f; break; + } + } + if(ih==2) { + z = one - z; + if(carry!=0) z -= scalbnf(one,q0); + } + } + + /* check if recomputation is needed */ + if(z==zero) { + j = 0; + for (i=jz-1;i>=jk;i--) j |= iq[i]; + if(j==0) { /* need recomputation */ + for(k=1;iq[jk-k]==0;k++); /* k = no. of terms needed */ + + for(i=jz+1;i<=jz+k;i++) { /* add q[jz+1] to q[jz+k] */ + f[jx+i] = (float) ipio2[jv+i]; + for(j=0,fw=0.0;j<=jx;j++) fw += x[j]*f[jx+i-j]; + q[i] = fw; + } + jz += k; + goto recompute; + } + } + + /* chop off zero terms */ + if(z==(float)0.0) { + jz -= 1; q0 -= 8; + while(iq[jz]==0) { jz--; q0-=8;} + } else { /* break z into 8-bit if necessary */ + z = scalbnf(z,-q0); + if(z>=two8) { + fw = (float)((int32_t)(twon8*z)); + iq[jz] = (int32_t)(z-two8*fw); + jz += 1; q0 += 8; + iq[jz] = (int32_t) fw; + } else iq[jz] = (int32_t) z ; + } + + /* convert integer "bit" chunk to floating-point value */ + fw = scalbnf(one,q0); + for(i=jz;i>=0;i--) { + q[i] = fw*(float)iq[i]; fw*=twon8; + } + + /* compute PIo2[0,...,jp]*q[jz,...,0] */ + for(i=jz;i>=0;i--) { + for(fw=0.0,k=0;k<=jp&&k<=jz-i;k++) fw += PIo2[k]*q[i+k]; + fq[jz-i] = fw; + } + + /* compress fq[] into y[] */ + switch(prec) { + case 0: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + break; + case 1: + case 2: + fw = 0.0; + for (i=jz;i>=0;i--) fw += fq[i]; + y[0] = (ih==0)? fw: -fw; + fw = fq[0]-fw; + for (i=1;i<=jz;i++) fw += fq[i]; + y[1] = (ih==0)? fw: -fw; + break; + case 3: /* painful */ + for (i=jz;i>0;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (i=jz;i>1;i--) { + fw = fq[i-1]+fq[i]; + fq[i] += fq[i-1]-fw; + fq[i-1] = fw; + } + for (fw=0.0,i=jz;i>=2;i--) fw += fq[i]; + if(ih==0) { + y[0] = fq[0]; y[1] = fq[1]; y[2] = fw; + } else { + y[0] = -fq[0]; y[1] = -fq[1]; y[2] = -fw; + } + } + return n&7; +} diff --git a/src/math/k_sin.c b/src/math/k_sin.c new file mode 100644 index 00000000..9def2589 --- /dev/null +++ b/src/math/k_sin.c @@ -0,0 +1,68 @@ + +/* @(#)k_sin.c 1.3 95/01/18 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __kernel_sin( x, y, iy) + * kernel sin function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input iy indicates whether y is 0. (if iy=0, y assume to be 0). + * + * Algorithm + * 1. Since sin(-x) = -sin(x), we need only to consider positive x. + * 2. if x < 2^-27 (hx<0x3e400000 0), return x with inexact if x!=0. + * 3. sin(x) is approximated by a polynomial of degree 13 on + * [0,pi/4] + * 3 13 + * sin(x) ~ x + S1*x + ... + S6*x + * where + * + * |sin(x) 2 4 6 8 10 12 | -58 + * |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 + * | x | + * + * 4. sin(x+y) = sin(x) + sin'(x')*y + * ~ sin(x) + (1-x*x/2)*y + * For better accuracy, let + * 3 2 2 2 2 + * r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) + * then 3 2 + * sin(x) = x + (S1*x + (x *(r-y/2)+y)) + */ + +#include <math.h> +#include "math_private.h" + +static const double +half = 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +S1 = -1.66666666666666324348e-01, /* 0xBFC55555, 0x55555549 */ +S2 = 8.33333333332248946124e-03, /* 0x3F811111, 0x1110F8A6 */ +S3 = -1.98412698298579493134e-04, /* 0xBF2A01A0, 0x19C161D5 */ +S4 = 2.75573137070700676789e-06, /* 0x3EC71DE3, 0x57B1FE7D */ +S5 = -2.50507602534068634195e-08, /* 0xBE5AE5E6, 0x8A2B9CEB */ +S6 = 1.58969099521155010221e-10; /* 0x3DE5D93A, 0x5ACFD57C */ + +double +__kernel_sin(double x, double y, int iy) +{ + double z,r,v; + int32_t ix; + GET_HIGH_WORD(ix,x); + ix &= 0x7fffffff; /* high word of x */ + if(ix<0x3e400000) /* |x| < 2**-27 */ + {if((int)x==0) return x;} /* generate inexact */ + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*S6))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/src/math/k_sinf.c b/src/math/k_sinf.c new file mode 100644 index 00000000..617f6148 --- /dev/null +++ b/src/math/k_sinf.c @@ -0,0 +1,42 @@ +/* k_sinf.c -- float version of k_sin.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +half = 5.0000000000e-01,/* 0x3f000000 */ +S1 = -1.6666667163e-01, /* 0xbe2aaaab */ +S2 = 8.3333337680e-03, /* 0x3c088889 */ +S3 = -1.9841270114e-04, /* 0xb9500d01 */ +S4 = 2.7557314297e-06, /* 0x3638ef1b */ +S5 = -2.5050759689e-08, /* 0xb2d72f34 */ +S6 = 1.5896910177e-10; /* 0x2f2ec9d3 */ + +float +__kernel_sinf(float x, float y, int iy) +{ + float z,r,v; + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; /* high word of x */ + if(ix<0x32000000) /* |x| < 2**-27 */ + {if((int)x==0) return x;} /* generate inexact */ + z = x*x; + v = z*x; + r = S2+z*(S3+z*(S4+z*(S5+z*S6))); + if(iy==0) return x+v*(S1+z*r); + else return x-((z*(half*y-v*r)-y)-v*S1); +} diff --git a/src/math/k_tan.c b/src/math/k_tan.c new file mode 100644 index 00000000..f721ae6d --- /dev/null +++ b/src/math/k_tan.c @@ -0,0 +1,149 @@ +/* @(#)k_tan.c 1.5 04/04/22 SMI */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* __kernel_tan( x, y, k ) + * kernel tan function on [-pi/4, pi/4], pi/4 ~ 0.7854 + * Input x is assumed to be bounded by ~pi/4 in magnitude. + * Input y is the tail of x. + * Input k indicates whether tan (if k = 1) or -1/tan (if k = -1) is returned. + * + * Algorithm + * 1. Since tan(-x) = -tan(x), we need only to consider positive x. + * 2. if x < 2^-28 (hx<0x3e300000 0), return x with inexact if x!=0. + * 3. tan(x) is approximated by a odd polynomial of degree 27 on + * [0,0.67434] + * 3 27 + * tan(x) ~ x + T1*x + ... + T13*x + * where + * + * |tan(x) 2 4 26 | -59.2 + * |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 + * | x | + * + * Note: tan(x+y) = tan(x) + tan'(x)*y + * ~ tan(x) + (1+x*x)*y + * Therefore, for better accuracy in computing tan(x+y), let + * 3 2 2 2 2 + * r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) + * then + * 3 2 + * tan(x+y) = x + (T1*x + (x *(r+y)+y)) + * + * 4. For x in [0.67434,pi/4], let y = pi/4 - x, then + * tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) + * = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) + */ + +#include <math.h> +#include "math_private.h" +static const double xxx[] = { + 3.33333333333334091986e-01, /* 3FD55555, 55555563 */ + 1.33333333333201242699e-01, /* 3FC11111, 1110FE7A */ + 5.39682539762260521377e-02, /* 3FABA1BA, 1BB341FE */ + 2.18694882948595424599e-02, /* 3F9664F4, 8406D637 */ + 8.86323982359930005737e-03, /* 3F8226E3, E96E8493 */ + 3.59207910759131235356e-03, /* 3F6D6D22, C9560328 */ + 1.45620945432529025516e-03, /* 3F57DBC8, FEE08315 */ + 5.88041240820264096874e-04, /* 3F4344D8, F2F26501 */ + 2.46463134818469906812e-04, /* 3F3026F7, 1A8D1068 */ + 7.81794442939557092300e-05, /* 3F147E88, A03792A6 */ + 7.14072491382608190305e-05, /* 3F12B80F, 32F0A7E9 */ + -1.85586374855275456654e-05, /* BEF375CB, DB605373 */ + 2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */ +/* one */ 1.00000000000000000000e+00, /* 3FF00000, 00000000 */ +/* pio4 */ 7.85398163397448278999e-01, /* 3FE921FB, 54442D18 */ +/* pio4lo */ 3.06161699786838301793e-17 /* 3C81A626, 33145C07 */ +}; +#define one xxx[13] +#define pio4 xxx[14] +#define pio4lo xxx[15] +#define T xxx +/* INDENT ON */ + +double +__kernel_tan(double x, double y, int iy) { + double z, r, v, w, s; + int32_t ix, hx; + + GET_HIGH_WORD(hx,x); + ix = hx & 0x7fffffff; /* high word of |x| */ + if (ix < 0x3e300000) { /* x < 2**-28 */ + if ((int) x == 0) { /* generate inexact */ + uint32_t low; + GET_LOW_WORD(low,x); + if (((ix | low) | (iy + 1)) == 0) + return one / fabs(x); + else { + if (iy == 1) + return x; + else { /* compute -1 / (x+y) carefully */ + double a, t; + + z = w = x + y; + SET_LOW_WORD(z, 0); + v = y - (z - x); + t = a = -one / w; + SET_LOW_WORD(t, 0); + s = one + t * z; + return t + a * (s + t * v); + } + } + } + } + if (ix >= 0x3FE59428) { /* |x| >= 0.6744 */ + if (hx < 0) { + x = -x; + y = -y; + } + z = pio4 - x; + w = pio4lo - y; + x = z + w; + y = 0.0; + } + z = x * x; + w = z * z; + /* + * Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + + w * T[11])))); + v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + + w * T[12]))))); + s = z * x; + r = y + z * (s * (r + v) + y); + r += T[0] * s; + w = x + r; + if (ix >= 0x3FE59428) { + v = (double) iy; + return (double) (1 - ((hx >> 30) & 2)) * + (v - 2.0 * (x - (w * w / (w + v) - r))); + } + if (iy == 1) + return w; + else { + /* + * if allow error up to 2 ulp, simply return + * -1.0 / (x+r) here + */ + /* compute -1.0 / (x+r) accurately */ + double a, t; + z = w; + SET_LOW_WORD(z,0); + v = r - (z - x); /* z+v = r+x */ + t = a = -1.0 / w; /* a = -1.0/w */ + SET_LOW_WORD(t,0); + s = 1.0 + t * z; + return t + a * (s + t * v); + } +} diff --git a/src/math/k_tanf.c b/src/math/k_tanf.c new file mode 100644 index 00000000..99ede58c --- /dev/null +++ b/src/math/k_tanf.c @@ -0,0 +1,105 @@ +/* k_tanf.c -- float version of k_tan.c + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" +static const float +one = 1.0000000000e+00, /* 0x3f800000 */ +pio4 = 7.8539812565e-01, /* 0x3f490fda */ +pio4lo= 3.7748947079e-08, /* 0x33222168 */ +T[] = { + 3.3333334327e-01, /* 0x3eaaaaab */ + 1.3333334029e-01, /* 0x3e088889 */ + 5.3968254477e-02, /* 0x3d5d0dd1 */ + 2.1869488060e-02, /* 0x3cb327a4 */ + 8.8632395491e-03, /* 0x3c11371f */ + 3.5920790397e-03, /* 0x3b6b6916 */ + 1.4562094584e-03, /* 0x3abede48 */ + 5.8804126456e-04, /* 0x3a1a26c8 */ + 2.4646313977e-04, /* 0x398137b9 */ + 7.8179444245e-05, /* 0x38a3f445 */ + 7.1407252108e-05, /* 0x3895c07a */ + -1.8558637748e-05, /* 0xb79bae5f */ + 2.5907305826e-05, /* 0x37d95384 */ +}; + +float +__kernel_tanf(float x, float y, int iy) +{ + float z,r,v,w,s; + int32_t ix,hx; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; /* high word of |x| */ + if(ix<0x31800000) { /* x < 2**-28 */ + if ((int) x == 0) { /* generate inexact */ + if ((ix | (iy + 1)) == 0) + return one / fabsf(x); + else { + if (iy == 1) + return x; + else { /* compute -1 / (x+y) carefully */ + double a, t; + + z = w = x + y; + GET_FLOAT_WORD(ix, z); + SET_FLOAT_WORD(z, ix & 0xfffff000); + v = y - (z - x); + t = a = -one / w; + GET_FLOAT_WORD(ix, t); + SET_FLOAT_WORD(t, ix & 0xfffff000); + s = one + t * z; + return t + a * (s + t * v); + } + } + } + } + if(ix>=0x3f2ca140) { /* |x|>=0.6744 */ + if(hx<0) {x = -x; y = -y;} + z = pio4-x; + w = pio4lo-y; + x = z+w; y = 0.0; + } + z = x*x; + w = z*z; + /* Break x^5*(T[1]+x^2*T[2]+...) into + * x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + + * x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) + */ + r = T[1]+w*(T[3]+w*(T[5]+w*(T[7]+w*(T[9]+w*T[11])))); + v = z*(T[2]+w*(T[4]+w*(T[6]+w*(T[8]+w*(T[10]+w*T[12]))))); + s = z*x; + r = y + z*(s*(r+v)+y); + r += T[0]*s; + w = x+r; + if(ix>=0x3f2ca140) { + v = (float)iy; + return (float)(1-((hx>>30)&2))*(v-(float)2.0*(x-(w*w/(w+v)-r))); + } + if(iy==1) return w; + else { /* if allow error up to 2 ulp, + simply return -1.0/(x+r) here */ + /* compute -1.0/(x+r) accurately */ + float a,t; + int32_t i; + z = w; + GET_FLOAT_WORD(i,z); + SET_FLOAT_WORD(z,i&0xfffff000); + v = r-(z - x); /* z+v = r+x */ + t = a = -(float)1.0/w; /* a = -1.0/w */ + GET_FLOAT_WORD(i,t); + SET_FLOAT_WORD(t,i&0xfffff000); + s = (float)1.0+t*z; + return t+a*(s+t*v); + } +} diff --git a/src/math/math_private.h b/src/math/math_private.h new file mode 100644 index 00000000..28a6a195 --- /dev/null +++ b/src/math/math_private.h @@ -0,0 +1,143 @@ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#ifndef _MATH_PRIVATE_H_ +#define _MATH_PRIVATE_H_ + +#include <inttypes.h> + +/* + * The original fdlibm code used statements like: + * n0 = ((*(int*)&one)>>29)^1; * index of high word * + * ix0 = *(n0+(int*)&x); * high word of x * + * ix1 = *((1-n0)+(int*)&x); * low word of x * + * to dig two 32 bit words out of the 64 bit IEEE floating point + * value. That is non-ANSI, and, moreover, the gcc instruction + * scheduler gets it wrong. We instead use the following macros. + * Unlike the original code, we determine the endianness at compile + * time, not at run time; I don't see much benefit to selecting + * endianness at run time. + */ + +/* + * A union which permits us to convert between a double and two 32 bit + * ints. + */ + +typedef union +{ + double value; + uint64_t words; +} ieee_double_shape_type; + +/* Get two 32 bit ints from a double. */ + +#define EXTRACT_WORDS(ix0,ix1,d) \ +do { \ + ieee_double_shape_type ew_u; \ + ew_u.value = (d); \ + (ix0) = ew_u.words >> 32; \ + (ix1) = (uint32_t)ew_u.words; \ +} while (0) + +/* Get the more significant 32 bit int from a double. */ + +#define GET_HIGH_WORD(i,d) \ +do { \ + ieee_double_shape_type gh_u; \ + gh_u.value = (d); \ + (i) = gh_u.words >> 32; \ +} while (0) + +/* Get the less significant 32 bit int from a double. */ + +#define GET_LOW_WORD(i,d) \ +do { \ + ieee_double_shape_type gl_u; \ + gl_u.value = (d); \ + (i) = (uint32_t)gl_u.words; \ +} while (0) + +/* Set a double from two 32 bit ints. */ + +#define INSERT_WORDS(d,ix0,ix1) \ +do { \ + ieee_double_shape_type iw_u; \ + iw_u.words = ((uint64_t)(ix0) << 32) | (ix1); \ + (d) = iw_u.value; \ +} while (0) + +/* Set the more significant 32 bits of a double from an int. */ + +#define SET_HIGH_WORD(d,v) \ +do { \ + ieee_double_shape_type sh_u; \ + sh_u.value = (d); \ + sh_u.words &= 0xffffffff; \ + sh_u.words |= ((uint64_t)(v) << 32); \ + (d) = sh_u.value; \ +} while (0) + +/* Set the less significant 32 bits of a double from an int. */ + +#define SET_LOW_WORD(d,v) \ +do { \ + ieee_double_shape_type sl_u; \ + sl_u.value = (d); \ + sl_u.words &= 0xffffffff00000000ull; \ + sl_u.words |= (uint32_t)(v); \ + (d) = sl_u.value; \ +} while (0) + +/* + * A union which permits us to convert between a float and a 32 bit + * int. + */ + +typedef union +{ + float value; + uint32_t word; +} ieee_float_shape_type; + +/* Get a 32 bit int from a float. */ + +#define GET_FLOAT_WORD(i,d) \ +do { \ + ieee_float_shape_type gf_u; \ + gf_u.value = (d); \ + (i) = gf_u.word; \ +} while (0) + +/* Set a float from a 32 bit int. */ + +#define SET_FLOAT_WORD(d,i) \ +do { \ + ieee_float_shape_type sf_u; \ + sf_u.word = (i); \ + (d) = sf_u.value; \ +} while (0) + +/* fdlibm kernel function */ +int __ieee754_rem_pio2(double,double*); +double __kernel_sin(double,double,int); +double __kernel_cos(double,double); +double __kernel_tan(double,double,int); +int __kernel_rem_pio2(double*,double*,int,int,int,const int*); + +/* float versions of fdlibm kernel functions */ +int __ieee754_rem_pio2f(float,float*); +float __kernel_sinf(float,float,int); +float __kernel_cosf(float,float); +float __kernel_tanf(float,float,int); +int __kernel_rem_pio2f(float*,float*,int,int,int,const int*); + +#endif /* !_MATH_PRIVATE_H_ */ diff --git a/src/math/s_asinh.c b/src/math/s_asinh.c new file mode 100644 index 00000000..26016091 --- /dev/null +++ b/src/math/s_asinh.c @@ -0,0 +1,53 @@ +/* @(#)s_asinh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* asinh(x) + * Method : + * Based on + * asinh(x) = sign(x) * log [ |x| + sqrt(x*x+1) ] + * we have + * asinh(x) := x if 1+x*x=1, + * := sign(x)*(log(x)+ln2)) for large |x|, else + * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else + * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2))) + */ + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +ln2 = 6.93147180559945286227e-01, /* 0x3FE62E42, 0xFEFA39EF */ +huge= 1.00000000000000000000e+300; + +double +asinh(double x) +{ + double t,w; + int32_t hx,ix; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) return x+x; /* x is inf or NaN */ + if(ix< 0x3e300000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x41b00000) { /* |x| > 2**28 */ + w = log(fabs(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = fabs(x); + w = log(2.0*t+one/(sqrt(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1p(fabs(x)+t/(one+sqrt(one+t))); + } + if(hx>0) return w; else return -w; +} diff --git a/src/math/s_asinhf.c b/src/math/s_asinhf.c new file mode 100644 index 00000000..04f8d072 --- /dev/null +++ b/src/math/s_asinhf.c @@ -0,0 +1,45 @@ +/* s_asinhf.c -- float version of s_asinh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0000000000e+00, /* 0x3F800000 */ +ln2 = 6.9314718246e-01, /* 0x3f317218 */ +huge= 1.0000000000e+30; + +float +asinhf(float x) +{ + float t,w; + int32_t hx,ix; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) return x+x; /* x is inf or NaN */ + if(ix< 0x31800000) { /* |x|<2**-28 */ + if(huge+x>one) return x; /* return x inexact except 0 */ + } + if(ix>0x4d800000) { /* |x| > 2**28 */ + w = logf(fabsf(x))+ln2; + } else if (ix>0x40000000) { /* 2**28 > |x| > 2.0 */ + t = fabsf(x); + w = logf((float)2.0*t+one/(sqrtf(x*x+one)+t)); + } else { /* 2.0 > |x| > 2**-28 */ + t = x*x; + w =log1pf(fabsf(x)+t/(one+sqrtf(one+t))); + } + if(hx>0) return w; else return -w; +} diff --git a/src/math/s_atan.c b/src/math/s_atan.c new file mode 100644 index 00000000..1faac024 --- /dev/null +++ b/src/math/s_atan.c @@ -0,0 +1,115 @@ +/* @(#)s_atan.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* atan(x) + * Method + * 1. Reduce x to positive by atan(x) = -atan(-x). + * 2. According to the integer k=4t+0.25 chopped, t=x, the argument + * is further reduced to one of the following intervals and the + * arctangent of t is evaluated by the corresponding formula: + * + * [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...) + * [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) ) + * [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) ) + * [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) ) + * [39/16,INF] atan(x) = atan(INF) + atan( -1/t ) + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double atanhi[] = { + 4.63647609000806093515e-01, /* atan(0.5)hi 0x3FDDAC67, 0x0561BB4F */ + 7.85398163397448278999e-01, /* atan(1.0)hi 0x3FE921FB, 0x54442D18 */ + 9.82793723247329054082e-01, /* atan(1.5)hi 0x3FEF730B, 0xD281F69B */ + 1.57079632679489655800e+00, /* atan(inf)hi 0x3FF921FB, 0x54442D18 */ +}; + +static const double atanlo[] = { + 2.26987774529616870924e-17, /* atan(0.5)lo 0x3C7A2B7F, 0x222F65E2 */ + 3.06161699786838301793e-17, /* atan(1.0)lo 0x3C81A626, 0x33145C07 */ + 1.39033110312309984516e-17, /* atan(1.5)lo 0x3C700788, 0x7AF0CBBD */ + 6.12323399573676603587e-17, /* atan(inf)lo 0x3C91A626, 0x33145C07 */ +}; + +static const double aT[] = { + 3.33333333333329318027e-01, /* 0x3FD55555, 0x5555550D */ + -1.99999999998764832476e-01, /* 0xBFC99999, 0x9998EBC4 */ + 1.42857142725034663711e-01, /* 0x3FC24924, 0x920083FF */ + -1.11111104054623557880e-01, /* 0xBFBC71C6, 0xFE231671 */ + 9.09088713343650656196e-02, /* 0x3FB745CD, 0xC54C206E */ + -7.69187620504482999495e-02, /* 0xBFB3B0F2, 0xAF749A6D */ + 6.66107313738753120669e-02, /* 0x3FB10D66, 0xA0D03D51 */ + -5.83357013379057348645e-02, /* 0xBFADDE2D, 0x52DEFD9A */ + 4.97687799461593236017e-02, /* 0x3FA97B4B, 0x24760DEB */ + -3.65315727442169155270e-02, /* 0xBFA2B444, 0x2C6A6C2F */ + 1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */ +}; + + static const double +one = 1.0, +huge = 1.0e300; + +double +atan(double x) +{ + double w,s1,s2,z; + int32_t ix,hx,id; + + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x44100000) { /* if |x| >= 2^66 */ + uint32_t low; + GET_LOW_WORD(low,x); + if(ix>0x7ff00000|| + (ix==0x7ff00000&&(low!=0))) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+atanlo[3]; + else return -atanhi[3]-atanlo[3]; + } if (ix < 0x3fdc0000) { /* |x| < 0.4375 */ + if (ix < 0x3e200000) { /* |x| < 2^-29 */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabs(x); + if (ix < 0x3ff30000) { /* |x| < 1.1875 */ + if (ix < 0x3fe60000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = (2.0*x-one)/(2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x40038000) { /* |x| < 2.4375 */ + id = 2; x = (x-1.5)/(one+1.5*x); + } else { /* 2.4375 <= |x| < 2^66 */ + id = 3; x = -1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); + s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} diff --git a/src/math/s_atanf.c b/src/math/s_atanf.c new file mode 100644 index 00000000..03067e18 --- /dev/null +++ b/src/math/s_atanf.c @@ -0,0 +1,95 @@ +/* s_atanf.c -- float version of s_atan.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float atanhi[] = { + 4.6364760399e-01, /* atan(0.5)hi 0x3eed6338 */ + 7.8539812565e-01, /* atan(1.0)hi 0x3f490fda */ + 9.8279368877e-01, /* atan(1.5)hi 0x3f7b985e */ + 1.5707962513e+00, /* atan(inf)hi 0x3fc90fda */ +}; + +static const float atanlo[] = { + 5.0121582440e-09, /* atan(0.5)lo 0x31ac3769 */ + 3.7748947079e-08, /* atan(1.0)lo 0x33222168 */ + 3.4473217170e-08, /* atan(1.5)lo 0x33140fb4 */ + 7.5497894159e-08, /* atan(inf)lo 0x33a22168 */ +}; + +static const float aT[] = { + 3.3333334327e-01, /* 0x3eaaaaaa */ + -2.0000000298e-01, /* 0xbe4ccccd */ + 1.4285714924e-01, /* 0x3e124925 */ + -1.1111110449e-01, /* 0xbde38e38 */ + 9.0908870101e-02, /* 0x3dba2e6e */ + -7.6918758452e-02, /* 0xbd9d8795 */ + 6.6610731184e-02, /* 0x3d886b35 */ + -5.8335702866e-02, /* 0xbd6ef16b */ + 4.9768779427e-02, /* 0x3d4bda59 */ + -3.6531571299e-02, /* 0xbd15a221 */ + 1.6285819933e-02, /* 0x3c8569d7 */ +}; + + static const float +one = 1.0, +huge = 1.0e30; + +float +atanf(float x) +{ + float w,s1,s2,z; + int32_t ix,hx,id; + + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x50800000) { /* if |x| >= 2^34 */ + if(ix>0x7f800000) + return x+x; /* NaN */ + if(hx>0) return atanhi[3]+atanlo[3]; + else return -atanhi[3]-atanlo[3]; + } if (ix < 0x3ee00000) { /* |x| < 0.4375 */ + if (ix < 0x31000000) { /* |x| < 2^-29 */ + if(huge+x>one) return x; /* raise inexact */ + } + id = -1; + } else { + x = fabsf(x); + if (ix < 0x3f980000) { /* |x| < 1.1875 */ + if (ix < 0x3f300000) { /* 7/16 <=|x|<11/16 */ + id = 0; x = ((float)2.0*x-one)/((float)2.0+x); + } else { /* 11/16<=|x|< 19/16 */ + id = 1; x = (x-one)/(x+one); + } + } else { + if (ix < 0x401c0000) { /* |x| < 2.4375 */ + id = 2; x = (x-(float)1.5)/(one+(float)1.5*x); + } else { /* 2.4375 <= |x| < 2^66 */ + id = 3; x = -(float)1.0/x; + } + }} + /* end of argument reduction */ + z = x*x; + w = z*z; + /* break sum from i=0 to 10 aT[i]z**(i+1) into odd and even poly */ + s1 = z*(aT[0]+w*(aT[2]+w*(aT[4]+w*(aT[6]+w*(aT[8]+w*aT[10]))))); + s2 = w*(aT[1]+w*(aT[3]+w*(aT[5]+w*(aT[7]+w*aT[9])))); + if (id<0) return x - x*(s1+s2); + else { + z = atanhi[id] - ((x*(s1+s2) - atanlo[id]) - x); + return (hx<0)? -z:z; + } +} diff --git a/src/math/s_cbrt.c b/src/math/s_cbrt.c new file mode 100644 index 00000000..8adcb191 --- /dev/null +++ b/src/math/s_cbrt.c @@ -0,0 +1,77 @@ +/* @(#)s_cbrt.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +/* cbrt(x) + * Return cube root of x + */ +static const uint32_t + B1 = 715094163, /* B1 = (682-0.03306235651)*2**20 */ + B2 = 696219795; /* B2 = (664-0.03306235651)*2**20 */ + +static const double +C = 5.42857142857142815906e-01, /* 19/35 = 0x3FE15F15, 0xF15F15F1 */ +D = -7.05306122448979611050e-01, /* -864/1225 = 0xBFE691DE, 0x2532C834 */ +E = 1.41428571428571436819e+00, /* 99/70 = 0x3FF6A0EA, 0x0EA0EA0F */ +F = 1.60714285714285720630e+00, /* 45/28 = 0x3FF9B6DB, 0x6DB6DB6E */ +G = 3.57142857142857150787e-01; /* 5/14 = 0x3FD6DB6D, 0xB6DB6DB7 */ + +double +cbrt(double x) +{ + int32_t hx; + double r,s,t=0.0,w; + uint32_t sign; + uint32_t high,low; + + GET_HIGH_WORD(hx,x); + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7ff00000) return(x+x); /* cbrt(NaN,INF) is itself */ + GET_LOW_WORD(low,x); + if((hx|low)==0) + return(x); /* cbrt(0) is itself */ + + SET_HIGH_WORD(x,hx); /* x <- |x| */ + /* rough cbrt to 5 bits */ + if(hx<0x00100000) /* subnormal number */ + {SET_HIGH_WORD(t,0x43500000); /* set t= 2**54 */ + t*=x; GET_HIGH_WORD(high,t); SET_HIGH_WORD(t,high/3+B2); + } + else + SET_HIGH_WORD(t,hx/3+B1); + + + /* new cbrt to 23 bits, may be implemented in single precision */ + r=t*t/x; + s=C+r*t; + t*=G+F/(s+E+D/s); + + /* chopped to 20 bits and make it larger than cbrt(x) */ + GET_HIGH_WORD(high,t); + INSERT_WORDS(t,high+0x00000001,0); + + + /* one step newton iteration to 53 bits with error less than 0.667 ulps */ + s=t*t; /* t*t is exact */ + r=x/s; + w=t+t; + r=(r-t)/(w+r); /* r-s is exact */ + t=t+t*r; + + /* retore the sign bit */ + GET_HIGH_WORD(high,t); + SET_HIGH_WORD(t,high|sign); + return(t); +} diff --git a/src/math/s_cbrtf.c b/src/math/s_cbrtf.c new file mode 100644 index 00000000..e7b46de7 --- /dev/null +++ b/src/math/s_cbrtf.c @@ -0,0 +1,67 @@ +/* s_cbrtf.c -- float version of s_cbrt.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +/* cbrtf(x) + * Return cube root of x + */ +static const unsigned + B1 = 709958130, /* B1 = (84+2/3-0.03306235651)*2**23 */ + B2 = 642849266; /* B2 = (76+2/3-0.03306235651)*2**23 */ + +static const float +C = 5.4285717010e-01, /* 19/35 = 0x3f0af8b0 */ +D = -7.0530611277e-01, /* -864/1225 = 0xbf348ef1 */ +E = 1.4142856598e+00, /* 99/70 = 0x3fb50750 */ +F = 1.6071428061e+00, /* 45/28 = 0x3fcdb6db */ +G = 3.5714286566e-01; /* 5/14 = 0x3eb6db6e */ + +float +cbrtf(float x) +{ + float r,s,t; + int32_t hx; + uint32_t sign; + uint32_t high; + + GET_FLOAT_WORD(hx,x); + sign=hx&0x80000000; /* sign= sign(x) */ + hx ^=sign; + if(hx>=0x7f800000) return(x+x); /* cbrt(NaN,INF) is itself */ + if(hx==0) + return(x); /* cbrt(0) is itself */ + + SET_FLOAT_WORD(x,hx); /* x <- |x| */ + /* rough cbrt to 5 bits */ + if(hx<0x00800000) /* subnormal number */ + {SET_FLOAT_WORD(t,0x4b800000); /* set t= 2**24 */ + t*=x; GET_FLOAT_WORD(high,t); SET_FLOAT_WORD(t,high/3+B2); + } + else + SET_FLOAT_WORD(t,hx/3+B1); + + + /* new cbrt to 23 bits */ + r=t*t/x; + s=C+r*t; + t*=G+F/(s+E+D/s); + + /* retore the sign bit */ + GET_FLOAT_WORD(high,t); + SET_FLOAT_WORD(t,high|sign); + return(t); +} diff --git a/src/math/s_ceil.c b/src/math/s_ceil.c new file mode 100644 index 00000000..1670cade --- /dev/null +++ b/src/math/s_ceil.c @@ -0,0 +1,68 @@ +/* @(#)s_ceil.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * ceil(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to ceil(x). + */ + +#include <math.h> +#include "math_private.h" + +static const double huge = 1.0e300; + +double +ceil(double x) +{ + int32_t i0,i1,j0; + uint32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;i1=0;} + else if((i0|i1)!=0) { i0=0x3ff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0>0) { + if(j0==20) i0+=1; + else { + j = i1 + (1<<(52-j0)); + if(j<i1) i0+=1; /* got a carry */ + i1 = j; + } + } + i1 &= (~i); + } + } + INSERT_WORDS(x,i0,i1); + return x; +} diff --git a/src/math/s_ceilf.c b/src/math/s_ceilf.c new file mode 100644 index 00000000..3615041f --- /dev/null +++ b/src/math/s_ceilf.c @@ -0,0 +1,49 @@ +/* s_ceilf.c -- float version of s_ceil.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float huge = 1.0e30; + +float +ceilf(float x) +{ + int32_t i0,j0; + uint32_t i; + + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0<0) {i0=0x80000000;} + else if(i0!=0) { i0=0x3f800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>(float)0.0) { /* raise inexact flag */ + if(i0>0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/src/math/s_copysign.c b/src/math/s_copysign.c new file mode 100644 index 00000000..59d3877c --- /dev/null +++ b/src/math/s_copysign.c @@ -0,0 +1,30 @@ +/* @(#)s_copysign.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * copysign(double x, double y) + * copysign(x,y) returns a value with the magnitude of x and + * with the sign bit of y. + */ + +#include <math.h> +#include "math_private.h" + +double +copysign(double x, double y) +{ + uint32_t hx,hy; + GET_HIGH_WORD(hx,x); + GET_HIGH_WORD(hy,y); + SET_HIGH_WORD(x,(hx&0x7fffffff)|(hy&0x80000000)); + return x; +} diff --git a/src/math/s_copysignf.c b/src/math/s_copysignf.c new file mode 100644 index 00000000..d650e8e5 --- /dev/null +++ b/src/math/s_copysignf.c @@ -0,0 +1,33 @@ +/* s_copysignf.c -- float version of s_copysign.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * copysignf(float x, float y) + * copysignf(x,y) returns a value with the magnitude of x and + * with the sign bit of y. + */ + +#include <math.h> +#include "math_private.h" + +float +copysignf(float x, float y) +{ + uint32_t ix,iy; + GET_FLOAT_WORD(ix,x); + GET_FLOAT_WORD(iy,y); + SET_FLOAT_WORD(x,(ix&0x7fffffff)|(iy&0x80000000)); + return x; +} diff --git a/src/math/s_cos.c b/src/math/s_cos.c new file mode 100644 index 00000000..1893ab13 --- /dev/null +++ b/src/math/s_cos.c @@ -0,0 +1,74 @@ +/* @(#)s_cos.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* cos(x) + * Return cosine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cosine function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include <math.h> +#include "math_private.h" + +double +cos(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_cos(x,z); + + /* cos(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_cos(y[0],y[1]); + case 1: return -__kernel_sin(y[0],y[1],1); + case 2: return -__kernel_cos(y[0],y[1]); + default: + return __kernel_sin(y[0],y[1],1); + } + } +} diff --git a/src/math/s_cosf.c b/src/math/s_cosf.c new file mode 100644 index 00000000..14b8e98b --- /dev/null +++ b/src/math/s_cosf.c @@ -0,0 +1,47 @@ +/* s_cosf.c -- float version of s_cos.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one=1.0; + +float +cosf(float x) +{ + float y[2],z=0.0; + int32_t n,ix; + + GET_FLOAT_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3f490fd8) return __kernel_cosf(x,z); + + /* cos(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,y); + switch(n&3) { + case 0: return __kernel_cosf(y[0],y[1]); + case 1: return -__kernel_sinf(y[0],y[1],1); + case 2: return -__kernel_cosf(y[0],y[1]); + default: + return __kernel_sinf(y[0],y[1],1); + } + } +} diff --git a/src/math/s_erf.c b/src/math/s_erf.c new file mode 100644 index 00000000..e321feea --- /dev/null +++ b/src/math/s_erf.c @@ -0,0 +1,298 @@ +/* @(#)s_erf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* double erf(double x) + * double erfc(double x) + * x + * 2 |\ + * erf(x) = --------- | exp(-t*t)dt + * sqrt(pi) \| + * 0 + * + * erfc(x) = 1-erf(x) + * Note that + * erf(-x) = -erf(x) + * erfc(-x) = 2 - erfc(x) + * + * Method: + * 1. For |x| in [0, 0.84375] + * erf(x) = x + x*R(x^2) + * erfc(x) = 1 - erf(x) if x in [-.84375,0.25] + * = 0.5 + ((0.5-x)-x*R) if x in [0.25,0.84375] + * where R = P/Q where P is an odd poly of degree 8 and + * Q is an odd poly of degree 10. + * -57.90 + * | R - (erf(x)-x)/x | <= 2 + * + * + * Remark. The formula is derived by noting + * erf(x) = (2/sqrt(pi))*(x - x^3/3 + x^5/10 - x^7/42 + ....) + * and that + * 2/sqrt(pi) = 1.128379167095512573896158903121545171688 + * is close to one. The interval is chosen because the fix + * point of erf(x) is near 0.6174 (i.e., erf(x)=x when x is + * near 0.6174), and by some experiment, 0.84375 is chosen to + * guarantee the error is less than one ulp for erf. + * + * 2. For |x| in [0.84375,1.25], let s = |x| - 1, and + * c = 0.84506291151 rounded to single (24 bits) + * erf(x) = sign(x) * (c + P1(s)/Q1(s)) + * erfc(x) = (1-c) - P1(s)/Q1(s) if x > 0 + * 1+(c+P1(s)/Q1(s)) if x < 0 + * |P1/Q1 - (erf(|x|)-c)| <= 2**-59.06 + * Remark: here we use the taylor series expansion at x=1. + * erf(1+s) = erf(1) + s*Poly(s) + * = 0.845.. + P1(s)/Q1(s) + * That is, we use rational approximation to approximate + * erf(1+s) - (c = (single)0.84506291151) + * Note that |P1/Q1|< 0.078 for x in [0.84375,1.25] + * where + * P1(s) = degree 6 poly in s + * Q1(s) = degree 6 poly in s + * + * 3. For x in [1.25,1/0.35(~2.857143)], + * erfc(x) = (1/x)*exp(-x*x-0.5625+R1/S1) + * erf(x) = 1 - erfc(x) + * where + * R1(z) = degree 7 poly in z, (z=1/x^2) + * S1(z) = degree 8 poly in z + * + * 4. For x in [1/0.35,28] + * erfc(x) = (1/x)*exp(-x*x-0.5625+R2/S2) if x > 0 + * = 2.0 - (1/x)*exp(-x*x-0.5625+R2/S2) if -6<x<0 + * = 2.0 - tiny (if x <= -6) + * erf(x) = sign(x)*(1.0 - erfc(x)) if x < 6, else + * erf(x) = sign(x)*(1.0 - tiny) + * where + * R2(z) = degree 6 poly in z, (z=1/x^2) + * S2(z) = degree 7 poly in z + * + * Note1: + * To compute exp(-x*x-0.5625+R/S), let s be a single + * precision number and s := x; then + * -x*x = -s*s + (s-x)*(s+x) + * exp(-x*x-0.5626+R/S) = + * exp(-s*s-0.5625)*exp((s-x)*(s+x)+R/S); + * Note2: + * Here 4 and 5 make use of the asymptotic series + * exp(-x*x) + * erfc(x) ~ ---------- * ( 1 + Poly(1/x^2) ) + * x*sqrt(pi) + * We use rational approximation to approximate + * g(s)=f(1/x^2) = log(erfc(x)*x) - x*x + 0.5625 + * Here is the error bound for R1/S1 and R2/S2 + * |R1/S1 - f(x)| < 2**(-62.57) + * |R2/S2 - f(x)| < 2**(-61.52) + * + * 5. For inf > x >= 28 + * erf(x) = sign(x) *(1 - tiny) (raise inexact) + * erfc(x) = tiny*tiny (raise underflow) if x > 0 + * = 2 - tiny if x<0 + * + * 7. Special case: + * erf(0) = 0, erf(inf) = 1, erf(-inf) = -1, + * erfc(0) = 1, erfc(inf) = 0, erfc(-inf) = 2, + * erfc/erf(NaN) is NaN + */ + + +#include <math.h> +#include "math_private.h" + +static const double +tiny = 1e-300, +half= 5.00000000000000000000e-01, /* 0x3FE00000, 0x00000000 */ +one = 1.00000000000000000000e+00, /* 0x3FF00000, 0x00000000 */ +two = 2.00000000000000000000e+00, /* 0x40000000, 0x00000000 */ + /* c = (float)0.84506291151 */ +erx = 8.45062911510467529297e-01, /* 0x3FEB0AC1, 0x60000000 */ +/* + * Coefficients for approximation to erf on [0,0.84375] + */ +efx = 1.28379167095512586316e-01, /* 0x3FC06EBA, 0x8214DB69 */ +efx8= 1.02703333676410069053e+00, /* 0x3FF06EBA, 0x8214DB69 */ +pp0 = 1.28379167095512558561e-01, /* 0x3FC06EBA, 0x8214DB68 */ +pp1 = -3.25042107247001499370e-01, /* 0xBFD4CD7D, 0x691CB913 */ +pp2 = -2.84817495755985104766e-02, /* 0xBF9D2A51, 0xDBD7194F */ +pp3 = -5.77027029648944159157e-03, /* 0xBF77A291, 0x236668E4 */ +pp4 = -2.37630166566501626084e-05, /* 0xBEF8EAD6, 0x120016AC */ +qq1 = 3.97917223959155352819e-01, /* 0x3FD97779, 0xCDDADC09 */ +qq2 = 6.50222499887672944485e-02, /* 0x3FB0A54C, 0x5536CEBA */ +qq3 = 5.08130628187576562776e-03, /* 0x3F74D022, 0xC4D36B0F */ +qq4 = 1.32494738004321644526e-04, /* 0x3F215DC9, 0x221C1A10 */ +qq5 = -3.96022827877536812320e-06, /* 0xBED09C43, 0x42A26120 */ +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +pa0 = -2.36211856075265944077e-03, /* 0xBF6359B8, 0xBEF77538 */ +pa1 = 4.14856118683748331666e-01, /* 0x3FDA8D00, 0xAD92B34D */ +pa2 = -3.72207876035701323847e-01, /* 0xBFD7D240, 0xFBB8C3F1 */ +pa3 = 3.18346619901161753674e-01, /* 0x3FD45FCA, 0x805120E4 */ +pa4 = -1.10894694282396677476e-01, /* 0xBFBC6398, 0x3D3E28EC */ +pa5 = 3.54783043256182359371e-02, /* 0x3FA22A36, 0x599795EB */ +pa6 = -2.16637559486879084300e-03, /* 0xBF61BF38, 0x0A96073F */ +qa1 = 1.06420880400844228286e-01, /* 0x3FBB3E66, 0x18EEE323 */ +qa2 = 5.40397917702171048937e-01, /* 0x3FE14AF0, 0x92EB6F33 */ +qa3 = 7.18286544141962662868e-02, /* 0x3FB2635C, 0xD99FE9A7 */ +qa4 = 1.26171219808761642112e-01, /* 0x3FC02660, 0xE763351F */ +qa5 = 1.36370839120290507362e-02, /* 0x3F8BEDC2, 0x6B51DD1C */ +qa6 = 1.19844998467991074170e-02, /* 0x3F888B54, 0x5735151D */ +/* + * Coefficients for approximation to erfc in [1.25,1/0.35] + */ +ra0 = -9.86494403484714822705e-03, /* 0xBF843412, 0x600D6435 */ +ra1 = -6.93858572707181764372e-01, /* 0xBFE63416, 0xE4BA7360 */ +ra2 = -1.05586262253232909814e+01, /* 0xC0251E04, 0x41B0E726 */ +ra3 = -6.23753324503260060396e+01, /* 0xC04F300A, 0xE4CBA38D */ +ra4 = -1.62396669462573470355e+02, /* 0xC0644CB1, 0x84282266 */ +ra5 = -1.84605092906711035994e+02, /* 0xC067135C, 0xEBCCABB2 */ +ra6 = -8.12874355063065934246e+01, /* 0xC0545265, 0x57E4D2F2 */ +ra7 = -9.81432934416914548592e+00, /* 0xC023A0EF, 0xC69AC25C */ +sa1 = 1.96512716674392571292e+01, /* 0x4033A6B9, 0xBD707687 */ +sa2 = 1.37657754143519042600e+02, /* 0x4061350C, 0x526AE721 */ +sa3 = 4.34565877475229228821e+02, /* 0x407B290D, 0xD58A1A71 */ +sa4 = 6.45387271733267880336e+02, /* 0x40842B19, 0x21EC2868 */ +sa5 = 4.29008140027567833386e+02, /* 0x407AD021, 0x57700314 */ +sa6 = 1.08635005541779435134e+02, /* 0x405B28A3, 0xEE48AE2C */ +sa7 = 6.57024977031928170135e+00, /* 0x401A47EF, 0x8E484A93 */ +sa8 = -6.04244152148580987438e-02, /* 0xBFAEEFF2, 0xEE749A62 */ +/* + * Coefficients for approximation to erfc in [1/.35,28] + */ +rb0 = -9.86494292470009928597e-03, /* 0xBF843412, 0x39E86F4A */ +rb1 = -7.99283237680523006574e-01, /* 0xBFE993BA, 0x70C285DE */ +rb2 = -1.77579549177547519889e+01, /* 0xC031C209, 0x555F995A */ +rb3 = -1.60636384855821916062e+02, /* 0xC064145D, 0x43C5ED98 */ +rb4 = -6.37566443368389627722e+02, /* 0xC083EC88, 0x1375F228 */ +rb5 = -1.02509513161107724954e+03, /* 0xC0900461, 0x6A2E5992 */ +rb6 = -4.83519191608651397019e+02, /* 0xC07E384E, 0x9BDC383F */ +sb1 = 3.03380607434824582924e+01, /* 0x403E568B, 0x261D5190 */ +sb2 = 3.25792512996573918826e+02, /* 0x40745CAE, 0x221B9F0A */ +sb3 = 1.53672958608443695994e+03, /* 0x409802EB, 0x189D5118 */ +sb4 = 3.19985821950859553908e+03, /* 0x40A8FFB7, 0x688C246A */ +sb5 = 2.55305040643316442583e+03, /* 0x40A3F219, 0xCEDF3BE6 */ +sb6 = 4.74528541206955367215e+02, /* 0x407DA874, 0xE79FE763 */ +sb7 = -2.24409524465858183362e+01; /* 0xC03670E2, 0x42712D62 */ + +double +erf(double x) +{ + int32_t hx,ix,i; + double R,S,P,Q,s,y,z,r; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erf(nan)=nan */ + i = ((uint32_t)hx>>31)<<1; + return (double)(1-i)+one/x; /* erf(+-inf)=+-1 */ + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3e300000) { /* |x|<2**-28 */ + if (ix < 0x00800000) + return 0.125*(8.0*x+efx8*x); /*avoid underflow */ + return x + efx*x; + } + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + return x + x*y; + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) return erx + P/Q; else return -erx - P/Q; + } + if (ix >= 0x40180000) { /* inf>|x|>=6 */ + if(hx>=0) return one-tiny; else return tiny-one; + } + x = fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6E) { /* |x| < 1/0.35 */ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/0.35 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + SET_LOW_WORD(z,0); + r = exp(-z*z-0.5625)*exp((z-x)*(z+x)+R/S); + if(hx>=0) return one-r/x; else return r/x-one; +} + +double +erfc(double x) +{ + int32_t hx,ix; + double R,S,P,Q,s,y,z,r; + GET_HIGH_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7ff00000) { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (double)(((uint32_t)hx>>31)<<1)+one/x; + } + + if(ix < 0x3feb0000) { /* |x|<0.84375 */ + if(ix < 0x3c700000) /* |x|<2**-56 */ + return one-x; + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + if(hx < 0x3fd00000) { /* x<1/4 */ + return one-(x+x*y); + } else { + r = x*y; + r += (x-half); + return half - r ; + } + } + if(ix < 0x3ff40000) { /* 0.84375 <= |x| < 1.25 */ + s = fabs(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) { + z = one-erx; return z - P/Q; + } else { + z = erx+P/Q; return one+z; + } + } + if (ix < 0x403c0000) { /* |x|<28 */ + x = fabs(x); + s = one/(x*x); + if(ix< 0x4006DB6D) { /* |x| < 1/.35 ~ 2.857143*/ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/.35 ~ 2.857143 */ + if(hx<0&&ix>=0x40180000) return two-tiny;/* x < -6 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + z = x; + SET_LOW_WORD(z,0); + r = exp(-z*z-0.5625)* + exp((z-x)*(z+x)+R/S); + if(hx>0) return r/x; else return two-r/x; + } else { + if(hx>0) return tiny*tiny; else return two-tiny; + } +} diff --git a/src/math/s_erff.c b/src/math/s_erff.c new file mode 100644 index 00000000..28e2f7b3 --- /dev/null +++ b/src/math/s_erff.c @@ -0,0 +1,207 @@ +/* s_erff.c -- float version of s_erf.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +tiny = 1e-30, +half= 5.0000000000e-01, /* 0x3F000000 */ +one = 1.0000000000e+00, /* 0x3F800000 */ +two = 2.0000000000e+00, /* 0x40000000 */ + /* c = (subfloat)0.84506291151 */ +erx = 8.4506291151e-01, /* 0x3f58560b */ +/* + * Coefficients for approximation to erf on [0,0.84375] + */ +efx = 1.2837916613e-01, /* 0x3e0375d4 */ +efx8= 1.0270333290e+00, /* 0x3f8375d4 */ +pp0 = 1.2837916613e-01, /* 0x3e0375d4 */ +pp1 = -3.2504209876e-01, /* 0xbea66beb */ +pp2 = -2.8481749818e-02, /* 0xbce9528f */ +pp3 = -5.7702702470e-03, /* 0xbbbd1489 */ +pp4 = -2.3763017452e-05, /* 0xb7c756b1 */ +qq1 = 3.9791721106e-01, /* 0x3ecbbbce */ +qq2 = 6.5022252500e-02, /* 0x3d852a63 */ +qq3 = 5.0813062117e-03, /* 0x3ba68116 */ +qq4 = 1.3249473704e-04, /* 0x390aee49 */ +qq5 = -3.9602282413e-06, /* 0xb684e21a */ +/* + * Coefficients for approximation to erf in [0.84375,1.25] + */ +pa0 = -2.3621185683e-03, /* 0xbb1acdc6 */ +pa1 = 4.1485610604e-01, /* 0x3ed46805 */ +pa2 = -3.7220788002e-01, /* 0xbebe9208 */ +pa3 = 3.1834661961e-01, /* 0x3ea2fe54 */ +pa4 = -1.1089469492e-01, /* 0xbde31cc2 */ +pa5 = 3.5478305072e-02, /* 0x3d1151b3 */ +pa6 = -2.1663755178e-03, /* 0xbb0df9c0 */ +qa1 = 1.0642088205e-01, /* 0x3dd9f331 */ +qa2 = 5.4039794207e-01, /* 0x3f0a5785 */ +qa3 = 7.1828655899e-02, /* 0x3d931ae7 */ +qa4 = 1.2617121637e-01, /* 0x3e013307 */ +qa5 = 1.3637083583e-02, /* 0x3c5f6e13 */ +qa6 = 1.1984500103e-02, /* 0x3c445aa3 */ +/* + * Coefficients for approximation to erfc in [1.25,1/0.35] + */ +ra0 = -9.8649440333e-03, /* 0xbc21a093 */ +ra1 = -6.9385856390e-01, /* 0xbf31a0b7 */ +ra2 = -1.0558626175e+01, /* 0xc128f022 */ +ra3 = -6.2375331879e+01, /* 0xc2798057 */ +ra4 = -1.6239666748e+02, /* 0xc322658c */ +ra5 = -1.8460508728e+02, /* 0xc3389ae7 */ +ra6 = -8.1287437439e+01, /* 0xc2a2932b */ +ra7 = -9.8143291473e+00, /* 0xc11d077e */ +sa1 = 1.9651271820e+01, /* 0x419d35ce */ +sa2 = 1.3765776062e+02, /* 0x4309a863 */ +sa3 = 4.3456588745e+02, /* 0x43d9486f */ +sa4 = 6.4538726807e+02, /* 0x442158c9 */ +sa5 = 4.2900814819e+02, /* 0x43d6810b */ +sa6 = 1.0863500214e+02, /* 0x42d9451f */ +sa7 = 6.5702495575e+00, /* 0x40d23f7c */ +sa8 = -6.0424413532e-02, /* 0xbd777f97 */ +/* + * Coefficients for approximation to erfc in [1/.35,28] + */ +rb0 = -9.8649431020e-03, /* 0xbc21a092 */ +rb1 = -7.9928326607e-01, /* 0xbf4c9dd4 */ +rb2 = -1.7757955551e+01, /* 0xc18e104b */ +rb3 = -1.6063638306e+02, /* 0xc320a2ea */ +rb4 = -6.3756646729e+02, /* 0xc41f6441 */ +rb5 = -1.0250950928e+03, /* 0xc480230b */ +rb6 = -4.8351919556e+02, /* 0xc3f1c275 */ +sb1 = 3.0338060379e+01, /* 0x41f2b459 */ +sb2 = 3.2579251099e+02, /* 0x43a2e571 */ +sb3 = 1.5367296143e+03, /* 0x44c01759 */ +sb4 = 3.1998581543e+03, /* 0x4547fdbb */ +sb5 = 2.5530502930e+03, /* 0x451f90ce */ +sb6 = 4.7452853394e+02, /* 0x43ed43a7 */ +sb7 = -2.2440952301e+01; /* 0xc1b38712 */ + +float +erff(float x) +{ + int32_t hx,ix,i; + float R,S,P,Q,s,y,z,r; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) { /* erf(nan)=nan */ + i = ((uint32_t)hx>>31)<<1; + return (float)(1-i)+one/x; /* erf(+-inf)=+-1 */ + } + + if(ix < 0x3f580000) { /* |x|<0.84375 */ + if(ix < 0x31800000) { /* |x|<2**-28 */ + if (ix < 0x04000000) + /*avoid underflow */ + return (float)0.125*((float)8.0*x+efx8*x); + return x + efx*x; + } + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + return x + x*y; + } + if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ + s = fabsf(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) return erx + P/Q; else return -erx - P/Q; + } + if (ix >= 0x40c00000) { /* inf>|x|>=6 */ + if(hx>=0) return one-tiny; else return tiny-one; + } + x = fabsf(x); + s = one/(x*x); + if(ix< 0x4036DB6E) { /* |x| < 1/0.35 */ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/0.35 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(z,ix&0xfffff000); + r = expf(-z*z-(float)0.5625)*expf((z-x)*(z+x)+R/S); + if(hx>=0) return one-r/x; else return r/x-one; +} + +float +erfcf(float x) +{ + int32_t hx,ix; + float R,S,P,Q,s,y,z,r; + GET_FLOAT_WORD(hx,x); + ix = hx&0x7fffffff; + if(ix>=0x7f800000) { /* erfc(nan)=nan */ + /* erfc(+-inf)=0,2 */ + return (float)(((uint32_t)hx>>31)<<1)+one/x; + } + + if(ix < 0x3f580000) { /* |x|<0.84375 */ + if(ix < 0x23800000) /* |x|<2**-56 */ + return one-x; + z = x*x; + r = pp0+z*(pp1+z*(pp2+z*(pp3+z*pp4))); + s = one+z*(qq1+z*(qq2+z*(qq3+z*(qq4+z*qq5)))); + y = r/s; + if(hx < 0x3e800000) { /* x<1/4 */ + return one-(x+x*y); + } else { + r = x*y; + r += (x-half); + return half - r ; + } + } + if(ix < 0x3fa00000) { /* 0.84375 <= |x| < 1.25 */ + s = fabsf(x)-one; + P = pa0+s*(pa1+s*(pa2+s*(pa3+s*(pa4+s*(pa5+s*pa6))))); + Q = one+s*(qa1+s*(qa2+s*(qa3+s*(qa4+s*(qa5+s*qa6))))); + if(hx>=0) { + z = one-erx; return z - P/Q; + } else { + z = erx+P/Q; return one+z; + } + } + if (ix < 0x41e00000) { /* |x|<28 */ + x = fabsf(x); + s = one/(x*x); + if(ix< 0x4036DB6D) { /* |x| < 1/.35 ~ 2.857143*/ + R=ra0+s*(ra1+s*(ra2+s*(ra3+s*(ra4+s*( + ra5+s*(ra6+s*ra7)))))); + S=one+s*(sa1+s*(sa2+s*(sa3+s*(sa4+s*( + sa5+s*(sa6+s*(sa7+s*sa8))))))); + } else { /* |x| >= 1/.35 ~ 2.857143 */ + if(hx<0&&ix>=0x40c00000) return two-tiny;/* x < -6 */ + R=rb0+s*(rb1+s*(rb2+s*(rb3+s*(rb4+s*( + rb5+s*rb6))))); + S=one+s*(sb1+s*(sb2+s*(sb3+s*(sb4+s*( + sb5+s*(sb6+s*sb7)))))); + } + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(z,ix&0xfffff000); + r = expf(-z*z-(float)0.5625)* + expf((z-x)*(z+x)+R/S); + if(hx>0) return r/x; else return two-r/x; + } else { + if(hx>0) return tiny*tiny; else return two-tiny; + } +} diff --git a/src/math/s_expm1.c b/src/math/s_expm1.c new file mode 100644 index 00000000..6f1f6675 --- /dev/null +++ b/src/math/s_expm1.c @@ -0,0 +1,217 @@ +/* @(#)s_expm1.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* expm1(x) + * Returns exp(x)-1, the exponential of x minus 1. + * + * Method + * 1. Argument reduction: + * Given x, find r and integer k such that + * + * x = k*ln2 + r, |r| <= 0.5*ln2 ~ 0.34658 + * + * Here a correction term c will be computed to compensate + * the error in r when rounded to a floating-point number. + * + * 2. Approximating expm1(r) by a special rational function on + * the interval [0,0.34658]: + * Since + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 - r^4/360 + ... + * we define R1(r*r) by + * r*(exp(r)+1)/(exp(r)-1) = 2+ r^2/6 * R1(r*r) + * That is, + * R1(r**2) = 6/r *((exp(r)+1)/(exp(r)-1) - 2/r) + * = 6/r * ( 1 + 2.0*(1/(exp(r)-1) - 1/r)) + * = 1 - r^2/60 + r^4/2520 - r^6/100800 + ... + * We use a special Reme algorithm on [0,0.347] to generate + * a polynomial of degree 5 in r*r to approximate R1. The + * maximum error of this polynomial approximation is bounded + * by 2**-61. In other words, + * R1(z) ~ 1.0 + Q1*z + Q2*z**2 + Q3*z**3 + Q4*z**4 + Q5*z**5 + * where Q1 = -1.6666666666666567384E-2, + * Q2 = 3.9682539681370365873E-4, + * Q3 = -9.9206344733435987357E-6, + * Q4 = 2.5051361420808517002E-7, + * Q5 = -6.2843505682382617102E-9; + * (where z=r*r, and the values of Q1 to Q5 are listed below) + * with error bounded by + * | 5 | -61 + * | 1.0+Q1*z+...+Q5*z - R1(z) | <= 2 + * | | + * + * expm1(r) = exp(r)-1 is then computed by the following + * specific way which minimize the accumulation rounding error: + * 2 3 + * r r [ 3 - (R1 + R1*r/2) ] + * expm1(r) = r + --- + --- * [--------------------] + * 2 2 [ 6 - r*(3 - R1*r/2) ] + * + * To compensate the error in the argument reduction, we use + * expm1(r+c) = expm1(r) + c + expm1(r)*c + * ~ expm1(r) + c + r*c + * Thus c+r*c will be added in as the correction terms for + * expm1(r+c). Now rearrange the term to avoid optimization + * screw up: + * ( 2 2 ) + * ({ ( r [ R1 - (3 - R1*r/2) ] ) } r ) + * expm1(r+c)~r - ({r*(--- * [--------------------]-c)-c} - --- ) + * ({ ( 2 [ 6 - r*(3 - R1*r/2) ] ) } 2 ) + * ( ) + * + * = r - E + * 3. Scale back to obtain expm1(x): + * From step 1, we have + * expm1(x) = either 2^k*[expm1(r)+1] - 1 + * = or 2^k*[expm1(r) + (1-2^-k)] + * 4. Implementation notes: + * (A). To save one multiplication, we scale the coefficient Qi + * to Qi*2^i, and replace z by (x^2)/2. + * (B). To achieve maximum accuracy, we compute expm1(x) by + * (i) if x < -56*ln2, return -1.0, (raise inexact if x!=inf) + * (ii) if k=0, return r-E + * (iii) if k=-1, return 0.5*(r-E)-0.5 + * (iv) if k=1 if r < -0.25, return 2*((r+0.5)- E) + * else return 1.0+2.0*(r-E); + * (v) if (k<-2||k>56) return 2^k(1-(E-r)) - 1 (or exp(x)-1) + * (vi) if k <= 20, return 2^k((1-2^-k)-(E-r)), else + * (vii) return 2^k(1-((E+2^-k)-r)) + * + * Special cases: + * expm1(INF) is INF, expm1(NaN) is NaN; + * expm1(-INF) is -1, and + * for finite argument, only expm1(0)=0 is exact. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Misc. info. + * For IEEE double + * if x > 7.09782712893383973096e+02 then expm1(x) overflow + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + */ + +#include <math.h> +#include "math_private.h" + +static const double +one = 1.0, +huge = 1.0e+300, +tiny = 1.0e-300, +o_threshold = 7.09782712893383973096e+02,/* 0x40862E42, 0xFEFA39EF */ +ln2_hi = 6.93147180369123816490e-01,/* 0x3fe62e42, 0xfee00000 */ +ln2_lo = 1.90821492927058770002e-10,/* 0x3dea39ef, 0x35793c76 */ +invln2 = 1.44269504088896338700e+00,/* 0x3ff71547, 0x652b82fe */ + /* scaled coefficients related to expm1 */ +Q1 = -3.33333333333331316428e-02, /* BFA11111 111110F4 */ +Q2 = 1.58730158725481460165e-03, /* 3F5A01A0 19FE5585 */ +Q3 = -7.93650757867487942473e-05, /* BF14CE19 9EAADBB7 */ +Q4 = 4.00821782732936239552e-06, /* 3ED0CFCA 86E65239 */ +Q5 = -2.01099218183624371326e-07; /* BE8AFDB7 6E09C32D */ + +double +expm1(double x) +{ + double y,hi,lo,c=0.0,t,e,hxs,hfx,r1; + int32_t k,xsb; + uint32_t hx; + + GET_HIGH_WORD(hx,x); + xsb = hx&0x80000000; /* sign bit of x */ + if(xsb==0) y=x; else y= -x; /* y = |x| */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out huge and non-finite argument */ + if(hx >= 0x4043687A) { /* if |x|>=56*ln2 */ + if(hx >= 0x40862E42) { /* if |x|>=709.78... */ + if(hx>=0x7ff00000) { + uint32_t low; + GET_LOW_WORD(low,x); + if(((hx&0xfffff)|low)!=0) + return x+x; /* NaN */ + else return (xsb==0)? x:-1.0;/* exp(+-inf)={inf,-1} */ + } + if(x > o_threshold) return huge*huge; /* overflow */ + } + if(xsb!=0) { /* x < -56*ln2, return -1.0 with inexact */ + if(x+tiny<0.0) /* raise inexact */ + return tiny-one; /* return -1 */ + } + } + + /* argument reduction */ + if(hx > 0x3fd62e42) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3FF0A2B2) { /* and |x| < 1.5 ln2 */ + if(xsb==0) + {hi = x - ln2_hi; lo = ln2_lo; k = 1;} + else + {hi = x + ln2_hi; lo = -ln2_lo; k = -1;} + } else { + k = invln2*x+((xsb==0)?0.5:-0.5); + t = k; + hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ + lo = t*ln2_lo; + } + x = hi - lo; + c = (hi-x)-lo; + } + else if(hx < 0x3c900000) { /* when |x|<2**-54, return x */ + t = huge+x; /* return x with inexact flags when x!=0 */ + return x - (t-(huge+x)); + } + else k = 0; + + /* x is now in primary range */ + hfx = 0.5*x; + hxs = x*hfx; + r1 = one+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))); + t = 3.0-r1*hfx; + e = hxs*((r1-t)/(6.0 - x*t)); + if(k==0) return x - (x*e-hxs); /* c is 0 */ + else { + e = (x*(e-c)-c); + e -= hxs; + if(k== -1) return 0.5*(x-e)-0.5; + if(k==1) { + if(x < -0.25) return -2.0*(e-(x+0.5)); + else return one+2.0*(x-e); + } + if (k <= -2 || k>56) { /* suffice to return exp(x)-1 */ + uint32_t high; + y = one-(e-x); + GET_HIGH_WORD(high,y); + SET_HIGH_WORD(y,high+(k<<20)); /* add k to y's exponent */ + return y-one; + } + t = one; + if(k<20) { + uint32_t high; + SET_HIGH_WORD(t,0x3ff00000 - (0x200000>>k)); /* t=1-2^-k */ + y = t-(e-x); + GET_HIGH_WORD(high,y); + SET_HIGH_WORD(y,high+(k<<20)); /* add k to y's exponent */ + } else { + uint32_t high; + SET_HIGH_WORD(t,((0x3ff-k)<<20)); /* 2^-k */ + y = x-(e+t); + y += one; + GET_HIGH_WORD(high,y); + SET_HIGH_WORD(y,high+(k<<20)); /* add k to y's exponent */ + } + } + return y; +} diff --git a/src/math/s_expm1f.c b/src/math/s_expm1f.c new file mode 100644 index 00000000..b22cf0f9 --- /dev/null +++ b/src/math/s_expm1f.c @@ -0,0 +1,122 @@ +/* s_expm1f.c -- float version of s_expm1.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +one = 1.0, +huge = 1.0e+30, +tiny = 1.0e-30, +o_threshold = 8.8721679688e+01,/* 0x42b17180 */ +ln2_hi = 6.9313812256e-01,/* 0x3f317180 */ +ln2_lo = 9.0580006145e-06,/* 0x3717f7d1 */ +invln2 = 1.4426950216e+00,/* 0x3fb8aa3b */ + /* scaled coefficients related to expm1 */ +Q1 = -3.3333335072e-02, /* 0xbd088889 */ +Q2 = 1.5873016091e-03, /* 0x3ad00d01 */ +Q3 = -7.9365076090e-05, /* 0xb8a670cd */ +Q4 = 4.0082177293e-06, /* 0x36867e54 */ +Q5 = -2.0109921195e-07; /* 0xb457edbb */ + +float +expm1f(float x) +{ + float y,hi,lo,c=0.0,t,e,hxs,hfx,r1; + int32_t k,xsb; + uint32_t hx; + + GET_FLOAT_WORD(hx,x); + xsb = hx&0x80000000; /* sign bit of x */ + if(xsb==0) y=x; else y= -x; /* y = |x| */ + hx &= 0x7fffffff; /* high word of |x| */ + + /* filter out huge and non-finite argument */ + if(hx >= 0x4195b844) { /* if |x|>=27*ln2 */ + if(hx >= 0x42b17218) { /* if |x|>=88.721... */ + if(hx>0x7f800000) + return x+x; /* NaN */ + if(hx==0x7f800000) + return (xsb==0)? x:-1.0;/* exp(+-inf)={inf,-1} */ + if(x > o_threshold) return huge*huge; /* overflow */ + } + if(xsb!=0) { /* x < -27*ln2, return -1.0 with inexact */ + if(x+tiny<(float)0.0) /* raise inexact */ + return tiny-one; /* return -1 */ + } + } + + /* argument reduction */ + if(hx > 0x3eb17218) { /* if |x| > 0.5 ln2 */ + if(hx < 0x3F851592) { /* and |x| < 1.5 ln2 */ + if(xsb==0) + {hi = x - ln2_hi; lo = ln2_lo; k = 1;} + else + {hi = x + ln2_hi; lo = -ln2_lo; k = -1;} + } else { + k = invln2*x+((xsb==0)?(float)0.5:(float)-0.5); + t = k; + hi = x - t*ln2_hi; /* t*ln2_hi is exact here */ + lo = t*ln2_lo; + } + x = hi - lo; + c = (hi-x)-lo; + } + else if(hx < 0x33000000) { /* when |x|<2**-25, return x */ + t = huge+x; /* return x with inexact flags when x!=0 */ + return x - (t-(huge+x)); + } + else k = 0; + + /* x is now in primary range */ + hfx = (float)0.5*x; + hxs = x*hfx; + r1 = one+hxs*(Q1+hxs*(Q2+hxs*(Q3+hxs*(Q4+hxs*Q5)))); + t = (float)3.0-r1*hfx; + e = hxs*((r1-t)/((float)6.0 - x*t)); + if(k==0) return x - (x*e-hxs); /* c is 0 */ + else { + e = (x*(e-c)-c); + e -= hxs; + if(k== -1) return (float)0.5*(x-e)-(float)0.5; + if(k==1) { + if(x < (float)-0.25) return -(float)2.0*(e-(x+(float)0.5)); + else return one+(float)2.0*(x-e); + } + if (k <= -2 || k>56) { /* suffice to return exp(x)-1 */ + int32_t i; + y = one-(e-x); + GET_FLOAT_WORD(i,y); + SET_FLOAT_WORD(y,i+(k<<23)); /* add k to y's exponent */ + return y-one; + } + t = one; + if(k<23) { + int32_t i; + SET_FLOAT_WORD(t,0x3f800000 - (0x1000000>>k)); /* t=1-2^-k */ + y = t-(e-x); + GET_FLOAT_WORD(i,y); + SET_FLOAT_WORD(y,i+(k<<23)); /* add k to y's exponent */ + } else { + int32_t i; + SET_FLOAT_WORD(t,((0x7f-k)<<23)); /* 2^-k */ + y = x-(e+t); + y += one; + GET_FLOAT_WORD(i,y); + SET_FLOAT_WORD(y,i+(k<<23)); /* add k to y's exponent */ + } + } + return y; +} diff --git a/src/math/s_fabs.c b/src/math/s_fabs.c new file mode 100644 index 00000000..74433250 --- /dev/null +++ b/src/math/s_fabs.c @@ -0,0 +1,27 @@ +/* @(#)s_fabs.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fabs(x) returns the absolute value of x. + */ + +#include <math.h> +#include "math_private.h" + +double +fabs(double x) +{ + uint32_t high; + GET_HIGH_WORD(high,x); + SET_HIGH_WORD(x,high&0x7fffffff); + return x; +} diff --git a/src/math/s_fabsf.c b/src/math/s_fabsf.c new file mode 100644 index 00000000..655d57d8 --- /dev/null +++ b/src/math/s_fabsf.c @@ -0,0 +1,30 @@ +/* s_fabsf.c -- float version of s_fabs.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * fabsf(x) returns the absolute value of x. + */ + +#include <math.h> +#include "math_private.h" + +float +fabsf(float x) +{ + uint32_t ix; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x7fffffff); + return x; +} diff --git a/src/math/s_floor.c b/src/math/s_floor.c new file mode 100644 index 00000000..273cf6f4 --- /dev/null +++ b/src/math/s_floor.c @@ -0,0 +1,69 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * floor(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floor(x). + */ + +#include <math.h> +#include "math_private.h" + +static const double huge = 1.0e300; + +double +floor(double x) +{ + int32_t i0,i1,j0; + uint32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=i1=0;} + else if(((i0&0x7fffffff)|i1)!=0) + { i0=0xbff00000;i1=0;} + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00100000)>>j0; + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + if(i0<0) { + if(j0==20) i0+=1; + else { + j = i1+(1<<(52-j0)); + if(j<i1) i0 +=1 ; /* got a carry */ + i1=j; + } + } + i1 &= (~i); + } + } + INSERT_WORDS(x,i0,i1); + return x; +} diff --git a/src/math/s_floorf.c b/src/math/s_floorf.c new file mode 100644 index 00000000..1164decc --- /dev/null +++ b/src/math/s_floorf.c @@ -0,0 +1,58 @@ +/* s_floorf.c -- float version of s_floor.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * floorf(x) + * Return x rounded toward -inf to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to floorf(x). + */ + +#include <math.h> +#include "math_private.h" + +static const float huge = 1.0e30; + +float +floorf(float x) +{ + int32_t i0,j0; + uint32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */ + if(i0>=0) {i0=0;} + else if((i0&0x7fffffff)!=0) + { i0=0xbf800000;} + } + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>(float)0.0) { /* raise inexact flag */ + if(i0<0) i0 += (0x00800000)>>j0; + i0 &= (~i); + } + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/src/math/s_ilogb.c b/src/math/s_ilogb.c new file mode 100644 index 00000000..f1ac498a --- /dev/null +++ b/src/math/s_ilogb.c @@ -0,0 +1,45 @@ +/* @(#)s_ilogb.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* ilogb(double x) + * return the binary exponent of non-zero x + * ilogb(0) = FP_ILOGB0 + * ilogb(NaN) = FP_ILOGBNAN (no signal is raised) + * ilogb(inf) = INT_MAX (no signal is raised) + */ + +#include <limits.h> + +#include <math.h> +#include "math_private.h" + +int ilogb(double x) +{ + int32_t hx,lx,ix; + + EXTRACT_WORDS(hx,lx,x); + hx &= 0x7fffffff; + if(hx<0x00100000) { + if((hx|lx)==0) + return FP_ILOGB0; + else /* subnormal x */ + if(hx==0) { + for (ix = -1043; lx>0; lx<<=1) ix -=1; + } else { + for (ix = -1022,hx<<=11; hx>0; hx<<=1) ix -=1; + } + return ix; + } + else if (hx<0x7ff00000) return (hx>>20)-1023; + else if (hx>0x7ff00000 || lx!=0) return FP_ILOGBNAN; + else return INT_MAX; +} diff --git a/src/math/s_ilogbf.c b/src/math/s_ilogbf.c new file mode 100644 index 00000000..30359fef --- /dev/null +++ b/src/math/s_ilogbf.c @@ -0,0 +1,37 @@ +/* s_ilogbf.c -- float version of s_ilogb.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <limits.h> + +#include <math.h> +#include "math_private.h" + +int ilogbf(float x) +{ + int32_t hx,ix; + + GET_FLOAT_WORD(hx,x); + hx &= 0x7fffffff; + if(hx<0x00800000) { + if(hx==0) + return FP_ILOGB0; + else /* subnormal x */ + for (ix = -126,hx<<=8; hx>0; hx<<=1) ix -=1; + return ix; + } + else if (hx<0x7f800000) return (hx>>23)-127; + else if (hx>0x7f800000) return FP_ILOGBNAN; + else return INT_MAX; +} diff --git a/src/math/s_ldexp.c b/src/math/s_ldexp.c new file mode 100644 index 00000000..f4d1cd6a --- /dev/null +++ b/src/math/s_ldexp.c @@ -0,0 +1,6 @@ +#include <math.h> + +double ldexp(double x, int n) +{ + return scalbn(x, n); +} diff --git a/src/math/s_ldexpf.c b/src/math/s_ldexpf.c new file mode 100644 index 00000000..3bad5f39 --- /dev/null +++ b/src/math/s_ldexpf.c @@ -0,0 +1,6 @@ +#include <math.h> + +float ldexpf(float x, int n) +{ + return scalbnf(x, n); +} diff --git a/src/math/s_llrint.c b/src/math/s_llrint.c new file mode 100644 index 00000000..2b1e00d0 --- /dev/null +++ b/src/math/s_llrint.c @@ -0,0 +1,8 @@ +#include <math.h> + +// FIXME: incorrect exception behavior + +long long llrint(double x) +{ + return rint(x); +} diff --git a/src/math/s_log1p.c b/src/math/s_log1p.c new file mode 100644 index 00000000..886d5ab1 --- /dev/null +++ b/src/math/s_log1p.c @@ -0,0 +1,157 @@ +/* @(#)s_log1p.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* double log1p(double x) + * + * Method : + * 1. Argument Reduction: find k and f such that + * 1+x = 2^k * (1+f), + * where sqrt(2)/2 < 1+f < sqrt(2) . + * + * Note. If k=0, then f=x is exact. However, if k!=0, then f + * may not be representable exactly. In that case, a correction + * term is need. Let u=1+x rounded. Let c = (1+x)-u, then + * log(1+x) - log(u) ~ c/u. Thus, we proceed to compute log(u), + * and add back the correction term c/u. + * (Note: when x > 2**53, one can simply return log(x)) + * + * 2. Approximation of log1p(f). + * Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s) + * = 2s + 2/3 s**3 + 2/5 s**5 + ....., + * = 2s + s*R + * We use a special Reme algorithm on [0,0.1716] to generate + * a polynomial of degree 14 to approximate R The maximum error + * of this polynomial approximation is bounded by 2**-58.45. In + * other words, + * 2 4 6 8 10 12 14 + * R(z) ~ Lp1*s +Lp2*s +Lp3*s +Lp4*s +Lp5*s +Lp6*s +Lp7*s + * (the values of Lp1 to Lp7 are listed in the program) + * and + * | 2 14 | -58.45 + * | Lp1*s +...+Lp7*s - R(z) | <= 2 + * | | + * Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2. + * In order to guarantee error in log below 1ulp, we compute log + * by + * log1p(f) = f - (hfsq - s*(hfsq+R)). + * + * 3. Finally, log1p(x) = k*ln2 + log1p(f). + * = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo))) + * Here ln2 is split into two floating point number: + * ln2_hi + ln2_lo, + * where n*ln2_hi is always exact for |n| < 2000. + * + * Special cases: + * log1p(x) is NaN with signal if x < -1 (including -INF) ; + * log1p(+INF) is +INF; log1p(-1) is -INF with signal; + * log1p(NaN) is that NaN with no signal. + * + * Accuracy: + * according to an error analysis, the error is always less than + * 1 ulp (unit in the last place). + * + * Constants: + * The hexadecimal values are the intended ones for the following + * constants. The decimal values may be used, provided that the + * compiler will convert from decimal to binary accurately enough + * to produce the hexadecimal values shown. + * + * Note: Assuming log() return accurate answer, the following + * algorithm can be used to compute log1p(x) to within a few ULP: + * + * u = 1+x; + * if(u==1.0) return x ; else + * return log(u)*(x/(u-1.0)); + * + * See HP-15C Advanced Functions Handbook, p.193. + */ + +#include <math.h> +#include "math_private.h" + +static const double +ln2_hi = 6.93147180369123816490e-01, /* 3fe62e42 fee00000 */ +ln2_lo = 1.90821492927058770002e-10, /* 3dea39ef 35793c76 */ +two54 = 1.80143985094819840000e+16, /* 43500000 00000000 */ +Lp1 = 6.666666666666735130e-01, /* 3FE55555 55555593 */ +Lp2 = 3.999999999940941908e-01, /* 3FD99999 9997FA04 */ +Lp3 = 2.857142874366239149e-01, /* 3FD24924 94229359 */ +Lp4 = 2.222219843214978396e-01, /* 3FCC71C5 1D8E78AF */ +Lp5 = 1.818357216161805012e-01, /* 3FC74664 96CB03DE */ +Lp6 = 1.531383769920937332e-01, /* 3FC39A09 D078C69F */ +Lp7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */ + +static const double zero = 0.0; + +double +log1p(double x) +{ + double hfsq,f=0,c=0,s,z,R,u; + int32_t k,hx,hu=0,ax; + + GET_HIGH_WORD(hx,x); + ax = hx&0x7fffffff; + + k = 1; + if (hx < 0x3FDA827A) { /* x < 0.41422 */ + if(ax>=0x3ff00000) { /* x <= -1.0 */ + if(x==-1.0) return -two54/zero; /* log1p(-1)=+inf */ + else return (x-x)/(x-x); /* log1p(x<-1)=NaN */ + } + if(ax<0x3e200000) { /* |x| < 2**-29 */ + if(two54+x>zero /* raise inexact */ + &&ax<0x3c900000) /* |x| < 2**-54 */ + return x; + else + return x - x*x*0.5; + } + if(hx>0||hx<=((int32_t)0xbfd2bec3)) { + k=0;f=x;hu=1;} /* -0.2929<x<0.41422 */ + } + if (hx >= 0x7ff00000) return x+x; + if(k!=0) { + if(hx<0x43400000) { + u = 1.0+x; + GET_HIGH_WORD(hu,u); + k = (hu>>20)-1023; + c = (k>0)? 1.0-(u-x):x-(u-1.0);/* correction term */ + c /= u; + } else { + u = x; + GET_HIGH_WORD(hu,u); + k = (hu>>20)-1023; + c = 0; + } + hu &= 0x000fffff; + if(hu<0x6a09e) { + SET_HIGH_WORD(u,hu|0x3ff00000); /* normalize u */ + } else { + k += 1; + SET_HIGH_WORD(u,hu|0x3fe00000); /* normalize u/2 */ + hu = (0x00100000-hu)>>2; + } + f = u-1.0; + } + hfsq=0.5*f*f; + if(hu==0) { /* |f| < 2**-20 */ + if(f==zero) { if(k==0) return zero; + else {c += k*ln2_lo; return k*ln2_hi+c;} } + R = hfsq*(1.0-0.66666666666666666*f); + if(k==0) return f-R; else + return k*ln2_hi-((R-(k*ln2_lo+c))-f); + } + s = f/(2.0+f); + z = s*s; + R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))); + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f); +} diff --git a/src/math/s_log1pf.c b/src/math/s_log1pf.c new file mode 100644 index 00000000..dcdd6bb3 --- /dev/null +++ b/src/math/s_log1pf.c @@ -0,0 +1,96 @@ +/* s_log1pf.c -- float version of s_log1p.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +ln2_hi = 6.9313812256e-01, /* 0x3f317180 */ +ln2_lo = 9.0580006145e-06, /* 0x3717f7d1 */ +two25 = 3.355443200e+07, /* 0x4c000000 */ +Lp1 = 6.6666668653e-01, /* 3F2AAAAB */ +Lp2 = 4.0000000596e-01, /* 3ECCCCCD */ +Lp3 = 2.8571429849e-01, /* 3E924925 */ +Lp4 = 2.2222198546e-01, /* 3E638E29 */ +Lp5 = 1.8183572590e-01, /* 3E3A3325 */ +Lp6 = 1.5313838422e-01, /* 3E1CD04F */ +Lp7 = 1.4798198640e-01; /* 3E178897 */ + +static const float zero = 0.0; + +float +log1pf(float x) +{ + float hfsq,f=0,c=0,s,z,R,u; + int32_t k,hx,hu=0,ax; + + GET_FLOAT_WORD(hx,x); + ax = hx&0x7fffffff; + + k = 1; + if (hx < 0x3ed413d7) { /* x < 0.41422 */ + if(ax>=0x3f800000) { /* x <= -1.0 */ + if(x==(float)-1.0) return -two25/zero; /* log1p(-1)=+inf */ + else return (x-x)/(x-x); /* log1p(x<-1)=NaN */ + } + if(ax<0x31000000) { /* |x| < 2**-29 */ + if(two25+x>zero /* raise inexact */ + &&ax<0x24800000) /* |x| < 2**-54 */ + return x; + else + return x - x*x*(float)0.5; + } + if(hx>0||hx<=((int32_t)0xbe95f61f)) { + k=0;f=x;hu=1;} /* -0.2929<x<0.41422 */ + } + if (hx >= 0x7f800000) return x+x; + if(k!=0) { + if(hx<0x5a000000) { + u = (float)1.0+x; + GET_FLOAT_WORD(hu,u); + k = (hu>>23)-127; + /* correction term */ + c = (k>0)? (float)1.0-(u-x):x-(u-(float)1.0); + c /= u; + } else { + u = x; + GET_FLOAT_WORD(hu,u); + k = (hu>>23)-127; + c = 0; + } + hu &= 0x007fffff; + if(hu<0x3504f7) { + SET_FLOAT_WORD(u,hu|0x3f800000);/* normalize u */ + } else { + k += 1; + SET_FLOAT_WORD(u,hu|0x3f000000); /* normalize u/2 */ + hu = (0x00800000-hu)>>2; + } + f = u-(float)1.0; + } + hfsq=(float)0.5*f*f; + if(hu==0) { /* |f| < 2**-20 */ + if(f==zero) { if(k==0) return zero; + else {c += k*ln2_lo; return k*ln2_hi+c;} } + R = hfsq*((float)1.0-(float)0.66666666666666666*f); + if(k==0) return f-R; else + return k*ln2_hi-((R-(k*ln2_lo+c))-f); + } + s = f/((float)2.0+f); + z = s*s; + R = z*(Lp1+z*(Lp2+z*(Lp3+z*(Lp4+z*(Lp5+z*(Lp6+z*Lp7)))))); + if(k==0) return f-(hfsq-s*(hfsq+R)); else + return k*ln2_hi-((hfsq-(s*(hfsq+R)+(k*ln2_lo+c)))-f); +} diff --git a/src/math/s_logb.c b/src/math/s_logb.c new file mode 100644 index 00000000..be399c77 --- /dev/null +++ b/src/math/s_logb.c @@ -0,0 +1,34 @@ +/* @(#)s_logb.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * double logb(x) + * IEEE 754 logb. Included to pass IEEE test suite. Not recommend. + * Use ilogb instead. + */ + +#include <math.h> +#include "math_private.h" + +double +logb(double x) +{ + int32_t lx,ix; + EXTRACT_WORDS(ix,lx,x); + ix &= 0x7fffffff; /* high |x| */ + if((ix|lx)==0) return -1.0/fabs(x); + if(ix>=0x7ff00000) return x*x; + if((ix>>=20)==0) /* IEEE 754 logb */ + return -1022.0; + else + return (double) (ix-1023); +} diff --git a/src/math/s_logbf.c b/src/math/s_logbf.c new file mode 100644 index 00000000..747664d3 --- /dev/null +++ b/src/math/s_logbf.c @@ -0,0 +1,31 @@ +/* s_logbf.c -- float version of s_logb.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +logbf(float x) +{ + int32_t ix; + GET_FLOAT_WORD(ix,x); + ix &= 0x7fffffff; /* high |x| */ + if(ix==0) return (float)-1.0/fabsf(x); + if(ix>=0x7f800000) return x*x; + if((ix>>=23)==0) /* IEEE 754 logb */ + return -126.0; + else + return (float) (ix-127); +} diff --git a/src/math/s_lrint.c b/src/math/s_lrint.c new file mode 100644 index 00000000..da8e1989 --- /dev/null +++ b/src/math/s_lrint.c @@ -0,0 +1,8 @@ +#include <math.h> + +// FIXME: incorrect exception behavior + +long lrint(double x) +{ + return rint(x); +} diff --git a/src/math/s_lrintf.c b/src/math/s_lrintf.c new file mode 100644 index 00000000..d0b469b9 --- /dev/null +++ b/src/math/s_lrintf.c @@ -0,0 +1,8 @@ +#include <math.h> + +// FIXME: incorrect exception behavior + +long lrintf(float x) +{ + return rintf(x); +} diff --git a/src/math/s_modf.c b/src/math/s_modf.c new file mode 100644 index 00000000..a5528d6b --- /dev/null +++ b/src/math/s_modf.c @@ -0,0 +1,71 @@ +/* @(#)s_modf.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * modf(double x, double *iptr) + * return fraction part of x, and return x's integral part in *iptr. + * Method: + * Bit twiddling. + * + * Exception: + * No exception. + */ + +#include <math.h> +#include "math_private.h" + +static const double one = 1.0; + +double +modf(double x, double *iptr) +{ + int32_t i0,i1,j0; + uint32_t i; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; /* exponent of x */ + if(j0<20) { /* integer part in high x */ + if(j0<0) { /* |x|<1 */ + INSERT_WORDS(*iptr,i0&0x80000000,0); /* *iptr = +-0 */ + return x; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) { /* x is integral */ + uint32_t high; + *iptr = x; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { + INSERT_WORDS(*iptr,i0&(~i),0); + return x - *iptr; + } + } + } else if (j0>51) { /* no fraction part */ + uint32_t high; + *iptr = x*one; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { /* fraction part in low x */ + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) { /* x is integral */ + uint32_t high; + *iptr = x; + GET_HIGH_WORD(high,x); + INSERT_WORDS(x,high&0x80000000,0); /* return +-0 */ + return x; + } else { + INSERT_WORDS(*iptr,i0,i1&(~i)); + return x - *iptr; + } + } +} diff --git a/src/math/s_modff.c b/src/math/s_modff.c new file mode 100644 index 00000000..de4dfd25 --- /dev/null +++ b/src/math/s_modff.c @@ -0,0 +1,52 @@ +/* s_modff.c -- float version of s_modf.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one = 1.0; + +float +modff(float x, float *iptr) +{ + int32_t i0,j0; + uint32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; /* exponent of x */ + if(j0<23) { /* integer part in x */ + if(j0<0) { /* |x|<1 */ + SET_FLOAT_WORD(*iptr,i0&0x80000000); /* *iptr = +-0 */ + return x; + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) { /* x is integral */ + uint32_t ix; + *iptr = x; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ + return x; + } else { + SET_FLOAT_WORD(*iptr,i0&(~i)); + return x - *iptr; + } + } + } else { /* no fraction part */ + uint32_t ix; + *iptr = x*one; + GET_FLOAT_WORD(ix,x); + SET_FLOAT_WORD(x,ix&0x80000000); /* return +-0 */ + return x; + } +} diff --git a/src/math/s_nextafter.c b/src/math/s_nextafter.c new file mode 100644 index 00000000..46d298ec --- /dev/null +++ b/src/math/s_nextafter.c @@ -0,0 +1,72 @@ +/* @(#)s_nextafter.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* IEEE functions + * nextafter(x,y) + * return the next machine floating-point number of x in the + * direction toward y. + * Special cases: + */ + +#include <math.h> +#include "math_private.h" + +double +nextafter(double x, double y) +{ + volatile double t; + int32_t hx,hy,ix,iy; + uint32_t lx,ly; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffff; /* |y| */ + + if(((ix>=0x7ff00000)&&((ix-0x7ff00000)|lx)!=0) || /* x is nan */ + ((iy>=0x7ff00000)&&((iy-0x7ff00000)|ly)!=0)) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if((ix|lx)==0) { /* x == 0 */ + INSERT_WORDS(x,hy&0x80000000,1); /* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if(hx>=0) { /* x > 0 */ + if(hx>hy||((hx==hy)&&(lx>ly))) { /* x > y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x < y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy||((hx==hy)&&(lx>ly))){/* x < y, x -= ulp */ + if(lx==0) hx -= 1; + lx -= 1; + } else { /* x > y, x += ulp */ + lx += 1; + if(lx==0) hx += 1; + } + } + hy = hx&0x7ff00000; + if(hy>=0x7ff00000) return x+x; /* overflow */ + if(hy<0x00100000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + INSERT_WORDS(y,hx,lx); + return y; + } + } + INSERT_WORDS(x,hx,lx); + return x; +} diff --git a/src/math/s_nextafterf.c b/src/math/s_nextafterf.c new file mode 100644 index 00000000..7ce08838 --- /dev/null +++ b/src/math/s_nextafterf.c @@ -0,0 +1,63 @@ +/* s_nextafterf.c -- float version of s_nextafter.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +nextafterf(float x, float y) +{ + volatile float t; + int32_t hx,hy,ix,iy; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + ix = hx&0x7fffffff; /* |x| */ + iy = hy&0x7fffffff; /* |y| */ + + if((ix>0x7f800000) || /* x is nan */ + (iy>0x7f800000)) /* y is nan */ + return x+y; + if(x==y) return y; /* x=y, return y */ + if(ix==0) { /* x == 0 */ + SET_FLOAT_WORD(x,(hy&0x80000000)|1);/* return +-minsubnormal */ + t = x*x; + if(t==x) return t; else return x; /* raise underflow flag */ + } + if(hx>=0) { /* x > 0 */ + if(hx>hy) { /* x > y, x -= ulp */ + hx -= 1; + } else { /* x < y, x += ulp */ + hx += 1; + } + } else { /* x < 0 */ + if(hy>=0||hx>hy){ /* x < y, x -= ulp */ + hx -= 1; + } else { /* x > y, x += ulp */ + hx += 1; + } + } + hy = hx&0x7f800000; + if(hy>=0x7f800000) return x+x; /* overflow */ + if(hy<0x00800000) { /* underflow */ + t = x*x; + if(t!=x) { /* raise underflow flag */ + SET_FLOAT_WORD(y,hx); + return y; + } + } + SET_FLOAT_WORD(x,hx); + return x; +} diff --git a/src/math/s_remquo.c b/src/math/s_remquo.c new file mode 100644 index 00000000..1a2992d6 --- /dev/null +++ b/src/math/s_remquo.c @@ -0,0 +1,149 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const double Zero[] = {0.0, -0.0,}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + */ +double +remquo(double x, double y, int *quo) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + uint32_t lx,ly,lz,q,sxy; + + EXTRACT_WORDS(hx,lx,x); + EXTRACT_WORDS(hy,ly,y); + sxy = (hx ^ hy) & 0x80000000; + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if((hy|ly)==0||(hx>=0x7ff00000)|| /* y=0,or x not finite */ + ((hy|((ly|-ly)>>31))>0x7ff00000)) /* or y is NaN */ + return (x*y)/(x*y); + if(hx<=hy) { + if((hx<hy)||(lx<ly)) { + q = 0; + goto fixup; /* |x|<|y| return x or x-y */ + } + if(lx==ly) { + *quo = 1; + return Zero[(uint32_t)sx>>31]; /* |x|=|y| return x*0*/ + } + } + + /* determine ix = ilogb(x) */ + if(hx<0x00100000) { /* subnormal x */ + if(hx==0) { + for (ix = -1043, i=lx; i>0; i<<=1) ix -=1; + } else { + for (ix = -1022,i=(hx<<11); i>0; i<<=1) ix -=1; + } + } else ix = (hx>>20)-1023; + + /* determine iy = ilogb(y) */ + if(hy<0x00100000) { /* subnormal y */ + if(hy==0) { + for (iy = -1043, i=ly; i>0; i<<=1) iy -=1; + } else { + for (iy = -1022,i=(hy<<11); i>0; i<<=1) iy -=1; + } + } else iy = (hy>>20)-1023; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -1022) + hx = 0x00100000|(0x000fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -1022-ix; + if(n<=31) { + hx = (hx<<n)|(lx>>(32-n)); + lx <<= n; + } else { + hx = lx<<(n-32); + lx = 0; + } + } + if(iy >= -1022) + hy = 0x00100000|(0x000fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -1022-iy; + if(n<=31) { + hy = (hy<<n)|(ly>>(32-n)); + ly <<= n; + } else { + hy = ly<<(n-32); + ly = 0; + } + } + + /* fix point fmod */ + n = ix - iy; + q = 0; + while(n--) { + hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1; + if(hz<0){hx = hx+hx+(lx>>31); lx = lx+lx;} + else {hx = hz+hz+(lz>>31); lx = lz+lz; q++;} + q <<= 1; + } + hz=hx-hy;lz=lx-ly; if(lx<ly) hz -= 1; + if(hz>=0) {hx=hz;lx=lz;q++;} + + /* convert back to floating value and restore the sign */ + if((hx|lx)==0) { /* return sign(x)*0 */ + *quo = (sxy ? -q : q); + return Zero[(uint32_t)sx>>31]; + } + while(hx<0x00100000) { /* normalize x */ + hx = hx+hx+(lx>>31); lx = lx+lx; + iy -= 1; + } + if(iy>= -1022) { /* normalize output */ + hx = ((hx-0x00100000)|((iy+1023)<<20)); + } else { /* subnormal output */ + n = -1022 - iy; + if(n<=20) { + lx = (lx>>n)|((uint32_t)hx<<(32-n)); + hx >>= n; + } else if (n<=31) { + lx = (hx<<(32-n))|(lx>>n); hx = sx; + } else { + lx = hx>>(n-32); hx = sx; + } + } +fixup: + INSERT_WORDS(x,hx,lx); + y = fabs(y); + if (y < 0x1p-1021) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5*y || (x==0.5*y && (q & 1))) { + q++; + x-=y; + } + GET_HIGH_WORD(hx,x); + SET_HIGH_WORD(x,hx^sx); + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/src/math/s_remquof.c b/src/math/s_remquof.c new file mode 100644 index 00000000..be2a561a --- /dev/null +++ b/src/math/s_remquof.c @@ -0,0 +1,118 @@ +/* @(#)e_fmod.c 1.3 95/01/18 */ +/*- + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunSoft, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float Zero[] = {0.0, -0.0,}; + +/* + * Return the IEEE remainder and set *quo to the last n bits of the + * quotient, rounded to the nearest integer. We choose n=31 because + * we wind up computing all the integer bits of the quotient anyway as + * a side-effect of computing the remainder by the shift and subtract + * method. In practice, this is far more bits than are needed to use + * remquo in reduction algorithms. + */ +float +remquof(float x, float y, int *quo) +{ + int32_t n,hx,hy,hz,ix,iy,sx,i; + uint32_t q,sxy; + + GET_FLOAT_WORD(hx,x); + GET_FLOAT_WORD(hy,y); + sxy = (hx ^ hy) & 0x80000000; + sx = hx&0x80000000; /* sign of x */ + hx ^=sx; /* |x| */ + hy &= 0x7fffffff; /* |y| */ + + /* purge off exception values */ + if(hy==0||hx>=0x7f800000||hy>0x7f800000) /* y=0,NaN;or x not finite */ + return (x*y)/(x*y); + if(hx<hy) { + q = 0; + goto fixup; /* |x|<|y| return x or x-y */ + } else if(hx==hy) { + *quo = 1; + return Zero[(uint32_t)sx>>31]; /* |x|=|y| return x*0*/ + } + + /* determine ix = ilogb(x) */ + if(hx<0x00800000) { /* subnormal x */ + for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; + } else ix = (hx>>23)-127; + + /* determine iy = ilogb(y) */ + if(hy<0x00800000) { /* subnormal y */ + for (iy = -126,i=(hy<<8); i>0; i<<=1) iy -=1; + } else iy = (hy>>23)-127; + + /* set up {hx,lx}, {hy,ly} and align y to x */ + if(ix >= -126) + hx = 0x00800000|(0x007fffff&hx); + else { /* subnormal x, shift x to normal */ + n = -126-ix; + hx <<= n; + } + if(iy >= -126) + hy = 0x00800000|(0x007fffff&hy); + else { /* subnormal y, shift y to normal */ + n = -126-iy; + hy <<= n; + } + + /* fix point fmod */ + n = ix - iy; + q = 0; + while(n--) { + hz=hx-hy; + if(hz<0) hx = hx << 1; + else {hx = hz << 1; q++;} + q <<= 1; + } + hz=hx-hy; + if(hz>=0) {hx=hz;q++;} + + /* convert back to floating value and restore the sign */ + if(hx==0) { /* return sign(x)*0 */ + *quo = (sxy ? -q : q); + return Zero[(uint32_t)sx>>31]; + } + while(hx<0x00800000) { /* normalize x */ + hx <<= 1; + iy -= 1; + } + if(iy>= -126) { /* normalize output */ + hx = ((hx-0x00800000)|((iy+127)<<23)); + } else { /* subnormal output */ + n = -126 - iy; + hx >>= n; + } +fixup: + SET_FLOAT_WORD(x,hx); + y = fabsf(y); + if (y < 0x1p-125f) { + if (x+x>y || (x+x==y && (q & 1))) { + q++; + x-=y; + } + } else if (x>0.5f*y || (x==0.5f*y && (q & 1))) { + q++; + x-=y; + } + GET_FLOAT_WORD(hx,x); + SET_FLOAT_WORD(x,hx^sx); + q &= 0x7fffffff; + *quo = (sxy ? -q : q); + return x; +} diff --git a/src/math/s_rint.c b/src/math/s_rint.c new file mode 100644 index 00000000..aec7d3c9 --- /dev/null +++ b/src/math/s_rint.c @@ -0,0 +1,80 @@ +/* @(#)s_rint.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * rint(x) + * Return x rounded to integral value according to the prevailing + * rounding mode. + * Method: + * Using floating addition. + * Exception: + * Inexact flag raised if x not equal to rint(x). + */ + +#include <math.h> +#include "math_private.h" + +/* + * TWO23 is long double instead of double to avoid a bug in gcc. Without + * this, gcc thinks that TWO23[sx]+x and w-TWO23[sx] already have double + * precision and doesn't clip them to double precision when they are + * assigned and returned. + */ +static const long double +TWO52[2]={ + 4.50359962737049600000e+15, /* 0x43300000, 0x00000000 */ + -4.50359962737049600000e+15, /* 0xC3300000, 0x00000000 */ +}; + +double +rint(double x) +{ + int32_t i0,j0,sx; + uint32_t i,i1; + double w,t; + EXTRACT_WORDS(i0,i1,x); + sx = (i0>>31)&1; + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { + if(((i0&0x7fffffff)|i1)==0) return x; + i1 |= (i0&0x0fffff); + i0 &= 0xfffe0000; + i0 |= ((i1|-i1)>>12)&0x80000; + SET_HIGH_WORD(x,i0); + w = TWO52[sx]+x; + t = w-TWO52[sx]; + GET_HIGH_WORD(i0,t); + SET_HIGH_WORD(t,(i0&0x7fffffff)|(sx<<31)); + return t; + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + i>>=1; + if(((i0&i)|i1)!=0) { + if(j0==19) i1 = 0x40000000; else + i0 = (i0&(~i))|((0x20000)>>j0); + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + i>>=1; + if((i1&i)!=0) i1 = (i1&(~i))|((0x40000000)>>(j0-20)); + } + INSERT_WORDS(x,i0,i1); + w = TWO52[sx]+x; + return w-TWO52[sx]; +} diff --git a/src/math/s_rintf.c b/src/math/s_rintf.c new file mode 100644 index 00000000..c441870d --- /dev/null +++ b/src/math/s_rintf.c @@ -0,0 +1,45 @@ +/* s_rintf.c -- float version of s_rint.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +TWO23[2]={ + 8.3886080000e+06, /* 0x4b000000 */ + -8.3886080000e+06, /* 0xcb000000 */ +}; + +float +rintf(float x) +{ + int32_t i0,j0,sx; + volatile float w,t; /* volatile works around gcc bug */ + GET_FLOAT_WORD(i0,x); + sx = (i0>>31)&1; + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { + if((i0&0x7fffffff)==0) return x; + w = TWO23[sx]+x; + t = w-TWO23[sx]; + return t; + } + w = TWO23[sx]+x; + return w-TWO23[sx]; + } + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ +} diff --git a/src/math/s_round.c b/src/math/s_round.c new file mode 100644 index 00000000..d5bea7a9 --- /dev/null +++ b/src/math/s_round.c @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2003, Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <math.h> + +double +round(double x) +{ + double t; + + if (!isfinite(x)) + return (x); + + if (x >= 0.0) { + t = ceil(x); + if (t - x > 0.5) + t -= 1.0; + return (t); + } else { + t = ceil(-x); + if (t + x > 0.5) + t -= 1.0; + return (-t); + } +} diff --git a/src/math/s_roundf.c b/src/math/s_roundf.c new file mode 100644 index 00000000..c4fc3e19 --- /dev/null +++ b/src/math/s_roundf.c @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2003, Steven G. Kargl + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <math.h> + +float +roundf(float x) +{ + float t; + + if (!isfinite(x)) + return (x); + + if (x >= 0.0) { + t = ceilf(x); + if (t - x > 0.5) + t -= 1.0; + return (t); + } else { + t = ceilf(-x); + if (t + x > 0.5) + t -= 1.0; + return (-t); + } +} diff --git a/src/math/s_scalbln.c b/src/math/s_scalbln.c new file mode 100644 index 00000000..12b9391b --- /dev/null +++ b/src/math/s_scalbln.c @@ -0,0 +1,61 @@ +/* @(#)s_scalbn.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * scalbn (double x, int n) + * scalbn(x,n) returns x* 2**n computed by exponent + * manipulation rather than by actually performing an + * exponentiation or a multiplication. + */ + +#include <math.h> +#include "math_private.h" + +static const double +two54 = 1.80143985094819840000e+16, /* 0x43500000, 0x00000000 */ +twom54 = 5.55111512312578270212e-17, /* 0x3C900000, 0x00000000 */ +huge = 1.0e+300, +tiny = 1.0e-300; + +double +scalbln (double x, long n) +{ + int32_t k,hx,lx; + EXTRACT_WORDS(hx,lx,x); + k = (hx&0x7ff00000)>>20; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((lx|(hx&0x7fffffff))==0) return x; /* +-0 */ + x *= two54; + GET_HIGH_WORD(hx,x); + k = ((hx&0x7ff00000)>>20) - 54; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0x7ff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0x7fe) return huge*copysign(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); return x;} + if (k <= -54) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysign(huge,x); /*overflow*/ + else return tiny*copysign(tiny,x); /*underflow*/ + } + k += 54; /* subnormal result */ + SET_HIGH_WORD(x,(hx&0x800fffff)|(k<<20)); + return x*twom54; +} + +double +scalbn (double x, int n) +{ + return scalbln(x, n); +} diff --git a/src/math/s_scalblnf.c b/src/math/s_scalblnf.c new file mode 100644 index 00000000..21e7641c --- /dev/null +++ b/src/math/s_scalblnf.c @@ -0,0 +1,57 @@ +/* s_scalbnf.c -- float version of s_scalbn.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float +two25 = 3.355443200e+07, /* 0x4c000000 */ +twom25 = 2.9802322388e-08, /* 0x33000000 */ +huge = 1.0e+30, +tiny = 1.0e-30; + +float +scalblnf (float x, long n) +{ + int32_t k,ix; + GET_FLOAT_WORD(ix,x); + k = (ix&0x7f800000)>>23; /* extract exponent */ + if (k==0) { /* 0 or subnormal x */ + if ((ix&0x7fffffff)==0) return x; /* +-0 */ + x *= two25; + GET_FLOAT_WORD(ix,x); + k = ((ix&0x7f800000)>>23) - 25; + if (n< -50000) return tiny*x; /*underflow*/ + } + if (k==0xff) return x+x; /* NaN or Inf */ + k = k+n; + if (k > 0xfe) return huge*copysignf(huge,x); /* overflow */ + if (k > 0) /* normal result */ + {SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); return x;} + if (k <= -25) { + if (n > 50000) /* in case integer overflow in n+k */ + return huge*copysignf(huge,x); /*overflow*/ + else return tiny*copysignf(tiny,x); /*underflow*/ + } + k += 25; /* subnormal result */ + SET_FLOAT_WORD(x,(ix&0x807fffff)|(k<<23)); + return x*twom25; +} + +float +scalbnf (float x, int n) +{ + return scalblnf(x, n); +} diff --git a/src/math/s_sin.c b/src/math/s_sin.c new file mode 100644 index 00000000..2a2774ed --- /dev/null +++ b/src/math/s_sin.c @@ -0,0 +1,74 @@ +/* @(#)s_sin.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* sin(x) + * Return sine function of x. + * + * kernel function: + * __kernel_sin ... sine function on [-pi/4,pi/4] + * __kernel_cos ... cose function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include <math.h> +#include "math_private.h" + +double +sin(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_sin(x,z,0); + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + switch(n&3) { + case 0: return __kernel_sin(y[0],y[1],1); + case 1: return __kernel_cos(y[0],y[1]); + case 2: return -__kernel_sin(y[0],y[1],1); + default: + return -__kernel_cos(y[0],y[1]); + } + } +} diff --git a/src/math/s_sinf.c b/src/math/s_sinf.c new file mode 100644 index 00000000..d2b8e806 --- /dev/null +++ b/src/math/s_sinf.c @@ -0,0 +1,45 @@ +/* s_sinf.c -- float version of s_sin.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +sinf(float x) +{ + float y[2],z=0.0; + int32_t n, ix; + + GET_FLOAT_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3f490fd8) return __kernel_sinf(x,z,0); + + /* sin(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,y); + switch(n&3) { + case 0: return __kernel_sinf(y[0],y[1],1); + case 1: return __kernel_cosf(y[0],y[1]); + case 2: return -__kernel_sinf(y[0],y[1],1); + default: + return -__kernel_cosf(y[0],y[1]); + } + } +} diff --git a/src/math/s_tan.c b/src/math/s_tan.c new file mode 100644 index 00000000..3333cb3d --- /dev/null +++ b/src/math/s_tan.c @@ -0,0 +1,68 @@ +/* @(#)s_tan.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* tan(x) + * Return tangent function of x. + * + * kernel function: + * __kernel_tan ... tangent function on [-pi/4,pi/4] + * __ieee754_rem_pio2 ... argument reduction routine + * + * Method. + * Let S,C and T denote the sin, cos and tan respectively on + * [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2 + * in [-pi/4 , +pi/4], and let n = k mod 4. + * We have + * + * n sin(x) cos(x) tan(x) + * ---------------------------------------------------------- + * 0 S C T + * 1 C -S -1/T + * 2 -S -C T + * 3 -C S -1/T + * ---------------------------------------------------------- + * + * Special cases: + * Let trig be any of sin, cos, or tan. + * trig(+-INF) is NaN, with signals; + * trig(NaN) is that NaN; + * + * Accuracy: + * TRIG(x) returns trig(x) nearly rounded + */ + +#include <math.h> +#include "math_private.h" + +double +tan(double x) +{ + double y[2],z=0.0; + int32_t n, ix; + + /* High word of x. */ + GET_HIGH_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3fe921fb) return __kernel_tan(x,z,1); + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7ff00000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2(x,y); + return __kernel_tan(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +} diff --git a/src/math/s_tanf.c b/src/math/s_tanf.c new file mode 100644 index 00000000..660dd9c3 --- /dev/null +++ b/src/math/s_tanf.c @@ -0,0 +1,40 @@ +/* s_tanf.c -- float version of s_tan.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +float +tanf(float x) +{ + float y[2],z=0.0; + int32_t n, ix; + + GET_FLOAT_WORD(ix,x); + + /* |x| ~< pi/4 */ + ix &= 0x7fffffff; + if(ix <= 0x3f490fda) return __kernel_tanf(x,z,1); + + /* tan(Inf or NaN) is NaN */ + else if (ix>=0x7f800000) return x-x; /* NaN */ + + /* argument reduction needed */ + else { + n = __ieee754_rem_pio2f(x,y); + return __kernel_tanf(y[0],y[1],1-((n&1)<<1)); /* 1 -- n even + -1 -- n odd */ + } +} diff --git a/src/math/s_tanh.c b/src/math/s_tanh.c new file mode 100644 index 00000000..78b8e849 --- /dev/null +++ b/src/math/s_tanh.c @@ -0,0 +1,74 @@ +/* @(#)s_tanh.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* Tanh(x) + * Return the Hyperbolic Tangent of x + * + * Method : + * x -x + * e - e + * 0. tanh(x) is defined to be ----------- + * x -x + * e + e + * 1. reduce x to non-negative by tanh(-x) = -tanh(x). + * 2. 0 <= x <= 2**-55 : tanh(x) := x*(one+x) + * -t + * 2**-55 < x <= 1 : tanh(x) := -----; t = expm1(-2x) + * t + 2 + * 2 + * 1 <= x <= 22.0 : tanh(x) := 1- ----- ; t=expm1(2x) + * t + 2 + * 22.0 < x <= INF : tanh(x) := 1. + * + * Special cases: + * tanh(NaN) is NaN; + * only tanh(0)=0 is exact for finite argument. + */ + +#include <math.h> +#include "math_private.h" + +static const double one=1.0, two=2.0, tiny = 1.0e-300; + +double +tanh(double x) +{ + double t,z; + int32_t jx,ix; + + /* High word of |x|. */ + GET_HIGH_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7ff00000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 22 */ + if (ix < 0x40360000) { /* |x|<22 */ + if (ix<0x3c800000) /* |x|<2**-55 */ + return x*(one+x); /* tanh(small) = small */ + if (ix>=0x3ff00000) { /* |x|>=1 */ + t = expm1(two*fabs(x)); + z = one - two/(t+two); + } else { + t = expm1(-two*fabs(x)); + z= -t/(t+two); + } + /* |x| > 22, return +-1 */ + } else { + z = one - tiny; /* raised inexact flag */ + } + return (jx>=0)? z: -z; +} diff --git a/src/math/s_tanhf.c b/src/math/s_tanhf.c new file mode 100644 index 00000000..a0820409 --- /dev/null +++ b/src/math/s_tanhf.c @@ -0,0 +1,52 @@ +/* s_tanhf.c -- float version of s_tanh.c. + * Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com. + */ + +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +#include <math.h> +#include "math_private.h" + +static const float one=1.0, two=2.0, tiny = 1.0e-30; + +float +tanhf(float x) +{ + float t,z; + int32_t jx,ix; + + GET_FLOAT_WORD(jx,x); + ix = jx&0x7fffffff; + + /* x is INF or NaN */ + if(ix>=0x7f800000) { + if (jx>=0) return one/x+one; /* tanh(+-inf)=+-1 */ + else return one/x-one; /* tanh(NaN) = NaN */ + } + + /* |x| < 22 */ + if (ix < 0x41b00000) { /* |x|<22 */ + if (ix<0x24000000) /* |x|<2**-55 */ + return x*(one+x); /* tanh(small) = small */ + if (ix>=0x3f800000) { /* |x|>=1 */ + t = expm1f(two*fabsf(x)); + z = one - two/(t+two); + } else { + t = expm1f(-two*fabsf(x)); + z= -t/(t+two); + } + /* |x| > 22, return +-1 */ + } else { + z = one - tiny; /* raised inexact flag */ + } + return (jx>=0)? z: -z; +} diff --git a/src/math/s_trunc.c b/src/math/s_trunc.c new file mode 100644 index 00000000..02c65567 --- /dev/null +++ b/src/math/s_trunc.c @@ -0,0 +1,58 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * trunc(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to trunc(x). + */ + +#include <math.h> +#include "math_private.h" + +static const double huge = 1.0e300; + +double +trunc(double x) +{ + int32_t i0,i1,j0; + uint32_t i,j; + EXTRACT_WORDS(i0,i1,x); + j0 = ((i0>>20)&0x7ff)-0x3ff; + if(j0<20) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0) {/* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000U; + i1 = 0; + } + } else { + i = (0x000fffff)>>j0; + if(((i0&i)|i1)==0) return x; /* x is integral */ + if(huge+x>0.0) { /* raise inexact flag */ + i0 &= (~i); i1=0; + } + } + } else if (j0>51) { + if(j0==0x400) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } else { + i = ((uint32_t)(0xffffffff))>>(j0-20); + if((i1&i)==0) return x; /* x is integral */ + if(huge+x>0.0) /* raise inexact flag */ + i1 &= (~i); + } + INSERT_WORDS(x,i0,i1); + return x; +} diff --git a/src/math/s_truncf.c b/src/math/s_truncf.c new file mode 100644 index 00000000..c253e62b --- /dev/null +++ b/src/math/s_truncf.c @@ -0,0 +1,50 @@ +/* @(#)s_floor.c 5.1 93/09/24 */ +/* + * ==================================================== + * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved. + * + * Developed at SunPro, a Sun Microsystems, Inc. business. + * Permission to use, copy, modify, and distribute this + * software is freely granted, provided that this notice + * is preserved. + * ==================================================== + */ + +/* + * truncf(x) + * Return x rounded toward 0 to integral value + * Method: + * Bit twiddling. + * Exception: + * Inexact flag raised if x not equal to truncf(x). + */ + +#include <math.h> +#include "math_private.h" + +static const float huge = 1.0e30F; + +float +truncf(float x) +{ + int32_t i0,j0; + uint32_t i; + GET_FLOAT_WORD(i0,x); + j0 = ((i0>>23)&0xff)-0x7f; + if(j0<23) { + if(j0<0) { /* raise inexact if x != 0 */ + if(huge+x>0.0F) /* |x|<1, so return 0*sign(x) */ + i0 &= 0x80000000; + } else { + i = (0x007fffff)>>j0; + if((i0&i)==0) return x; /* x is integral */ + if(huge+x>0.0F) /* raise inexact flag */ + i0 &= (~i); + } + } else { + if(j0==0x80) return x+x; /* inf or NaN */ + else return x; /* x is integral */ + } + SET_FLOAT_WORD(x,i0); + return x; +} diff --git a/src/misc/basename.c b/src/misc/basename.c new file mode 100644 index 00000000..c87e00cd --- /dev/null +++ b/src/misc/basename.c @@ -0,0 +1,12 @@ +#include <string.h> +#include <libgen.h> + +char *basename(char *s) +{ + size_t i; + if (!s || !*s) return "."; + i = strlen(s)-1; + for (; i&&s[i]=='/'; i--) s[i] = 0; + for (; i&&s[i-1]!='/'; i--); + return s+i; +} diff --git a/src/misc/bswap_32.c b/src/misc/bswap_32.c new file mode 100644 index 00000000..a2418ca9 --- /dev/null +++ b/src/misc/bswap_32.c @@ -0,0 +1,7 @@ +#include <endian.h> +#include <stdint.h> + +uint32_t bswap_32(uint32_t x) +{ + return x>>24 | x>>16&0xff00 | x<<16&0xff0000 | x<<24; +} diff --git a/src/misc/bswap_64.c b/src/misc/bswap_64.c new file mode 100644 index 00000000..961300b5 --- /dev/null +++ b/src/misc/bswap_64.c @@ -0,0 +1,9 @@ +#include <endian.h> +#include <stdint.h> + +uint32_t bswap_32(uint32_t); + +uint64_t bswap_64(uint64_t x) +{ + return bswap_32(x)+0LL<<32 | bswap_32(x>>32); +} diff --git a/src/misc/crypt.c b/src/misc/crypt.c new file mode 100644 index 00000000..42918ef0 --- /dev/null +++ b/src/misc/crypt.c @@ -0,0 +1,2578 @@ +/* + * FreeSec: libcrypt for NetBSD + * + * Copyright (c) 1994 David Burren + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren <davidb@werj.com.au>. + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * ARCHITECTURE ASSUMPTIONS: + * It is assumed that the 8-byte arrays passed by reference can be + * addressed as arrays of uint32_t's (ie. the CPU is not picky about + * alignment). + */ + +#include <stdint.h> +#include "libc.h" + +static int i64c(int i) +{ + i &= 0x3f; + if (i == 0) + return '.'; + if (i == 1) + return '/'; + if (i < 12) + return ('0' - 2 + i); + if (i < 38) + return ('A' - 12 + i); + return ('a' - 38 + i); +} + +/* Generated by const_des_init() (removed) */ + +static const uint8_t final_perm[64] = { + 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, + 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, + 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, + 0x3c, 0x34, 0x2c, 0x24, 0x1c, 0x14, 0x0c, 0x04, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, +}; +static const uint8_t m_sbox[4][4096] = { + { + 0xef, 0xe3, 0xe1, 0xed, 0xe8, 0xe4, 0xee, 0xe7, + 0xe6, 0xef, 0xeb, 0xe2, 0xe3, 0xe8, 0xe4, 0xee, + 0xe9, 0xec, 0xe7, 0xe0, 0xe2, 0xe1, 0xed, 0xea, + 0xec, 0xe6, 0xe0, 0xe9, 0xe5, 0xeb, 0xea, 0xe5, + 0xe0, 0xed, 0xee, 0xe8, 0xe7, 0xea, 0xeb, 0xe1, + 0xea, 0xe3, 0xe4, 0xef, 0xed, 0xe4, 0xe1, 0xe2, + 0xe5, 0xeb, 0xe8, 0xe6, 0xec, 0xe7, 0xe6, 0xec, + 0xe9, 0xe0, 0xe3, 0xe5, 0xe2, 0xee, 0xef, 0xe9, + 0x0f, 0x03, 0x01, 0x0d, 0x08, 0x04, 0x0e, 0x07, + 0x06, 0x0f, 0x0b, 0x02, 0x03, 0x08, 0x04, 0x0e, + 0x09, 0x0c, 0x07, 0x00, 0x02, 0x01, 0x0d, 0x0a, + 0x0c, 0x06, 0x00, 0x09, 0x05, 0x0b, 0x0a, 0x05, + 0x00, 0x0d, 0x0e, 0x08, 0x07, 0x0a, 0x0b, 0x01, + 0x0a, 0x03, 0x04, 0x0f, 0x0d, 0x04, 0x01, 0x02, + 0x05, 0x0b, 0x08, 0x06, 0x0c, 0x07, 0x06, 0x0c, + 0x09, 0x00, 0x03, 0x05, 0x02, 0x0e, 0x0f, 0x09, + 0x4f, 0x43, 0x41, 0x4d, 0x48, 0x44, 0x4e, 0x47, + 0x46, 0x4f, 0x4b, 0x42, 0x43, 0x48, 0x44, 0x4e, + 0x49, 0x4c, 0x47, 0x40, 0x42, 0x41, 0x4d, 0x4a, + 0x4c, 0x46, 0x40, 0x49, 0x45, 0x4b, 0x4a, 0x45, + 0x40, 0x4d, 0x4e, 0x48, 0x47, 0x4a, 0x4b, 0x41, + 0x4a, 0x43, 0x44, 0x4f, 0x4d, 0x44, 0x41, 0x42, + 0x45, 0x4b, 0x48, 0x46, 0x4c, 0x47, 0x46, 0x4c, + 0x49, 0x40, 0x43, 0x45, 0x42, 0x4e, 0x4f, 0x49, + 0xff, 0xf3, 0xf1, 0xfd, 0xf8, 0xf4, 0xfe, 0xf7, + 0xf6, 0xff, 0xfb, 0xf2, 0xf3, 0xf8, 0xf4, 0xfe, + 0xf9, 0xfc, 0xf7, 0xf0, 0xf2, 0xf1, 0xfd, 0xfa, + 0xfc, 0xf6, 0xf0, 0xf9, 0xf5, 0xfb, 0xfa, 0xf5, + 0xf0, 0xfd, 0xfe, 0xf8, 0xf7, 0xfa, 0xfb, 0xf1, + 0xfa, 0xf3, 0xf4, 0xff, 0xfd, 0xf4, 0xf1, 0xf2, + 0xf5, 0xfb, 0xf8, 0xf6, 0xfc, 0xf7, 0xf6, 0xfc, + 0xf9, 0xf0, 0xf3, 0xf5, 0xf2, 0xfe, 0xff, 0xf9, + 0xdf, 0xd3, 0xd1, 0xdd, 0xd8, 0xd4, 0xde, 0xd7, + 0xd6, 0xdf, 0xdb, 0xd2, 0xd3, 0xd8, 0xd4, 0xde, + 0xd9, 0xdc, 0xd7, 0xd0, 0xd2, 0xd1, 0xdd, 0xda, + 0xdc, 0xd6, 0xd0, 0xd9, 0xd5, 0xdb, 0xda, 0xd5, + 0xd0, 0xdd, 0xde, 0xd8, 0xd7, 0xda, 0xdb, 0xd1, + 0xda, 0xd3, 0xd4, 0xdf, 0xdd, 0xd4, 0xd1, 0xd2, + 0xd5, 0xdb, 0xd8, 0xd6, 0xdc, 0xd7, 0xd6, 0xdc, + 0xd9, 0xd0, 0xd3, 0xd5, 0xd2, 0xde, 0xdf, 0xd9, + 0x7f, 0x73, 0x71, 0x7d, 0x78, 0x74, 0x7e, 0x77, + 0x76, 0x7f, 0x7b, 0x72, 0x73, 0x78, 0x74, 0x7e, + 0x79, 0x7c, 0x77, 0x70, 0x72, 0x71, 0x7d, 0x7a, + 0x7c, 0x76, 0x70, 0x79, 0x75, 0x7b, 0x7a, 0x75, + 0x70, 0x7d, 0x7e, 0x78, 0x77, 0x7a, 0x7b, 0x71, + 0x7a, 0x73, 0x74, 0x7f, 0x7d, 0x74, 0x71, 0x72, + 0x75, 0x7b, 0x78, 0x76, 0x7c, 0x77, 0x76, 0x7c, + 0x79, 0x70, 0x73, 0x75, 0x72, 0x7e, 0x7f, 0x79, + 0x1f, 0x13, 0x11, 0x1d, 0x18, 0x14, 0x1e, 0x17, + 0x16, 0x1f, 0x1b, 0x12, 0x13, 0x18, 0x14, 0x1e, + 0x19, 0x1c, 0x17, 0x10, 0x12, 0x11, 0x1d, 0x1a, + 0x1c, 0x16, 0x10, 0x19, 0x15, 0x1b, 0x1a, 0x15, + 0x10, 0x1d, 0x1e, 0x18, 0x17, 0x1a, 0x1b, 0x11, + 0x1a, 0x13, 0x14, 0x1f, 0x1d, 0x14, 0x11, 0x12, + 0x15, 0x1b, 0x18, 0x16, 0x1c, 0x17, 0x16, 0x1c, + 0x19, 0x10, 0x13, 0x15, 0x12, 0x1e, 0x1f, 0x19, + 0x4f, 0x43, 0x41, 0x4d, 0x48, 0x44, 0x4e, 0x47, + 0x46, 0x4f, 0x4b, 0x42, 0x43, 0x48, 0x44, 0x4e, + 0x49, 0x4c, 0x47, 0x40, 0x42, 0x41, 0x4d, 0x4a, + 0x4c, 0x46, 0x40, 0x49, 0x45, 0x4b, 0x4a, 0x45, + 0x40, 0x4d, 0x4e, 0x48, 0x47, 0x4a, 0x4b, 0x41, + 0x4a, 0x43, 0x44, 0x4f, 0x4d, 0x44, 0x41, 0x42, + 0x45, 0x4b, 0x48, 0x46, 0x4c, 0x47, 0x46, 0x4c, + 0x49, 0x40, 0x43, 0x45, 0x42, 0x4e, 0x4f, 0x49, + 0x2f, 0x23, 0x21, 0x2d, 0x28, 0x24, 0x2e, 0x27, + 0x26, 0x2f, 0x2b, 0x22, 0x23, 0x28, 0x24, 0x2e, + 0x29, 0x2c, 0x27, 0x20, 0x22, 0x21, 0x2d, 0x2a, + 0x2c, 0x26, 0x20, 0x29, 0x25, 0x2b, 0x2a, 0x25, + 0x20, 0x2d, 0x2e, 0x28, 0x27, 0x2a, 0x2b, 0x21, + 0x2a, 0x23, 0x24, 0x2f, 0x2d, 0x24, 0x21, 0x22, + 0x25, 0x2b, 0x28, 0x26, 0x2c, 0x27, 0x26, 0x2c, + 0x29, 0x20, 0x23, 0x25, 0x22, 0x2e, 0x2f, 0x29, + 0xef, 0xe3, 0xe1, 0xed, 0xe8, 0xe4, 0xee, 0xe7, + 0xe6, 0xef, 0xeb, 0xe2, 0xe3, 0xe8, 0xe4, 0xee, + 0xe9, 0xec, 0xe7, 0xe0, 0xe2, 0xe1, 0xed, 0xea, + 0xec, 0xe6, 0xe0, 0xe9, 0xe5, 0xeb, 0xea, 0xe5, + 0xe0, 0xed, 0xee, 0xe8, 0xe7, 0xea, 0xeb, 0xe1, + 0xea, 0xe3, 0xe4, 0xef, 0xed, 0xe4, 0xe1, 0xe2, + 0xe5, 0xeb, 0xe8, 0xe6, 0xec, 0xe7, 0xe6, 0xec, + 0xe9, 0xe0, 0xe3, 0xe5, 0xe2, 0xee, 0xef, 0xe9, + 0xff, 0xf3, 0xf1, 0xfd, 0xf8, 0xf4, 0xfe, 0xf7, + 0xf6, 0xff, 0xfb, 0xf2, 0xf3, 0xf8, 0xf4, 0xfe, + 0xf9, 0xfc, 0xf7, 0xf0, 0xf2, 0xf1, 0xfd, 0xfa, + 0xfc, 0xf6, 0xf0, 0xf9, 0xf5, 0xfb, 0xfa, 0xf5, + 0xf0, 0xfd, 0xfe, 0xf8, 0xf7, 0xfa, 0xfb, 0xf1, + 0xfa, 0xf3, 0xf4, 0xff, 0xfd, 0xf4, 0xf1, 0xf2, + 0xf5, 0xfb, 0xf8, 0xf6, 0xfc, 0xf7, 0xf6, 0xfc, + 0xf9, 0xf0, 0xf3, 0xf5, 0xf2, 0xfe, 0xff, 0xf9, + 0x2f, 0x23, 0x21, 0x2d, 0x28, 0x24, 0x2e, 0x27, + 0x26, 0x2f, 0x2b, 0x22, 0x23, 0x28, 0x24, 0x2e, + 0x29, 0x2c, 0x27, 0x20, 0x22, 0x21, 0x2d, 0x2a, + 0x2c, 0x26, 0x20, 0x29, 0x25, 0x2b, 0x2a, 0x25, + 0x20, 0x2d, 0x2e, 0x28, 0x27, 0x2a, 0x2b, 0x21, + 0x2a, 0x23, 0x24, 0x2f, 0x2d, 0x24, 0x21, 0x22, + 0x25, 0x2b, 0x28, 0x26, 0x2c, 0x27, 0x26, 0x2c, + 0x29, 0x20, 0x23, 0x25, 0x22, 0x2e, 0x2f, 0x29, + 0xbf, 0xb3, 0xb1, 0xbd, 0xb8, 0xb4, 0xbe, 0xb7, + 0xb6, 0xbf, 0xbb, 0xb2, 0xb3, 0xb8, 0xb4, 0xbe, + 0xb9, 0xbc, 0xb7, 0xb0, 0xb2, 0xb1, 0xbd, 0xba, + 0xbc, 0xb6, 0xb0, 0xb9, 0xb5, 0xbb, 0xba, 0xb5, + 0xb0, 0xbd, 0xbe, 0xb8, 0xb7, 0xba, 0xbb, 0xb1, + 0xba, 0xb3, 0xb4, 0xbf, 0xbd, 0xb4, 0xb1, 0xb2, + 0xb5, 0xbb, 0xb8, 0xb6, 0xbc, 0xb7, 0xb6, 0xbc, + 0xb9, 0xb0, 0xb3, 0xb5, 0xb2, 0xbe, 0xbf, 0xb9, + 0xdf, 0xd3, 0xd1, 0xdd, 0xd8, 0xd4, 0xde, 0xd7, + 0xd6, 0xdf, 0xdb, 0xd2, 0xd3, 0xd8, 0xd4, 0xde, + 0xd9, 0xdc, 0xd7, 0xd0, 0xd2, 0xd1, 0xdd, 0xda, + 0xdc, 0xd6, 0xd0, 0xd9, 0xd5, 0xdb, 0xda, 0xd5, + 0xd0, 0xdd, 0xde, 0xd8, 0xd7, 0xda, 0xdb, 0xd1, + 0xda, 0xd3, 0xd4, 0xdf, 0xdd, 0xd4, 0xd1, 0xd2, + 0xd5, 0xdb, 0xd8, 0xd6, 0xdc, 0xd7, 0xd6, 0xdc, + 0xd9, 0xd0, 0xd3, 0xd5, 0xd2, 0xde, 0xdf, 0xd9, + 0x8f, 0x83, 0x81, 0x8d, 0x88, 0x84, 0x8e, 0x87, + 0x86, 0x8f, 0x8b, 0x82, 0x83, 0x88, 0x84, 0x8e, + 0x89, 0x8c, 0x87, 0x80, 0x82, 0x81, 0x8d, 0x8a, + 0x8c, 0x86, 0x80, 0x89, 0x85, 0x8b, 0x8a, 0x85, + 0x80, 0x8d, 0x8e, 0x88, 0x87, 0x8a, 0x8b, 0x81, + 0x8a, 0x83, 0x84, 0x8f, 0x8d, 0x84, 0x81, 0x82, + 0x85, 0x8b, 0x88, 0x86, 0x8c, 0x87, 0x86, 0x8c, + 0x89, 0x80, 0x83, 0x85, 0x82, 0x8e, 0x8f, 0x89, + 0x1f, 0x13, 0x11, 0x1d, 0x18, 0x14, 0x1e, 0x17, + 0x16, 0x1f, 0x1b, 0x12, 0x13, 0x18, 0x14, 0x1e, + 0x19, 0x1c, 0x17, 0x10, 0x12, 0x11, 0x1d, 0x1a, + 0x1c, 0x16, 0x10, 0x19, 0x15, 0x1b, 0x1a, 0x15, + 0x10, 0x1d, 0x1e, 0x18, 0x17, 0x1a, 0x1b, 0x11, + 0x1a, 0x13, 0x14, 0x1f, 0x1d, 0x14, 0x11, 0x12, + 0x15, 0x1b, 0x18, 0x16, 0x1c, 0x17, 0x16, 0x1c, + 0x19, 0x10, 0x13, 0x15, 0x12, 0x1e, 0x1f, 0x19, + 0x3f, 0x33, 0x31, 0x3d, 0x38, 0x34, 0x3e, 0x37, + 0x36, 0x3f, 0x3b, 0x32, 0x33, 0x38, 0x34, 0x3e, + 0x39, 0x3c, 0x37, 0x30, 0x32, 0x31, 0x3d, 0x3a, + 0x3c, 0x36, 0x30, 0x39, 0x35, 0x3b, 0x3a, 0x35, + 0x30, 0x3d, 0x3e, 0x38, 0x37, 0x3a, 0x3b, 0x31, + 0x3a, 0x33, 0x34, 0x3f, 0x3d, 0x34, 0x31, 0x32, + 0x35, 0x3b, 0x38, 0x36, 0x3c, 0x37, 0x36, 0x3c, + 0x39, 0x30, 0x33, 0x35, 0x32, 0x3e, 0x3f, 0x39, + 0xaf, 0xa3, 0xa1, 0xad, 0xa8, 0xa4, 0xae, 0xa7, + 0xa6, 0xaf, 0xab, 0xa2, 0xa3, 0xa8, 0xa4, 0xae, + 0xa9, 0xac, 0xa7, 0xa0, 0xa2, 0xa1, 0xad, 0xaa, + 0xac, 0xa6, 0xa0, 0xa9, 0xa5, 0xab, 0xaa, 0xa5, + 0xa0, 0xad, 0xae, 0xa8, 0xa7, 0xaa, 0xab, 0xa1, + 0xaa, 0xa3, 0xa4, 0xaf, 0xad, 0xa4, 0xa1, 0xa2, + 0xa5, 0xab, 0xa8, 0xa6, 0xac, 0xa7, 0xa6, 0xac, + 0xa9, 0xa0, 0xa3, 0xa5, 0xa2, 0xae, 0xaf, 0xa9, + 0xaf, 0xa3, 0xa1, 0xad, 0xa8, 0xa4, 0xae, 0xa7, + 0xa6, 0xaf, 0xab, 0xa2, 0xa3, 0xa8, 0xa4, 0xae, + 0xa9, 0xac, 0xa7, 0xa0, 0xa2, 0xa1, 0xad, 0xaa, + 0xac, 0xa6, 0xa0, 0xa9, 0xa5, 0xab, 0xaa, 0xa5, + 0xa0, 0xad, 0xae, 0xa8, 0xa7, 0xaa, 0xab, 0xa1, + 0xaa, 0xa3, 0xa4, 0xaf, 0xad, 0xa4, 0xa1, 0xa2, + 0xa5, 0xab, 0xa8, 0xa6, 0xac, 0xa7, 0xa6, 0xac, + 0xa9, 0xa0, 0xa3, 0xa5, 0xa2, 0xae, 0xaf, 0xa9, + 0x6f, 0x63, 0x61, 0x6d, 0x68, 0x64, 0x6e, 0x67, + 0x66, 0x6f, 0x6b, 0x62, 0x63, 0x68, 0x64, 0x6e, + 0x69, 0x6c, 0x67, 0x60, 0x62, 0x61, 0x6d, 0x6a, + 0x6c, 0x66, 0x60, 0x69, 0x65, 0x6b, 0x6a, 0x65, + 0x60, 0x6d, 0x6e, 0x68, 0x67, 0x6a, 0x6b, 0x61, + 0x6a, 0x63, 0x64, 0x6f, 0x6d, 0x64, 0x61, 0x62, + 0x65, 0x6b, 0x68, 0x66, 0x6c, 0x67, 0x66, 0x6c, + 0x69, 0x60, 0x63, 0x65, 0x62, 0x6e, 0x6f, 0x69, + 0x6f, 0x63, 0x61, 0x6d, 0x68, 0x64, 0x6e, 0x67, + 0x66, 0x6f, 0x6b, 0x62, 0x63, 0x68, 0x64, 0x6e, + 0x69, 0x6c, 0x67, 0x60, 0x62, 0x61, 0x6d, 0x6a, + 0x6c, 0x66, 0x60, 0x69, 0x65, 0x6b, 0x6a, 0x65, + 0x60, 0x6d, 0x6e, 0x68, 0x67, 0x6a, 0x6b, 0x61, + 0x6a, 0x63, 0x64, 0x6f, 0x6d, 0x64, 0x61, 0x62, + 0x65, 0x6b, 0x68, 0x66, 0x6c, 0x67, 0x66, 0x6c, + 0x69, 0x60, 0x63, 0x65, 0x62, 0x6e, 0x6f, 0x69, + 0xcf, 0xc3, 0xc1, 0xcd, 0xc8, 0xc4, 0xce, 0xc7, + 0xc6, 0xcf, 0xcb, 0xc2, 0xc3, 0xc8, 0xc4, 0xce, + 0xc9, 0xcc, 0xc7, 0xc0, 0xc2, 0xc1, 0xcd, 0xca, + 0xcc, 0xc6, 0xc0, 0xc9, 0xc5, 0xcb, 0xca, 0xc5, + 0xc0, 0xcd, 0xce, 0xc8, 0xc7, 0xca, 0xcb, 0xc1, + 0xca, 0xc3, 0xc4, 0xcf, 0xcd, 0xc4, 0xc1, 0xc2, + 0xc5, 0xcb, 0xc8, 0xc6, 0xcc, 0xc7, 0xc6, 0xcc, + 0xc9, 0xc0, 0xc3, 0xc5, 0xc2, 0xce, 0xcf, 0xc9, + 0xcf, 0xc3, 0xc1, 0xcd, 0xc8, 0xc4, 0xce, 0xc7, + 0xc6, 0xcf, 0xcb, 0xc2, 0xc3, 0xc8, 0xc4, 0xce, + 0xc9, 0xcc, 0xc7, 0xc0, 0xc2, 0xc1, 0xcd, 0xca, + 0xcc, 0xc6, 0xc0, 0xc9, 0xc5, 0xcb, 0xca, 0xc5, + 0xc0, 0xcd, 0xce, 0xc8, 0xc7, 0xca, 0xcb, 0xc1, + 0xca, 0xc3, 0xc4, 0xcf, 0xcd, 0xc4, 0xc1, 0xc2, + 0xc5, 0xcb, 0xc8, 0xc6, 0xcc, 0xc7, 0xc6, 0xcc, + 0xc9, 0xc0, 0xc3, 0xc5, 0xc2, 0xce, 0xcf, 0xc9, + 0xbf, 0xb3, 0xb1, 0xbd, 0xb8, 0xb4, 0xbe, 0xb7, + 0xb6, 0xbf, 0xbb, 0xb2, 0xb3, 0xb8, 0xb4, 0xbe, + 0xb9, 0xbc, 0xb7, 0xb0, 0xb2, 0xb1, 0xbd, 0xba, + 0xbc, 0xb6, 0xb0, 0xb9, 0xb5, 0xbb, 0xba, 0xb5, + 0xb0, 0xbd, 0xbe, 0xb8, 0xb7, 0xba, 0xbb, 0xb1, + 0xba, 0xb3, 0xb4, 0xbf, 0xbd, 0xb4, 0xb1, 0xb2, + 0xb5, 0xbb, 0xb8, 0xb6, 0xbc, 0xb7, 0xb6, 0xbc, + 0xb9, 0xb0, 0xb3, 0xb5, 0xb2, 0xbe, 0xbf, 0xb9, + 0x5f, 0x53, 0x51, 0x5d, 0x58, 0x54, 0x5e, 0x57, + 0x56, 0x5f, 0x5b, 0x52, 0x53, 0x58, 0x54, 0x5e, + 0x59, 0x5c, 0x57, 0x50, 0x52, 0x51, 0x5d, 0x5a, + 0x5c, 0x56, 0x50, 0x59, 0x55, 0x5b, 0x5a, 0x55, + 0x50, 0x5d, 0x5e, 0x58, 0x57, 0x5a, 0x5b, 0x51, + 0x5a, 0x53, 0x54, 0x5f, 0x5d, 0x54, 0x51, 0x52, + 0x55, 0x5b, 0x58, 0x56, 0x5c, 0x57, 0x56, 0x5c, + 0x59, 0x50, 0x53, 0x55, 0x52, 0x5e, 0x5f, 0x59, + 0x9f, 0x93, 0x91, 0x9d, 0x98, 0x94, 0x9e, 0x97, + 0x96, 0x9f, 0x9b, 0x92, 0x93, 0x98, 0x94, 0x9e, + 0x99, 0x9c, 0x97, 0x90, 0x92, 0x91, 0x9d, 0x9a, + 0x9c, 0x96, 0x90, 0x99, 0x95, 0x9b, 0x9a, 0x95, + 0x90, 0x9d, 0x9e, 0x98, 0x97, 0x9a, 0x9b, 0x91, + 0x9a, 0x93, 0x94, 0x9f, 0x9d, 0x94, 0x91, 0x92, + 0x95, 0x9b, 0x98, 0x96, 0x9c, 0x97, 0x96, 0x9c, + 0x99, 0x90, 0x93, 0x95, 0x92, 0x9e, 0x9f, 0x99, + 0x9f, 0x93, 0x91, 0x9d, 0x98, 0x94, 0x9e, 0x97, + 0x96, 0x9f, 0x9b, 0x92, 0x93, 0x98, 0x94, 0x9e, + 0x99, 0x9c, 0x97, 0x90, 0x92, 0x91, 0x9d, 0x9a, + 0x9c, 0x96, 0x90, 0x99, 0x95, 0x9b, 0x9a, 0x95, + 0x90, 0x9d, 0x9e, 0x98, 0x97, 0x9a, 0x9b, 0x91, + 0x9a, 0x93, 0x94, 0x9f, 0x9d, 0x94, 0x91, 0x92, + 0x95, 0x9b, 0x98, 0x96, 0x9c, 0x97, 0x96, 0x9c, + 0x99, 0x90, 0x93, 0x95, 0x92, 0x9e, 0x9f, 0x99, + 0x5f, 0x53, 0x51, 0x5d, 0x58, 0x54, 0x5e, 0x57, + 0x56, 0x5f, 0x5b, 0x52, 0x53, 0x58, 0x54, 0x5e, + 0x59, 0x5c, 0x57, 0x50, 0x52, 0x51, 0x5d, 0x5a, + 0x5c, 0x56, 0x50, 0x59, 0x55, 0x5b, 0x5a, 0x55, + 0x50, 0x5d, 0x5e, 0x58, 0x57, 0x5a, 0x5b, 0x51, + 0x5a, 0x53, 0x54, 0x5f, 0x5d, 0x54, 0x51, 0x52, + 0x55, 0x5b, 0x58, 0x56, 0x5c, 0x57, 0x56, 0x5c, + 0x59, 0x50, 0x53, 0x55, 0x52, 0x5e, 0x5f, 0x59, + 0x0f, 0x03, 0x01, 0x0d, 0x08, 0x04, 0x0e, 0x07, + 0x06, 0x0f, 0x0b, 0x02, 0x03, 0x08, 0x04, 0x0e, + 0x09, 0x0c, 0x07, 0x00, 0x02, 0x01, 0x0d, 0x0a, + 0x0c, 0x06, 0x00, 0x09, 0x05, 0x0b, 0x0a, 0x05, + 0x00, 0x0d, 0x0e, 0x08, 0x07, 0x0a, 0x0b, 0x01, + 0x0a, 0x03, 0x04, 0x0f, 0x0d, 0x04, 0x01, 0x02, + 0x05, 0x0b, 0x08, 0x06, 0x0c, 0x07, 0x06, 0x0c, + 0x09, 0x00, 0x03, 0x05, 0x02, 0x0e, 0x0f, 0x09, + 0x3f, 0x33, 0x31, 0x3d, 0x38, 0x34, 0x3e, 0x37, + 0x36, 0x3f, 0x3b, 0x32, 0x33, 0x38, 0x34, 0x3e, + 0x39, 0x3c, 0x37, 0x30, 0x32, 0x31, 0x3d, 0x3a, + 0x3c, 0x36, 0x30, 0x39, 0x35, 0x3b, 0x3a, 0x35, + 0x30, 0x3d, 0x3e, 0x38, 0x37, 0x3a, 0x3b, 0x31, + 0x3a, 0x33, 0x34, 0x3f, 0x3d, 0x34, 0x31, 0x32, + 0x35, 0x3b, 0x38, 0x36, 0x3c, 0x37, 0x36, 0x3c, + 0x39, 0x30, 0x33, 0x35, 0x32, 0x3e, 0x3f, 0x39, + 0x7f, 0x73, 0x71, 0x7d, 0x78, 0x74, 0x7e, 0x77, + 0x76, 0x7f, 0x7b, 0x72, 0x73, 0x78, 0x74, 0x7e, + 0x79, 0x7c, 0x77, 0x70, 0x72, 0x71, 0x7d, 0x7a, + 0x7c, 0x76, 0x70, 0x79, 0x75, 0x7b, 0x7a, 0x75, + 0x70, 0x7d, 0x7e, 0x78, 0x77, 0x7a, 0x7b, 0x71, + 0x7a, 0x73, 0x74, 0x7f, 0x7d, 0x74, 0x71, 0x72, + 0x75, 0x7b, 0x78, 0x76, 0x7c, 0x77, 0x76, 0x7c, + 0x79, 0x70, 0x73, 0x75, 0x72, 0x7e, 0x7f, 0x79, + 0x8f, 0x83, 0x81, 0x8d, 0x88, 0x84, 0x8e, 0x87, + 0x86, 0x8f, 0x8b, 0x82, 0x83, 0x88, 0x84, 0x8e, + 0x89, 0x8c, 0x87, 0x80, 0x82, 0x81, 0x8d, 0x8a, + 0x8c, 0x86, 0x80, 0x89, 0x85, 0x8b, 0x8a, 0x85, + 0x80, 0x8d, 0x8e, 0x88, 0x87, 0x8a, 0x8b, 0x81, + 0x8a, 0x83, 0x84, 0x8f, 0x8d, 0x84, 0x81, 0x82, + 0x85, 0x8b, 0x88, 0x86, 0x8c, 0x87, 0x86, 0x8c, + 0x89, 0x80, 0x83, 0x85, 0x82, 0x8e, 0x8f, 0x89, + 0x4f, 0x43, 0x41, 0x4d, 0x48, 0x44, 0x4e, 0x47, + 0x46, 0x4f, 0x4b, 0x42, 0x43, 0x48, 0x44, 0x4e, + 0x49, 0x4c, 0x47, 0x40, 0x42, 0x41, 0x4d, 0x4a, + 0x4c, 0x46, 0x40, 0x49, 0x45, 0x4b, 0x4a, 0x45, + 0x40, 0x4d, 0x4e, 0x48, 0x47, 0x4a, 0x4b, 0x41, + 0x4a, 0x43, 0x44, 0x4f, 0x4d, 0x44, 0x41, 0x42, + 0x45, 0x4b, 0x48, 0x46, 0x4c, 0x47, 0x46, 0x4c, + 0x49, 0x40, 0x43, 0x45, 0x42, 0x4e, 0x4f, 0x49, + 0xff, 0xf3, 0xf1, 0xfd, 0xf8, 0xf4, 0xfe, 0xf7, + 0xf6, 0xff, 0xfb, 0xf2, 0xf3, 0xf8, 0xf4, 0xfe, + 0xf9, 0xfc, 0xf7, 0xf0, 0xf2, 0xf1, 0xfd, 0xfa, + 0xfc, 0xf6, 0xf0, 0xf9, 0xf5, 0xfb, 0xfa, 0xf5, + 0xf0, 0xfd, 0xfe, 0xf8, 0xf7, 0xfa, 0xfb, 0xf1, + 0xfa, 0xf3, 0xf4, 0xff, 0xfd, 0xf4, 0xf1, 0xf2, + 0xf5, 0xfb, 0xf8, 0xf6, 0xfc, 0xf7, 0xf6, 0xfc, + 0xf9, 0xf0, 0xf3, 0xf5, 0xf2, 0xfe, 0xff, 0xf9, + 0x1f, 0x13, 0x11, 0x1d, 0x18, 0x14, 0x1e, 0x17, + 0x16, 0x1f, 0x1b, 0x12, 0x13, 0x18, 0x14, 0x1e, + 0x19, 0x1c, 0x17, 0x10, 0x12, 0x11, 0x1d, 0x1a, + 0x1c, 0x16, 0x10, 0x19, 0x15, 0x1b, 0x1a, 0x15, + 0x10, 0x1d, 0x1e, 0x18, 0x17, 0x1a, 0x1b, 0x11, + 0x1a, 0x13, 0x14, 0x1f, 0x1d, 0x14, 0x11, 0x12, + 0x15, 0x1b, 0x18, 0x16, 0x1c, 0x17, 0x16, 0x1c, + 0x19, 0x10, 0x13, 0x15, 0x12, 0x1e, 0x1f, 0x19, + 0xcf, 0xc3, 0xc1, 0xcd, 0xc8, 0xc4, 0xce, 0xc7, + 0xc6, 0xcf, 0xcb, 0xc2, 0xc3, 0xc8, 0xc4, 0xce, + 0xc9, 0xcc, 0xc7, 0xc0, 0xc2, 0xc1, 0xcd, 0xca, + 0xcc, 0xc6, 0xc0, 0xc9, 0xc5, 0xcb, 0xca, 0xc5, + 0xc0, 0xcd, 0xce, 0xc8, 0xc7, 0xca, 0xcb, 0xc1, + 0xca, 0xc3, 0xc4, 0xcf, 0xcd, 0xc4, 0xc1, 0xc2, + 0xc5, 0xcb, 0xc8, 0xc6, 0xcc, 0xc7, 0xc6, 0xcc, + 0xc9, 0xc0, 0xc3, 0xc5, 0xc2, 0xce, 0xcf, 0xc9, + 0xef, 0xe3, 0xe1, 0xed, 0xe8, 0xe4, 0xee, 0xe7, + 0xe6, 0xef, 0xeb, 0xe2, 0xe3, 0xe8, 0xe4, 0xee, + 0xe9, 0xec, 0xe7, 0xe0, 0xe2, 0xe1, 0xed, 0xea, + 0xec, 0xe6, 0xe0, 0xe9, 0xe5, 0xeb, 0xea, 0xe5, + 0xe0, 0xed, 0xee, 0xe8, 0xe7, 0xea, 0xeb, 0xe1, + 0xea, 0xe3, 0xe4, 0xef, 0xed, 0xe4, 0xe1, 0xe2, + 0xe5, 0xeb, 0xe8, 0xe6, 0xec, 0xe7, 0xe6, 0xec, + 0xe9, 0xe0, 0xe3, 0xe5, 0xe2, 0xee, 0xef, 0xe9, + 0x8f, 0x83, 0x81, 0x8d, 0x88, 0x84, 0x8e, 0x87, + 0x86, 0x8f, 0x8b, 0x82, 0x83, 0x88, 0x84, 0x8e, + 0x89, 0x8c, 0x87, 0x80, 0x82, 0x81, 0x8d, 0x8a, + 0x8c, 0x86, 0x80, 0x89, 0x85, 0x8b, 0x8a, 0x85, + 0x80, 0x8d, 0x8e, 0x88, 0x87, 0x8a, 0x8b, 0x81, + 0x8a, 0x83, 0x84, 0x8f, 0x8d, 0x84, 0x81, 0x82, + 0x85, 0x8b, 0x88, 0x86, 0x8c, 0x87, 0x86, 0x8c, + 0x89, 0x80, 0x83, 0x85, 0x82, 0x8e, 0x8f, 0x89, + 0x8f, 0x83, 0x81, 0x8d, 0x88, 0x84, 0x8e, 0x87, + 0x86, 0x8f, 0x8b, 0x82, 0x83, 0x88, 0x84, 0x8e, + 0x89, 0x8c, 0x87, 0x80, 0x82, 0x81, 0x8d, 0x8a, + 0x8c, 0x86, 0x80, 0x89, 0x85, 0x8b, 0x8a, 0x85, + 0x80, 0x8d, 0x8e, 0x88, 0x87, 0x8a, 0x8b, 0x81, + 0x8a, 0x83, 0x84, 0x8f, 0x8d, 0x84, 0x81, 0x82, + 0x85, 0x8b, 0x88, 0x86, 0x8c, 0x87, 0x86, 0x8c, + 0x89, 0x80, 0x83, 0x85, 0x82, 0x8e, 0x8f, 0x89, + 0x2f, 0x23, 0x21, 0x2d, 0x28, 0x24, 0x2e, 0x27, + 0x26, 0x2f, 0x2b, 0x22, 0x23, 0x28, 0x24, 0x2e, + 0x29, 0x2c, 0x27, 0x20, 0x22, 0x21, 0x2d, 0x2a, + 0x2c, 0x26, 0x20, 0x29, 0x25, 0x2b, 0x2a, 0x25, + 0x20, 0x2d, 0x2e, 0x28, 0x27, 0x2a, 0x2b, 0x21, + 0x2a, 0x23, 0x24, 0x2f, 0x2d, 0x24, 0x21, 0x22, + 0x25, 0x2b, 0x28, 0x26, 0x2c, 0x27, 0x26, 0x2c, + 0x29, 0x20, 0x23, 0x25, 0x22, 0x2e, 0x2f, 0x29, + 0xdf, 0xd3, 0xd1, 0xdd, 0xd8, 0xd4, 0xde, 0xd7, + 0xd6, 0xdf, 0xdb, 0xd2, 0xd3, 0xd8, 0xd4, 0xde, + 0xd9, 0xdc, 0xd7, 0xd0, 0xd2, 0xd1, 0xdd, 0xda, + 0xdc, 0xd6, 0xd0, 0xd9, 0xd5, 0xdb, 0xda, 0xd5, + 0xd0, 0xdd, 0xde, 0xd8, 0xd7, 0xda, 0xdb, 0xd1, + 0xda, 0xd3, 0xd4, 0xdf, 0xdd, 0xd4, 0xd1, 0xd2, + 0xd5, 0xdb, 0xd8, 0xd6, 0xdc, 0xd7, 0xd6, 0xdc, + 0xd9, 0xd0, 0xd3, 0xd5, 0xd2, 0xde, 0xdf, 0xd9, + 0x4f, 0x43, 0x41, 0x4d, 0x48, 0x44, 0x4e, 0x47, + 0x46, 0x4f, 0x4b, 0x42, 0x43, 0x48, 0x44, 0x4e, + 0x49, 0x4c, 0x47, 0x40, 0x42, 0x41, 0x4d, 0x4a, + 0x4c, 0x46, 0x40, 0x49, 0x45, 0x4b, 0x4a, 0x45, + 0x40, 0x4d, 0x4e, 0x48, 0x47, 0x4a, 0x4b, 0x41, + 0x4a, 0x43, 0x44, 0x4f, 0x4d, 0x44, 0x41, 0x42, + 0x45, 0x4b, 0x48, 0x46, 0x4c, 0x47, 0x46, 0x4c, + 0x49, 0x40, 0x43, 0x45, 0x42, 0x4e, 0x4f, 0x49, + 0x6f, 0x63, 0x61, 0x6d, 0x68, 0x64, 0x6e, 0x67, + 0x66, 0x6f, 0x6b, 0x62, 0x63, 0x68, 0x64, 0x6e, + 0x69, 0x6c, 0x67, 0x60, 0x62, 0x61, 0x6d, 0x6a, + 0x6c, 0x66, 0x60, 0x69, 0x65, 0x6b, 0x6a, 0x65, + 0x60, 0x6d, 0x6e, 0x68, 0x67, 0x6a, 0x6b, 0x61, + 0x6a, 0x63, 0x64, 0x6f, 0x6d, 0x64, 0x61, 0x62, + 0x65, 0x6b, 0x68, 0x66, 0x6c, 0x67, 0x66, 0x6c, + 0x69, 0x60, 0x63, 0x65, 0x62, 0x6e, 0x6f, 0x69, + 0x9f, 0x93, 0x91, 0x9d, 0x98, 0x94, 0x9e, 0x97, + 0x96, 0x9f, 0x9b, 0x92, 0x93, 0x98, 0x94, 0x9e, + 0x99, 0x9c, 0x97, 0x90, 0x92, 0x91, 0x9d, 0x9a, + 0x9c, 0x96, 0x90, 0x99, 0x95, 0x9b, 0x9a, 0x95, + 0x90, 0x9d, 0x9e, 0x98, 0x97, 0x9a, 0x9b, 0x91, + 0x9a, 0x93, 0x94, 0x9f, 0x9d, 0x94, 0x91, 0x92, + 0x95, 0x9b, 0x98, 0x96, 0x9c, 0x97, 0x96, 0x9c, + 0x99, 0x90, 0x93, 0x95, 0x92, 0x9e, 0x9f, 0x99, + 0x2f, 0x23, 0x21, 0x2d, 0x28, 0x24, 0x2e, 0x27, + 0x26, 0x2f, 0x2b, 0x22, 0x23, 0x28, 0x24, 0x2e, + 0x29, 0x2c, 0x27, 0x20, 0x22, 0x21, 0x2d, 0x2a, + 0x2c, 0x26, 0x20, 0x29, 0x25, 0x2b, 0x2a, 0x25, + 0x20, 0x2d, 0x2e, 0x28, 0x27, 0x2a, 0x2b, 0x21, + 0x2a, 0x23, 0x24, 0x2f, 0x2d, 0x24, 0x21, 0x22, + 0x25, 0x2b, 0x28, 0x26, 0x2c, 0x27, 0x26, 0x2c, + 0x29, 0x20, 0x23, 0x25, 0x22, 0x2e, 0x2f, 0x29, + 0x1f, 0x13, 0x11, 0x1d, 0x18, 0x14, 0x1e, 0x17, + 0x16, 0x1f, 0x1b, 0x12, 0x13, 0x18, 0x14, 0x1e, + 0x19, 0x1c, 0x17, 0x10, 0x12, 0x11, 0x1d, 0x1a, + 0x1c, 0x16, 0x10, 0x19, 0x15, 0x1b, 0x1a, 0x15, + 0x10, 0x1d, 0x1e, 0x18, 0x17, 0x1a, 0x1b, 0x11, + 0x1a, 0x13, 0x14, 0x1f, 0x1d, 0x14, 0x11, 0x12, + 0x15, 0x1b, 0x18, 0x16, 0x1c, 0x17, 0x16, 0x1c, + 0x19, 0x10, 0x13, 0x15, 0x12, 0x1e, 0x1f, 0x19, + 0xbf, 0xb3, 0xb1, 0xbd, 0xb8, 0xb4, 0xbe, 0xb7, + 0xb6, 0xbf, 0xbb, 0xb2, 0xb3, 0xb8, 0xb4, 0xbe, + 0xb9, 0xbc, 0xb7, 0xb0, 0xb2, 0xb1, 0xbd, 0xba, + 0xbc, 0xb6, 0xb0, 0xb9, 0xb5, 0xbb, 0xba, 0xb5, + 0xb0, 0xbd, 0xbe, 0xb8, 0xb7, 0xba, 0xbb, 0xb1, + 0xba, 0xb3, 0xb4, 0xbf, 0xbd, 0xb4, 0xb1, 0xb2, + 0xb5, 0xbb, 0xb8, 0xb6, 0xbc, 0xb7, 0xb6, 0xbc, + 0xb9, 0xb0, 0xb3, 0xb5, 0xb2, 0xbe, 0xbf, 0xb9, + 0x7f, 0x73, 0x71, 0x7d, 0x78, 0x74, 0x7e, 0x77, + 0x76, 0x7f, 0x7b, 0x72, 0x73, 0x78, 0x74, 0x7e, + 0x79, 0x7c, 0x77, 0x70, 0x72, 0x71, 0x7d, 0x7a, + 0x7c, 0x76, 0x70, 0x79, 0x75, 0x7b, 0x7a, 0x75, + 0x70, 0x7d, 0x7e, 0x78, 0x77, 0x7a, 0x7b, 0x71, + 0x7a, 0x73, 0x74, 0x7f, 0x7d, 0x74, 0x71, 0x72, + 0x75, 0x7b, 0x78, 0x76, 0x7c, 0x77, 0x76, 0x7c, + 0x79, 0x70, 0x73, 0x75, 0x72, 0x7e, 0x7f, 0x79, + 0xff, 0xf3, 0xf1, 0xfd, 0xf8, 0xf4, 0xfe, 0xf7, + 0xf6, 0xff, 0xfb, 0xf2, 0xf3, 0xf8, 0xf4, 0xfe, + 0xf9, 0xfc, 0xf7, 0xf0, 0xf2, 0xf1, 0xfd, 0xfa, + 0xfc, 0xf6, 0xf0, 0xf9, 0xf5, 0xfb, 0xfa, 0xf5, + 0xf0, 0xfd, 0xfe, 0xf8, 0xf7, 0xfa, 0xfb, 0xf1, + 0xfa, 0xf3, 0xf4, 0xff, 0xfd, 0xf4, 0xf1, 0xf2, + 0xf5, 0xfb, 0xf8, 0xf6, 0xfc, 0xf7, 0xf6, 0xfc, + 0xf9, 0xf0, 0xf3, 0xf5, 0xf2, 0xfe, 0xff, 0xf9, + 0x5f, 0x53, 0x51, 0x5d, 0x58, 0x54, 0x5e, 0x57, + 0x56, 0x5f, 0x5b, 0x52, 0x53, 0x58, 0x54, 0x5e, + 0x59, 0x5c, 0x57, 0x50, 0x52, 0x51, 0x5d, 0x5a, + 0x5c, 0x56, 0x50, 0x59, 0x55, 0x5b, 0x5a, 0x55, + 0x50, 0x5d, 0x5e, 0x58, 0x57, 0x5a, 0x5b, 0x51, + 0x5a, 0x53, 0x54, 0x5f, 0x5d, 0x54, 0x51, 0x52, + 0x55, 0x5b, 0x58, 0x56, 0x5c, 0x57, 0x56, 0x5c, + 0x59, 0x50, 0x53, 0x55, 0x52, 0x5e, 0x5f, 0x59, + 0xcf, 0xc3, 0xc1, 0xcd, 0xc8, 0xc4, 0xce, 0xc7, + 0xc6, 0xcf, 0xcb, 0xc2, 0xc3, 0xc8, 0xc4, 0xce, + 0xc9, 0xcc, 0xc7, 0xc0, 0xc2, 0xc1, 0xcd, 0xca, + 0xcc, 0xc6, 0xc0, 0xc9, 0xc5, 0xcb, 0xca, 0xc5, + 0xc0, 0xcd, 0xce, 0xc8, 0xc7, 0xca, 0xcb, 0xc1, + 0xca, 0xc3, 0xc4, 0xcf, 0xcd, 0xc4, 0xc1, 0xc2, + 0xc5, 0xcb, 0xc8, 0xc6, 0xcc, 0xc7, 0xc6, 0xcc, + 0xc9, 0xc0, 0xc3, 0xc5, 0xc2, 0xce, 0xcf, 0xc9, + 0xbf, 0xb3, 0xb1, 0xbd, 0xb8, 0xb4, 0xbe, 0xb7, + 0xb6, 0xbf, 0xbb, 0xb2, 0xb3, 0xb8, 0xb4, 0xbe, + 0xb9, 0xbc, 0xb7, 0xb0, 0xb2, 0xb1, 0xbd, 0xba, + 0xbc, 0xb6, 0xb0, 0xb9, 0xb5, 0xbb, 0xba, 0xb5, + 0xb0, 0xbd, 0xbe, 0xb8, 0xb7, 0xba, 0xbb, 0xb1, + 0xba, 0xb3, 0xb4, 0xbf, 0xbd, 0xb4, 0xb1, 0xb2, + 0xb5, 0xbb, 0xb8, 0xb6, 0xbc, 0xb7, 0xb6, 0xbc, + 0xb9, 0xb0, 0xb3, 0xb5, 0xb2, 0xbe, 0xbf, 0xb9, + 0x9f, 0x93, 0x91, 0x9d, 0x98, 0x94, 0x9e, 0x97, + 0x96, 0x9f, 0x9b, 0x92, 0x93, 0x98, 0x94, 0x9e, + 0x99, 0x9c, 0x97, 0x90, 0x92, 0x91, 0x9d, 0x9a, + 0x9c, 0x96, 0x90, 0x99, 0x95, 0x9b, 0x9a, 0x95, + 0x90, 0x9d, 0x9e, 0x98, 0x97, 0x9a, 0x9b, 0x91, + 0x9a, 0x93, 0x94, 0x9f, 0x9d, 0x94, 0x91, 0x92, + 0x95, 0x9b, 0x98, 0x96, 0x9c, 0x97, 0x96, 0x9c, + 0x99, 0x90, 0x93, 0x95, 0x92, 0x9e, 0x9f, 0x99, + 0x3f, 0x33, 0x31, 0x3d, 0x38, 0x34, 0x3e, 0x37, + 0x36, 0x3f, 0x3b, 0x32, 0x33, 0x38, 0x34, 0x3e, + 0x39, 0x3c, 0x37, 0x30, 0x32, 0x31, 0x3d, 0x3a, + 0x3c, 0x36, 0x30, 0x39, 0x35, 0x3b, 0x3a, 0x35, + 0x30, 0x3d, 0x3e, 0x38, 0x37, 0x3a, 0x3b, 0x31, + 0x3a, 0x33, 0x34, 0x3f, 0x3d, 0x34, 0x31, 0x32, + 0x35, 0x3b, 0x38, 0x36, 0x3c, 0x37, 0x36, 0x3c, + 0x39, 0x30, 0x33, 0x35, 0x32, 0x3e, 0x3f, 0x39, + 0x7f, 0x73, 0x71, 0x7d, 0x78, 0x74, 0x7e, 0x77, + 0x76, 0x7f, 0x7b, 0x72, 0x73, 0x78, 0x74, 0x7e, + 0x79, 0x7c, 0x77, 0x70, 0x72, 0x71, 0x7d, 0x7a, + 0x7c, 0x76, 0x70, 0x79, 0x75, 0x7b, 0x7a, 0x75, + 0x70, 0x7d, 0x7e, 0x78, 0x77, 0x7a, 0x7b, 0x71, + 0x7a, 0x73, 0x74, 0x7f, 0x7d, 0x74, 0x71, 0x72, + 0x75, 0x7b, 0x78, 0x76, 0x7c, 0x77, 0x76, 0x7c, + 0x79, 0x70, 0x73, 0x75, 0x72, 0x7e, 0x7f, 0x79, + 0xef, 0xe3, 0xe1, 0xed, 0xe8, 0xe4, 0xee, 0xe7, + 0xe6, 0xef, 0xeb, 0xe2, 0xe3, 0xe8, 0xe4, 0xee, + 0xe9, 0xec, 0xe7, 0xe0, 0xe2, 0xe1, 0xed, 0xea, + 0xec, 0xe6, 0xe0, 0xe9, 0xe5, 0xeb, 0xea, 0xe5, + 0xe0, 0xed, 0xee, 0xe8, 0xe7, 0xea, 0xeb, 0xe1, + 0xea, 0xe3, 0xe4, 0xef, 0xed, 0xe4, 0xe1, 0xe2, + 0xe5, 0xeb, 0xe8, 0xe6, 0xec, 0xe7, 0xe6, 0xec, + 0xe9, 0xe0, 0xe3, 0xe5, 0xe2, 0xee, 0xef, 0xe9, + 0x3f, 0x33, 0x31, 0x3d, 0x38, 0x34, 0x3e, 0x37, + 0x36, 0x3f, 0x3b, 0x32, 0x33, 0x38, 0x34, 0x3e, + 0x39, 0x3c, 0x37, 0x30, 0x32, 0x31, 0x3d, 0x3a, + 0x3c, 0x36, 0x30, 0x39, 0x35, 0x3b, 0x3a, 0x35, + 0x30, 0x3d, 0x3e, 0x38, 0x37, 0x3a, 0x3b, 0x31, + 0x3a, 0x33, 0x34, 0x3f, 0x3d, 0x34, 0x31, 0x32, + 0x35, 0x3b, 0x38, 0x36, 0x3c, 0x37, 0x36, 0x3c, + 0x39, 0x30, 0x33, 0x35, 0x32, 0x3e, 0x3f, 0x39, + 0xaf, 0xa3, 0xa1, 0xad, 0xa8, 0xa4, 0xae, 0xa7, + 0xa6, 0xaf, 0xab, 0xa2, 0xa3, 0xa8, 0xa4, 0xae, + 0xa9, 0xac, 0xa7, 0xa0, 0xa2, 0xa1, 0xad, 0xaa, + 0xac, 0xa6, 0xa0, 0xa9, 0xa5, 0xab, 0xaa, 0xa5, + 0xa0, 0xad, 0xae, 0xa8, 0xa7, 0xaa, 0xab, 0xa1, + 0xaa, 0xa3, 0xa4, 0xaf, 0xad, 0xa4, 0xa1, 0xa2, + 0xa5, 0xab, 0xa8, 0xa6, 0xac, 0xa7, 0xa6, 0xac, + 0xa9, 0xa0, 0xa3, 0xa5, 0xa2, 0xae, 0xaf, 0xa9, + 0xaf, 0xa3, 0xa1, 0xad, 0xa8, 0xa4, 0xae, 0xa7, + 0xa6, 0xaf, 0xab, 0xa2, 0xa3, 0xa8, 0xa4, 0xae, + 0xa9, 0xac, 0xa7, 0xa0, 0xa2, 0xa1, 0xad, 0xaa, + 0xac, 0xa6, 0xa0, 0xa9, 0xa5, 0xab, 0xaa, 0xa5, + 0xa0, 0xad, 0xae, 0xa8, 0xa7, 0xaa, 0xab, 0xa1, + 0xaa, 0xa3, 0xa4, 0xaf, 0xad, 0xa4, 0xa1, 0xa2, + 0xa5, 0xab, 0xa8, 0xa6, 0xac, 0xa7, 0xa6, 0xac, + 0xa9, 0xa0, 0xa3, 0xa5, 0xa2, 0xae, 0xaf, 0xa9, + 0x0f, 0x03, 0x01, 0x0d, 0x08, 0x04, 0x0e, 0x07, + 0x06, 0x0f, 0x0b, 0x02, 0x03, 0x08, 0x04, 0x0e, + 0x09, 0x0c, 0x07, 0x00, 0x02, 0x01, 0x0d, 0x0a, + 0x0c, 0x06, 0x00, 0x09, 0x05, 0x0b, 0x0a, 0x05, + 0x00, 0x0d, 0x0e, 0x08, 0x07, 0x0a, 0x0b, 0x01, + 0x0a, 0x03, 0x04, 0x0f, 0x0d, 0x04, 0x01, 0x02, + 0x05, 0x0b, 0x08, 0x06, 0x0c, 0x07, 0x06, 0x0c, + 0x09, 0x00, 0x03, 0x05, 0x02, 0x0e, 0x0f, 0x09, + 0x5f, 0x53, 0x51, 0x5d, 0x58, 0x54, 0x5e, 0x57, + 0x56, 0x5f, 0x5b, 0x52, 0x53, 0x58, 0x54, 0x5e, + 0x59, 0x5c, 0x57, 0x50, 0x52, 0x51, 0x5d, 0x5a, + 0x5c, 0x56, 0x50, 0x59, 0x55, 0x5b, 0x5a, 0x55, + 0x50, 0x5d, 0x5e, 0x58, 0x57, 0x5a, 0x5b, 0x51, + 0x5a, 0x53, 0x54, 0x5f, 0x5d, 0x54, 0x51, 0x52, + 0x55, 0x5b, 0x58, 0x56, 0x5c, 0x57, 0x56, 0x5c, + 0x59, 0x50, 0x53, 0x55, 0x52, 0x5e, 0x5f, 0x59, + 0x6f, 0x63, 0x61, 0x6d, 0x68, 0x64, 0x6e, 0x67, + 0x66, 0x6f, 0x6b, 0x62, 0x63, 0x68, 0x64, 0x6e, + 0x69, 0x6c, 0x67, 0x60, 0x62, 0x61, 0x6d, 0x6a, + 0x6c, 0x66, 0x60, 0x69, 0x65, 0x6b, 0x6a, 0x65, + 0x60, 0x6d, 0x6e, 0x68, 0x67, 0x6a, 0x6b, 0x61, + 0x6a, 0x63, 0x64, 0x6f, 0x6d, 0x64, 0x61, 0x62, + 0x65, 0x6b, 0x68, 0x66, 0x6c, 0x67, 0x66, 0x6c, + 0x69, 0x60, 0x63, 0x65, 0x62, 0x6e, 0x6f, 0x69, + 0x0f, 0x03, 0x01, 0x0d, 0x08, 0x04, 0x0e, 0x07, + 0x06, 0x0f, 0x0b, 0x02, 0x03, 0x08, 0x04, 0x0e, + 0x09, 0x0c, 0x07, 0x00, 0x02, 0x01, 0x0d, 0x0a, + 0x0c, 0x06, 0x00, 0x09, 0x05, 0x0b, 0x0a, 0x05, + 0x00, 0x0d, 0x0e, 0x08, 0x07, 0x0a, 0x0b, 0x01, + 0x0a, 0x03, 0x04, 0x0f, 0x0d, 0x04, 0x01, 0x02, + 0x05, 0x0b, 0x08, 0x06, 0x0c, 0x07, 0x06, 0x0c, + 0x09, 0x00, 0x03, 0x05, 0x02, 0x0e, 0x0f, 0x09, + 0xdf, 0xd3, 0xd1, 0xdd, 0xd8, 0xd4, 0xde, 0xd7, + 0xd6, 0xdf, 0xdb, 0xd2, 0xd3, 0xd8, 0xd4, 0xde, + 0xd9, 0xdc, 0xd7, 0xd0, 0xd2, 0xd1, 0xdd, 0xda, + 0xdc, 0xd6, 0xd0, 0xd9, 0xd5, 0xdb, 0xda, 0xd5, + 0xd0, 0xdd, 0xde, 0xd8, 0xd7, 0xda, 0xdb, 0xd1, + 0xda, 0xd3, 0xd4, 0xdf, 0xdd, 0xd4, 0xd1, 0xd2, + 0xd5, 0xdb, 0xd8, 0xd6, 0xdc, 0xd7, 0xd6, 0xdc, + 0xd9, 0xd0, 0xd3, 0xd5, 0xd2, 0xde, 0xdf, 0xd9, + },{ + 0xa7, 0xad, 0xad, 0xa8, 0xae, 0xab, 0xa3, 0xa5, + 0xa0, 0xa6, 0xa6, 0xaf, 0xa9, 0xa0, 0xaa, 0xa3, + 0xa1, 0xa4, 0xa2, 0xa7, 0xa8, 0xa2, 0xa5, 0xac, + 0xab, 0xa1, 0xac, 0xaa, 0xa4, 0xae, 0xaf, 0xa9, + 0xaa, 0xa3, 0xa6, 0xaf, 0xa9, 0xa0, 0xa0, 0xa6, + 0xac, 0xaa, 0xab, 0xa1, 0xa7, 0xad, 0xad, 0xa8, + 0xaf, 0xa9, 0xa1, 0xa4, 0xa3, 0xa5, 0xae, 0xab, + 0xa5, 0xac, 0xa2, 0xa7, 0xa8, 0xa2, 0xa4, 0xae, + 0xd7, 0xdd, 0xdd, 0xd8, 0xde, 0xdb, 0xd3, 0xd5, + 0xd0, 0xd6, 0xd6, 0xdf, 0xd9, 0xd0, 0xda, 0xd3, + 0xd1, 0xd4, 0xd2, 0xd7, 0xd8, 0xd2, 0xd5, 0xdc, + 0xdb, 0xd1, 0xdc, 0xda, 0xd4, 0xde, 0xdf, 0xd9, + 0xda, 0xd3, 0xd6, 0xdf, 0xd9, 0xd0, 0xd0, 0xd6, + 0xdc, 0xda, 0xdb, 0xd1, 0xd7, 0xdd, 0xdd, 0xd8, + 0xdf, 0xd9, 0xd1, 0xd4, 0xd3, 0xd5, 0xde, 0xdb, + 0xd5, 0xdc, 0xd2, 0xd7, 0xd8, 0xd2, 0xd4, 0xde, + 0x07, 0x0d, 0x0d, 0x08, 0x0e, 0x0b, 0x03, 0x05, + 0x00, 0x06, 0x06, 0x0f, 0x09, 0x00, 0x0a, 0x03, + 0x01, 0x04, 0x02, 0x07, 0x08, 0x02, 0x05, 0x0c, + 0x0b, 0x01, 0x0c, 0x0a, 0x04, 0x0e, 0x0f, 0x09, + 0x0a, 0x03, 0x06, 0x0f, 0x09, 0x00, 0x00, 0x06, + 0x0c, 0x0a, 0x0b, 0x01, 0x07, 0x0d, 0x0d, 0x08, + 0x0f, 0x09, 0x01, 0x04, 0x03, 0x05, 0x0e, 0x0b, + 0x05, 0x0c, 0x02, 0x07, 0x08, 0x02, 0x04, 0x0e, + 0x77, 0x7d, 0x7d, 0x78, 0x7e, 0x7b, 0x73, 0x75, + 0x70, 0x76, 0x76, 0x7f, 0x79, 0x70, 0x7a, 0x73, + 0x71, 0x74, 0x72, 0x77, 0x78, 0x72, 0x75, 0x7c, + 0x7b, 0x71, 0x7c, 0x7a, 0x74, 0x7e, 0x7f, 0x79, + 0x7a, 0x73, 0x76, 0x7f, 0x79, 0x70, 0x70, 0x76, + 0x7c, 0x7a, 0x7b, 0x71, 0x77, 0x7d, 0x7d, 0x78, + 0x7f, 0x79, 0x71, 0x74, 0x73, 0x75, 0x7e, 0x7b, + 0x75, 0x7c, 0x72, 0x77, 0x78, 0x72, 0x74, 0x7e, + 0x97, 0x9d, 0x9d, 0x98, 0x9e, 0x9b, 0x93, 0x95, + 0x90, 0x96, 0x96, 0x9f, 0x99, 0x90, 0x9a, 0x93, + 0x91, 0x94, 0x92, 0x97, 0x98, 0x92, 0x95, 0x9c, + 0x9b, 0x91, 0x9c, 0x9a, 0x94, 0x9e, 0x9f, 0x99, + 0x9a, 0x93, 0x96, 0x9f, 0x99, 0x90, 0x90, 0x96, + 0x9c, 0x9a, 0x9b, 0x91, 0x97, 0x9d, 0x9d, 0x98, + 0x9f, 0x99, 0x91, 0x94, 0x93, 0x95, 0x9e, 0x9b, + 0x95, 0x9c, 0x92, 0x97, 0x98, 0x92, 0x94, 0x9e, + 0x07, 0x0d, 0x0d, 0x08, 0x0e, 0x0b, 0x03, 0x05, + 0x00, 0x06, 0x06, 0x0f, 0x09, 0x00, 0x0a, 0x03, + 0x01, 0x04, 0x02, 0x07, 0x08, 0x02, 0x05, 0x0c, + 0x0b, 0x01, 0x0c, 0x0a, 0x04, 0x0e, 0x0f, 0x09, + 0x0a, 0x03, 0x06, 0x0f, 0x09, 0x00, 0x00, 0x06, + 0x0c, 0x0a, 0x0b, 0x01, 0x07, 0x0d, 0x0d, 0x08, + 0x0f, 0x09, 0x01, 0x04, 0x03, 0x05, 0x0e, 0x0b, + 0x05, 0x0c, 0x02, 0x07, 0x08, 0x02, 0x04, 0x0e, + 0xe7, 0xed, 0xed, 0xe8, 0xee, 0xeb, 0xe3, 0xe5, + 0xe0, 0xe6, 0xe6, 0xef, 0xe9, 0xe0, 0xea, 0xe3, + 0xe1, 0xe4, 0xe2, 0xe7, 0xe8, 0xe2, 0xe5, 0xec, + 0xeb, 0xe1, 0xec, 0xea, 0xe4, 0xee, 0xef, 0xe9, + 0xea, 0xe3, 0xe6, 0xef, 0xe9, 0xe0, 0xe0, 0xe6, + 0xec, 0xea, 0xeb, 0xe1, 0xe7, 0xed, 0xed, 0xe8, + 0xef, 0xe9, 0xe1, 0xe4, 0xe3, 0xe5, 0xee, 0xeb, + 0xe5, 0xec, 0xe2, 0xe7, 0xe8, 0xe2, 0xe4, 0xee, + 0x97, 0x9d, 0x9d, 0x98, 0x9e, 0x9b, 0x93, 0x95, + 0x90, 0x96, 0x96, 0x9f, 0x99, 0x90, 0x9a, 0x93, + 0x91, 0x94, 0x92, 0x97, 0x98, 0x92, 0x95, 0x9c, + 0x9b, 0x91, 0x9c, 0x9a, 0x94, 0x9e, 0x9f, 0x99, + 0x9a, 0x93, 0x96, 0x9f, 0x99, 0x90, 0x90, 0x96, + 0x9c, 0x9a, 0x9b, 0x91, 0x97, 0x9d, 0x9d, 0x98, + 0x9f, 0x99, 0x91, 0x94, 0x93, 0x95, 0x9e, 0x9b, + 0x95, 0x9c, 0x92, 0x97, 0x98, 0x92, 0x94, 0x9e, + 0x67, 0x6d, 0x6d, 0x68, 0x6e, 0x6b, 0x63, 0x65, + 0x60, 0x66, 0x66, 0x6f, 0x69, 0x60, 0x6a, 0x63, + 0x61, 0x64, 0x62, 0x67, 0x68, 0x62, 0x65, 0x6c, + 0x6b, 0x61, 0x6c, 0x6a, 0x64, 0x6e, 0x6f, 0x69, + 0x6a, 0x63, 0x66, 0x6f, 0x69, 0x60, 0x60, 0x66, + 0x6c, 0x6a, 0x6b, 0x61, 0x67, 0x6d, 0x6d, 0x68, + 0x6f, 0x69, 0x61, 0x64, 0x63, 0x65, 0x6e, 0x6b, + 0x65, 0x6c, 0x62, 0x67, 0x68, 0x62, 0x64, 0x6e, + 0x37, 0x3d, 0x3d, 0x38, 0x3e, 0x3b, 0x33, 0x35, + 0x30, 0x36, 0x36, 0x3f, 0x39, 0x30, 0x3a, 0x33, + 0x31, 0x34, 0x32, 0x37, 0x38, 0x32, 0x35, 0x3c, + 0x3b, 0x31, 0x3c, 0x3a, 0x34, 0x3e, 0x3f, 0x39, + 0x3a, 0x33, 0x36, 0x3f, 0x39, 0x30, 0x30, 0x36, + 0x3c, 0x3a, 0x3b, 0x31, 0x37, 0x3d, 0x3d, 0x38, + 0x3f, 0x39, 0x31, 0x34, 0x33, 0x35, 0x3e, 0x3b, + 0x35, 0x3c, 0x32, 0x37, 0x38, 0x32, 0x34, 0x3e, + 0x37, 0x3d, 0x3d, 0x38, 0x3e, 0x3b, 0x33, 0x35, + 0x30, 0x36, 0x36, 0x3f, 0x39, 0x30, 0x3a, 0x33, + 0x31, 0x34, 0x32, 0x37, 0x38, 0x32, 0x35, 0x3c, + 0x3b, 0x31, 0x3c, 0x3a, 0x34, 0x3e, 0x3f, 0x39, + 0x3a, 0x33, 0x36, 0x3f, 0x39, 0x30, 0x30, 0x36, + 0x3c, 0x3a, 0x3b, 0x31, 0x37, 0x3d, 0x3d, 0x38, + 0x3f, 0x39, 0x31, 0x34, 0x33, 0x35, 0x3e, 0x3b, + 0x35, 0x3c, 0x32, 0x37, 0x38, 0x32, 0x34, 0x3e, + 0x47, 0x4d, 0x4d, 0x48, 0x4e, 0x4b, 0x43, 0x45, + 0x40, 0x46, 0x46, 0x4f, 0x49, 0x40, 0x4a, 0x43, + 0x41, 0x44, 0x42, 0x47, 0x48, 0x42, 0x45, 0x4c, + 0x4b, 0x41, 0x4c, 0x4a, 0x44, 0x4e, 0x4f, 0x49, + 0x4a, 0x43, 0x46, 0x4f, 0x49, 0x40, 0x40, 0x46, + 0x4c, 0x4a, 0x4b, 0x41, 0x47, 0x4d, 0x4d, 0x48, + 0x4f, 0x49, 0x41, 0x44, 0x43, 0x45, 0x4e, 0x4b, + 0x45, 0x4c, 0x42, 0x47, 0x48, 0x42, 0x44, 0x4e, + 0xf7, 0xfd, 0xfd, 0xf8, 0xfe, 0xfb, 0xf3, 0xf5, + 0xf0, 0xf6, 0xf6, 0xff, 0xf9, 0xf0, 0xfa, 0xf3, + 0xf1, 0xf4, 0xf2, 0xf7, 0xf8, 0xf2, 0xf5, 0xfc, + 0xfb, 0xf1, 0xfc, 0xfa, 0xf4, 0xfe, 0xff, 0xf9, + 0xfa, 0xf3, 0xf6, 0xff, 0xf9, 0xf0, 0xf0, 0xf6, + 0xfc, 0xfa, 0xfb, 0xf1, 0xf7, 0xfd, 0xfd, 0xf8, + 0xff, 0xf9, 0xf1, 0xf4, 0xf3, 0xf5, 0xfe, 0xfb, + 0xf5, 0xfc, 0xf2, 0xf7, 0xf8, 0xf2, 0xf4, 0xfe, + 0x67, 0x6d, 0x6d, 0x68, 0x6e, 0x6b, 0x63, 0x65, + 0x60, 0x66, 0x66, 0x6f, 0x69, 0x60, 0x6a, 0x63, + 0x61, 0x64, 0x62, 0x67, 0x68, 0x62, 0x65, 0x6c, + 0x6b, 0x61, 0x6c, 0x6a, 0x64, 0x6e, 0x6f, 0x69, + 0x6a, 0x63, 0x66, 0x6f, 0x69, 0x60, 0x60, 0x66, + 0x6c, 0x6a, 0x6b, 0x61, 0x67, 0x6d, 0x6d, 0x68, + 0x6f, 0x69, 0x61, 0x64, 0x63, 0x65, 0x6e, 0x6b, + 0x65, 0x6c, 0x62, 0x67, 0x68, 0x62, 0x64, 0x6e, + 0x57, 0x5d, 0x5d, 0x58, 0x5e, 0x5b, 0x53, 0x55, + 0x50, 0x56, 0x56, 0x5f, 0x59, 0x50, 0x5a, 0x53, + 0x51, 0x54, 0x52, 0x57, 0x58, 0x52, 0x55, 0x5c, + 0x5b, 0x51, 0x5c, 0x5a, 0x54, 0x5e, 0x5f, 0x59, + 0x5a, 0x53, 0x56, 0x5f, 0x59, 0x50, 0x50, 0x56, + 0x5c, 0x5a, 0x5b, 0x51, 0x57, 0x5d, 0x5d, 0x58, + 0x5f, 0x59, 0x51, 0x54, 0x53, 0x55, 0x5e, 0x5b, + 0x55, 0x5c, 0x52, 0x57, 0x58, 0x52, 0x54, 0x5e, + 0xa7, 0xad, 0xad, 0xa8, 0xae, 0xab, 0xa3, 0xa5, + 0xa0, 0xa6, 0xa6, 0xaf, 0xa9, 0xa0, 0xaa, 0xa3, + 0xa1, 0xa4, 0xa2, 0xa7, 0xa8, 0xa2, 0xa5, 0xac, + 0xab, 0xa1, 0xac, 0xaa, 0xa4, 0xae, 0xaf, 0xa9, + 0xaa, 0xa3, 0xa6, 0xaf, 0xa9, 0xa0, 0xa0, 0xa6, + 0xac, 0xaa, 0xab, 0xa1, 0xa7, 0xad, 0xad, 0xa8, + 0xaf, 0xa9, 0xa1, 0xa4, 0xa3, 0xa5, 0xae, 0xab, + 0xa5, 0xac, 0xa2, 0xa7, 0xa8, 0xa2, 0xa4, 0xae, + 0x17, 0x1d, 0x1d, 0x18, 0x1e, 0x1b, 0x13, 0x15, + 0x10, 0x16, 0x16, 0x1f, 0x19, 0x10, 0x1a, 0x13, + 0x11, 0x14, 0x12, 0x17, 0x18, 0x12, 0x15, 0x1c, + 0x1b, 0x11, 0x1c, 0x1a, 0x14, 0x1e, 0x1f, 0x19, + 0x1a, 0x13, 0x16, 0x1f, 0x19, 0x10, 0x10, 0x16, + 0x1c, 0x1a, 0x1b, 0x11, 0x17, 0x1d, 0x1d, 0x18, + 0x1f, 0x19, 0x11, 0x14, 0x13, 0x15, 0x1e, 0x1b, + 0x15, 0x1c, 0x12, 0x17, 0x18, 0x12, 0x14, 0x1e, + 0x27, 0x2d, 0x2d, 0x28, 0x2e, 0x2b, 0x23, 0x25, + 0x20, 0x26, 0x26, 0x2f, 0x29, 0x20, 0x2a, 0x23, + 0x21, 0x24, 0x22, 0x27, 0x28, 0x22, 0x25, 0x2c, + 0x2b, 0x21, 0x2c, 0x2a, 0x24, 0x2e, 0x2f, 0x29, + 0x2a, 0x23, 0x26, 0x2f, 0x29, 0x20, 0x20, 0x26, + 0x2c, 0x2a, 0x2b, 0x21, 0x27, 0x2d, 0x2d, 0x28, + 0x2f, 0x29, 0x21, 0x24, 0x23, 0x25, 0x2e, 0x2b, + 0x25, 0x2c, 0x22, 0x27, 0x28, 0x22, 0x24, 0x2e, + 0xd7, 0xdd, 0xdd, 0xd8, 0xde, 0xdb, 0xd3, 0xd5, + 0xd0, 0xd6, 0xd6, 0xdf, 0xd9, 0xd0, 0xda, 0xd3, + 0xd1, 0xd4, 0xd2, 0xd7, 0xd8, 0xd2, 0xd5, 0xdc, + 0xdb, 0xd1, 0xdc, 0xda, 0xd4, 0xde, 0xdf, 0xd9, + 0xda, 0xd3, 0xd6, 0xdf, 0xd9, 0xd0, 0xd0, 0xd6, + 0xdc, 0xda, 0xdb, 0xd1, 0xd7, 0xdd, 0xdd, 0xd8, + 0xdf, 0xd9, 0xd1, 0xd4, 0xd3, 0xd5, 0xde, 0xdb, + 0xd5, 0xdc, 0xd2, 0xd7, 0xd8, 0xd2, 0xd4, 0xde, + 0x87, 0x8d, 0x8d, 0x88, 0x8e, 0x8b, 0x83, 0x85, + 0x80, 0x86, 0x86, 0x8f, 0x89, 0x80, 0x8a, 0x83, + 0x81, 0x84, 0x82, 0x87, 0x88, 0x82, 0x85, 0x8c, + 0x8b, 0x81, 0x8c, 0x8a, 0x84, 0x8e, 0x8f, 0x89, + 0x8a, 0x83, 0x86, 0x8f, 0x89, 0x80, 0x80, 0x86, + 0x8c, 0x8a, 0x8b, 0x81, 0x87, 0x8d, 0x8d, 0x88, + 0x8f, 0x89, 0x81, 0x84, 0x83, 0x85, 0x8e, 0x8b, + 0x85, 0x8c, 0x82, 0x87, 0x88, 0x82, 0x84, 0x8e, + 0xc7, 0xcd, 0xcd, 0xc8, 0xce, 0xcb, 0xc3, 0xc5, + 0xc0, 0xc6, 0xc6, 0xcf, 0xc9, 0xc0, 0xca, 0xc3, + 0xc1, 0xc4, 0xc2, 0xc7, 0xc8, 0xc2, 0xc5, 0xcc, + 0xcb, 0xc1, 0xcc, 0xca, 0xc4, 0xce, 0xcf, 0xc9, + 0xca, 0xc3, 0xc6, 0xcf, 0xc9, 0xc0, 0xc0, 0xc6, + 0xcc, 0xca, 0xcb, 0xc1, 0xc7, 0xcd, 0xcd, 0xc8, + 0xcf, 0xc9, 0xc1, 0xc4, 0xc3, 0xc5, 0xce, 0xcb, + 0xc5, 0xcc, 0xc2, 0xc7, 0xc8, 0xc2, 0xc4, 0xce, + 0x57, 0x5d, 0x5d, 0x58, 0x5e, 0x5b, 0x53, 0x55, + 0x50, 0x56, 0x56, 0x5f, 0x59, 0x50, 0x5a, 0x53, + 0x51, 0x54, 0x52, 0x57, 0x58, 0x52, 0x55, 0x5c, + 0x5b, 0x51, 0x5c, 0x5a, 0x54, 0x5e, 0x5f, 0x59, + 0x5a, 0x53, 0x56, 0x5f, 0x59, 0x50, 0x50, 0x56, + 0x5c, 0x5a, 0x5b, 0x51, 0x57, 0x5d, 0x5d, 0x58, + 0x5f, 0x59, 0x51, 0x54, 0x53, 0x55, 0x5e, 0x5b, + 0x55, 0x5c, 0x52, 0x57, 0x58, 0x52, 0x54, 0x5e, + 0x77, 0x7d, 0x7d, 0x78, 0x7e, 0x7b, 0x73, 0x75, + 0x70, 0x76, 0x76, 0x7f, 0x79, 0x70, 0x7a, 0x73, + 0x71, 0x74, 0x72, 0x77, 0x78, 0x72, 0x75, 0x7c, + 0x7b, 0x71, 0x7c, 0x7a, 0x74, 0x7e, 0x7f, 0x79, + 0x7a, 0x73, 0x76, 0x7f, 0x79, 0x70, 0x70, 0x76, + 0x7c, 0x7a, 0x7b, 0x71, 0x77, 0x7d, 0x7d, 0x78, + 0x7f, 0x79, 0x71, 0x74, 0x73, 0x75, 0x7e, 0x7b, + 0x75, 0x7c, 0x72, 0x77, 0x78, 0x72, 0x74, 0x7e, + 0xe7, 0xed, 0xed, 0xe8, 0xee, 0xeb, 0xe3, 0xe5, + 0xe0, 0xe6, 0xe6, 0xef, 0xe9, 0xe0, 0xea, 0xe3, + 0xe1, 0xe4, 0xe2, 0xe7, 0xe8, 0xe2, 0xe5, 0xec, + 0xeb, 0xe1, 0xec, 0xea, 0xe4, 0xee, 0xef, 0xe9, + 0xea, 0xe3, 0xe6, 0xef, 0xe9, 0xe0, 0xe0, 0xe6, + 0xec, 0xea, 0xeb, 0xe1, 0xe7, 0xed, 0xed, 0xe8, + 0xef, 0xe9, 0xe1, 0xe4, 0xe3, 0xe5, 0xee, 0xeb, + 0xe5, 0xec, 0xe2, 0xe7, 0xe8, 0xe2, 0xe4, 0xee, + 0xb7, 0xbd, 0xbd, 0xb8, 0xbe, 0xbb, 0xb3, 0xb5, + 0xb0, 0xb6, 0xb6, 0xbf, 0xb9, 0xb0, 0xba, 0xb3, + 0xb1, 0xb4, 0xb2, 0xb7, 0xb8, 0xb2, 0xb5, 0xbc, + 0xbb, 0xb1, 0xbc, 0xba, 0xb4, 0xbe, 0xbf, 0xb9, + 0xba, 0xb3, 0xb6, 0xbf, 0xb9, 0xb0, 0xb0, 0xb6, + 0xbc, 0xba, 0xbb, 0xb1, 0xb7, 0xbd, 0xbd, 0xb8, + 0xbf, 0xb9, 0xb1, 0xb4, 0xb3, 0xb5, 0xbe, 0xbb, + 0xb5, 0xbc, 0xb2, 0xb7, 0xb8, 0xb2, 0xb4, 0xbe, + 0xc7, 0xcd, 0xcd, 0xc8, 0xce, 0xcb, 0xc3, 0xc5, + 0xc0, 0xc6, 0xc6, 0xcf, 0xc9, 0xc0, 0xca, 0xc3, + 0xc1, 0xc4, 0xc2, 0xc7, 0xc8, 0xc2, 0xc5, 0xcc, + 0xcb, 0xc1, 0xcc, 0xca, 0xc4, 0xce, 0xcf, 0xc9, + 0xca, 0xc3, 0xc6, 0xcf, 0xc9, 0xc0, 0xc0, 0xc6, + 0xcc, 0xca, 0xcb, 0xc1, 0xc7, 0xcd, 0xcd, 0xc8, + 0xcf, 0xc9, 0xc1, 0xc4, 0xc3, 0xc5, 0xce, 0xcb, + 0xc5, 0xcc, 0xc2, 0xc7, 0xc8, 0xc2, 0xc4, 0xce, + 0x47, 0x4d, 0x4d, 0x48, 0x4e, 0x4b, 0x43, 0x45, + 0x40, 0x46, 0x46, 0x4f, 0x49, 0x40, 0x4a, 0x43, + 0x41, 0x44, 0x42, 0x47, 0x48, 0x42, 0x45, 0x4c, + 0x4b, 0x41, 0x4c, 0x4a, 0x44, 0x4e, 0x4f, 0x49, + 0x4a, 0x43, 0x46, 0x4f, 0x49, 0x40, 0x40, 0x46, + 0x4c, 0x4a, 0x4b, 0x41, 0x47, 0x4d, 0x4d, 0x48, + 0x4f, 0x49, 0x41, 0x44, 0x43, 0x45, 0x4e, 0x4b, + 0x45, 0x4c, 0x42, 0x47, 0x48, 0x42, 0x44, 0x4e, + 0xb7, 0xbd, 0xbd, 0xb8, 0xbe, 0xbb, 0xb3, 0xb5, + 0xb0, 0xb6, 0xb6, 0xbf, 0xb9, 0xb0, 0xba, 0xb3, + 0xb1, 0xb4, 0xb2, 0xb7, 0xb8, 0xb2, 0xb5, 0xbc, + 0xbb, 0xb1, 0xbc, 0xba, 0xb4, 0xbe, 0xbf, 0xb9, + 0xba, 0xb3, 0xb6, 0xbf, 0xb9, 0xb0, 0xb0, 0xb6, + 0xbc, 0xba, 0xbb, 0xb1, 0xb7, 0xbd, 0xbd, 0xb8, + 0xbf, 0xb9, 0xb1, 0xb4, 0xb3, 0xb5, 0xbe, 0xbb, + 0xb5, 0xbc, 0xb2, 0xb7, 0xb8, 0xb2, 0xb4, 0xbe, + 0x27, 0x2d, 0x2d, 0x28, 0x2e, 0x2b, 0x23, 0x25, + 0x20, 0x26, 0x26, 0x2f, 0x29, 0x20, 0x2a, 0x23, + 0x21, 0x24, 0x22, 0x27, 0x28, 0x22, 0x25, 0x2c, + 0x2b, 0x21, 0x2c, 0x2a, 0x24, 0x2e, 0x2f, 0x29, + 0x2a, 0x23, 0x26, 0x2f, 0x29, 0x20, 0x20, 0x26, + 0x2c, 0x2a, 0x2b, 0x21, 0x27, 0x2d, 0x2d, 0x28, + 0x2f, 0x29, 0x21, 0x24, 0x23, 0x25, 0x2e, 0x2b, + 0x25, 0x2c, 0x22, 0x27, 0x28, 0x22, 0x24, 0x2e, + 0xf7, 0xfd, 0xfd, 0xf8, 0xfe, 0xfb, 0xf3, 0xf5, + 0xf0, 0xf6, 0xf6, 0xff, 0xf9, 0xf0, 0xfa, 0xf3, + 0xf1, 0xf4, 0xf2, 0xf7, 0xf8, 0xf2, 0xf5, 0xfc, + 0xfb, 0xf1, 0xfc, 0xfa, 0xf4, 0xfe, 0xff, 0xf9, + 0xfa, 0xf3, 0xf6, 0xff, 0xf9, 0xf0, 0xf0, 0xf6, + 0xfc, 0xfa, 0xfb, 0xf1, 0xf7, 0xfd, 0xfd, 0xf8, + 0xff, 0xf9, 0xf1, 0xf4, 0xf3, 0xf5, 0xfe, 0xfb, + 0xf5, 0xfc, 0xf2, 0xf7, 0xf8, 0xf2, 0xf4, 0xfe, + 0x87, 0x8d, 0x8d, 0x88, 0x8e, 0x8b, 0x83, 0x85, + 0x80, 0x86, 0x86, 0x8f, 0x89, 0x80, 0x8a, 0x83, + 0x81, 0x84, 0x82, 0x87, 0x88, 0x82, 0x85, 0x8c, + 0x8b, 0x81, 0x8c, 0x8a, 0x84, 0x8e, 0x8f, 0x89, + 0x8a, 0x83, 0x86, 0x8f, 0x89, 0x80, 0x80, 0x86, + 0x8c, 0x8a, 0x8b, 0x81, 0x87, 0x8d, 0x8d, 0x88, + 0x8f, 0x89, 0x81, 0x84, 0x83, 0x85, 0x8e, 0x8b, + 0x85, 0x8c, 0x82, 0x87, 0x88, 0x82, 0x84, 0x8e, + 0x17, 0x1d, 0x1d, 0x18, 0x1e, 0x1b, 0x13, 0x15, + 0x10, 0x16, 0x16, 0x1f, 0x19, 0x10, 0x1a, 0x13, + 0x11, 0x14, 0x12, 0x17, 0x18, 0x12, 0x15, 0x1c, + 0x1b, 0x11, 0x1c, 0x1a, 0x14, 0x1e, 0x1f, 0x19, + 0x1a, 0x13, 0x16, 0x1f, 0x19, 0x10, 0x10, 0x16, + 0x1c, 0x1a, 0x1b, 0x11, 0x17, 0x1d, 0x1d, 0x18, + 0x1f, 0x19, 0x11, 0x14, 0x13, 0x15, 0x1e, 0x1b, + 0x15, 0x1c, 0x12, 0x17, 0x18, 0x12, 0x14, 0x1e, + 0xd7, 0xdd, 0xdd, 0xd8, 0xde, 0xdb, 0xd3, 0xd5, + 0xd0, 0xd6, 0xd6, 0xdf, 0xd9, 0xd0, 0xda, 0xd3, + 0xd1, 0xd4, 0xd2, 0xd7, 0xd8, 0xd2, 0xd5, 0xdc, + 0xdb, 0xd1, 0xdc, 0xda, 0xd4, 0xde, 0xdf, 0xd9, + 0xda, 0xd3, 0xd6, 0xdf, 0xd9, 0xd0, 0xd0, 0xd6, + 0xdc, 0xda, 0xdb, 0xd1, 0xd7, 0xdd, 0xdd, 0xd8, + 0xdf, 0xd9, 0xd1, 0xd4, 0xd3, 0xd5, 0xde, 0xdb, + 0xd5, 0xdc, 0xd2, 0xd7, 0xd8, 0xd2, 0xd4, 0xde, + 0x17, 0x1d, 0x1d, 0x18, 0x1e, 0x1b, 0x13, 0x15, + 0x10, 0x16, 0x16, 0x1f, 0x19, 0x10, 0x1a, 0x13, + 0x11, 0x14, 0x12, 0x17, 0x18, 0x12, 0x15, 0x1c, + 0x1b, 0x11, 0x1c, 0x1a, 0x14, 0x1e, 0x1f, 0x19, + 0x1a, 0x13, 0x16, 0x1f, 0x19, 0x10, 0x10, 0x16, + 0x1c, 0x1a, 0x1b, 0x11, 0x17, 0x1d, 0x1d, 0x18, + 0x1f, 0x19, 0x11, 0x14, 0x13, 0x15, 0x1e, 0x1b, + 0x15, 0x1c, 0x12, 0x17, 0x18, 0x12, 0x14, 0x1e, + 0x67, 0x6d, 0x6d, 0x68, 0x6e, 0x6b, 0x63, 0x65, + 0x60, 0x66, 0x66, 0x6f, 0x69, 0x60, 0x6a, 0x63, + 0x61, 0x64, 0x62, 0x67, 0x68, 0x62, 0x65, 0x6c, + 0x6b, 0x61, 0x6c, 0x6a, 0x64, 0x6e, 0x6f, 0x69, + 0x6a, 0x63, 0x66, 0x6f, 0x69, 0x60, 0x60, 0x66, + 0x6c, 0x6a, 0x6b, 0x61, 0x67, 0x6d, 0x6d, 0x68, + 0x6f, 0x69, 0x61, 0x64, 0x63, 0x65, 0x6e, 0x6b, + 0x65, 0x6c, 0x62, 0x67, 0x68, 0x62, 0x64, 0x6e, + 0xa7, 0xad, 0xad, 0xa8, 0xae, 0xab, 0xa3, 0xa5, + 0xa0, 0xa6, 0xa6, 0xaf, 0xa9, 0xa0, 0xaa, 0xa3, + 0xa1, 0xa4, 0xa2, 0xa7, 0xa8, 0xa2, 0xa5, 0xac, + 0xab, 0xa1, 0xac, 0xaa, 0xa4, 0xae, 0xaf, 0xa9, + 0xaa, 0xa3, 0xa6, 0xaf, 0xa9, 0xa0, 0xa0, 0xa6, + 0xac, 0xaa, 0xab, 0xa1, 0xa7, 0xad, 0xad, 0xa8, + 0xaf, 0xa9, 0xa1, 0xa4, 0xa3, 0xa5, 0xae, 0xab, + 0xa5, 0xac, 0xa2, 0xa7, 0xa8, 0xa2, 0xa4, 0xae, + 0x47, 0x4d, 0x4d, 0x48, 0x4e, 0x4b, 0x43, 0x45, + 0x40, 0x46, 0x46, 0x4f, 0x49, 0x40, 0x4a, 0x43, + 0x41, 0x44, 0x42, 0x47, 0x48, 0x42, 0x45, 0x4c, + 0x4b, 0x41, 0x4c, 0x4a, 0x44, 0x4e, 0x4f, 0x49, + 0x4a, 0x43, 0x46, 0x4f, 0x49, 0x40, 0x40, 0x46, + 0x4c, 0x4a, 0x4b, 0x41, 0x47, 0x4d, 0x4d, 0x48, + 0x4f, 0x49, 0x41, 0x44, 0x43, 0x45, 0x4e, 0x4b, + 0x45, 0x4c, 0x42, 0x47, 0x48, 0x42, 0x44, 0x4e, + 0xd7, 0xdd, 0xdd, 0xd8, 0xde, 0xdb, 0xd3, 0xd5, + 0xd0, 0xd6, 0xd6, 0xdf, 0xd9, 0xd0, 0xda, 0xd3, + 0xd1, 0xd4, 0xd2, 0xd7, 0xd8, 0xd2, 0xd5, 0xdc, + 0xdb, 0xd1, 0xdc, 0xda, 0xd4, 0xde, 0xdf, 0xd9, + 0xda, 0xd3, 0xd6, 0xdf, 0xd9, 0xd0, 0xd0, 0xd6, + 0xdc, 0xda, 0xdb, 0xd1, 0xd7, 0xdd, 0xdd, 0xd8, + 0xdf, 0xd9, 0xd1, 0xd4, 0xd3, 0xd5, 0xde, 0xdb, + 0xd5, 0xdc, 0xd2, 0xd7, 0xd8, 0xd2, 0xd4, 0xde, + 0x97, 0x9d, 0x9d, 0x98, 0x9e, 0x9b, 0x93, 0x95, + 0x90, 0x96, 0x96, 0x9f, 0x99, 0x90, 0x9a, 0x93, + 0x91, 0x94, 0x92, 0x97, 0x98, 0x92, 0x95, 0x9c, + 0x9b, 0x91, 0x9c, 0x9a, 0x94, 0x9e, 0x9f, 0x99, + 0x9a, 0x93, 0x96, 0x9f, 0x99, 0x90, 0x90, 0x96, + 0x9c, 0x9a, 0x9b, 0x91, 0x97, 0x9d, 0x9d, 0x98, + 0x9f, 0x99, 0x91, 0x94, 0x93, 0x95, 0x9e, 0x9b, + 0x95, 0x9c, 0x92, 0x97, 0x98, 0x92, 0x94, 0x9e, + 0x07, 0x0d, 0x0d, 0x08, 0x0e, 0x0b, 0x03, 0x05, + 0x00, 0x06, 0x06, 0x0f, 0x09, 0x00, 0x0a, 0x03, + 0x01, 0x04, 0x02, 0x07, 0x08, 0x02, 0x05, 0x0c, + 0x0b, 0x01, 0x0c, 0x0a, 0x04, 0x0e, 0x0f, 0x09, + 0x0a, 0x03, 0x06, 0x0f, 0x09, 0x00, 0x00, 0x06, + 0x0c, 0x0a, 0x0b, 0x01, 0x07, 0x0d, 0x0d, 0x08, + 0x0f, 0x09, 0x01, 0x04, 0x03, 0x05, 0x0e, 0x0b, + 0x05, 0x0c, 0x02, 0x07, 0x08, 0x02, 0x04, 0x0e, + 0x87, 0x8d, 0x8d, 0x88, 0x8e, 0x8b, 0x83, 0x85, + 0x80, 0x86, 0x86, 0x8f, 0x89, 0x80, 0x8a, 0x83, + 0x81, 0x84, 0x82, 0x87, 0x88, 0x82, 0x85, 0x8c, + 0x8b, 0x81, 0x8c, 0x8a, 0x84, 0x8e, 0x8f, 0x89, + 0x8a, 0x83, 0x86, 0x8f, 0x89, 0x80, 0x80, 0x86, + 0x8c, 0x8a, 0x8b, 0x81, 0x87, 0x8d, 0x8d, 0x88, + 0x8f, 0x89, 0x81, 0x84, 0x83, 0x85, 0x8e, 0x8b, + 0x85, 0x8c, 0x82, 0x87, 0x88, 0x82, 0x84, 0x8e, + 0x67, 0x6d, 0x6d, 0x68, 0x6e, 0x6b, 0x63, 0x65, + 0x60, 0x66, 0x66, 0x6f, 0x69, 0x60, 0x6a, 0x63, + 0x61, 0x64, 0x62, 0x67, 0x68, 0x62, 0x65, 0x6c, + 0x6b, 0x61, 0x6c, 0x6a, 0x64, 0x6e, 0x6f, 0x69, + 0x6a, 0x63, 0x66, 0x6f, 0x69, 0x60, 0x60, 0x66, + 0x6c, 0x6a, 0x6b, 0x61, 0x67, 0x6d, 0x6d, 0x68, + 0x6f, 0x69, 0x61, 0x64, 0x63, 0x65, 0x6e, 0x6b, + 0x65, 0x6c, 0x62, 0x67, 0x68, 0x62, 0x64, 0x6e, + 0xf7, 0xfd, 0xfd, 0xf8, 0xfe, 0xfb, 0xf3, 0xf5, + 0xf0, 0xf6, 0xf6, 0xff, 0xf9, 0xf0, 0xfa, 0xf3, + 0xf1, 0xf4, 0xf2, 0xf7, 0xf8, 0xf2, 0xf5, 0xfc, + 0xfb, 0xf1, 0xfc, 0xfa, 0xf4, 0xfe, 0xff, 0xf9, + 0xfa, 0xf3, 0xf6, 0xff, 0xf9, 0xf0, 0xf0, 0xf6, + 0xfc, 0xfa, 0xfb, 0xf1, 0xf7, 0xfd, 0xfd, 0xf8, + 0xff, 0xf9, 0xf1, 0xf4, 0xf3, 0xf5, 0xfe, 0xfb, + 0xf5, 0xfc, 0xf2, 0xf7, 0xf8, 0xf2, 0xf4, 0xfe, + 0x97, 0x9d, 0x9d, 0x98, 0x9e, 0x9b, 0x93, 0x95, + 0x90, 0x96, 0x96, 0x9f, 0x99, 0x90, 0x9a, 0x93, + 0x91, 0x94, 0x92, 0x97, 0x98, 0x92, 0x95, 0x9c, + 0x9b, 0x91, 0x9c, 0x9a, 0x94, 0x9e, 0x9f, 0x99, + 0x9a, 0x93, 0x96, 0x9f, 0x99, 0x90, 0x90, 0x96, + 0x9c, 0x9a, 0x9b, 0x91, 0x97, 0x9d, 0x9d, 0x98, + 0x9f, 0x99, 0x91, 0x94, 0x93, 0x95, 0x9e, 0x9b, + 0x95, 0x9c, 0x92, 0x97, 0x98, 0x92, 0x94, 0x9e, + 0x37, 0x3d, 0x3d, 0x38, 0x3e, 0x3b, 0x33, 0x35, + 0x30, 0x36, 0x36, 0x3f, 0x39, 0x30, 0x3a, 0x33, + 0x31, 0x34, 0x32, 0x37, 0x38, 0x32, 0x35, 0x3c, + 0x3b, 0x31, 0x3c, 0x3a, 0x34, 0x3e, 0x3f, 0x39, + 0x3a, 0x33, 0x36, 0x3f, 0x39, 0x30, 0x30, 0x36, + 0x3c, 0x3a, 0x3b, 0x31, 0x37, 0x3d, 0x3d, 0x38, + 0x3f, 0x39, 0x31, 0x34, 0x33, 0x35, 0x3e, 0x3b, + 0x35, 0x3c, 0x32, 0x37, 0x38, 0x32, 0x34, 0x3e, + 0x87, 0x8d, 0x8d, 0x88, 0x8e, 0x8b, 0x83, 0x85, + 0x80, 0x86, 0x86, 0x8f, 0x89, 0x80, 0x8a, 0x83, + 0x81, 0x84, 0x82, 0x87, 0x88, 0x82, 0x85, 0x8c, + 0x8b, 0x81, 0x8c, 0x8a, 0x84, 0x8e, 0x8f, 0x89, + 0x8a, 0x83, 0x86, 0x8f, 0x89, 0x80, 0x80, 0x86, + 0x8c, 0x8a, 0x8b, 0x81, 0x87, 0x8d, 0x8d, 0x88, + 0x8f, 0x89, 0x81, 0x84, 0x83, 0x85, 0x8e, 0x8b, + 0x85, 0x8c, 0x82, 0x87, 0x88, 0x82, 0x84, 0x8e, + 0x07, 0x0d, 0x0d, 0x08, 0x0e, 0x0b, 0x03, 0x05, + 0x00, 0x06, 0x06, 0x0f, 0x09, 0x00, 0x0a, 0x03, + 0x01, 0x04, 0x02, 0x07, 0x08, 0x02, 0x05, 0x0c, + 0x0b, 0x01, 0x0c, 0x0a, 0x04, 0x0e, 0x0f, 0x09, + 0x0a, 0x03, 0x06, 0x0f, 0x09, 0x00, 0x00, 0x06, + 0x0c, 0x0a, 0x0b, 0x01, 0x07, 0x0d, 0x0d, 0x08, + 0x0f, 0x09, 0x01, 0x04, 0x03, 0x05, 0x0e, 0x0b, + 0x05, 0x0c, 0x02, 0x07, 0x08, 0x02, 0x04, 0x0e, + 0x77, 0x7d, 0x7d, 0x78, 0x7e, 0x7b, 0x73, 0x75, + 0x70, 0x76, 0x76, 0x7f, 0x79, 0x70, 0x7a, 0x73, + 0x71, 0x74, 0x72, 0x77, 0x78, 0x72, 0x75, 0x7c, + 0x7b, 0x71, 0x7c, 0x7a, 0x74, 0x7e, 0x7f, 0x79, + 0x7a, 0x73, 0x76, 0x7f, 0x79, 0x70, 0x70, 0x76, + 0x7c, 0x7a, 0x7b, 0x71, 0x77, 0x7d, 0x7d, 0x78, + 0x7f, 0x79, 0x71, 0x74, 0x73, 0x75, 0x7e, 0x7b, + 0x75, 0x7c, 0x72, 0x77, 0x78, 0x72, 0x74, 0x7e, + 0xb7, 0xbd, 0xbd, 0xb8, 0xbe, 0xbb, 0xb3, 0xb5, + 0xb0, 0xb6, 0xb6, 0xbf, 0xb9, 0xb0, 0xba, 0xb3, + 0xb1, 0xb4, 0xb2, 0xb7, 0xb8, 0xb2, 0xb5, 0xbc, + 0xbb, 0xb1, 0xbc, 0xba, 0xb4, 0xbe, 0xbf, 0xb9, + 0xba, 0xb3, 0xb6, 0xbf, 0xb9, 0xb0, 0xb0, 0xb6, + 0xbc, 0xba, 0xbb, 0xb1, 0xb7, 0xbd, 0xbd, 0xb8, + 0xbf, 0xb9, 0xb1, 0xb4, 0xb3, 0xb5, 0xbe, 0xbb, + 0xb5, 0xbc, 0xb2, 0xb7, 0xb8, 0xb2, 0xb4, 0xbe, + 0x47, 0x4d, 0x4d, 0x48, 0x4e, 0x4b, 0x43, 0x45, + 0x40, 0x46, 0x46, 0x4f, 0x49, 0x40, 0x4a, 0x43, + 0x41, 0x44, 0x42, 0x47, 0x48, 0x42, 0x45, 0x4c, + 0x4b, 0x41, 0x4c, 0x4a, 0x44, 0x4e, 0x4f, 0x49, + 0x4a, 0x43, 0x46, 0x4f, 0x49, 0x40, 0x40, 0x46, + 0x4c, 0x4a, 0x4b, 0x41, 0x47, 0x4d, 0x4d, 0x48, + 0x4f, 0x49, 0x41, 0x44, 0x43, 0x45, 0x4e, 0x4b, + 0x45, 0x4c, 0x42, 0x47, 0x48, 0x42, 0x44, 0x4e, + 0x17, 0x1d, 0x1d, 0x18, 0x1e, 0x1b, 0x13, 0x15, + 0x10, 0x16, 0x16, 0x1f, 0x19, 0x10, 0x1a, 0x13, + 0x11, 0x14, 0x12, 0x17, 0x18, 0x12, 0x15, 0x1c, + 0x1b, 0x11, 0x1c, 0x1a, 0x14, 0x1e, 0x1f, 0x19, + 0x1a, 0x13, 0x16, 0x1f, 0x19, 0x10, 0x10, 0x16, + 0x1c, 0x1a, 0x1b, 0x11, 0x17, 0x1d, 0x1d, 0x18, + 0x1f, 0x19, 0x11, 0x14, 0x13, 0x15, 0x1e, 0x1b, + 0x15, 0x1c, 0x12, 0x17, 0x18, 0x12, 0x14, 0x1e, + 0xf7, 0xfd, 0xfd, 0xf8, 0xfe, 0xfb, 0xf3, 0xf5, + 0xf0, 0xf6, 0xf6, 0xff, 0xf9, 0xf0, 0xfa, 0xf3, + 0xf1, 0xf4, 0xf2, 0xf7, 0xf8, 0xf2, 0xf5, 0xfc, + 0xfb, 0xf1, 0xfc, 0xfa, 0xf4, 0xfe, 0xff, 0xf9, + 0xfa, 0xf3, 0xf6, 0xff, 0xf9, 0xf0, 0xf0, 0xf6, + 0xfc, 0xfa, 0xfb, 0xf1, 0xf7, 0xfd, 0xfd, 0xf8, + 0xff, 0xf9, 0xf1, 0xf4, 0xf3, 0xf5, 0xfe, 0xfb, + 0xf5, 0xfc, 0xf2, 0xf7, 0xf8, 0xf2, 0xf4, 0xfe, + 0x27, 0x2d, 0x2d, 0x28, 0x2e, 0x2b, 0x23, 0x25, + 0x20, 0x26, 0x26, 0x2f, 0x29, 0x20, 0x2a, 0x23, + 0x21, 0x24, 0x22, 0x27, 0x28, 0x22, 0x25, 0x2c, + 0x2b, 0x21, 0x2c, 0x2a, 0x24, 0x2e, 0x2f, 0x29, + 0x2a, 0x23, 0x26, 0x2f, 0x29, 0x20, 0x20, 0x26, + 0x2c, 0x2a, 0x2b, 0x21, 0x27, 0x2d, 0x2d, 0x28, + 0x2f, 0x29, 0x21, 0x24, 0x23, 0x25, 0x2e, 0x2b, + 0x25, 0x2c, 0x22, 0x27, 0x28, 0x22, 0x24, 0x2e, + 0xe7, 0xed, 0xed, 0xe8, 0xee, 0xeb, 0xe3, 0xe5, + 0xe0, 0xe6, 0xe6, 0xef, 0xe9, 0xe0, 0xea, 0xe3, + 0xe1, 0xe4, 0xe2, 0xe7, 0xe8, 0xe2, 0xe5, 0xec, + 0xeb, 0xe1, 0xec, 0xea, 0xe4, 0xee, 0xef, 0xe9, + 0xea, 0xe3, 0xe6, 0xef, 0xe9, 0xe0, 0xe0, 0xe6, + 0xec, 0xea, 0xeb, 0xe1, 0xe7, 0xed, 0xed, 0xe8, + 0xef, 0xe9, 0xe1, 0xe4, 0xe3, 0xe5, 0xee, 0xeb, + 0xe5, 0xec, 0xe2, 0xe7, 0xe8, 0xe2, 0xe4, 0xee, + 0xc7, 0xcd, 0xcd, 0xc8, 0xce, 0xcb, 0xc3, 0xc5, + 0xc0, 0xc6, 0xc6, 0xcf, 0xc9, 0xc0, 0xca, 0xc3, + 0xc1, 0xc4, 0xc2, 0xc7, 0xc8, 0xc2, 0xc5, 0xcc, + 0xcb, 0xc1, 0xcc, 0xca, 0xc4, 0xce, 0xcf, 0xc9, + 0xca, 0xc3, 0xc6, 0xcf, 0xc9, 0xc0, 0xc0, 0xc6, + 0xcc, 0xca, 0xcb, 0xc1, 0xc7, 0xcd, 0xcd, 0xc8, + 0xcf, 0xc9, 0xc1, 0xc4, 0xc3, 0xc5, 0xce, 0xcb, + 0xc5, 0xcc, 0xc2, 0xc7, 0xc8, 0xc2, 0xc4, 0xce, + 0x37, 0x3d, 0x3d, 0x38, 0x3e, 0x3b, 0x33, 0x35, + 0x30, 0x36, 0x36, 0x3f, 0x39, 0x30, 0x3a, 0x33, + 0x31, 0x34, 0x32, 0x37, 0x38, 0x32, 0x35, 0x3c, + 0x3b, 0x31, 0x3c, 0x3a, 0x34, 0x3e, 0x3f, 0x39, + 0x3a, 0x33, 0x36, 0x3f, 0x39, 0x30, 0x30, 0x36, + 0x3c, 0x3a, 0x3b, 0x31, 0x37, 0x3d, 0x3d, 0x38, + 0x3f, 0x39, 0x31, 0x34, 0x33, 0x35, 0x3e, 0x3b, + 0x35, 0x3c, 0x32, 0x37, 0x38, 0x32, 0x34, 0x3e, + 0x57, 0x5d, 0x5d, 0x58, 0x5e, 0x5b, 0x53, 0x55, + 0x50, 0x56, 0x56, 0x5f, 0x59, 0x50, 0x5a, 0x53, + 0x51, 0x54, 0x52, 0x57, 0x58, 0x52, 0x55, 0x5c, + 0x5b, 0x51, 0x5c, 0x5a, 0x54, 0x5e, 0x5f, 0x59, + 0x5a, 0x53, 0x56, 0x5f, 0x59, 0x50, 0x50, 0x56, + 0x5c, 0x5a, 0x5b, 0x51, 0x57, 0x5d, 0x5d, 0x58, + 0x5f, 0x59, 0x51, 0x54, 0x53, 0x55, 0x5e, 0x5b, + 0x55, 0x5c, 0x52, 0x57, 0x58, 0x52, 0x54, 0x5e, + 0xb7, 0xbd, 0xbd, 0xb8, 0xbe, 0xbb, 0xb3, 0xb5, + 0xb0, 0xb6, 0xb6, 0xbf, 0xb9, 0xb0, 0xba, 0xb3, + 0xb1, 0xb4, 0xb2, 0xb7, 0xb8, 0xb2, 0xb5, 0xbc, + 0xbb, 0xb1, 0xbc, 0xba, 0xb4, 0xbe, 0xbf, 0xb9, + 0xba, 0xb3, 0xb6, 0xbf, 0xb9, 0xb0, 0xb0, 0xb6, + 0xbc, 0xba, 0xbb, 0xb1, 0xb7, 0xbd, 0xbd, 0xb8, + 0xbf, 0xb9, 0xb1, 0xb4, 0xb3, 0xb5, 0xbe, 0xbb, + 0xb5, 0xbc, 0xb2, 0xb7, 0xb8, 0xb2, 0xb4, 0xbe, + 0xa7, 0xad, 0xad, 0xa8, 0xae, 0xab, 0xa3, 0xa5, + 0xa0, 0xa6, 0xa6, 0xaf, 0xa9, 0xa0, 0xaa, 0xa3, + 0xa1, 0xa4, 0xa2, 0xa7, 0xa8, 0xa2, 0xa5, 0xac, + 0xab, 0xa1, 0xac, 0xaa, 0xa4, 0xae, 0xaf, 0xa9, + 0xaa, 0xa3, 0xa6, 0xaf, 0xa9, 0xa0, 0xa0, 0xa6, + 0xac, 0xaa, 0xab, 0xa1, 0xa7, 0xad, 0xad, 0xa8, + 0xaf, 0xa9, 0xa1, 0xa4, 0xa3, 0xa5, 0xae, 0xab, + 0xa5, 0xac, 0xa2, 0xa7, 0xa8, 0xa2, 0xa4, 0xae, + 0x57, 0x5d, 0x5d, 0x58, 0x5e, 0x5b, 0x53, 0x55, + 0x50, 0x56, 0x56, 0x5f, 0x59, 0x50, 0x5a, 0x53, + 0x51, 0x54, 0x52, 0x57, 0x58, 0x52, 0x55, 0x5c, + 0x5b, 0x51, 0x5c, 0x5a, 0x54, 0x5e, 0x5f, 0x59, + 0x5a, 0x53, 0x56, 0x5f, 0x59, 0x50, 0x50, 0x56, + 0x5c, 0x5a, 0x5b, 0x51, 0x57, 0x5d, 0x5d, 0x58, + 0x5f, 0x59, 0x51, 0x54, 0x53, 0x55, 0x5e, 0x5b, + 0x55, 0x5c, 0x52, 0x57, 0x58, 0x52, 0x54, 0x5e, + 0xe7, 0xed, 0xed, 0xe8, 0xee, 0xeb, 0xe3, 0xe5, + 0xe0, 0xe6, 0xe6, 0xef, 0xe9, 0xe0, 0xea, 0xe3, + 0xe1, 0xe4, 0xe2, 0xe7, 0xe8, 0xe2, 0xe5, 0xec, + 0xeb, 0xe1, 0xec, 0xea, 0xe4, 0xee, 0xef, 0xe9, + 0xea, 0xe3, 0xe6, 0xef, 0xe9, 0xe0, 0xe0, 0xe6, + 0xec, 0xea, 0xeb, 0xe1, 0xe7, 0xed, 0xed, 0xe8, + 0xef, 0xe9, 0xe1, 0xe4, 0xe3, 0xe5, 0xee, 0xeb, + 0xe5, 0xec, 0xe2, 0xe7, 0xe8, 0xe2, 0xe4, 0xee, + 0x27, 0x2d, 0x2d, 0x28, 0x2e, 0x2b, 0x23, 0x25, + 0x20, 0x26, 0x26, 0x2f, 0x29, 0x20, 0x2a, 0x23, + 0x21, 0x24, 0x22, 0x27, 0x28, 0x22, 0x25, 0x2c, + 0x2b, 0x21, 0x2c, 0x2a, 0x24, 0x2e, 0x2f, 0x29, + 0x2a, 0x23, 0x26, 0x2f, 0x29, 0x20, 0x20, 0x26, + 0x2c, 0x2a, 0x2b, 0x21, 0x27, 0x2d, 0x2d, 0x28, + 0x2f, 0x29, 0x21, 0x24, 0x23, 0x25, 0x2e, 0x2b, + 0x25, 0x2c, 0x22, 0x27, 0x28, 0x22, 0x24, 0x2e, + 0x77, 0x7d, 0x7d, 0x78, 0x7e, 0x7b, 0x73, 0x75, + 0x70, 0x76, 0x76, 0x7f, 0x79, 0x70, 0x7a, 0x73, + 0x71, 0x74, 0x72, 0x77, 0x78, 0x72, 0x75, 0x7c, + 0x7b, 0x71, 0x7c, 0x7a, 0x74, 0x7e, 0x7f, 0x79, + 0x7a, 0x73, 0x76, 0x7f, 0x79, 0x70, 0x70, 0x76, + 0x7c, 0x7a, 0x7b, 0x71, 0x77, 0x7d, 0x7d, 0x78, + 0x7f, 0x79, 0x71, 0x74, 0x73, 0x75, 0x7e, 0x7b, + 0x75, 0x7c, 0x72, 0x77, 0x78, 0x72, 0x74, 0x7e, + 0xc7, 0xcd, 0xcd, 0xc8, 0xce, 0xcb, 0xc3, 0xc5, + 0xc0, 0xc6, 0xc6, 0xcf, 0xc9, 0xc0, 0xca, 0xc3, + 0xc1, 0xc4, 0xc2, 0xc7, 0xc8, 0xc2, 0xc5, 0xcc, + 0xcb, 0xc1, 0xcc, 0xca, 0xc4, 0xce, 0xcf, 0xc9, + 0xca, 0xc3, 0xc6, 0xcf, 0xc9, 0xc0, 0xc0, 0xc6, + 0xcc, 0xca, 0xcb, 0xc1, 0xc7, 0xcd, 0xcd, 0xc8, + 0xcf, 0xc9, 0xc1, 0xc4, 0xc3, 0xc5, 0xce, 0xcb, + 0xc5, 0xcc, 0xc2, 0xc7, 0xc8, 0xc2, 0xc4, 0xce, + },{ + 0x2c, 0x2a, 0x21, 0x2f, 0x2a, 0x24, 0x2f, 0x22, + 0x29, 0x27, 0x22, 0x2c, 0x26, 0x29, 0x28, 0x25, + 0x20, 0x26, 0x2d, 0x21, 0x23, 0x2d, 0x24, 0x2e, + 0x2e, 0x20, 0x27, 0x2b, 0x25, 0x23, 0x2b, 0x28, + 0x29, 0x24, 0x2e, 0x23, 0x2f, 0x22, 0x25, 0x2c, + 0x22, 0x29, 0x28, 0x25, 0x2c, 0x2f, 0x23, 0x2a, + 0x27, 0x2b, 0x20, 0x2e, 0x24, 0x21, 0x2a, 0x27, + 0x21, 0x26, 0x2d, 0x20, 0x2b, 0x28, 0x26, 0x2d, + 0xec, 0xea, 0xe1, 0xef, 0xea, 0xe4, 0xef, 0xe2, + 0xe9, 0xe7, 0xe2, 0xec, 0xe6, 0xe9, 0xe8, 0xe5, + 0xe0, 0xe6, 0xed, 0xe1, 0xe3, 0xed, 0xe4, 0xee, + 0xee, 0xe0, 0xe7, 0xeb, 0xe5, 0xe3, 0xeb, 0xe8, + 0xe9, 0xe4, 0xee, 0xe3, 0xef, 0xe2, 0xe5, 0xec, + 0xe2, 0xe9, 0xe8, 0xe5, 0xec, 0xef, 0xe3, 0xea, + 0xe7, 0xeb, 0xe0, 0xee, 0xe4, 0xe1, 0xea, 0xe7, + 0xe1, 0xe6, 0xed, 0xe0, 0xeb, 0xe8, 0xe6, 0xed, + 0xcc, 0xca, 0xc1, 0xcf, 0xca, 0xc4, 0xcf, 0xc2, + 0xc9, 0xc7, 0xc2, 0xcc, 0xc6, 0xc9, 0xc8, 0xc5, + 0xc0, 0xc6, 0xcd, 0xc1, 0xc3, 0xcd, 0xc4, 0xce, + 0xce, 0xc0, 0xc7, 0xcb, 0xc5, 0xc3, 0xcb, 0xc8, + 0xc9, 0xc4, 0xce, 0xc3, 0xcf, 0xc2, 0xc5, 0xcc, + 0xc2, 0xc9, 0xc8, 0xc5, 0xcc, 0xcf, 0xc3, 0xca, + 0xc7, 0xcb, 0xc0, 0xce, 0xc4, 0xc1, 0xca, 0xc7, + 0xc1, 0xc6, 0xcd, 0xc0, 0xcb, 0xc8, 0xc6, 0xcd, + 0xbc, 0xba, 0xb1, 0xbf, 0xba, 0xb4, 0xbf, 0xb2, + 0xb9, 0xb7, 0xb2, 0xbc, 0xb6, 0xb9, 0xb8, 0xb5, + 0xb0, 0xb6, 0xbd, 0xb1, 0xb3, 0xbd, 0xb4, 0xbe, + 0xbe, 0xb0, 0xb7, 0xbb, 0xb5, 0xb3, 0xbb, 0xb8, + 0xb9, 0xb4, 0xbe, 0xb3, 0xbf, 0xb2, 0xb5, 0xbc, + 0xb2, 0xb9, 0xb8, 0xb5, 0xbc, 0xbf, 0xb3, 0xba, + 0xb7, 0xbb, 0xb0, 0xbe, 0xb4, 0xb1, 0xba, 0xb7, + 0xb1, 0xb6, 0xbd, 0xb0, 0xbb, 0xb8, 0xb6, 0xbd, + 0x4c, 0x4a, 0x41, 0x4f, 0x4a, 0x44, 0x4f, 0x42, + 0x49, 0x47, 0x42, 0x4c, 0x46, 0x49, 0x48, 0x45, + 0x40, 0x46, 0x4d, 0x41, 0x43, 0x4d, 0x44, 0x4e, + 0x4e, 0x40, 0x47, 0x4b, 0x45, 0x43, 0x4b, 0x48, + 0x49, 0x44, 0x4e, 0x43, 0x4f, 0x42, 0x45, 0x4c, + 0x42, 0x49, 0x48, 0x45, 0x4c, 0x4f, 0x43, 0x4a, + 0x47, 0x4b, 0x40, 0x4e, 0x44, 0x41, 0x4a, 0x47, + 0x41, 0x46, 0x4d, 0x40, 0x4b, 0x48, 0x46, 0x4d, + 0x2c, 0x2a, 0x21, 0x2f, 0x2a, 0x24, 0x2f, 0x22, + 0x29, 0x27, 0x22, 0x2c, 0x26, 0x29, 0x28, 0x25, + 0x20, 0x26, 0x2d, 0x21, 0x23, 0x2d, 0x24, 0x2e, + 0x2e, 0x20, 0x27, 0x2b, 0x25, 0x23, 0x2b, 0x28, + 0x29, 0x24, 0x2e, 0x23, 0x2f, 0x22, 0x25, 0x2c, + 0x22, 0x29, 0x28, 0x25, 0x2c, 0x2f, 0x23, 0x2a, + 0x27, 0x2b, 0x20, 0x2e, 0x24, 0x21, 0x2a, 0x27, + 0x21, 0x26, 0x2d, 0x20, 0x2b, 0x28, 0x26, 0x2d, + 0x1c, 0x1a, 0x11, 0x1f, 0x1a, 0x14, 0x1f, 0x12, + 0x19, 0x17, 0x12, 0x1c, 0x16, 0x19, 0x18, 0x15, + 0x10, 0x16, 0x1d, 0x11, 0x13, 0x1d, 0x14, 0x1e, + 0x1e, 0x10, 0x17, 0x1b, 0x15, 0x13, 0x1b, 0x18, + 0x19, 0x14, 0x1e, 0x13, 0x1f, 0x12, 0x15, 0x1c, + 0x12, 0x19, 0x18, 0x15, 0x1c, 0x1f, 0x13, 0x1a, + 0x17, 0x1b, 0x10, 0x1e, 0x14, 0x11, 0x1a, 0x17, + 0x11, 0x16, 0x1d, 0x10, 0x1b, 0x18, 0x16, 0x1d, + 0xcc, 0xca, 0xc1, 0xcf, 0xca, 0xc4, 0xcf, 0xc2, + 0xc9, 0xc7, 0xc2, 0xcc, 0xc6, 0xc9, 0xc8, 0xc5, + 0xc0, 0xc6, 0xcd, 0xc1, 0xc3, 0xcd, 0xc4, 0xce, + 0xce, 0xc0, 0xc7, 0xcb, 0xc5, 0xc3, 0xcb, 0xc8, + 0xc9, 0xc4, 0xce, 0xc3, 0xcf, 0xc2, 0xc5, 0xcc, + 0xc2, 0xc9, 0xc8, 0xc5, 0xcc, 0xcf, 0xc3, 0xca, + 0xc7, 0xcb, 0xc0, 0xce, 0xc4, 0xc1, 0xca, 0xc7, + 0xc1, 0xc6, 0xcd, 0xc0, 0xcb, 0xc8, 0xc6, 0xcd, + 0x7c, 0x7a, 0x71, 0x7f, 0x7a, 0x74, 0x7f, 0x72, + 0x79, 0x77, 0x72, 0x7c, 0x76, 0x79, 0x78, 0x75, + 0x70, 0x76, 0x7d, 0x71, 0x73, 0x7d, 0x74, 0x7e, + 0x7e, 0x70, 0x77, 0x7b, 0x75, 0x73, 0x7b, 0x78, + 0x79, 0x74, 0x7e, 0x73, 0x7f, 0x72, 0x75, 0x7c, + 0x72, 0x79, 0x78, 0x75, 0x7c, 0x7f, 0x73, 0x7a, + 0x77, 0x7b, 0x70, 0x7e, 0x74, 0x71, 0x7a, 0x77, + 0x71, 0x76, 0x7d, 0x70, 0x7b, 0x78, 0x76, 0x7d, + 0x4c, 0x4a, 0x41, 0x4f, 0x4a, 0x44, 0x4f, 0x42, + 0x49, 0x47, 0x42, 0x4c, 0x46, 0x49, 0x48, 0x45, + 0x40, 0x46, 0x4d, 0x41, 0x43, 0x4d, 0x44, 0x4e, + 0x4e, 0x40, 0x47, 0x4b, 0x45, 0x43, 0x4b, 0x48, + 0x49, 0x44, 0x4e, 0x43, 0x4f, 0x42, 0x45, 0x4c, + 0x42, 0x49, 0x48, 0x45, 0x4c, 0x4f, 0x43, 0x4a, + 0x47, 0x4b, 0x40, 0x4e, 0x44, 0x41, 0x4a, 0x47, + 0x41, 0x46, 0x4d, 0x40, 0x4b, 0x48, 0x46, 0x4d, + 0xac, 0xaa, 0xa1, 0xaf, 0xaa, 0xa4, 0xaf, 0xa2, + 0xa9, 0xa7, 0xa2, 0xac, 0xa6, 0xa9, 0xa8, 0xa5, + 0xa0, 0xa6, 0xad, 0xa1, 0xa3, 0xad, 0xa4, 0xae, + 0xae, 0xa0, 0xa7, 0xab, 0xa5, 0xa3, 0xab, 0xa8, + 0xa9, 0xa4, 0xae, 0xa3, 0xaf, 0xa2, 0xa5, 0xac, + 0xa2, 0xa9, 0xa8, 0xa5, 0xac, 0xaf, 0xa3, 0xaa, + 0xa7, 0xab, 0xa0, 0xae, 0xa4, 0xa1, 0xaa, 0xa7, + 0xa1, 0xa6, 0xad, 0xa0, 0xab, 0xa8, 0xa6, 0xad, + 0x7c, 0x7a, 0x71, 0x7f, 0x7a, 0x74, 0x7f, 0x72, + 0x79, 0x77, 0x72, 0x7c, 0x76, 0x79, 0x78, 0x75, + 0x70, 0x76, 0x7d, 0x71, 0x73, 0x7d, 0x74, 0x7e, + 0x7e, 0x70, 0x77, 0x7b, 0x75, 0x73, 0x7b, 0x78, + 0x79, 0x74, 0x7e, 0x73, 0x7f, 0x72, 0x75, 0x7c, + 0x72, 0x79, 0x78, 0x75, 0x7c, 0x7f, 0x73, 0x7a, + 0x77, 0x7b, 0x70, 0x7e, 0x74, 0x71, 0x7a, 0x77, + 0x71, 0x76, 0x7d, 0x70, 0x7b, 0x78, 0x76, 0x7d, + 0xbc, 0xba, 0xb1, 0xbf, 0xba, 0xb4, 0xbf, 0xb2, + 0xb9, 0xb7, 0xb2, 0xbc, 0xb6, 0xb9, 0xb8, 0xb5, + 0xb0, 0xb6, 0xbd, 0xb1, 0xb3, 0xbd, 0xb4, 0xbe, + 0xbe, 0xb0, 0xb7, 0xbb, 0xb5, 0xb3, 0xbb, 0xb8, + 0xb9, 0xb4, 0xbe, 0xb3, 0xbf, 0xb2, 0xb5, 0xbc, + 0xb2, 0xb9, 0xb8, 0xb5, 0xbc, 0xbf, 0xb3, 0xba, + 0xb7, 0xbb, 0xb0, 0xbe, 0xb4, 0xb1, 0xba, 0xb7, + 0xb1, 0xb6, 0xbd, 0xb0, 0xbb, 0xb8, 0xb6, 0xbd, + 0xdc, 0xda, 0xd1, 0xdf, 0xda, 0xd4, 0xdf, 0xd2, + 0xd9, 0xd7, 0xd2, 0xdc, 0xd6, 0xd9, 0xd8, 0xd5, + 0xd0, 0xd6, 0xdd, 0xd1, 0xd3, 0xdd, 0xd4, 0xde, + 0xde, 0xd0, 0xd7, 0xdb, 0xd5, 0xd3, 0xdb, 0xd8, + 0xd9, 0xd4, 0xde, 0xd3, 0xdf, 0xd2, 0xd5, 0xdc, + 0xd2, 0xd9, 0xd8, 0xd5, 0xdc, 0xdf, 0xd3, 0xda, + 0xd7, 0xdb, 0xd0, 0xde, 0xd4, 0xd1, 0xda, 0xd7, + 0xd1, 0xd6, 0xdd, 0xd0, 0xdb, 0xd8, 0xd6, 0xdd, + 0x6c, 0x6a, 0x61, 0x6f, 0x6a, 0x64, 0x6f, 0x62, + 0x69, 0x67, 0x62, 0x6c, 0x66, 0x69, 0x68, 0x65, + 0x60, 0x66, 0x6d, 0x61, 0x63, 0x6d, 0x64, 0x6e, + 0x6e, 0x60, 0x67, 0x6b, 0x65, 0x63, 0x6b, 0x68, + 0x69, 0x64, 0x6e, 0x63, 0x6f, 0x62, 0x65, 0x6c, + 0x62, 0x69, 0x68, 0x65, 0x6c, 0x6f, 0x63, 0x6a, + 0x67, 0x6b, 0x60, 0x6e, 0x64, 0x61, 0x6a, 0x67, + 0x61, 0x66, 0x6d, 0x60, 0x6b, 0x68, 0x66, 0x6d, + 0x1c, 0x1a, 0x11, 0x1f, 0x1a, 0x14, 0x1f, 0x12, + 0x19, 0x17, 0x12, 0x1c, 0x16, 0x19, 0x18, 0x15, + 0x10, 0x16, 0x1d, 0x11, 0x13, 0x1d, 0x14, 0x1e, + 0x1e, 0x10, 0x17, 0x1b, 0x15, 0x13, 0x1b, 0x18, + 0x19, 0x14, 0x1e, 0x13, 0x1f, 0x12, 0x15, 0x1c, + 0x12, 0x19, 0x18, 0x15, 0x1c, 0x1f, 0x13, 0x1a, + 0x17, 0x1b, 0x10, 0x1e, 0x14, 0x11, 0x1a, 0x17, + 0x11, 0x16, 0x1d, 0x10, 0x1b, 0x18, 0x16, 0x1d, + 0x8c, 0x8a, 0x81, 0x8f, 0x8a, 0x84, 0x8f, 0x82, + 0x89, 0x87, 0x82, 0x8c, 0x86, 0x89, 0x88, 0x85, + 0x80, 0x86, 0x8d, 0x81, 0x83, 0x8d, 0x84, 0x8e, + 0x8e, 0x80, 0x87, 0x8b, 0x85, 0x83, 0x8b, 0x88, + 0x89, 0x84, 0x8e, 0x83, 0x8f, 0x82, 0x85, 0x8c, + 0x82, 0x89, 0x88, 0x85, 0x8c, 0x8f, 0x83, 0x8a, + 0x87, 0x8b, 0x80, 0x8e, 0x84, 0x81, 0x8a, 0x87, + 0x81, 0x86, 0x8d, 0x80, 0x8b, 0x88, 0x86, 0x8d, + 0x5c, 0x5a, 0x51, 0x5f, 0x5a, 0x54, 0x5f, 0x52, + 0x59, 0x57, 0x52, 0x5c, 0x56, 0x59, 0x58, 0x55, + 0x50, 0x56, 0x5d, 0x51, 0x53, 0x5d, 0x54, 0x5e, + 0x5e, 0x50, 0x57, 0x5b, 0x55, 0x53, 0x5b, 0x58, + 0x59, 0x54, 0x5e, 0x53, 0x5f, 0x52, 0x55, 0x5c, + 0x52, 0x59, 0x58, 0x55, 0x5c, 0x5f, 0x53, 0x5a, + 0x57, 0x5b, 0x50, 0x5e, 0x54, 0x51, 0x5a, 0x57, + 0x51, 0x56, 0x5d, 0x50, 0x5b, 0x58, 0x56, 0x5d, + 0x5c, 0x5a, 0x51, 0x5f, 0x5a, 0x54, 0x5f, 0x52, + 0x59, 0x57, 0x52, 0x5c, 0x56, 0x59, 0x58, 0x55, + 0x50, 0x56, 0x5d, 0x51, 0x53, 0x5d, 0x54, 0x5e, + 0x5e, 0x50, 0x57, 0x5b, 0x55, 0x53, 0x5b, 0x58, + 0x59, 0x54, 0x5e, 0x53, 0x5f, 0x52, 0x55, 0x5c, + 0x52, 0x59, 0x58, 0x55, 0x5c, 0x5f, 0x53, 0x5a, + 0x57, 0x5b, 0x50, 0x5e, 0x54, 0x51, 0x5a, 0x57, + 0x51, 0x56, 0x5d, 0x50, 0x5b, 0x58, 0x56, 0x5d, + 0x0c, 0x0a, 0x01, 0x0f, 0x0a, 0x04, 0x0f, 0x02, + 0x09, 0x07, 0x02, 0x0c, 0x06, 0x09, 0x08, 0x05, + 0x00, 0x06, 0x0d, 0x01, 0x03, 0x0d, 0x04, 0x0e, + 0x0e, 0x00, 0x07, 0x0b, 0x05, 0x03, 0x0b, 0x08, + 0x09, 0x04, 0x0e, 0x03, 0x0f, 0x02, 0x05, 0x0c, + 0x02, 0x09, 0x08, 0x05, 0x0c, 0x0f, 0x03, 0x0a, + 0x07, 0x0b, 0x00, 0x0e, 0x04, 0x01, 0x0a, 0x07, + 0x01, 0x06, 0x0d, 0x00, 0x0b, 0x08, 0x06, 0x0d, + 0x3c, 0x3a, 0x31, 0x3f, 0x3a, 0x34, 0x3f, 0x32, + 0x39, 0x37, 0x32, 0x3c, 0x36, 0x39, 0x38, 0x35, + 0x30, 0x36, 0x3d, 0x31, 0x33, 0x3d, 0x34, 0x3e, + 0x3e, 0x30, 0x37, 0x3b, 0x35, 0x33, 0x3b, 0x38, + 0x39, 0x34, 0x3e, 0x33, 0x3f, 0x32, 0x35, 0x3c, + 0x32, 0x39, 0x38, 0x35, 0x3c, 0x3f, 0x33, 0x3a, + 0x37, 0x3b, 0x30, 0x3e, 0x34, 0x31, 0x3a, 0x37, + 0x31, 0x36, 0x3d, 0x30, 0x3b, 0x38, 0x36, 0x3d, + 0xfc, 0xfa, 0xf1, 0xff, 0xfa, 0xf4, 0xff, 0xf2, + 0xf9, 0xf7, 0xf2, 0xfc, 0xf6, 0xf9, 0xf8, 0xf5, + 0xf0, 0xf6, 0xfd, 0xf1, 0xf3, 0xfd, 0xf4, 0xfe, + 0xfe, 0xf0, 0xf7, 0xfb, 0xf5, 0xf3, 0xfb, 0xf8, + 0xf9, 0xf4, 0xfe, 0xf3, 0xff, 0xf2, 0xf5, 0xfc, + 0xf2, 0xf9, 0xf8, 0xf5, 0xfc, 0xff, 0xf3, 0xfa, + 0xf7, 0xfb, 0xf0, 0xfe, 0xf4, 0xf1, 0xfa, 0xf7, + 0xf1, 0xf6, 0xfd, 0xf0, 0xfb, 0xf8, 0xf6, 0xfd, + 0xfc, 0xfa, 0xf1, 0xff, 0xfa, 0xf4, 0xff, 0xf2, + 0xf9, 0xf7, 0xf2, 0xfc, 0xf6, 0xf9, 0xf8, 0xf5, + 0xf0, 0xf6, 0xfd, 0xf1, 0xf3, 0xfd, 0xf4, 0xfe, + 0xfe, 0xf0, 0xf7, 0xfb, 0xf5, 0xf3, 0xfb, 0xf8, + 0xf9, 0xf4, 0xfe, 0xf3, 0xff, 0xf2, 0xf5, 0xfc, + 0xf2, 0xf9, 0xf8, 0xf5, 0xfc, 0xff, 0xf3, 0xfa, + 0xf7, 0xfb, 0xf0, 0xfe, 0xf4, 0xf1, 0xfa, 0xf7, + 0xf1, 0xf6, 0xfd, 0xf0, 0xfb, 0xf8, 0xf6, 0xfd, + 0xac, 0xaa, 0xa1, 0xaf, 0xaa, 0xa4, 0xaf, 0xa2, + 0xa9, 0xa7, 0xa2, 0xac, 0xa6, 0xa9, 0xa8, 0xa5, + 0xa0, 0xa6, 0xad, 0xa1, 0xa3, 0xad, 0xa4, 0xae, + 0xae, 0xa0, 0xa7, 0xab, 0xa5, 0xa3, 0xab, 0xa8, + 0xa9, 0xa4, 0xae, 0xa3, 0xaf, 0xa2, 0xa5, 0xac, + 0xa2, 0xa9, 0xa8, 0xa5, 0xac, 0xaf, 0xa3, 0xaa, + 0xa7, 0xab, 0xa0, 0xae, 0xa4, 0xa1, 0xaa, 0xa7, + 0xa1, 0xa6, 0xad, 0xa0, 0xab, 0xa8, 0xa6, 0xad, + 0xdc, 0xda, 0xd1, 0xdf, 0xda, 0xd4, 0xdf, 0xd2, + 0xd9, 0xd7, 0xd2, 0xdc, 0xd6, 0xd9, 0xd8, 0xd5, + 0xd0, 0xd6, 0xdd, 0xd1, 0xd3, 0xdd, 0xd4, 0xde, + 0xde, 0xd0, 0xd7, 0xdb, 0xd5, 0xd3, 0xdb, 0xd8, + 0xd9, 0xd4, 0xde, 0xd3, 0xdf, 0xd2, 0xd5, 0xdc, + 0xd2, 0xd9, 0xd8, 0xd5, 0xdc, 0xdf, 0xd3, 0xda, + 0xd7, 0xdb, 0xd0, 0xde, 0xd4, 0xd1, 0xda, 0xd7, + 0xd1, 0xd6, 0xdd, 0xd0, 0xdb, 0xd8, 0xd6, 0xdd, + 0x3c, 0x3a, 0x31, 0x3f, 0x3a, 0x34, 0x3f, 0x32, + 0x39, 0x37, 0x32, 0x3c, 0x36, 0x39, 0x38, 0x35, + 0x30, 0x36, 0x3d, 0x31, 0x33, 0x3d, 0x34, 0x3e, + 0x3e, 0x30, 0x37, 0x3b, 0x35, 0x33, 0x3b, 0x38, + 0x39, 0x34, 0x3e, 0x33, 0x3f, 0x32, 0x35, 0x3c, + 0x32, 0x39, 0x38, 0x35, 0x3c, 0x3f, 0x33, 0x3a, + 0x37, 0x3b, 0x30, 0x3e, 0x34, 0x31, 0x3a, 0x37, + 0x31, 0x36, 0x3d, 0x30, 0x3b, 0x38, 0x36, 0x3d, + 0x0c, 0x0a, 0x01, 0x0f, 0x0a, 0x04, 0x0f, 0x02, + 0x09, 0x07, 0x02, 0x0c, 0x06, 0x09, 0x08, 0x05, + 0x00, 0x06, 0x0d, 0x01, 0x03, 0x0d, 0x04, 0x0e, + 0x0e, 0x00, 0x07, 0x0b, 0x05, 0x03, 0x0b, 0x08, + 0x09, 0x04, 0x0e, 0x03, 0x0f, 0x02, 0x05, 0x0c, + 0x02, 0x09, 0x08, 0x05, 0x0c, 0x0f, 0x03, 0x0a, + 0x07, 0x0b, 0x00, 0x0e, 0x04, 0x01, 0x0a, 0x07, + 0x01, 0x06, 0x0d, 0x00, 0x0b, 0x08, 0x06, 0x0d, + 0x9c, 0x9a, 0x91, 0x9f, 0x9a, 0x94, 0x9f, 0x92, + 0x99, 0x97, 0x92, 0x9c, 0x96, 0x99, 0x98, 0x95, + 0x90, 0x96, 0x9d, 0x91, 0x93, 0x9d, 0x94, 0x9e, + 0x9e, 0x90, 0x97, 0x9b, 0x95, 0x93, 0x9b, 0x98, + 0x99, 0x94, 0x9e, 0x93, 0x9f, 0x92, 0x95, 0x9c, + 0x92, 0x99, 0x98, 0x95, 0x9c, 0x9f, 0x93, 0x9a, + 0x97, 0x9b, 0x90, 0x9e, 0x94, 0x91, 0x9a, 0x97, + 0x91, 0x96, 0x9d, 0x90, 0x9b, 0x98, 0x96, 0x9d, + 0xec, 0xea, 0xe1, 0xef, 0xea, 0xe4, 0xef, 0xe2, + 0xe9, 0xe7, 0xe2, 0xec, 0xe6, 0xe9, 0xe8, 0xe5, + 0xe0, 0xe6, 0xed, 0xe1, 0xe3, 0xed, 0xe4, 0xee, + 0xee, 0xe0, 0xe7, 0xeb, 0xe5, 0xe3, 0xeb, 0xe8, + 0xe9, 0xe4, 0xee, 0xe3, 0xef, 0xe2, 0xe5, 0xec, + 0xe2, 0xe9, 0xe8, 0xe5, 0xec, 0xef, 0xe3, 0xea, + 0xe7, 0xeb, 0xe0, 0xee, 0xe4, 0xe1, 0xea, 0xe7, + 0xe1, 0xe6, 0xed, 0xe0, 0xeb, 0xe8, 0xe6, 0xed, + 0x8c, 0x8a, 0x81, 0x8f, 0x8a, 0x84, 0x8f, 0x82, + 0x89, 0x87, 0x82, 0x8c, 0x86, 0x89, 0x88, 0x85, + 0x80, 0x86, 0x8d, 0x81, 0x83, 0x8d, 0x84, 0x8e, + 0x8e, 0x80, 0x87, 0x8b, 0x85, 0x83, 0x8b, 0x88, + 0x89, 0x84, 0x8e, 0x83, 0x8f, 0x82, 0x85, 0x8c, + 0x82, 0x89, 0x88, 0x85, 0x8c, 0x8f, 0x83, 0x8a, + 0x87, 0x8b, 0x80, 0x8e, 0x84, 0x81, 0x8a, 0x87, + 0x81, 0x86, 0x8d, 0x80, 0x8b, 0x88, 0x86, 0x8d, + 0x9c, 0x9a, 0x91, 0x9f, 0x9a, 0x94, 0x9f, 0x92, + 0x99, 0x97, 0x92, 0x9c, 0x96, 0x99, 0x98, 0x95, + 0x90, 0x96, 0x9d, 0x91, 0x93, 0x9d, 0x94, 0x9e, + 0x9e, 0x90, 0x97, 0x9b, 0x95, 0x93, 0x9b, 0x98, + 0x99, 0x94, 0x9e, 0x93, 0x9f, 0x92, 0x95, 0x9c, + 0x92, 0x99, 0x98, 0x95, 0x9c, 0x9f, 0x93, 0x9a, + 0x97, 0x9b, 0x90, 0x9e, 0x94, 0x91, 0x9a, 0x97, + 0x91, 0x96, 0x9d, 0x90, 0x9b, 0x98, 0x96, 0x9d, + 0x6c, 0x6a, 0x61, 0x6f, 0x6a, 0x64, 0x6f, 0x62, + 0x69, 0x67, 0x62, 0x6c, 0x66, 0x69, 0x68, 0x65, + 0x60, 0x66, 0x6d, 0x61, 0x63, 0x6d, 0x64, 0x6e, + 0x6e, 0x60, 0x67, 0x6b, 0x65, 0x63, 0x6b, 0x68, + 0x69, 0x64, 0x6e, 0x63, 0x6f, 0x62, 0x65, 0x6c, + 0x62, 0x69, 0x68, 0x65, 0x6c, 0x6f, 0x63, 0x6a, + 0x67, 0x6b, 0x60, 0x6e, 0x64, 0x61, 0x6a, 0x67, + 0x61, 0x66, 0x6d, 0x60, 0x6b, 0x68, 0x66, 0x6d, + 0x4c, 0x4a, 0x41, 0x4f, 0x4a, 0x44, 0x4f, 0x42, + 0x49, 0x47, 0x42, 0x4c, 0x46, 0x49, 0x48, 0x45, + 0x40, 0x46, 0x4d, 0x41, 0x43, 0x4d, 0x44, 0x4e, + 0x4e, 0x40, 0x47, 0x4b, 0x45, 0x43, 0x4b, 0x48, + 0x49, 0x44, 0x4e, 0x43, 0x4f, 0x42, 0x45, 0x4c, + 0x42, 0x49, 0x48, 0x45, 0x4c, 0x4f, 0x43, 0x4a, + 0x47, 0x4b, 0x40, 0x4e, 0x44, 0x41, 0x4a, 0x47, + 0x41, 0x46, 0x4d, 0x40, 0x4b, 0x48, 0x46, 0x4d, + 0xbc, 0xba, 0xb1, 0xbf, 0xba, 0xb4, 0xbf, 0xb2, + 0xb9, 0xb7, 0xb2, 0xbc, 0xb6, 0xb9, 0xb8, 0xb5, + 0xb0, 0xb6, 0xbd, 0xb1, 0xb3, 0xbd, 0xb4, 0xbe, + 0xbe, 0xb0, 0xb7, 0xbb, 0xb5, 0xb3, 0xbb, 0xb8, + 0xb9, 0xb4, 0xbe, 0xb3, 0xbf, 0xb2, 0xb5, 0xbc, + 0xb2, 0xb9, 0xb8, 0xb5, 0xbc, 0xbf, 0xb3, 0xba, + 0xb7, 0xbb, 0xb0, 0xbe, 0xb4, 0xb1, 0xba, 0xb7, + 0xb1, 0xb6, 0xbd, 0xb0, 0xbb, 0xb8, 0xb6, 0xbd, + 0x2c, 0x2a, 0x21, 0x2f, 0x2a, 0x24, 0x2f, 0x22, + 0x29, 0x27, 0x22, 0x2c, 0x26, 0x29, 0x28, 0x25, + 0x20, 0x26, 0x2d, 0x21, 0x23, 0x2d, 0x24, 0x2e, + 0x2e, 0x20, 0x27, 0x2b, 0x25, 0x23, 0x2b, 0x28, + 0x29, 0x24, 0x2e, 0x23, 0x2f, 0x22, 0x25, 0x2c, + 0x22, 0x29, 0x28, 0x25, 0x2c, 0x2f, 0x23, 0x2a, + 0x27, 0x2b, 0x20, 0x2e, 0x24, 0x21, 0x2a, 0x27, + 0x21, 0x26, 0x2d, 0x20, 0x2b, 0x28, 0x26, 0x2d, + 0x8c, 0x8a, 0x81, 0x8f, 0x8a, 0x84, 0x8f, 0x82, + 0x89, 0x87, 0x82, 0x8c, 0x86, 0x89, 0x88, 0x85, + 0x80, 0x86, 0x8d, 0x81, 0x83, 0x8d, 0x84, 0x8e, + 0x8e, 0x80, 0x87, 0x8b, 0x85, 0x83, 0x8b, 0x88, + 0x89, 0x84, 0x8e, 0x83, 0x8f, 0x82, 0x85, 0x8c, + 0x82, 0x89, 0x88, 0x85, 0x8c, 0x8f, 0x83, 0x8a, + 0x87, 0x8b, 0x80, 0x8e, 0x84, 0x81, 0x8a, 0x87, + 0x81, 0x86, 0x8d, 0x80, 0x8b, 0x88, 0x86, 0x8d, + 0x1c, 0x1a, 0x11, 0x1f, 0x1a, 0x14, 0x1f, 0x12, + 0x19, 0x17, 0x12, 0x1c, 0x16, 0x19, 0x18, 0x15, + 0x10, 0x16, 0x1d, 0x11, 0x13, 0x1d, 0x14, 0x1e, + 0x1e, 0x10, 0x17, 0x1b, 0x15, 0x13, 0x1b, 0x18, + 0x19, 0x14, 0x1e, 0x13, 0x1f, 0x12, 0x15, 0x1c, + 0x12, 0x19, 0x18, 0x15, 0x1c, 0x1f, 0x13, 0x1a, + 0x17, 0x1b, 0x10, 0x1e, 0x14, 0x11, 0x1a, 0x17, + 0x11, 0x16, 0x1d, 0x10, 0x1b, 0x18, 0x16, 0x1d, + 0xcc, 0xca, 0xc1, 0xcf, 0xca, 0xc4, 0xcf, 0xc2, + 0xc9, 0xc7, 0xc2, 0xcc, 0xc6, 0xc9, 0xc8, 0xc5, + 0xc0, 0xc6, 0xcd, 0xc1, 0xc3, 0xcd, 0xc4, 0xce, + 0xce, 0xc0, 0xc7, 0xcb, 0xc5, 0xc3, 0xcb, 0xc8, + 0xc9, 0xc4, 0xce, 0xc3, 0xcf, 0xc2, 0xc5, 0xcc, + 0xc2, 0xc9, 0xc8, 0xc5, 0xcc, 0xcf, 0xc3, 0xca, + 0xc7, 0xcb, 0xc0, 0xce, 0xc4, 0xc1, 0xca, 0xc7, + 0xc1, 0xc6, 0xcd, 0xc0, 0xcb, 0xc8, 0xc6, 0xcd, + 0xbc, 0xba, 0xb1, 0xbf, 0xba, 0xb4, 0xbf, 0xb2, + 0xb9, 0xb7, 0xb2, 0xbc, 0xb6, 0xb9, 0xb8, 0xb5, + 0xb0, 0xb6, 0xbd, 0xb1, 0xb3, 0xbd, 0xb4, 0xbe, + 0xbe, 0xb0, 0xb7, 0xbb, 0xb5, 0xb3, 0xbb, 0xb8, + 0xb9, 0xb4, 0xbe, 0xb3, 0xbf, 0xb2, 0xb5, 0xbc, + 0xb2, 0xb9, 0xb8, 0xb5, 0xbc, 0xbf, 0xb3, 0xba, + 0xb7, 0xbb, 0xb0, 0xbe, 0xb4, 0xb1, 0xba, 0xb7, + 0xb1, 0xb6, 0xbd, 0xb0, 0xbb, 0xb8, 0xb6, 0xbd, + 0x7c, 0x7a, 0x71, 0x7f, 0x7a, 0x74, 0x7f, 0x72, + 0x79, 0x77, 0x72, 0x7c, 0x76, 0x79, 0x78, 0x75, + 0x70, 0x76, 0x7d, 0x71, 0x73, 0x7d, 0x74, 0x7e, + 0x7e, 0x70, 0x77, 0x7b, 0x75, 0x73, 0x7b, 0x78, + 0x79, 0x74, 0x7e, 0x73, 0x7f, 0x72, 0x75, 0x7c, + 0x72, 0x79, 0x78, 0x75, 0x7c, 0x7f, 0x73, 0x7a, + 0x77, 0x7b, 0x70, 0x7e, 0x74, 0x71, 0x7a, 0x77, + 0x71, 0x76, 0x7d, 0x70, 0x7b, 0x78, 0x76, 0x7d, + 0xac, 0xaa, 0xa1, 0xaf, 0xaa, 0xa4, 0xaf, 0xa2, + 0xa9, 0xa7, 0xa2, 0xac, 0xa6, 0xa9, 0xa8, 0xa5, + 0xa0, 0xa6, 0xad, 0xa1, 0xa3, 0xad, 0xa4, 0xae, + 0xae, 0xa0, 0xa7, 0xab, 0xa5, 0xa3, 0xab, 0xa8, + 0xa9, 0xa4, 0xae, 0xa3, 0xaf, 0xa2, 0xa5, 0xac, + 0xa2, 0xa9, 0xa8, 0xa5, 0xac, 0xaf, 0xa3, 0xaa, + 0xa7, 0xab, 0xa0, 0xae, 0xa4, 0xa1, 0xaa, 0xa7, + 0xa1, 0xa6, 0xad, 0xa0, 0xab, 0xa8, 0xa6, 0xad, + 0x1c, 0x1a, 0x11, 0x1f, 0x1a, 0x14, 0x1f, 0x12, + 0x19, 0x17, 0x12, 0x1c, 0x16, 0x19, 0x18, 0x15, + 0x10, 0x16, 0x1d, 0x11, 0x13, 0x1d, 0x14, 0x1e, + 0x1e, 0x10, 0x17, 0x1b, 0x15, 0x13, 0x1b, 0x18, + 0x19, 0x14, 0x1e, 0x13, 0x1f, 0x12, 0x15, 0x1c, + 0x12, 0x19, 0x18, 0x15, 0x1c, 0x1f, 0x13, 0x1a, + 0x17, 0x1b, 0x10, 0x1e, 0x14, 0x11, 0x1a, 0x17, + 0x11, 0x16, 0x1d, 0x10, 0x1b, 0x18, 0x16, 0x1d, + 0xdc, 0xda, 0xd1, 0xdf, 0xda, 0xd4, 0xdf, 0xd2, + 0xd9, 0xd7, 0xd2, 0xdc, 0xd6, 0xd9, 0xd8, 0xd5, + 0xd0, 0xd6, 0xdd, 0xd1, 0xd3, 0xdd, 0xd4, 0xde, + 0xde, 0xd0, 0xd7, 0xdb, 0xd5, 0xd3, 0xdb, 0xd8, + 0xd9, 0xd4, 0xde, 0xd3, 0xdf, 0xd2, 0xd5, 0xdc, + 0xd2, 0xd9, 0xd8, 0xd5, 0xdc, 0xdf, 0xd3, 0xda, + 0xd7, 0xdb, 0xd0, 0xde, 0xd4, 0xd1, 0xda, 0xd7, + 0xd1, 0xd6, 0xdd, 0xd0, 0xdb, 0xd8, 0xd6, 0xdd, + 0xec, 0xea, 0xe1, 0xef, 0xea, 0xe4, 0xef, 0xe2, + 0xe9, 0xe7, 0xe2, 0xec, 0xe6, 0xe9, 0xe8, 0xe5, + 0xe0, 0xe6, 0xed, 0xe1, 0xe3, 0xed, 0xe4, 0xee, + 0xee, 0xe0, 0xe7, 0xeb, 0xe5, 0xe3, 0xeb, 0xe8, + 0xe9, 0xe4, 0xee, 0xe3, 0xef, 0xe2, 0xe5, 0xec, + 0xe2, 0xe9, 0xe8, 0xe5, 0xec, 0xef, 0xe3, 0xea, + 0xe7, 0xeb, 0xe0, 0xee, 0xe4, 0xe1, 0xea, 0xe7, + 0xe1, 0xe6, 0xed, 0xe0, 0xeb, 0xe8, 0xe6, 0xed, + 0x7c, 0x7a, 0x71, 0x7f, 0x7a, 0x74, 0x7f, 0x72, + 0x79, 0x77, 0x72, 0x7c, 0x76, 0x79, 0x78, 0x75, + 0x70, 0x76, 0x7d, 0x71, 0x73, 0x7d, 0x74, 0x7e, + 0x7e, 0x70, 0x77, 0x7b, 0x75, 0x73, 0x7b, 0x78, + 0x79, 0x74, 0x7e, 0x73, 0x7f, 0x72, 0x75, 0x7c, + 0x72, 0x79, 0x78, 0x75, 0x7c, 0x7f, 0x73, 0x7a, + 0x77, 0x7b, 0x70, 0x7e, 0x74, 0x71, 0x7a, 0x77, + 0x71, 0x76, 0x7d, 0x70, 0x7b, 0x78, 0x76, 0x7d, + 0x2c, 0x2a, 0x21, 0x2f, 0x2a, 0x24, 0x2f, 0x22, + 0x29, 0x27, 0x22, 0x2c, 0x26, 0x29, 0x28, 0x25, + 0x20, 0x26, 0x2d, 0x21, 0x23, 0x2d, 0x24, 0x2e, + 0x2e, 0x20, 0x27, 0x2b, 0x25, 0x23, 0x2b, 0x28, + 0x29, 0x24, 0x2e, 0x23, 0x2f, 0x22, 0x25, 0x2c, + 0x22, 0x29, 0x28, 0x25, 0x2c, 0x2f, 0x23, 0x2a, + 0x27, 0x2b, 0x20, 0x2e, 0x24, 0x21, 0x2a, 0x27, + 0x21, 0x26, 0x2d, 0x20, 0x2b, 0x28, 0x26, 0x2d, + 0x8c, 0x8a, 0x81, 0x8f, 0x8a, 0x84, 0x8f, 0x82, + 0x89, 0x87, 0x82, 0x8c, 0x86, 0x89, 0x88, 0x85, + 0x80, 0x86, 0x8d, 0x81, 0x83, 0x8d, 0x84, 0x8e, + 0x8e, 0x80, 0x87, 0x8b, 0x85, 0x83, 0x8b, 0x88, + 0x89, 0x84, 0x8e, 0x83, 0x8f, 0x82, 0x85, 0x8c, + 0x82, 0x89, 0x88, 0x85, 0x8c, 0x8f, 0x83, 0x8a, + 0x87, 0x8b, 0x80, 0x8e, 0x84, 0x81, 0x8a, 0x87, + 0x81, 0x86, 0x8d, 0x80, 0x8b, 0x88, 0x86, 0x8d, + 0xdc, 0xda, 0xd1, 0xdf, 0xda, 0xd4, 0xdf, 0xd2, + 0xd9, 0xd7, 0xd2, 0xdc, 0xd6, 0xd9, 0xd8, 0xd5, + 0xd0, 0xd6, 0xdd, 0xd1, 0xd3, 0xdd, 0xd4, 0xde, + 0xde, 0xd0, 0xd7, 0xdb, 0xd5, 0xd3, 0xdb, 0xd8, + 0xd9, 0xd4, 0xde, 0xd3, 0xdf, 0xd2, 0xd5, 0xdc, + 0xd2, 0xd9, 0xd8, 0xd5, 0xdc, 0xdf, 0xd3, 0xda, + 0xd7, 0xdb, 0xd0, 0xde, 0xd4, 0xd1, 0xda, 0xd7, + 0xd1, 0xd6, 0xdd, 0xd0, 0xdb, 0xd8, 0xd6, 0xdd, + 0xfc, 0xfa, 0xf1, 0xff, 0xfa, 0xf4, 0xff, 0xf2, + 0xf9, 0xf7, 0xf2, 0xfc, 0xf6, 0xf9, 0xf8, 0xf5, + 0xf0, 0xf6, 0xfd, 0xf1, 0xf3, 0xfd, 0xf4, 0xfe, + 0xfe, 0xf0, 0xf7, 0xfb, 0xf5, 0xf3, 0xfb, 0xf8, + 0xf9, 0xf4, 0xfe, 0xf3, 0xff, 0xf2, 0xf5, 0xfc, + 0xf2, 0xf9, 0xf8, 0xf5, 0xfc, 0xff, 0xf3, 0xfa, + 0xf7, 0xfb, 0xf0, 0xfe, 0xf4, 0xf1, 0xfa, 0xf7, + 0xf1, 0xf6, 0xfd, 0xf0, 0xfb, 0xf8, 0xf6, 0xfd, + 0x6c, 0x6a, 0x61, 0x6f, 0x6a, 0x64, 0x6f, 0x62, + 0x69, 0x67, 0x62, 0x6c, 0x66, 0x69, 0x68, 0x65, + 0x60, 0x66, 0x6d, 0x61, 0x63, 0x6d, 0x64, 0x6e, + 0x6e, 0x60, 0x67, 0x6b, 0x65, 0x63, 0x6b, 0x68, + 0x69, 0x64, 0x6e, 0x63, 0x6f, 0x62, 0x65, 0x6c, + 0x62, 0x69, 0x68, 0x65, 0x6c, 0x6f, 0x63, 0x6a, + 0x67, 0x6b, 0x60, 0x6e, 0x64, 0x61, 0x6a, 0x67, + 0x61, 0x66, 0x6d, 0x60, 0x6b, 0x68, 0x66, 0x6d, + 0x9c, 0x9a, 0x91, 0x9f, 0x9a, 0x94, 0x9f, 0x92, + 0x99, 0x97, 0x92, 0x9c, 0x96, 0x99, 0x98, 0x95, + 0x90, 0x96, 0x9d, 0x91, 0x93, 0x9d, 0x94, 0x9e, + 0x9e, 0x90, 0x97, 0x9b, 0x95, 0x93, 0x9b, 0x98, + 0x99, 0x94, 0x9e, 0x93, 0x9f, 0x92, 0x95, 0x9c, + 0x92, 0x99, 0x98, 0x95, 0x9c, 0x9f, 0x93, 0x9a, + 0x97, 0x9b, 0x90, 0x9e, 0x94, 0x91, 0x9a, 0x97, + 0x91, 0x96, 0x9d, 0x90, 0x9b, 0x98, 0x96, 0x9d, + 0xfc, 0xfa, 0xf1, 0xff, 0xfa, 0xf4, 0xff, 0xf2, + 0xf9, 0xf7, 0xf2, 0xfc, 0xf6, 0xf9, 0xf8, 0xf5, + 0xf0, 0xf6, 0xfd, 0xf1, 0xf3, 0xfd, 0xf4, 0xfe, + 0xfe, 0xf0, 0xf7, 0xfb, 0xf5, 0xf3, 0xfb, 0xf8, + 0xf9, 0xf4, 0xfe, 0xf3, 0xff, 0xf2, 0xf5, 0xfc, + 0xf2, 0xf9, 0xf8, 0xf5, 0xfc, 0xff, 0xf3, 0xfa, + 0xf7, 0xfb, 0xf0, 0xfe, 0xf4, 0xf1, 0xfa, 0xf7, + 0xf1, 0xf6, 0xfd, 0xf0, 0xfb, 0xf8, 0xf6, 0xfd, + 0xcc, 0xca, 0xc1, 0xcf, 0xca, 0xc4, 0xcf, 0xc2, + 0xc9, 0xc7, 0xc2, 0xcc, 0xc6, 0xc9, 0xc8, 0xc5, + 0xc0, 0xc6, 0xcd, 0xc1, 0xc3, 0xcd, 0xc4, 0xce, + 0xce, 0xc0, 0xc7, 0xcb, 0xc5, 0xc3, 0xcb, 0xc8, + 0xc9, 0xc4, 0xce, 0xc3, 0xcf, 0xc2, 0xc5, 0xcc, + 0xc2, 0xc9, 0xc8, 0xc5, 0xcc, 0xcf, 0xc3, 0xca, + 0xc7, 0xcb, 0xc0, 0xce, 0xc4, 0xc1, 0xca, 0xc7, + 0xc1, 0xc6, 0xcd, 0xc0, 0xcb, 0xc8, 0xc6, 0xcd, + 0x0c, 0x0a, 0x01, 0x0f, 0x0a, 0x04, 0x0f, 0x02, + 0x09, 0x07, 0x02, 0x0c, 0x06, 0x09, 0x08, 0x05, + 0x00, 0x06, 0x0d, 0x01, 0x03, 0x0d, 0x04, 0x0e, + 0x0e, 0x00, 0x07, 0x0b, 0x05, 0x03, 0x0b, 0x08, + 0x09, 0x04, 0x0e, 0x03, 0x0f, 0x02, 0x05, 0x0c, + 0x02, 0x09, 0x08, 0x05, 0x0c, 0x0f, 0x03, 0x0a, + 0x07, 0x0b, 0x00, 0x0e, 0x04, 0x01, 0x0a, 0x07, + 0x01, 0x06, 0x0d, 0x00, 0x0b, 0x08, 0x06, 0x0d, + 0x5c, 0x5a, 0x51, 0x5f, 0x5a, 0x54, 0x5f, 0x52, + 0x59, 0x57, 0x52, 0x5c, 0x56, 0x59, 0x58, 0x55, + 0x50, 0x56, 0x5d, 0x51, 0x53, 0x5d, 0x54, 0x5e, + 0x5e, 0x50, 0x57, 0x5b, 0x55, 0x53, 0x5b, 0x58, + 0x59, 0x54, 0x5e, 0x53, 0x5f, 0x52, 0x55, 0x5c, + 0x52, 0x59, 0x58, 0x55, 0x5c, 0x5f, 0x53, 0x5a, + 0x57, 0x5b, 0x50, 0x5e, 0x54, 0x51, 0x5a, 0x57, + 0x51, 0x56, 0x5d, 0x50, 0x5b, 0x58, 0x56, 0x5d, + 0x9c, 0x9a, 0x91, 0x9f, 0x9a, 0x94, 0x9f, 0x92, + 0x99, 0x97, 0x92, 0x9c, 0x96, 0x99, 0x98, 0x95, + 0x90, 0x96, 0x9d, 0x91, 0x93, 0x9d, 0x94, 0x9e, + 0x9e, 0x90, 0x97, 0x9b, 0x95, 0x93, 0x9b, 0x98, + 0x99, 0x94, 0x9e, 0x93, 0x9f, 0x92, 0x95, 0x9c, + 0x92, 0x99, 0x98, 0x95, 0x9c, 0x9f, 0x93, 0x9a, + 0x97, 0x9b, 0x90, 0x9e, 0x94, 0x91, 0x9a, 0x97, + 0x91, 0x96, 0x9d, 0x90, 0x9b, 0x98, 0x96, 0x9d, + 0x6c, 0x6a, 0x61, 0x6f, 0x6a, 0x64, 0x6f, 0x62, + 0x69, 0x67, 0x62, 0x6c, 0x66, 0x69, 0x68, 0x65, + 0x60, 0x66, 0x6d, 0x61, 0x63, 0x6d, 0x64, 0x6e, + 0x6e, 0x60, 0x67, 0x6b, 0x65, 0x63, 0x6b, 0x68, + 0x69, 0x64, 0x6e, 0x63, 0x6f, 0x62, 0x65, 0x6c, + 0x62, 0x69, 0x68, 0x65, 0x6c, 0x6f, 0x63, 0x6a, + 0x67, 0x6b, 0x60, 0x6e, 0x64, 0x61, 0x6a, 0x67, + 0x61, 0x66, 0x6d, 0x60, 0x6b, 0x68, 0x66, 0x6d, + 0xac, 0xaa, 0xa1, 0xaf, 0xaa, 0xa4, 0xaf, 0xa2, + 0xa9, 0xa7, 0xa2, 0xac, 0xa6, 0xa9, 0xa8, 0xa5, + 0xa0, 0xa6, 0xad, 0xa1, 0xa3, 0xad, 0xa4, 0xae, + 0xae, 0xa0, 0xa7, 0xab, 0xa5, 0xa3, 0xab, 0xa8, + 0xa9, 0xa4, 0xae, 0xa3, 0xaf, 0xa2, 0xa5, 0xac, + 0xa2, 0xa9, 0xa8, 0xa5, 0xac, 0xaf, 0xa3, 0xaa, + 0xa7, 0xab, 0xa0, 0xae, 0xa4, 0xa1, 0xaa, 0xa7, + 0xa1, 0xa6, 0xad, 0xa0, 0xab, 0xa8, 0xa6, 0xad, + 0x3c, 0x3a, 0x31, 0x3f, 0x3a, 0x34, 0x3f, 0x32, + 0x39, 0x37, 0x32, 0x3c, 0x36, 0x39, 0x38, 0x35, + 0x30, 0x36, 0x3d, 0x31, 0x33, 0x3d, 0x34, 0x3e, + 0x3e, 0x30, 0x37, 0x3b, 0x35, 0x33, 0x3b, 0x38, + 0x39, 0x34, 0x3e, 0x33, 0x3f, 0x32, 0x35, 0x3c, + 0x32, 0x39, 0x38, 0x35, 0x3c, 0x3f, 0x33, 0x3a, + 0x37, 0x3b, 0x30, 0x3e, 0x34, 0x31, 0x3a, 0x37, + 0x31, 0x36, 0x3d, 0x30, 0x3b, 0x38, 0x36, 0x3d, + 0x4c, 0x4a, 0x41, 0x4f, 0x4a, 0x44, 0x4f, 0x42, + 0x49, 0x47, 0x42, 0x4c, 0x46, 0x49, 0x48, 0x45, + 0x40, 0x46, 0x4d, 0x41, 0x43, 0x4d, 0x44, 0x4e, + 0x4e, 0x40, 0x47, 0x4b, 0x45, 0x43, 0x4b, 0x48, + 0x49, 0x44, 0x4e, 0x43, 0x4f, 0x42, 0x45, 0x4c, + 0x42, 0x49, 0x48, 0x45, 0x4c, 0x4f, 0x43, 0x4a, + 0x47, 0x4b, 0x40, 0x4e, 0x44, 0x41, 0x4a, 0x47, + 0x41, 0x46, 0x4d, 0x40, 0x4b, 0x48, 0x46, 0x4d, + 0x0c, 0x0a, 0x01, 0x0f, 0x0a, 0x04, 0x0f, 0x02, + 0x09, 0x07, 0x02, 0x0c, 0x06, 0x09, 0x08, 0x05, + 0x00, 0x06, 0x0d, 0x01, 0x03, 0x0d, 0x04, 0x0e, + 0x0e, 0x00, 0x07, 0x0b, 0x05, 0x03, 0x0b, 0x08, + 0x09, 0x04, 0x0e, 0x03, 0x0f, 0x02, 0x05, 0x0c, + 0x02, 0x09, 0x08, 0x05, 0x0c, 0x0f, 0x03, 0x0a, + 0x07, 0x0b, 0x00, 0x0e, 0x04, 0x01, 0x0a, 0x07, + 0x01, 0x06, 0x0d, 0x00, 0x0b, 0x08, 0x06, 0x0d, + 0x5c, 0x5a, 0x51, 0x5f, 0x5a, 0x54, 0x5f, 0x52, + 0x59, 0x57, 0x52, 0x5c, 0x56, 0x59, 0x58, 0x55, + 0x50, 0x56, 0x5d, 0x51, 0x53, 0x5d, 0x54, 0x5e, + 0x5e, 0x50, 0x57, 0x5b, 0x55, 0x53, 0x5b, 0x58, + 0x59, 0x54, 0x5e, 0x53, 0x5f, 0x52, 0x55, 0x5c, + 0x52, 0x59, 0x58, 0x55, 0x5c, 0x5f, 0x53, 0x5a, + 0x57, 0x5b, 0x50, 0x5e, 0x54, 0x51, 0x5a, 0x57, + 0x51, 0x56, 0x5d, 0x50, 0x5b, 0x58, 0x56, 0x5d, + 0xec, 0xea, 0xe1, 0xef, 0xea, 0xe4, 0xef, 0xe2, + 0xe9, 0xe7, 0xe2, 0xec, 0xe6, 0xe9, 0xe8, 0xe5, + 0xe0, 0xe6, 0xed, 0xe1, 0xe3, 0xed, 0xe4, 0xee, + 0xee, 0xe0, 0xe7, 0xeb, 0xe5, 0xe3, 0xeb, 0xe8, + 0xe9, 0xe4, 0xee, 0xe3, 0xef, 0xe2, 0xe5, 0xec, + 0xe2, 0xe9, 0xe8, 0xe5, 0xec, 0xef, 0xe3, 0xea, + 0xe7, 0xeb, 0xe0, 0xee, 0xe4, 0xe1, 0xea, 0xe7, + 0xe1, 0xe6, 0xed, 0xe0, 0xeb, 0xe8, 0xe6, 0xed, + 0x3c, 0x3a, 0x31, 0x3f, 0x3a, 0x34, 0x3f, 0x32, + 0x39, 0x37, 0x32, 0x3c, 0x36, 0x39, 0x38, 0x35, + 0x30, 0x36, 0x3d, 0x31, 0x33, 0x3d, 0x34, 0x3e, + 0x3e, 0x30, 0x37, 0x3b, 0x35, 0x33, 0x3b, 0x38, + 0x39, 0x34, 0x3e, 0x33, 0x3f, 0x32, 0x35, 0x3c, + 0x32, 0x39, 0x38, 0x35, 0x3c, 0x3f, 0x33, 0x3a, + 0x37, 0x3b, 0x30, 0x3e, 0x34, 0x31, 0x3a, 0x37, + 0x31, 0x36, 0x3d, 0x30, 0x3b, 0x38, 0x36, 0x3d, + },{ + 0x4d, 0x41, 0x42, 0x4f, 0x48, 0x4d, 0x44, 0x48, + 0x46, 0x4a, 0x4f, 0x43, 0x4b, 0x47, 0x41, 0x44, + 0x4a, 0x4c, 0x49, 0x45, 0x43, 0x46, 0x4e, 0x4b, + 0x45, 0x40, 0x40, 0x4e, 0x4c, 0x49, 0x47, 0x42, + 0x47, 0x42, 0x4b, 0x41, 0x44, 0x4e, 0x41, 0x47, + 0x49, 0x44, 0x4c, 0x4a, 0x4e, 0x48, 0x42, 0x4d, + 0x40, 0x4f, 0x46, 0x4c, 0x4a, 0x49, 0x4d, 0x40, + 0x4f, 0x43, 0x43, 0x45, 0x45, 0x46, 0x48, 0x4b, + 0xdd, 0xd1, 0xd2, 0xdf, 0xd8, 0xdd, 0xd4, 0xd8, + 0xd6, 0xda, 0xdf, 0xd3, 0xdb, 0xd7, 0xd1, 0xd4, + 0xda, 0xdc, 0xd9, 0xd5, 0xd3, 0xd6, 0xde, 0xdb, + 0xd5, 0xd0, 0xd0, 0xde, 0xdc, 0xd9, 0xd7, 0xd2, + 0xd7, 0xd2, 0xdb, 0xd1, 0xd4, 0xde, 0xd1, 0xd7, + 0xd9, 0xd4, 0xdc, 0xda, 0xde, 0xd8, 0xd2, 0xdd, + 0xd0, 0xdf, 0xd6, 0xdc, 0xda, 0xd9, 0xdd, 0xd0, + 0xdf, 0xd3, 0xd3, 0xd5, 0xd5, 0xd6, 0xd8, 0xdb, + 0xbd, 0xb1, 0xb2, 0xbf, 0xb8, 0xbd, 0xb4, 0xb8, + 0xb6, 0xba, 0xbf, 0xb3, 0xbb, 0xb7, 0xb1, 0xb4, + 0xba, 0xbc, 0xb9, 0xb5, 0xb3, 0xb6, 0xbe, 0xbb, + 0xb5, 0xb0, 0xb0, 0xbe, 0xbc, 0xb9, 0xb7, 0xb2, + 0xb7, 0xb2, 0xbb, 0xb1, 0xb4, 0xbe, 0xb1, 0xb7, + 0xb9, 0xb4, 0xbc, 0xba, 0xbe, 0xb8, 0xb2, 0xbd, + 0xb0, 0xbf, 0xb6, 0xbc, 0xba, 0xb9, 0xbd, 0xb0, + 0xbf, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb8, 0xbb, + 0x0d, 0x01, 0x02, 0x0f, 0x08, 0x0d, 0x04, 0x08, + 0x06, 0x0a, 0x0f, 0x03, 0x0b, 0x07, 0x01, 0x04, + 0x0a, 0x0c, 0x09, 0x05, 0x03, 0x06, 0x0e, 0x0b, + 0x05, 0x00, 0x00, 0x0e, 0x0c, 0x09, 0x07, 0x02, + 0x07, 0x02, 0x0b, 0x01, 0x04, 0x0e, 0x01, 0x07, + 0x09, 0x04, 0x0c, 0x0a, 0x0e, 0x08, 0x02, 0x0d, + 0x00, 0x0f, 0x06, 0x0c, 0x0a, 0x09, 0x0d, 0x00, + 0x0f, 0x03, 0x03, 0x05, 0x05, 0x06, 0x08, 0x0b, + 0x2d, 0x21, 0x22, 0x2f, 0x28, 0x2d, 0x24, 0x28, + 0x26, 0x2a, 0x2f, 0x23, 0x2b, 0x27, 0x21, 0x24, + 0x2a, 0x2c, 0x29, 0x25, 0x23, 0x26, 0x2e, 0x2b, + 0x25, 0x20, 0x20, 0x2e, 0x2c, 0x29, 0x27, 0x22, + 0x27, 0x22, 0x2b, 0x21, 0x24, 0x2e, 0x21, 0x27, + 0x29, 0x24, 0x2c, 0x2a, 0x2e, 0x28, 0x22, 0x2d, + 0x20, 0x2f, 0x26, 0x2c, 0x2a, 0x29, 0x2d, 0x20, + 0x2f, 0x23, 0x23, 0x25, 0x25, 0x26, 0x28, 0x2b, + 0xbd, 0xb1, 0xb2, 0xbf, 0xb8, 0xbd, 0xb4, 0xb8, + 0xb6, 0xba, 0xbf, 0xb3, 0xbb, 0xb7, 0xb1, 0xb4, + 0xba, 0xbc, 0xb9, 0xb5, 0xb3, 0xb6, 0xbe, 0xbb, + 0xb5, 0xb0, 0xb0, 0xbe, 0xbc, 0xb9, 0xb7, 0xb2, + 0xb7, 0xb2, 0xbb, 0xb1, 0xb4, 0xbe, 0xb1, 0xb7, + 0xb9, 0xb4, 0xbc, 0xba, 0xbe, 0xb8, 0xb2, 0xbd, + 0xb0, 0xbf, 0xb6, 0xbc, 0xba, 0xb9, 0xbd, 0xb0, + 0xbf, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb8, 0xbb, + 0xed, 0xe1, 0xe2, 0xef, 0xe8, 0xed, 0xe4, 0xe8, + 0xe6, 0xea, 0xef, 0xe3, 0xeb, 0xe7, 0xe1, 0xe4, + 0xea, 0xec, 0xe9, 0xe5, 0xe3, 0xe6, 0xee, 0xeb, + 0xe5, 0xe0, 0xe0, 0xee, 0xec, 0xe9, 0xe7, 0xe2, + 0xe7, 0xe2, 0xeb, 0xe1, 0xe4, 0xee, 0xe1, 0xe7, + 0xe9, 0xe4, 0xec, 0xea, 0xee, 0xe8, 0xe2, 0xed, + 0xe0, 0xef, 0xe6, 0xec, 0xea, 0xe9, 0xed, 0xe0, + 0xef, 0xe3, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, 0xeb, + 0x7d, 0x71, 0x72, 0x7f, 0x78, 0x7d, 0x74, 0x78, + 0x76, 0x7a, 0x7f, 0x73, 0x7b, 0x77, 0x71, 0x74, + 0x7a, 0x7c, 0x79, 0x75, 0x73, 0x76, 0x7e, 0x7b, + 0x75, 0x70, 0x70, 0x7e, 0x7c, 0x79, 0x77, 0x72, + 0x77, 0x72, 0x7b, 0x71, 0x74, 0x7e, 0x71, 0x77, + 0x79, 0x74, 0x7c, 0x7a, 0x7e, 0x78, 0x72, 0x7d, + 0x70, 0x7f, 0x76, 0x7c, 0x7a, 0x79, 0x7d, 0x70, + 0x7f, 0x73, 0x73, 0x75, 0x75, 0x76, 0x78, 0x7b, + 0xfd, 0xf1, 0xf2, 0xff, 0xf8, 0xfd, 0xf4, 0xf8, + 0xf6, 0xfa, 0xff, 0xf3, 0xfb, 0xf7, 0xf1, 0xf4, + 0xfa, 0xfc, 0xf9, 0xf5, 0xf3, 0xf6, 0xfe, 0xfb, + 0xf5, 0xf0, 0xf0, 0xfe, 0xfc, 0xf9, 0xf7, 0xf2, + 0xf7, 0xf2, 0xfb, 0xf1, 0xf4, 0xfe, 0xf1, 0xf7, + 0xf9, 0xf4, 0xfc, 0xfa, 0xfe, 0xf8, 0xf2, 0xfd, + 0xf0, 0xff, 0xf6, 0xfc, 0xfa, 0xf9, 0xfd, 0xf0, + 0xff, 0xf3, 0xf3, 0xf5, 0xf5, 0xf6, 0xf8, 0xfb, + 0x4d, 0x41, 0x42, 0x4f, 0x48, 0x4d, 0x44, 0x48, + 0x46, 0x4a, 0x4f, 0x43, 0x4b, 0x47, 0x41, 0x44, + 0x4a, 0x4c, 0x49, 0x45, 0x43, 0x46, 0x4e, 0x4b, + 0x45, 0x40, 0x40, 0x4e, 0x4c, 0x49, 0x47, 0x42, + 0x47, 0x42, 0x4b, 0x41, 0x44, 0x4e, 0x41, 0x47, + 0x49, 0x44, 0x4c, 0x4a, 0x4e, 0x48, 0x42, 0x4d, + 0x40, 0x4f, 0x46, 0x4c, 0x4a, 0x49, 0x4d, 0x40, + 0x4f, 0x43, 0x43, 0x45, 0x45, 0x46, 0x48, 0x4b, + 0x0d, 0x01, 0x02, 0x0f, 0x08, 0x0d, 0x04, 0x08, + 0x06, 0x0a, 0x0f, 0x03, 0x0b, 0x07, 0x01, 0x04, + 0x0a, 0x0c, 0x09, 0x05, 0x03, 0x06, 0x0e, 0x0b, + 0x05, 0x00, 0x00, 0x0e, 0x0c, 0x09, 0x07, 0x02, + 0x07, 0x02, 0x0b, 0x01, 0x04, 0x0e, 0x01, 0x07, + 0x09, 0x04, 0x0c, 0x0a, 0x0e, 0x08, 0x02, 0x0d, + 0x00, 0x0f, 0x06, 0x0c, 0x0a, 0x09, 0x0d, 0x00, + 0x0f, 0x03, 0x03, 0x05, 0x05, 0x06, 0x08, 0x0b, + 0x9d, 0x91, 0x92, 0x9f, 0x98, 0x9d, 0x94, 0x98, + 0x96, 0x9a, 0x9f, 0x93, 0x9b, 0x97, 0x91, 0x94, + 0x9a, 0x9c, 0x99, 0x95, 0x93, 0x96, 0x9e, 0x9b, + 0x95, 0x90, 0x90, 0x9e, 0x9c, 0x99, 0x97, 0x92, + 0x97, 0x92, 0x9b, 0x91, 0x94, 0x9e, 0x91, 0x97, + 0x99, 0x94, 0x9c, 0x9a, 0x9e, 0x98, 0x92, 0x9d, + 0x90, 0x9f, 0x96, 0x9c, 0x9a, 0x99, 0x9d, 0x90, + 0x9f, 0x93, 0x93, 0x95, 0x95, 0x96, 0x98, 0x9b, + 0x8d, 0x81, 0x82, 0x8f, 0x88, 0x8d, 0x84, 0x88, + 0x86, 0x8a, 0x8f, 0x83, 0x8b, 0x87, 0x81, 0x84, + 0x8a, 0x8c, 0x89, 0x85, 0x83, 0x86, 0x8e, 0x8b, + 0x85, 0x80, 0x80, 0x8e, 0x8c, 0x89, 0x87, 0x82, + 0x87, 0x82, 0x8b, 0x81, 0x84, 0x8e, 0x81, 0x87, + 0x89, 0x84, 0x8c, 0x8a, 0x8e, 0x88, 0x82, 0x8d, + 0x80, 0x8f, 0x86, 0x8c, 0x8a, 0x89, 0x8d, 0x80, + 0x8f, 0x83, 0x83, 0x85, 0x85, 0x86, 0x88, 0x8b, + 0x1d, 0x11, 0x12, 0x1f, 0x18, 0x1d, 0x14, 0x18, + 0x16, 0x1a, 0x1f, 0x13, 0x1b, 0x17, 0x11, 0x14, + 0x1a, 0x1c, 0x19, 0x15, 0x13, 0x16, 0x1e, 0x1b, + 0x15, 0x10, 0x10, 0x1e, 0x1c, 0x19, 0x17, 0x12, + 0x17, 0x12, 0x1b, 0x11, 0x14, 0x1e, 0x11, 0x17, + 0x19, 0x14, 0x1c, 0x1a, 0x1e, 0x18, 0x12, 0x1d, + 0x10, 0x1f, 0x16, 0x1c, 0x1a, 0x19, 0x1d, 0x10, + 0x1f, 0x13, 0x13, 0x15, 0x15, 0x16, 0x18, 0x1b, + 0xdd, 0xd1, 0xd2, 0xdf, 0xd8, 0xdd, 0xd4, 0xd8, + 0xd6, 0xda, 0xdf, 0xd3, 0xdb, 0xd7, 0xd1, 0xd4, + 0xda, 0xdc, 0xd9, 0xd5, 0xd3, 0xd6, 0xde, 0xdb, + 0xd5, 0xd0, 0xd0, 0xde, 0xdc, 0xd9, 0xd7, 0xd2, + 0xd7, 0xd2, 0xdb, 0xd1, 0xd4, 0xde, 0xd1, 0xd7, + 0xd9, 0xd4, 0xdc, 0xda, 0xde, 0xd8, 0xd2, 0xdd, + 0xd0, 0xdf, 0xd6, 0xdc, 0xda, 0xd9, 0xdd, 0xd0, + 0xdf, 0xd3, 0xd3, 0xd5, 0xd5, 0xd6, 0xd8, 0xdb, + 0xad, 0xa1, 0xa2, 0xaf, 0xa8, 0xad, 0xa4, 0xa8, + 0xa6, 0xaa, 0xaf, 0xa3, 0xab, 0xa7, 0xa1, 0xa4, + 0xaa, 0xac, 0xa9, 0xa5, 0xa3, 0xa6, 0xae, 0xab, + 0xa5, 0xa0, 0xa0, 0xae, 0xac, 0xa9, 0xa7, 0xa2, + 0xa7, 0xa2, 0xab, 0xa1, 0xa4, 0xae, 0xa1, 0xa7, + 0xa9, 0xa4, 0xac, 0xaa, 0xae, 0xa8, 0xa2, 0xad, + 0xa0, 0xaf, 0xa6, 0xac, 0xaa, 0xa9, 0xad, 0xa0, + 0xaf, 0xa3, 0xa3, 0xa5, 0xa5, 0xa6, 0xa8, 0xab, + 0x3d, 0x31, 0x32, 0x3f, 0x38, 0x3d, 0x34, 0x38, + 0x36, 0x3a, 0x3f, 0x33, 0x3b, 0x37, 0x31, 0x34, + 0x3a, 0x3c, 0x39, 0x35, 0x33, 0x36, 0x3e, 0x3b, + 0x35, 0x30, 0x30, 0x3e, 0x3c, 0x39, 0x37, 0x32, + 0x37, 0x32, 0x3b, 0x31, 0x34, 0x3e, 0x31, 0x37, + 0x39, 0x34, 0x3c, 0x3a, 0x3e, 0x38, 0x32, 0x3d, + 0x30, 0x3f, 0x36, 0x3c, 0x3a, 0x39, 0x3d, 0x30, + 0x3f, 0x33, 0x33, 0x35, 0x35, 0x36, 0x38, 0x3b, + 0xed, 0xe1, 0xe2, 0xef, 0xe8, 0xed, 0xe4, 0xe8, + 0xe6, 0xea, 0xef, 0xe3, 0xeb, 0xe7, 0xe1, 0xe4, + 0xea, 0xec, 0xe9, 0xe5, 0xe3, 0xe6, 0xee, 0xeb, + 0xe5, 0xe0, 0xe0, 0xee, 0xec, 0xe9, 0xe7, 0xe2, + 0xe7, 0xe2, 0xeb, 0xe1, 0xe4, 0xee, 0xe1, 0xe7, + 0xe9, 0xe4, 0xec, 0xea, 0xee, 0xe8, 0xe2, 0xed, + 0xe0, 0xef, 0xe6, 0xec, 0xea, 0xe9, 0xed, 0xe0, + 0xef, 0xe3, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, 0xeb, + 0xcd, 0xc1, 0xc2, 0xcf, 0xc8, 0xcd, 0xc4, 0xc8, + 0xc6, 0xca, 0xcf, 0xc3, 0xcb, 0xc7, 0xc1, 0xc4, + 0xca, 0xcc, 0xc9, 0xc5, 0xc3, 0xc6, 0xce, 0xcb, + 0xc5, 0xc0, 0xc0, 0xce, 0xcc, 0xc9, 0xc7, 0xc2, + 0xc7, 0xc2, 0xcb, 0xc1, 0xc4, 0xce, 0xc1, 0xc7, + 0xc9, 0xc4, 0xcc, 0xca, 0xce, 0xc8, 0xc2, 0xcd, + 0xc0, 0xcf, 0xc6, 0xcc, 0xca, 0xc9, 0xcd, 0xc0, + 0xcf, 0xc3, 0xc3, 0xc5, 0xc5, 0xc6, 0xc8, 0xcb, + 0x3d, 0x31, 0x32, 0x3f, 0x38, 0x3d, 0x34, 0x38, + 0x36, 0x3a, 0x3f, 0x33, 0x3b, 0x37, 0x31, 0x34, + 0x3a, 0x3c, 0x39, 0x35, 0x33, 0x36, 0x3e, 0x3b, + 0x35, 0x30, 0x30, 0x3e, 0x3c, 0x39, 0x37, 0x32, + 0x37, 0x32, 0x3b, 0x31, 0x34, 0x3e, 0x31, 0x37, + 0x39, 0x34, 0x3c, 0x3a, 0x3e, 0x38, 0x32, 0x3d, + 0x30, 0x3f, 0x36, 0x3c, 0x3a, 0x39, 0x3d, 0x30, + 0x3f, 0x33, 0x33, 0x35, 0x35, 0x36, 0x38, 0x3b, + 0x9d, 0x91, 0x92, 0x9f, 0x98, 0x9d, 0x94, 0x98, + 0x96, 0x9a, 0x9f, 0x93, 0x9b, 0x97, 0x91, 0x94, + 0x9a, 0x9c, 0x99, 0x95, 0x93, 0x96, 0x9e, 0x9b, + 0x95, 0x90, 0x90, 0x9e, 0x9c, 0x99, 0x97, 0x92, + 0x97, 0x92, 0x9b, 0x91, 0x94, 0x9e, 0x91, 0x97, + 0x99, 0x94, 0x9c, 0x9a, 0x9e, 0x98, 0x92, 0x9d, + 0x90, 0x9f, 0x96, 0x9c, 0x9a, 0x99, 0x9d, 0x90, + 0x9f, 0x93, 0x93, 0x95, 0x95, 0x96, 0x98, 0x9b, + 0x5d, 0x51, 0x52, 0x5f, 0x58, 0x5d, 0x54, 0x58, + 0x56, 0x5a, 0x5f, 0x53, 0x5b, 0x57, 0x51, 0x54, + 0x5a, 0x5c, 0x59, 0x55, 0x53, 0x56, 0x5e, 0x5b, + 0x55, 0x50, 0x50, 0x5e, 0x5c, 0x59, 0x57, 0x52, + 0x57, 0x52, 0x5b, 0x51, 0x54, 0x5e, 0x51, 0x57, + 0x59, 0x54, 0x5c, 0x5a, 0x5e, 0x58, 0x52, 0x5d, + 0x50, 0x5f, 0x56, 0x5c, 0x5a, 0x59, 0x5d, 0x50, + 0x5f, 0x53, 0x53, 0x55, 0x55, 0x56, 0x58, 0x5b, + 0x7d, 0x71, 0x72, 0x7f, 0x78, 0x7d, 0x74, 0x78, + 0x76, 0x7a, 0x7f, 0x73, 0x7b, 0x77, 0x71, 0x74, + 0x7a, 0x7c, 0x79, 0x75, 0x73, 0x76, 0x7e, 0x7b, + 0x75, 0x70, 0x70, 0x7e, 0x7c, 0x79, 0x77, 0x72, + 0x77, 0x72, 0x7b, 0x71, 0x74, 0x7e, 0x71, 0x77, + 0x79, 0x74, 0x7c, 0x7a, 0x7e, 0x78, 0x72, 0x7d, + 0x70, 0x7f, 0x76, 0x7c, 0x7a, 0x79, 0x7d, 0x70, + 0x7f, 0x73, 0x73, 0x75, 0x75, 0x76, 0x78, 0x7b, + 0xcd, 0xc1, 0xc2, 0xcf, 0xc8, 0xcd, 0xc4, 0xc8, + 0xc6, 0xca, 0xcf, 0xc3, 0xcb, 0xc7, 0xc1, 0xc4, + 0xca, 0xcc, 0xc9, 0xc5, 0xc3, 0xc6, 0xce, 0xcb, + 0xc5, 0xc0, 0xc0, 0xce, 0xcc, 0xc9, 0xc7, 0xc2, + 0xc7, 0xc2, 0xcb, 0xc1, 0xc4, 0xce, 0xc1, 0xc7, + 0xc9, 0xc4, 0xcc, 0xca, 0xce, 0xc8, 0xc2, 0xcd, + 0xc0, 0xcf, 0xc6, 0xcc, 0xca, 0xc9, 0xcd, 0xc0, + 0xcf, 0xc3, 0xc3, 0xc5, 0xc5, 0xc6, 0xc8, 0xcb, + 0x5d, 0x51, 0x52, 0x5f, 0x58, 0x5d, 0x54, 0x58, + 0x56, 0x5a, 0x5f, 0x53, 0x5b, 0x57, 0x51, 0x54, + 0x5a, 0x5c, 0x59, 0x55, 0x53, 0x56, 0x5e, 0x5b, + 0x55, 0x50, 0x50, 0x5e, 0x5c, 0x59, 0x57, 0x52, + 0x57, 0x52, 0x5b, 0x51, 0x54, 0x5e, 0x51, 0x57, + 0x59, 0x54, 0x5c, 0x5a, 0x5e, 0x58, 0x52, 0x5d, + 0x50, 0x5f, 0x56, 0x5c, 0x5a, 0x59, 0x5d, 0x50, + 0x5f, 0x53, 0x53, 0x55, 0x55, 0x56, 0x58, 0x5b, + 0x2d, 0x21, 0x22, 0x2f, 0x28, 0x2d, 0x24, 0x28, + 0x26, 0x2a, 0x2f, 0x23, 0x2b, 0x27, 0x21, 0x24, + 0x2a, 0x2c, 0x29, 0x25, 0x23, 0x26, 0x2e, 0x2b, + 0x25, 0x20, 0x20, 0x2e, 0x2c, 0x29, 0x27, 0x22, + 0x27, 0x22, 0x2b, 0x21, 0x24, 0x2e, 0x21, 0x27, + 0x29, 0x24, 0x2c, 0x2a, 0x2e, 0x28, 0x22, 0x2d, + 0x20, 0x2f, 0x26, 0x2c, 0x2a, 0x29, 0x2d, 0x20, + 0x2f, 0x23, 0x23, 0x25, 0x25, 0x26, 0x28, 0x2b, + 0xad, 0xa1, 0xa2, 0xaf, 0xa8, 0xad, 0xa4, 0xa8, + 0xa6, 0xaa, 0xaf, 0xa3, 0xab, 0xa7, 0xa1, 0xa4, + 0xaa, 0xac, 0xa9, 0xa5, 0xa3, 0xa6, 0xae, 0xab, + 0xa5, 0xa0, 0xa0, 0xae, 0xac, 0xa9, 0xa7, 0xa2, + 0xa7, 0xa2, 0xab, 0xa1, 0xa4, 0xae, 0xa1, 0xa7, + 0xa9, 0xa4, 0xac, 0xaa, 0xae, 0xa8, 0xa2, 0xad, + 0xa0, 0xaf, 0xa6, 0xac, 0xaa, 0xa9, 0xad, 0xa0, + 0xaf, 0xa3, 0xa3, 0xa5, 0xa5, 0xa6, 0xa8, 0xab, + 0xfd, 0xf1, 0xf2, 0xff, 0xf8, 0xfd, 0xf4, 0xf8, + 0xf6, 0xfa, 0xff, 0xf3, 0xfb, 0xf7, 0xf1, 0xf4, + 0xfa, 0xfc, 0xf9, 0xf5, 0xf3, 0xf6, 0xfe, 0xfb, + 0xf5, 0xf0, 0xf0, 0xfe, 0xfc, 0xf9, 0xf7, 0xf2, + 0xf7, 0xf2, 0xfb, 0xf1, 0xf4, 0xfe, 0xf1, 0xf7, + 0xf9, 0xf4, 0xfc, 0xfa, 0xfe, 0xf8, 0xf2, 0xfd, + 0xf0, 0xff, 0xf6, 0xfc, 0xfa, 0xf9, 0xfd, 0xf0, + 0xff, 0xf3, 0xf3, 0xf5, 0xf5, 0xf6, 0xf8, 0xfb, + 0x6d, 0x61, 0x62, 0x6f, 0x68, 0x6d, 0x64, 0x68, + 0x66, 0x6a, 0x6f, 0x63, 0x6b, 0x67, 0x61, 0x64, + 0x6a, 0x6c, 0x69, 0x65, 0x63, 0x66, 0x6e, 0x6b, + 0x65, 0x60, 0x60, 0x6e, 0x6c, 0x69, 0x67, 0x62, + 0x67, 0x62, 0x6b, 0x61, 0x64, 0x6e, 0x61, 0x67, + 0x69, 0x64, 0x6c, 0x6a, 0x6e, 0x68, 0x62, 0x6d, + 0x60, 0x6f, 0x66, 0x6c, 0x6a, 0x69, 0x6d, 0x60, + 0x6f, 0x63, 0x63, 0x65, 0x65, 0x66, 0x68, 0x6b, + 0x8d, 0x81, 0x82, 0x8f, 0x88, 0x8d, 0x84, 0x88, + 0x86, 0x8a, 0x8f, 0x83, 0x8b, 0x87, 0x81, 0x84, + 0x8a, 0x8c, 0x89, 0x85, 0x83, 0x86, 0x8e, 0x8b, + 0x85, 0x80, 0x80, 0x8e, 0x8c, 0x89, 0x87, 0x82, + 0x87, 0x82, 0x8b, 0x81, 0x84, 0x8e, 0x81, 0x87, + 0x89, 0x84, 0x8c, 0x8a, 0x8e, 0x88, 0x82, 0x8d, + 0x80, 0x8f, 0x86, 0x8c, 0x8a, 0x89, 0x8d, 0x80, + 0x8f, 0x83, 0x83, 0x85, 0x85, 0x86, 0x88, 0x8b, + 0x1d, 0x11, 0x12, 0x1f, 0x18, 0x1d, 0x14, 0x18, + 0x16, 0x1a, 0x1f, 0x13, 0x1b, 0x17, 0x11, 0x14, + 0x1a, 0x1c, 0x19, 0x15, 0x13, 0x16, 0x1e, 0x1b, + 0x15, 0x10, 0x10, 0x1e, 0x1c, 0x19, 0x17, 0x12, + 0x17, 0x12, 0x1b, 0x11, 0x14, 0x1e, 0x11, 0x17, + 0x19, 0x14, 0x1c, 0x1a, 0x1e, 0x18, 0x12, 0x1d, + 0x10, 0x1f, 0x16, 0x1c, 0x1a, 0x19, 0x1d, 0x10, + 0x1f, 0x13, 0x13, 0x15, 0x15, 0x16, 0x18, 0x1b, + 0x6d, 0x61, 0x62, 0x6f, 0x68, 0x6d, 0x64, 0x68, + 0x66, 0x6a, 0x6f, 0x63, 0x6b, 0x67, 0x61, 0x64, + 0x6a, 0x6c, 0x69, 0x65, 0x63, 0x66, 0x6e, 0x6b, + 0x65, 0x60, 0x60, 0x6e, 0x6c, 0x69, 0x67, 0x62, + 0x67, 0x62, 0x6b, 0x61, 0x64, 0x6e, 0x61, 0x67, + 0x69, 0x64, 0x6c, 0x6a, 0x6e, 0x68, 0x62, 0x6d, + 0x60, 0x6f, 0x66, 0x6c, 0x6a, 0x69, 0x6d, 0x60, + 0x6f, 0x63, 0x63, 0x65, 0x65, 0x66, 0x68, 0x6b, + 0x1d, 0x11, 0x12, 0x1f, 0x18, 0x1d, 0x14, 0x18, + 0x16, 0x1a, 0x1f, 0x13, 0x1b, 0x17, 0x11, 0x14, + 0x1a, 0x1c, 0x19, 0x15, 0x13, 0x16, 0x1e, 0x1b, + 0x15, 0x10, 0x10, 0x1e, 0x1c, 0x19, 0x17, 0x12, + 0x17, 0x12, 0x1b, 0x11, 0x14, 0x1e, 0x11, 0x17, + 0x19, 0x14, 0x1c, 0x1a, 0x1e, 0x18, 0x12, 0x1d, + 0x10, 0x1f, 0x16, 0x1c, 0x1a, 0x19, 0x1d, 0x10, + 0x1f, 0x13, 0x13, 0x15, 0x15, 0x16, 0x18, 0x1b, + 0x6d, 0x61, 0x62, 0x6f, 0x68, 0x6d, 0x64, 0x68, + 0x66, 0x6a, 0x6f, 0x63, 0x6b, 0x67, 0x61, 0x64, + 0x6a, 0x6c, 0x69, 0x65, 0x63, 0x66, 0x6e, 0x6b, + 0x65, 0x60, 0x60, 0x6e, 0x6c, 0x69, 0x67, 0x62, + 0x67, 0x62, 0x6b, 0x61, 0x64, 0x6e, 0x61, 0x67, + 0x69, 0x64, 0x6c, 0x6a, 0x6e, 0x68, 0x62, 0x6d, + 0x60, 0x6f, 0x66, 0x6c, 0x6a, 0x69, 0x6d, 0x60, + 0x6f, 0x63, 0x63, 0x65, 0x65, 0x66, 0x68, 0x6b, + 0x4d, 0x41, 0x42, 0x4f, 0x48, 0x4d, 0x44, 0x48, + 0x46, 0x4a, 0x4f, 0x43, 0x4b, 0x47, 0x41, 0x44, + 0x4a, 0x4c, 0x49, 0x45, 0x43, 0x46, 0x4e, 0x4b, + 0x45, 0x40, 0x40, 0x4e, 0x4c, 0x49, 0x47, 0x42, + 0x47, 0x42, 0x4b, 0x41, 0x44, 0x4e, 0x41, 0x47, + 0x49, 0x44, 0x4c, 0x4a, 0x4e, 0x48, 0x42, 0x4d, + 0x40, 0x4f, 0x46, 0x4c, 0x4a, 0x49, 0x4d, 0x40, + 0x4f, 0x43, 0x43, 0x45, 0x45, 0x46, 0x48, 0x4b, + 0xbd, 0xb1, 0xb2, 0xbf, 0xb8, 0xbd, 0xb4, 0xb8, + 0xb6, 0xba, 0xbf, 0xb3, 0xbb, 0xb7, 0xb1, 0xb4, + 0xba, 0xbc, 0xb9, 0xb5, 0xb3, 0xb6, 0xbe, 0xbb, + 0xb5, 0xb0, 0xb0, 0xbe, 0xbc, 0xb9, 0xb7, 0xb2, + 0xb7, 0xb2, 0xbb, 0xb1, 0xb4, 0xbe, 0xb1, 0xb7, + 0xb9, 0xb4, 0xbc, 0xba, 0xbe, 0xb8, 0xb2, 0xbd, + 0xb0, 0xbf, 0xb6, 0xbc, 0xba, 0xb9, 0xbd, 0xb0, + 0xbf, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb8, 0xbb, + 0xbd, 0xb1, 0xb2, 0xbf, 0xb8, 0xbd, 0xb4, 0xb8, + 0xb6, 0xba, 0xbf, 0xb3, 0xbb, 0xb7, 0xb1, 0xb4, + 0xba, 0xbc, 0xb9, 0xb5, 0xb3, 0xb6, 0xbe, 0xbb, + 0xb5, 0xb0, 0xb0, 0xbe, 0xbc, 0xb9, 0xb7, 0xb2, + 0xb7, 0xb2, 0xbb, 0xb1, 0xb4, 0xbe, 0xb1, 0xb7, + 0xb9, 0xb4, 0xbc, 0xba, 0xbe, 0xb8, 0xb2, 0xbd, + 0xb0, 0xbf, 0xb6, 0xbc, 0xba, 0xb9, 0xbd, 0xb0, + 0xbf, 0xb3, 0xb3, 0xb5, 0xb5, 0xb6, 0xb8, 0xbb, + 0xdd, 0xd1, 0xd2, 0xdf, 0xd8, 0xdd, 0xd4, 0xd8, + 0xd6, 0xda, 0xdf, 0xd3, 0xdb, 0xd7, 0xd1, 0xd4, + 0xda, 0xdc, 0xd9, 0xd5, 0xd3, 0xd6, 0xde, 0xdb, + 0xd5, 0xd0, 0xd0, 0xde, 0xdc, 0xd9, 0xd7, 0xd2, + 0xd7, 0xd2, 0xdb, 0xd1, 0xd4, 0xde, 0xd1, 0xd7, + 0xd9, 0xd4, 0xdc, 0xda, 0xde, 0xd8, 0xd2, 0xdd, + 0xd0, 0xdf, 0xd6, 0xdc, 0xda, 0xd9, 0xdd, 0xd0, + 0xdf, 0xd3, 0xd3, 0xd5, 0xd5, 0xd6, 0xd8, 0xdb, + 0xdd, 0xd1, 0xd2, 0xdf, 0xd8, 0xdd, 0xd4, 0xd8, + 0xd6, 0xda, 0xdf, 0xd3, 0xdb, 0xd7, 0xd1, 0xd4, + 0xda, 0xdc, 0xd9, 0xd5, 0xd3, 0xd6, 0xde, 0xdb, + 0xd5, 0xd0, 0xd0, 0xde, 0xdc, 0xd9, 0xd7, 0xd2, + 0xd7, 0xd2, 0xdb, 0xd1, 0xd4, 0xde, 0xd1, 0xd7, + 0xd9, 0xd4, 0xdc, 0xda, 0xde, 0xd8, 0xd2, 0xdd, + 0xd0, 0xdf, 0xd6, 0xdc, 0xda, 0xd9, 0xdd, 0xd0, + 0xdf, 0xd3, 0xd3, 0xd5, 0xd5, 0xd6, 0xd8, 0xdb, + 0x8d, 0x81, 0x82, 0x8f, 0x88, 0x8d, 0x84, 0x88, + 0x86, 0x8a, 0x8f, 0x83, 0x8b, 0x87, 0x81, 0x84, + 0x8a, 0x8c, 0x89, 0x85, 0x83, 0x86, 0x8e, 0x8b, + 0x85, 0x80, 0x80, 0x8e, 0x8c, 0x89, 0x87, 0x82, + 0x87, 0x82, 0x8b, 0x81, 0x84, 0x8e, 0x81, 0x87, + 0x89, 0x84, 0x8c, 0x8a, 0x8e, 0x88, 0x82, 0x8d, + 0x80, 0x8f, 0x86, 0x8c, 0x8a, 0x89, 0x8d, 0x80, + 0x8f, 0x83, 0x83, 0x85, 0x85, 0x86, 0x88, 0x8b, + 0xcd, 0xc1, 0xc2, 0xcf, 0xc8, 0xcd, 0xc4, 0xc8, + 0xc6, 0xca, 0xcf, 0xc3, 0xcb, 0xc7, 0xc1, 0xc4, + 0xca, 0xcc, 0xc9, 0xc5, 0xc3, 0xc6, 0xce, 0xcb, + 0xc5, 0xc0, 0xc0, 0xce, 0xcc, 0xc9, 0xc7, 0xc2, + 0xc7, 0xc2, 0xcb, 0xc1, 0xc4, 0xce, 0xc1, 0xc7, + 0xc9, 0xc4, 0xcc, 0xca, 0xce, 0xc8, 0xc2, 0xcd, + 0xc0, 0xcf, 0xc6, 0xcc, 0xca, 0xc9, 0xcd, 0xc0, + 0xcf, 0xc3, 0xc3, 0xc5, 0xc5, 0xc6, 0xc8, 0xcb, + 0x1d, 0x11, 0x12, 0x1f, 0x18, 0x1d, 0x14, 0x18, + 0x16, 0x1a, 0x1f, 0x13, 0x1b, 0x17, 0x11, 0x14, + 0x1a, 0x1c, 0x19, 0x15, 0x13, 0x16, 0x1e, 0x1b, + 0x15, 0x10, 0x10, 0x1e, 0x1c, 0x19, 0x17, 0x12, + 0x17, 0x12, 0x1b, 0x11, 0x14, 0x1e, 0x11, 0x17, + 0x19, 0x14, 0x1c, 0x1a, 0x1e, 0x18, 0x12, 0x1d, + 0x10, 0x1f, 0x16, 0x1c, 0x1a, 0x19, 0x1d, 0x10, + 0x1f, 0x13, 0x13, 0x15, 0x15, 0x16, 0x18, 0x1b, + 0x3d, 0x31, 0x32, 0x3f, 0x38, 0x3d, 0x34, 0x38, + 0x36, 0x3a, 0x3f, 0x33, 0x3b, 0x37, 0x31, 0x34, + 0x3a, 0x3c, 0x39, 0x35, 0x33, 0x36, 0x3e, 0x3b, + 0x35, 0x30, 0x30, 0x3e, 0x3c, 0x39, 0x37, 0x32, + 0x37, 0x32, 0x3b, 0x31, 0x34, 0x3e, 0x31, 0x37, + 0x39, 0x34, 0x3c, 0x3a, 0x3e, 0x38, 0x32, 0x3d, + 0x30, 0x3f, 0x36, 0x3c, 0x3a, 0x39, 0x3d, 0x30, + 0x3f, 0x33, 0x33, 0x35, 0x35, 0x36, 0x38, 0x3b, + 0x4d, 0x41, 0x42, 0x4f, 0x48, 0x4d, 0x44, 0x48, + 0x46, 0x4a, 0x4f, 0x43, 0x4b, 0x47, 0x41, 0x44, + 0x4a, 0x4c, 0x49, 0x45, 0x43, 0x46, 0x4e, 0x4b, + 0x45, 0x40, 0x40, 0x4e, 0x4c, 0x49, 0x47, 0x42, + 0x47, 0x42, 0x4b, 0x41, 0x44, 0x4e, 0x41, 0x47, + 0x49, 0x44, 0x4c, 0x4a, 0x4e, 0x48, 0x42, 0x4d, + 0x40, 0x4f, 0x46, 0x4c, 0x4a, 0x49, 0x4d, 0x40, + 0x4f, 0x43, 0x43, 0x45, 0x45, 0x46, 0x48, 0x4b, + 0x7d, 0x71, 0x72, 0x7f, 0x78, 0x7d, 0x74, 0x78, + 0x76, 0x7a, 0x7f, 0x73, 0x7b, 0x77, 0x71, 0x74, + 0x7a, 0x7c, 0x79, 0x75, 0x73, 0x76, 0x7e, 0x7b, + 0x75, 0x70, 0x70, 0x7e, 0x7c, 0x79, 0x77, 0x72, + 0x77, 0x72, 0x7b, 0x71, 0x74, 0x7e, 0x71, 0x77, + 0x79, 0x74, 0x7c, 0x7a, 0x7e, 0x78, 0x72, 0x7d, + 0x70, 0x7f, 0x76, 0x7c, 0x7a, 0x79, 0x7d, 0x70, + 0x7f, 0x73, 0x73, 0x75, 0x75, 0x76, 0x78, 0x7b, + 0xad, 0xa1, 0xa2, 0xaf, 0xa8, 0xad, 0xa4, 0xa8, + 0xa6, 0xaa, 0xaf, 0xa3, 0xab, 0xa7, 0xa1, 0xa4, + 0xaa, 0xac, 0xa9, 0xa5, 0xa3, 0xa6, 0xae, 0xab, + 0xa5, 0xa0, 0xa0, 0xae, 0xac, 0xa9, 0xa7, 0xa2, + 0xa7, 0xa2, 0xab, 0xa1, 0xa4, 0xae, 0xa1, 0xa7, + 0xa9, 0xa4, 0xac, 0xaa, 0xae, 0xa8, 0xa2, 0xad, + 0xa0, 0xaf, 0xa6, 0xac, 0xaa, 0xa9, 0xad, 0xa0, + 0xaf, 0xa3, 0xa3, 0xa5, 0xa5, 0xa6, 0xa8, 0xab, + 0xed, 0xe1, 0xe2, 0xef, 0xe8, 0xed, 0xe4, 0xe8, + 0xe6, 0xea, 0xef, 0xe3, 0xeb, 0xe7, 0xe1, 0xe4, + 0xea, 0xec, 0xe9, 0xe5, 0xe3, 0xe6, 0xee, 0xeb, + 0xe5, 0xe0, 0xe0, 0xee, 0xec, 0xe9, 0xe7, 0xe2, + 0xe7, 0xe2, 0xeb, 0xe1, 0xe4, 0xee, 0xe1, 0xe7, + 0xe9, 0xe4, 0xec, 0xea, 0xee, 0xe8, 0xe2, 0xed, + 0xe0, 0xef, 0xe6, 0xec, 0xea, 0xe9, 0xed, 0xe0, + 0xef, 0xe3, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, 0xeb, + 0x7d, 0x71, 0x72, 0x7f, 0x78, 0x7d, 0x74, 0x78, + 0x76, 0x7a, 0x7f, 0x73, 0x7b, 0x77, 0x71, 0x74, + 0x7a, 0x7c, 0x79, 0x75, 0x73, 0x76, 0x7e, 0x7b, + 0x75, 0x70, 0x70, 0x7e, 0x7c, 0x79, 0x77, 0x72, + 0x77, 0x72, 0x7b, 0x71, 0x74, 0x7e, 0x71, 0x77, + 0x79, 0x74, 0x7c, 0x7a, 0x7e, 0x78, 0x72, 0x7d, + 0x70, 0x7f, 0x76, 0x7c, 0x7a, 0x79, 0x7d, 0x70, + 0x7f, 0x73, 0x73, 0x75, 0x75, 0x76, 0x78, 0x7b, + 0xad, 0xa1, 0xa2, 0xaf, 0xa8, 0xad, 0xa4, 0xa8, + 0xa6, 0xaa, 0xaf, 0xa3, 0xab, 0xa7, 0xa1, 0xa4, + 0xaa, 0xac, 0xa9, 0xa5, 0xa3, 0xa6, 0xae, 0xab, + 0xa5, 0xa0, 0xa0, 0xae, 0xac, 0xa9, 0xa7, 0xa2, + 0xa7, 0xa2, 0xab, 0xa1, 0xa4, 0xae, 0xa1, 0xa7, + 0xa9, 0xa4, 0xac, 0xaa, 0xae, 0xa8, 0xa2, 0xad, + 0xa0, 0xaf, 0xa6, 0xac, 0xaa, 0xa9, 0xad, 0xa0, + 0xaf, 0xa3, 0xa3, 0xa5, 0xa5, 0xa6, 0xa8, 0xab, + 0x9d, 0x91, 0x92, 0x9f, 0x98, 0x9d, 0x94, 0x98, + 0x96, 0x9a, 0x9f, 0x93, 0x9b, 0x97, 0x91, 0x94, + 0x9a, 0x9c, 0x99, 0x95, 0x93, 0x96, 0x9e, 0x9b, + 0x95, 0x90, 0x90, 0x9e, 0x9c, 0x99, 0x97, 0x92, + 0x97, 0x92, 0x9b, 0x91, 0x94, 0x9e, 0x91, 0x97, + 0x99, 0x94, 0x9c, 0x9a, 0x9e, 0x98, 0x92, 0x9d, + 0x90, 0x9f, 0x96, 0x9c, 0x9a, 0x99, 0x9d, 0x90, + 0x9f, 0x93, 0x93, 0x95, 0x95, 0x96, 0x98, 0x9b, + 0xfd, 0xf1, 0xf2, 0xff, 0xf8, 0xfd, 0xf4, 0xf8, + 0xf6, 0xfa, 0xff, 0xf3, 0xfb, 0xf7, 0xf1, 0xf4, + 0xfa, 0xfc, 0xf9, 0xf5, 0xf3, 0xf6, 0xfe, 0xfb, + 0xf5, 0xf0, 0xf0, 0xfe, 0xfc, 0xf9, 0xf7, 0xf2, + 0xf7, 0xf2, 0xfb, 0xf1, 0xf4, 0xfe, 0xf1, 0xf7, + 0xf9, 0xf4, 0xfc, 0xfa, 0xfe, 0xf8, 0xf2, 0xfd, + 0xf0, 0xff, 0xf6, 0xfc, 0xfa, 0xf9, 0xfd, 0xf0, + 0xff, 0xf3, 0xf3, 0xf5, 0xf5, 0xf6, 0xf8, 0xfb, + 0x5d, 0x51, 0x52, 0x5f, 0x58, 0x5d, 0x54, 0x58, + 0x56, 0x5a, 0x5f, 0x53, 0x5b, 0x57, 0x51, 0x54, + 0x5a, 0x5c, 0x59, 0x55, 0x53, 0x56, 0x5e, 0x5b, + 0x55, 0x50, 0x50, 0x5e, 0x5c, 0x59, 0x57, 0x52, + 0x57, 0x52, 0x5b, 0x51, 0x54, 0x5e, 0x51, 0x57, + 0x59, 0x54, 0x5c, 0x5a, 0x5e, 0x58, 0x52, 0x5d, + 0x50, 0x5f, 0x56, 0x5c, 0x5a, 0x59, 0x5d, 0x50, + 0x5f, 0x53, 0x53, 0x55, 0x55, 0x56, 0x58, 0x5b, + 0x6d, 0x61, 0x62, 0x6f, 0x68, 0x6d, 0x64, 0x68, + 0x66, 0x6a, 0x6f, 0x63, 0x6b, 0x67, 0x61, 0x64, + 0x6a, 0x6c, 0x69, 0x65, 0x63, 0x66, 0x6e, 0x6b, + 0x65, 0x60, 0x60, 0x6e, 0x6c, 0x69, 0x67, 0x62, + 0x67, 0x62, 0x6b, 0x61, 0x64, 0x6e, 0x61, 0x67, + 0x69, 0x64, 0x6c, 0x6a, 0x6e, 0x68, 0x62, 0x6d, + 0x60, 0x6f, 0x66, 0x6c, 0x6a, 0x69, 0x6d, 0x60, + 0x6f, 0x63, 0x63, 0x65, 0x65, 0x66, 0x68, 0x6b, + 0x0d, 0x01, 0x02, 0x0f, 0x08, 0x0d, 0x04, 0x08, + 0x06, 0x0a, 0x0f, 0x03, 0x0b, 0x07, 0x01, 0x04, + 0x0a, 0x0c, 0x09, 0x05, 0x03, 0x06, 0x0e, 0x0b, + 0x05, 0x00, 0x00, 0x0e, 0x0c, 0x09, 0x07, 0x02, + 0x07, 0x02, 0x0b, 0x01, 0x04, 0x0e, 0x01, 0x07, + 0x09, 0x04, 0x0c, 0x0a, 0x0e, 0x08, 0x02, 0x0d, + 0x00, 0x0f, 0x06, 0x0c, 0x0a, 0x09, 0x0d, 0x00, + 0x0f, 0x03, 0x03, 0x05, 0x05, 0x06, 0x08, 0x0b, + 0x8d, 0x81, 0x82, 0x8f, 0x88, 0x8d, 0x84, 0x88, + 0x86, 0x8a, 0x8f, 0x83, 0x8b, 0x87, 0x81, 0x84, + 0x8a, 0x8c, 0x89, 0x85, 0x83, 0x86, 0x8e, 0x8b, + 0x85, 0x80, 0x80, 0x8e, 0x8c, 0x89, 0x87, 0x82, + 0x87, 0x82, 0x8b, 0x81, 0x84, 0x8e, 0x81, 0x87, + 0x89, 0x84, 0x8c, 0x8a, 0x8e, 0x88, 0x82, 0x8d, + 0x80, 0x8f, 0x86, 0x8c, 0x8a, 0x89, 0x8d, 0x80, + 0x8f, 0x83, 0x83, 0x85, 0x85, 0x86, 0x88, 0x8b, + 0xfd, 0xf1, 0xf2, 0xff, 0xf8, 0xfd, 0xf4, 0xf8, + 0xf6, 0xfa, 0xff, 0xf3, 0xfb, 0xf7, 0xf1, 0xf4, + 0xfa, 0xfc, 0xf9, 0xf5, 0xf3, 0xf6, 0xfe, 0xfb, + 0xf5, 0xf0, 0xf0, 0xfe, 0xfc, 0xf9, 0xf7, 0xf2, + 0xf7, 0xf2, 0xfb, 0xf1, 0xf4, 0xfe, 0xf1, 0xf7, + 0xf9, 0xf4, 0xfc, 0xfa, 0xfe, 0xf8, 0xf2, 0xfd, + 0xf0, 0xff, 0xf6, 0xfc, 0xfa, 0xf9, 0xfd, 0xf0, + 0xff, 0xf3, 0xf3, 0xf5, 0xf5, 0xf6, 0xf8, 0xfb, + 0x0d, 0x01, 0x02, 0x0f, 0x08, 0x0d, 0x04, 0x08, + 0x06, 0x0a, 0x0f, 0x03, 0x0b, 0x07, 0x01, 0x04, + 0x0a, 0x0c, 0x09, 0x05, 0x03, 0x06, 0x0e, 0x0b, + 0x05, 0x00, 0x00, 0x0e, 0x0c, 0x09, 0x07, 0x02, + 0x07, 0x02, 0x0b, 0x01, 0x04, 0x0e, 0x01, 0x07, + 0x09, 0x04, 0x0c, 0x0a, 0x0e, 0x08, 0x02, 0x0d, + 0x00, 0x0f, 0x06, 0x0c, 0x0a, 0x09, 0x0d, 0x00, + 0x0f, 0x03, 0x03, 0x05, 0x05, 0x06, 0x08, 0x0b, + 0xed, 0xe1, 0xe2, 0xef, 0xe8, 0xed, 0xe4, 0xe8, + 0xe6, 0xea, 0xef, 0xe3, 0xeb, 0xe7, 0xe1, 0xe4, + 0xea, 0xec, 0xe9, 0xe5, 0xe3, 0xe6, 0xee, 0xeb, + 0xe5, 0xe0, 0xe0, 0xee, 0xec, 0xe9, 0xe7, 0xe2, + 0xe7, 0xe2, 0xeb, 0xe1, 0xe4, 0xee, 0xe1, 0xe7, + 0xe9, 0xe4, 0xec, 0xea, 0xee, 0xe8, 0xe2, 0xed, + 0xe0, 0xef, 0xe6, 0xec, 0xea, 0xe9, 0xed, 0xe0, + 0xef, 0xe3, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, 0xeb, + 0x5d, 0x51, 0x52, 0x5f, 0x58, 0x5d, 0x54, 0x58, + 0x56, 0x5a, 0x5f, 0x53, 0x5b, 0x57, 0x51, 0x54, + 0x5a, 0x5c, 0x59, 0x55, 0x53, 0x56, 0x5e, 0x5b, + 0x55, 0x50, 0x50, 0x5e, 0x5c, 0x59, 0x57, 0x52, + 0x57, 0x52, 0x5b, 0x51, 0x54, 0x5e, 0x51, 0x57, + 0x59, 0x54, 0x5c, 0x5a, 0x5e, 0x58, 0x52, 0x5d, + 0x50, 0x5f, 0x56, 0x5c, 0x5a, 0x59, 0x5d, 0x50, + 0x5f, 0x53, 0x53, 0x55, 0x55, 0x56, 0x58, 0x5b, + 0x2d, 0x21, 0x22, 0x2f, 0x28, 0x2d, 0x24, 0x28, + 0x26, 0x2a, 0x2f, 0x23, 0x2b, 0x27, 0x21, 0x24, + 0x2a, 0x2c, 0x29, 0x25, 0x23, 0x26, 0x2e, 0x2b, + 0x25, 0x20, 0x20, 0x2e, 0x2c, 0x29, 0x27, 0x22, + 0x27, 0x22, 0x2b, 0x21, 0x24, 0x2e, 0x21, 0x27, + 0x29, 0x24, 0x2c, 0x2a, 0x2e, 0x28, 0x22, 0x2d, + 0x20, 0x2f, 0x26, 0x2c, 0x2a, 0x29, 0x2d, 0x20, + 0x2f, 0x23, 0x23, 0x25, 0x25, 0x26, 0x28, 0x2b, + 0x9d, 0x91, 0x92, 0x9f, 0x98, 0x9d, 0x94, 0x98, + 0x96, 0x9a, 0x9f, 0x93, 0x9b, 0x97, 0x91, 0x94, + 0x9a, 0x9c, 0x99, 0x95, 0x93, 0x96, 0x9e, 0x9b, + 0x95, 0x90, 0x90, 0x9e, 0x9c, 0x99, 0x97, 0x92, + 0x97, 0x92, 0x9b, 0x91, 0x94, 0x9e, 0x91, 0x97, + 0x99, 0x94, 0x9c, 0x9a, 0x9e, 0x98, 0x92, 0x9d, + 0x90, 0x9f, 0x96, 0x9c, 0x9a, 0x99, 0x9d, 0x90, + 0x9f, 0x93, 0x93, 0x95, 0x95, 0x96, 0x98, 0x9b, + 0x3d, 0x31, 0x32, 0x3f, 0x38, 0x3d, 0x34, 0x38, + 0x36, 0x3a, 0x3f, 0x33, 0x3b, 0x37, 0x31, 0x34, + 0x3a, 0x3c, 0x39, 0x35, 0x33, 0x36, 0x3e, 0x3b, + 0x35, 0x30, 0x30, 0x3e, 0x3c, 0x39, 0x37, 0x32, + 0x37, 0x32, 0x3b, 0x31, 0x34, 0x3e, 0x31, 0x37, + 0x39, 0x34, 0x3c, 0x3a, 0x3e, 0x38, 0x32, 0x3d, + 0x30, 0x3f, 0x36, 0x3c, 0x3a, 0x39, 0x3d, 0x30, + 0x3f, 0x33, 0x33, 0x35, 0x35, 0x36, 0x38, 0x3b, + 0x2d, 0x21, 0x22, 0x2f, 0x28, 0x2d, 0x24, 0x28, + 0x26, 0x2a, 0x2f, 0x23, 0x2b, 0x27, 0x21, 0x24, + 0x2a, 0x2c, 0x29, 0x25, 0x23, 0x26, 0x2e, 0x2b, + 0x25, 0x20, 0x20, 0x2e, 0x2c, 0x29, 0x27, 0x22, + 0x27, 0x22, 0x2b, 0x21, 0x24, 0x2e, 0x21, 0x27, + 0x29, 0x24, 0x2c, 0x2a, 0x2e, 0x28, 0x22, 0x2d, + 0x20, 0x2f, 0x26, 0x2c, 0x2a, 0x29, 0x2d, 0x20, + 0x2f, 0x23, 0x23, 0x25, 0x25, 0x26, 0x28, 0x2b, + 0xcd, 0xc1, 0xc2, 0xcf, 0xc8, 0xcd, 0xc4, 0xc8, + 0xc6, 0xca, 0xcf, 0xc3, 0xcb, 0xc7, 0xc1, 0xc4, + 0xca, 0xcc, 0xc9, 0xc5, 0xc3, 0xc6, 0xce, 0xcb, + 0xc5, 0xc0, 0xc0, 0xce, 0xcc, 0xc9, 0xc7, 0xc2, + 0xc7, 0xc2, 0xcb, 0xc1, 0xc4, 0xce, 0xc1, 0xc7, + 0xc9, 0xc4, 0xcc, 0xca, 0xce, 0xc8, 0xc2, 0xcd, + 0xc0, 0xcf, 0xc6, 0xcc, 0xca, 0xc9, 0xcd, 0xc0, + 0xcf, 0xc3, 0xc3, 0xc5, 0xc5, 0xc6, 0xc8, 0xcb, + }, +}; + +static const uint8_t key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static const uint8_t key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static const uint8_t comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +static const uint8_t pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return 0; + if (ch >= 'a') + return (ch - 'a' + 38); + if (ch > 'Z') + return 0; + if (ch >= 'A') + return (ch - 'A' + 12); + if (ch > '9') + return 0; + if (ch >= '.') + return (ch - '.'); + return 0; +} + + + +struct des_ctx { + uint32_t saltbits; /* referenced 5 times */ + uint8_t un_pbox[32]; /* 2 times */ + uint8_t inv_comp_perm[56]; /* 3 times */ + uint8_t inv_key_perm[64]; /* 3 times */ + uint32_t en_keysl[16], en_keysr[16]; /* 2 times each */ + uint32_t fp_maskl[8][256], fp_maskr[8][256]; /* 9 times each */ + uint32_t key_perm_maskl[8][128], key_perm_maskr[8][128]; /* 9 times */ + uint32_t comp_maskl[8][128], comp_maskr[8][128]; /* 9 times each */ + uint32_t psbox[4][256]; /* 5 times */ +}; +#define D (*ctx) +#define saltbits (D.saltbits ) +#define old_salt (D.old_salt ) +#define old_rawkey0 (D.old_rawkey0 ) +#define old_rawkey1 (D.old_rawkey1 ) +#define un_pbox (D.un_pbox ) +#define inv_comp_perm (D.inv_comp_perm ) +#define inv_key_perm (D.inv_key_perm ) +#define en_keysl (D.en_keysl ) +#define en_keysr (D.en_keysr ) +#define de_keysl (D.de_keysl ) +#define de_keysr (D.de_keysr ) +#define ip_maskl (D.ip_maskl ) +#define ip_maskr (D.ip_maskr ) +#define fp_maskl (D.fp_maskl ) +#define fp_maskr (D.fp_maskr ) +#define key_perm_maskl (D.key_perm_maskl ) +#define key_perm_maskr (D.key_perm_maskr ) +#define comp_maskl (D.comp_maskl ) +#define comp_maskr (D.comp_maskr ) +#define psbox (D.psbox ) + +static struct des_ctx *des_init(struct des_ctx *ctx) +{ + int i, j, b, k, inbit, obit; + uint32_t p; + + saltbits = 0; + + /* Initialise the inverted key permutation. */ + for (i = 0; i < 64; i++) { + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = (uint8_t)i; + inv_comp_perm[i] = 255; + } + + /* Invert the key compression permutation. */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = (uint8_t)i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + uint32_t il, ir; + uint32_t fl, fr; + for (i = 0; i < 256; i++) { + fl = 0; + fr = 0; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & (0x80>>j)) { + obit = final_perm[inbit]; + if (obit < 32) + fl |= 0x80000000UL >> obit; + else + fr |= 0x80000000UL >> obit-32; + } + } + fp_maskl[k][i] = fl; + fp_maskr[k][i] = fr; + } + for (i = 0; i < 128; i++) { + il = 0; + ir = 0; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & (0x80 >> j + 1)) { + obit = inv_key_perm[inbit]; + if (obit == 255) + continue; + if (obit < 28) + il |= 0x8000000UL >> obit; + else + ir |= 0x8000000UL >> obit-28; + } + } + key_perm_maskl[k][i] = il; + key_perm_maskr[k][i] = ir; + il = 0; + ir = 0; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & (0x80 >> j + 1)) { + obit = inv_comp_perm[inbit]; + if (obit == 255) + continue; + if (obit < 24) + il |= 0x800000UL >> obit; + else + ir |= 0x800000UL >> obit-24; + } + } + comp_maskl[k][i] = il; + comp_maskr[k][i] = ir; + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = (uint8_t)i; + + for (b = 0; b < 4; b++) { + for (i = 0; i < 256; i++) { + p = 0; + for (j = 0; j < 8; j++) { + if (i & (0x80 >> j)) + p |= 0x80000000>>un_pbox[8 * b + j]; + } + psbox[b][i] = p; + } + } + + return ctx; +} + + +static void setup_salt(struct des_ctx *ctx, uint32_t salt) +{ + uint32_t obit, saltbit; + int i; + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + +static void des_setkey(struct des_ctx *ctx, const unsigned char *key) +{ + uint32_t k0, k1, rawkey0, rawkey1; + int shifts, round; + + rawkey0 = key[0]<<24 | key[1]<<16 | key[2]<<8 | key[3]; + rawkey1 = key[4]<<24 | key[5]<<16 | key[6]<<8 | key[7]; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + uint32_t t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } +} + + +static void do_des(struct des_ctx *ctx, uint32_t *l_out, uint32_t *r_out, int count) +{ + uint32_t l, r, *kl, *kr; + uint32_t f = f; /* silence gcc */ + uint32_t r48l, r48r; + int round; + + /* Do initial permutation (IP). */ + l = r = 0; + + do { + /* Do each round. */ + kl = en_keysl; + kr = en_keysr; + round = 16; + do { + /* Expand R to 48 bits (simulate the E-box). */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* Now that we've permuted things, complete f(). */ + f ^= l; + l = r; + r = f; + } while (--round); + r = l; + l = f; + } while (--count); + + /* Do final permutation (inverse of IP). */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; +} + +#define DES_OUT_BUFSIZE 21 + +static void +to64_msb_first(char *s, unsigned v) +{ + *s++ = i64c(v >> 18); /* bits 23..18 */ + *s++ = i64c(v >> 12); /* bits 17..12 */ + *s++ = i64c(v >> 6); /* bits 11..6 */ + *s = i64c(v); /* bits 5..0 */ +} + +static char * +des_crypt(struct des_ctx *ctx, char output[DES_OUT_BUFSIZE], + const unsigned char *key, const unsigned char *setting) +{ + uint32_t salt, r0, r1, keybuf[2]; + uint8_t *q; + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (uint8_t *)keybuf; + while (q - (uint8_t *)keybuf != 8) { + *q = *key << 1; + if (*q) + key++; + q++; + } + des_setkey(ctx, (char *)keybuf); + + /* + * setting - 2 bytes of salt + * key - up to 8 characters + */ + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + setup_salt(ctx, salt); + /* Do it. */ + do_des(ctx, /*0, 0,*/ &r0, &r1, 25 /* count */); + + /* Now encode the result. */ + /* Each call takes low-order 24 bits and stores 4 chars */ + /* bits 31..8 of r0 */ + to64_msb_first(output + 2, (r0 >> 8)); + /* bits 7..0 of r0 and 31..16 of r1 */ + to64_msb_first(output + 6, (r0 << 16) | (r1 >> 16)); + /* bits 15..0 of r1 and two zero bits (plus extra zero byte) */ + to64_msb_first(output + 10, (r1 << 8)); + /* extra zero byte is encoded as '.', fixing it */ + output[13] = '\0'; + + return output; +} + +#undef C +#undef init_perm +#undef final_perm +#undef m_sbox +#undef D +#undef const_ctx +#undef saltbits +#undef old_salt +#undef old_rawkey0 +#undef old_rawkey1 +#undef un_pbox +#undef inv_comp_perm +#undef inv_key_perm +#undef en_keysl +#undef en_keysr +#undef de_keysl +#undef de_keysr +#undef ip_maskl +#undef ip_maskr +#undef fp_maskl +#undef fp_maskr +#undef key_perm_maskl +#undef key_perm_maskr +#undef comp_maskl +#undef comp_maskr +#undef psbox + +struct crypt_data; + +char *__crypt_r(const char *clear, const char *salt, struct crypt_data *data) +{ + struct des_ctx des_ctx = { 0 }; + +#if 0 + /* MD5 or SHA? */ + if (salt[0] == '$' && salt[1] && salt[2] == '$') { + if (salt[1] == '1') + return md5_crypt((char *)data, clear, salt); + } +#endif + + des_init(&des_ctx); + return des_crypt(&des_ctx, (char *)data, clear, salt); +} + +weak_alias(__crypt_r, crypt_r); + +char *crypt(const char *clear, const char *salt) +{ + static char buf[128]; + return __crypt_r(clear, salt, (void *)buf); +} diff --git a/src/misc/cuserid.c b/src/misc/cuserid.c new file mode 100644 index 00000000..8ad0b9f3 --- /dev/null +++ b/src/misc/cuserid.c @@ -0,0 +1,13 @@ +#include <pwd.h> +#include <stdio.h> +#include <unistd.h> + +char *cuserid(char *buf) +{ + struct passwd pw, *ppw; + long pwb[256]; + if (getpwuid_r(geteuid(), &pw, (void *)pwb, sizeof pwb, &ppw)) + return 0; + snprintf(buf, L_cuserid, "%s", pw.pw_name); + return buf; +} diff --git a/src/misc/dirname.c b/src/misc/dirname.c new file mode 100644 index 00000000..8f70dbb1 --- /dev/null +++ b/src/misc/dirname.c @@ -0,0 +1,15 @@ +#include <string.h> +#include <libgen.h> + +char *dirname(char *s) +{ + size_t i; + if (!s || !*s || !strchr(s, '/')) return "."; + i = strlen(s)-1; + for (; i&&s[i]=='/'; i--); + for (; i&&s[i-1]!='/'; i--); + for (; i&&s[i-1]=='/'; i--); + if (!i && *s=='/') i++; + s[i] = 0; + return s; +} diff --git a/src/misc/ffs.c b/src/misc/ffs.c new file mode 100644 index 00000000..2f7cb321 --- /dev/null +++ b/src/misc/ffs.c @@ -0,0 +1,9 @@ +#include <strings.h> + +int ffs(int i) +{ + unsigned int j = i; + for (i=1; j && !(j&1); j>>=1, i++); + if (j) return i; + return 0; +} diff --git a/src/misc/ftw.c b/src/misc/ftw.c new file mode 100644 index 00000000..de01e39c --- /dev/null +++ b/src/misc/ftw.c @@ -0,0 +1,9 @@ +#include <ftw.h> +#include "libc.h" + +int ftw(const char *path, int (*fn)(const char *, const struct stat *, int), int fd_limit) +{ + return nftw(path, (void *)fn, fd_limit, FTW_PHYS); +} + +LFS64(ftw); diff --git a/src/misc/getdomainname.c b/src/misc/getdomainname.c new file mode 100644 index 00000000..7eb113d7 --- /dev/null +++ b/src/misc/getdomainname.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include <sys/utsname.h> +#include <string.h> + +int getdomainname(char *name, size_t len) +{ + *name = 0; + return 0; +} diff --git a/src/misc/getgrouplist.c b/src/misc/getgrouplist.c new file mode 100644 index 00000000..88f273d7 --- /dev/null +++ b/src/misc/getgrouplist.c @@ -0,0 +1,11 @@ +#include <grp.h> + +/* FIXME */ + +int getgrouplist(const char *user, gid_t gid, gid_t *groups, int *ngroups) +{ + if (*ngroups<1) return -1; + *groups = gid; + *ngroups = 1; + return 0; +} diff --git a/src/misc/getopt.c b/src/misc/getopt.c new file mode 100644 index 00000000..abf0e847 --- /dev/null +++ b/src/misc/getopt.c @@ -0,0 +1,63 @@ +#include <unistd.h> +#include <wchar.h> +#include <string.h> +#include <limits.h> +#include <stdlib.h> + +char *optarg; +int optind=1, opterr=1, optopt; +static int optpos; + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i; + wchar_t c, d; + int k, l; + char *optchar; + + if (optind >= argc || !argv[optind] || argv[optind][0] != '-' || !argv[optind][1]) + return -1; + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { + k = 1; + c = 0xfffd; /* replacement char */ + } + optchar = argv[optind]+optpos; + optopt = c; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + for (i=0; (l = mbtowc(&d, optstring+i, MB_LEN_MAX)) && d!=c; i+=l>0?l:1); + + if (d != c) { + if (optstring[0] != ':' && opterr) { + write(2, argv[0], strlen(argv[0])); + write(2, ": illegal option: ", 18); + write(2, optchar, k); + write(2, "\n", 1); + } + return '?'; + } + if (optstring[i+1] == ':') { + if (optind >= argc) { + if (optstring[0] == ':') return ':'; + if (opterr) { + write(2, argv[0], strlen(argv[0])); + write(2, ": option requires an argument: ", 31); + write(2, optchar, k); + write(2, "\n", 1); + } + return '?'; + } + optarg = argv[optind++] + optpos; + optpos = 0; + } + return c; +} diff --git a/src/misc/getpriority.c b/src/misc/getpriority.c new file mode 100644 index 00000000..2fb26b2b --- /dev/null +++ b/src/misc/getpriority.c @@ -0,0 +1,9 @@ +#include <sys/resource.h> +#include "syscall.h" + +int getpriority(int which, id_t who) +{ + int ret = syscall2(__NR_getpriority, which, who); + if (ret < 0) return ret; + return 20-ret; +} diff --git a/src/misc/getrlimit.c b/src/misc/getrlimit.c new file mode 100644 index 00000000..db25943b --- /dev/null +++ b/src/misc/getrlimit.c @@ -0,0 +1,15 @@ +#include <sys/resource.h> +#include "syscall.h" +#include "libc.h" + +int getrlimit(int resource, struct rlimit *rlim) +{ + long k_rlim[2]; + if (syscall2(__NR_ugetrlimit, resource, (long)k_rlim) < 0) + return -1; + rlim->rlim_cur = k_rlim[0]; + rlim->rlim_max = k_rlim[1]; + return 0; +} + +LFS64(getrlimit); diff --git a/src/misc/getrusage.c b/src/misc/getrusage.c new file mode 100644 index 00000000..1b8850f9 --- /dev/null +++ b/src/misc/getrusage.c @@ -0,0 +1,20 @@ +#include <sys/resource.h> +#include <string.h> +#include "syscall.h" + +/* this is a huge hack to make up for the kernel's stupid 32bit time_t + * without having to recopy the whole rusage structure ourselves.. */ + +int getrusage(int who, struct rusage *ru) +{ + struct { long tv_sec, tv_usec; } ktv[2]; + char *fakeaddr = ((char *)ru + sizeof(struct timeval [2]) - sizeof ktv); + if (syscall2(__NR_getrusage, who, (long)fakeaddr) < 0) + return -1; + memcpy(ktv, fakeaddr, sizeof ktv); + ru->ru_utime.tv_sec = ktv[0].tv_sec; + ru->ru_utime.tv_usec = ktv[0].tv_usec; + ru->ru_stime.tv_sec = ktv[1].tv_sec; + ru->ru_stime.tv_usec = ktv[1].tv_usec; + return 0; +} diff --git a/src/misc/getsubopt.c b/src/misc/getsubopt.c new file mode 100644 index 00000000..dac9bf9e --- /dev/null +++ b/src/misc/getsubopt.c @@ -0,0 +1,23 @@ +#include <stdlib.h> +#include <string.h> + +int getsubopt(char **opt, char *const *keys, char **val) +{ + char *s = *opt; + int i; + + *val = NULL; + *opt = strchr(s, ','); + if (*opt) *(*opt)++ = 0; + else *opt = s + strlen(s); + + for (i=0; keys[i]; i++) { + size_t l = strlen(keys[i]); + if (strncmp(keys[i], s, l)) continue; + if (s[l] == '=') + *val = s + l; + else if (s[l]) continue; + return i; + } + return -1; +} diff --git a/src/misc/ioctl.c b/src/misc/ioctl.c new file mode 100644 index 00000000..808b7c9c --- /dev/null +++ b/src/misc/ioctl.c @@ -0,0 +1,13 @@ +#include <sys/ioctl.h> +#include <stdarg.h> +#include "syscall.h" + +int ioctl(int fd, int req, ...) +{ + void *arg; + va_list ap; + va_start(ap, req); + arg = va_arg(ap, void *); + va_end(ap); + return syscall3(__NR_ioctl, fd, req, (long)arg); +} diff --git a/src/misc/lockf.c b/src/misc/lockf.c new file mode 100644 index 00000000..d8f82efd --- /dev/null +++ b/src/misc/lockf.c @@ -0,0 +1,33 @@ +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include "libc.h" + +int lockf(int fd, int op, off_t size) +{ + struct flock l = { + .l_type = F_WRLCK, + .l_whence = SEEK_CUR, + .l_len = size, + }; + switch (op) { + case F_TEST: + l.l_type = F_RDLCK; + if (fcntl(fd, F_GETLK, &l) < 0) + return -1; + if (l.l_type == F_UNLCK || l.l_pid == getpid()) + return 0; + errno = EACCES; + return -1; + case F_ULOCK: + l.l_type = F_UNLCK; + case F_TLOCK: + return fcntl(fd, F_SETLK, &l); + case F_LOCK: + return fcntl(fd, F_SETLKW, &l); + } + errno = EINVAL; + return -1; +} + +LFS64(lockf); diff --git a/src/misc/nftw.c b/src/misc/nftw.c new file mode 100644 index 00000000..1b94ac15 --- /dev/null +++ b/src/misc/nftw.c @@ -0,0 +1,121 @@ +#include <ftw.h> +#include <dirent.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#include "libc.h" + +struct history +{ + struct history *chain; + dev_t dev; + ino_t ino; + int level; + int base; +}; + +#undef dirfd +#define dirfd(d) (*(int *)d) + +static int do_nftw(char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags, struct history *h) +{ + size_t l = strlen(path), j = l && path[l-1]=='/' ? l-1 : l; + struct stat st; + struct history new; + int type; + int r; + struct FTW lev; + char *name; + + if ((flags & FTW_PHYS) ? lstat(path, &st) : stat(path, &st) < 0) { + if (!(flags & FTW_PHYS) && errno==ENOENT && !lstat(path, &st)) + type = FTW_SLN; + else if (errno != EACCES) return -1; + else type = FTW_NS; + } else if (S_ISDIR(st.st_mode)) { + if (access(path, R_OK) < 0) type = FTW_DNR; + else if (flags & FTW_DEPTH) type = FTW_DP; + else type = FTW_D; + } else if (S_ISLNK(st.st_mode)) { + if (flags & FTW_PHYS) type = FTW_SL; + else type = FTW_SLN; + } else { + type = FTW_F; + } + + if ((flags & FTW_MOUNT) && h + && (st.st_dev != h->dev || st.st_ino != h->ino)) + return 0; + + new.chain = h; + new.dev = st.st_dev; + new.ino = st.st_ino; + new.level = h ? h->level+1 : 0; + new.base = l+1; + + lev.level = new.level; + lev.base = h ? h->base : (name=strrchr(path, '/')) ? name-path : 0; + + if (!(flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) + return r; + + for (; h; h = h->chain) + if (h->dev == st.st_dev && h->ino == st.st_ino) + return 0; + + if ((type == FTW_D || type == FTW_DP) && fd_limit) { + DIR *d = opendir(path); + if (d) { + struct dirent *de; + while ((de = readdir(d))) { + if (de->d_name[0] == '.' + && (!de->d_name[1] + || (de->d_name[1]=='.' + && !de->d_name[2]))) continue; + if (strlen(de->d_name) >= PATH_MAX-l) { + errno = ENAMETOOLONG; + closedir(d); + return -1; + } + path[j]='/'; + strcpy(path+j+1, de->d_name); + if ((r=do_nftw(path, fn, fd_limit-1, flags, &new))) { + closedir(d); + return r; + } + } + closedir(d); + } else if (errno != EACCES) { + return -1; + } + } + + path[l] = 0; + if ((flags & FTW_DEPTH) && (r=fn(path, &st, type, &lev))) + return r; + + return 0; +} + +int nftw(const char *path, int (*fn)(const char *, const struct stat *, int, struct FTW *), int fd_limit, int flags) +{ + size_t l; + char pathbuf[PATH_MAX+1]; + + if (fd_limit <= 0) return 0; + + l = strlen(path); + if (l > PATH_MAX) { + errno = ENAMETOOLONG; + return -1; + } + memcpy(pathbuf, path, l+1); + + return do_nftw(pathbuf, fn, fd_limit, flags, NULL); +} + +LFS64(nftw); diff --git a/src/misc/openpty.c b/src/misc/openpty.c new file mode 100644 index 00000000..0b4eb221 --- /dev/null +++ b/src/misc/openpty.c @@ -0,0 +1,33 @@ +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <pty.h> +#include <stdio.h> + +/* Nonstandard, but vastly superior to the standard functions */ + +int openpty(int *m, int *s, char *name, const struct termios *tio, const struct winsize *ws) +{ + int n=0; + char buf[20]; + + *m = open("/dev/ptmx", O_RDWR|O_NOCTTY); + if (!*m) return -1; + + if (ioctl(*m, TIOCSPTLCK, &n) || ioctl (*m, TIOCGPTN, &n)) { + close(*m); + return -1; + } + + if (!name) name = buf; + snprintf(name, sizeof buf, "/dev/pts/%d", n); + if ((*s = open(name, O_RDWR|O_NOCTTY)) < 0) { + close(*m); + return -1; + } + + if (tio) tcsetattr(*s, TCSANOW, tio); + if (ws) ioctl(*s, TIOCSWINSZ, ws); + + return 0; +} diff --git a/src/misc/pty.c b/src/misc/pty.c new file mode 100644 index 00000000..0d25a836 --- /dev/null +++ b/src/misc/pty.c @@ -0,0 +1,35 @@ +#include <stdlib.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <fcntl.h> + +int posix_openpt(int flags) +{ + return open("/dev/ptmx", flags); +} + +int grantpt(int fd) +{ + return 0; +} + +int unlockpt(int fd) +{ + int unlock = 0; + return ioctl(fd, TIOCSPTLCK, &unlock); +} + +char *ptsname(int fd) +{ + static char buf[9 + sizeof(int)*3 + 1]; + char *s = buf+sizeof(buf)-1; + int pty; + if (ioctl (fd, TIOCGPTN, &pty)) + return NULL; + if (pty) for (; pty; pty/=10) *--s = '0' + pty%10; + else *--s = '0'; + s -= 9; + s[0] = '/'; s[1] = 'd'; s[2] = 'e'; s[3] = 'v'; + s[4] = '/'; s[5] = 'p'; s[6] = 't'; s[7] = 's'; s[8] = '/'; + return s; +} diff --git a/src/misc/realpath.c b/src/misc/realpath.c new file mode 100644 index 00000000..f6b55495 --- /dev/null +++ b/src/misc/realpath.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +char *realpath(const char *filename, char *resolved) +{ + return 0; +} diff --git a/src/misc/sched_yield.c b/src/misc/sched_yield.c new file mode 100644 index 00000000..8a68519e --- /dev/null +++ b/src/misc/sched_yield.c @@ -0,0 +1,10 @@ +#include <sched.h> +#include "syscall.h" +#include "libc.h" + +int __yield() +{ + return syscall0(__NR_sched_yield); +} + +weak_alias(__yield, sched_yield); diff --git a/src/misc/setpriority.c b/src/misc/setpriority.c new file mode 100644 index 00000000..26da4b83 --- /dev/null +++ b/src/misc/setpriority.c @@ -0,0 +1,7 @@ +#include <sys/resource.h> +#include "syscall.h" + +int setpriority(int which, id_t who, int prio) +{ + return syscall3(__NR_getpriority, which, who, prio); +} diff --git a/src/misc/setrlimit.c b/src/misc/setrlimit.c new file mode 100644 index 00000000..7fdfc4e5 --- /dev/null +++ b/src/misc/setrlimit.c @@ -0,0 +1,11 @@ +#include <sys/resource.h> +#include "syscall.h" +#include "libc.h" + +int setrlimit(int resource, const struct rlimit *rlim) +{ + long k_rlim[2] = { rlim->rlim_cur, rlim->rlim_max }; + return syscall2(__NR_setrlimit, resource, (long)k_rlim); +} + +LFS64(setrlimit); diff --git a/src/misc/syslog.c b/src/misc/syslog.c new file mode 100644 index 00000000..4809d2da --- /dev/null +++ b/src/misc/syslog.c @@ -0,0 +1,115 @@ +#include <stdarg.h> +#include <sys/socket.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <syslog.h> +#include <time.h> +#include <signal.h> +#include <string.h> +#include "libc.h" + +static int lock; +static const char *log_ident; +static int log_opt; +static int log_facility = LOG_USER; +static int log_mask = 0xff; +static FILE *log_f; + +int setlogmask(int maskpri) +{ + int old = log_mask; + if (maskpri) log_mask = maskpri; + return old; +} + +static const struct { + short sun_family; + char sun_path[9]; +} log_addr = { + AF_UNIX, + "/dev/log" +}; + +void closelog(void) +{ + LOCK(&lock); + if (log_f) fclose(log_f); + log_f = NULL; + UNLOCK(&lock); +} + +static void __openlog(const char *ident, int opt, int facility) +{ + int fd; + + log_ident = ident; + log_opt = opt; + log_facility = facility; + + if (!(opt & LOG_NDELAY) || log_f) return; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + fcntl(fd, F_SETFD, FD_CLOEXEC); + if (connect(fd, (void *)&log_addr, sizeof(short) + sizeof "/dev/log") < 0) + close(fd); + else log_f = fdopen(fd, "wb"); +} + +void openlog(const char *ident, int opt, int facility) +{ + LOCK(&lock); + __openlog(ident, opt, facility); + UNLOCK(&lock); +} + +void syslog(int priority, const char *message, ...) +{ + struct sigaction sa; + va_list ap; + char timebuf[16]; + time_t now; + struct tm tm; + //const char *fmt, *ident, *sep; + //int i; + + if (!(log_mask & LOG_MASK(priority&7)) || (priority&~0x3ff)) return; + + LOCK(&lock); + + if (!log_f) __openlog(log_ident, log_opt | LOG_NDELAY, log_facility); + if (!log_f) { + UNLOCK(&lock); + return; + } + + memset(&sa, 0, sizeof sa); + sa.sa_handler = SIG_IGN; + if (sigaction(SIGPIPE, &sa, &sa) < 0) { + // we must abandon logging or we might cause SIGPIPE + UNLOCK(&lock); + return; + } + + now = time(NULL); + gmtime_r(&now, &tm); + strftime(timebuf, sizeof timebuf, "%b %e %T", &tm); + + fprintf(log_f, "<%d>%s ", priority, timebuf); + if (log_ident) fprintf(log_f, "%s", log_ident); + if (log_opt & LOG_PID) fprintf(log_f, "[%d]", getpid()); + if (log_ident) fprintf(log_f, ": "); + + va_start(ap, message); + vfprintf(log_f, message, ap); + va_end(ap); + fputc(0, log_f); + fflush(log_f); + + // Note: LOG_CONS is not supported because it is annoying!! + // syslogd will send messages to console if it deems them appropriate! + + sigaction(SIGPIPE, &sa, NULL); + + UNLOCK(&lock); +} diff --git a/src/misc/uname.c b/src/misc/uname.c new file mode 100644 index 00000000..fbe86643 --- /dev/null +++ b/src/misc/uname.c @@ -0,0 +1,8 @@ +#include <sys/utsname.h> +#include <string.h> +#include "syscall.h" + +int uname(struct utsname *uts) +{ + return syscall1(__NR_uname, (long)uts); +} diff --git a/src/mman/madvise.c b/src/mman/madvise.c new file mode 100644 index 00000000..f03647ca --- /dev/null +++ b/src/mman/madvise.c @@ -0,0 +1,10 @@ +#include <sys/mman.h> +#include "syscall.h" +#include "libc.h" + +int __madvise(void *addr, size_t len, int advice) +{ + return syscall3(__NR_madvise, (long)addr, len, advice); +} + +weak_alias(__madvise, madvise); diff --git a/src/mman/mlock.c b/src/mman/mlock.c new file mode 100644 index 00000000..3c7c653c --- /dev/null +++ b/src/mman/mlock.c @@ -0,0 +1,7 @@ +#include <sys/mman.h> +#include "syscall.h" + +int mlock(const void *addr, size_t len) +{ + return syscall2(__NR_mlock, (long)addr, len); +} diff --git a/src/mman/mlockall.c b/src/mman/mlockall.c new file mode 100644 index 00000000..782fc9db --- /dev/null +++ b/src/mman/mlockall.c @@ -0,0 +1,7 @@ +#include <sys/mman.h> +#include "syscall.h" + +int mlockall(int flags) +{ + return syscall1(__NR_mlockall, flags); +} diff --git a/src/mman/mmap.c b/src/mman/mmap.c new file mode 100644 index 00000000..93c76582 --- /dev/null +++ b/src/mman/mmap.c @@ -0,0 +1,18 @@ +#include <unistd.h> +#include <sys/mman.h> +#include <errno.h> +#include <limits.h> +#include "syscall.h" +#include "libc.h" + +void *__mmap(void *start, size_t len, int prot, int flags, int fd, off_t off) +{ + if (sizeof(off_t) > sizeof(long)) + if (((long)off & 0xfff) | ((long)((unsigned long long)off>>(12 + 8*(sizeof(off_t)-sizeof(long)))))) + start = (void *)-1; + return (void *)syscall6(__NR_mmap2, (long)start, len, prot, flags, fd, off>>12); +} + +weak_alias(__mmap, mmap); + +LFS64(mmap); diff --git a/src/mman/mprotect.c b/src/mman/mprotect.c new file mode 100644 index 00000000..11d5e231 --- /dev/null +++ b/src/mman/mprotect.c @@ -0,0 +1,7 @@ +#include <sys/mman.h> +#include "syscall.h" + +int mprotect(void *addr, size_t len, int prot) +{ + return syscall3(__NR_mprotect, (long)addr, len, prot); +} diff --git a/src/mman/mremap.c b/src/mman/mremap.c new file mode 100644 index 00000000..78491ef4 --- /dev/null +++ b/src/mman/mremap.c @@ -0,0 +1,19 @@ +#include <unistd.h> +#include <sys/mman.h> +#include <stdarg.h> +#include "syscall.h" +#include "libc.h" + +void *__mremap(void *old_addr, size_t old_len, size_t new_len, int flags, ...) +{ + va_list ap; + void *new_addr; + + va_start(ap, flags); + new_addr = va_arg(ap, void *); + va_end(ap); + + return (void *)syscall5(__NR_mremap, (long)old_addr, old_len, new_len, flags, (long)new_addr); +} + +weak_alias(__mremap, mremap); diff --git a/src/mman/msync.c b/src/mman/msync.c new file mode 100644 index 00000000..e0926470 --- /dev/null +++ b/src/mman/msync.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include <sys/mman.h> +#include "syscall.h" + +int msync(void *start, size_t len, int flags) +{ + return syscall3(__NR_msync, (long)start, len, flags); +} diff --git a/src/mman/munlock.c b/src/mman/munlock.c new file mode 100644 index 00000000..0db59815 --- /dev/null +++ b/src/mman/munlock.c @@ -0,0 +1,7 @@ +#include <sys/mman.h> +#include "syscall.h" + +int munlock(const void *addr, size_t len) +{ + return syscall2(__NR_munlock, (long)addr, len); +} diff --git a/src/mman/munlockall.c b/src/mman/munlockall.c new file mode 100644 index 00000000..ce3e86cc --- /dev/null +++ b/src/mman/munlockall.c @@ -0,0 +1,7 @@ +#include <sys/mman.h> +#include "syscall.h" + +int munlockall(void) +{ + return syscall0(__NR_munlockall); +} diff --git a/src/mman/munmap.c b/src/mman/munmap.c new file mode 100644 index 00000000..c9661cda --- /dev/null +++ b/src/mman/munmap.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include <sys/mman.h> +#include "syscall.h" +#include "libc.h" + +int __munmap(void *start, size_t len) +{ + return syscall2(__NR_munmap, (long)start, len); +} + +weak_alias(__munmap, munmap); diff --git a/src/mman/posix_madvise.c b/src/mman/posix_madvise.c new file mode 100644 index 00000000..4727ad75 --- /dev/null +++ b/src/mman/posix_madvise.c @@ -0,0 +1,6 @@ +#include <sys/mman.h> + +int posix_madvise(void *addr, size_t len, int advice) +{ + return 0; +} diff --git a/src/multibyte/btowc.c b/src/multibyte/btowc.c new file mode 100644 index 00000000..9d2c3b16 --- /dev/null +++ b/src/multibyte/btowc.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <wchar.h> + +wint_t btowc(int c) +{ + return c<128U ? c : EOF; +} diff --git a/src/multibyte/decode.c b/src/multibyte/decode.c new file mode 100644 index 00000000..8d3d3c0b --- /dev/null +++ b/src/multibyte/decode.c @@ -0,0 +1,47 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +/* Decodes UTF-8 byte-by-byte. The c argument must be initialized to 0 + * to begin decoding; when finished it will contain the Unicode scalar + * value decoded. Return value is 1 if finished, 0 if in-progress, and + * -1 if an invalid sequence was encountered. After an invalid sequence, + * the state (in c) automatically resets to 0 if a continuation byte was + * expected to facilitate a calling idiom of immediately retrying a + * failed decode call after processing the invalid sequence. If the + * second try fails, the byte is invalid as a starter as well. + * + * A trivial usage idiom is: + * while (src<end && (n=decode(dst, *src))>=0) 1[dst+=n]=0, src++; + */ + +int decode(unsigned *c, unsigned b) +{ + if (!*c) { + if (b < 0x80) { + *c = b; + return 1; + } else if (b-SA >= SB-SA) { + *c = FAILSTATE; + return -1; + } + *c = bittab[b-SA]; + return 0; + } + + if (OOB(*c,b)) { + *c = 0; + return -1; + } + *c = *c<<6 | b-0x80; + return !(*c&(1U<<31)); +} diff --git a/src/multibyte/internal.c b/src/multibyte/internal.c new file mode 100644 index 00000000..e9b938dd --- /dev/null +++ b/src/multibyte/internal.c @@ -0,0 +1,60 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <inttypes.h> + +#include "internal.h" + +#define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) ) +#define D(x) C((x+16)) +#define E(x) ( ( x==0 ? R(0xa0,0xc0) : \ + x==0xd ? R(0x80,0xa0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | x ) +#ifdef I_FAILED_TO_RTFM_RFC3629 +#define F0(x) (( x==0 ? R(0x90,0xc0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | x ) +#define F8(x) (( x==0 ? R(0xa0,0xc0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | ( R(0x80,0xc0) >> 18 ) \ + | x ) +#define FC(x) (( x==0 ? R(0x88,0xc0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | ( R(0x80,0xc0) >> 18 ) \ + | ( R(0x80,0xc0) >> 24 ) \ + | x ) +#define F(x) ( x<8 ? F0(x) : x<12 ? F8((x&3)) : x<14 ? FC((x&1)) : -1 ) +#else +#define F(x) ( ( x>=5 ? 0 : \ + x==0 ? R(0x90,0xc0) : \ + x==4 ? R(0x80,0xa0) : \ + R(0x80,0xc0) ) \ + | ( R(0x80,0xc0) >> 6 ) \ + | ( R(0x80,0xc0) >> 12 ) \ + | x ) +#endif + +const uint32_t bittab[] = { + C(0x2),C(0x3),C(0x4),C(0x5),C(0x6),C(0x7), + C(0x8),C(0x9),C(0xa),C(0xb),C(0xc),C(0xd),C(0xe),C(0xf), + D(0x0),D(0x1),D(0x2),D(0x3),D(0x4),D(0x5),D(0x6),D(0x7), + D(0x8),D(0x9),D(0xa),D(0xb),D(0xc),D(0xd),D(0xe),D(0xf), + E(0x0),E(0x1),E(0x2),E(0x3),E(0x4),E(0x5),E(0x6),E(0x7), + E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf), + F(0x0),F(0x1),F(0x2),F(0x3),F(0x4), +#ifdef I_FAILED_TO_RTFM_RFC3629 + F(0x5),F(0x6),F(0x7), + F(0x8),F(0x9),F(0xa),F(0xb),F(0xc),F(0xd) +#endif +}; diff --git a/src/multibyte/internal.h b/src/multibyte/internal.h new file mode 100644 index 00000000..427519a2 --- /dev/null +++ b/src/multibyte/internal.h @@ -0,0 +1,61 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#define LIBC +#ifndef LIBC +/* rename functions not to conflict with libc */ +#ifndef myprefix +#define myprefix fsmu8_ +#endif +#define concat2(a,b) a ## b +#define concat(a,b) concat2(a,b) +#define prefix(b) concat(myprefix,b) + +#undef mblen +#undef mbrlen +#undef mbrtowc +#undef mbsinit +#undef mbsnrtowcs +#undef mbsrtowcs +#undef wcrtomb +#undef wcsrtombs +#undef wcstombs +#undef wctomb +#define mblen prefix(mblen) +#define mbrlen prefix(mbrlen) +#define mbrtowc prefix(mbrtowc) +#define mbsinit prefix(mbsinit) +#define mbsnrtowcs prefix(mbsnrtowcs) +#define mbsrtowcs prefix(mbsrtowcs) +#define mbstowcs prefix(mbstowcs) +#define wcrtomb prefix(wcrtomb) +#define wcsnrtombs prefix(wcsnrtombs) +#define wcsrtombs prefix(wcsrtombs) +#define wcstombs prefix(wcstombs) +#define wctomb prefix(wctomb) + +#define bittab prefix(bittab) +#else +#define bittab __fsmu8 +#endif + +extern const uint32_t bittab[]; + +/* Upper 6 state bits are a negative integer offset to bound-check next byte */ +/* equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f */ +#define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7) + +/* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */ +#define R(a,b) ((uint32_t)((a==0x80 ? 0x40-b : -a) << 23)) +#define FAILSTATE R(0x80,0x80) + +#ifdef I_FAILED_TO_RTFM_RFC3629 +#define SA 0xc2 +#define SB 0xfe +#else +#define SA 0xc2 +#define SB 0xf5 +#endif diff --git a/src/multibyte/mblen.c b/src/multibyte/mblen.c new file mode 100644 index 00000000..26d35649 --- /dev/null +++ b/src/multibyte/mblen.c @@ -0,0 +1,17 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int mblen(const char *s, size_t n) +{ + return mbtowc(0, s, n); +} diff --git a/src/multibyte/mbrlen.c b/src/multibyte/mbrlen.c new file mode 100644 index 00000000..c9a9f033 --- /dev/null +++ b/src/multibyte/mbrlen.c @@ -0,0 +1,18 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbrlen(const char *s, size_t n, mbstate_t *st) +{ + static unsigned internal; + return mbrtowc(0, s, n, st ? st : (mbstate_t *)&internal); +} diff --git a/src/multibyte/mbrtowc.c b/src/multibyte/mbrtowc.c new file mode 100644 index 00000000..09badebe --- /dev/null +++ b/src/multibyte/mbrtowc.c @@ -0,0 +1,58 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbrtowc(wchar_t *wc, const char *src, size_t n, mbstate_t *st) +{ + static unsigned internal_state; + unsigned c; + const unsigned char *s = src; + const unsigned N = n; + + if (!st) st = (void *)&internal_state; + c = *(unsigned *)st; + + if (!s) { + s = ""; + wc = (void *)&wc; + n = 1; + } else if (!wc) wc = (void *)&wc; + + if (!n) return -2; + if (!c) { + if ((unsigned)*s < 0x80) return !!(*wc = *s); + if ((unsigned)*s-SA > SB-SA) goto ilseq; + c = bittab[*s++-SA]; n--; + } + + if (n) { + if (OOB(c,*s)) goto ilseq; +loop: + c = c<<6 | *s++-0x80; n--; + if (!(c&(1U<<31))) { + *(unsigned *)st = 0; + *wc = c; + return N-n; + } + if (n) { + if ((unsigned)*s-0x80 >= 0x40) goto ilseq; + goto loop; + } + } + + *(unsigned *)st = c; + return -2; +ilseq: + *(unsigned *)st = FAILSTATE; + errno = EILSEQ; + return -1; +} diff --git a/src/multibyte/mbsinit.c b/src/multibyte/mbsinit.c new file mode 100644 index 00000000..d307e5a7 --- /dev/null +++ b/src/multibyte/mbsinit.c @@ -0,0 +1,17 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int mbsinit(const mbstate_t *st) +{ + return !*(unsigned *)st; +} diff --git a/src/multibyte/mbsnrtowcs.c b/src/multibyte/mbsnrtowcs.c new file mode 100644 index 00000000..c6f0207f --- /dev/null +++ b/src/multibyte/mbsnrtowcs.c @@ -0,0 +1,61 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> +#include <stdio.h> + +#include "internal.h" + +size_t mbsnrtowcs(wchar_t *wcs, const char **src, size_t n, size_t wn, mbstate_t *st) +{ + size_t l, cnt=0, n2; + wchar_t *ws, wbuf[256]; + const char *s = *src; + + if (!wcs) ws = wbuf, wn = sizeof wbuf / sizeof *wbuf; + else ws = wcs; + + /* making sure output buffer size is at most n/4 will ensure + * that mbsrtowcs never reads more than n input bytes. thus + * we can use mbsrtowcs as long as it's practical.. */ + + while ( s && wn && ( (n2=n/4)>=wn || n2>32 ) ) { + if (n2>=wn) n2=wn; + n -= n2; + l = mbsrtowcs(ws, &s, n2, st); + if (!(l+1)) { + cnt = l; + wn = 0; + break; + } + if (ws != wbuf) { + ws += l; + wn -= l; + } + cnt += l; + } + if (s) while (wn && n) { + l = mbrtowc(ws, s, n, st); + if (l+2<=2) { + if (!(l+1)) { + cnt = l; + break; + } + /* have to roll back partial character */ + *(unsigned *)st = 0; + break; + } + s += l; n -= l; + /* safe - this loop runs fewer than sizeof(wbuf)/8 times */ + ws++; wn--; + cnt++; + } + if (wcs) *src = s; + return cnt; +} diff --git a/src/multibyte/mbsrtowcs.c b/src/multibyte/mbsrtowcs.c new file mode 100644 index 00000000..e2b43480 --- /dev/null +++ b/src/multibyte/mbsrtowcs.c @@ -0,0 +1,121 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbsrtowcs(wchar_t *ws, const char **src, size_t wn, mbstate_t *st) +{ + unsigned c; + const unsigned char *s = *src; + const wchar_t *wsorig = ws; + + if (!st) st = (void *)&c, c = 0; + else c = *(unsigned *)st; + + if (c) { + *(unsigned *)st = 0; + if (!ws) { + wn = 0; + goto resume0; + } + goto resume; + } + + if (!ws) for (wn=0;;) { + if ((unsigned)*s-SA >= SB-SA) { + while (((unsigned)s&3) && (unsigned)*s-1<0x7f) s++, wn++; + while (!(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) s+=4, wn+=4; + while ((unsigned)*s-1<0x7f) s++, wn++; + if (!*s) return wn; + if ((unsigned)*s-SA >= SB-SA) goto ilseq2; + } + c = bittab[*s++-SA]; + do { +resume0: + if (OOB(c,*s)) goto ilseq2; s++; + c <<= 6; if (!(c&(1U<<31))) break; +#ifdef I_FAILED_TO_RTFM_RFC3629 + if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2; + c <<= 6; if (!(c&(1U<<31))) break; + if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2; + c <<= 6; if (!(c&(1U<<31))) break; +#endif + if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2; + c <<= 6; if (!(c&(1U<<31))) break; + if ((unsigned)*s++-0x80 >= 0x40) goto ilseq2; + } while (0); + wn++; c = 0; + } + + while (wn) { + if ((unsigned)*s-SA >= SB-SA) { + if (wn >= 7) { + while (((unsigned)s&3) && (unsigned)*s-1<0x7f) { + *ws++ = *s++; + wn--; + } + while (wn>=4 && !(( *(uint32_t*)s | *(uint32_t*)s-0x01010101) & 0x80808080)) { + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + *ws++ = *s++; + wn -= 4; + } + } + while (wn && (unsigned)*s-1<0x7f) { + *ws++ = *s++; + wn--; + } + if (!wn) break; + if (!*s) { + *ws = 0; + *src = 0; + return ws-wsorig; + } + if ((unsigned)*s-SA >= SB-SA) goto ilseq; + } + c = bittab[*s++-SA]; + do { +resume: + if (OOB(c,*s)) goto ilseq; + c = (c<<6) | *s++-0x80; + if (!(c&(1U<<31))) break; + +#ifdef I_FAILED_TO_RTFM_RFC3629 + if ((unsigned)*s-0x80 >= 0x40) goto ilseq; + c = (c<<6) | *s++-0x80; + if (!(c&(1U<<31))) break; + + if ((unsigned)*s-0x80 >= 0x40) goto ilseq; + c = (c<<6) | *s++-0x80; + if (!(c&(1U<<31))) break; +#endif + + if ((unsigned)*s-0x80 >= 0x40) goto ilseq; + c = (c<<6) | *s++-0x80; + if (!(c&(1U<<31))) break; + + if ((unsigned)*s-0x80 >= 0x40) goto ilseq; + c = (c<<6) | *s++-0x80; + } while (0); + + *ws++ = c; wn--; c = 0; + } + *src = s; + return ws-wsorig; +ilseq: + *src = s; +ilseq2: + /* enter permanently failing state */ + *(unsigned *)st = FAILSTATE; + errno = EILSEQ; + return -1; +} diff --git a/src/multibyte/mbstowcs.c b/src/multibyte/mbstowcs.c new file mode 100644 index 00000000..23e1d925 --- /dev/null +++ b/src/multibyte/mbstowcs.c @@ -0,0 +1,18 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t mbstowcs(wchar_t *ws, const char *s, size_t wn) +{ + mbstate_t st = { 0 }; + return mbsrtowcs(ws, (void*)&s, wn, &st); +} diff --git a/src/multibyte/mbtowc.c b/src/multibyte/mbtowc.c new file mode 100644 index 00000000..bdcaeb3c --- /dev/null +++ b/src/multibyte/mbtowc.c @@ -0,0 +1,19 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int mbtowc(wchar_t *wc, const char *s, size_t n) +{ + mbstate_t st = { 0 }; + n = mbrtowc(wc, s, n, &st); + return n+2 ? n : -1; +} diff --git a/src/multibyte/wcrtomb.c b/src/multibyte/wcrtomb.c new file mode 100644 index 00000000..36180c8f --- /dev/null +++ b/src/multibyte/wcrtomb.c @@ -0,0 +1,38 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcrtomb(char *s, wchar_t wc, mbstate_t *st) +{ + if (!s) return 1; + if ((unsigned)wc < 0x80) { + *s = wc; + return 1; + } else if ((unsigned)wc < 0x800) { + *s++ = 0xc0 | (wc>>6); + *s = 0x80 | (wc&0x3f); + return 2; + } else if ((unsigned)wc < 0xd800 || (unsigned)wc-0xe000 < 0x2000) { + *s++ = 0xe0 | (wc>>12); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 3; + } else if ((unsigned)wc-0x10000 < 0x100000) { + *s++ = 0xf0 | (wc>>18); + *s++ = 0x80 | ((wc>>12)&0x3f); + *s++ = 0x80 | ((wc>>6)&0x3f); + *s = 0x80 | (wc&0x3f); + return 4; + } + errno = EILSEQ; + return -1; +} diff --git a/src/multibyte/wcsnrtombs.c b/src/multibyte/wcsnrtombs.c new file mode 100644 index 00000000..666f6f3f --- /dev/null +++ b/src/multibyte/wcsnrtombs.c @@ -0,0 +1,51 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcsnrtombs(char *dst, const wchar_t **wcs, size_t wn, size_t n, mbstate_t *st) +{ + size_t l, cnt=0, n2; + char *s, buf[256]; + const wchar_t *ws = *wcs; + + if (!dst) s = buf, n = sizeof buf; + else s = dst; + + while ( n && ( (n2=wn)>=n || n2>32 ) ) { + if (n2>=n) n2=n; + wn -= n2; + l = wcsrtombs(s, &ws, n2, 0); + if (!(l+1)) { + cnt = l; + n = 0; + break; + } + if (s != buf) { + s += l; + n -= l; + } + cnt += l; + } + while (n && wn) { + l = wcrtomb(s, *ws, 0); + if (!(l+1)) { + cnt = l; + break; + } + ws++; wn--; + /* safe - this loop runs fewer than sizeof(buf) times */ + s+=l; n-=l; + cnt++; + } + if (dst) *wcs = ws; + return cnt; +} diff --git a/src/multibyte/wcsrtombs.c b/src/multibyte/wcsrtombs.c new file mode 100644 index 00000000..3c48d65b --- /dev/null +++ b/src/multibyte/wcsrtombs.c @@ -0,0 +1,58 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcsrtombs(char *s, const wchar_t **ws, size_t n, mbstate_t *st) +{ + const wchar_t *ws2; + char buf[4]; + size_t N = n, l; + if (!s) { + for (n=0, ws2=*ws; *ws2; ws2++) { + if (*ws2 >= 0x80) { + l = wcrtomb(buf, *ws2, 0); + if (!(l+1)) return -1; + n += l; + } else n++; + } + return n; + } + while (n>=4 && **ws) { + if (**ws >= 0x80) { + l = wcrtomb(s, **ws, 0); + if (!(l+1)) return -1; + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + while (n && **ws) { + if (**ws >= 0x80) { + l = wcrtomb(buf, **ws, 0); + if (!(l+1)) return -1; + if (l>n) return N-n; + wcrtomb(s, **ws, 0); + s += l; + n -= l; + } else { + *s++ = **ws; + n--; + } + (*ws)++; + } + if (n) *s = 0; + *ws = 0; + return N-n; +} diff --git a/src/multibyte/wcstombs.c b/src/multibyte/wcstombs.c new file mode 100644 index 00000000..b9c1b18a --- /dev/null +++ b/src/multibyte/wcstombs.c @@ -0,0 +1,17 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +size_t wcstombs(char *s, const wchar_t *ws, size_t n) +{ + return wcsrtombs(s, &ws, n, 0); +} diff --git a/src/multibyte/wctob.c b/src/multibyte/wctob.c new file mode 100644 index 00000000..d6353ee1 --- /dev/null +++ b/src/multibyte/wctob.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <wchar.h> + +int wctob(wint_t c) +{ + if (c < 128U) return c; + return EOF; +} diff --git a/src/multibyte/wctomb.c b/src/multibyte/wctomb.c new file mode 100644 index 00000000..6910ef37 --- /dev/null +++ b/src/multibyte/wctomb.c @@ -0,0 +1,18 @@ +/* + * This code was written by Rich Felker in 2010; no copyright is claimed. + * This code is in the public domain. Attribution is appreciated but + * unnecessary. + */ + +#include <stdlib.h> +#include <inttypes.h> +#include <wchar.h> +#include <errno.h> + +#include "internal.h" + +int wctomb(char *s, wchar_t wc) +{ + if (!s) return 0; + return wcrtomb(s, wc, 0); +} diff --git a/src/network/__dns.c b/src/network/__dns.c new file mode 100644 index 00000000..73ec422d --- /dev/null +++ b/src/network/__dns.c @@ -0,0 +1,267 @@ +#include <stdint.h> +#include <netdb.h> +#include <stdio.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <time.h> +#include <ctype.h> +#include <unistd.h> +#include "__dns.h" +#include "stdio_impl.h" + +#define TIMEOUT 5 +#define RETRY 1 +#define PACKET_MAX 512 +#define PTR_MAX (64 + sizeof ".in-addr.arpa") + +int __dns_doqueries(unsigned char *dest, const char *name, int *rr, int rrcnt) +{ + time_t t0 = time(0); + int fd; + FILE *f, _f; + unsigned char _buf[64]; + char line[64], *s, *z; + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa = {0}, ns[3] = {{0}}; + socklen_t sl; + int nns; + int family; + unsigned char q[280] = "", *r = dest; + int ql; + int rlen; + int got = 0, failed = 0; + int errcode = EAI_AGAIN; + int i, j; + struct timeval tv; + fd_set fds; + int id; + + /* Construct query template - RR and ID will be filled later */ + if (strlen(name)-1 >= 254U) return -1; + q[2] = q[5] = 1; + strcpy(q+13, name); + for (i=13; q[i]; i=j+1) { + for (j=i; q[j] && q[j] != '.'; j++); + if (j-i-1u > 62u) return -1; + q[i-1] = j-i; + } + q[i+3] = 1; + ql = i+4; + + /* Make a reasonably unpredictable id */ + gettimeofday(&tv, 0); + id = tv.tv_usec + tv.tv_usec/256 & 0xffff; + + /* Get nameservers from resolv.conf, fallback to localhost */ + f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); + if (f) for (nns=0; nns<3 && fgets(line, sizeof line, f); ) { + if (strncmp(line, "nameserver", 10) || !isspace(line[10])) + continue; + for (s=line+11; isspace(*s); s++); + for (z=s; *z && !isspace(*z); z++); + *z=0; + if (__ipparse(ns+nns, family, s) < 0) continue; + ns[nns].sin.sin_port = htons(53); + family = ns[nns++].sin.sin_family; + sl = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; + } + if (f) __fclose_ca(f); + if (!nns) { + ns[0].sin.sin_family = AF_INET; + ns[0].sin.sin_port = htons(53); + nns=1; + sl = sizeof sa.sin; + } + + /* Get local address and open/bind a socket */ + sa.sin.sin_family = family; + fd = socket(family, SOCK_DGRAM, 0); + if (bind(fd, (void *)&sa, sl) < 0) { + close(fd); + return -1; + } + /* Nonblocking to work around Linux UDP select bug */ + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + + /* Loop until we timeout; break early on success */ + for (; time(0)-t0 < TIMEOUT; ) { + + /* Query all configured namservers in parallel */ + for (i=0; i<rrcnt; i++) if (rr[i]) for (j=0; j<nns; j++) { + q[0] = id+i >> 8; + q[1] = id+i; + q[ql-3] = rr[i]; + sendto(fd, q, ql, MSG_NOSIGNAL, (void *)&ns[j], sl); + } + + /* Wait for a response, or until time to retry */ + FD_ZERO(&fds); + FD_SET(fd, &fds); + tv.tv_sec = RETRY; + tv.tv_usec = 0; + if (select(fd+1, &fds, 0, 0, &tv) <= 0) continue; + + /* Process any and all replies */ + while (got+failed < rrcnt && (rlen = recvfrom(fd, r, 512, 0, + (void *)&sa, (socklen_t[1]){sl})) >= 2) + { + /* Ignore replies from addresses we didn't send to */ + for (i=0; i<nns; i++) if (!memcmp(ns+i, &sa, sl)) break; + if (i==nns) continue; + + /* Compute index of the query from id */ + i = r[0]*256+r[1] - id & 0xffff; + if ((unsigned)i >= rrcnt || !rr[i]) continue; + + /* Interpret the result code */ + switch (r[3] & 15) { + case 0: + got++; + break; + case 3: + if (1) errcode = EAI_NONAME; else + default: + errcode = EAI_FAIL; + failed++; + } + + /* Mark this record as answered */ + rr[i] = 0; + r += 512; + } + + /* Check to see if we have answers to all queries */ + if (got+failed == rrcnt) break; + } + close(fd); + + /* Return the number of results, or an error code if none */ + if (got) return got; + return errcode; +} + +static void mkptr4(char *s, const unsigned char *ip) +{ + sprintf(s, "%d.%d.%d.%d.in-addr.arpa", + ip[3], ip[2], ip[1], ip[0]); +} + +static void mkptr6(char *s, const unsigned char *ip) +{ + static const char xdigits[] = "0123456789abcdef"; + int i; + for (i=15; i>=0; i--) { + *s++ = xdigits[ip[i]&15]; *s++ = '.'; + *s++ = xdigits[ip[i]>>4]; *s++ = '.'; + } + strcpy(s, "ip6.arpa"); +} + +int __dns_query(unsigned char *r, const void *a, int family, int ptr) +{ + char buf[PTR_MAX]; + int rr[2], rrcnt = 1; + + if (ptr) { + if (family == AF_INET6) mkptr6(buf, a); + else mkptr4(buf, a); + rr[0] = RR_PTR; + a = buf; + } else if (family == AF_INET6) { + rr[0] = RR_AAAA; + } else { + rr[0] = RR_A; + if (family != AF_INET) rr[rrcnt++] = RR_AAAA; + } + + return __dns_doqueries(r, a, rr, rrcnt); +} + + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +static int decname(char *s, const unsigned char *b, const unsigned char *p) +{ + /* Remember jump destinations to detect loops and abort */ + size_t seen[PACKET_MAX/8/sizeof(size_t)] = { 0 }; + char *sz = s + HOST_NAME_MAX; + const unsigned char *pz = b+512; + for (;;) { + if (p>=pz) return -1; + else if (*p&0xc0) { + int j = (p[0]&1) | p[1]; + if (BITOP(seen, j, &)) return -1; + BITOP(seen, j, |=); + p = b + j; + } else if (*p) { + if (p+*p+1>=pz || s+*p>=sz) return -1; + memcpy(s, p+1, *p); + s += *p+1; + p += *p+1; + s[-1] = *p ? '.' : 0; + } else return 0; + } +} + +int __dns_get_rr(void *dest, size_t stride, size_t maxlen, size_t limit, const unsigned char *r, int rr, int dec) +{ + int qdcount, ancount; + const unsigned char *p; + char tmp[256]; + int found = 0; + int len; + + if ((r[3]&15)) return 0; + p = r+12; + qdcount = r[4]*256 + r[5]; + ancount = r[6]*256 + r[7]; + if (qdcount+ancount > 64) return -1; + while (qdcount--) { + while (p-r < 512 && *p-1U < 127) p++; + if (*p>193 || (*p==193 && p[1]>254) || p>r+506) + return -1; + p += 5 + !!*p; + } + while (ancount--) { + while (p-r < 512 && *p-1U < 127) p++; + if (*p>193 || (*p==193 && p[1]>254) || p>r+506) + return -1; + p += 1 + !!*p; + len = p[8]*256 + p[9]; + if (p+len > r+512) return -1; + if (p[1]==rr && len <= maxlen) { + if (dec && decname(tmp, r, p+10)<0) return -1; + if (dest && limit) { + if (dec) strcpy(dest, tmp); + else memcpy(dest, p+10, len); + dest = (char *)dest + stride; + limit--; + } + found++; + } + p += 10 + len; + } + return found; +} + +int __dns_count_addrs(const unsigned char *r, int cnt) +{ + int found=0, res, i; + static const int p[2][2] = { { 4, RR_A }, { 16, RR_AAAA } }; + + while (cnt--) for (i=0; i<2; i++) { + res = __dns_get_rr(0, 0, p[i][0], -1, r, p[i][1], 0); + if (res < 0) return res; + found += res; + r += 512; + } + return found; +} diff --git a/src/network/__dns.h b/src/network/__dns.h new file mode 100644 index 00000000..9a3f7402 --- /dev/null +++ b/src/network/__dns.h @@ -0,0 +1,14 @@ +#include <stddef.h> + +#define RR_A 1 +#define RR_CNAME 5 +#define RR_PTR 12 +#define RR_AAAA 28 + +int __dns_count_addrs(const unsigned char *, int); +int __dns_get_rr(void *, size_t, size_t, size_t, const unsigned char *, int, int); + +int __dns_query(unsigned char *, const void *, int, int); +int __ipparse(void *, int, const char *); + +int __dns_doqueries(unsigned char *, const char *, int *, int); diff --git a/src/network/__ipparse.c b/src/network/__ipparse.c new file mode 100644 index 00000000..ca9e5890 --- /dev/null +++ b/src/network/__ipparse.c @@ -0,0 +1,40 @@ +#include <string.h> +#include <stdlib.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include "__dns.h" +#include <stdio.h> + +int __ipparse(void *dest, int family, const char *s) +{ + unsigned char *d = dest; + unsigned long a[16] = { 0 }; + const char *z; + int i; + + if (family == AF_INET6) goto not_v4; + + for (i=0; i<4 && *s; i++) { + a[i] = strtoul(s, (char **)&z, 0); + if (z==s || (*z && *z != '.')) goto not_v4; + s=z+1; + } + switch (i) { + case 0: + a[1] = a[0] & 0xffffff; + a[0] >>= 24; + case 1: + a[2] = a[1] & 0xffff; + a[1] >>= 16; + case 2: + a[3] = a[2] & 0xff; + a[2] >>= 8; + } + ((struct sockaddr_in *)d)->sin_family = AF_INET; + d = (void *)&((struct sockaddr_in *)d)->sin_addr; + for (i=0; i<4; i++) d[i] = a[i]; + return 0; + +not_v4: + return -1; +} diff --git a/src/network/accept.c b/src/network/accept.c new file mode 100644 index 00000000..83704096 --- /dev/null +++ b/src/network/accept.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +int accept(int fd, struct sockaddr *addr, socklen_t *len) +{ + unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len }; + int ret; + CANCELPT_BEGIN; + ret = syscall2(__NR_socketcall, SYS_ACCEPT, (long)args); + CANCELPT_END; + return ret; +} diff --git a/src/network/bind.c b/src/network/bind.c new file mode 100644 index 00000000..3bef382c --- /dev/null +++ b/src/network/bind.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int bind(int fd, const struct sockaddr *addr, socklen_t len) +{ + unsigned long args[] = { fd, (unsigned long)addr, len }; + return syscall2(__NR_socketcall, SYS_BIND, (long)args); +} diff --git a/src/network/connect.c b/src/network/connect.c new file mode 100644 index 00000000..6074122e --- /dev/null +++ b/src/network/connect.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +int connect(int fd, const struct sockaddr *addr, socklen_t len) +{ + unsigned long args[] = { fd, (unsigned long)addr, len }; + int ret; + CANCELPT_BEGIN; + ret = syscall2(__NR_socketcall, SYS_CONNECT, (long)args); + CANCELPT_END; + return ret; +} diff --git a/src/network/dn_expand.c b/src/network/dn_expand.c new file mode 100644 index 00000000..01b449bb --- /dev/null +++ b/src/network/dn_expand.c @@ -0,0 +1,28 @@ +#include <resolv.h> +#include <string.h> + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +int dn_expand(unsigned char *b, unsigned char *pz, unsigned char *p, unsigned char *s, int outlen) +{ + /* Remember jump destinations to detect loops and abort */ + size_t seen[512/8/sizeof(size_t)] = { 0 }; + unsigned char *sz = s + outlen; + if (pz-b > 512) return -1; + for (;;) { + if (p>=pz) return -1; + else if (*p&0xc0) { + int j = (p[0]&1) | p[1]; + if (BITOP(seen, j, &)) return -1; + BITOP(seen, j, |=); + p = b + j; + } else if (*p) { + if (p+*p+1>=pz || s+*p>=sz) return -1; + memcpy(s, p+1, *p); + s += *p+1; + p += *p+1; + s[-1] = *p ? '.' : 0; + } else return 0; + } +} diff --git a/src/network/ent.c b/src/network/ent.c new file mode 100644 index 00000000..4c2f24b5 --- /dev/null +++ b/src/network/ent.c @@ -0,0 +1,26 @@ +#include "libc.h" + +void sethostent(int x) +{ +} + +void *gethostent() +{ + return 0; +} + +void endhostent(void) +{ +} + +weak_alias(sethostent, setnetent); +weak_alias(gethostent, getnetent); +weak_alias(endhostent, endnetent); + +weak_alias(sethostent, setservent); +weak_alias(gethostent, getservent); +weak_alias(endhostent, endservent); + +weak_alias(sethostent, setprotoent); +weak_alias(gethostent, getprotoent); +weak_alias(endhostent, endprotoent); diff --git a/src/network/freeaddrinfo.c b/src/network/freeaddrinfo.c new file mode 100644 index 00000000..df3798ae --- /dev/null +++ b/src/network/freeaddrinfo.c @@ -0,0 +1,7 @@ +#include <stdlib.h> +#include <netdb.h> + +void freeaddrinfo(struct addrinfo *p) +{ + free(p); +} diff --git a/src/network/gai_strerror.c b/src/network/gai_strerror.c new file mode 100644 index 00000000..ea00bed7 --- /dev/null +++ b/src/network/gai_strerror.c @@ -0,0 +1,21 @@ +#include <netdb.h> + +static const char msgs[] = + "Invalid flags\0" + "Name does not resolve\0" + "Try again\0" + "Non-recoverable error\0" + "Unrecognized address family or invalid length\0" + "Unrecognized socket type\0" + "Unrecognized service\0" + "Out of memory\0" + "System error\0" + "Overflow\0" + "\0Unknown error"; + +const char *gai_strerror(int ecode) +{ + const char *s; + for (s=msgs, ecode++; ecode && *s; ecode++, s++) for (; *s; s++); + return *s ? s : s+1; +} diff --git a/src/network/getaddrinfo.c b/src/network/getaddrinfo.c new file mode 100644 index 00000000..90e85f6a --- /dev/null +++ b/src/network/getaddrinfo.c @@ -0,0 +1,224 @@ +#include <stdlib.h> +#include <stdio.h> +#include <netdb.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include "__dns.h" +#include "stdio_impl.h" + +static int is_valid(const char *host) +{ + const unsigned char *s; + if (strlen(host)-1 > 254 || mbstowcs(0, host, 0) > 255) return 0; + for (s=host; *s>=0x80 || *s=='.' || *s=='-' || isalnum(*s); s++); + return !*s; +} + +#if 0 +static int have_af(int family) +{ + struct sockaddr_in6 sin6 = { .sin6_family = family }; + socklen_t sl = family == AF_INET + ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6); + int sock = socket(family, SOCK_STREAM, 0); + int have = !bind(sock, (void *)&sin6, sl); + close(sock); + return have; +} +#endif + +#include <stdlib.h> +#include <netdb.h> + +union sa { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; +}; + +struct aibuf { + struct addrinfo ai; + union sa sa; +}; + +/* Extra slots needed for storing canonical name */ +#define EXTRA ((256+sizeof(struct aibuf)-1)/sizeof(struct aibuf)) + +int getaddrinfo(const char *host, const char *serv, const struct addrinfo *hint, struct addrinfo **res) +{ + int flags = hint ? hint->ai_flags : 0; + int family = hint ? hint->ai_family : AF_UNSPEC; + int type = hint ? hint->ai_socktype : 0; + int proto = hint ? hint->ai_protocol : 0; + unsigned long port = 0; + struct aibuf *buf; + union sa sa = {{0}}; + unsigned char reply[1024]; + int i, j; + //char hostbuf[256]; + char line[512]; + FILE *f, _f; + unsigned char _buf[64]; + char *z; + int result; + int cnt; + + if (host && strlen(host)>255) return EAI_NONAME; + if (serv && strlen(serv)>32) return EAI_SERVICE; + + if (type && !proto) + proto = type==SOCK_DGRAM ? IPPROTO_UDP : IPPROTO_TCP; + if (!type && proto) + type = proto==IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM; + + if (serv) { + port = strtoul(serv, &z, 0); + if (!*z && port > 65535) return EAI_SERVICE; + if (!port) { + if (flags & AI_NUMERICSERV) return EAI_SERVICE; + + //f = fopen("/etc/services", "rb"); + return EAI_SERVICE; + } + port = htons(port); + } + + if (!host) { + if (family == AF_UNSPEC) family = AF_INET; + buf = calloc(sizeof *buf, 1+EXTRA); + if (!buf) return EAI_MEMORY; + buf->ai.ai_protocol = proto; + buf->ai.ai_socktype = type; + buf->ai.ai_addr = (void *)&buf->sa; + buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; + buf->ai.ai_family = family; + buf->sa.sin.sin_family = family; + buf->sa.sin.sin_port = port; + if (!(flags & AI_PASSIVE)) { + if (family == AF_INET) { + 0[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=127; + 3[(uint8_t*)&buf->sa.sin.sin_addr.s_addr]=1; + } else buf[0].sa.sin6.sin6_addr.s6_addr[15] = 1; + } + *res = &buf->ai; + return 0; + } + + /* Try as a numeric address */ + if (__ipparse(&sa, family, host) >= 0) { + buf = calloc(sizeof *buf, 1+EXTRA); + if (!buf) return EAI_MEMORY; + family = sa.sin.sin_family; + buf->ai.ai_protocol = proto; + buf->ai.ai_socktype = type; + buf->ai.ai_addr = (void *)&buf->sa; + buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; + buf->ai.ai_family = family; + buf->sa = sa; + buf->sa.sin.sin_port = port; + *res = &buf->ai; + return 0; + } + + if (flags & AI_NUMERICHOST) return EAI_NONAME; + + f = __fopen_rb_ca("/etc/hosts", &_f, _buf, sizeof _buf); + if (f) while (fgets(line, sizeof line, f)) { + char *p; + size_t l = strlen(host); + + if ((p=strchr(line, '#'))) *p++='\n', *p=0; + for(p=line+1; (p=strstr(p, host)) && + (!isspace(p[-1]) || !isspace(p[l])); p++); + if (!p) continue; + __fclose_ca(f); + + /* Isolate IP address to parse */ + for (p=line; *p && !isspace(*p); p++); + *p++ = 0; + if (__ipparse(&sa, family, line) < 0) return EAI_NONAME; + + /* Allocate and fill result buffer */ + buf = calloc(sizeof *buf, 1+EXTRA); + if (!buf) return EAI_MEMORY; + family = sa.sin.sin_family; + buf->ai.ai_protocol = proto; + buf->ai.ai_socktype = type; + buf->ai.ai_addr = (void *)&buf->sa; + buf->ai.ai_addrlen = family==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; + buf->ai.ai_family = family; + buf->sa = sa; + buf->sa.sin.sin_port = port; + + /* Extract first name as canonical name */ + for (; *p && isspace(*p); p++); + buf->ai.ai_canonname = (void *)(buf+1); + snprintf(buf->ai.ai_canonname, 256, "%s", p); + for (p=buf->ai.ai_canonname; *p && !isspace(*p); p++); + *p = 0; + if (!is_valid(buf->ai.ai_canonname)) + buf->ai.ai_canonname = 0; + + *res = &buf->ai; + return 0; + } + if (f) __fclose_ca(f); + +#if 0 + f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); + if (f) while (fgets(line, sizeof line, f)) { + if (!isspace(line[10]) || (strncmp(line, "search", 6) + && strncmp(line, "domain", 6))) continue; + } + if (f) __fclose_ca(f); +#endif + + /* Perform one or more DNS queries for host */ + memset(reply, 0, sizeof reply); + result = __dns_query(reply, host, family, 0); + if (result < 0) return result; + + cnt = __dns_count_addrs(reply, result); + if (cnt <= 0) return EAI_NONAME; + + buf = calloc(sizeof *buf, cnt+EXTRA); + if (!buf) return EAI_MEMORY; + + i = 0; + if (family != AF_INET6) { + j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply, RR_A, 0); + while (j--) buf[i++].sa.sin.sin_family = AF_INET; + } + if (family != AF_INET) { + j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply, RR_AAAA, 0); + while (j--) buf[i++].sa.sin.sin_family = AF_INET6; + } + if (result>1) { + j = __dns_get_rr(&buf[i].sa.sin.sin_addr, sizeof *buf, 4, cnt-i, reply+512, RR_A, 0); + while (j--) buf[i++].sa.sin.sin_family = AF_INET; + j = __dns_get_rr(&buf[i].sa.sin6.sin6_addr, sizeof *buf, 16, cnt-i, reply+512, RR_AAAA, 0); + while (j--) buf[i++].sa.sin.sin_family = AF_INET6; + } + + if (__dns_get_rr((void *)&buf[cnt], 0, 256, 1, reply, RR_CNAME, 1) < 0) + strcpy((void *)&buf[cnt], host); + + for (i=0; i<cnt; i++) { + buf[i].ai.ai_protocol = proto; + buf[i].ai.ai_socktype = type; + buf[i].ai.ai_addr = (void *)&buf[i].sa; + buf[i].ai.ai_addrlen = buf[i].sa.sin.sin_family==AF_INET6 + ? sizeof sa.sin6 : sizeof sa.sin; + buf[i].ai.ai_family = buf[i].sa.sin.sin_family; + buf[i].sa.sin.sin_port = port; + buf[i].ai.ai_next = &buf[i+1].ai; + buf[i].ai.ai_canonname = (void *)&buf[cnt]; + } + buf[cnt-1].ai.ai_next = 0; + *res = &buf->ai; + + return 0; +} diff --git a/src/network/gethostbyaddr.c b/src/network/gethostbyaddr.c new file mode 100644 index 00000000..51e1c569 --- /dev/null +++ b/src/network/gethostbyaddr.c @@ -0,0 +1,15 @@ +#define _GNU_SOURCE + +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> + +struct hostent *gethostbyaddr(const void *a, socklen_t l, int af) +{ + static struct hostent h; + static long buf[512/sizeof(long)]; + struct hostent *res; + if (gethostbyaddr_r(a, l, af, &h, + (void *)buf, sizeof buf, &res, &h_errno)) return 0; + return &h; +} diff --git a/src/network/gethostbyaddr_r.c b/src/network/gethostbyaddr_r.c new file mode 100644 index 00000000..cdb1d503 --- /dev/null +++ b/src/network/gethostbyaddr_r.c @@ -0,0 +1,71 @@ +#define _GNU_SOURCE + +#include <sys/socket.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> +#include <errno.h> +#include <inttypes.h> + +int gethostbyaddr_r(const void *a, socklen_t l, int af, + struct hostent *h, char *buf, size_t buflen, + struct hostent **res, int *err) +{ + union { + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } sa = { .sin.sin_family = af }; + socklen_t sl = af==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin; + int i; + + /* Load address argument into sockaddr structure */ + if (af==AF_INET6 && l==16) memcpy(&sa.sin6.sin6_addr, a, 16); + else if (af==AF_INET && l==4) memcpy(&sa.sin.sin_addr, a, 4); + else { + *err = NO_RECOVERY; + return -1; + } + + /* Align buffer and check for space for pointers and ip address */ + i = (uintptr_t)buf & sizeof(char *)-1; + if (!i) i = sizeof(char *); + if (buflen <= 5*sizeof(char *)-i + l) { + errno = ERANGE; + return -1; + } + buf += sizeof(char *)-i; + buflen -= 5*sizeof(char *)-i + l; + + h->h_addr_list = (void *)buf; + buf += 2*sizeof(char *); + h->h_aliases = (void *)buf; + buf += 2*sizeof(char *); + + h->h_addr_list[0] = buf; + memcpy(h->h_addr_list[0], a, l); + buf += l; + h->h_addr_list[1] = 0; + h->h_aliases[0] = buf; + h->h_aliases[1] = 0; + + switch (getnameinfo((void *)&sa, sl, buf, buflen, 0, 0, 0)) { + case EAI_AGAIN: + *err = TRY_AGAIN; + return -1; + case EAI_OVERFLOW: + errno = ERANGE; + default: + case EAI_MEMORY: + case EAI_SYSTEM: + case EAI_FAIL: + *err = NO_RECOVERY; + return -1; + case 0: + break; + } + + h->h_addrtype = af; + h->h_name = h->h_aliases[0]; + *res = h; + return 0; +} diff --git a/src/network/gethostbyname.c b/src/network/gethostbyname.c new file mode 100644 index 00000000..5088a51e --- /dev/null +++ b/src/network/gethostbyname.c @@ -0,0 +1,63 @@ +#define _GNU_SOURCE + +#include <sys/socket.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> + +struct hostent *gethostbyname(const char *name) +{ + return gethostbyname2(name, AF_INET); +} + +#if 0 +struct hostent *gethostbyname(const char *name) +{ + static struct hostent h; + static char *h_aliases[3]; + static char h_canon[256]; + static char *h_addr_list[10]; + static char h_addr_data[10][4]; + static const struct addrinfo hint = { + .ai_family = AF_INET, .ai_flags = AI_CANONNAME + }; + struct addrinfo *ai, *p; + int i; + + switch (getaddrinfo(name, 0, &hint, &ai)) { + case EAI_NONAME: + h_errno = HOST_NOT_FOUND; + break; + case EAI_AGAIN: + h_errno = TRY_AGAIN; + break; + case EAI_FAIL: + h_errno = NO_RECOVERY; + break; + default: + case EAI_MEMORY: + case EAI_SYSTEM: + h_errno = NO_DATA; + break; + case 0: + break; + } + + strcpy(h_canon, ai->ai_canonname); + h.h_name = h_canon; + h.h_aliases = h_aliases; + h.h_aliases[0] = h_canon; + h.h_aliases[1] = strcmp(h_canon, name) ? (char *)name : 0; + h.h_length = 4; + h.h_addr_list = h_addr_list; + for (i=0, p=ai; i<sizeof h_addr_data/4 && p; i++, p=p->ai_next) { + h.h_addr_list[i] = h_addr_data[i]; + memcpy(h.h_addr_list[i], + &((struct sockaddr_in *)p->ai_addr)->sin_addr, 4); + } + h.h_addr_list[i] = 0; + h.h_addrtype = AF_INET; + freeaddrinfo(ai); + return &h; +} +#endif diff --git a/src/network/gethostbyname2.c b/src/network/gethostbyname2.c new file mode 100644 index 00000000..9fbe2647 --- /dev/null +++ b/src/network/gethostbyname2.c @@ -0,0 +1,16 @@ +#define _GNU_SOURCE + +#include <sys/socket.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> + +struct hostent *gethostbyname2(const char *name, int af) +{ + static struct hostent h; + static long buf[512/sizeof(long)]; + struct hostent *res; + if (gethostbyname2_r(name, af, &h, + (void *)buf, sizeof buf, &res, &h_errno)) return 0; + return &h; +} diff --git a/src/network/gethostbyname2_r.c b/src/network/gethostbyname2_r.c new file mode 100644 index 00000000..c2ed75b4 --- /dev/null +++ b/src/network/gethostbyname2_r.c @@ -0,0 +1,99 @@ +#define _GNU_SOURCE + +#include <sys/socket.h> +#include <netdb.h> +#include <string.h> +#include <netinet/in.h> +#include <errno.h> +#include <inttypes.h> + +int gethostbyname2_r(const char *name, int af, + struct hostent *h, char *buf, size_t buflen, + struct hostent **res, int *err) +{ + struct addrinfo hint = { + .ai_family = af==AF_INET6 ? af : AF_INET, + .ai_flags = AI_CANONNAME + }; + struct addrinfo *ai, *p; + int i; + size_t need; + const char *canon; + + af = hint.ai_family; + + /* Align buffer */ + i = (uintptr_t)buf & sizeof(char *)-1; + if (i) { + if (buflen < sizeof(char *)-i) { + errno = ERANGE; + return -1; + } + buf += sizeof(char *)-i; + buflen -= sizeof(char *)-i; + } + + getaddrinfo(name, 0, &hint, &ai); + switch (getaddrinfo(name, 0, &hint, &ai)) { + case EAI_NONAME: + *err = HOST_NOT_FOUND; + return -1; + case EAI_AGAIN: + *err = TRY_AGAIN; + return -1; + default: + case EAI_MEMORY: + case EAI_SYSTEM: + case EAI_FAIL: + *err = NO_RECOVERY; + return -1; + case 0: + break; + } + + h->h_addrtype = af; + h->h_length = af==AF_INET6 ? 16 : 4; + + canon = ai->ai_canonname ? ai->ai_canonname : name; + need = 4*sizeof(char *); + for (i=0, p=ai; p; i++, p=p->ai_next) + need += sizeof(char *) + h->h_length; + need += strlen(name)+1; + need += strlen(canon)+1; + + if (need > buflen) { + freeaddrinfo(ai); + errno = ERANGE; + return -1; + } + + h->h_aliases = (void *)buf; + buf += 3*sizeof(char *); + h->h_addr_list = (void *)buf; + buf += (i+1)*sizeof(char *); + + h->h_name = h->h_aliases[0] = buf; + strcpy(h->h_name, canon); + buf += strlen(h->h_name)+1; + + if (strcmp(h->h_name, name)) { + h->h_aliases[1] = buf; + strcpy(h->h_aliases[1], name); + buf += strlen(h->h_aliases[1])+1; + } else h->h_aliases[1] = 0; + + h->h_aliases[2] = 0; + + for (i=0, p=ai; p; i++, p=p->ai_next) { + h->h_addr_list[i] = (void *)buf; + buf += h->h_length; + memcpy(h->h_addr_list[i], + &((struct sockaddr_in *)p->ai_addr)->sin_addr, + h->h_length); + } + h->h_addr_list[i] = 0; + + *res = h; + freeaddrinfo(ai); + return 0; +} diff --git a/src/network/gethostbyname_r.c b/src/network/gethostbyname_r.c new file mode 100644 index 00000000..cd872541 --- /dev/null +++ b/src/network/gethostbyname_r.c @@ -0,0 +1,11 @@ +#define _GNU_SOURCE + +#include <sys/socket.h> +#include <netdb.h> + +int gethostbyname_r(const char *name, + struct hostent *h, char *buf, size_t buflen, + struct hostent **res, int *err) +{ + return gethostbyname2_r(name, AF_INET, h, buf, buflen, res, err); +} diff --git a/src/network/getnameinfo.c b/src/network/getnameinfo.c new file mode 100644 index 00000000..0763ca88 --- /dev/null +++ b/src/network/getnameinfo.c @@ -0,0 +1,54 @@ +#include <netdb.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "__dns.h" + +int getnameinfo(const struct sockaddr *sa, socklen_t sl, + char *node, socklen_t nodelen, + char *serv, socklen_t servlen, + int flags) +{ + char buf[256]; + unsigned char reply[512]; + int af = sa->sa_family; + unsigned char *a; + + switch (af) { + case AF_INET: + a = (void *)&((struct sockaddr_in *)sa)->sin_addr; + if (sl != sizeof(struct sockaddr_in)) return EAI_FAMILY; + break; + case AF_INET6: + a = (void *)&((struct sockaddr_in6 *)sa)->sin6_addr; + if (sl != sizeof(struct sockaddr_in6)) return EAI_FAMILY; + break; + default: + return EAI_FAMILY; + } + + if (node && nodelen) { + if ((flags & NI_NUMERICHOST) + || __dns_query(reply, a, af, 1) <= 0 + || __dns_get_rr(buf, 0, 256, 1, reply, RR_PTR, 1) <= 0) + { + if (flags & NI_NAMEREQD) return EAI_NONAME; + inet_ntop(af, a, buf, sizeof buf); + } + if (strlen(buf) >= nodelen) return EAI_OVERFLOW; + strcpy(node, buf); + } + + if (serv && servlen) { + if (snprintf(buf, sizeof buf, "%d", + ntohs(((struct sockaddr_in *)sa)->sin_port))>=servlen) + return EAI_OVERFLOW; + strcpy(serv, buf); + } + + return 0; +} diff --git a/src/network/getpeername.c b/src/network/getpeername.c new file mode 100644 index 00000000..7ecbe375 --- /dev/null +++ b/src/network/getpeername.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int getpeername(int fd, struct sockaddr *addr, socklen_t *len) +{ + unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len }; + return syscall2(__NR_socketcall, SYS_GETPEERNAME, (long)args); +} diff --git a/src/network/getservbyname.c b/src/network/getservbyname.c new file mode 100644 index 00000000..0b00ce11 --- /dev/null +++ b/src/network/getservbyname.c @@ -0,0 +1,12 @@ +#define _GNU_SOURCE +#include <netdb.h> + +struct servent *getservbyname(const char *name, const char *prots) +{ + static struct servent se; + static long buf[32/sizeof(long)]; + struct servent *res; + if (getservbyname_r(name, prots, &se, (void *)buf, sizeof buf, &res)) + return 0; + return &se; +} diff --git a/src/network/getservbyname_r.c b/src/network/getservbyname_r.c new file mode 100644 index 00000000..5c025150 --- /dev/null +++ b/src/network/getservbyname_r.c @@ -0,0 +1,41 @@ +#define _GNU_SOURCE +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <inttypes.h> +#include <errno.h> +#include <string.h> + +int getservbyname_r(const char *name, const char *prots, + struct servent *se, char *buf, size_t buflen, struct servent **res) +{ + struct addrinfo *ai, hint = { .ai_family = AF_INET }; + int i; + + /* Align buffer */ + i = (uintptr_t)buf & sizeof(char *)-1; + if (!i) i = sizeof(char *); + if (buflen < 3*sizeof(char *)-i) { + errno = ERANGE; + return -1; + } + buf += sizeof(char *)-i; + buflen -= sizeof(char *)-i; + + if (!strcmp(prots, "tcp")) hint.ai_protocol = IPPROTO_TCP; + else if (!strcmp(prots, "udp")) hint.ai_protocol = IPPROTO_UDP; + else return -1; + + if (getaddrinfo(0, name, &hint, &ai) < 0) return -1; + + se->s_name = (char *)name; + se->s_aliases = (void *)buf; + se->s_aliases[0] = se->s_name; + se->s_aliases[1] = 0; + se->s_port = ((struct sockaddr_in *)ai->ai_addr)->sin_port; + se->s_proto = (char *)prots; + + freeaddrinfo(ai); + *res = se; + return 0; +} diff --git a/src/network/getservbyport.c b/src/network/getservbyport.c new file mode 100644 index 00000000..c9ecbb11 --- /dev/null +++ b/src/network/getservbyport.c @@ -0,0 +1,12 @@ +#define _GNU_SOURCE +#include <netdb.h> + +struct servent *getservbyport(int port, const char *prots) +{ + static struct servent se; + static long buf[32/sizeof(long)]; + struct servent *res; + if (getservbyport_r(port, prots, &se, (void *)buf, sizeof buf, &res)) + return 0; + return &se; +} diff --git a/src/network/getservbyport_r.c b/src/network/getservbyport_r.c new file mode 100644 index 00000000..004a6168 --- /dev/null +++ b/src/network/getservbyport_r.c @@ -0,0 +1,43 @@ +#define _GNU_SOURCE +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <inttypes.h> +#include <errno.h> +#include <string.h> + +int getservbyport_r(int port, const char *prots, + struct servent *se, char *buf, size_t buflen, struct servent **res) +{ + int i; + struct sockaddr_in sin = { + .sin_family = AF_INET, + .sin_port = port, + }; + + /* Align buffer */ + i = (uintptr_t)buf & sizeof(char *)-1; + if (!i) i = sizeof(char *); + if (buflen < 3*sizeof(char *)-i) { + errno = ERANGE; + return -1; + } + buf += sizeof(char *)-i; + buflen -= sizeof(char *)-i; + + if (strcmp(prots, "tcp") && strcmp(prots, "udp")) return -1; + + se->s_port = port; + se->s_proto = (char *)prots; + se->s_aliases = (void *)buf; + buf += 2*sizeof(char *); + buflen -= 2*sizeof(char *); + se->s_aliases[1] = 0; + se->s_aliases[0] = se->s_name = buf; + + if (getnameinfo((void *)&sin, sizeof sin, 0, 0, buf, buflen, + strcmp(prots, "udp") ? 0 : NI_DGRAM) < 0) return -1; + + *res = se; + return 0; +} diff --git a/src/network/getsockname.c b/src/network/getsockname.c new file mode 100644 index 00000000..4b1002f8 --- /dev/null +++ b/src/network/getsockname.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int getsockname(int fd, struct sockaddr *addr, socklen_t *len) +{ + unsigned long args[] = { fd, (unsigned long)addr, (unsigned long)len }; + return syscall2(__NR_socketcall, SYS_GETSOCKNAME, (long)args); +} diff --git a/src/network/getsockopt.c b/src/network/getsockopt.c new file mode 100644 index 00000000..8c818863 --- /dev/null +++ b/src/network/getsockopt.c @@ -0,0 +1,13 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int getsockopt(int fd, int level, int optname, void *optval, socklen_t *optlen) +{ + unsigned long args[] = { + fd, level, optname, + (unsigned long)optval, + (unsigned long)optlen + }; + return syscall2(__NR_socketcall, SYS_GETSOCKOPT, (long)args); +} diff --git a/src/network/h_errno.c b/src/network/h_errno.c new file mode 100644 index 00000000..73ead046 --- /dev/null +++ b/src/network/h_errno.c @@ -0,0 +1 @@ +int h_errno; diff --git a/src/network/hstrerror.c b/src/network/hstrerror.c new file mode 100644 index 00000000..b7a6ab6c --- /dev/null +++ b/src/network/hstrerror.c @@ -0,0 +1,16 @@ +#define _GNU_SOURCE +#include <netdb.h> + +static const char msgs[] = + "Host not found\0" + "Try again\0" + "Non-recoverable error\0" + "Address not available\0" + "\0Unknown error"; + +const char *hstrerror(int ecode) +{ + const char *s; + for (s=msgs, ecode--; ecode && *s; ecode--, s++) for (; *s; s++); + return *s ? s : s+1; +} diff --git a/src/network/htonl.c b/src/network/htonl.c new file mode 100644 index 00000000..b21dace0 --- /dev/null +++ b/src/network/htonl.c @@ -0,0 +1,10 @@ +#include <netinet/in.h> + +uint32_t htonl(uint32_t n) +{ + union { + uint8_t b[4]; + uint32_t i; + } u = { { n>>24, n>>16, n>>8, n } }; + return u.i; +} diff --git a/src/network/htons.c b/src/network/htons.c new file mode 100644 index 00000000..522504a5 --- /dev/null +++ b/src/network/htons.c @@ -0,0 +1,10 @@ +#include <netinet/in.h> + +uint16_t htons(uint16_t n) +{ + union { + uint8_t b[2]; + uint16_t s; + } u = { { n>>8, n } }; + return u.s; +} diff --git a/src/network/in6addr_any.c b/src/network/in6addr_any.c new file mode 100644 index 00000000..995387fa --- /dev/null +++ b/src/network/in6addr_any.c @@ -0,0 +1,3 @@ +#include <netinet/in.h> + +const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; diff --git a/src/network/in6addr_loopback.c b/src/network/in6addr_loopback.c new file mode 100644 index 00000000..b96005b8 --- /dev/null +++ b/src/network/in6addr_loopback.c @@ -0,0 +1,3 @@ +#include <netinet/in.h> + +const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; diff --git a/src/network/inet_addr.c b/src/network/inet_addr.c new file mode 100644 index 00000000..84137281 --- /dev/null +++ b/src/network/inet_addr.c @@ -0,0 +1,11 @@ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include "__dns.h" + +in_addr_t inet_addr(const char *p) +{ + struct sockaddr_in sin; + if (__ipparse(&sin, AF_INET, p)) return -1; + return sin.sin_addr.s_addr; +} diff --git a/src/network/inet_aton.c b/src/network/inet_aton.c new file mode 100644 index 00000000..ea4ee165 --- /dev/null +++ b/src/network/inet_aton.c @@ -0,0 +1,7 @@ +#include <sys/socket.h> +#include <arpa/inet.h> + +int inet_aton(const char *cp, struct in_addr *inp) +{ + return inet_pton(AF_INET, cp, (void *)inp) > 0; +} diff --git a/src/network/inet_ntoa.c b/src/network/inet_ntoa.c new file mode 100644 index 00000000..71411e0b --- /dev/null +++ b/src/network/inet_ntoa.c @@ -0,0 +1,10 @@ +#include <arpa/inet.h> +#include <stdio.h> + +char *inet_ntoa(struct in_addr in) +{ + static char buf[16]; + unsigned char *a = (void *)∈ + snprintf(buf, sizeof buf, "%d.%d.%d.%d", a[0], a[1], a[2], a[3]); + return buf; +} diff --git a/src/network/inet_ntop.c b/src/network/inet_ntop.c new file mode 100644 index 00000000..3e8a6db0 --- /dev/null +++ b/src/network/inet_ntop.c @@ -0,0 +1,48 @@ +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +const char *inet_ntop(int af, const void *a0, char *s, socklen_t l) +{ + const unsigned char *a = a0; + int i, j, max, best; + char buf[100]; + + switch (af) { + case AF_INET: + if (snprintf(s, l, "%d.%d.%d.%d", a[0],a[1],a[2],a[3]) < l) + return s; + break; + case AF_INET6: + memset(buf, 'x', sizeof buf); + buf[sizeof buf-1]=0; + snprintf(buf, sizeof buf, + "%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x:%.0x%x", + a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7], + a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15]); + /* Replace longest /(^0|:)[:0]{2,}/ with "::" */ + for (i=best=0, max=2; buf[i]; i++) { + if (i && buf[i] != ':') continue; + j = strspn(buf+i, ":0"); + if (j>max) best=i, max=j; + } + if (max>2) { + buf[best] = buf[best+1] = ':'; + strcpy(buf+best+2, buf+best+max); + } + if (strlen(buf) < l) { + strcpy(s, buf); + return s; + } + break; + default: + errno = EAFNOSUPPORT; + return 0; + } + errno = ENOSPC; + return 0; +} diff --git a/src/network/inet_pton.c b/src/network/inet_pton.c new file mode 100644 index 00000000..349c4025 --- /dev/null +++ b/src/network/inet_pton.c @@ -0,0 +1,31 @@ +#include <sys/socket.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <ctype.h> +#include <errno.h> +#include "__dns.h" + +int inet_pton(int af, const char *s, void *a0) +{ + unsigned char *a = a0; + const char *z; + unsigned long x; + int i; + + /* Reimplement this because inet_pton cannot accept special v4 forms */ + if (af==AF_INET) { + for (i=0; i<4 && *s; i++) { + a[i] = x = strtoul(s, (char **)&z, 10); + if (!isdigit(*s) || z==s || (*z && *z != '.') || x>255) + return 0; + s=z+1; + } + return 0; + } else if (af==AF_INET6) { + return !__ipparse(a, AF_INET6, s); + } + + errno = EAFNOSUPPORT; + return 0; +} diff --git a/src/network/listen.c b/src/network/listen.c new file mode 100644 index 00000000..7c8c1a8a --- /dev/null +++ b/src/network/listen.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int listen(int fd, int backlog) +{ + unsigned long args[] = { fd, backlog }; + return syscall2(__NR_socketcall, SYS_LISTEN, (long)args); +} diff --git a/src/network/ntohl.c b/src/network/ntohl.c new file mode 100644 index 00000000..64379196 --- /dev/null +++ b/src/network/ntohl.c @@ -0,0 +1,10 @@ +#include <netinet/in.h> + +uint32_t ntohl(uint32_t n) +{ + union { + uint32_t i; + uint8_t b[4]; + } u = { n }; + return (u.b[0]<<24) | (u.b[1]<<16) | (u.b[2]<<8) | u.b[3]; +} diff --git a/src/network/ntohs.c b/src/network/ntohs.c new file mode 100644 index 00000000..3544a479 --- /dev/null +++ b/src/network/ntohs.c @@ -0,0 +1,10 @@ +#include <netinet/in.h> + +uint16_t ntohs(uint16_t n) +{ + union { + uint16_t s; + uint8_t b[2]; + } u = { n }; + return (u.b[0]<<8) | u.b[1]; +} diff --git a/src/network/proto.c b/src/network/proto.c new file mode 100644 index 00000000..8c25c53a --- /dev/null +++ b/src/network/proto.c @@ -0,0 +1,58 @@ +#include <netdb.h> +#include <stdio.h> +#include <string.h> + +/* do we really need all these?? */ + +static int idx; +static const unsigned char protos[][6] = { + "\000ip", + "\001icmp", + "\002igmp", + "\003ggp", + "\006tcp", + "\014pup", + "\021udp", + "\026idp", + "\377raw" + "\0\0" +}; + +void endprotoent(void) +{ + idx = 0; +} + +void setprotoent(int stayopen) +{ + idx = 0; +} + +struct protoent *getprotoent(void) +{ + static struct protoent p; + static const char *aliases; + if (!protos[idx][1]) return NULL; + p.p_proto = protos[idx][0]; + p.p_name = (char *)protos[idx++]+1; + p.p_aliases = (char **)&aliases; + return &p; +} + +struct protoent *getprotobyname(const char *name) +{ + struct protoent *p; + endprotoent(); + do p = getprotoent(); + while (p && strcmp(name, p->p_name)); + return p; +} + +struct protoent *getprotobynumber(int num) +{ + struct protoent *p; + endprotoent(); + do p = getprotoent(); + while (p && p->p_proto != num); + return p; +} diff --git a/src/network/recv.c b/src/network/recv.c new file mode 100644 index 00000000..521a4b19 --- /dev/null +++ b/src/network/recv.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t recv(int fd, void *buf, size_t len, int flags) +{ + unsigned long args[] = { fd, (unsigned long)buf, len, flags }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_RECV, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/recvfrom.c b/src/network/recvfrom.c new file mode 100644 index 00000000..df50114b --- /dev/null +++ b/src/network/recvfrom.c @@ -0,0 +1,17 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t recvfrom(int fd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *alen) +{ + unsigned long args[] = { + fd, (unsigned long)buf, len, flags, + (unsigned long)addr, (unsigned long)alen + }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_RECVFROM, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/recvmsg.c b/src/network/recvmsg.c new file mode 100644 index 00000000..ead16f9c --- /dev/null +++ b/src/network/recvmsg.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t recvmsg(int fd, struct msghdr *msg, int flags) +{ + unsigned long args[] = { fd, (unsigned long)msg, flags }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_RECVMSG, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/res_init.c b/src/network/res_init.c new file mode 100644 index 00000000..cbd5b155 --- /dev/null +++ b/src/network/res_init.c @@ -0,0 +1,4 @@ +int res_init() +{ + return 0; +} diff --git a/src/network/res_query.c b/src/network/res_query.c new file mode 100644 index 00000000..4ebeb102 --- /dev/null +++ b/src/network/res_query.c @@ -0,0 +1,20 @@ +#include <netdb.h> +#include "__dns.h" + +int res_query(const char *name, int class, int type, unsigned char *dest, int len) +{ + if (class != 1 || len < 512) + return -1; + switch(__dns_doqueries(dest, name, &type, 1)) { + case EAI_NONAME: + h_errno = HOST_NOT_FOUND; + return -1; + case EAI_AGAIN: + h_errno = TRY_AGAIN; + return -1; + case EAI_FAIL: + h_errno = NO_RECOVERY; + return -1; + } + return 512; +} diff --git a/src/network/send.c b/src/network/send.c new file mode 100644 index 00000000..d72fb03c --- /dev/null +++ b/src/network/send.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t send(int fd, const void *buf, size_t len, int flags) +{ + unsigned long args[] = { fd, (unsigned long)buf, len, flags }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_SEND, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/sendmsg.c b/src/network/sendmsg.c new file mode 100644 index 00000000..ea2fe482 --- /dev/null +++ b/src/network/sendmsg.c @@ -0,0 +1,14 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) +{ + unsigned long args[] = { fd, (unsigned long)msg, flags }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_SENDMSG, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/sendto.c b/src/network/sendto.c new file mode 100644 index 00000000..5d224b0b --- /dev/null +++ b/src/network/sendto.c @@ -0,0 +1,17 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" +#include "libc.h" + +ssize_t sendto(int fd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t alen) +{ + unsigned long args[] = { + fd, (unsigned long)buf, len, flags, + (unsigned long)addr, alen + }; + ssize_t r; + CANCELPT_BEGIN; + r = syscall2(__NR_socketcall, SYS_SENDTO, (long)args); + CANCELPT_END; + return r; +} diff --git a/src/network/serv.c b/src/network/serv.c new file mode 100644 index 00000000..5ade6ad1 --- /dev/null +++ b/src/network/serv.c @@ -0,0 +1,16 @@ +#include <netdb.h> +#include <stdio.h> +#include <string.h> + +void endservent(void) +{ +} + +void setservent(int stayopen) +{ +} + +struct servent *getservent(void) +{ + return 0; +} diff --git a/src/network/setsockopt.c b/src/network/setsockopt.c new file mode 100644 index 00000000..b50303b8 --- /dev/null +++ b/src/network/setsockopt.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + unsigned long args[] = { fd, level, optname, (unsigned long)optval, optlen }; + return syscall2(__NR_socketcall, SYS_SETSOCKOPT, (long)args); +} diff --git a/src/network/shutdown.c b/src/network/shutdown.c new file mode 100644 index 00000000..91950c8a --- /dev/null +++ b/src/network/shutdown.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int shutdown(int fd, int how) +{ + unsigned long args[] = { fd, how }; + return syscall2(__NR_socketcall, SYS_SHUTDOWN, (long)args); +} diff --git a/src/network/sockatmark.c b/src/network/sockatmark.c new file mode 100644 index 00000000..5328a855 --- /dev/null +++ b/src/network/sockatmark.c @@ -0,0 +1,11 @@ +#include <sys/socket.h> +#include <sys/ioctl.h> +#include "socketcall.h" + +int sockatmark(int s) +{ + int ret; + if (ioctl(s, SIOCATMARK, &ret) < 0) + return -1; + return ret; +} diff --git a/src/network/socket.c b/src/network/socket.c new file mode 100644 index 00000000..afaeb411 --- /dev/null +++ b/src/network/socket.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int socket(int domain, int type, int protocol) +{ + unsigned long args[] = { domain, type, protocol }; + return syscall2(__NR_socketcall, SYS_SOCKET, (long)args); +} diff --git a/src/network/socketcall.h b/src/network/socketcall.h new file mode 100644 index 00000000..9ae98587 --- /dev/null +++ b/src/network/socketcall.h @@ -0,0 +1,24 @@ +#define SYS_SOCKET 1 +#define SYS_BIND 2 +#define SYS_CONNECT 3 +#define SYS_LISTEN 4 +#define SYS_ACCEPT 5 +#define SYS_GETSOCKNAME 6 +#define SYS_GETPEERNAME 7 +#define SYS_SOCKETPAIR 8 +#define SYS_SEND 9 +#define SYS_RECV 10 +#define SYS_SENDTO 11 +#define SYS_RECVFROM 12 +#define SYS_SHUTDOWN 13 +#define SYS_SETSOCKOPT 14 +#define SYS_GETSOCKOPT 15 +#define SYS_SENDMSG 16 +#define SYS_RECVMSG 17 + +#define FIOSETOWN 0x8901 +#define SIOCSPGRP 0x8902 +#define FIOGETOWN 0x8903 +#define SIOCGPGRP 0x8904 +#define SIOCATMARK 0x8905 +#define SIOCGSTAMP 0x8906 diff --git a/src/network/socketpair.c b/src/network/socketpair.c new file mode 100644 index 00000000..65a47fd9 --- /dev/null +++ b/src/network/socketpair.c @@ -0,0 +1,9 @@ +#include <sys/socket.h> +#include "syscall.h" +#include "socketcall.h" + +int socketpair(int domain, int type, int protocol, int fd[2]) +{ + unsigned long args[] = { domain, type, protocol, (unsigned long)fd }; + return syscall2(__NR_socketcall, SYS_SOCKETPAIR, (long)args); +} diff --git a/src/passwd/getgr_r.c b/src/passwd/getgr_r.c new file mode 100644 index 00000000..5b1333e3 --- /dev/null +++ b/src/passwd/getgr_r.c @@ -0,0 +1,53 @@ +#include "pwf.h" + +#define FIX(x) (gr->gr_##x = gr->gr_##x-line+buf) + +static int getgr_r(const char *name, gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) +{ + FILE *f; + char *line = 0; + size_t len = 0; + char **mem = 0; + size_t nmem = 0; + int rv = 0; + size_t i; + + f = fopen("/etc/group", "rb"); + if (!f) return errno; + + *res = 0; + while (__getgrent_a(f, gr, &line, &len, &mem, &nmem)) { + if (name && !strcmp(name, gr->gr_name) + || !name && gr->gr_gid == gid) { + if (size < len + nmem*sizeof(char *) + 32) { + rv = ERANGE; + break; + } + *res = gr; + buf += (16-(uintptr_t)buf)%16; + gr->gr_mem = (void *)buf; + buf += nmem*sizeof(char *); + memcpy(buf, line, len); + FIX(name); + FIX(passwd); + for (i=0; mem[i]; i++) + gr->gr_mem[i] = mem[i]-line+buf; + gr->gr_mem[i] = 0; + break; + } + } + free(mem); + free(line); + fclose(f); + return rv; +} + +int getgrnam_r(const char *name, struct group *gr, char *buf, size_t size, struct group **res) +{ + return getgr_r(name, 0, gr, buf, size, res); +} + +int getgruid_r(gid_t gid, struct group *gr, char *buf, size_t size, struct group **res) +{ + return getgr_r(0, gid, gr, buf, size, res); +} diff --git a/src/passwd/getgrent.c b/src/passwd/getgrent.c new file mode 100644 index 00000000..e9d25eba --- /dev/null +++ b/src/passwd/getgrent.c @@ -0,0 +1,39 @@ +#include "pwf.h" + +static FILE *f; + +void setgrent() +{ + if (f) fclose(f); + f = 0; +} + +weak_alias(setgrent, endgrent); + +struct group *getgrent() +{ + static char *line, **mem; + static struct group gr; + size_t size=0, nmem=0; + if (!f) f = fopen("/etc/group", "rb"); + if (!f) return 0; + return __getgrent_a(f, &gr, &line, &size, &mem, &nmem); +} + +struct group *getgrgid(gid_t gid) +{ + struct group *gr; + setgrent(); + while ((gr=getgrent()) && gr->gr_gid != gid); + endgrent(); + return gr; +} + +struct group *getgrnam(const char *name) +{ + struct group *gr; + setgrent(); + while ((gr=getgrent()) && strcmp(gr->gr_name, name)); + endgrent(); + return gr; +} diff --git a/src/passwd/getgrent_a.c b/src/passwd/getgrent_a.c new file mode 100644 index 00000000..ccb51d52 --- /dev/null +++ b/src/passwd/getgrent_a.c @@ -0,0 +1,46 @@ +#include "pwf.h" + +struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem) +{ + ssize_t l; + char *s, *mems; + size_t i; + + for (;;) { + if ((l=getline(line, size, f)) < 0) { + free(*line); + *line = 0; + return 0; + } + line[0][l-1] = 0; + + s = line[0]; + gr->gr_name = s++; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; gr->gr_passwd = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; gr->gr_gid = atoi(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; mems = s; + break; + } + + for (*nmem=!!*s; *s; s++) + if (*s==',') ++*nmem; + free(*mem); + *mem = calloc(sizeof(char *), *nmem+1); + if (!*mem) { + free(*line); + *line = 0; + return 0; + } + mem[0][0] = mems; + for (s=mems, i=0; *s; s++) + if (*s==',') *s++ = 0, mem[0][++i] = s; + mem[0][++i] = 0; + gr->gr_mem = *mem; + return gr; +} diff --git a/src/passwd/getpw_r.c b/src/passwd/getpw_r.c new file mode 100644 index 00000000..7b331e8a --- /dev/null +++ b/src/passwd/getpw_r.c @@ -0,0 +1,46 @@ +#include "pwf.h" + +#define FIX(x) (pw->pw_##x = pw->pw_##x-line+buf) + +static int getpw_r(const char *name, uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res) +{ + FILE *f; + char *line = 0; + size_t len = 0; + int rv = 0; + + f = fopen("/etc/passwd", "rb"); + if (!f) return errno; + + *res = 0; + while (__getpwent_a(f, pw, &line, &len)) { + if (name && !strcmp(name, pw->pw_name) + || !name && pw->pw_uid == uid) { + if (size < len) { + rv = ERANGE; + break; + } + *res = pw; + memcpy(buf, line, len); + FIX(name); + FIX(passwd); + FIX(gecos); + FIX(dir); + FIX(shell); + break; + } + } + free(line); + fclose(f); + return rv; +} + +int getpwnam_r(const char *name, struct passwd *pw, char *buf, size_t size, struct passwd **res) +{ + return getpw_r(name, 0, pw, buf, size, res); +} + +int getpwuid_r(uid_t uid, struct passwd *pw, char *buf, size_t size, struct passwd **res) +{ + return getpw_r(0, uid, pw, buf, size, res); +} diff --git a/src/passwd/getpwent.c b/src/passwd/getpwent.c new file mode 100644 index 00000000..dabd411a --- /dev/null +++ b/src/passwd/getpwent.c @@ -0,0 +1,39 @@ +#include "pwf.h" + +static FILE *f; + +void setpwent() +{ + if (f) fclose(f); + f = 0; +} + +weak_alias(setpwent, endpwent); + +struct passwd *getpwent() +{ + static char *line; + static struct passwd pw; + size_t size=0; + if (!f) f = fopen("/etc/passwd", "rb"); + if (!f) return 0; + return __getpwent_a(f, &pw, &line, &size); +} + +struct passwd *getpwuid(uid_t uid) +{ + struct passwd *pw; + setpwent(); + while ((pw=getpwent()) && pw->pw_uid != uid); + endpwent(); + return pw; +} + +struct passwd *getpwnam(const char *name) +{ + struct passwd *pw; + setpwent(); + while ((pw=getpwent()) && strcmp(pw->pw_name, name)); + endpwent(); + return pw; +} diff --git a/src/passwd/getpwent_a.c b/src/passwd/getpwent_a.c new file mode 100644 index 00000000..aaf84edd --- /dev/null +++ b/src/passwd/getpwent_a.c @@ -0,0 +1,37 @@ +#include "pwf.h" + +struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size) +{ + ssize_t l; + char *s; + for (;;) { + if ((l=getline(line, size, f)) < 0) { + free(*line); + *line = 0; + return 0; + } + line[0][l-1] = 0; + + s = line[0]; + pw->pw_name = s++; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_passwd = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_uid = atoi(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_gid = atoi(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_gecos = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_dir = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; pw->pw_shell = s; + return pw; + } +} diff --git a/src/passwd/getspent.c b/src/passwd/getspent.c new file mode 100644 index 00000000..8574a480 --- /dev/null +++ b/src/passwd/getspent.c @@ -0,0 +1,14 @@ +#include "pwf.h" + +void setspent() +{ +} + +void endspent() +{ +} + +struct spwd *getspent() +{ + return 0; +} diff --git a/src/passwd/getspnam.c b/src/passwd/getspnam.c new file mode 100644 index 00000000..041f8965 --- /dev/null +++ b/src/passwd/getspnam.c @@ -0,0 +1,17 @@ +#include "pwf.h" + +#define LINE_LIM 256 + +struct spwd *getspnam(const char *name) +{ + static struct spwd sp; + static char *line; + struct spwd *res; + int e; + + if (!line) line = malloc(LINE_LIM); + if (!line) return 0; + e = getspnam_r(name, &sp, line, LINE_LIM, &res); + if (e) errno = e; + return res; +} diff --git a/src/passwd/getspnam_r.c b/src/passwd/getspnam_r.c new file mode 100644 index 00000000..1dd39ce0 --- /dev/null +++ b/src/passwd/getspnam_r.c @@ -0,0 +1,89 @@ +#include <fcntl.h> +#include <unistd.h> +#include "pwf.h" + +/* This implementation support Openwall-style TCB passwords in place of + * traditional shadow, if the appropriate directories and files exist. + * Thus, it is careful to avoid following symlinks or blocking on fifos + * which a malicious user might create in place of his or her TCB shadow + * file. It also avoids any allocation to prevent memory-exhaustion + * attacks via huge TCB shadow files. */ + +int getspnam_r(const char *name, struct spwd *sp, char *buf, size_t size, struct spwd **res) +{ + char path[20+NAME_MAX]; + FILE *f = 0; + int rv = 0; + int fd; + size_t k, l = strlen(name); + char *s; + int skip = 0; + + *res = 0; + + /* Disallow potentially-malicious user names */ + if (*name=='.' || strchr(name, '/') || !l) + return EINVAL; + + /* Buffer size must at least be able to hold name, plus some.. */ + if (size < l+100) return ERANGE; + + /* Protect against truncation */ + if (snprintf(path, sizeof path, "/etc/tcb/%s/shadow", name) >= sizeof path) + return EINVAL; + + fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK); + if (fd >= 0) { + f = fdopen(fd, "rb"); + if (!f) { + close(fd); + return errno; + } + } else { + f = fopen("/etc/shadow", "rb"); + if (!f) return errno; + } + + while (fgets(buf, size, f) && (k=strlen(buf))>0) { + if (skip || strncmp(name, buf, l)) { + skip = buf[k-1] != '\n'; + continue; + } + if (buf[k-1] != '\n') { + rv = ERANGE; + break; + } + buf[k-1] = 0; + + s = buf; + sp->sp_namp = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_pwdp = s; + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_lstchg = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_min = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_max = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_warn = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_inact = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_expire = atol(s); + if (!(s = strchr(s, ':'))) continue; + + *s++ = 0; sp->sp_flag = atol(s); + *res = sp; + break; + } + fclose(f); + return rv; +} diff --git a/src/passwd/lckpwdf.c b/src/passwd/lckpwdf.c new file mode 100644 index 00000000..2feda617 --- /dev/null +++ b/src/passwd/lckpwdf.c @@ -0,0 +1,11 @@ +#include <shadow.h> + +int lckpwdf() +{ + return 0; +} + +int ulckpwdf() +{ + return 0; +} diff --git a/src/passwd/pwf.h b/src/passwd/pwf.h new file mode 100644 index 00000000..0a76ef80 --- /dev/null +++ b/src/passwd/pwf.h @@ -0,0 +1,13 @@ +#include <pwd.h> +#include <grp.h> +#include <shadow.h> +#include <stdio.h> +#include <errno.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> +#include "libc.h" + +struct passwd *__getpwent_a(FILE *f, struct passwd *pw, char **line, size_t *size); +struct spwd *__getspent_a(FILE *f, struct spwd *sp, char **line, size_t *size); +struct group *__getgrent_a(FILE *f, struct group *gr, char **line, size_t *size, char ***mem, size_t *nmem); diff --git a/src/prng/__rand48_step.c b/src/prng/__rand48_step.c new file mode 100644 index 00000000..755b4f2f --- /dev/null +++ b/src/prng/__rand48_step.c @@ -0,0 +1,14 @@ +#include <stdlib.h> +#include <inttypes.h> + +uint64_t __rand48_step(unsigned short *xi, unsigned short *lc) +{ + uint64_t a, x; + x = xi[0] | xi[1]<<16 | xi[2]+0ULL<<32; + a = lc[0] | lc[1]<<16 | lc[2]+0ULL<<32; + x = a*x + lc[3]; + xi[0] = x; + xi[1] = x>>16; + xi[2] = x>>32; + return x & 0xffffffffffffull; +} diff --git a/src/prng/__seed48.c b/src/prng/__seed48.c new file mode 100644 index 00000000..05a4539e --- /dev/null +++ b/src/prng/__seed48.c @@ -0,0 +1 @@ +unsigned short __seed48[7] = { 0, 0, 0, 0xe66d, 0xdeec, 0x5, 0xb }; diff --git a/src/prng/drand48.c b/src/prng/drand48.c new file mode 100644 index 00000000..d808353c --- /dev/null +++ b/src/prng/drand48.c @@ -0,0 +1,19 @@ +#include <stdlib.h> +#include <inttypes.h> + +uint64_t __rand48_step(unsigned short *xi, unsigned short *lc); +extern unsigned short __seed48[7]; + +double erand48(unsigned short s[3]) +{ + union { + uint64_t u; + double f; + } x = { 0x3ff0000000000000ULL | __rand48_step(s, __seed48+3)<<4 }; + return x.f - 1.0; +} + +double drand48(void) +{ + return erand48(__seed48); +} diff --git a/src/prng/lcong48.c b/src/prng/lcong48.c new file mode 100644 index 00000000..32b27d42 --- /dev/null +++ b/src/prng/lcong48.c @@ -0,0 +1,9 @@ +#include <stdlib.h> +#include <string.h> + +extern unsigned short __seed48[7]; + +void lcong48(unsigned short p[7]) +{ + memcpy(__seed48, p, sizeof __seed48); +} diff --git a/src/prng/lrand48.c b/src/prng/lrand48.c new file mode 100644 index 00000000..a3c4e4e2 --- /dev/null +++ b/src/prng/lrand48.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include <inttypes.h> + +uint64_t __rand48_step(unsigned short *xi, unsigned short *lc); +extern unsigned short __seed48[7]; + +long nrand48(unsigned short s[3]) +{ + return __rand48_step(s, __seed48+3) >> 17; +} + +long lrand48(void) +{ + return nrand48(__seed48); +} diff --git a/src/prng/mrand48.c b/src/prng/mrand48.c new file mode 100644 index 00000000..ee650fc3 --- /dev/null +++ b/src/prng/mrand48.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include <inttypes.h> + +uint64_t __rand48_step(unsigned short *xi, unsigned short *lc); +extern unsigned short __seed48[7]; + +long jrand48(unsigned short s[3]) +{ + return __rand48_step(s, __seed48+3) >> 16; +} + +long mrand48(void) +{ + return jrand48(__seed48); +} diff --git a/src/prng/rand.c b/src/prng/rand.c new file mode 100644 index 00000000..e3ce6347 --- /dev/null +++ b/src/prng/rand.c @@ -0,0 +1,13 @@ +#include <stdlib.h> + +static unsigned seed; + +void srand(unsigned s) +{ + seed = s-1; +} + +int rand(void) +{ + return (seed = (seed+1) * 1103515245 + 12345 - 1)+1 & 0x7fffffff; +} diff --git a/src/prng/rand_r.c b/src/prng/rand_r.c new file mode 100644 index 00000000..e96cfba9 --- /dev/null +++ b/src/prng/rand_r.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +int rand_r(unsigned *seed) +{ + return (*seed = *seed * 1103515245 + 12345) & 0x7fffffff; +} diff --git a/src/prng/random.c b/src/prng/random.c new file mode 100644 index 00000000..e6b7fd1f --- /dev/null +++ b/src/prng/random.c @@ -0,0 +1,8 @@ +#include <stdlib.h> + +/* FIXME */ + +long random() +{ + return rand(); +} diff --git a/src/prng/seed48.c b/src/prng/seed48.c new file mode 100644 index 00000000..e0699c09 --- /dev/null +++ b/src/prng/seed48.c @@ -0,0 +1,12 @@ +#include <stdlib.h> +#include <string.h> + +extern unsigned short __seed48[7]; + +unsigned short *seed48(unsigned short *s) +{ + static unsigned short p[3]; + memcpy(p, __seed48, sizeof p); + memcpy(__seed48, s, sizeof p); + return p; +} diff --git a/src/prng/srand48.c b/src/prng/srand48.c new file mode 100644 index 00000000..0a56f6a0 --- /dev/null +++ b/src/prng/srand48.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +void srand48(long seed) +{ + seed48((unsigned short [3]){ 0x330e, seed, seed>>16 }); +} diff --git a/src/prng/srandom.c b/src/prng/srandom.c new file mode 100644 index 00000000..77f4dcbd --- /dev/null +++ b/src/prng/srandom.c @@ -0,0 +1,8 @@ +#include <stdlib.h> + +/* FIXME */ + +void srandom(unsigned seed) +{ + return srand(seed); +} diff --git a/src/process/execl.c b/src/process/execl.c new file mode 100644 index 00000000..4c6eaa94 --- /dev/null +++ b/src/process/execl.c @@ -0,0 +1,20 @@ +#include <unistd.h> +#include <stdarg.h> + +int execl(const char *path, ...) +{ + int argc; + va_list ap; + va_start(ap, path); + for (argc=0; va_arg(ap, const char *); argc++); + va_end(ap); + { + int i; + char *argv[argc+1]; + va_start(ap, path); + for (i=0; i<argc; i++) + argv[i] = va_arg(ap, char *); + argv[i] = NULL; + return execv(path, argv); + } +} diff --git a/src/process/execle.c b/src/process/execle.c new file mode 100644 index 00000000..37f629d9 --- /dev/null +++ b/src/process/execle.c @@ -0,0 +1,22 @@ +#include <unistd.h> +#include <stdarg.h> + +int execle(const char *path, ...) +{ + int argc; + va_list ap; + va_start(ap, path); + for (argc=0; va_arg(ap, const char *); argc++); + va_end(ap); + { + int i; + char *argv[argc+1]; + char **envp; + va_start(ap, path); + for (i=0; i<argc; i++) + argv[i] = va_arg(ap, char *); + argv[i] = NULL; + envp = va_arg(ap, char **); + return execve(path, argv, envp); + } +} diff --git a/src/process/execlp.c b/src/process/execlp.c new file mode 100644 index 00000000..33fb0f7f --- /dev/null +++ b/src/process/execlp.c @@ -0,0 +1,20 @@ +#include <unistd.h> +#include <stdarg.h> + +int execlp(const char *file, ...) +{ + int argc; + va_list ap; + va_start(ap, file); + for (argc=0; va_arg(ap, const char *); argc++); + va_end(ap); + { + int i; + char *argv[argc+1]; + va_start(ap, file); + for (i=0; i<argc; i++) + argv[i] = va_arg(ap, char *); + argv[i] = NULL; + return execvp(file, argv); + } +} diff --git a/src/process/execv.c b/src/process/execv.c new file mode 100644 index 00000000..2ac0dec0 --- /dev/null +++ b/src/process/execv.c @@ -0,0 +1,8 @@ +#include <unistd.h> + +extern char **__environ; + +int execv(const char *path, char *const argv[]) +{ + return execve(path, argv, __environ); +} diff --git a/src/process/execve.c b/src/process/execve.c new file mode 100644 index 00000000..2a0b62d6 --- /dev/null +++ b/src/process/execve.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include "syscall.h" + +int execve(const char *path, char *const argv[], char *const envp[]) +{ + /* do we need to use environ if envp is null? */ + return syscall3(__NR_execve, (long)path, (long)argv, (long)envp); +} diff --git a/src/process/execvp.c b/src/process/execvp.c new file mode 100644 index 00000000..d799ddae --- /dev/null +++ b/src/process/execvp.c @@ -0,0 +1,34 @@ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +extern char **__environ; + +int execvp(const char *file, char *const argv[]) +{ + const char *p, *z, *path = getenv("PATH"); + int l; + + if (strchr(file, '/')) + return execve(file, argv, __environ); + + /* FIXME: integer overflows */ + if (!path) path = "/usr/local/bin:/bin:/usr/bin"; + l = strlen(file) + strlen(path) + 2; + + for(p=path; p && *p; p=z) { + char b[l]; + z = strchr(p, ':'); + if (z) { + memcpy(b, p, z-p); + b[z++-p] = 0; + } else strcpy(b, p); + strcat(b, "/"); + strcat(b, file); + if (!access(b, X_OK)) + return execve(b, argv, __environ); + } + errno = ENOENT; + return -1; +} diff --git a/src/process/fork.c b/src/process/fork.c new file mode 100644 index 00000000..1213f0f5 --- /dev/null +++ b/src/process/fork.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include "syscall.h" + +/* FIXME: add support for atfork stupidity */ + +pid_t fork(void) +{ + return syscall0(__NR_fork); +} diff --git a/src/process/system.c b/src/process/system.c new file mode 100644 index 00000000..0f1c07b5 --- /dev/null +++ b/src/process/system.c @@ -0,0 +1,45 @@ +#include <unistd.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/wait.h> +#include <errno.h> + +int system(const char *cmd) +{ + pid_t pid; + sigset_t old, new; + struct sigaction sa, oldint, oldquit; + int status; + + if (!cmd) return 1; + + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + sigaction(SIGINT, &sa, &oldint); + sigaction(SIGQUIT, &sa, &oldquit); + sigaddset(&sa.sa_mask, SIGCHLD); + sigprocmask(SIG_BLOCK, &new, &old); + + pid = fork(); + if (pid <= 0) { + sigaction(SIGINT, &oldint, NULL); + sigaction(SIGQUIT, &oldquit, NULL); + sigprocmask(SIG_SETMASK, &old, NULL); + if (pid == 0) { + execl("/bin/sh", "sh", "-c", cmd, (char *)0); + _exit(127); + } + return -1; + } + while (waitpid(pid, &status, 0) == -1) + if (errno != EINTR) { + status = -1; + break; + } + sigaction(SIGINT, &oldint, NULL); + sigaction(SIGQUIT, &oldquit, NULL); + sigprocmask(SIG_SETMASK, &old, NULL); + return status; +} diff --git a/src/process/vfork.c b/src/process/vfork.c new file mode 100644 index 00000000..32a7a6ed --- /dev/null +++ b/src/process/vfork.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t vfork(void) +{ + /* vfork syscall cannot be made from C code */ + return syscall0(__NR_fork); +} diff --git a/src/process/wait.c b/src/process/wait.c new file mode 100644 index 00000000..34da102d --- /dev/null +++ b/src/process/wait.c @@ -0,0 +1,6 @@ +#include <sys/wait.h> + +pid_t wait(int *status) +{ + return waitpid((pid_t)-1, status, 0); +} diff --git a/src/process/waitid.c b/src/process/waitid.c new file mode 100644 index 00000000..0ec0d55c --- /dev/null +++ b/src/process/waitid.c @@ -0,0 +1,7 @@ +#include <sys/wait.h> +#include "syscall.h" + +int waitid(idtype_t type, id_t id, siginfo_t *info, int options) +{ + return syscall5(__NR_waitid, type, id, (long)info, options, 0); +} diff --git a/src/process/waitpid.c b/src/process/waitpid.c new file mode 100644 index 00000000..0ddcd15a --- /dev/null +++ b/src/process/waitpid.c @@ -0,0 +1,7 @@ +#include <sys/wait.h> +#include "syscall.h" + +pid_t waitpid(pid_t pid, int *status, int options) +{ + return syscall4(__NR_wait4, pid, (long)status, options, 0); +} diff --git a/src/regex/fnmatch.c b/src/regex/fnmatch.c new file mode 100644 index 00000000..5f2fccdb --- /dev/null +++ b/src/regex/fnmatch.c @@ -0,0 +1,150 @@ +#include <fnmatch.h> +#include <wctype.h> +#include <string.h> +#include <wchar.h> +#include <stdlib.h> +#include <limits.h> + +static int next(const char **s) +{ + wchar_t c; + int l = mbtowc(&c, *s, MB_LEN_MAX); + /* hack to allow literal matches of invalid byte sequences */ + if (l < 0) return (unsigned char)*(*s)++ - 0x100; + *s += l; + return c; +} + +#define BRACKET_ERROR -0x100 +#define BRACKET_NOCHAR -0x101 + +static int bracket_next(const char **s) +{ + int c; + int type; + if (**s == '[') { + type = *(*s+1); + if (type == '.' || type == '=') { + *s += 2; + c = next(s); + if (c <= 0) return BRACKET_ERROR; + if (**s == type && *(*s+1) == ']') { + *s += 2; + return c; + } + for (; **s && (**s != type || *(*s+1) != ']'); (*s)++); + if (!**s) return BRACKET_ERROR; + *s += 2; + return BRACKET_NOCHAR; + } + } + c = next(s); + if (c <= 0) return BRACKET_ERROR; + return c; +} + +#define __FNM_CONT 0x8000 + +int fnmatch(const char *p, const char *s, int flags) +{ + int c, d, k; + int not; + int match; + int first; + int no_slash = (flags & FNM_PATHNAME) ? '/' : 0; + int no_period = (flags & FNM_PERIOD) && !(flags & __FNM_CONT) ? '.' : 0x100; + + flags |= __FNM_CONT; + + while ((c = *p++)) { + switch (c) { + case '?': + k = next(&s); + if (!k || k == no_period || k == no_slash) + return FNM_NOMATCH; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + c = *p++; + goto literal; + } + if (*s++ != c) return FNM_NOMATCH; + break; + case '*': + for (; *p == '*'; p++); + if (*p && !*s) return FNM_NOMATCH; + if (*s == no_period) + return FNM_NOMATCH; + if (!*p && (!no_slash || !strchr(s, no_slash))) + return 0; + for (; *s; s++) + if (!fnmatch(p, s, flags)) + return 0; + else if (*s == no_slash) + break; + return FNM_NOMATCH; + case '[': + not = (*p == '!' || *p == '^'); + if (not) p++; + k = next(&s); + if (!k || k == no_slash || k == no_period) + return FNM_NOMATCH; + match = 0; + first = 1; + for (;;) { + if (!*p) return FNM_NOMATCH; + if (*p == ']' && !first) break; + first = 0; + if (*p == '[' && *(p+1) == ':') { + const char *z; + p += 2; + for (z=p; *z && (*z != ':' || *(z+1) != ']'); z++); + if (!*z || z-p > 32) { /* FIXME: symbolic const? */ + return FNM_NOMATCH; + } else { + char class[z-p+1]; + memcpy(class, p, z-p); + class[z-p] = 0; + if (iswctype(k, wctype(class))) + match = 1; + } + p = z+2; + continue; + } + c = bracket_next(&p); + if (c == BRACKET_ERROR) + return FNM_NOMATCH; + if (c == BRACKET_NOCHAR) + continue; + if (*p == '-' && *(p+1) != ']') { + p++; + d = bracket_next(&p); + if (d == BRACKET_ERROR) + return FNM_NOMATCH; + if (d == BRACKET_NOCHAR) + continue; + if (k >= c && k <= d) + match = 1; + continue; + } + if (k == c) match = 1; + } + p++; + if (not == match) + return FNM_NOMATCH; + break; + default: + literal: + if (*s++ != c) + return FNM_NOMATCH; + if (c == no_slash && (flags & FNM_PERIOD)) { + no_period = '.'; + continue; + } + break; + } + no_period = 0x100; + } + if (*s) return FNM_NOMATCH; + return 0; +} diff --git a/src/regex/glob.c b/src/regex/glob.c new file mode 100644 index 00000000..9a70f0bc --- /dev/null +++ b/src/regex/glob.c @@ -0,0 +1,238 @@ +#include <glob.h> +#include <fnmatch.h> +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <stddef.h> +#include <unistd.h> +#include <stdio.h> +#include "libc.h" + +struct match +{ + struct match *next; + char name[1]; +}; + +static int is_literal(const char *p, int useesc) +{ + int bracket = 0; + for (; *p; p++) { + switch (*p) { + case '\\': + if (!useesc) break; + case '?': + case '*': + return 0; + case '[': + bracket = 1; + break; + case ']': + if (bracket) return 0; + break; + } + } + return 1; +} + +static int append(struct match **tail, const char *name, size_t len, int mark) +{ + struct match *new = malloc(sizeof(struct match) + len + 1); + if (!new) return -1; + (*tail)->next = new; + new->next = NULL; + strcpy(new->name, name); + if (mark) strcat(new->name, "/"); + *tail = new; + return 0; +} + +static int match_in_dir(const char *d, const char *p, int flags, int (*errfunc)(const char *path, int err), struct match **tail) +{ + DIR *dir; + long long de_buf[(sizeof(struct dirent) + NAME_MAX + sizeof(long long))/sizeof(long long)]; + struct dirent *de; + char pat[strlen(p)+1]; + char *p2; + size_t l = strlen(d); + int literal; + int fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | FNM_PERIOD; + int error; + + if ((p2 = strchr(p, '/'))) { + strcpy(pat, p); + pat[p2-p] = 0; + for (; *p2 == '/'; p2++); + p = pat; + } + literal = is_literal(p, !(flags & GLOB_NOESCAPE)); + if (*d == '/' && !*(d+1)) l = 0; + + /* rely on opendir failing for nondirectory objects */ + dir = opendir(*d ? d : "."); + error = errno; + if (!dir) { + /* this is not an error -- we let opendir call stat for us */ + if (error == ENOTDIR) return 0; + if (error == EACCES && !*p) { + struct stat st; + if (!stat(d, &st) && S_ISDIR(st.st_mode)) { + if (append(tail, d, l, l)) + return GLOB_NOSPACE; + return 0; + } + } + if (errfunc(d, error) || (flags & GLOB_ERR)) + return GLOB_ABORTED; + return 0; + } + if (!*p) { + error = append(tail, d, l, l) ? GLOB_NOSPACE : 0; + closedir(dir); + return error; + } + while (!(error = readdir_r(dir, (void *)de_buf, &de)) && de) { + char namebuf[l+de->d_reclen+2], *name = namebuf; + if (!literal && fnmatch(p, de->d_name, fnm_flags)) + continue; + if (literal && strcmp(p, de->d_name)) + continue; + if (p2 && de->d_type && !S_ISDIR(de->d_type<<12) && !S_ISLNK(de->d_type<<12)) + continue; + if (*d) { + memcpy(name, d, l); + name[l] = '/'; + strcpy(name+l+1, de->d_name); + } else { + name = de->d_name; + } + if (p2) { + if ((error = match_in_dir(name, p2, flags, errfunc, tail))) { + closedir(dir); + return error; + } + } else { + int mark = 0; + if (flags & GLOB_MARK) { + if (de->d_type) + mark = S_ISDIR(de->d_type<<12); + else { + struct stat st; + stat(name, &st); + mark = S_ISDIR(st.st_mode); + } + } + if (append(tail, name, l+de->d_reclen+1, mark)) { + closedir(dir); + return GLOB_NOSPACE; + } + } + } + closedir(dir); + if (error && (errfunc(d, error) || (flags & GLOB_ERR))) + return GLOB_ABORTED; + return 0; +} + +static int ignore_err(const char *path, int err) +{ + return 0; +} + +static void freelist(struct match *head) +{ + struct match *match, *next; + for (match=head->next; match; match=next) { + next = match->next; + free(match); + } +} + +static int sort(const void *a, const void *b) +{ + return strcmp(*(const char **)a, *(const char **)b); +} + +int glob(const char *pat, int flags, int (*errfunc)(const char *path, int err), glob_t *g) +{ + const char *p=pat, *d; + struct match head = { .next = NULL }, *tail = &head; + size_t cnt, i; + size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; + int error = 0; + + if (*p == '/') { + for (; *p == '/'; p++); + d = "/"; + } else { + d = ""; + } + + if (!errfunc) errfunc = ignore_err; + + if (!(flags & GLOB_APPEND)) { + g->gl_offs = offs; + g->gl_pathc = 0; + g->gl_pathv = NULL; + } + + if (*p) error = match_in_dir(d, p, flags, errfunc, &tail); + if (error == GLOB_NOSPACE) { + freelist(&head); + return error; + } + + for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); + if (!cnt) { + if (flags & GLOB_NOCHECK) { + tail = &head; + if (append(&tail, pat, strlen(pat), 0)) + return GLOB_NOSPACE; + cnt++; + } else + return GLOB_NOMATCH; + } + + if (flags & GLOB_APPEND) { + char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); + if (!pathv) { + freelist(&head); + return GLOB_NOSPACE; + } + g->gl_pathv = pathv; + offs += g->gl_pathc; + } else { + g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); + if (!g->gl_pathv) { + freelist(&head); + return GLOB_NOSPACE; + } + for (i=0; i<offs; i++) + g->gl_pathv[i] = NULL; + } + for (i=0, tail=head.next; i<cnt; tail=tail->next, i++) + g->gl_pathv[offs + i] = tail->name; + g->gl_pathv[offs + i] = NULL; + g->gl_pathc += cnt; + + if (!(flags & GLOB_NOSORT)) + qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); + + return error; +} + +void globfree(glob_t *g) +{ + size_t i; + for (i=0; i<g->gl_pathc; i++) + free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); + free(g->gl_pathv); + g->gl_pathc = 0; + g->gl_pathv = NULL; +} + +LFS64(glob); +LFS64(globfree); diff --git a/src/regex/regcomp.c b/src/regex/regcomp.c new file mode 100644 index 00000000..3307942e --- /dev/null +++ b/src/regex/regcomp.c @@ -0,0 +1,3362 @@ +/* + regcomp.c - TRE POSIX compatible regex compilation functions. + + Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi> + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <regex.h> +#include <limits.h> +#include <stdint.h> + +#include "tre.h" + +#include <assert.h> + +/*********************************************************************** + from tre-ast.c and tre-ast.h +***********************************************************************/ + +/* The different AST node types. */ +typedef enum { + LITERAL, + CATENATION, + ITERATION, + UNION +} tre_ast_type_t; + +/* Special subtypes of TRE_LITERAL. */ +#define EMPTY -1 /* Empty leaf (denotes empty string). */ +#define ASSERTION -2 /* Assertion leaf. */ +#define TAG -3 /* Tag leaf. */ +#define BACKREF -4 /* Back reference leaf. */ + +#define IS_SPECIAL(x) ((x)->code_min < 0) +#define IS_EMPTY(x) ((x)->code_min == EMPTY) +#define IS_ASSERTION(x) ((x)->code_min == ASSERTION) +#define IS_TAG(x) ((x)->code_min == TAG) +#define IS_BACKREF(x) ((x)->code_min == BACKREF) + +/* Taken from tre-compile.h */ +typedef struct { + int position; + int code_min; + int code_max; + int *tags; + int assertions; + tre_ctype_t class; + tre_ctype_t *neg_classes; + int backref; +} tre_pos_and_tags_t; + +/* A generic AST node. All AST nodes consist of this node on the top + level with `obj' pointing to the actual content. */ +typedef struct { + tre_ast_type_t type; /* Type of the node. */ + void *obj; /* Pointer to actual node. */ + int nullable; + int submatch_id; + int num_submatches; + int num_tags; + tre_pos_and_tags_t *firstpos; + tre_pos_and_tags_t *lastpos; +} tre_ast_node_t; + + +/* A "literal" node. These are created for assertions, back references, + tags, matching parameter settings, and all expressions that match one + character. */ +typedef struct { + long code_min; + long code_max; + int position; + tre_ctype_t class; + tre_ctype_t *neg_classes; +} tre_literal_t; + +/* A "catenation" node. These are created when two regexps are concatenated. + If there are more than one subexpressions in sequence, the `left' part + holds all but the last, and `right' part holds the last subexpression + (catenation is left associative). */ +typedef struct { + tre_ast_node_t *left; + tre_ast_node_t *right; +} tre_catenation_t; + +/* An "iteration" node. These are created for the "*", "+", "?", and "{m,n}" + operators. */ +typedef struct { + /* Subexpression to match. */ + tre_ast_node_t *arg; + /* Minimum number of consecutive matches. */ + int min; + /* Maximum number of consecutive matches. */ + int max; +} tre_iteration_t; + +/* An "union" node. These are created for the "|" operator. */ +typedef struct { + tre_ast_node_t *left; + tre_ast_node_t *right; +} tre_union_t; + +static tre_ast_node_t * +tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size) +{ + tre_ast_node_t *node; + + node = tre_mem_calloc(mem, sizeof(*node)); + if (!node) + return NULL; + node->obj = tre_mem_calloc(mem, size); + if (!node->obj) + return NULL; + node->type = type; + node->nullable = -1; + node->submatch_id = -1; + + return node; +} + +static tre_ast_node_t * +tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position) +{ + tre_ast_node_t *node; + tre_literal_t *lit; + + node = tre_ast_new_node(mem, LITERAL, sizeof(tre_literal_t)); + if (!node) + return NULL; + lit = node->obj; + lit->code_min = code_min; + lit->code_max = code_max; + lit->position = position; + + return node; +} + +static tre_ast_node_t * +tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max) +{ + tre_ast_node_t *node; + tre_iteration_t *iter; + + node = tre_ast_new_node(mem, ITERATION, sizeof(tre_iteration_t)); + if (!node) + return NULL; + iter = node->obj; + iter->arg = arg; + iter->min = min; + iter->max = max; + node->num_submatches = arg->num_submatches; + + return node; +} + +static tre_ast_node_t * +tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right) +{ + tre_ast_node_t *node; + + node = tre_ast_new_node(mem, UNION, sizeof(tre_union_t)); + if (node == NULL) + return NULL; + ((tre_union_t *)node->obj)->left = left; + ((tre_union_t *)node->obj)->right = right; + node->num_submatches = left->num_submatches + right->num_submatches; + + return node; +} + +static tre_ast_node_t * +tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left, + tre_ast_node_t *right) +{ + tre_ast_node_t *node; + + node = tre_ast_new_node(mem, CATENATION, sizeof(tre_catenation_t)); + if (node == NULL) + return NULL; + ((tre_catenation_t *)node->obj)->left = left; + ((tre_catenation_t *)node->obj)->right = right; + node->num_submatches = left->num_submatches + right->num_submatches; + + return node; +} + +/*********************************************************************** + from tre-stack.c and tre-stack.h +***********************************************************************/ + +/* Just to save some typing. */ +#define STACK_PUSH(s, value) \ + do \ + { \ + status = tre_stack_push(s, (void *)(value)); \ + } \ + while (0) + +#define STACK_PUSHX(s, value) \ + { \ + status = tre_stack_push(s, (void *)(value)); \ + if (status != REG_OK) \ + break; \ + } + +#define STACK_PUSHR(s, value) \ + { \ + reg_errcode_t status; \ + status = tre_stack_push(s, (void *)(value)); \ + if (status != REG_OK) \ + return status; \ + } + +typedef struct tre_stack_rec { + int size; + int max_size; + int increment; + int ptr; + void **stack; +} tre_stack_t; + + +static tre_stack_t * +tre_stack_new(int size, int max_size, int increment) +{ + tre_stack_t *s; + + s = xmalloc(sizeof(*s)); + if (s != NULL) + { + s->stack = xmalloc(sizeof(*s->stack) * size); + if (s->stack == NULL) + { + xfree(s); + return NULL; + } + s->size = size; + s->max_size = max_size; + s->increment = increment; + s->ptr = 0; + } + return s; +} + +static void +tre_stack_destroy(tre_stack_t *s) +{ + xfree(s->stack); + xfree(s); +} + +static int +tre_stack_num_objects(tre_stack_t *s) +{ + return s->ptr; +} + +static reg_errcode_t +tre_stack_push(tre_stack_t *s, void *value) +{ + if (s->ptr < s->size) + { + s->stack[s->ptr] = value; + s->ptr++; + } + else + { + if (s->size >= s->max_size) + { + DPRINT(("tre_stack_push: stack full\n")); + return REG_ESPACE; + } + else + { + void **new_buffer; + int new_size; + DPRINT(("tre_stack_push: trying to realloc more space\n")); + new_size = s->size + s->increment; + if (new_size > s->max_size) + new_size = s->max_size; + new_buffer = xrealloc(s->stack, sizeof(*new_buffer) * new_size); + if (new_buffer == NULL) + { + DPRINT(("tre_stack_push: realloc failed.\n")); + return REG_ESPACE; + } + DPRINT(("tre_stack_push: realloc succeeded.\n")); + assert(new_size > s->size); + s->size = new_size; + s->stack = new_buffer; + tre_stack_push(s, value); + } + } + return REG_OK; +} + +static void * +tre_stack_pop(tre_stack_t *s) +{ + return s->stack[--s->ptr]; +} + + +/*********************************************************************** + from tre-parse.c and tre-parse.h +***********************************************************************/ + +/* Parse context. */ +typedef struct { + /* Memory allocator. The AST is allocated using this. */ + tre_mem_t mem; + /* Stack used for keeping track of regexp syntax. */ + tre_stack_t *stack; + /* The parse result. */ + tre_ast_node_t *result; + /* The regexp to parse and its length. */ + const tre_char_t *re; + /* The first character of the entire regexp. */ + const tre_char_t *re_start; + /* The first character after the end of the regexp. */ + const tre_char_t *re_end; + int len; + /* Current submatch ID. */ + int submatch_id; + /* Current position (number of literal). */ + int position; + /* The highest back reference or -1 if none seen so far. */ + int max_backref; + /* Compilation flags. */ + int cflags; + /* If this flag is set the top-level submatch is not captured. */ + int nofirstsub; +} tre_parse_ctx_t; + +static reg_errcode_t +tre_new_item(tre_mem_t mem, int min, int max, int *i, int *max_i, + tre_ast_node_t ***items) +{ + reg_errcode_t status; + tre_ast_node_t **array = *items; + /* Allocate more space if necessary. */ + if (*i >= *max_i) + { + tre_ast_node_t **new_items; + DPRINT(("out of array space, i = %d\n", *i)); + /* If the array is already 1024 items large, give up -- there's + probably an error in the regexp (e.g. not a '\0' terminated + string and missing ']') */ + if (*max_i > 1024) + return REG_ESPACE; + *max_i *= 2; + new_items = xrealloc(array, sizeof(*items) * *max_i); + if (new_items == NULL) + return REG_ESPACE; + *items = array = new_items; + } + array[*i] = tre_ast_new_literal(mem, min, max, -1); + status = array[*i] == NULL ? REG_ESPACE : REG_OK; + (*i)++; + return status; +} + + +/* Expands a character class to character ranges. */ +static reg_errcode_t +tre_expand_ctype(tre_mem_t mem, tre_ctype_t class, tre_ast_node_t ***items, + int *i, int *max_i, int cflags) +{ + reg_errcode_t status = REG_OK; + tre_cint_t c; + int j, min = -1, max = 0; + assert(TRE_MB_CUR_MAX == 1); + + DPRINT((" expanding class to character ranges\n")); + for (j = 0; (j < 256) && (status == REG_OK); j++) + { + c = j; + if (tre_isctype(c, class) + || ((cflags & REG_ICASE) + && (tre_isctype(tre_tolower(c), class) + || tre_isctype(tre_toupper(c), class)))) +{ + if (min < 0) + min = c; + max = c; + } + else if (min >= 0) + { + DPRINT((" range %c (%d) to %c (%d)\n", min, min, max, max)); + status = tre_new_item(mem, min, max, i, max_i, items); + min = -1; + } + } + if (min >= 0 && status == REG_OK) + status = tre_new_item(mem, min, max, i, max_i, items); + return status; +} + + +static int +tre_compare_items(const void *a, const void *b) +{ + tre_ast_node_t *node_a = *(tre_ast_node_t **)a; + tre_ast_node_t *node_b = *(tre_ast_node_t **)b; + tre_literal_t *l_a = node_a->obj, *l_b = node_b->obj; + int a_min = l_a->code_min, b_min = l_b->code_min; + + if (a_min < b_min) + return -1; + else if (a_min > b_min) + return 1; + else + return 0; +} + +/* Maximum number of character classes that can occur in a negated bracket + expression. */ +#define MAX_NEG_CLASSES 64 + +/* Maximum length of character class names. */ +#define MAX_CLASS_NAME + +static reg_errcode_t +tre_parse_bracket_items(tre_parse_ctx_t *ctx, int negate, + tre_ctype_t neg_classes[], int *num_neg_classes, + tre_ast_node_t ***items, int *num_items, + int *items_size) +{ + const tre_char_t *re = ctx->re; + reg_errcode_t status = REG_OK; + tre_ctype_t class = (tre_ctype_t)0; + int i = *num_items; + int max_i = *items_size; + int skip; + + /* Build an array of the items in the bracket expression. */ + while (status == REG_OK) + { + skip = 0; + if (re == ctx->re_end) + { + status = REG_EBRACK; + } + else if (*re == ']' && re > ctx->re) + { + DPRINT(("tre_parse_bracket: done: '%.*" STRF "'\n", + ctx->re_end - re, re)); + re++; + break; + } + else + { + tre_cint_t min = 0, max = 0; + + class = (tre_ctype_t)0; + if (re + 2 < ctx->re_end + && *(re + 1) == '-' && *(re + 2) != ']') + { + DPRINT(("tre_parse_bracket: range: '%.*" STRF "'\n", + ctx->re_end - re, re)); + min = *re; + max = *(re + 2); + re += 3; + /* XXX - Should use collation order instead of encoding values + in character ranges. */ + if (min > max) + status = REG_ERANGE; + } + else if (re + 1 < ctx->re_end + && *re == '[' && *(re + 1) == '.') + status = REG_ECOLLATE; + else if (re + 1 < ctx->re_end + && *re == '[' && *(re + 1) == '=') + status = REG_ECOLLATE; + else if (re + 1 < ctx->re_end + && *re == '[' && *(re + 1) == ':') + { + char tmp_str[64]; + const tre_char_t *endptr = re + 2; + int len; + DPRINT(("tre_parse_bracket: class: '%.*" STRF "'\n", + ctx->re_end - re, re)); + while (endptr < ctx->re_end && *endptr != ':') + endptr++; + if (endptr != ctx->re_end) + { + len = MIN(endptr - re - 2, 63); +#ifdef TRE_WCHAR + { + tre_char_t tmp_wcs[64]; + wcsncpy(tmp_wcs, re + 2, len); + tmp_wcs[len] = '\0'; +#if defined HAVE_WCSRTOMBS + { + mbstate_t state; + const tre_char_t *src = tmp_wcs; + memset(&state, '\0', sizeof(state)); + len = wcsrtombs(tmp_str, &src, sizeof(tmp_str), &state); + } +#elif defined HAVE_WCSTOMBS + len = wcstombs(tmp_str, tmp_wcs, 63); +#endif /* defined HAVE_WCSTOMBS */ + } +#else /* !TRE_WCHAR */ + strncpy(tmp_str, re + 2, len); +#endif /* !TRE_WCHAR */ + tmp_str[len] = '\0'; + DPRINT((" class name: %s\n", tmp_str)); + class = tre_ctype(tmp_str); + if (!class) + status = REG_ECTYPE; + /* Optimize character classes for 8 bit character sets. */ + if (status == REG_OK && TRE_MB_CUR_MAX == 1) + { + status = tre_expand_ctype(ctx->mem, class, items, + &i, &max_i, ctx->cflags); + class = (tre_ctype_t)0; + skip = 1; + } + re = endptr + 2; + } + else + status = REG_ECTYPE; + min = 0; + max = TRE_CHAR_MAX; + } + else + { + DPRINT(("tre_parse_bracket: char: '%.*" STRF "'\n", + ctx->re_end - re, re)); + if (*re == '-' && *(re + 1) != ']' + && ctx->re != re) + /* Two ranges are not allowed to share and endpoint. */ + status = REG_ERANGE; + min = max = *re++; + } + + if (status != REG_OK) + break; + + if (class && negate) + if (*num_neg_classes >= MAX_NEG_CLASSES) + status = REG_ESPACE; + else + neg_classes[(*num_neg_classes)++] = class; + else if (!skip) + { + status = tre_new_item(ctx->mem, min, max, &i, &max_i, items); + if (status != REG_OK) + break; + ((tre_literal_t*)((*items)[i-1])->obj)->class = class; + } + + /* Add opposite-case counterpoints if REG_ICASE is present. + This is broken if there are more than two "same" characters. */ + if (ctx->cflags & REG_ICASE && !class && status == REG_OK && !skip) + { + int cmin, ccurr; + + DPRINT(("adding opposite-case counterpoints\n")); + while (min <= max) + { + if (tre_islower(min)) + { + cmin = ccurr = tre_toupper(min++); + while (tre_islower(min) && tre_toupper(min) == ccurr + 1 + && min <= max) + ccurr = tre_toupper(min++); + status = tre_new_item(ctx->mem, cmin, ccurr, + &i, &max_i, items); + } + else if (tre_isupper(min)) + { + cmin = ccurr = tre_tolower(min++); + while (tre_isupper(min) && tre_tolower(min) == ccurr + 1 + && min <= max) + ccurr = tre_tolower(min++); + status = tre_new_item(ctx->mem, cmin, ccurr, + &i, &max_i, items); + } + else min++; + if (status != REG_OK) + break; + } + if (status != REG_OK) + break; + } + } + } + *num_items = i; + *items_size = max_i; + ctx->re = re; + return status; +} + +static reg_errcode_t +tre_parse_bracket(tre_parse_ctx_t *ctx, tre_ast_node_t **result) +{ + tre_ast_node_t *node = NULL; + int negate = 0; + reg_errcode_t status = REG_OK; + tre_ast_node_t **items, *u, *n; + int i = 0, j, max_i = 32, curr_max, curr_min; + tre_ctype_t neg_classes[MAX_NEG_CLASSES]; + int num_neg_classes = 0; + + /* Start off with an array of `max_i' elements. */ + items = xmalloc(sizeof(*items) * max_i); + if (items == NULL) + return REG_ESPACE; + + if (*ctx->re == '^') + { + DPRINT(("tre_parse_bracket: negate: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + negate = 1; + ctx->re++; + } + + status = tre_parse_bracket_items(ctx, negate, neg_classes, &num_neg_classes, + &items, &i, &max_i); + + if (status != REG_OK) + goto parse_bracket_done; + + /* Sort the array if we need to negate it. */ + if (negate) + qsort(items, i, sizeof(*items), tre_compare_items); + + curr_max = curr_min = 0; + /* Build a union of the items in the array, negated if necessary. */ + for (j = 0; j < i && status == REG_OK; j++) + { + int min, max; + tre_literal_t *l = items[j]->obj; + min = l->code_min; + max = l->code_max; + + DPRINT(("item: %d - %d, class %ld, curr_max = %d\n", + (int)l->code_min, (int)l->code_max, (long)l->class, curr_max)); + + if (negate) + { + if (min < curr_max) + { + /* Overlap. */ + curr_max = MAX(max + 1, curr_max); + DPRINT(("overlap, curr_max = %d\n", curr_max)); + l = NULL; + } + else + { + /* No overlap. */ + curr_max = min - 1; + if (curr_max >= curr_min) + { + DPRINT(("no overlap\n")); + l->code_min = curr_min; + l->code_max = curr_max; + } + else + { + DPRINT(("no overlap, zero room\n")); + l = NULL; + } + curr_min = curr_max = max + 1; + } + } + + if (l != NULL) + { + int k; + DPRINT(("creating %d - %d\n", (int)l->code_min, (int)l->code_max)); + l->position = ctx->position; + if (num_neg_classes > 0) + { + l->neg_classes = tre_mem_alloc(ctx->mem, + (sizeof(l->neg_classes) + * (num_neg_classes + 1))); + if (l->neg_classes == NULL) + { + status = REG_ESPACE; + break; + } + for (k = 0; k < num_neg_classes; k++) + l->neg_classes[k] = neg_classes[k]; + l->neg_classes[k] = (tre_ctype_t)0; + } + else + l->neg_classes = NULL; + if (node == NULL) + node = items[j]; + else + { + u = tre_ast_new_union(ctx->mem, node, items[j]); + if (u == NULL) + status = REG_ESPACE; + node = u; + } + } + } + + if (status != REG_OK) + goto parse_bracket_done; + + if (negate) + { + int k; + DPRINT(("final: creating %d - %d\n", curr_min, (int)TRE_CHAR_MAX)); + n = tre_ast_new_literal(ctx->mem, curr_min, TRE_CHAR_MAX, ctx->position); + if (n == NULL) + status = REG_ESPACE; + else + { + tre_literal_t *l = n->obj; + if (num_neg_classes > 0) + { + l->neg_classes = tre_mem_alloc(ctx->mem, + (sizeof(l->neg_classes) + * (num_neg_classes + 1))); + if (l->neg_classes == NULL) + { + status = REG_ESPACE; + goto parse_bracket_done; + } + for (k = 0; k < num_neg_classes; k++) + l->neg_classes[k] = neg_classes[k]; + l->neg_classes[k] = (tre_ctype_t)0; + } + else + l->neg_classes = NULL; + if (node == NULL) + node = n; + else + { + u = tre_ast_new_union(ctx->mem, node, n); + if (u == NULL) + status = REG_ESPACE; + node = u; + } + } + } + + if (status != REG_OK) + goto parse_bracket_done; + +#ifdef TRE_DEBUG + tre_ast_print(node); +#endif /* TRE_DEBUG */ + + parse_bracket_done: + xfree(items); + ctx->position++; + *result = node; + return status; +} + + +/* Parses a positive decimal integer. Returns -1 if the string does not + contain a valid number. */ +static int +tre_parse_int(const tre_char_t **regex, const tre_char_t *regex_end) +{ + int num = -1; + const tre_char_t *r = *regex; + while (r < regex_end && *r >= '0' && *r <= '9') + { + if (num < 0) + num = 0; + num = num * 10 + *r - '0'; + r++; + } + *regex = r; + return num; +} + + +static reg_errcode_t +tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result) +{ + int min, max; + const tre_char_t *r = ctx->re; + const tre_char_t *start; + int counts_set = 0; + + /* Parse number (minimum repetition count). */ + min = -1; + if (r < ctx->re_end && *r >= '0' && *r <= '9') { + DPRINT(("tre_parse: min count: '%.*" STRF "'\n", ctx->re_end - r, r)); + min = tre_parse_int(&r, ctx->re_end); + } + + /* Parse comma and second number (maximum repetition count). */ + max = min; + if (r < ctx->re_end && *r == ',') + { + r++; + DPRINT(("tre_parse: max count: '%.*" STRF "'\n", ctx->re_end - r, r)); + max = tre_parse_int(&r, ctx->re_end); + } + + /* Check that the repeat counts are sane. */ + if ((max >= 0 && min > max) || max > RE_DUP_MAX) + return REG_BADBR; + + + /* + '{' + optionally followed immediately by a number == minimum repcount + optionally followed by , then a number == maximum repcount + */ + + + do { + int done; + start = r; + + /* Parse count limit settings */ + done = 0; + if (!counts_set) + while (r + 1 < ctx->re_end && !done) + { + switch (*r) + { + case ',': + r++; + break; + case ' ': + r++; + break; + case '}': + done = 1; + break; + default: + done = 1; + break; + } + } + } while (start != r); + + /* Missing }. */ + if (r >= ctx->re_end) + return REG_EBRACE; + + /* Empty contents of {}. */ + if (r == ctx->re) + return REG_BADBR; + + /* Parse the ending '}' or '\}'.*/ + if (ctx->cflags & REG_EXTENDED) + { + if (r >= ctx->re_end || *r != '}') + return REG_BADBR; + r++; + } + else + { + if (r + 1 >= ctx->re_end + || *r != '\\' + || *(r + 1) != '}') + return REG_BADBR; + r += 2; + } + + + /* Create the AST node(s). */ + if (min == 0 && max == 0) + { + *result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (*result == NULL) + return REG_ESPACE; + } + else + { + if (min < 0 && max < 0) + /* Only approximate parameters set, no repetitions. */ + min = max = 1; + + *result = tre_ast_new_iter(ctx->mem, *result, min, max); + if (!*result) + return REG_ESPACE; + } + + ctx->re = r; + return REG_OK; +} + +typedef enum { + PARSE_RE = 0, + PARSE_ATOM, + PARSE_MARK_FOR_SUBMATCH, + PARSE_BRANCH, + PARSE_PIECE, + PARSE_CATENATION, + PARSE_POST_CATENATION, + PARSE_UNION, + PARSE_POST_UNION, + PARSE_POSTFIX, + PARSE_RESTORE_CFLAGS +} tre_parse_re_stack_symbol_t; + + +static reg_errcode_t +tre_parse(tre_parse_ctx_t *ctx) +{ + tre_ast_node_t *result = NULL; + tre_parse_re_stack_symbol_t symbol; + reg_errcode_t status = REG_OK; + tre_stack_t *stack = ctx->stack; + int bottom = tre_stack_num_objects(stack); + int depth = 0; + + DPRINT(("tre_parse: parsing '%.*" STRF "', len = %d\n", + ctx->len, ctx->re, ctx->len)); + + if (!ctx->nofirstsub) + { + STACK_PUSH(stack, ctx->re); + STACK_PUSH(stack, ctx->submatch_id); + STACK_PUSH(stack, PARSE_MARK_FOR_SUBMATCH); + ctx->submatch_id++; + } + STACK_PUSH(stack, PARSE_RE); + ctx->re_start = ctx->re; + ctx->re_end = ctx->re + ctx->len; + + + /* The following is basically just a recursive descent parser. I use + an explicit stack instead of recursive functions mostly because of + two reasons: compatibility with systems which have an overflowable + call stack, and efficiency (both in lines of code and speed). */ + while (tre_stack_num_objects(stack) > bottom && status == REG_OK) + { + if (status != REG_OK) + break; + symbol = (tre_parse_re_stack_symbol_t)tre_stack_pop(stack); + switch (symbol) + { + case PARSE_RE: + /* Parse a full regexp. A regexp is one or more branches, + separated by the union operator `|'. */ + if (ctx->cflags & REG_EXTENDED) + STACK_PUSHX(stack, PARSE_UNION); + STACK_PUSHX(stack, PARSE_BRANCH); + break; + + case PARSE_BRANCH: + /* Parse a branch. A branch is one or more pieces, concatenated. + A piece is an atom possibly followed by a postfix operator. */ + STACK_PUSHX(stack, PARSE_CATENATION); + STACK_PUSHX(stack, PARSE_PIECE); + break; + + case PARSE_PIECE: + /* Parse a piece. A piece is an atom possibly followed by one + or more postfix operators. */ + STACK_PUSHX(stack, PARSE_POSTFIX); + STACK_PUSHX(stack, PARSE_ATOM); + break; + + case PARSE_CATENATION: + /* If the expression has not ended, parse another piece. */ + { + tre_char_t c; + if (ctx->re >= ctx->re_end) + break; + c = *ctx->re; + if (ctx->cflags & REG_EXTENDED && c == '|') + break; + if ((ctx->cflags & REG_EXTENDED + && c == ')' && depth > 0) + || (!(ctx->cflags & REG_EXTENDED) + && (c == '\\' + && *(ctx->re + 1) == ')'))) + { + if (!(ctx->cflags & REG_EXTENDED) && depth == 0) + status = REG_EPAREN; + DPRINT(("tre_parse: group end: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + depth--; + if (!(ctx->cflags & REG_EXTENDED)) + ctx->re += 2; + break; + } + + /* Left associative concatenation. */ + STACK_PUSHX(stack, PARSE_CATENATION); + STACK_PUSHX(stack, result); + STACK_PUSHX(stack, PARSE_POST_CATENATION); + STACK_PUSHX(stack, PARSE_PIECE); + break; + } + + case PARSE_POST_CATENATION: + { + tre_ast_node_t *tree = tre_stack_pop(stack); + tre_ast_node_t *tmp_node; + tmp_node = tre_ast_new_catenation(ctx->mem, tree, result); + if (!tmp_node) + return REG_ESPACE; + result = tmp_node; + break; + } + + case PARSE_UNION: + if (ctx->re >= ctx->re_end) + break; + switch (*ctx->re) + { + case '|': + DPRINT(("tre_parse: union: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + STACK_PUSHX(stack, PARSE_UNION); + STACK_PUSHX(stack, result); + STACK_PUSHX(stack, PARSE_POST_UNION); + STACK_PUSHX(stack, PARSE_BRANCH); + ctx->re++; + break; + + case ')': + ctx->re++; + break; + + default: + break; + } + break; + + case PARSE_POST_UNION: + { + tre_ast_node_t *tmp_node; + tre_ast_node_t *tree = tre_stack_pop(stack); + tmp_node = tre_ast_new_union(ctx->mem, tree, result); + if (!tmp_node) + return REG_ESPACE; + result = tmp_node; + break; + } + + case PARSE_POSTFIX: + /* Parse postfix operators. */ + if (ctx->re >= ctx->re_end) + break; + switch (*ctx->re) + { + case '+': + case '?': + if (!(ctx->cflags & REG_EXTENDED)) + break; + case '*': + { + tre_ast_node_t *tmp_node; + int rep_min = 0; + int rep_max = -1; + if (*ctx->re == '+') + rep_min = 1; + if (*ctx->re == '?') + rep_max = 1; + + ctx->re++; + tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max); + if (tmp_node == NULL) + return REG_ESPACE; + result = tmp_node; + STACK_PUSHX(stack, PARSE_POSTFIX); + break; + } + + case '\\': + /* "\{" is special without REG_EXTENDED */ + if (!(ctx->cflags & REG_EXTENDED) + && ctx->re + 1 < ctx->re_end + && *(ctx->re + 1) == '{') + { + ctx->re++; + goto parse_brace; + } + else + break; + + case '{': + /* "{" is literal without REG_EXTENDED */ + if (!(ctx->cflags & REG_EXTENDED)) + break; + + parse_brace: + DPRINT(("tre_parse: bound: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + ctx->re++; + + status = tre_parse_bound(ctx, &result); + if (status != REG_OK) + return status; + STACK_PUSHX(stack, PARSE_POSTFIX); + break; + } + break; + + case PARSE_ATOM: + /* Parse an atom. An atom is a regular expression enclosed in `()', + an empty set of `()', a bracket expression, `.', `^', `$', + a `\' followed by a character, or a single character. */ + + /* End of regexp? (empty string). */ + if (ctx->re >= ctx->re_end) + goto parse_literal; + + switch (*ctx->re) + { + case '(': /* parenthesized subexpression */ + + if (ctx->cflags & REG_EXTENDED + || (ctx->re > ctx->re_start + && *(ctx->re - 1) == '\\')) + { + depth++; + { + DPRINT(("tre_parse: group begin: '%.*" STRF + "', submatch %d\n", + ctx->re_end - ctx->re, ctx->re, + ctx->submatch_id)); + ctx->re++; + /* First parse a whole RE, then mark the resulting tree + for submatching. */ + STACK_PUSHX(stack, ctx->submatch_id); + STACK_PUSHX(stack, PARSE_MARK_FOR_SUBMATCH); + STACK_PUSHX(stack, PARSE_RE); + ctx->submatch_id++; + } + } + else + goto parse_literal; + break; + + case ')': /* end of current subexpression */ + if ((ctx->cflags & REG_EXTENDED && depth > 0) + || (ctx->re > ctx->re_start + && *(ctx->re - 1) == '\\')) + { + DPRINT(("tre_parse: empty: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + /* We were expecting an atom, but instead the current + subexpression was closed. POSIX leaves the meaning of + this to be implementation-defined. We interpret this as + an empty expression (which matches an empty string). */ + result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (result == NULL) + return REG_ESPACE; + if (!(ctx->cflags & REG_EXTENDED)) + ctx->re--; + } + else + goto parse_literal; + break; + + case '[': /* bracket expression */ + DPRINT(("tre_parse: bracket: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + ctx->re++; + status = tre_parse_bracket(ctx, &result); + if (status != REG_OK) + return status; + break; + + case '\\': + /* If this is "\(" or "\)" chew off the backslash and + try again. */ + if (!(ctx->cflags & REG_EXTENDED) + && ctx->re + 1 < ctx->re_end + && (*(ctx->re + 1) == '(' + || *(ctx->re + 1) == ')')) + { + ctx->re++; + STACK_PUSHX(stack, PARSE_ATOM); + break; + } + + if (ctx->re + 1 >= ctx->re_end) + /* Trailing backslash. */ + return REG_EESCAPE; + + DPRINT(("tre_parse: bleep: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + ctx->re++; + switch (*ctx->re) + { + default: + if (!(ctx->cflags & REG_EXTENDED) && tre_isdigit(*ctx->re)) + { + /* Back reference. */ + int val = *ctx->re - '0'; + DPRINT(("tre_parse: backref: '%.*" STRF "'\n", + ctx->re_end - ctx->re + 1, ctx->re - 1)); + result = tre_ast_new_literal(ctx->mem, BACKREF, val, + ctx->position); + if (result == NULL) + return REG_ESPACE; + ctx->position++; + ctx->max_backref = MAX(val, ctx->max_backref); + ctx->re++; + } + else + { + /* Escaped character. */ + DPRINT(("tre_parse: escaped: '%.*" STRF "'\n", + ctx->re_end - ctx->re + 1, ctx->re - 1)); + result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re, + ctx->position); + ctx->position++; + ctx->re++; + } + break; + } + if (result == NULL) + return REG_ESPACE; + break; + + case '.': /* the any-symbol */ + DPRINT(("tre_parse: any: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + if (ctx->cflags & REG_NEWLINE) + { + tre_ast_node_t *tmp1; + tre_ast_node_t *tmp2; + tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n' - 1, + ctx->position); + if (!tmp1) + return REG_ESPACE; + tmp2 = tre_ast_new_literal(ctx->mem, '\n' + 1, TRE_CHAR_MAX, + ctx->position + 1); + if (!tmp2) + return REG_ESPACE; + result = tre_ast_new_union(ctx->mem, tmp1, tmp2); + if (!result) + return REG_ESPACE; + ctx->position += 2; + } + else + { + result = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, + ctx->position); + if (!result) + return REG_ESPACE; + ctx->position++; + } + ctx->re++; + break; + + case '^': /* beginning of line assertion */ + /* '^' has a special meaning everywhere in EREs, and in the + beginning of the RE and after \( is BREs. */ + if (ctx->cflags & REG_EXTENDED + || (ctx->re - 2 >= ctx->re_start + && *(ctx->re - 2) == '\\' + && *(ctx->re - 1) == '(') + || ctx->re == ctx->re_start) + { + DPRINT(("tre_parse: BOL: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + result = tre_ast_new_literal(ctx->mem, ASSERTION, + ASSERT_AT_BOL, -1); + if (result == NULL) + return REG_ESPACE; + ctx->re++; + } + else + goto parse_literal; + break; + + case '$': /* end of line assertion. */ + /* '$' is special everywhere in EREs, and in the end of the + string and before \) is BREs. */ + if (ctx->cflags & REG_EXTENDED + || (ctx->re + 2 < ctx->re_end + && *(ctx->re + 1) == '\\' + && *(ctx->re + 2) == ')') + || ctx->re + 1 == ctx->re_end) + { + DPRINT(("tre_parse: EOL: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + result = tre_ast_new_literal(ctx->mem, ASSERTION, + ASSERT_AT_EOL, -1); + if (result == NULL) + return REG_ESPACE; + ctx->re++; + } + else + goto parse_literal; + break; + + default: + parse_literal: + + /* We are expecting an atom. If the subexpression (or the whole + regexp ends here, we interpret it as an empty expression + (which matches an empty string). */ + if ( + (ctx->re >= ctx->re_end + || *ctx->re == '*' + || (ctx->cflags & REG_EXTENDED + && (*ctx->re == '|' + || *ctx->re == '{' + || *ctx->re == '+' + || *ctx->re == '?')) + /* Test for "\)" in BRE mode. */ + || (!(ctx->cflags & REG_EXTENDED) + && ctx->re + 1 < ctx->re_end + && *ctx->re == '\\' + && *(ctx->re + 1) == '{'))) + { + DPRINT(("tre_parse: empty: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (!result) + return REG_ESPACE; + break; + } + + DPRINT(("tre_parse: literal: '%.*" STRF "'\n", + ctx->re_end - ctx->re, ctx->re)); + /* Note that we can't use an tre_isalpha() test here, since there + may be characters which are alphabetic but neither upper or + lower case. */ + if (ctx->cflags & REG_ICASE + && (tre_isupper(*ctx->re) || tre_islower(*ctx->re))) + { + tre_ast_node_t *tmp1; + tre_ast_node_t *tmp2; + + /* XXX - Can there be more than one opposite-case + counterpoints for some character in some locale? Or + more than two characters which all should be regarded + the same character if case is ignored? If yes, there + does not seem to be a portable way to detect it. I guess + that at least for multi-character collating elements there + could be several opposite-case counterpoints, but they + cannot be supported portably anyway. */ + tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(*ctx->re), + tre_toupper(*ctx->re), + ctx->position); + if (!tmp1) + return REG_ESPACE; + tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(*ctx->re), + tre_tolower(*ctx->re), + ctx->position); + if (!tmp2) + return REG_ESPACE; + result = tre_ast_new_union(ctx->mem, tmp1, tmp2); + if (!result) + return REG_ESPACE; + } + else + { + result = tre_ast_new_literal(ctx->mem, *ctx->re, *ctx->re, + ctx->position); + if (!result) + return REG_ESPACE; + } + ctx->position++; + ctx->re++; + break; + } + break; + + case PARSE_MARK_FOR_SUBMATCH: + { + int submatch_id = (int)tre_stack_pop(stack); + + if (result->submatch_id >= 0) + { + tre_ast_node_t *n, *tmp_node; + n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1); + if (n == NULL) + return REG_ESPACE; + tmp_node = tre_ast_new_catenation(ctx->mem, n, result); + if (tmp_node == NULL) + return REG_ESPACE; + tmp_node->num_submatches = result->num_submatches; + result = tmp_node; + } + result->submatch_id = submatch_id; + result->num_submatches++; + break; + } + + case PARSE_RESTORE_CFLAGS: + ctx->cflags = (int)tre_stack_pop(stack); + break; + } + } + + /* Check for missing closing parentheses. */ + if (depth > 0) + return REG_EPAREN; + + if (status == REG_OK) + ctx->result = result; + + return status; +} + + +/*********************************************************************** + from tre-compile.c +***********************************************************************/ + +/* + Algorithms to setup tags so that submatch addressing can be done. +*/ + + +/* Inserts a catenation node to the root of the tree given in `node'. + As the left child a new tag with number `tag_id' to `node' is added, + and the right child is the old root. */ +/* OR */ +/* Inserts a catenation node to the root of the tree given in `node'. + As the right child a new tag with number `tag_id' to `node' is added, + and the left child is the old root. */ +static reg_errcode_t +tre_add_tag(tre_mem_t mem, tre_ast_node_t *node, int tag_id, int right) +{ + tre_catenation_t *c; + tre_ast_node_t *child_tag, *child_old; + + DPRINT(("add_tag_%s: tag %d\n", right ? "right" : "left", tag_id)); + + c = tre_mem_alloc(mem, sizeof(*c)); + if (c == NULL) + return REG_ESPACE; + child_tag = tre_ast_new_literal(mem, TAG, tag_id, -1); + if (child_tag == NULL) + return REG_ESPACE; + child_old = tre_mem_alloc(mem, sizeof(tre_ast_node_t)); + if (child_old == NULL) + return REG_ESPACE; + + child_old->obj = node->obj; + child_old->type = node->type; + child_old->nullable = -1; + child_old->submatch_id = -1; + child_old->firstpos = NULL; + child_old->lastpos = NULL; + child_old->num_tags = 0; + node->obj = c; + node->type = CATENATION; + + c->right = c->left = child_old; + if (right) c->right = child_tag; + else c->left = child_tag; + + return REG_OK; +} + +typedef enum { + ADDTAGS_RECURSE, + ADDTAGS_AFTER_ITERATION, + ADDTAGS_AFTER_UNION_LEFT, + ADDTAGS_AFTER_UNION_RIGHT, + ADDTAGS_AFTER_CAT_LEFT, + ADDTAGS_AFTER_CAT_RIGHT, + ADDTAGS_SET_SUBMATCH_END +} tre_addtags_symbol_t; + + +typedef struct { + int tag; + int next_tag; +} tre_tag_states_t; + +/* Adds tags to appropriate locations in the parse tree in `tree', so that + subexpressions marked for submatch addressing can be traced. */ +static reg_errcode_t +tre_add_tags(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree, + tre_tnfa_t *tnfa) +{ + reg_errcode_t status = REG_OK; + tre_addtags_symbol_t symbol; + tre_ast_node_t *node = tree; /* Tree node we are currently looking at. */ + int bottom = tre_stack_num_objects(stack); + /* True for first pass (counting number of needed tags) */ + int first_pass = (mem == NULL || tnfa == NULL); + int *regset, *orig_regset; + int num_tags = 0; /* Total number of tags. */ + int tag = 0; /* The tag that is to be added next. */ + int next_tag = 1; /* Next tag to use after this one. */ + int *parents; /* Stack of submatches the current submatch is + contained in. */ + tre_tag_states_t *saved_states; + + tre_tag_direction_t direction = TRE_TAG_MINIMIZE; + if (!first_pass) + tnfa->end_tag = 0; + + regset = xmalloc(sizeof(*regset) * ((tnfa->num_submatches + 1) * 2)); + if (regset == NULL) + return REG_ESPACE; + regset[0] = -1; + orig_regset = regset; + + parents = xmalloc(sizeof(*parents) * (tnfa->num_submatches + 1)); + if (parents == NULL) + { + xfree(regset); + return REG_ESPACE; + } + parents[0] = -1; + + saved_states = xmalloc(sizeof(*saved_states) * (tnfa->num_submatches + 1)); + if (saved_states == NULL) + { + xfree(regset); + xfree(parents); + return REG_ESPACE; + } + else + { + unsigned int i; + for (i = 0; i <= tnfa->num_submatches; i++) + saved_states[i].tag = -1; + } + + STACK_PUSH(stack, node); + STACK_PUSH(stack, ADDTAGS_RECURSE); + + while (tre_stack_num_objects(stack) > bottom) + { + if (status != REG_OK) + break; + + symbol = (tre_addtags_symbol_t)tre_stack_pop(stack); + switch (symbol) + { + + case ADDTAGS_SET_SUBMATCH_END: + { + int id = (int)tre_stack_pop(stack); + int i; + + /* Add end of this submatch to regset. */ + for (i = 0; regset[i] >= 0; i++); + regset[i] = id * 2 + 1; + regset[i + 1] = -1; + + /* Pop this submatch from the parents stack. */ + for (i = 0; parents[i] >= 0; i++); + parents[i - 1] = -1; + break; + } + + case ADDTAGS_RECURSE: + node = tre_stack_pop(stack); + + if (node->submatch_id >= 0) + { + int id = node->submatch_id; + int i; + + + /* Add start of this submatch to regset. */ + for (i = 0; regset[i] >= 0; i++); + regset[i] = id * 2; + regset[i + 1] = -1; + + if (!first_pass) + { + for (i = 0; parents[i] >= 0; i++); + tnfa->submatch_data[id].parents = NULL; + if (i > 0) + { + int *p = xmalloc(sizeof(*p) * (i + 1)); + if (p == NULL) + { + status = REG_ESPACE; + break; + } + assert(tnfa->submatch_data[id].parents == NULL); + tnfa->submatch_data[id].parents = p; + for (i = 0; parents[i] >= 0; i++) + p[i] = parents[i]; + p[i] = -1; + } + } + + /* Add end of this submatch to regset after processing this + node. */ + STACK_PUSHX(stack, node->submatch_id); + STACK_PUSHX(stack, ADDTAGS_SET_SUBMATCH_END); + } + + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = node->obj; + + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + int i; + DPRINT(("Literal %d-%d\n", + (int)lit->code_min, (int)lit->code_max)); + if (regset[0] >= 0) + { + /* Regset is not empty, so add a tag before the + literal or backref. */ + if (!first_pass) + { + status = tre_add_tag(mem, node, tag, 0 /*left*/); + tnfa->tag_directions[tag] = direction; + /* Go through the regset and set submatch data for + submatches that are using this tag. */ + for (i = 0; regset[i] >= 0; i++) + { + int id = regset[i] >> 1; + int start = !(regset[i] & 1); + DPRINT((" Using tag %d for %s offset of " + "submatch %d\n", tag, + start ? "start" : "end", id)); + if (start) + tnfa->submatch_data[id].so_tag = tag; + else + tnfa->submatch_data[id].eo_tag = tag; + } + } + else + { + DPRINT((" num_tags = 1\n")); + node->num_tags = 1; + } + + DPRINT((" num_tags++\n")); + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } + } + else + { + assert(!IS_TAG(lit)); + } + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + tre_ast_node_t *left = cat->left; + tre_ast_node_t *right = cat->right; + int reserved_tag = -1; + DPRINT(("Catenation, next_tag = %d\n", next_tag)); + + + /* After processing right child. */ + STACK_PUSHX(stack, node); + STACK_PUSHX(stack, ADDTAGS_AFTER_CAT_RIGHT); + + /* Process right child. */ + STACK_PUSHX(stack, right); + STACK_PUSHX(stack, ADDTAGS_RECURSE); + + /* After processing left child. */ + STACK_PUSHX(stack, next_tag + left->num_tags); + DPRINT((" Pushing %d for after left\n", + next_tag + left->num_tags)); + if (left->num_tags > 0 && right->num_tags > 0) + { + /* Reserve the next tag to the right child. */ + DPRINT((" Reserving next_tag %d to right child\n", + next_tag)); + reserved_tag = next_tag; + next_tag++; + } + STACK_PUSHX(stack, reserved_tag); + STACK_PUSHX(stack, ADDTAGS_AFTER_CAT_LEFT); + + /* Process left child. */ + STACK_PUSHX(stack, left); + STACK_PUSHX(stack, ADDTAGS_RECURSE); + + } + break; + case ITERATION: + { + tre_iteration_t *iter = node->obj; + DPRINT(("Iteration\n")); + + if (first_pass) + { + STACK_PUSHX(stack, regset[0] >= 0); + } + else + { + STACK_PUSHX(stack, tag); + } + STACK_PUSHX(stack, node); + STACK_PUSHX(stack, ADDTAGS_AFTER_ITERATION); + + STACK_PUSHX(stack, iter->arg); + STACK_PUSHX(stack, ADDTAGS_RECURSE); + + /* Regset is not empty, so add a tag here. */ + if (regset[0] >= 0) + { + if (!first_pass) + { + int i; + status = tre_add_tag(mem, node, tag, 0 /*left*/); + tnfa->tag_directions[tag] = direction; + /* Go through the regset and set submatch data for + submatches that are using this tag. */ + for (i = 0; regset[i] >= 0; i++) + { + int id = regset[i] >> 1; + int start = !(regset[i] & 1); + DPRINT((" Using tag %d for %s offset of " + "submatch %d\n", tag, + start ? "start" : "end", id)); + if (start) + tnfa->submatch_data[id].so_tag = tag; + else + tnfa->submatch_data[id].eo_tag = tag; + } + } + + DPRINT((" num_tags++\n")); + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } + direction = TRE_TAG_MINIMIZE; + } + break; + case UNION: + { + tre_union_t *uni = node->obj; + tre_ast_node_t *left = uni->left; + tre_ast_node_t *right = uni->right; + int left_tag; + int right_tag; + + if (regset[0] >= 0) + { + left_tag = next_tag; + right_tag = next_tag + 1; + } + else + { + left_tag = tag; + right_tag = next_tag; + } + + DPRINT(("Union\n")); + + /* After processing right child. */ + STACK_PUSHX(stack, right_tag); + STACK_PUSHX(stack, left_tag); + STACK_PUSHX(stack, regset); + STACK_PUSHX(stack, regset[0] >= 0); + STACK_PUSHX(stack, node); + STACK_PUSHX(stack, right); + STACK_PUSHX(stack, left); + STACK_PUSHX(stack, ADDTAGS_AFTER_UNION_RIGHT); + + /* Process right child. */ + STACK_PUSHX(stack, right); + STACK_PUSHX(stack, ADDTAGS_RECURSE); + + /* After processing left child. */ + STACK_PUSHX(stack, ADDTAGS_AFTER_UNION_LEFT); + + /* Process left child. */ + STACK_PUSHX(stack, left); + STACK_PUSHX(stack, ADDTAGS_RECURSE); + + /* Regset is not empty, so add a tag here. */ + if (regset[0] >= 0) + { + if (!first_pass) + { + int i; + status = tre_add_tag(mem, node, tag, 0 /*left*/); + tnfa->tag_directions[tag] = direction; + /* Go through the regset and set submatch data for + submatches that are using this tag. */ + for (i = 0; regset[i] >= 0; i++) + { + int id = regset[i] >> 1; + int start = !(regset[i] & 1); + DPRINT((" Using tag %d for %s offset of " + "submatch %d\n", tag, + start ? "start" : "end", id)); + if (start) + tnfa->submatch_data[id].so_tag = tag; + else + tnfa->submatch_data[id].eo_tag = tag; + } + } + + DPRINT((" num_tags++\n")); + regset[0] = -1; + tag = next_tag; + num_tags++; + next_tag++; + } + + if (node->num_submatches > 0) + { + /* The next two tags are reserved for markers. */ + next_tag++; + tag = next_tag; + next_tag++; + } + + break; + } + } + + if (node->submatch_id >= 0) + { + int i; + /* Push this submatch on the parents stack. */ + for (i = 0; parents[i] >= 0; i++); + parents[i] = node->submatch_id; + parents[i + 1] = -1; + } + + break; /* end case: ADDTAGS_RECURSE */ + + case ADDTAGS_AFTER_ITERATION: + { + int enter_tag; + node = tre_stack_pop(stack); + if (first_pass) + node->num_tags = ((tre_iteration_t *)node->obj)->arg->num_tags + + (int)tre_stack_pop(stack); + else + enter_tag = (int)tre_stack_pop(stack); + + DPRINT(("After iteration\n")); + direction = TRE_TAG_MAXIMIZE; + break; + } + + case ADDTAGS_AFTER_CAT_LEFT: + { + int new_tag = (int)tre_stack_pop(stack); + next_tag = (int)tre_stack_pop(stack); + DPRINT(("After cat left, tag = %d, next_tag = %d\n", + tag, next_tag)); + if (new_tag >= 0) + { + DPRINT((" Setting tag to %d\n", new_tag)); + tag = new_tag; + } + break; + } + + case ADDTAGS_AFTER_CAT_RIGHT: + DPRINT(("After cat right\n")); + node = tre_stack_pop(stack); + if (first_pass) + node->num_tags = ((tre_catenation_t *)node->obj)->left->num_tags + + ((tre_catenation_t *)node->obj)->right->num_tags; + break; + + case ADDTAGS_AFTER_UNION_LEFT: + DPRINT(("After union left\n")); + /* Lift the bottom of the `regset' array so that when processing + the right operand the items currently in the array are + invisible. The original bottom was saved at ADDTAGS_UNION and + will be restored at ADDTAGS_AFTER_UNION_RIGHT below. */ + while (*regset >= 0) + regset++; + break; + + case ADDTAGS_AFTER_UNION_RIGHT: + { + int added_tags, tag_left, tag_right; + tre_ast_node_t *left = tre_stack_pop(stack); + tre_ast_node_t *right = tre_stack_pop(stack); + DPRINT(("After union right\n")); + node = tre_stack_pop(stack); + added_tags = (int)tre_stack_pop(stack); + if (first_pass) + { + node->num_tags = ((tre_union_t *)node->obj)->left->num_tags + + ((tre_union_t *)node->obj)->right->num_tags + added_tags + + ((node->num_submatches > 0) ? 2 : 0); + } + regset = tre_stack_pop(stack); + tag_left = (int)tre_stack_pop(stack); + tag_right = (int)tre_stack_pop(stack); + + /* Add tags after both children, the left child gets a smaller + tag than the right child. This guarantees that we prefer + the left child over the right child. */ + /* XXX - This is not always necessary (if the children have + tags which must be seen for every match of that child). */ + /* XXX - Check if this is the only place where tre_add_tag_right + is used. If so, use tre_add_tag_left (putting the tag before + the child as opposed after the child) and throw away + tre_add_tag_right. */ + if (node->num_submatches > 0) + { + if (!first_pass) + { + status = tre_add_tag(mem, left, tag_left, 1 /*right*/); + tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE; + status = tre_add_tag(mem, right, tag_right, 1 /*right*/); + tnfa->tag_directions[tag] = TRE_TAG_MAXIMIZE; + } + DPRINT((" num_tags += 2\n")); + num_tags += 2; + } + direction = TRE_TAG_MAXIMIZE; + break; + } + + default: + assert(0); + break; + + } /* end switch(symbol) */ + } /* end while(tre_stack_num_objects(stack) > bottom) */ + + if (!first_pass) + { + int i; + /* Go through the regset and set submatch data for + submatches that are using this tag. */ + for (i = 0; regset[i] >= 0; i++) + { + int id = regset[i] >> 1; + int start = !(regset[i] & 1); + DPRINT((" Using tag %d for %s offset of " + "submatch %d\n", num_tags, + start ? "start" : "end", id)); + if (start) + tnfa->submatch_data[id].so_tag = num_tags; + else + tnfa->submatch_data[id].eo_tag = num_tags; + } + } + + DPRINT(("tre_add_tags: %s complete. Number of tags %d.\n", + first_pass? "First pass" : "Second pass", num_tags)); + + assert(tree->num_tags == num_tags); + tnfa->end_tag = num_tags; + tnfa->num_tags = num_tags; + xfree(orig_regset); + xfree(parents); + xfree(saved_states); + return status; +} + + + +/* + AST to TNFA compilation routines. +*/ + +typedef enum { + COPY_RECURSE, + COPY_SET_RESULT_PTR +} tre_copyast_symbol_t; + +/* Flags for tre_copy_ast(). */ +#define COPY_REMOVE_TAGS 1 +#define COPY_MAXIMIZE_FIRST_TAG 2 + +static reg_errcode_t +tre_copy_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + int flags, int *pos_add, tre_tag_direction_t *tag_directions, + tre_ast_node_t **copy, int *max_pos) +{ + reg_errcode_t status = REG_OK; + int bottom = tre_stack_num_objects(stack); + int num_copied = 0; + int first_tag = 1; + tre_ast_node_t **result = copy; + tre_copyast_symbol_t symbol; + + STACK_PUSH(stack, ast); + STACK_PUSH(stack, COPY_RECURSE); + + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + tre_ast_node_t *node; + if (status != REG_OK) + break; + + symbol = (tre_copyast_symbol_t)tre_stack_pop(stack); + switch (symbol) + { + case COPY_SET_RESULT_PTR: + result = tre_stack_pop(stack); + break; + case COPY_RECURSE: + node = tre_stack_pop(stack); + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = node->obj; + int pos = lit->position; + int min = lit->code_min; + int max = lit->code_max; + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + /* XXX - e.g. [ab] has only one position but two + nodes, so we are creating holes in the state space + here. Not fatal, just wastes memory. */ + pos += *pos_add; + num_copied++; + } + else if (IS_TAG(lit) && (flags & COPY_REMOVE_TAGS)) + { + /* Change this tag to empty. */ + min = EMPTY; + max = pos = -1; + } + else if (IS_TAG(lit) && (flags & COPY_MAXIMIZE_FIRST_TAG) + && first_tag) + { + /* Maximize the first tag. */ + tag_directions[max] = TRE_TAG_MAXIMIZE; + first_tag = 0; + } + *result = tre_ast_new_literal(mem, min, max, pos); + if (*result == NULL) + status = REG_ESPACE; + + if (pos > *max_pos) + *max_pos = pos; + break; + } + case UNION: + { + tre_union_t *uni = node->obj; + tre_union_t *copy; + *result = tre_ast_new_union(mem, uni->left, uni->right); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + copy = (*result)->obj; + result = ©->left; + STACK_PUSHX(stack, uni->right); + STACK_PUSHX(stack, COPY_RECURSE); + STACK_PUSHX(stack, ©->right); + STACK_PUSHX(stack, COPY_SET_RESULT_PTR); + STACK_PUSHX(stack, uni->left); + STACK_PUSHX(stack, COPY_RECURSE); + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + tre_catenation_t *copy; + *result = tre_ast_new_catenation(mem, cat->left, cat->right); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + copy = (*result)->obj; + copy->left = NULL; + copy->right = NULL; + result = ©->left; + + STACK_PUSHX(stack, cat->right); + STACK_PUSHX(stack, COPY_RECURSE); + STACK_PUSHX(stack, ©->right); + STACK_PUSHX(stack, COPY_SET_RESULT_PTR); + STACK_PUSHX(stack, cat->left); + STACK_PUSHX(stack, COPY_RECURSE); + break; + } + case ITERATION: + { + tre_iteration_t *iter = node->obj; + STACK_PUSHX(stack, iter->arg); + STACK_PUSHX(stack, COPY_RECURSE); + *result = tre_ast_new_iter(mem, iter->arg, iter->min, iter->max); + if (*result == NULL) + { + status = REG_ESPACE; + break; + } + iter = (*result)->obj; + result = &iter->arg; + break; + } + default: + assert(0); + break; + } + break; + } + } + *pos_add += num_copied; + return status; +} + +typedef enum { + EXPAND_RECURSE, + EXPAND_AFTER_ITER +} tre_expand_ast_symbol_t; + +/* Expands each iteration node that has a finite nonzero minimum or maximum + iteration count to a catenated sequence of copies of the node. */ +static reg_errcode_t +tre_expand_ast(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *ast, + int *position, tre_tag_direction_t *tag_directions, + int *max_depth) +{ + reg_errcode_t status = REG_OK; + int bottom = tre_stack_num_objects(stack); + int pos_add = 0; + int pos_add_total = 0; + int max_pos = 0; + /* Approximate parameter nesting level. */ + int iter_depth = 0; + + STACK_PUSHR(stack, ast); + STACK_PUSHR(stack, EXPAND_RECURSE); + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + tre_ast_node_t *node; + tre_expand_ast_symbol_t symbol; + + if (status != REG_OK) + break; + + DPRINT(("pos_add %d\n", pos_add)); + + symbol = (tre_expand_ast_symbol_t)tre_stack_pop(stack); + node = tre_stack_pop(stack); + switch (symbol) + { + case EXPAND_RECURSE: + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit= node->obj; + if (!IS_SPECIAL(lit) || IS_BACKREF(lit)) + { + lit->position += pos_add; + if (lit->position > max_pos) + max_pos = lit->position; + } + break; + } + case UNION: + { + tre_union_t *uni = node->obj; + STACK_PUSHX(stack, uni->right); + STACK_PUSHX(stack, EXPAND_RECURSE); + STACK_PUSHX(stack, uni->left); + STACK_PUSHX(stack, EXPAND_RECURSE); + break; + } + case CATENATION: + { + tre_catenation_t *cat = node->obj; + STACK_PUSHX(stack, cat->right); + STACK_PUSHX(stack, EXPAND_RECURSE); + STACK_PUSHX(stack, cat->left); + STACK_PUSHX(stack, EXPAND_RECURSE); + break; + } + case ITERATION: + { + tre_iteration_t *iter = node->obj; + STACK_PUSHX(stack, pos_add); + STACK_PUSHX(stack, node); + STACK_PUSHX(stack, EXPAND_AFTER_ITER); + STACK_PUSHX(stack, iter->arg); + STACK_PUSHX(stack, EXPAND_RECURSE); + /* If we are going to expand this node at EXPAND_AFTER_ITER + then don't increase the `pos' fields of the nodes now, it + will get done when expanding. */ + if (iter->min > 1 || iter->max > 1) + pos_add = 0; + iter_depth++; + DPRINT(("iter\n")); + break; + } + default: + assert(0); + break; + } + break; + case EXPAND_AFTER_ITER: + { + tre_iteration_t *iter = node->obj; + int pos_add_last; + pos_add = (int)tre_stack_pop(stack); + pos_add_last = pos_add; + if (iter->min > 1 || iter->max > 1) + { + tre_ast_node_t *seq1 = NULL, *seq2 = NULL; + int i; + int pos_add_save = pos_add; + + /* Create a catenated sequence of copies of the node. */ + for (i = 0; i < iter->min; i++) + { + tre_ast_node_t *copy; + /* Remove tags from all but the last copy. */ + int flags = ((i + 1 < iter->min) + ? COPY_REMOVE_TAGS + : COPY_MAXIMIZE_FIRST_TAG); + DPRINT((" pos_add %d\n", pos_add)); + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, flags, + &pos_add, tag_directions, ©, + &max_pos); + if (status != REG_OK) + return status; + if (seq1 != NULL) + seq1 = tre_ast_new_catenation(mem, seq1, copy); + else + seq1 = copy; + if (seq1 == NULL) + return REG_ESPACE; + } + + if (iter->max == -1) + { + /* No upper limit. */ + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, 0, + &pos_add, NULL, &seq2, &max_pos); + if (status != REG_OK) + return status; + seq2 = tre_ast_new_iter(mem, seq2, 0, -1); + if (seq2 == NULL) + return REG_ESPACE; + } + else + { + for (i = iter->min; i < iter->max; i++) + { + tre_ast_node_t *tmp, *copy; + pos_add_save = pos_add; + status = tre_copy_ast(mem, stack, iter->arg, 0, + &pos_add, NULL, ©, &max_pos); + if (status != REG_OK) + return status; + if (seq2 != NULL) + seq2 = tre_ast_new_catenation(mem, copy, seq2); + else + seq2 = copy; + if (seq2 == NULL) + return REG_ESPACE; + tmp = tre_ast_new_literal(mem, EMPTY, -1, -1); + if (tmp == NULL) + return REG_ESPACE; + seq2 = tre_ast_new_union(mem, tmp, seq2); + if (seq2 == NULL) + return REG_ESPACE; + } + } + + pos_add = pos_add_save; + if (seq1 == NULL) + seq1 = seq2; + else if (seq2 != NULL) + seq1 = tre_ast_new_catenation(mem, seq1, seq2); + if (seq1 == NULL) + return REG_ESPACE; + node->obj = seq1->obj; + node->type = seq1->type; + } + + iter_depth--; + pos_add_total += pos_add - pos_add_last; + if (iter_depth == 0) + pos_add = pos_add_total; + + break; + } + default: + assert(0); + break; + } + } + + *position += pos_add_total; + + /* `max_pos' should never be larger than `*position' if the above + code works, but just an extra safeguard let's make sure + `*position' is set large enough so enough memory will be + allocated for the transition table. */ + if (max_pos > *position) + *position = max_pos; + +#ifdef TRE_DEBUG + DPRINT(("Expanded AST:\n")); + tre_ast_print(ast); + DPRINT(("*position %d, max_pos %d\n", *position, max_pos)); +#endif + + return status; +} + +static tre_pos_and_tags_t * +tre_set_empty(tre_mem_t mem) +{ + tre_pos_and_tags_t *new_set; + + new_set = tre_mem_calloc(mem, sizeof(*new_set)); + if (new_set == NULL) + return NULL; + + new_set[0].position = -1; + new_set[0].code_min = -1; + new_set[0].code_max = -1; + + return new_set; +} + +static tre_pos_and_tags_t * +tre_set_one(tre_mem_t mem, int position, int code_min, int code_max, + tre_ctype_t class, tre_ctype_t *neg_classes, int backref) +{ + tre_pos_and_tags_t *new_set; + + new_set = tre_mem_calloc(mem, sizeof(*new_set) * 2); + if (new_set == NULL) + return NULL; + + new_set[0].position = position; + new_set[0].code_min = code_min; + new_set[0].code_max = code_max; + new_set[0].class = class; + new_set[0].neg_classes = neg_classes; + new_set[0].backref = backref; + new_set[1].position = -1; + new_set[1].code_min = -1; + new_set[1].code_max = -1; + + return new_set; +} + +static tre_pos_and_tags_t * +tre_set_union(tre_mem_t mem, tre_pos_and_tags_t *set1, tre_pos_and_tags_t *set2, + int *tags, int assertions) +{ + int s1, s2, i, j; + tre_pos_and_tags_t *new_set; + int *new_tags; + int num_tags; + + for (num_tags = 0; tags != NULL && tags[num_tags] >= 0; num_tags++); + for (s1 = 0; set1[s1].position >= 0; s1++); + for (s2 = 0; set2[s2].position >= 0; s2++); + new_set = tre_mem_calloc(mem, sizeof(*new_set) * (s1 + s2 + 1)); + if (!new_set ) + return NULL; + + for (s1 = 0; set1[s1].position >= 0; s1++) + { + new_set[s1].position = set1[s1].position; + new_set[s1].code_min = set1[s1].code_min; + new_set[s1].code_max = set1[s1].code_max; + new_set[s1].assertions = set1[s1].assertions | assertions; + new_set[s1].class = set1[s1].class; + new_set[s1].neg_classes = set1[s1].neg_classes; + new_set[s1].backref = set1[s1].backref; + if (set1[s1].tags == NULL && tags == NULL) + new_set[s1].tags = NULL; + else + { + for (i = 0; set1[s1].tags != NULL && set1[s1].tags[i] >= 0; i++); + new_tags = tre_mem_alloc(mem, (sizeof(*new_tags) + * (i + num_tags + 1))); + if (new_tags == NULL) + return NULL; + for (j = 0; j < i; j++) + new_tags[j] = set1[s1].tags[j]; + for (i = 0; i < num_tags; i++) + new_tags[j + i] = tags[i]; + new_tags[j + i] = -1; + new_set[s1].tags = new_tags; + } + } + + for (s2 = 0; set2[s2].position >= 0; s2++) + { + new_set[s1 + s2].position = set2[s2].position; + new_set[s1 + s2].code_min = set2[s2].code_min; + new_set[s1 + s2].code_max = set2[s2].code_max; + /* XXX - why not | assertions here as well? */ + new_set[s1 + s2].assertions = set2[s2].assertions; + new_set[s1 + s2].class = set2[s2].class; + new_set[s1 + s2].neg_classes = set2[s2].neg_classes; + new_set[s1 + s2].backref = set2[s2].backref; + if (set2[s2].tags == NULL) + new_set[s1 + s2].tags = NULL; + else + { + for (i = 0; set2[s2].tags[i] >= 0; i++); + new_tags = tre_mem_alloc(mem, sizeof(*new_tags) * (i + 1)); + if (new_tags == NULL) + return NULL; + for (j = 0; j < i; j++) + new_tags[j] = set2[s2].tags[j]; + new_tags[j] = -1; + new_set[s1 + s2].tags = new_tags; + } + } + new_set[s1 + s2].position = -1; + return new_set; +} + +/* Finds the empty path through `node' which is the one that should be + taken according to POSIX.2 rules, and adds the tags on that path to + `tags'. `tags' may be NULL. If `num_tags_seen' is not NULL, it is + set to the number of tags seen on the path. */ +static reg_errcode_t +tre_match_empty(tre_stack_t *stack, tre_ast_node_t *node, int *tags, + int *assertions, int *num_tags_seen) +{ + tre_literal_t *lit; + tre_union_t *uni; + tre_catenation_t *cat; + tre_iteration_t *iter; + int i; + int bottom = tre_stack_num_objects(stack); + reg_errcode_t status = REG_OK; + if (num_tags_seen) + *num_tags_seen = 0; + + status = tre_stack_push(stack, node); + + /* Walk through the tree recursively. */ + while (status == REG_OK && tre_stack_num_objects(stack) > bottom) + { + node = tre_stack_pop(stack); + + switch (node->type) + { + case LITERAL: + lit = (tre_literal_t *)node->obj; + switch (lit->code_min) + { + case TAG: + if (lit->code_max >= 0) + { + if (tags != NULL) + { + /* Add the tag to `tags'. */ + for (i = 0; tags[i] >= 0; i++) + if (tags[i] == lit->code_max) + break; + if (tags[i] < 0) + { + tags[i] = lit->code_max; + tags[i + 1] = -1; + } + } + if (num_tags_seen) + (*num_tags_seen)++; + } + break; + case ASSERTION: + assert(lit->code_max >= 1 + || lit->code_max <= ASSERT_LAST); + if (assertions != NULL) + *assertions |= lit->code_max; + break; + case EMPTY: + break; + default: + assert(0); + break; + } + break; + + case UNION: + /* Subexpressions starting earlier take priority over ones + starting later, so we prefer the left subexpression over the + right subexpression. */ + uni = (tre_union_t *)node->obj; + if (uni->left->nullable) + STACK_PUSHX(stack, uni->left) + else if (uni->right->nullable) + STACK_PUSHX(stack, uni->right) + else + assert(0); + break; + + case CATENATION: + /* The path must go through both children. */ + cat = (tre_catenation_t *)node->obj; + assert(cat->left->nullable); + assert(cat->right->nullable); + STACK_PUSHX(stack, cat->left); + STACK_PUSHX(stack, cat->right); + break; + + case ITERATION: + /* A match with an empty string is preferred over no match at + all, so we go through the argument if possible. */ + iter = (tre_iteration_t *)node->obj; + if (iter->arg->nullable) + STACK_PUSHX(stack, iter->arg); + break; + + default: + assert(0); + break; + } + } + + return status; +} + + +typedef enum { + NFL_RECURSE, + NFL_POST_UNION, + NFL_POST_CATENATION, + NFL_POST_ITERATION +} tre_nfl_stack_symbol_t; + + +/* Computes and fills in the fields `nullable', `firstpos', and `lastpos' for + the nodes of the AST `tree'. */ +static reg_errcode_t +tre_compute_nfl(tre_mem_t mem, tre_stack_t *stack, tre_ast_node_t *tree) +{ + int bottom = tre_stack_num_objects(stack); + + STACK_PUSHR(stack, tree); + STACK_PUSHR(stack, NFL_RECURSE); + + while (tre_stack_num_objects(stack) > bottom) + { + tre_nfl_stack_symbol_t symbol; + tre_ast_node_t *node; + + symbol = (tre_nfl_stack_symbol_t) tre_stack_pop(stack); + node = tre_stack_pop(stack); + switch (symbol) + { + case NFL_RECURSE: + switch (node->type) + { + case LITERAL: + { + tre_literal_t *lit = (tre_literal_t *)node->obj; + if (IS_BACKREF(lit)) + { + /* Back references: nullable = false, firstpos = {i}, + lastpos = {i}. */ + node->nullable = 0; + node->firstpos = tre_set_one(mem, lit->position, 0, + TRE_CHAR_MAX, 0, NULL, -1); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_one(mem, lit->position, 0, + TRE_CHAR_MAX, 0, NULL, + lit->code_max); + if (!node->lastpos) + return REG_ESPACE; + } + else if (lit->code_min < 0) + { + /* Tags, empty strings and zero width assertions: + nullable = true, firstpos = {}, and lastpos = {}. */ + node->nullable = 1; + node->firstpos = tre_set_empty(mem); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_empty(mem); + if (!node->lastpos) + return REG_ESPACE; + } + else + { + /* Literal at position i: nullable = false, firstpos = {i}, + lastpos = {i}. */ + node->nullable = 0; + node->firstpos = + tre_set_one(mem, lit->position, lit->code_min, + lit->code_max, 0, NULL, -1); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_one(mem, lit->position, + lit->code_min, lit->code_max, + lit->class, lit->neg_classes, + -1); + if (!node->lastpos) + return REG_ESPACE; + } + break; + } + + case UNION: + /* Compute the attributes for the two subtrees, and after that + for this node. */ + STACK_PUSHR(stack, node); + STACK_PUSHR(stack, NFL_POST_UNION); + STACK_PUSHR(stack, ((tre_union_t *)node->obj)->right); + STACK_PUSHR(stack, NFL_RECURSE); + STACK_PUSHR(stack, ((tre_union_t *)node->obj)->left); + STACK_PUSHR(stack, NFL_RECURSE); + break; + + case CATENATION: + /* Compute the attributes for the two subtrees, and after that + for this node. */ + STACK_PUSHR(stack, node); + STACK_PUSHR(stack, NFL_POST_CATENATION); + STACK_PUSHR(stack, ((tre_catenation_t *)node->obj)->right); + STACK_PUSHR(stack, NFL_RECURSE); + STACK_PUSHR(stack, ((tre_catenation_t *)node->obj)->left); + STACK_PUSHR(stack, NFL_RECURSE); + break; + + case ITERATION: + /* Compute the attributes for the subtree, and after that for + this node. */ + STACK_PUSHR(stack, node); + STACK_PUSHR(stack, NFL_POST_ITERATION); + STACK_PUSHR(stack, ((tre_iteration_t *)node->obj)->arg); + STACK_PUSHR(stack, NFL_RECURSE); + break; + } + break; /* end case: NFL_RECURSE */ + + case NFL_POST_UNION: + { + tre_union_t *uni = (tre_union_t *)node->obj; + node->nullable = uni->left->nullable || uni->right->nullable; + node->firstpos = tre_set_union(mem, uni->left->firstpos, + uni->right->firstpos, NULL, 0); + if (!node->firstpos) + return REG_ESPACE; + node->lastpos = tre_set_union(mem, uni->left->lastpos, + uni->right->lastpos, NULL, 0); + if (!node->lastpos) + return REG_ESPACE; + break; + } + + case NFL_POST_ITERATION: + { + tre_iteration_t *iter = (tre_iteration_t *)node->obj; + + if (iter->min == 0 || iter->arg->nullable) + node->nullable = 1; + else + node->nullable = 0; + node->firstpos = iter->arg->firstpos; + node->lastpos = iter->arg->lastpos; + break; + } + + case NFL_POST_CATENATION: + { + int num_tags, *tags, assertions; + reg_errcode_t status; + tre_catenation_t *cat = node->obj; + node->nullable = cat->left->nullable && cat->right->nullable; + + /* Compute firstpos. */ + if (cat->left->nullable) + { + /* The left side matches the empty string. Make a first pass + with tre_match_empty() to get the number of tags. */ + status = tre_match_empty(stack, cat->left, + NULL, NULL, &num_tags); + if (status != REG_OK) + return status; + /* Allocate arrays for the tags and parameters. */ + tags = xmalloc(sizeof(*tags) * (num_tags + 1)); + if (!tags) + return REG_ESPACE; + tags[0] = -1; + assertions = 0; + /* Second pass with tre_mach_empty() to get the list of + tags. */ + status = tre_match_empty(stack, cat->left, tags, + &assertions, NULL); + if (status != REG_OK) + { + xfree(tags); + return status; + } + node->firstpos = + tre_set_union(mem, cat->right->firstpos, cat->left->firstpos, + tags, assertions); + xfree(tags); + if (!node->firstpos) + return REG_ESPACE; + } + else + { + node->firstpos = cat->left->firstpos; + } + + /* Compute lastpos. */ + if (cat->right->nullable) + { + /* The right side matches the empty string. Make a first pass + with tre_match_empty() to get the number of tags. */ + status = tre_match_empty(stack, cat->right, + NULL, NULL, &num_tags); + if (status != REG_OK) + return status; + /* Allocate arrays for the tags and parameters. */ + tags = xmalloc(sizeof(int) * (num_tags + 1)); + if (!tags) + return REG_ESPACE; + tags[0] = -1; + assertions = 0; + /* Second pass with tre_mach_empty() to get the list of + tags. */ + status = tre_match_empty(stack, cat->right, tags, + &assertions, NULL); + if (status != REG_OK) + { + xfree(tags); + return status; + } + node->lastpos = + tre_set_union(mem, cat->left->lastpos, cat->right->lastpos, + tags, assertions); + xfree(tags); + if (!node->lastpos) + return REG_ESPACE; + } + else + { + node->lastpos = cat->right->lastpos; + } + break; + } + + default: + assert(0); + break; + } + } + + return REG_OK; +} + + +/* Adds a transition from each position in `p1' to each position in `p2'. */ +static reg_errcode_t +tre_make_trans(tre_pos_and_tags_t *p1, tre_pos_and_tags_t *p2, + tre_tnfa_transition_t *transitions, + int *counts, int *offs) +{ + tre_pos_and_tags_t *orig_p2 = p2; + tre_tnfa_transition_t *trans; + int i, j, k, l, dup, prev_p2_pos; + + if (transitions != NULL) + while (p1->position >= 0) + { + p2 = orig_p2; + prev_p2_pos = -1; + while (p2->position >= 0) + { + /* Optimization: if this position was already handled, skip it. */ + if (p2->position == prev_p2_pos) + { + p2++; + continue; + } + prev_p2_pos = p2->position; + /* Set `trans' to point to the next unused transition from + position `p1->position'. */ + trans = transitions + offs[p1->position]; + while (trans->state != NULL) + { +#if 0 + /* If we find a previous transition from `p1->position' to + `p2->position', it is overwritten. This can happen only + if there are nested loops in the regexp, like in "((a)*)*". + In POSIX.2 repetition using the outer loop is always + preferred over using the inner loop. Therefore the + transition for the inner loop is useless and can be thrown + away. */ + /* XXX - The same position is used for all nodes in a bracket + expression, so this optimization cannot be used (it will + break bracket expressions) unless I figure out a way to + detect it here. */ + if (trans->state_id == p2->position) + { + DPRINT(("*")); + break; + } +#endif + trans++; + } + + if (trans->state == NULL) + (trans + 1)->state = NULL; + /* Use the character ranges, assertions, etc. from `p1' for + the transition from `p1' to `p2'. */ + trans->code_min = p1->code_min; + trans->code_max = p1->code_max; + trans->state = transitions + offs[p2->position]; + trans->state_id = p2->position; + trans->assertions = p1->assertions | p2->assertions + | (p1->class ? ASSERT_CHAR_CLASS : 0) + | (p1->neg_classes != NULL ? ASSERT_CHAR_CLASS_NEG : 0); + if (p1->backref >= 0) + { + assert((trans->assertions & ASSERT_CHAR_CLASS) == 0); + assert(p2->backref < 0); + trans->u.backref = p1->backref; + trans->assertions |= ASSERT_BACKREF; + } + else + trans->u.class = p1->class; + if (p1->neg_classes != NULL) + { + for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++); + trans->neg_classes = + xmalloc(sizeof(*trans->neg_classes) * (i + 1)); + if (trans->neg_classes == NULL) + return REG_ESPACE; + for (i = 0; p1->neg_classes[i] != (tre_ctype_t)0; i++) + trans->neg_classes[i] = p1->neg_classes[i]; + trans->neg_classes[i] = (tre_ctype_t)0; + } + else + trans->neg_classes = NULL; + + /* Find out how many tags this transition has. */ + i = 0; + if (p1->tags != NULL) + while(p1->tags[i] >= 0) + i++; + j = 0; + if (p2->tags != NULL) + while(p2->tags[j] >= 0) + j++; + + /* If we are overwriting a transition, free the old tag array. */ + if (trans->tags != NULL) + xfree(trans->tags); + trans->tags = NULL; + + /* If there were any tags, allocate an array and fill it. */ + if (i + j > 0) + { + trans->tags = xmalloc(sizeof(*trans->tags) * (i + j + 1)); + if (!trans->tags) + return REG_ESPACE; + i = 0; + if (p1->tags != NULL) + while(p1->tags[i] >= 0) + { + trans->tags[i] = p1->tags[i]; + i++; + } + l = i; + j = 0; + if (p2->tags != NULL) + while (p2->tags[j] >= 0) + { + /* Don't add duplicates. */ + dup = 0; + for (k = 0; k < i; k++) + if (trans->tags[k] == p2->tags[j]) + { + dup = 1; + break; + } + if (!dup) + trans->tags[l++] = p2->tags[j]; + j++; + } + trans->tags[l] = -1; + } + + +#ifdef TRE_DEBUG + { + int *tags; + + DPRINT((" %2d -> %2d on %3d", p1->position, p2->position, + p1->code_min)); + if (p1->code_max != p1->code_min) + DPRINT(("-%3d", p1->code_max)); + tags = trans->tags; + if (tags) + { + DPRINT((", tags [")); + while (*tags >= 0) + { + DPRINT(("%d", *tags)); + tags++; + if (*tags >= 0) + DPRINT((",")); + } + DPRINT(("]")); + } + if (trans->assertions) + DPRINT((", assert %d", trans->assertions)); + if (trans->assertions & ASSERT_BACKREF) + DPRINT((", backref %d", trans->u.backref)); + else if (trans->class) + DPRINT((", class %ld", (long)trans->class)); + if (trans->neg_classes) + DPRINT((", neg_classes %p", trans->neg_classes)); + DPRINT(("\n")); + } +#endif /* TRE_DEBUG */ + p2++; + } + p1++; + } + else + /* Compute a maximum limit for the number of transitions leaving + from each state. */ + while (p1->position >= 0) + { + p2 = orig_p2; + while (p2->position >= 0) + { + counts[p1->position]++; + p2++; + } + p1++; + } + return REG_OK; +} + +/* Converts the syntax tree to a TNFA. All the transitions in the TNFA are + labelled with one character range (there are no transitions on empty + strings). The TNFA takes O(n^2) space in the worst case, `n' is size of + the regexp. */ +static reg_errcode_t +tre_ast_to_tnfa(tre_ast_node_t *node, tre_tnfa_transition_t *transitions, + int *counts, int *offs) +{ + tre_union_t *uni; + tre_catenation_t *cat; + tre_iteration_t *iter; + reg_errcode_t errcode = REG_OK; + + /* XXX - recurse using a stack!. */ + switch (node->type) + { + case LITERAL: + break; + case UNION: + uni = (tre_union_t *)node->obj; + errcode = tre_ast_to_tnfa(uni->left, transitions, counts, offs); + if (errcode != REG_OK) + return errcode; + errcode = tre_ast_to_tnfa(uni->right, transitions, counts, offs); + break; + + case CATENATION: + cat = (tre_catenation_t *)node->obj; + /* Add a transition from each position in cat->left->lastpos + to each position in cat->right->firstpos. */ + errcode = tre_make_trans(cat->left->lastpos, cat->right->firstpos, + transitions, counts, offs); + if (errcode != REG_OK) + return errcode; + errcode = tre_ast_to_tnfa(cat->left, transitions, counts, offs); + if (errcode != REG_OK) + return errcode; + errcode = tre_ast_to_tnfa(cat->right, transitions, counts, offs); + break; + + case ITERATION: + iter = (tre_iteration_t *)node->obj; + assert(iter->max == -1 || iter->max == 1); + + if (iter->max == -1) + { + assert(iter->min == 0 || iter->min == 1); + /* Add a transition from each last position in the iterated + expression to each first position. */ + errcode = tre_make_trans(iter->arg->lastpos, iter->arg->firstpos, + transitions, counts, offs); + if (errcode != REG_OK) + return errcode; + } + errcode = tre_ast_to_tnfa(iter->arg, transitions, counts, offs); + break; + } + return errcode; +} + + +static void +tre_free(regex_t *preg) +{ + tre_tnfa_t *tnfa; + unsigned int i; + tre_tnfa_transition_t *trans; + + tnfa = (void *)preg->TRE_REGEX_T_FIELD; + if (!tnfa) + return; + + for (i = 0; i < tnfa->num_transitions; i++) + if (tnfa->transitions[i].state) + { + if (tnfa->transitions[i].tags) + xfree(tnfa->transitions[i].tags); + if (tnfa->transitions[i].neg_classes) + xfree(tnfa->transitions[i].neg_classes); + } + if (tnfa->transitions) + xfree(tnfa->transitions); + + if (tnfa->initial) + { + for (trans = tnfa->initial; trans->state; trans++) + { + if (trans->tags) + xfree(trans->tags); + } + xfree(tnfa->initial); + } + + if (tnfa->submatch_data) + { + for (i = 0; i < tnfa->num_submatches; i++) + if (tnfa->submatch_data[i].parents) + xfree(tnfa->submatch_data[i].parents); + xfree(tnfa->submatch_data); + } + + if (tnfa->tag_directions) + xfree(tnfa->tag_directions); + xfree(tnfa); +} + + +#define ERROR_EXIT(err) \ + do \ + { \ + errcode = err; \ + if (1) goto error_exit; \ + } \ + while (0) + + +static int +tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags) +{ + tre_stack_t *stack; + tre_ast_node_t *tree, *tmp_ast_l, *tmp_ast_r; + tre_pos_and_tags_t *p; + int *counts = NULL, *offs = NULL; + int i, add = 0; + tre_tnfa_transition_t *transitions, *initial; + tre_tnfa_t *tnfa = NULL; + tre_submatch_data_t *submatch_data; + tre_tag_direction_t *tag_directions = NULL; + reg_errcode_t errcode; + tre_mem_t mem; + + /* Parse context. */ + tre_parse_ctx_t parse_ctx; + + /* Allocate a stack used throughout the compilation process for various + purposes. */ + stack = tre_stack_new(512, 10240, 128); + if (!stack) + return REG_ESPACE; + /* Allocate a fast memory allocator. */ + mem = tre_mem_new(); + if (!mem) + { + tre_stack_destroy(stack); + return REG_ESPACE; + } + + /* Parse the regexp. */ + memset(&parse_ctx, 0, sizeof(parse_ctx)); + parse_ctx.mem = mem; + parse_ctx.stack = stack; + parse_ctx.re = regex; + parse_ctx.len = n; + parse_ctx.cflags = cflags; + parse_ctx.max_backref = -1; + DPRINT(("tre_compile: parsing '%.*" STRF "'\n", n, regex)); + errcode = tre_parse(&parse_ctx); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + preg->re_nsub = parse_ctx.submatch_id - 1; + tree = parse_ctx.result; + +#ifdef TRE_DEBUG + tre_ast_print(tree); +#endif /* TRE_DEBUG */ + + /* Referring to nonexistent subexpressions is illegal. */ + if (parse_ctx.max_backref > (int)preg->re_nsub) + ERROR_EXIT(REG_ESUBREG); + + /* Allocate the TNFA struct. */ + tnfa = xcalloc(1, sizeof(tre_tnfa_t)); + if (tnfa == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->have_backrefs = parse_ctx.max_backref >= 0; + tnfa->num_submatches = parse_ctx.submatch_id; + + /* Set up tags for submatch addressing. If REG_NOSUB is set and the + regexp does not have back references, this can be skipped. */ + if (tnfa->have_backrefs || !(cflags & REG_NOSUB)) + { + DPRINT(("tre_compile: setting up tags\n")); + + /* Figure out how many tags we will need. */ + errcode = tre_add_tags(NULL, stack, tree, tnfa); + if (errcode != REG_OK) + ERROR_EXIT(errcode); +#ifdef TRE_DEBUG + tre_ast_print(tree); +#endif /* TRE_DEBUG */ + + if (tnfa->num_tags > 0) + { + tag_directions = xmalloc(sizeof(*tag_directions) + * (tnfa->num_tags + 1)); + if (tag_directions == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->tag_directions = tag_directions; + memset(tag_directions, -1, + sizeof(*tag_directions) * (tnfa->num_tags + 1)); + } + + submatch_data = xcalloc(parse_ctx.submatch_id, sizeof(*submatch_data)); + if (submatch_data == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->submatch_data = submatch_data; + + errcode = tre_add_tags(mem, stack, tree, tnfa); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + +#ifdef TRE_DEBUG + for (i = 0; i < parse_ctx.submatch_id; i++) + DPRINT(("pmatch[%d] = {t%d, t%d}\n", + i, submatch_data[i].so_tag, submatch_data[i].eo_tag)); + for (i = 0; i < tnfa->num_tags; i++) + DPRINT(("t%d is %s\n", i, + tag_directions[i] == TRE_TAG_MINIMIZE ? + "minimized" : "maximized")); +#endif /* TRE_DEBUG */ + } + + /* Expand iteration nodes. */ + errcode = tre_expand_ast(mem, stack, tree, &parse_ctx.position, + tag_directions, NULL); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + + /* Add a dummy node for the final state. + XXX - For certain patterns this dummy node can be optimized away, + for example "a*" or "ab*". Figure out a simple way to detect + this possibility. */ + tmp_ast_l = tree; + tmp_ast_r = tre_ast_new_literal(mem, 0, 0, parse_ctx.position++); + if (tmp_ast_r == NULL) + ERROR_EXIT(REG_ESPACE); + + tree = tre_ast_new_catenation(mem, tmp_ast_l, tmp_ast_r); + if (tree == NULL) + ERROR_EXIT(REG_ESPACE); + +#ifdef TRE_DEBUG + tre_ast_print(tree); + DPRINT(("Number of states: %d\n", parse_ctx.position)); +#endif /* TRE_DEBUG */ + + errcode = tre_compute_nfl(mem, stack, tree); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + + counts = xmalloc(sizeof(int) * parse_ctx.position); + if (counts == NULL) + ERROR_EXIT(REG_ESPACE); + + offs = xmalloc(sizeof(int) * parse_ctx.position); + if (offs == NULL) + ERROR_EXIT(REG_ESPACE); + + for (i = 0; i < parse_ctx.position; i++) + counts[i] = 0; + tre_ast_to_tnfa(tree, NULL, counts, NULL); + + add = 0; + for (i = 0; i < parse_ctx.position; i++) + { + offs[i] = add; + add += counts[i] + 1; + counts[i] = 0; + } + transitions = xcalloc(add + 1, sizeof(*transitions)); + if (transitions == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->transitions = transitions; + tnfa->num_transitions = add; + + DPRINT(("Converting to TNFA:\n")); + errcode = tre_ast_to_tnfa(tree, transitions, counts, offs); + if (errcode != REG_OK) + ERROR_EXIT(errcode); + + p = tree->firstpos; + i = 0; + while (p->position >= 0) + { + i++; + +#ifdef TRE_DEBUG + { + int *tags; + DPRINT(("initial: %d", p->position)); + tags = p->tags; + if (tags != NULL) + { + if (*tags >= 0) + DPRINT(("/")); + while (*tags >= 0) + { + DPRINT(("%d", *tags)); + tags++; + if (*tags >= 0) + DPRINT((",")); + } + } + DPRINT((", assert %d", p->assertions)); + DPRINT(("\n")); + } +#endif /* TRE_DEBUG */ + + p++; + } + + initial = xcalloc(i + 1, sizeof(tre_tnfa_transition_t)); + if (initial == NULL) + ERROR_EXIT(REG_ESPACE); + tnfa->initial = initial; + + i = 0; + for (p = tree->firstpos; p->position >= 0; p++) + { + initial[i].state = transitions + offs[p->position]; + initial[i].state_id = p->position; + initial[i].tags = NULL; + /* Copy the arrays p->tags, they are allocated + from a tre_mem object. */ + if (p->tags) + { + int j; + for (j = 0; p->tags[j] >= 0; j++); + initial[i].tags = xmalloc(sizeof(*p->tags) * (j + 1)); + if (!initial[i].tags) + ERROR_EXIT(REG_ESPACE); + memcpy(initial[i].tags, p->tags, sizeof(*p->tags) * (j + 1)); + } + initial[i].assertions = p->assertions; + i++; + } + initial[i].state = NULL; + + tnfa->num_transitions = add; + tnfa->final = transitions + offs[tree->lastpos[0].position]; + tnfa->num_states = parse_ctx.position; + tnfa->cflags = cflags; + + DPRINT(("final state %p\n", (void *)tnfa->final)); + + tre_mem_destroy(mem); + tre_stack_destroy(stack); + xfree(counts); + xfree(offs); + + preg->TRE_REGEX_T_FIELD = (void *)tnfa; + return REG_OK; + + error_exit: + /* Free everything that was allocated and return the error code. */ + tre_mem_destroy(mem); + if (stack != NULL) + tre_stack_destroy(stack); + if (counts != NULL) + xfree(counts); + if (offs != NULL) + xfree(offs); + preg->TRE_REGEX_T_FIELD = (void *)tnfa; + tre_free(preg); + return errcode; +} + + +/*********************************************************************** + from regcomp.c +***********************************************************************/ + +int +regcomp(regex_t *preg, const char *regex, int cflags) +{ + int ret; + tre_char_t *wregex; + size_t n = strlen(regex); + + if (n+1 > SIZE_MAX/sizeof(tre_char_t)) + return REG_ESPACE; + wregex = xmalloc(sizeof(tre_char_t) * (n + 1)); + if (wregex == NULL) + return REG_ESPACE; + + n = mbstowcs(wregex, regex, n+1); + if (n == (size_t)-1) { + xfree(wregex); + return REG_BADPAT; + } + + ret = tre_compile(preg, wregex, n, cflags); + xfree(wregex); + + return ret; +} + +void +regfree(regex_t *preg) +{ + tre_free(preg); +} + +/* EOF */ diff --git a/src/regex/regerror.c b/src/regex/regerror.c new file mode 100644 index 00000000..39d70b2a --- /dev/null +++ b/src/regex/regerror.c @@ -0,0 +1,75 @@ +/* + regerror.c - POSIX regerror() implementation for TRE. + + Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi>. + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include <string.h> +#include <regex.h> + +/* Error message strings for error codes listed in `regex.h'. This list + needs to be in sync with the codes listed there, naturally. */ + +/* Converted to single string by Rich Felker to remove the need for + * data relocations at runtime, 27 Feb 2006. */ + +static const char tre_error_messages[] = { + "No error\0" + "No match\0" + "Invalid regexp\0" + "Unknown collating element\0" + "Unknown character class name\0" + "Trailing backslash\0" + "Invalid back reference\0" + "Missing ']'\0" + "Missing ')'\0" + "Missing '}'\0" + "Invalid contents of {}\0" + "Invalid character range\0" + "Out of memory\0" + "XXX\0" +}; + +size_t +regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size) +{ + const char *err; + size_t err_len; + + if (errcode >= 0 && errcode <= REG_BADRPT) + for (err=tre_error_messages; errcode; errcode--, err+=strlen(err)+1); + else + err = "Unknown error"; + + err_len = strlen(err) + 1; + if (errbuf_size > 0 && errbuf != NULL) + { + if (err_len > errbuf_size) + { + memcpy(errbuf, err, errbuf_size - 1); + errbuf[errbuf_size - 1] = '\0'; + } + else + { + strcpy(errbuf, err); + } + } + return err_len; +} + +/* EOF */ diff --git a/src/regex/regexec.c b/src/regex/regexec.c new file mode 100644 index 00000000..0c3d2834 --- /dev/null +++ b/src/regex/regexec.c @@ -0,0 +1,1107 @@ +/* + regexec.c - TRE POSIX compatible matching functions (and more). + + Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi>. + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> +#include <limits.h> + +#include <regex.h> + +#include "tre.h" + +#include <assert.h> + +static void +tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, + const tre_tnfa_t *tnfa, int *tags, int match_eo); + +/*********************************************************************** + from tre-match-utils.h +***********************************************************************/ + +#define GET_NEXT_WCHAR() do { \ + prev_c = next_c; pos += pos_add_next; \ + if ((pos_add_next = mbtowc(&next_c, str_byte, MB_LEN_MAX)) <= 0) { \ + if (pos_add_next < 0) return REG_NOMATCH; \ + else pos_add_next++; \ + } \ + str_byte += pos_add_next; \ + } while (0) + +#define CHECK_ASSERTIONS(assertions) \ + (((assertions & ASSERT_AT_BOL) \ + && (pos > 0 || reg_notbol) \ + && (prev_c != L'\n' || !reg_newline)) \ + || ((assertions & ASSERT_AT_EOL) \ + && (next_c != L'\0' || reg_noteol) \ + && (next_c != L'\n' || !reg_newline))) + +/* Returns 1 if `t1' wins `t2', 0 otherwise. */ +static int +tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions, + int *t1, int *t2) +{ + int i; + for (i = 0; i < num_tags; i++) + { + if (tag_directions[i] == TRE_TAG_MINIMIZE) + { + if (t1[i] < t2[i]) + return 1; + if (t1[i] > t2[i]) + return 0; + } + else + { + if (t1[i] > t2[i]) + return 1; + if (t1[i] < t2[i]) + return 0; + } + } + /* assert(0);*/ + return 0; +} + +static int +tre_neg_char_classes_match(tre_ctype_t *classes, tre_cint_t wc, int icase) +{ + DPRINT(("neg_char_classes_test: %p, %d, %d\n", classes, wc, icase)); + while (*classes != (tre_ctype_t)0) + if ((!icase && tre_isctype(wc, *classes)) + || (icase && (tre_isctype(tre_toupper(wc), *classes) + || tre_isctype(tre_tolower(wc), *classes)))) + return 1; /* Match. */ + else + classes++; + return 0; /* No match. */ +} + + +/*********************************************************************** + from tre-match-parallel.c +***********************************************************************/ + +/* + This algorithm searches for matches basically by reading characters + in the searched string one by one, starting at the beginning. All + matching paths in the TNFA are traversed in parallel. When two or + more paths reach the same state, exactly one is chosen according to + tag ordering rules; if returning submatches is not required it does + not matter which path is chosen. + + The worst case time required for finding the leftmost and longest + match, or determining that there is no match, is always linearly + dependent on the length of the text being searched. + + This algorithm cannot handle TNFAs with back referencing nodes. + See `tre-match-backtrack.c'. +*/ + + +typedef struct { + tre_tnfa_transition_t *state; + int *tags; +} tre_tnfa_reach_t; + +typedef struct { + int pos; + int **tags; +} tre_reach_pos_t; + + +#ifdef TRE_DEBUG +static void +tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_reach_t *reach, int num_tags) +{ + int i; + + while (reach->state != NULL) + { + DPRINT((" %p", (void *)reach->state)); + if (num_tags > 0) + { + DPRINT(("/")); + for (i = 0; i < num_tags; i++) + { + DPRINT(("%d:%d", i, reach->tags[i])); + if (i < (num_tags-1)) + DPRINT((",")); + } + } + reach++; + } + DPRINT(("\n")); + +} +#endif /* TRE_DEBUG */ + +static reg_errcode_t +tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, + int *match_tags, int eflags, int *match_end_ofs) +{ + /* State variables required by GET_NEXT_WCHAR. */ + tre_char_t prev_c = 0, next_c = 0; + const char *str_byte = string; + int pos = -1; + int pos_add_next = 1; +#ifdef TRE_MBSTATE + mbstate_t mbstate; +#endif /* TRE_MBSTATE */ + int reg_notbol = eflags & REG_NOTBOL; + int reg_noteol = eflags & REG_NOTEOL; + int reg_newline = tnfa->cflags & REG_NEWLINE; + + char *buf; + tre_tnfa_transition_t *trans_i; + tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i; + tre_reach_pos_t *reach_pos; + int *tag_i; + int num_tags, i; + + int match_eo = -1; /* end offset of match (-1 if no match found yet) */ + int new_match = 0; + int *tmp_tags = NULL; + int *tmp_iptr; + +#ifdef TRE_MBSTATE + memset(&mbstate, '\0', sizeof(mbstate)); +#endif /* TRE_MBSTATE */ + + if (!match_tags) + num_tags = 0; + else + num_tags = tnfa->num_tags; + + /* Allocate memory for temporary data required for matching. This needs to + be done for every matching operation to be thread safe. This allocates + everything in a single large block from the stack frame using alloca() + or with malloc() if alloca is unavailable. */ + { + int tbytes, rbytes, pbytes, xbytes, total_bytes; + char *tmp_buf; + /* Compute the length of the block we need. */ + tbytes = sizeof(*tmp_tags) * num_tags; + rbytes = sizeof(*reach_next) * (tnfa->num_states + 1); + pbytes = sizeof(*reach_pos) * tnfa->num_states; + xbytes = sizeof(int) * num_tags; + total_bytes = + (sizeof(long) - 1) * 4 /* for alignment paddings */ + + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes; + + /* Allocate the memory. */ +#ifdef TRE_USE_ALLOCA + buf = alloca(total_bytes); +#else /* !TRE_USE_ALLOCA */ + buf = xmalloc(total_bytes); +#endif /* !TRE_USE_ALLOCA */ + if (buf == NULL) + return REG_ESPACE; + memset(buf, 0, total_bytes); + + /* Get the various pointers within tmp_buf (properly aligned). */ + tmp_tags = (void *)buf; + tmp_buf = buf + tbytes; + tmp_buf += ALIGN(tmp_buf, long); + reach_next = (void *)tmp_buf; + tmp_buf += rbytes; + tmp_buf += ALIGN(tmp_buf, long); + reach = (void *)tmp_buf; + tmp_buf += rbytes; + tmp_buf += ALIGN(tmp_buf, long); + reach_pos = (void *)tmp_buf; + tmp_buf += pbytes; + tmp_buf += ALIGN(tmp_buf, long); + for (i = 0; i < tnfa->num_states; i++) + { + reach[i].tags = (void *)tmp_buf; + tmp_buf += xbytes; + reach_next[i].tags = (void *)tmp_buf; + tmp_buf += xbytes; + } + } + + for (i = 0; i < tnfa->num_states; i++) + reach_pos[i].pos = -1; + + GET_NEXT_WCHAR(); + pos = 0; + + DPRINT(("length: %d\n", len)); + DPRINT(("pos:chr/code | states and tags\n")); + DPRINT(("-------------+------------------------------------------------\n")); + + reach_next_i = reach_next; + while (1) + { + /* If no match found yet, add the initial states to `reach_next'. */ + if (match_eo < 0) + { + DPRINT((" init >")); + trans_i = tnfa->initial; + while (trans_i->state != NULL) + { + if (reach_pos[trans_i->state_id].pos < pos) + { + if (trans_i->assertions + && CHECK_ASSERTIONS(trans_i->assertions)) + { + DPRINT(("assertion failed\n")); + trans_i++; + continue; + } + + DPRINT((" %p", (void *)trans_i->state)); + reach_next_i->state = trans_i->state; + for (i = 0; i < num_tags; i++) + reach_next_i->tags[i] = -1; + tag_i = trans_i->tags; + if (tag_i) + while (*tag_i >= 0) + { + if (*tag_i < num_tags) + reach_next_i->tags[*tag_i] = pos; + tag_i++; + } + if (reach_next_i->state == tnfa->final) + { + DPRINT((" found empty match\n")); + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = reach_next_i->tags[i]; + } + reach_pos[trans_i->state_id].pos = pos; + reach_pos[trans_i->state_id].tags = &reach_next_i->tags; + reach_next_i++; + } + trans_i++; + } + DPRINT(("\n")); + reach_next_i->state = NULL; + } + else + { + if (num_tags == 0 || reach_next_i == reach_next) + /* We have found a match. */ + break; + } + + /* Check for end of string. */ + if (!next_c) break; + + GET_NEXT_WCHAR(); + +#ifdef TRE_DEBUG + DPRINT(("%3d:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c)); + tre_print_reach(tnfa, reach_next, num_tags); + DPRINT(("%3d:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c)); + tre_print_reach(tnfa, reach_next, num_tags); +#endif /* TRE_DEBUG */ + + /* Swap `reach' and `reach_next'. */ + reach_i = reach; + reach = reach_next; + reach_next = reach_i; + + /* For each state in `reach' see if there is a transition leaving with + the current input symbol to a state not yet in `reach_next', and + add the destination states to `reach_next'. */ + reach_next_i = reach_next; + for (reach_i = reach; reach_i->state; reach_i++) + { + for (trans_i = reach_i->state; trans_i->state; trans_i++) + { + /* Does this transition match the input symbol? */ + if (trans_i->code_min <= prev_c && + trans_i->code_max >= prev_c) + { + if (trans_i->assertions + && (CHECK_ASSERTIONS(trans_i->assertions) + /* Handle character class transitions. */ + || ((trans_i->assertions & ASSERT_CHAR_CLASS) + && !(tnfa->cflags & REG_ICASE) + && !tre_isctype((tre_cint_t)prev_c, + trans_i->u.class)) + || ((trans_i->assertions & ASSERT_CHAR_CLASS) + && (tnfa->cflags & REG_ICASE) + && (!tre_isctype(tre_tolower((tre_cint_t)prev_c), + trans_i->u.class) + && !tre_isctype(tre_toupper((tre_cint_t)prev_c), + trans_i->u.class))) + || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) + && tre_neg_char_classes_match(trans_i->neg_classes, + (tre_cint_t)prev_c, + tnfa->cflags & REG_ICASE)))) + { + DPRINT(("assertion failed\n")); + continue; + } + + /* Compute the tags after this transition. */ + for (i = 0; i < num_tags; i++) + tmp_tags[i] = reach_i->tags[i]; + tag_i = trans_i->tags; + if (tag_i != NULL) + while (*tag_i >= 0) + { + if (*tag_i < num_tags) + tmp_tags[*tag_i] = pos; + tag_i++; + } + + if (reach_pos[trans_i->state_id].pos < pos) + { + /* Found an unvisited node. */ + reach_next_i->state = trans_i->state; + tmp_iptr = reach_next_i->tags; + reach_next_i->tags = tmp_tags; + tmp_tags = tmp_iptr; + reach_pos[trans_i->state_id].pos = pos; + reach_pos[trans_i->state_id].tags = &reach_next_i->tags; + + if (reach_next_i->state == tnfa->final + && (match_eo == -1 + || (num_tags > 0 + && reach_next_i->tags[0] <= match_tags[0]))) + { + DPRINT((" found match %p\n", trans_i->state)); + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = reach_next_i->tags[i]; + } + reach_next_i++; + + } + else + { + assert(reach_pos[trans_i->state_id].pos == pos); + /* Another path has also reached this state. We choose + the winner by examining the tag values for both + paths. */ + if (tre_tag_order(num_tags, tnfa->tag_directions, + tmp_tags, + *reach_pos[trans_i->state_id].tags)) + { + /* The new path wins. */ + tmp_iptr = *reach_pos[trans_i->state_id].tags; + *reach_pos[trans_i->state_id].tags = tmp_tags; + if (trans_i->state == tnfa->final) + { + DPRINT((" found better match\n")); + match_eo = pos; + new_match = 1; + for (i = 0; i < num_tags; i++) + match_tags[i] = tmp_tags[i]; + } + tmp_tags = tmp_iptr; + } + } + } + } + } + reach_next_i->state = NULL; + } + + DPRINT(("match end offset = %d\n", match_eo)); + +#ifndef TRE_USE_ALLOCA + if (buf) + xfree(buf); +#endif /* !TRE_USE_ALLOCA */ + + *match_end_ofs = match_eo; + return match_eo >= 0 ? REG_OK : REG_NOMATCH; +} + + +/*********************************************************************** + from tre-match-backtrack.c +***********************************************************************/ + +/* + This matcher is for regexps that use back referencing. Regexp matching + with back referencing is an NP-complete problem on the number of back + references. The easiest way to match them is to use a backtracking + routine which basically goes through all possible paths in the TNFA + and chooses the one which results in the best (leftmost and longest) + match. This can be spectacularly expensive and may run out of stack + space, but there really is no better known generic algorithm. Quoting + Henry Spencer from comp.compilers: + <URL: http://compilers.iecc.com/comparch/article/93-03-102> + + POSIX.2 REs require longest match, which is really exciting to + implement since the obsolete ("basic") variant also includes + \<digit>. I haven't found a better way of tackling this than doing + a preliminary match using a DFA (or simulation) on a modified RE + that just replicates subREs for \<digit>, and then doing a + backtracking match to determine whether the subRE matches were + right. This can be rather slow, but I console myself with the + thought that people who use \<digit> deserve very slow execution. + (Pun unintentional but very appropriate.) + +*/ + +typedef struct { + int pos; + const char *str_byte; + tre_tnfa_transition_t *state; + int state_id; + int next_c; + int *tags; +#ifdef TRE_MBSTATE + mbstate_t mbstate; +#endif /* TRE_MBSTATE */ +} tre_backtrack_item_t; + +typedef struct tre_backtrack_struct { + tre_backtrack_item_t item; + struct tre_backtrack_struct *prev; + struct tre_backtrack_struct *next; +} *tre_backtrack_t; + +#ifdef TRE_MBSTATE +#define BT_STACK_MBSTATE_IN stack->item.mbstate = (mbstate) +#define BT_STACK_MBSTATE_OUT (mbstate) = stack->item.mbstate +#else /* !TRE_MBSTATE */ +#define BT_STACK_MBSTATE_IN +#define BT_STACK_MBSTATE_OUT +#endif /* !TRE_MBSTATE */ + + +#ifdef TRE_USE_ALLOCA +#define tre_bt_mem_new tre_mem_newa +#define tre_bt_mem_alloc tre_mem_alloca +#define tre_bt_mem_destroy(obj) do { } while (0) +#else /* !TRE_USE_ALLOCA */ +#define tre_bt_mem_new tre_mem_new +#define tre_bt_mem_alloc tre_mem_alloc +#define tre_bt_mem_destroy tre_mem_destroy +#endif /* !TRE_USE_ALLOCA */ + + +#define BT_STACK_PUSH(_pos, _str_byte, _str_wide, _state, _state_id, _next_c, _tags, _mbstate) \ + do \ + { \ + int i; \ + if (!stack->next) \ + { \ + tre_backtrack_t s; \ + s = tre_bt_mem_alloc(mem, sizeof(*s)); \ + if (!s) \ + { \ + tre_bt_mem_destroy(mem); \ + if (tags) \ + xfree(tags); \ + if (pmatch) \ + xfree(pmatch); \ + if (states_seen) \ + xfree(states_seen); \ + return REG_ESPACE; \ + } \ + s->prev = stack; \ + s->next = NULL; \ + s->item.tags = tre_bt_mem_alloc(mem, \ + sizeof(*tags) * tnfa->num_tags); \ + if (!s->item.tags) \ + { \ + tre_bt_mem_destroy(mem); \ + if (tags) \ + xfree(tags); \ + if (pmatch) \ + xfree(pmatch); \ + if (states_seen) \ + xfree(states_seen); \ + return REG_ESPACE; \ + } \ + stack->next = s; \ + stack = s; \ + } \ + else \ + stack = stack->next; \ + stack->item.pos = (_pos); \ + stack->item.str_byte = (_str_byte); \ + stack->item.state = (_state); \ + stack->item.state_id = (_state_id); \ + stack->item.next_c = (_next_c); \ + for (i = 0; i < tnfa->num_tags; i++) \ + stack->item.tags[i] = (_tags)[i]; \ + BT_STACK_MBSTATE_IN; \ + } \ + while (0) + +#define BT_STACK_POP() \ + do \ + { \ + int i; \ + assert(stack->prev); \ + pos = stack->item.pos; \ + str_byte = stack->item.str_byte; \ + state = stack->item.state; \ + next_c = stack->item.next_c; \ + for (i = 0; i < tnfa->num_tags; i++) \ + tags[i] = stack->item.tags[i]; \ + BT_STACK_MBSTATE_OUT; \ + stack = stack->prev; \ + } \ + while (0) + +#undef MIN +#define MIN(a, b) ((a) <= (b) ? (a) : (b)) + +static reg_errcode_t +tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, + int len, int *match_tags, + int eflags, int *match_end_ofs) +{ + /* State variables required by GET_NEXT_WCHAR. */ + tre_char_t prev_c = 0, next_c = 0; + const char *str_byte = string; + int pos = 0; + int pos_add_next = 1; +#ifdef TRE_MBSTATE + mbstate_t mbstate; +#endif /* TRE_MBSTATE */ + int reg_notbol = eflags & REG_NOTBOL; + int reg_noteol = eflags & REG_NOTEOL; + int reg_newline = tnfa->cflags & REG_NEWLINE; + + /* These are used to remember the necessary values of the above + variables to return to the position where the current search + started from. */ + int next_c_start; + const char *str_byte_start; + int pos_start = -1; +#ifdef TRE_MBSTATE + mbstate_t mbstate_start; +#endif /* TRE_MBSTATE */ + + /* Compilation flags for this regexp. */ + int cflags = tnfa->cflags; + + /* End offset of best match so far, or -1 if no match found yet. */ + int match_eo = -1; + /* Tag arrays. */ + int *next_tags, *tags = NULL; + /* Current TNFA state. */ + tre_tnfa_transition_t *state; + int *states_seen = NULL; + + /* Memory allocator to for allocating the backtracking stack. */ + tre_mem_t mem = tre_bt_mem_new(); + + /* The backtracking stack. */ + tre_backtrack_t stack; + + tre_tnfa_transition_t *trans_i; + regmatch_t *pmatch = NULL; + int ret; + +#ifdef TRE_MBSTATE + memset(&mbstate, '\0', sizeof(mbstate)); +#endif /* TRE_MBSTATE */ + + if (!mem) + return REG_ESPACE; + stack = tre_bt_mem_alloc(mem, sizeof(*stack)); + if (!stack) + { + ret = REG_ESPACE; + goto error_exit; + } + stack->prev = NULL; + stack->next = NULL; + +#ifdef TRE_USE_ALLOCA + tags = alloca(sizeof(*tags) * tnfa->num_tags); + pmatch = alloca(sizeof(*pmatch) * tnfa->num_submatches); + states_seen = alloca(sizeof(*states_seen) * tnfa->num_states); +#else /* !TRE_USE_ALLOCA */ + tags = xmalloc(sizeof(*tags) * tnfa->num_tags); + if (!tags) + { + ret = REG_ESPACE; + goto error_exit; + } + pmatch = xmalloc(sizeof(*pmatch) * tnfa->num_submatches); + if (!pmatch) + { + ret = REG_ESPACE; + goto error_exit; + } + states_seen = xmalloc(sizeof(*states_seen) * tnfa->num_states); + if (!states_seen) + { + ret = REG_ESPACE; + goto error_exit; + } +#endif /* !TRE_USE_ALLOCA */ + + retry: + { + int i; + for (i = 0; i < tnfa->num_tags; i++) + { + tags[i] = -1; + if (match_tags) + match_tags[i] = -1; + } + for (i = 0; i < tnfa->num_states; i++) + states_seen[i] = 0; + } + + state = NULL; + pos = pos_start; + GET_NEXT_WCHAR(); + pos_start = pos; + next_c_start = next_c; + str_byte_start = str_byte; +#ifdef TRE_MBSTATE + mbstate_start = mbstate; +#endif /* TRE_MBSTATE */ + + /* Handle initial states. */ + next_tags = NULL; + for (trans_i = tnfa->initial; trans_i->state; trans_i++) + { + DPRINT(("> init %p, prev_c %lc\n", trans_i->state, (tre_cint_t)prev_c)); + if (trans_i->assertions && CHECK_ASSERTIONS(trans_i->assertions)) + { + DPRINT(("assert failed\n")); + continue; + } + if (state == NULL) + { + /* Start from this state. */ + state = trans_i->state; + next_tags = trans_i->tags; + } + else + { + /* Backtrack to this state. */ + DPRINT(("saving state %d for backtracking\n", trans_i->state_id)); + BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, + trans_i->state_id, next_c, tags, mbstate); + { + int *tmp = trans_i->tags; + if (tmp) + while (*tmp >= 0) + stack->item.tags[*tmp++] = pos; + } + } + } + + if (next_tags) + for (; *next_tags >= 0; next_tags++) + tags[*next_tags] = pos; + + + DPRINT(("entering match loop, pos %d, str_byte %p\n", pos, str_byte)); + DPRINT(("pos:chr/code | state and tags\n")); + DPRINT(("-------------+------------------------------------------------\n")); + + if (state == NULL) + goto backtrack; + + while (1) + { + tre_tnfa_transition_t *trans_i, *next_state; + int empty_br_match; + + DPRINT(("start loop\n")); + if (state == tnfa->final) + { + DPRINT((" match found, %d %d\n", match_eo, pos)); + if (match_eo < pos + || (match_eo == pos + && match_tags + && tre_tag_order(tnfa->num_tags, tnfa->tag_directions, + tags, match_tags))) + { + int i; + /* This match wins the previous match. */ + DPRINT((" win previous\n")); + match_eo = pos; + if (match_tags) + for (i = 0; i < tnfa->num_tags; i++) + match_tags[i] = tags[i]; + } + /* Our TNFAs never have transitions leaving from the final state, + so we jump right to backtracking. */ + goto backtrack; + } + +#ifdef TRE_DEBUG + DPRINT(("%3d:%2lc/%05d | %p ", pos, (tre_cint_t)next_c, (int)next_c, + state)); + { + int i; + for (i = 0; i < tnfa->num_tags; i++) + DPRINT(("%d%s", tags[i], i < tnfa->num_tags - 1 ? ", " : "")); + DPRINT(("\n")); + } +#endif /* TRE_DEBUG */ + + /* Go to the next character in the input string. */ + empty_br_match = 0; + trans_i = state; + if (trans_i->state && trans_i->assertions & ASSERT_BACKREF) + { + /* This is a back reference state. All transitions leaving from + this state have the same back reference "assertion". Instead + of reading the next character, we match the back reference. */ + int so, eo, bt = trans_i->u.backref; + int bt_len; + int result; + + DPRINT((" should match back reference %d\n", bt)); + /* Get the substring we need to match against. Remember to + turn off REG_NOSUB temporarily. */ + tre_fill_pmatch(bt + 1, pmatch, tnfa->cflags & !REG_NOSUB, + tnfa, tags, pos); + so = pmatch[bt].rm_so; + eo = pmatch[bt].rm_eo; + bt_len = eo - so; + + if (len < 0) + { + result = strncmp((char*)string + so, str_byte - 1, bt_len); + } + else if (len - pos < bt_len) + result = 1; + else + result = memcmp((char*)string + so, str_byte - 1, bt_len); + + /* We can ignore multibyte characters here because the backref + string is already aligned at character boundaries. */ + if (result == 0) + { + /* Back reference matched. Check for infinite loop. */ + if (bt_len == 0) + empty_br_match = 1; + if (empty_br_match && states_seen[trans_i->state_id]) + { + DPRINT((" avoid loop\n")); + goto backtrack; + } + + states_seen[trans_i->state_id] = empty_br_match; + + /* Advance in input string and resync `prev_c', `next_c' + and pos. */ + DPRINT((" back reference matched\n")); + str_byte += bt_len - 1; + pos += bt_len - 1; + GET_NEXT_WCHAR(); + DPRINT((" pos now %d\n", pos)); + } + else + { + DPRINT((" back reference did not match\n")); + goto backtrack; + } + } + else + { + /* Check for end of string. */ + if (len < 0) + { + if (next_c == L'\0') + goto backtrack; + } + else + { + if (pos >= len) + goto backtrack; + } + + /* Read the next character. */ + GET_NEXT_WCHAR(); + } + + next_state = NULL; + for (trans_i = state; trans_i->state; trans_i++) + { + DPRINT((" transition %d-%d (%c-%c) %d to %d\n", + trans_i->code_min, trans_i->code_max, + trans_i->code_min, trans_i->code_max, + trans_i->assertions, trans_i->state_id)); + if (trans_i->code_min <= prev_c && trans_i->code_max >= prev_c) + { + if (trans_i->assertions + && (CHECK_ASSERTIONS(trans_i->assertions) + /* Handle character class transitions. */ + || ((trans_i->assertions & ASSERT_CHAR_CLASS) + && !(cflags & REG_ICASE) + && !tre_isctype((tre_cint_t)prev_c, trans_i->u.class)) + || ((trans_i->assertions & ASSERT_CHAR_CLASS) + && (cflags & REG_ICASE) + && (!tre_isctype(tre_tolower((tre_cint_t)prev_c), + trans_i->u.class) + && !tre_isctype(tre_toupper((tre_cint_t)prev_c), + trans_i->u.class))) + || ((trans_i->assertions & ASSERT_CHAR_CLASS_NEG) + && tre_neg_char_classes_match(trans_i->neg_classes, + (tre_cint_t)prev_c, + cflags & REG_ICASE)))) + { + DPRINT((" assertion failed\n")); + continue; + } + + if (next_state == NULL) + { + /* First matching transition. */ + DPRINT((" Next state is %d\n", trans_i->state_id)); + next_state = trans_i->state; + next_tags = trans_i->tags; + } + else + { + /* Second mathing transition. We may need to backtrack here + to take this transition instead of the first one, so we + push this transition in the backtracking stack so we can + jump back here if needed. */ + DPRINT((" saving state %d for backtracking\n", + trans_i->state_id)); + BT_STACK_PUSH(pos, str_byte, str_wide, trans_i->state, + trans_i->state_id, next_c, tags, mbstate); + { + int *tmp; + for (tmp = trans_i->tags; tmp && *tmp >= 0; tmp++) + stack->item.tags[*tmp] = pos; + } +#if 0 /* XXX - it's important not to look at all transitions here to keep + the stack small! */ + break; +#endif + } + } + } + + if (next_state != NULL) + { + /* Matching transitions were found. Take the first one. */ + state = next_state; + + /* Update the tag values. */ + if (next_tags) + while (*next_tags >= 0) + tags[*next_tags++] = pos; + } + else + { + backtrack: + /* A matching transition was not found. Try to backtrack. */ + if (stack->prev) + { + DPRINT((" backtracking\n")); + if (stack->item.state->assertions && ASSERT_BACKREF) + { + DPRINT((" states_seen[%d] = 0\n", + stack->item.state_id)); + states_seen[stack->item.state_id] = 0; + } + + BT_STACK_POP(); + } + else if (match_eo < 0) + { + /* Try starting from a later position in the input string. */ + /* Check for end of string. */ + if (len < 0) + { + if (next_c == L'\0') + { + DPRINT(("end of string.\n")); + break; + } + } + else + { + if (pos >= len) + { + DPRINT(("end of string.\n")); + break; + } + } + DPRINT(("restarting from next start position\n")); + next_c = next_c_start; +#ifdef TRE_MBSTATE + mbstate = mbstate_start; +#endif /* TRE_MBSTATE */ + str_byte = str_byte_start; + goto retry; + } + else + { + DPRINT(("finished\n")); + break; + } + } + } + + ret = match_eo >= 0 ? REG_OK : REG_NOMATCH; + *match_end_ofs = match_eo; + + error_exit: + tre_bt_mem_destroy(mem); +#ifndef TRE_USE_ALLOCA + if (tags) + xfree(tags); + if (pmatch) + xfree(pmatch); + if (states_seen) + xfree(states_seen); +#endif /* !TRE_USE_ALLOCA */ + + return ret; +} + + +/*********************************************************************** + from regexec.c +***********************************************************************/ + +/* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match + endpoint values. */ +static void +tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, + const tre_tnfa_t *tnfa, int *tags, int match_eo) +{ + tre_submatch_data_t *submatch_data; + unsigned int i, j; + int *parents; + + i = 0; + if (match_eo >= 0 && !(cflags & REG_NOSUB)) + { + /* Construct submatch offsets from the tags. */ + DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo)); + submatch_data = tnfa->submatch_data; + while (i < tnfa->num_submatches && i < nmatch) + { + if (submatch_data[i].so_tag == tnfa->end_tag) + pmatch[i].rm_so = match_eo; + else + pmatch[i].rm_so = tags[submatch_data[i].so_tag]; + + if (submatch_data[i].eo_tag == tnfa->end_tag) + pmatch[i].rm_eo = match_eo; + else + pmatch[i].rm_eo = tags[submatch_data[i].eo_tag]; + + /* If either of the endpoints were not used, this submatch + was not part of the match. */ + if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + + DPRINT(("pmatch[%d] = {t%d = %d, t%d = %d}\n", i, + submatch_data[i].so_tag, pmatch[i].rm_so, + submatch_data[i].eo_tag, pmatch[i].rm_eo)); + i++; + } + /* Reset all submatches that are not within all of their parent + submatches. */ + i = 0; + while (i < tnfa->num_submatches && i < nmatch) + { + if (pmatch[i].rm_eo == -1) + assert(pmatch[i].rm_so == -1); + assert(pmatch[i].rm_so <= pmatch[i].rm_eo); + + parents = submatch_data[i].parents; + if (parents != NULL) + for (j = 0; parents[j] >= 0; j++) + { + DPRINT(("pmatch[%d] parent %d\n", i, parents[j])); + if (pmatch[i].rm_so < pmatch[parents[j]].rm_so + || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo) + pmatch[i].rm_so = pmatch[i].rm_eo = -1; + } + i++; + } + } + + while (i < nmatch) + { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; + i++; + } +} + + +/* + Wrapper functions for POSIX compatible regexp matching. +*/ + +static int +tre_match(const tre_tnfa_t *tnfa, const void *string, size_t len, + size_t nmatch, regmatch_t pmatch[], int eflags) +{ + reg_errcode_t status; + int *tags = NULL, eo; + if (tnfa->num_tags > 0 && nmatch > 0) + { +#ifdef TRE_USE_ALLOCA + tags = alloca(sizeof(*tags) * tnfa->num_tags); +#else /* !TRE_USE_ALLOCA */ + tags = xmalloc(sizeof(*tags) * tnfa->num_tags); +#endif /* !TRE_USE_ALLOCA */ + if (tags == NULL) + return REG_ESPACE; + } + + /* Dispatch to the appropriate matcher. */ + if (tnfa->have_backrefs) + { + /* The regex has back references, use the backtracking matcher. */ + status = tre_tnfa_run_backtrack(tnfa, string, len, tags, eflags, &eo); + } + else + { + /* Exact matching, no back references, use the parallel matcher. */ + status = tre_tnfa_run_parallel(tnfa, string, len, tags, eflags, &eo); + } + + if (status == REG_OK) + /* A match was found, so fill the submatch registers. */ + tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo); +#ifndef TRE_USE_ALLOCA + if (tags) + xfree(tags); +#endif /* !TRE_USE_ALLOCA */ + return status; +} + +int +regexec(const regex_t *preg, const char *str, + size_t nmatch, regmatch_t pmatch[], int eflags) +{ + return tre_match((void *)preg->TRE_REGEX_T_FIELD, str, -1, + nmatch, pmatch, eflags); +} + +/* EOF */ diff --git a/src/regex/tre-mem.c b/src/regex/tre-mem.c new file mode 100644 index 00000000..d7bdd3db --- /dev/null +++ b/src/regex/tre-mem.c @@ -0,0 +1,163 @@ +/* + tre-mem.c - TRE memory allocator + + Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi> + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +/* + This memory allocator is for allocating small memory blocks efficiently + in terms of memory overhead and execution speed. The allocated blocks + cannot be freed individually, only all at once. There can be multiple + allocators, though. +*/ + +#include <stdlib.h> +#include <string.h> + +#include "tre.h" + + +/* Returns a new memory allocator or NULL if out of memory. */ +tre_mem_t +tre_mem_new_impl(int provided, void *provided_block) +{ + tre_mem_t mem; + if (provided) + { + mem = provided_block; + memset(mem, 0, sizeof(*mem)); + } + else + mem = xcalloc(1, sizeof(*mem)); + if (mem == NULL) + return NULL; + return mem; +} + + +/* Frees the memory allocator and all memory allocated with it. */ +void +tre_mem_destroy(tre_mem_t mem) +{ + tre_list_t *tmp, *l = mem->blocks; + + while (l != NULL) + { + xfree(l->data); + tmp = l->next; + xfree(l); + l = tmp; + } + xfree(mem); +} + + +/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the + allocated block or NULL if an underlying malloc() failed. */ +void * +tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, + int zero, size_t size) +{ + void *ptr; + + if (mem->failed) + { + DPRINT(("tre_mem_alloc: oops, called after failure?!\n")); + return NULL; + } + +#ifdef MALLOC_DEBUGGING + if (!provided) + { + ptr = xmalloc(1); + if (ptr == NULL) + { + DPRINT(("tre_mem_alloc: xmalloc forced failure\n")); + mem->failed = 1; + return NULL; + } + xfree(ptr); + } +#endif /* MALLOC_DEBUGGING */ + + if (mem->n < size) + { + /* We need more memory than is available in the current block. + Allocate a new block. */ + tre_list_t *l; + if (provided) + { + DPRINT(("tre_mem_alloc: using provided block\n")); + if (provided_block == NULL) + { + DPRINT(("tre_mem_alloc: provided block was NULL\n")); + mem->failed = 1; + return NULL; + } + mem->ptr = provided_block; + mem->n = TRE_MEM_BLOCK_SIZE; + } + else + { + int block_size; + if (size * 8 > TRE_MEM_BLOCK_SIZE) + block_size = size * 8; + else + block_size = TRE_MEM_BLOCK_SIZE; + DPRINT(("tre_mem_alloc: allocating new %d byte block\n", + block_size)); + l = xmalloc(sizeof(*l)); + if (l == NULL) + { + mem->failed = 1; + return NULL; + } + l->data = xmalloc(block_size); + if (l->data == NULL) + { + xfree(l); + mem->failed = 1; + return NULL; + } + l->next = NULL; + if (mem->current != NULL) + mem->current->next = l; + if (mem->blocks == NULL) + mem->blocks = l; + mem->current = l; + mem->ptr = l->data; + mem->n = block_size; + } + } + + /* Make sure the next pointer will be aligned. */ + size += ALIGN(mem->ptr + size, long); + + /* Allocate from current block. */ + ptr = mem->ptr; + mem->ptr += size; + mem->n -= size; + + /* Set to zero if needed. */ + if (zero) + memset(ptr, 0, size); + + return ptr; +} + +/* EOF */ diff --git a/src/regex/tre.h b/src/regex/tre.h new file mode 100644 index 00000000..bfd171f4 --- /dev/null +++ b/src/regex/tre.h @@ -0,0 +1,269 @@ +/* + tre-internal.h - TRE internal definitions + + Copyright (c) 2001-2006 Ville Laurikari <vl@iki.fi>. + + This 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. + + This 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 this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include <regex.h> +#include <wchar.h> +#include <wctype.h> + +#define TRE_MULTIBYTE 1 +#undef TRE_MBSTATE +#define TRE_WCHAR 1 +#define TRE_USE_SYSTEM_WCTYPE 1 +#define HAVE_WCSTOMBS 1 +#define TRE_MB_CUR_MAX MB_CUR_MAX + +#define NDEBUG + +#define TRE_REGEX_T_FIELD __opaque +typedef int reg_errcode_t; + +typedef wchar_t tre_char_t; + + +#ifdef TRE_DEBUG +#include <stdio.h> +#define DPRINT(msg) do {printf msg; fflush(stdout);} while(0) +#else /* !TRE_DEBUG */ +#define DPRINT(msg) do { } while(0) +#endif /* !TRE_DEBUG */ + +#define elementsof(x) ( sizeof(x) / sizeof(x[0]) ) + +#if 1 +int __mbtowc(wchar_t *, const char *); +#define tre_mbrtowc(pwc, s, n, ps) (__mbtowc((pwc), (s))) +#else +#define tre_mbrtowc(pwc, s, n, ps) (mbtowc((pwc), (s), (n))) +#endif + +/* Wide characters. */ +typedef wint_t tre_cint_t; +#define TRE_CHAR_MAX WCHAR_MAX + +#ifdef TRE_MULTIBYTE +#define TRE_MB_CUR_MAX MB_CUR_MAX +#else /* !TRE_MULTIBYTE */ +#define TRE_MB_CUR_MAX 1 +#endif /* !TRE_MULTIBYTE */ + +#define tre_isalnum iswalnum +#define tre_isalpha iswalpha +#define tre_isblank iswblank +#define tre_iscntrl iswcntrl +#define tre_isdigit iswdigit +#define tre_isgraph iswgraph +#define tre_islower iswlower +#define tre_isprint iswprint +#define tre_ispunct iswpunct +#define tre_isspace iswspace +#define tre_isupper iswupper +#define tre_isxdigit iswxdigit + +#define tre_tolower towlower +#define tre_toupper towupper +#define tre_strlen wcslen + +/* Use system provided iswctype() and wctype(). */ +typedef wctype_t tre_ctype_t; +#define tre_isctype iswctype +#define tre_ctype wctype + +/* Returns number of bytes to add to (char *)ptr to make it + properly aligned for the type. */ +#define ALIGN(ptr, type) \ + ((((long)ptr) % sizeof(type)) \ + ? (sizeof(type) - (((long)ptr) % sizeof(type))) \ + : 0) + +#undef MAX +#undef MIN +#define MAX(a, b) (((a) >= (b)) ? (a) : (b)) +#define MIN(a, b) (((a) <= (b)) ? (a) : (b)) + +/* Define STRF to the correct printf formatter for strings. */ +#define STRF "ls" + +/* TNFA transition type. A TNFA state is an array of transitions, + the terminator is a transition with NULL `state'. */ +typedef struct tnfa_transition tre_tnfa_transition_t; + +struct tnfa_transition { + /* Range of accepted characters. */ + tre_cint_t code_min; + tre_cint_t code_max; + /* Pointer to the destination state. */ + tre_tnfa_transition_t *state; + /* ID number of the destination state. */ + int state_id; + /* -1 terminated array of tags (or NULL). */ + int *tags; + /* Assertion bitmap. */ + int assertions; + /* Assertion parameters. */ + union { + /* Character class assertion. */ + tre_ctype_t class; + /* Back reference assertion. */ + int backref; + } u; + /* Negative character class assertions. */ + tre_ctype_t *neg_classes; +}; + + +/* Assertions. */ +#define ASSERT_AT_BOL 1 /* Beginning of line. */ +#define ASSERT_AT_EOL 2 /* End of line. */ +#define ASSERT_CHAR_CLASS 4 /* Character class in `class'. */ +#define ASSERT_CHAR_CLASS_NEG 8 /* Character classes in `neg_classes'. */ +#define ASSERT_AT_BOW 16 /* Beginning of word. */ +#define ASSERT_AT_EOW 32 /* End of word. */ +#define ASSERT_AT_WB 64 /* Word boundary. */ +#define ASSERT_AT_WB_NEG 128 /* Not a word boundary. */ +#define ASSERT_BACKREF 256 /* A back reference in `backref'. */ +#define ASSERT_LAST 256 + +/* Tag directions. */ +typedef enum { + TRE_TAG_MINIMIZE = 0, + TRE_TAG_MAXIMIZE = 1 +} tre_tag_direction_t; + +/* Instructions to compute submatch register values from tag values + after a successful match. */ +struct tre_submatch_data { + /* Tag that gives the value for rm_so (submatch start offset). */ + int so_tag; + /* Tag that gives the value for rm_eo (submatch end offset). */ + int eo_tag; + /* List of submatches this submatch is contained in. */ + int *parents; +}; + +typedef struct tre_submatch_data tre_submatch_data_t; + + +/* TNFA definition. */ +typedef struct tnfa tre_tnfa_t; + +struct tnfa { + tre_tnfa_transition_t *transitions; + unsigned int num_transitions; + tre_tnfa_transition_t *initial; + tre_tnfa_transition_t *final; + tre_submatch_data_t *submatch_data; + unsigned int num_submatches; + tre_tag_direction_t *tag_directions; + int num_tags; + int end_tag; + int num_states; + int cflags; + int have_backrefs; +}; + +#if 0 +static int +tre_compile(regex_t *preg, const tre_char_t *regex, size_t n, int cflags); + +static void +tre_free(regex_t *preg); + +static void +tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, + const tre_tnfa_t *tnfa, int *tags, int match_eo); + +static reg_errcode_t +tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, + tre_str_type_t type, int *match_tags, int eflags, + int *match_end_ofs); + +static reg_errcode_t +tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len, + tre_str_type_t type, int *match_tags, int eflags, + int *match_end_ofs); + +static reg_errcode_t +tre_tnfa_run_backtrack(const tre_tnfa_t *tnfa, const void *string, + int len, tre_str_type_t type, int *match_tags, + int eflags, int *match_end_ofs); +#endif + +/* from tre-mem.h: */ + +#define TRE_MEM_BLOCK_SIZE 1024 + +typedef struct tre_list { + void *data; + struct tre_list *next; +} tre_list_t; + +typedef struct tre_mem_struct { + tre_list_t *blocks; + tre_list_t *current; + char *ptr; + size_t n; + int failed; + void **provided; +} *tre_mem_t; + +#define tre_mem_new_impl __tre_mem_new_impl +#define tre_mem_alloc_impl __tre_mem_alloc_impl +#define tre_mem_destroy __tre_mem_destroy + +tre_mem_t tre_mem_new_impl(int provided, void *provided_block); +void *tre_mem_alloc_impl(tre_mem_t mem, int provided, void *provided_block, + int zero, size_t size); + +/* Returns a new memory allocator or NULL if out of memory. */ +#define tre_mem_new() tre_mem_new_impl(0, NULL) + +/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the + allocated block or NULL if an underlying malloc() failed. */ +#define tre_mem_alloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 0, size) + +/* Allocates a block of `size' bytes from `mem'. Returns a pointer to the + allocated block or NULL if an underlying malloc() failed. The memory + is set to zero. */ +#define tre_mem_calloc(mem, size) tre_mem_alloc_impl(mem, 0, NULL, 1, size) + +#ifdef TRE_USE_ALLOCA +/* alloca() versions. Like above, but memory is allocated with alloca() + instead of malloc(). */ + +#define tre_mem_newa() \ + tre_mem_new_impl(1, alloca(sizeof(struct tre_mem_struct))) + +#define tre_mem_alloca(mem, size) \ + ((mem)->n >= (size) \ + ? tre_mem_alloc_impl((mem), 1, NULL, 0, (size)) \ + : tre_mem_alloc_impl((mem), 1, alloca(TRE_MEM_BLOCK_SIZE), 0, (size))) +#endif /* TRE_USE_ALLOCA */ + + +/* Frees the memory allocator and all memory allocated with it. */ +void tre_mem_destroy(tre_mem_t mem); + +#define xmalloc malloc +#define xcalloc calloc +#define xfree free +#define xrealloc realloc + +/* EOF */ diff --git a/src/select/poll.c b/src/select/poll.c new file mode 100644 index 00000000..e92943e1 --- /dev/null +++ b/src/select/poll.c @@ -0,0 +1,12 @@ +#include <poll.h> +#include "syscall.h" +#include "libc.h" + +int poll(struct pollfd *fds, nfds_t n, int timeout) +{ + int r; + CANCELPT_BEGIN; + r = syscall3(__NR_poll, (long)fds, n, timeout); + CANCELPT_END; + return r; +} diff --git a/src/select/pselect.c b/src/select/pselect.c new file mode 100644 index 00000000..795c5b0d --- /dev/null +++ b/src/select/pselect.c @@ -0,0 +1,15 @@ +#include <sys/select.h> +#include "syscall.h" +#include "libc.h" + +int pselect(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, const struct timespec *ts, const sigset_t *mask) +{ + int r; + long data[2] = { (long)mask, 8 }; + struct timespec ts_tmp; + if (ts) ts_tmp = *ts; + CANCELPT_BEGIN; + r = syscall6(__NR_pselect6, n, (long)rfds, (long)wfds, (long)efds, ts ? (long)&ts_tmp : 0, (long)data); + CANCELPT_END; + return r; +} diff --git a/src/select/select.c b/src/select/select.c new file mode 100644 index 00000000..a604d094 --- /dev/null +++ b/src/select/select.c @@ -0,0 +1,12 @@ +#include <sys/select.h> +#include "syscall.h" +#include "libc.h" + +int select(int n, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv) +{ + int r; + CANCELPT_BEGIN; + r = syscall5(__NR__newselect, n, (long)rfds, (long)wfds, (long)efds, (long)tv); + CANCELPT_END; + return r; +} diff --git a/src/setjmp/i386/longjmp.s b/src/setjmp/i386/longjmp.s new file mode 100644 index 00000000..c1a956c3 --- /dev/null +++ b/src/setjmp/i386/longjmp.s @@ -0,0 +1,22 @@ +.global _longjmp +.global longjmp +.type _longjmp,%function +.type longjmp,%function +_longjmp: +longjmp: + movl 4(%esp),%edx + movl 8(%esp),%eax + testl %eax,%eax + jnz .L0 + incl %eax +.L0: + movl (%edx),%ebx + movl 4(%edx),%esi + movl 8(%edx),%edi + movl 12(%edx),%ebp + movl 16(%edx),%ecx + movl %ecx,%esp + movl 20(%edx),%ecx + jmp *%ecx +.size _longjmp,.-_longjmp +.size longjmp,.-longjmp diff --git a/src/setjmp/i386/setjmp.s b/src/setjmp/i386/setjmp.s new file mode 100644 index 00000000..6c078b10 --- /dev/null +++ b/src/setjmp/i386/setjmp.s @@ -0,0 +1,23 @@ +.global __setjmp +.global _setjmp +.global setjmp +.type __setjmp,%function +.type _setjmp,%function +.type setjmp,%function +__setjmp: +_setjmp: +setjmp: + movl 4(%esp), %eax + movl %ebx, (%eax) + movl %esi, 4(%eax) + movl %edi, 8(%eax) + movl %ebp, 12(%eax) + leal 4(%esp), %ecx + movl %ecx, 16(%eax) + movl (%esp), %ecx + movl %ecx, 20(%eax) + xorl %eax, %eax + ret +.size __setjmp,.-__setjmp +.size _setjmp,.-_setjmp +.size setjmp,.-setjmp diff --git a/src/setjmp/longjmp.c b/src/setjmp/longjmp.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/setjmp/longjmp.c diff --git a/src/setjmp/setjmp.c b/src/setjmp/setjmp.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/setjmp/setjmp.c diff --git a/src/signal/bsd_signal.c b/src/signal/bsd_signal.c new file mode 100644 index 00000000..0b9a6855 --- /dev/null +++ b/src/signal/bsd_signal.c @@ -0,0 +1,6 @@ +#include <signal.h> + +void (*bsd_signal(int sig, void (*func)(int)))(int) +{ + return signal(sig, func); +} diff --git a/src/signal/getitimer.c b/src/signal/getitimer.c new file mode 100644 index 00000000..222d113e --- /dev/null +++ b/src/signal/getitimer.c @@ -0,0 +1,12 @@ +#include <sys/time.h> +#include "syscall.h" + +int getitimer(int which, struct itimerval *old) +{ + int ret; + long kold[4]; + + if (!(ret = syscall2(__NR_getitimer, which, (long)&kold))) + *old = (struct itimerval){ { kold[0], kold[1] }, { kold[2], kold[3] } }; + return ret; +} diff --git a/src/signal/i386/sigsetjmp.s b/src/signal/i386/sigsetjmp.s new file mode 100644 index 00000000..0e7eefb0 --- /dev/null +++ b/src/signal/i386/sigsetjmp.s @@ -0,0 +1,13 @@ +.global sigsetjmp +sigsetjmp: + mov 4(%esp),%eax + mov 8(%esp),%ecx + mov %ecx,24(%eax) + jecxz 1f + add $28,%eax + push %eax + push $0 + push $2 + call sigprocmask + add $12,%esp +1: jmp setjmp diff --git a/src/signal/kill.c b/src/signal/kill.c new file mode 100644 index 00000000..cc4b51e1 --- /dev/null +++ b/src/signal/kill.c @@ -0,0 +1,7 @@ +#include <signal.h> +#include "syscall.h" + +int kill(pid_t pid, int sig) +{ + return syscall2(__NR_kill, pid, sig); +} diff --git a/src/signal/killpg.c b/src/signal/killpg.c new file mode 100644 index 00000000..315ed447 --- /dev/null +++ b/src/signal/killpg.c @@ -0,0 +1,11 @@ +#include <signal.h> +#include <errno.h> + +int killpg(pid_t pgid, int sig) +{ + if (pgid < 0) { + errno = EINVAL; + return -1; + } + return kill(-pgid, sig); +} diff --git a/src/signal/raise.c b/src/signal/raise.c new file mode 100644 index 00000000..52f8b428 --- /dev/null +++ b/src/signal/raise.c @@ -0,0 +1,7 @@ +#include <signal.h> +#include "syscall.h" + +int raise(int sig) +{ + return __syscall_kill(__syscall_getpid(), sig); +} diff --git a/src/signal/setitimer.c b/src/signal/setitimer.c new file mode 100644 index 00000000..cacab036 --- /dev/null +++ b/src/signal/setitimer.c @@ -0,0 +1,15 @@ +#include <sys/time.h> +#include "syscall.h" + +int setitimer(int which, const struct itimerval *new, struct itimerval *old) +{ + int ret; + long knew[4] = { + new->it_interval.tv_sec, new->it_interval.tv_usec, + new->it_value.tv_sec, new->it_value.tv_usec + }, kold[4]; + + if (!(ret = syscall3(__NR_setitimer, which, (long)&knew, old ? (long)&kold : 0)) && old) + *old = (struct itimerval){ { kold[0], kold[1] }, { kold[2], kold[3] } }; + return ret; +} diff --git a/src/signal/sigaction.c b/src/signal/sigaction.c new file mode 100644 index 00000000..4acd1730 --- /dev/null +++ b/src/signal/sigaction.c @@ -0,0 +1,48 @@ +#include <stdlib.h> +#include <signal.h> +#include <errno.h> +#include "syscall.h" +#include "pthread_impl.h" + +static void restorer() +{ + syscall0(__NR_rt_sigreturn); +} + +int __libc_sigaction(int sig, const struct sigaction *sa, struct sigaction *old) +{ + struct { + void *handler; + unsigned long flags; + void (*restorer)(void); + sigset_t mask; + } ksa, kold; + long pksa=0, pkold=0; + if (sa) { + ksa.handler = sa->sa_handler; + ksa.flags = sa->sa_flags; + ksa.restorer = restorer; + ksa.mask = sa->sa_mask; + pksa = (long)&ksa; + } + if (old) pkold = (long)&kold; + if (syscall4(__NR_rt_sigaction, sig, pksa, pkold, 8)) + return -1; + if (old) { + old->sa_handler = kold.handler; + old->sa_flags = kold.flags; + old->sa_mask = kold.mask; + } + return 0; +} + +int __sigaction(int sig, const struct sigaction *sa, struct sigaction *old) +{ + if (sig == SIGCANCEL || sig == SIGSYSCALL) { + errno = EINVAL; + return -1; + } + return __libc_sigaction(sig, sa, old); +} + +weak_alias(__sigaction, sigaction); diff --git a/src/signal/sigaddset.c b/src/signal/sigaddset.c new file mode 100644 index 00000000..23e655db --- /dev/null +++ b/src/signal/sigaddset.c @@ -0,0 +1,13 @@ +#include <signal.h> +#include <errno.h> + +int sigaddset(sigset_t *set, int sig) +{ + unsigned s = sig-1; + if (s >= 8*sizeof(sigset_t)) { + errno = EINVAL; + return -1; + } + set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1); + return 0; +} diff --git a/src/signal/sigaltstack.c b/src/signal/sigaltstack.c new file mode 100644 index 00000000..3cc2d456 --- /dev/null +++ b/src/signal/sigaltstack.c @@ -0,0 +1,8 @@ +#include <signal.h> +#include "syscall.h" + +int sigaltstack(const stack_t *ss, stack_t *old) +{ + /* depends on kernel struct matching */ + return syscall2(__NR_sigaltstack, (long)ss, (long)old); +} diff --git a/src/signal/sigdelset.c b/src/signal/sigdelset.c new file mode 100644 index 00000000..14042fb8 --- /dev/null +++ b/src/signal/sigdelset.c @@ -0,0 +1,13 @@ +#include <signal.h> +#include <errno.h> + +int sigdelset(sigset_t *set, int sig) +{ + unsigned s = sig-1; + if (s >= 8*sizeof(sigset_t)) { + errno = EINVAL; + return -1; + } + set->__bits[s/8/sizeof *set->__bits] &=~(1UL<<(s&8*sizeof *set->__bits-1)); + return 0; +} diff --git a/src/signal/sigemptyset.c b/src/signal/sigemptyset.c new file mode 100644 index 00000000..91f77adf --- /dev/null +++ b/src/signal/sigemptyset.c @@ -0,0 +1,8 @@ +#include <signal.h> +#include <string.h> + +int sigemptyset(sigset_t *set) +{ + memset(set, 0, sizeof *set); + return 0; +} diff --git a/src/signal/sigfillset.c b/src/signal/sigfillset.c new file mode 100644 index 00000000..fab50a52 --- /dev/null +++ b/src/signal/sigfillset.c @@ -0,0 +1,8 @@ +#include <signal.h> +#include <string.h> + +int sigfillset(sigset_t *set) +{ + memset(set, -1, sizeof *set); + return 0; +} diff --git a/src/signal/sighold.c b/src/signal/sighold.c new file mode 100644 index 00000000..5b0f6b18 --- /dev/null +++ b/src/signal/sighold.c @@ -0,0 +1,11 @@ +#include <signal.h> +#include <stdlib.h> + +int sighold(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + if (sigaddset(&mask, sig) < 0) return -1; + return sigprocmask(SIG_BLOCK, &mask, NULL); +} diff --git a/src/signal/sigignore.c b/src/signal/sigignore.c new file mode 100644 index 00000000..98dff61e --- /dev/null +++ b/src/signal/sigignore.c @@ -0,0 +1,12 @@ +#include <signal.h> +#include <stdlib.h> + +int sigignore(int sig) +{ + struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + return sigaction(sig, &sa, NULL); +} diff --git a/src/signal/siginterrupt.c b/src/signal/siginterrupt.c new file mode 100644 index 00000000..60b34054 --- /dev/null +++ b/src/signal/siginterrupt.c @@ -0,0 +1,13 @@ +#include <stdlib.h> +#include <signal.h> + +int siginterrupt(int sig, int flag) +{ + struct sigaction sa; + + sigaction(sig, NULL, &sa); + if (flag) sa.sa_flags &= ~SA_RESTART; + else sa.sa_flags |= SA_RESTART; + + return sigaction(sig, &sa, NULL); +} diff --git a/src/signal/sigismember.c b/src/signal/sigismember.c new file mode 100644 index 00000000..afd29e52 --- /dev/null +++ b/src/signal/sigismember.c @@ -0,0 +1,12 @@ +#include <signal.h> +#include <errno.h> + +int sigismember(const sigset_t *set, int sig) +{ + unsigned s = sig-1; + if (s >= 8*sizeof(sigset_t)) { + errno = EINVAL; + return -1; + } + return !!(set->__bits[s/8/sizeof *set->__bits] & 1UL<<(s&8*sizeof *set->__bits-1)); +} diff --git a/src/signal/siglongjmp.c b/src/signal/siglongjmp.c new file mode 100644 index 00000000..33ac30ea --- /dev/null +++ b/src/signal/siglongjmp.c @@ -0,0 +1,12 @@ +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> + +void siglongjmp(sigjmp_buf buf, int ret) +{ + long *flag = buf + sizeof(jmp_buf)/sizeof(long); + sigset_t *mask = (void *)(flag + 1); + if (*flag) + sigprocmask (SIG_SETMASK, mask, NULL); + longjmp((void *)buf, ret); +} diff --git a/src/signal/signal.c b/src/signal/signal.c new file mode 100644 index 00000000..08902760 --- /dev/null +++ b/src/signal/signal.c @@ -0,0 +1,13 @@ +#include <signal.h> +#include <stddef.h> +#include "syscall.h" + +int __sigaction(int, const struct sigaction *, struct sigaction *); + +void (*signal(int sig, void (*func)(int)))(int) +{ + struct sigaction sa = { .sa_handler = func, .sa_flags = SA_RESTART }; + if (__sigaction(sig, &sa, &sa) < 0) + return SIG_ERR; + return sa.sa_handler; +} diff --git a/src/signal/sigpause.c b/src/signal/sigpause.c new file mode 100644 index 00000000..263c00f5 --- /dev/null +++ b/src/signal/sigpause.c @@ -0,0 +1,11 @@ +#include <signal.h> +#include <stdlib.h> + +int sigpause(int sig) +{ + sigset_t mask; + + if (sigprocmask(0, NULL, &mask) < 0 || sigdelset(&mask, sig) < 0) + return -1; + return sigsuspend(&mask); +} diff --git a/src/signal/sigpending.c b/src/signal/sigpending.c new file mode 100644 index 00000000..7deda256 --- /dev/null +++ b/src/signal/sigpending.c @@ -0,0 +1,7 @@ +#include <signal.h> +#include "syscall.h" + +int sigpending(sigset_t *set) +{ + return syscall2(__NR_rt_sigpending, (long)set, SYSCALL_SIGSET_SIZE); +} diff --git a/src/signal/sigprocmask.c b/src/signal/sigprocmask.c new file mode 100644 index 00000000..e89f8765 --- /dev/null +++ b/src/signal/sigprocmask.c @@ -0,0 +1,23 @@ +#include <signal.h> +#include "syscall.h" +#include "libc.h" + +int __libc_sigprocmask(int how, const sigset_t *set, sigset_t *old) +{ + return syscall4(__NR_rt_sigprocmask, how, (long)set, (long)old, 8); +} + +int __sigprocmask(int how, const sigset_t *set, sigset_t *old) +{ + sigset_t tmp; + /* Quickly mask out bits 32 and 33 (thread control signals) */ + if (0 && how != SIG_UNBLOCK && (set->__bits[4/sizeof *set->__bits] & 3UL<<(32&8*sizeof *set->__bits-1))) { + tmp = *set; + set = &tmp; + tmp.__bits[4/sizeof *set->__bits] &= ~(3UL<<(32&8*sizeof *set->__bits-1)); + } + return __libc_sigprocmask(how, set, old); +} + +weak_alias(__sigprocmask, sigprocmask); +weak_alias(__sigprocmask, pthread_sigmask); diff --git a/src/signal/sigqueue.c b/src/signal/sigqueue.c new file mode 100644 index 00000000..ce3abf6c --- /dev/null +++ b/src/signal/sigqueue.c @@ -0,0 +1,14 @@ +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include "syscall.h" + +int sigqueue(pid_t pid, int sig, const union sigval value) +{ + siginfo_t si = { + .si_signo = sig, + .si_code = -1, + .si_value = value, + }; + return syscall3(__NR_rt_sigqueueinfo, pid, sig, (long)&si); +} diff --git a/src/signal/sigrelse.c b/src/signal/sigrelse.c new file mode 100644 index 00000000..b0b3024b --- /dev/null +++ b/src/signal/sigrelse.c @@ -0,0 +1,11 @@ +#include <signal.h> +#include <stdlib.h> + +int sigrelse(int sig) +{ + sigset_t mask; + + sigemptyset(&mask); + if (sigaddset(&mask, sig) < 0) return -1; + return sigprocmask(SIG_UNBLOCK, &mask, NULL); +} diff --git a/src/signal/sigrtmax.c b/src/signal/sigrtmax.c new file mode 100644 index 00000000..0ef29873 --- /dev/null +++ b/src/signal/sigrtmax.c @@ -0,0 +1,4 @@ +int __libc_current_sigrtmax() +{ + return 64; +} diff --git a/src/signal/sigrtmin.c b/src/signal/sigrtmin.c new file mode 100644 index 00000000..7ad06d22 --- /dev/null +++ b/src/signal/sigrtmin.c @@ -0,0 +1,4 @@ +int __libc_current_sigrtmin() +{ + return 34; +} diff --git a/src/signal/sigset.c b/src/signal/sigset.c new file mode 100644 index 00000000..1b6b38fd --- /dev/null +++ b/src/signal/sigset.c @@ -0,0 +1,28 @@ +#include <signal.h> +#include <stdlib.h> + +void (*sigset(int sig, void (*handler)(int)))(int) +{ + struct sigaction sa, sa_old; + sigset_t mask; + + sigemptyset(&mask); + if (sigaddset(&mask, sig) < 0) + return SIG_ERR; + + if (handler == SIG_HOLD) { + if (sigaction(sig, NULL, &sa_old) < 0) + return SIG_ERR; + if (sigprocmask(SIG_BLOCK, &mask, &mask) < 0) + return SIG_ERR; + } else { + sa.sa_handler = handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + if (sigaction(sig, &sa, &sa_old) < 0) + return SIG_ERR; + if (sigprocmask(SIG_UNBLOCK, &mask, &mask) < 0) + return SIG_ERR; + } + return sigismember(&mask, sig) ? SIG_HOLD : sa_old.sa_handler; +} diff --git a/src/signal/sigsetjmp.c b/src/signal/sigsetjmp.c new file mode 100644 index 00000000..a6667a27 --- /dev/null +++ b/src/signal/sigsetjmp.c @@ -0,0 +1,17 @@ +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> + +/* !!! This function will not work unless the compiler performs + * tail call optimization. Machine-specific asm versions should + * be created instead even though the workaround (tail call) + * is entirely non-machine-specific... */ + +int sigsetjmp(sigjmp_buf buf, int save) +{ + long *flag = buf + sizeof(jmp_buf)/sizeof(long); + sigset_t *mask = (void *)(flag + 1); + if ((*flag = save)) + sigprocmask (SIG_SETMASK, NULL, mask); + return setjmp((void *)buf); +} diff --git a/src/signal/sigsuspend.c b/src/signal/sigsuspend.c new file mode 100644 index 00000000..1acdab06 --- /dev/null +++ b/src/signal/sigsuspend.c @@ -0,0 +1,7 @@ +#include <signal.h> +#include "syscall.h" + +int sigsuspend(const sigset_t *mask) +{ + return syscall2(__NR_rt_sigsuspend, (long)mask, sizeof(sigset_t)); +} diff --git a/src/signal/sigtimedwait.c b/src/signal/sigtimedwait.c new file mode 100644 index 00000000..155185de --- /dev/null +++ b/src/signal/sigtimedwait.c @@ -0,0 +1,12 @@ +#include <signal.h> +#include "syscall.h" + +int sigtimedwait(const sigset_t *mask, siginfo_t *si, const struct timespec *timeout) +{ + long k_timeout[2]; + if (timeout) { + k_timeout[0] = timeout->tv_sec; + k_timeout[1] = timeout->tv_nsec; + } + return syscall4(__NR_rt_sigtimedwait, (long)mask, (long)si, timeout ? (long)k_timeout : 0, SYSCALL_SIGSET_SIZE); +} diff --git a/src/signal/sigwait.c b/src/signal/sigwait.c new file mode 100644 index 00000000..9569d6b0 --- /dev/null +++ b/src/signal/sigwait.c @@ -0,0 +1,11 @@ +#include <signal.h> +#include <stddef.h> + +int sigwait(const sigset_t *mask, int *sig) +{ + siginfo_t si; + if (sigtimedwait(mask, &si, NULL) < 0) + return -1; + *sig = si.si_signo; + return 0; +} diff --git a/src/signal/sigwaitinfo.c b/src/signal/sigwaitinfo.c new file mode 100644 index 00000000..e79feb91 --- /dev/null +++ b/src/signal/sigwaitinfo.c @@ -0,0 +1,7 @@ +#include <signal.h> +#include <stddef.h> + +int sigwaitinfo(const sigset_t *mask, siginfo_t *si) +{ + return sigtimedwait(mask, si, NULL); +} diff --git a/src/stat/chmod.c b/src/stat/chmod.c new file mode 100644 index 00000000..cb310fec --- /dev/null +++ b/src/stat/chmod.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int chmod(const char *path, mode_t mode) +{ + return syscall2(__NR_chmod, (long)path, mode); +} diff --git a/src/stat/fchmod.c b/src/stat/fchmod.c new file mode 100644 index 00000000..91897383 --- /dev/null +++ b/src/stat/fchmod.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int fchmod(int fd, mode_t mode) +{ + return syscall2(__NR_fchmod, fd, mode); +} diff --git a/src/stat/fchmodat.c b/src/stat/fchmodat.c new file mode 100644 index 00000000..f4f22b2c --- /dev/null +++ b/src/stat/fchmodat.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int fchmodat(int fd, const char *path, mode_t mode, int flag) +{ + return syscall4(__NR_fchmodat, fd, (long)path, mode, flag); +} diff --git a/src/stat/fstat.c b/src/stat/fstat.c new file mode 100644 index 00000000..5f643301 --- /dev/null +++ b/src/stat/fstat.c @@ -0,0 +1,10 @@ +#include <sys/stat.h> +#include "syscall.h" +#include "libc.h" + +int fstat(int fd, struct stat *buf) +{ + return syscall2(__NR_fstat64, fd, (long)buf); +} + +LFS64(fstat); diff --git a/src/stat/fstatat.c b/src/stat/fstatat.c new file mode 100644 index 00000000..22bc2934 --- /dev/null +++ b/src/stat/fstatat.c @@ -0,0 +1,10 @@ +#include <sys/stat.h> +#include "syscall.h" +#include "libc.h" + +int fstatat(int fd, const char *path, struct stat *buf, int flag) +{ + return syscall4(__NR_fstatat64, fd, (long)path, (long)buf, flag); +} + +LFS64(fstatat); diff --git a/src/stat/fstatvfs.c b/src/stat/fstatvfs.c new file mode 100644 index 00000000..83bd7486 --- /dev/null +++ b/src/stat/fstatvfs.c @@ -0,0 +1,13 @@ +#include <sys/statvfs.h> +#include "syscall.h" +#include "libc.h" + +int fstatvfs(int fd, struct statvfs *buf) +{ + return syscall2(__NR_fstatfs64, fd, (long)buf); +} + +weak_alias(fstatvfs, fstatfs); + +LFS64(fstatvfs); +LFS64(fstatfs); diff --git a/src/stat/lstat.c b/src/stat/lstat.c new file mode 100644 index 00000000..afdb5538 --- /dev/null +++ b/src/stat/lstat.c @@ -0,0 +1,10 @@ +#include <sys/stat.h> +#include "syscall.h" +#include "libc.h" + +int lstat(const char *path, struct stat *buf) +{ + return syscall2(__NR_lstat64, (long)path, (long)buf); +} + +LFS64(lstat); diff --git a/src/stat/mkdir.c b/src/stat/mkdir.c new file mode 100644 index 00000000..8295cad5 --- /dev/null +++ b/src/stat/mkdir.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int mkdir(const char *path, mode_t mode) +{ + return syscall2(__NR_mkdir, (long)path, mode); +} diff --git a/src/stat/mkdirat.c b/src/stat/mkdirat.c new file mode 100644 index 00000000..1fb38250 --- /dev/null +++ b/src/stat/mkdirat.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int mkdirat(int fd, const char *path, mode_t mode) +{ + return syscall3(__NR_mkdirat, fd, (long)path, mode); +} diff --git a/src/stat/mkfifo.c b/src/stat/mkfifo.c new file mode 100644 index 00000000..60efcf73 --- /dev/null +++ b/src/stat/mkfifo.c @@ -0,0 +1,6 @@ +#include <sys/stat.h> + +int mkfifo(const char *path, mode_t mode) +{ + return mknod(path, mode | S_IFIFO, 0); +} diff --git a/src/stat/mkfifoat.c b/src/stat/mkfifoat.c new file mode 100644 index 00000000..d3a1f970 --- /dev/null +++ b/src/stat/mkfifoat.c @@ -0,0 +1,6 @@ +#include <sys/stat.h> + +int mkfifoat(int fd, const char *path, mode_t mode) +{ + return mknodat(fd, path, mode | S_IFIFO, 0); +} diff --git a/src/stat/mknod.c b/src/stat/mknod.c new file mode 100644 index 00000000..0123eeef --- /dev/null +++ b/src/stat/mknod.c @@ -0,0 +1,10 @@ +#include <sys/stat.h> +#include "syscall.h" + +int mknod(const char *path, mode_t mode, dev_t dev) +{ + /* since dev_t is system-specific anyway we defer to the idiotic + * legacy-compatible bitfield mapping of the type.. at least we've + * made it large enough to leave space for future expansion.. */ + return syscall3(__NR_mknod, (long)path, mode, dev & 0xffff); +} diff --git a/src/stat/mknodat.c b/src/stat/mknodat.c new file mode 100644 index 00000000..b5687e47 --- /dev/null +++ b/src/stat/mknodat.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +int mknodat(int fd, const char *path, mode_t mode, dev_t dev) +{ + return syscall4(__NR_mknodat, fd, (long)path, mode, dev & 0xffff); +} diff --git a/src/stat/stat.c b/src/stat/stat.c new file mode 100644 index 00000000..67640cc1 --- /dev/null +++ b/src/stat/stat.c @@ -0,0 +1,10 @@ +#include <sys/stat.h> +#include "syscall.h" +#include "libc.h" + +int stat(const char *path, struct stat *buf) +{ + return syscall2(__NR_stat64, (long)path, (long)buf); +} + +LFS64(stat); diff --git a/src/stat/statvfs.c b/src/stat/statvfs.c new file mode 100644 index 00000000..55a05d52 --- /dev/null +++ b/src/stat/statvfs.c @@ -0,0 +1,13 @@ +#include <sys/statvfs.h> +#include "syscall.h" +#include "libc.h" + +int statvfs(const char *path, struct statvfs *buf) +{ + return syscall2(__NR_statfs64, (long)path, (long)buf); +} + +weak_alias(statvfs, statfs); + +LFS64(statvfs); +LFS64(statfs); diff --git a/src/stat/umask.c b/src/stat/umask.c new file mode 100644 index 00000000..49cb48a6 --- /dev/null +++ b/src/stat/umask.c @@ -0,0 +1,7 @@ +#include <sys/stat.h> +#include "syscall.h" + +mode_t umask(mode_t mode) +{ + return syscall1(__NR_umask, mode); +} diff --git a/src/stdio/__fclose_ca.c b/src/stdio/__fclose_ca.c new file mode 100644 index 00000000..e0b12a15 --- /dev/null +++ b/src/stdio/__fclose_ca.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int __fclose_ca(FILE *f) +{ + return f->close(f); +} diff --git a/src/stdio/__fdopen.c b/src/stdio/__fdopen.c new file mode 100644 index 00000000..6ad7c57d --- /dev/null +++ b/src/stdio/__fdopen.c @@ -0,0 +1,52 @@ +#include "stdio_impl.h" + +FILE *__fdopen(int fd, const char *mode) +{ + FILE *f; + struct termios tio; + int plus = !!strchr(mode, '+'); + + /* Check for valid initial mode character */ + if (!strchr("rwa", *mode)) return 0; + + /* Allocate FILE+buffer or fail */ + if (!(f=malloc(sizeof *f + UNGET + BUFSIZ))) return 0; + + /* Zero-fill only the struct, not the buffer */ + memset(f, 0, sizeof *f); + + /* Impose mode restrictions */ + if (!plus) f->flags = (*mode == 'r') ? F_NOWR : F_NORD; + + /* Set append mode on fd if opened for append */ + if (*mode == 'a') { + int flags = __syscall_fcntl(fd, F_GETFL, 0); + __syscall_fcntl(fd, F_SETFL, flags | O_APPEND); + } + + f->fd = fd; + f->buf = (unsigned char *)f + sizeof *f + UNGET; + f->buf_size = BUFSIZ; + + /* Activate line buffered mode for terminals */ + f->lbf = EOF; + if (!(f->flags & F_NOWR) && !__syscall_ioctl(fd, TCGETS, &tio)) + f->lbf = '\n'; + + /* Initialize op ptrs. No problem if some are unneeded. */ + f->read = __stdio_read; + f->write = __stdio_write; + f->seek = __stdio_seek; + f->close = __stdio_close; + + /* Add new FILE to open file list */ + OFLLOCK(); + f->next = ofl_head; + if (ofl_head) ofl_head->prev = f; + ofl_head = f; + OFLUNLOCK(); + + return f; +} + +weak_alias(__fdopen, fdopen); diff --git a/src/stdio/__fopen_rb_ca.c b/src/stdio/__fopen_rb_ca.c new file mode 100644 index 00000000..57d9b73c --- /dev/null +++ b/src/stdio/__fopen_rb_ca.c @@ -0,0 +1,18 @@ +#include "stdio_impl.h" + +FILE *__fopen_rb_ca(const char *filename, FILE *f, unsigned char *buf, size_t len) +{ + memset(f, 0, sizeof *f); + + f->fd = __syscall_open(filename, O_RDONLY, 0); + if (f->fd < 0) return 0; + + f->flags = F_NOWR | F_PERM; + f->buf = buf + UNGET; + f->buf_size = len - UNGET; + f->read = __stdio_read; + f->seek = __stdio_seek; + f->close = __stdio_close; + + return f; +} diff --git a/src/stdio/__fpending.c b/src/stdio/__fpending.c new file mode 100644 index 00000000..a4334e23 --- /dev/null +++ b/src/stdio/__fpending.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +size_t __fpending(FILE *f) +{ + return f->wend ? f->wpos - f->wbase : 0; +} diff --git a/src/stdio/__ofl.c b/src/stdio/__ofl.c new file mode 100644 index 00000000..7d9652c8 --- /dev/null +++ b/src/stdio/__ofl.c @@ -0,0 +1,3 @@ +#include "stdio_impl.h" + +struct ofl __ofl; diff --git a/src/stdio/__overflow.c b/src/stdio/__overflow.c new file mode 100644 index 00000000..e35104de --- /dev/null +++ b/src/stdio/__overflow.c @@ -0,0 +1,52 @@ +#include "stdio_impl.h" + +static int overflow(FILE *f, int c) +{ + /* Initialize if we're not already writing */ + if (!f->wend) { + /* Fail if we're in error state or unwritable. */ + if (f->flags & (F_ERR|F_NOWR)) return EOF; + + /* Set byte orientation -1,0=>-1; 1=>1 */ + f->mode |= f->mode-1; + + /* Clear read buffer (easier than summoning nasal demons) */ + f->rpos = f->rend = f->rstop = 0; + + /* Activate write through the buffer */ + f->wpos = f->wbase = f->buf; + f->wend = f->buf + f->buf_size; + f->wstop = (f->lbf < 0) ? f->wend - 1 : 0; + } + + /* Buffer can always hold at least 1 byte... */ + if (c != EOF) { + *f->wpos++ = c; + if (f->wpos <= f->wstop && c != f->lbf) return c; + } + /* ...since if the next call fails, buffer is empty. */ + if (f->write(f, f->wbase, f->wpos - f->wbase) < 0) { + f->flags |= F_ERR; + f->wpos = f->wbase = f->wend = f->wstop = 0; + return EOF; + } + + /* Buffer is empty so reset position to beginning */ + f->wpos = f->wbase; + + return c; +} + +int __overflow(FILE *f, int c) +{ + return overflow(f, c & 0xff); +} + +int __oflow(FILE *f) +{ + overflow(f, EOF); + return (f->flags & F_ERR) ? EOF : 0; +} + +/* Link flush-on-exit code iff any stdio write functions are linked. */ +int (*const __fflush_on_exit)(FILE *) = fflush; diff --git a/src/stdio/__scanf.c b/src/stdio/__scanf.c new file mode 100644 index 00000000..185615d3 --- /dev/null +++ b/src/stdio/__scanf.c @@ -0,0 +1,487 @@ +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <wchar.h> +#include <wctype.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <float.h> + +#include "__scanf.h" + +static int read(rctx_t *r) +{ + if (--r->w < 0) return r->w = -1; + if (r->u) r->u = 0; + else r->read(r); + return r->c; +} + +static void unread(rctx_t *r) +{ + //if (r->u || r->w < 0) return; + if (r->w < 0) return; + r->w++; + r->u = 1; +} + +#define SIZE_hh -2 +#define SIZE_h -1 +#define SIZE_def 0 +#define SIZE_l 1 +#define SIZE_ll 2 +#define SIZE_L 3 + +static void store_int(void *dest, int size, int neg, unsigned long long i) +{ + if (!dest) return; + if (neg) i = -i; + switch (size) { + case SIZE_hh: + *(char *)dest = i; + break; + case SIZE_h: + *(short *)dest = i; + break; + case SIZE_def: + *(int *)dest = i; + break; + case SIZE_l: + *(long *)dest = i; + break; + case SIZE_ll: + *(long long *)dest = i; + break; + } +} + +static void *arg_n(va_list ap, unsigned int n) +{ + void *p; + unsigned int i; + va_list ap2; + va_copy(ap2, ap); + for (i=n; i>1; i--) va_arg(ap2, void *); + p = va_arg(ap2, void *); + va_end(ap2); + return p; +} + +int __scanf(rctx_t *r, const wchar_t *fmt, va_list ap) +{ + int mode=0; + int width; + int size; + const wchar_t *p, *z; + int c, l, t, m; + long long dummy; + char *s; + wchar_t *wcs; + mbstate_t st; + int wide = r->wide; + void *dest=NULL; + int invert; + unsigned long long i=0; + int neg=0; + int matches=0; + long double f; + int (*is_space)(int) = r->is_space; + + for (p=fmt; *p; ) { + if (is_space(*p)) { + do p++; while (is_space(*p)); + do r->w=1; while (is_space(read(r))); + unread(r); + continue; + } else if (*p != '%' || p[1] == '%') { + if (*p == '%') p++; + r->w = 1; + if (*p++ != read(r)) + goto match_fail; + continue; + } + p++; + if (mode != 1) { + for (z=p; isdigit(*z); z++); + if (*z != '$' && *z != '*') { + if (mode == 0) mode = 1; + else goto fmt_fail; + } else if (*z != '*') { + int pos = 0; + mode = 2; + for (; p<z; p++) { + pos = 10*pos + *p - '0'; + } + p++; + if (!pos) goto fmt_fail; + dest = arg_n(ap, pos); + } + } + if (*p == '*') { + dest = NULL; + p++; + } else if (mode == 1) { + dest = va_arg(ap, void *); + } + + if (!*p) goto fmt_fail; + + width = 0; + for (; isdigit(*p); p++) { + width = 10*width + *p - '0'; + } + + size = 0; + switch (*p++) { + case 0: + goto fmt_fail; + case 'h': + if (*p == 'h') p++, size = SIZE_hh; + else size = SIZE_h; + break; + case 'l': + if (*p == 'l') p++, size = SIZE_ll; + else size = SIZE_l; + break; + case 'j': + size = SIZE_ll; + break; + case 'z': + case 't': + size = SIZE_l; + break; + case 'L': + size = SIZE_L; + break; + case 'd': case 'i': case 'o': case 'u': case 'x': + case 'a': case 'e': case 'f': case 'g': + case 'A': case 'E': case 'F': case 'G': case 'X': + case 's': case 'c': case '[': + case 'S': case 'C': + case 'p': case 'n': + p--; + break; + default: + goto fmt_fail; + } + + t = *p++; + + switch (t) { + case 'C': + case 'c': + if (width < 1) width = 1; + case 's': + if (size == SIZE_l) t &= ~0x20; + case 'd': case 'i': case 'o': case 'u': case 'x': + case 'a': case 'e': case 'f': case 'g': + case 'A': case 'E': case 'F': case 'G': case 'X': + case '[': case 'S': + case 'p': case 'n': + if (width < 1) width = INT_MAX; + break; + default: + goto fmt_fail; + } + + r->w = width; + + if (t != 'n') { + if (read(r) < 0) goto input_fail; + unread(r); + } + + switch (t) { + case 'n': + store_int(dest, size, 0, r->l - r->u); + /* do not increment match count, etc! */ + continue; + case 'C': + wcs = dest ? dest : (void *)&dummy; + st = (mbstate_t){ 0 }; + while ((c=read(r)) >= 0) { + if (wide) { + if (dest) *wcs++ = c; + } else { + char ch = c; + switch (mbrtowc(wcs, &ch, 1, &st)) { + case -1: + goto enc_fail; + case -2: + break; + default: + if (dest) wcs++; + } + } + } + if (r->w > 0) goto match_fail; + break; + case 'c': + s = dest ? dest : (void *)&dummy; + while ((c=read(r)) >= 0) { + if (wide) { + if ((l=wctomb(s, c)) < 0) + goto enc_fail; + if (dest) s += l; + } else { + if (dest) *s++ = c; + } + } + if (r->w > 0) goto match_fail; + break; + case '[': + wcs = dest ? dest : (void *)&dummy; + s = dest ? dest : (void *)&dummy; + if (!wide && size == SIZE_l) st = (mbstate_t){ 0 }; + + if (*p == '^') p++, invert = 1; + else invert = 0; + + if (wide) { + for (m=0; (c=read(r)) >= 0; m=1) { + for (z=p; *z && *z != c && (*z != ']' || z==p); z++); + if (!*z) goto fmt_fail; + if (*z == c && (*z != ']' || z==p)) { + if (invert) break; + } else { + if (!invert) break; + } + if (size == SIZE_l) { + if (dest) *wcs++ = c; + } else { + if ((l=wctomb(s, c)) < 0) + goto enc_fail; + if (dest) s += l; + } + } + for (p++; *p && *p != ']'; p++); + p++; + } else { + unsigned char scanset[257]; + memset(scanset, invert, sizeof scanset); + scanset[0] = 0; + for (z=p; *z && (*z != ']' || z==p); z++) + scanset[1+*z] = 1-invert; + if (!*z) goto fmt_fail; + p=z+1; + c=0; + for (m=0; scanset[(c=read(r))+1]; m=1) { + if (size == SIZE_l) { + char ch = c; + switch (mbrtowc(wcs, &ch, 1, &st)) { + case -1: + goto enc_fail; + case -2: + break; + default: + if (dest) wcs++; + } + } else { + if (dest) *s++ = c; + } + } + } + if (!m) goto match_fail; + if (dest) { + if (size == SIZE_l) *wcs++ = 0; + else *s++ = 0; + } + break; + default: + /* read unlimited number of spaces, then reset width */ + do r->w = 1; while (is_space(c = read(r))); + if (c < 0) goto input_fail; + unread(r); + r->w = width; + } + + switch (t) { + case 'p': + case 'X': + t = 'x'; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + i = m = neg = 0; + if ((c=read(r)) == '-') neg=1; + else if (c != '+') unread(r); + switch (t) { + case 'i': + case 'x': + if ((c=read(r)) != '0') { + if (t == 'i') t = 'd'; + unread(r); + break; + } + if (((c=read(r))|0x20) != 'x') { + if (t == 'i') { + t = 'o'; + /* lone 0 is valid octal */ + if ((unsigned)(c-'0') >= 8) { + m = 1; + goto int_finish; + } + } + unread(r); + break; + } + t = 'x'; + } + } + + switch (t) { + case 'd': + case 'u': + for (m=0; isdigit(c=read(r)); m=1) + i = 10*i + c-'0'; + goto int_finish; + case 'o': + for (m=0; (unsigned)(c=read(r))-'0' < 8; m=1) + i = (i<<3) + c-'0'; + goto int_finish; + case 'x': + for (m=0; ; m=1) { + if (isdigit(c=read(r))) { + i = (i<<4) + c-'0'; + } else if ((unsigned)(c|0x20)-'a' < 6) { + i = (i<<4) + (c|0x20)-'a'+10; + } else break; + } + int_finish: + if (!m) goto match_fail; + store_int(dest, size, neg, i); + break; + case 'a': + case 'e': + case 'f': + case 'g': + f = 0.0; + neg = m = 0; + if ((c=read(r)) == '-') neg=1; + else if (c != '+') unread(r); + /* FIXME: check for INF/NAN strings here */ + if (read(r)=='0' && (m=1, (read(r)|0x20) == 'x')) + goto hexfloat; + else unread(r); + for (; isdigit(c=read(r)); m=1) + f = 10.0 * f + (c-'0'); + if (c=='.') { + double mag = 10.0; + for (; isdigit(c=read(r)); mag*=10.0) + f += (c-'0')/mag; + } + if ((c|0x20)=='e') { + int ex=0, en=0; + m = 0; + if ((c=read(r))=='-') en=1; + else if (c!='+') unread(r); + for (; isdigit(c=read(r)); m=1) + if (ex < LDBL_MAX_10_EXP) + ex = 10 * ex + (c-'0'); + if (ex > LDBL_MAX_10_EXP) + f = en ? 0 : INFINITY; + else { + if (en) while (ex--) f/=10.0; + else while (ex--) f*=10.0; + } + } + goto writefloat; +hexfloat: + m = 0; + for (; isxdigit(c=read(r)); m=1) + if (isdigit(c)) f = 16.0*f + (c-'0'); + else f = 16.0*f + ((c|32)-'a'+10); + if (c=='.') { + double mag = 1/16.0; + for (; isxdigit(c=read(r)); mag*=1/16.0) + if (isdigit(c)) f += (c-'0')*mag; + else f += ((c|32)-'a'+10)*mag; + } + if ((c|0x20)=='p') { + int ex=0, en=0; + m = 0; + if ((c=read(r))=='-') en=1; + else if (c!='+') unread(r); + for (; isdigit(c=read(r)); m=1) + if (ex < LDBL_MAX_EXP) + ex = 10 * ex + (c-'0'); + if (ex > LDBL_MAX_EXP) + f = en ? 0 : INFINITY; + else { + if (en) while (ex--) f*=0.5; + else while (ex--) f*=2.0; + } + } +writefloat: + if (!m) goto match_fail; + if (neg) f *= -1.0; + if (dest) switch (size) { + case SIZE_def: + *(float *)dest = f; + break; + case SIZE_l: + *(double *)dest = f; + break; + case SIZE_L: + *(long double *)dest = f; + break; + } + break; + case 'S': + wcs = dest ? dest : (void *)&dummy; + st = (mbstate_t){ 0 }; + while((c=read(r)) >= 0) { + if (wide) { + if (is_space(c)) break; + if (dest) *wcs++ = c; + } else { + char ch = c; + if (is_space(c)) break; + switch (mbrtowc(wcs, &ch, 1, &st)) { + case -1: + goto enc_fail; + case -2: + break; + default: + if (dest) wcs++; + } + } + } + if (dest) *wcs++ = 0; + break; + case 's': + s = dest ? dest : (void *)&dummy; + while((c=read(r)) >= 0) { + if (wide) { + if (is_space(c)) break; + if ((l=wctomb(s, c)) < 0) + goto enc_fail; + if (dest) s += l; + } else { + if (is_space(c)) break; + if (dest) *s++ = c; + } + } + if (dest) *s++ = 0; + break; + } + + /* unread will do nothing if field width was exhausted */ + unread(r); + if (dest) matches++; + } + return matches; +enc_fail: + errno = EILSEQ; +fmt_fail: +input_fail: + if (!matches) matches--; +match_fail: + unread(r); + return matches; +} diff --git a/src/stdio/__scanf.h b/src/stdio/__scanf.h new file mode 100644 index 00000000..e549b979 --- /dev/null +++ b/src/stdio/__scanf.h @@ -0,0 +1,16 @@ +#include <wchar.h> + +typedef struct rctx +{ + void (*read)(struct rctx *); + void *opaque; + int wide; + int (*is_space)(); + int l; + int e; + int c; + int u; + int w; +} rctx_t; + +int __scanf(rctx_t *, const wchar_t *, va_list); diff --git a/src/stdio/__stdio_close.c b/src/stdio/__stdio_close.c new file mode 100644 index 00000000..24fef33f --- /dev/null +++ b/src/stdio/__stdio_close.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int __stdio_close(FILE *f) +{ + return __syscall_close(f->fd); +} diff --git a/src/stdio/__stdio_read.c b/src/stdio/__stdio_read.c new file mode 100644 index 00000000..ee7e1258 --- /dev/null +++ b/src/stdio/__stdio_read.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +size_t __stdio_read(FILE *f, unsigned char *buf, size_t len) +{ + return __syscall_read(f->fd, buf, len); +} diff --git a/src/stdio/__stdio_seek.c b/src/stdio/__stdio_seek.c new file mode 100644 index 00000000..fdb9fe7f --- /dev/null +++ b/src/stdio/__stdio_seek.c @@ -0,0 +1,15 @@ +#include "stdio_impl.h" + +static off_t retneg1(FILE *f, off_t off, int whence) +{ + return -1; +} + +off_t __stdio_seek(FILE *f, off_t off, int whence) +{ + off_t ret = __syscall_lseek(f->fd, off, whence); + /* Detect unseekable files and optimize future failures out */ + if (ret < 0 && off == 0 && whence == SEEK_CUR) + f->seek = retneg1; + return ret; +} diff --git a/src/stdio/__stdio_write.c b/src/stdio/__stdio_write.c new file mode 100644 index 00000000..78545626 --- /dev/null +++ b/src/stdio/__stdio_write.c @@ -0,0 +1,9 @@ +#include "stdio_impl.h" + +size_t __stdio_write(FILE *f, const unsigned char *buf, size_t len) +{ + const unsigned char *stop = buf+len; + ssize_t cnt = 1; + for (; buf<stop && (cnt=__syscall_write(f->fd, buf, len))>0; buf+=cnt); + return len-(stop-buf); +} diff --git a/src/stdio/__uflow.c b/src/stdio/__uflow.c new file mode 100644 index 00000000..5a51d610 --- /dev/null +++ b/src/stdio/__uflow.c @@ -0,0 +1,7 @@ +#include "stdio_impl.h" + +int __uflow(FILE *f) +{ + if (__underflow(f) < 0) return EOF; + else return *f->rpos++; +} diff --git a/src/stdio/__underflow.c b/src/stdio/__underflow.c new file mode 100644 index 00000000..b769f4e4 --- /dev/null +++ b/src/stdio/__underflow.c @@ -0,0 +1,38 @@ +#include "stdio_impl.h" + +int __underflow(FILE *f) +{ + ssize_t cnt; + + /* Read from buffer (Do we ever get called when this is true??) */ + if (f->rpos < f->rstop) return *f->rpos; + + /* Initialize if we're not already reading */ + if (!f->rstop) { + /* Fail immediately if unreadable, eof, or error state. */ + if (f->flags & (F_EOF|F_ERR|F_NORD)) return EOF; + + /* Set byte orientation -1,0=>-1; 1=>1 */ + f->mode |= f->mode-1; + + /* Flush any unwritten output; fail on error. */ + if (f->wpos > f->buf && __oflow(f)) return EOF; + + /* Disallow writes to buffer. */ + f->wstop = 0; + } + + /* Perform the underlying read operation */ + if ((cnt=f->read(f, f->buf, f->buf_size)) + 1 <= 1) { + /* Set flags and leave read mode */ + f->flags |= F_EOF | (cnt & F_ERR); + f->rpos = f->rend = f->rstop = 0; + return EOF; + } + + /* Setup buffer pointers for reading from buffer */ + f->rpos = f->buf; + f->rend = f->rstop = f->buf + cnt; + + return *f->rpos; +} diff --git a/src/stdio/asprintf.c b/src/stdio/asprintf.c new file mode 100644 index 00000000..79e59c2d --- /dev/null +++ b/src/stdio/asprintf.c @@ -0,0 +1,14 @@ +#include <stdio.h> +#include <stdarg.h> + +int vasprintf(char **, const char *, va_list); + +int asprintf(char **s, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vasprintf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/clearerr.c b/src/stdio/clearerr.c new file mode 100644 index 00000000..3bf94d30 --- /dev/null +++ b/src/stdio/clearerr.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +void clearerr(FILE *f) +{ + FLOCK(f); + f->flags &= ~(F_EOF|F_ERR); + FUNLOCK(f); +} + +weak_alias(clearerr, clearerr_unlocked); diff --git a/src/stdio/dprintf.c b/src/stdio/dprintf.c new file mode 100644 index 00000000..fa28322f --- /dev/null +++ b/src/stdio/dprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int dprintf(int fd, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vdprintf(fd, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/fclose.c b/src/stdio/fclose.c new file mode 100644 index 00000000..26bc37e8 --- /dev/null +++ b/src/stdio/fclose.c @@ -0,0 +1,21 @@ +#include "stdio_impl.h" + +int fclose(FILE *f) +{ + int r; + int perm = f->flags & F_PERM; + + if (!perm) { + OFLLOCK(); + if (f->prev) f->prev->next = f->next; + if (f->next) f->next->prev = f->prev; + if (ofl_head == f) ofl_head = f->next; + OFLUNLOCK(); + } + + r = fflush(f) | f->close(f); + + if (!perm) free(f); + + return r; +} diff --git a/src/stdio/feof.c b/src/stdio/feof.c new file mode 100644 index 00000000..f2b739b5 --- /dev/null +++ b/src/stdio/feof.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#undef feof + +int feof(FILE *f) +{ + return !!(f->flags & F_EOF); +} + +weak_alias(feof, feof_unlocked); diff --git a/src/stdio/ferror.c b/src/stdio/ferror.c new file mode 100644 index 00000000..f535fbed --- /dev/null +++ b/src/stdio/ferror.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#undef ferror + +int ferror(FILE *f) +{ + return !!(f->flags & F_ERR); +} + +weak_alias(ferror, ferror_unlocked); diff --git a/src/stdio/fflush.c b/src/stdio/fflush.c new file mode 100644 index 00000000..cf3f5b0e --- /dev/null +++ b/src/stdio/fflush.c @@ -0,0 +1,50 @@ +#include "stdio_impl.h" + +static int __fflush_unlocked(FILE *f) +{ + /* If writing, flush output. */ + if (f->wpos > f->buf && __oflow(f)) return -1; + + /* If reading, sync position, per POSIX */ + if (f->rpos < f->rend) f->seek(f, f->rpos-f->rend, SEEK_CUR); + f->rpos = f->rend; + + /* Hook for special behavior on flush */ + if (f->flush) f->flush(f); + + return (f->flags & F_ERR) ? EOF : 0; +} + +/* stdout.c will override this if linked */ +static FILE *const __dummy = 0; +weak_alias(__dummy, __stdout_to_flush); + +int fflush(FILE *f) +{ + int r; + FILE *next; + + if (f) { + FLOCK(f); + r = __fflush_unlocked(f); + FUNLOCK(f); + return r; + } + + r = __stdout_to_flush ? fflush(__stdout_to_flush) : 0; + + OFLLOCK(); + for (f=ofl_head; f; f=next) { + FLOCK(f); + OFLUNLOCK(); + r |= __fflush_unlocked(f); + OFLLOCK(); + next = f->next; + FUNLOCK(f); + } + OFLUNLOCK(); + + return r; +} + +weak_alias(__fflush_unlocked, fflush_unlocked); diff --git a/src/stdio/fgetc.c b/src/stdio/fgetc.c new file mode 100644 index 00000000..3a7f1e30 --- /dev/null +++ b/src/stdio/fgetc.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int fgetc(FILE *f) +{ + int c; + FLOCK(f); + c = f->rpos < f->rstop ? *f->rpos++ : __uflow(f); + FUNLOCK(f); + return c; +} diff --git a/src/stdio/fgetpos.c b/src/stdio/fgetpos.c new file mode 100644 index 00000000..5b663d1e --- /dev/null +++ b/src/stdio/fgetpos.c @@ -0,0 +1,11 @@ +#include "stdio_impl.h" + +int fgetpos(FILE *f, fpos_t *pos) +{ + off_t off = __ftello(f); + if (off < 0) return -1; + *(off_t *)pos = off; + return 0; +} + +LFS64(fgetpos); diff --git a/src/stdio/fgets.c b/src/stdio/fgets.c new file mode 100644 index 00000000..7939303e --- /dev/null +++ b/src/stdio/fgets.c @@ -0,0 +1,34 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +char *fgets(char *s, int n, FILE *f) +{ + char *p = s; + unsigned char *z; + size_t k; + + if (!n--) return 0; + + FLOCK(f); + + while (n && !feof(f)) { + z = memchr(f->rpos, '\n', f->rend - f->rpos); + k = z ? z - f->rpos + 1 : f->rend - f->rpos; + k = MIN(k, n); + memcpy(p, f->rpos, k); + f->rpos += k; + p += k; + n -= k; + if (z) break; + __underflow(f); + } + *p = 0; + if (ferror(f)) p = s; + + FUNLOCK(f); + + return (p == s) ? 0 : s; +} + +weak_alias(fgets, fgets_unlocked); diff --git a/src/stdio/fgetwc.c b/src/stdio/fgetwc.c new file mode 100644 index 00000000..c990545f --- /dev/null +++ b/src/stdio/fgetwc.c @@ -0,0 +1,51 @@ +#include "stdio_impl.h" + +wint_t __fgetwc_unlocked(FILE *f) +{ + mbstate_t st = { 0 }; + wchar_t wc; + int c; + unsigned char b; + size_t l; + + f->mode |= f->mode+1; + + /* Convert character from buffer if possible */ + if (f->rpos < f->rend) { + l = mbrtowc(&wc, f->rpos, f->rend - f->rpos, &st); + if (l+2 >= 2) { + f->rpos += l + !l; /* l==0 means 1 byte, null */ + return wc; + } + if (l == -1) { + f->rpos++; + return WEOF; + } + } else l = -2; + + /* Convert character byte-by-byte from __uflow */ + while (l == -2) { + b = c = __uflow(f); + if (c < 0) { + if (!mbsinit(&st)) errno = EILSEQ; + return WEOF; + } + l = mbrtowc(&wc, &b, 1, &st); + if (l == -1) return WEOF; + } + + FUNLOCK(f); + return wc; +} + +wint_t fgetwc(FILE *f) +{ + wint_t c; + FLOCK(f); + c = __fgetwc_unlocked(f); + FUNLOCK(f); + return c; +} + +weak_alias(__fgetwc_unlocked, fgetwc_unlocked); +weak_alias(__fgetwc_unlocked, getwc_unlocked); diff --git a/src/stdio/fgetws.c b/src/stdio/fgetws.c new file mode 100644 index 00000000..2e76b565 --- /dev/null +++ b/src/stdio/fgetws.c @@ -0,0 +1,27 @@ +#include "stdio_impl.h" + +wint_t __fgetwc_unlocked(FILE *); + +wchar_t *fgetws(wchar_t *s, int n, FILE *f) +{ + wchar_t *p = s; + + if (!n--) return s; + + FLOCK(f); + + for (; n; n--) { + wint_t c = __fgetwc_unlocked(f); + if (c == WEOF) break; + *p++ = c; + if (c == '\n') break; + } + *p = 0; + if (ferror(f)) p = s; + + FUNLOCK(f); + + return (p == s) ? NULL : s; +} + +weak_alias(fgetws, fgetws_unlocked); diff --git a/src/stdio/fileno.c b/src/stdio/fileno.c new file mode 100644 index 00000000..9ffb26d5 --- /dev/null +++ b/src/stdio/fileno.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int fileno(FILE *f) +{ + return f->fd; +} + +weak_alias(fileno, fileno_unlocked); diff --git a/src/stdio/fmemopen.c b/src/stdio/fmemopen.c new file mode 100644 index 00000000..77a60746 --- /dev/null +++ b/src/stdio/fmemopen.c @@ -0,0 +1,16 @@ +#if 0 +#include "stdio_impl.h" + +static ssize_t mread(FILE *f, unsigned char *buf, size_t len) +{ + memcpy(buf, +} + +FILE *fmemopen(void *buf, size_t size, const char *mode) +{ + FILE *f = calloc(sizeof(FILE), 1); + if (!f) return 0; + + // +} +#endif diff --git a/src/stdio/fopen.c b/src/stdio/fopen.c new file mode 100644 index 00000000..670cf5f3 --- /dev/null +++ b/src/stdio/fopen.c @@ -0,0 +1,34 @@ +#include "stdio_impl.h" + +FILE *fopen(const char *filename, const char *mode) +{ + FILE *f; + int fd; + int flags; + int plus = !!strchr(mode, '+'); + + /* Check for valid initial mode character */ + if (!strchr("rwa", *mode)) { + errno = EINVAL; + return 0; + } + + /* Compute the flags to pass to open() */ + if (plus) flags = O_RDWR; + else if (*mode == 'r') flags = O_RDONLY; + else flags = O_WRONLY; + if (*mode != 'r') flags |= O_CREAT; + if (*mode == 'w') flags |= O_TRUNC; + if (*mode == 'a') flags |= O_APPEND; + + fd = __syscall_open(filename, flags, 0666); + if (fd < 0) return 0; + + f = __fdopen(fd, mode); + if (f) return f; + + __syscall_close(fd); + return 0; +} + +LFS64(fopen); diff --git a/src/stdio/fprintf.c b/src/stdio/fprintf.c new file mode 100644 index 00000000..a220cc10 --- /dev/null +++ b/src/stdio/fprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int fprintf(FILE *f, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vfprintf(f, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/fputc.c b/src/stdio/fputc.c new file mode 100644 index 00000000..ec859385 --- /dev/null +++ b/src/stdio/fputc.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int fputc(int c, FILE *f) +{ + FLOCK(f); + if (c != f->lbf && f->wpos + 1 < f->wend) *f->wpos++ = c; + else c = __overflow(f, c); + FUNLOCK(f); + return c; +} diff --git a/src/stdio/fputs.c b/src/stdio/fputs.c new file mode 100644 index 00000000..e6bdb204 --- /dev/null +++ b/src/stdio/fputs.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int fputs(const char *s, FILE *f) +{ + size_t l = strlen(s); + if (!l) return 0; + return (int)fwrite(s, l, 1, f) - 1; +} + +weak_alias(fputs, fputs_unlocked); diff --git a/src/stdio/fputwc.c b/src/stdio/fputwc.c new file mode 100644 index 00000000..b48bb74d --- /dev/null +++ b/src/stdio/fputwc.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +wint_t __fputwc_unlocked(wchar_t c, FILE *f) +{ + char mbc[MB_LEN_MAX]; + int l; + + f->mode |= f->mode+1; + + if (isascii(c)) { + if (c != f->lbf && f->wpos + 1 < f->wend) *f->wpos++ = c; + else c = __overflow(f, c); + } else if (f->wpos + MB_LEN_MAX < f->wend) { + l = wctomb(f->wpos, c); + if (l < 0) c = WEOF; + else f->wpos += l; + } else { + l = wctomb(mbc, c); + if (l < 0 || __fwritex(mbc, l, f) < l) c = WEOF; + } + return c; +} + +wint_t fputwc(wchar_t c, FILE *f) +{ + FLOCK(f); + c = __fputwc_unlocked(c, f); + FUNLOCK(f); + return 0; +} + +weak_alias(__fputwc_unlocked, fputwc_unlocked); +weak_alias(__fputwc_unlocked, putwc_unlocked); diff --git a/src/stdio/fputws.c b/src/stdio/fputws.c new file mode 100644 index 00000000..9057853b --- /dev/null +++ b/src/stdio/fputws.c @@ -0,0 +1,23 @@ +#include "stdio_impl.h" + +int fputws(const wchar_t *ws, FILE *f) +{ + unsigned char buf[BUFSIZ]; + size_t l=0; + + FLOCK(f); + + f->mode |= f->mode+1; + + while (ws && (l = wcsrtombs(buf, (void*)&ws, sizeof buf, 0))+1 > 1) + if (__fwritex(buf, l, f) < l) { + FUNLOCK(f); + return -1; + } + + FUNLOCK(f); + + return l; /* 0 or -1 */ +} + +weak_alias(fputws, fputws_unlocked); diff --git a/src/stdio/fread.c b/src/stdio/fread.c new file mode 100644 index 00000000..0fa0b2aa --- /dev/null +++ b/src/stdio/fread.c @@ -0,0 +1,49 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +size_t fread(void *destv, size_t size, size_t nmemb, FILE *f) +{ + unsigned char *dest = destv; + size_t len = size*nmemb, l = len, k; + + /* Never touch the file if length is zero.. */ + if (!l) return 0; + + FLOCK(f); + + for (;;) { + /* First exhaust the buffer. */ + k = MIN(f->rend - f->rpos, l); + memcpy(dest, f->rpos, k); + f->rpos += k; + dest += k; + l -= k; + + /* Stop on EOF or errors */ + if (f->flags & (F_EOF|F_ERR|F_NORD)) goto eof; + + /* Done? Or going unbuffered? */ + if (!l || l > f->buf_size/2) break; + + /* Otherwise, refill & read thru buffer. */ + __underflow(f); + } + + /* Read the remainder directly */ + for (; l; l-=k, dest+=k) { + k = f->read(f, dest, l); + if (k+1<=1) { + f->flags |= F_EOF | (F_ERR & k); + goto eof; + } + } + + FUNLOCK(f); + return nmemb; +eof: + FUNLOCK(f); + return (len-l)/size; +} + +weak_alias(fread, fread_unlocked); diff --git a/src/stdio/freopen.c b/src/stdio/freopen.c new file mode 100644 index 00000000..8d3af9fc --- /dev/null +++ b/src/stdio/freopen.c @@ -0,0 +1,47 @@ +#include "stdio_impl.h" + +/* The basic idea of this implementation is to open a new FILE, + * hack the necessary parts of the new FILE into the old one, then + * close the new FILE. */ + +/* Locking is not necessary because, in the event of failure, the stream + * passed to freopen is invalid as soon as freopen is called. */ + +FILE *freopen(const char *filename, const char *mode, FILE *f) +{ + int fl; + FILE *f2; + + fflush(f); + + if (!filename) { + f2 = fopen("/dev/null", mode); + if (!f2) goto fail; + fl = __syscall_fcntl(f2->fd, F_GETFL, 0); + if (fl < 0 || __syscall_fcntl(f->fd, F_SETFL, fl) < 0) + goto fail2; + } else { + f2 = fopen(filename, mode); + if (!f2) goto fail; + if (__syscall_dup2(f2->fd, f->fd) < 0) + goto fail2; + } + + f->flags = (f->flags & F_PERM) | f2->flags; + f->read = f2->read; + f->write = f2->write; + f->seek = f2->seek; + f->close = f2->close; + f->flush = f2->flush; + + fclose(f2); + return f; + +fail2: + fclose(f2); +fail: + fclose(f); + return NULL; +} + +LFS64(freopen); diff --git a/src/stdio/fscanf.c b/src/stdio/fscanf.c new file mode 100644 index 00000000..51fc9b30 --- /dev/null +++ b/src/stdio/fscanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int fscanf(FILE *f, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vfscanf(f, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/fseek.c b/src/stdio/fseek.c new file mode 100644 index 00000000..bfaad375 --- /dev/null +++ b/src/stdio/fseek.c @@ -0,0 +1,38 @@ +#include "stdio_impl.h" + +int __fseeko_unlocked(FILE *f, off_t off, int whence) +{ + /* Adjust relative offset for unread data in buffer, if any. */ + if (whence == SEEK_CUR) off -= f->rend - f->rpos; + + /* If writing, flush output. */ + if (f->wpos > f->buf && __oflow(f)) return -1; + + /* Perform the underlying seek operation. */ + if (f->seek(f, off, whence) < 0) return -1; + + /* If seek succeeded, file is seekable and we discard read buffer. */ + f->rpos = f->rend = f->rstop = 0; + f->flags &= ~F_EOF; + + FUNLOCK(f); + return 0; +} + +int __fseeko(FILE *f, off_t off, int whence) +{ + int result; + FLOCK(f); + result = __fseeko_unlocked(f, off, whence); + FUNLOCK(f); + return result; +} + +int fseek(FILE *f, long off, int whence) +{ + return __fseeko(f, off, whence); +} + +weak_alias(__fseeko, fseeko); + +LFS64(fseeko); diff --git a/src/stdio/fsetpos.c b/src/stdio/fsetpos.c new file mode 100644 index 00000000..5d76c8cd --- /dev/null +++ b/src/stdio/fsetpos.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int fsetpos(FILE *f, const fpos_t *pos) +{ + return __fseeko(f, *(const off_t *)pos, SEEK_SET); +} + +LFS64(fsetpos); diff --git a/src/stdio/ftell.c b/src/stdio/ftell.c new file mode 100644 index 00000000..aa1f5381 --- /dev/null +++ b/src/stdio/ftell.c @@ -0,0 +1,35 @@ +#include "stdio_impl.h" + +off_t __ftello_unlocked(FILE *f) +{ + off_t pos = f->seek(f, 0, SEEK_CUR); + if (pos < 0) { + FUNLOCK(f); + return pos; + } + /* Adjust for data in buffer. */ + return pos - (f->rend - f->rpos) + (f->wpos - f->wbase); +} + +off_t __ftello(FILE *f) +{ + off_t pos; + FLOCK(f); + pos = __ftello_unlocked(f); + FUNLOCK(f); + return pos; +} + +long ftell(FILE *f) +{ + off_t pos = __ftello(f); + if (pos > LONG_MAX) { + errno = EOVERFLOW; + return -1; + } + return pos; +} + +weak_alias(__ftello, ftello); + +LFS64(ftello); diff --git a/src/stdio/fwide.c b/src/stdio/fwide.c new file mode 100644 index 00000000..f4da47f6 --- /dev/null +++ b/src/stdio/fwide.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +#define SH (8*sizeof(int)-1) +#define NORMALIZE(x) ((x)>>SH | -((-(x))>>SH)) + +int fwide(FILE *f, int mode) +{ + if (!f->mode) f->mode = NORMALIZE(mode); + return f->mode; +} diff --git a/src/stdio/fwrite.c b/src/stdio/fwrite.c new file mode 100644 index 00000000..23974fe1 --- /dev/null +++ b/src/stdio/fwrite.c @@ -0,0 +1,51 @@ +#include "stdio_impl.h" + +size_t __fwritex(const unsigned char *s, size_t l, FILE *f) +{ + size_t i = 0; + size_t k = f->wend - f->wpos; + + /* Handle line-buffered mode by breaking into 2 parts */ + if (f->lbf >= 0) { + /* Match /^(.*\n|)/ */ + for (i=l; i && s[i-1] != '\n'; i--); + if (i) { + f->lbf = EOF; + __fwritex(s, i, f); + f->lbf = '\n'; + __oflow(f); + return ferror(f) ? 0 : i + __fwritex(s+i, l-i, f); + } + } + + /* Buffer initial segment */ + if (k > l) k = l; + memcpy(f->wpos, s, k); + f->wpos += k; + if (f->wpos < f->wend) return l; + + /* If there's work left to do, flush buffer */ + __oflow(f); + if (ferror(f)) return 0; + + /* If the remainder will not fit in buffer, write it directly */ + if (l - k >= f->wend - f->wpos) + return k + f->write(f, s+k, l-k); + + /* Otherwise, buffer the remainder */ + memcpy(f->wpos, s+k, l-k); + f->wpos += l-k; + return l; +} + +size_t fwrite(const void *src, size_t size, size_t nmemb, FILE *f) +{ + size_t l = size*nmemb; + if (!l) return l; + FLOCK(f); + l = __fwritex(src, l, f); + FUNLOCK(f); + return l/size; +} + +weak_alias(fwrite, fwrite_unlocked); diff --git a/src/stdio/fwscanf.c b/src/stdio/fwscanf.c new file mode 100644 index 00000000..a6892cf6 --- /dev/null +++ b/src/stdio/fwscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int fwscanf(FILE *f, const wchar_t *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vfwscanf(f, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/getc.c b/src/stdio/getc.c new file mode 100644 index 00000000..b739b0a5 --- /dev/null +++ b/src/stdio/getc.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int getc(FILE *f) +{ + return fgetc(f); +} diff --git a/src/stdio/getc_unlocked.c b/src/stdio/getc_unlocked.c new file mode 100644 index 00000000..629223ea --- /dev/null +++ b/src/stdio/getc_unlocked.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int getc_unlocked(FILE *f) +{ + return f->rpos < f->rstop ? *f->rpos++ : __uflow(f); +} + +weak_alias (getc_unlocked, fgetc_unlocked); diff --git a/src/stdio/getchar.c b/src/stdio/getchar.c new file mode 100644 index 00000000..c1012658 --- /dev/null +++ b/src/stdio/getchar.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int getchar(void) +{ + return fgetc(stdin); +} diff --git a/src/stdio/getchar_unlocked.c b/src/stdio/getchar_unlocked.c new file mode 100644 index 00000000..299cb958 --- /dev/null +++ b/src/stdio/getchar_unlocked.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int getchar_unlocked(void) +{ + return stdin->rpos < stdin->rstop ? *stdin->rpos++ : __uflow(stdin); +} diff --git a/src/stdio/getdelim.c b/src/stdio/getdelim.c new file mode 100644 index 00000000..f770d20b --- /dev/null +++ b/src/stdio/getdelim.c @@ -0,0 +1,59 @@ +#include "stdio_impl.h" + +#define MIN(a,b) ((a)<(b) ? (a) : (b)) + +ssize_t getdelim(char **s, size_t *n, int delim, FILE *f) +{ + char *tmp; + unsigned char *z; + size_t k; + size_t i=0; + + if (!n || !s) { + errno = EINVAL; + return -1; + } + + if (!*s) *n=0; + + FLOCK(f); + + while (!feof(f)) { + z = memchr(f->rpos, delim, f->rend - f->rpos); + k = z ? z - f->rpos + 1 : f->rend - f->rpos; + if (i+k >= *n) { + if (k >= SIZE_MAX-i) goto oom; + *n = i+k+1; + if (*n < SIZE_MAX/2) *n *= 2; + tmp = realloc(*s, *n); + if (!tmp) { + *n = i+k+1; + tmp = realloc(*s, *n); + if (!tmp) goto oom; + } + *s = tmp; + } + memcpy(*s+i, f->rpos, k); + f->rpos += k; + i += k; + if (z) break; + __underflow(f); + } + (*s)[i] = 0; + if (feof(f) || ferror(f)) { + FUNLOCK(f); + return -1; + } + + FUNLOCK(f); + + if (i > SSIZE_MAX) { + errno = EOVERFLOW; + return -1; + } + + return i; +oom: + errno = ENOMEM; + return -1; +} diff --git a/src/stdio/getline.c b/src/stdio/getline.c new file mode 100644 index 00000000..a3a6651b --- /dev/null +++ b/src/stdio/getline.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +ssize_t getline(char **s, size_t *n, FILE *f) +{ + return getdelim(s, n, '\n', f); +} diff --git a/src/stdio/gets.c b/src/stdio/gets.c new file mode 100644 index 00000000..24319eb2 --- /dev/null +++ b/src/stdio/gets.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +char *gets(char *s) +{ + char *ret = fgets(s, INT_MAX, stdin); + if (ret && s[strlen(s)-1] == '\n') s[strlen(s)-1] = 0; + return ret; +} diff --git a/src/stdio/getw.c b/src/stdio/getw.c new file mode 100644 index 00000000..de9e985a --- /dev/null +++ b/src/stdio/getw.c @@ -0,0 +1,7 @@ +#include <stdio.h> + +int getw(FILE *f) +{ + int x; + return fread(&x, sizeof x, 1, f) ? x : EOF; +} diff --git a/src/stdio/getwc.c b/src/stdio/getwc.c new file mode 100644 index 00000000..a2818bc4 --- /dev/null +++ b/src/stdio/getwc.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +wint_t getwc(FILE *f) +{ + return fgetwc(f); +} diff --git a/src/stdio/getwchar.c b/src/stdio/getwchar.c new file mode 100644 index 00000000..2295bd40 --- /dev/null +++ b/src/stdio/getwchar.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +wint_t getwchar(void) +{ + return fgetwc(stdin); +} + +weak_alias(getwchar, getwchar_unlocked); diff --git a/src/stdio/pclose.c b/src/stdio/pclose.c new file mode 100644 index 00000000..c2fe7a24 --- /dev/null +++ b/src/stdio/pclose.c @@ -0,0 +1,10 @@ +#include "stdio_impl.h" + +int pclose(FILE *f) +{ + int status; + fclose(f); + while (waitpid(f->pipe_pid, &status, 0) == -1) + if (errno != EINTR) return -1; + return status; +} diff --git a/src/stdio/perror.c b/src/stdio/perror.c new file mode 100644 index 00000000..e4637c8a --- /dev/null +++ b/src/stdio/perror.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include "stdio_impl.h" + +void perror(const char *msg) +{ +#if 1 + if (msg) fprintf(stderr, "%s: %m\n", msg, strerror(errno)); + else fprintf(stderr, "%m\n"); +#else + FILE *f = stderr; + char *errstr = strerror(errno); + + FLOCK(f); + + if (msg) { + __fwritex(msg, strlen(msg), f); + __putc_unlocked(':', f); + __putc_unlocked(' ', f); + } + __fwritex(errstr, strlen(errstr), f); + __putc_unlocked('\n', f); + + FUNLOCK(f); +#endif +} diff --git a/src/stdio/popen.c b/src/stdio/popen.c new file mode 100644 index 00000000..1d33e9d6 --- /dev/null +++ b/src/stdio/popen.c @@ -0,0 +1,43 @@ +#include "stdio_impl.h" + +FILE *popen(const char *cmd, const char *mode) +{ + int p[2]; + int op; + pid_t pid; + FILE *f; + const char *modes = "rw", *mi = strchr(modes, *mode); + + if (mi) { + op = mi-modes; + } else { + errno = EINVAL; + return 0; + } + + if (pipe(p)) return NULL; + f = fdopen(p[op], mode); + if (!f) { + close(p[0]); + close(p[1]); + return NULL; + } + + pid = fork(); + switch (pid) { + case -1: + fclose(f); + close(p[0]); + close(p[1]); + return NULL; + case 0: + dup2(p[1-op], 1-op); + close(p[0]); + close(p[1]); + execl("/bin/sh", "sh", "-c", cmd, (char *)0); + _exit(127); + } + close(p[1-op]); + f->pipe_pid = pid; + return f; +} diff --git a/src/stdio/printf.c b/src/stdio/printf.c new file mode 100644 index 00000000..efeb8b33 --- /dev/null +++ b/src/stdio/printf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int printf(const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vprintf(fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/putc.c b/src/stdio/putc.c new file mode 100644 index 00000000..3c9dc11e --- /dev/null +++ b/src/stdio/putc.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int putc(int c, FILE *f) +{ + return fputc(c, f); +} + +weak_alias(putc, _IO_putc); diff --git a/src/stdio/putc_unlocked.c b/src/stdio/putc_unlocked.c new file mode 100644 index 00000000..f01da717 --- /dev/null +++ b/src/stdio/putc_unlocked.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +int putc_unlocked(int c, FILE *f) +{ + return f->wpos < f->wstop ? (*f->wpos++ = c) : __overflow(f, c); +} + +weak_alias(putc_unlocked, fputc_unlocked); diff --git a/src/stdio/putchar.c b/src/stdio/putchar.c new file mode 100644 index 00000000..945636d5 --- /dev/null +++ b/src/stdio/putchar.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int putchar(int c) +{ + return fputc(c, stdout); +} diff --git a/src/stdio/putchar_unlocked.c b/src/stdio/putchar_unlocked.c new file mode 100644 index 00000000..72d47d15 --- /dev/null +++ b/src/stdio/putchar_unlocked.c @@ -0,0 +1,7 @@ +#include "stdio_impl.h" + +int putchar_unlocked(int c) +{ + return stdout->wpos < stdout->wstop ? + (*stdout->wpos++ = c) : __overflow(stdout, c); +} diff --git a/src/stdio/puts.c b/src/stdio/puts.c new file mode 100644 index 00000000..eb70efdc --- /dev/null +++ b/src/stdio/puts.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +int puts(const char *s) +{ + return -(fputs(s, stdout) < 0 || putchar('\n') < 0); +} diff --git a/src/stdio/putw.c b/src/stdio/putw.c new file mode 100644 index 00000000..137832ee --- /dev/null +++ b/src/stdio/putw.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int putw(int x, FILE *f) +{ + return fwrite(&x, sizeof x, 1, f) ? x : EOF; +} diff --git a/src/stdio/putwc.c b/src/stdio/putwc.c new file mode 100644 index 00000000..80b54a47 --- /dev/null +++ b/src/stdio/putwc.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +wint_t putwc(wchar_t c, FILE *f) +{ + return fputwc(c, f); +} diff --git a/src/stdio/putwchar.c b/src/stdio/putwchar.c new file mode 100644 index 00000000..3aacc1cf --- /dev/null +++ b/src/stdio/putwchar.c @@ -0,0 +1,8 @@ +#include "stdio_impl.h" + +wint_t putwchar(wchar_t c) +{ + return fputwc(c, stdout); +} + +weak_alias(putwchar, putwchar_unlocked); diff --git a/src/stdio/remove.c b/src/stdio/remove.c new file mode 100644 index 00000000..8e338277 --- /dev/null +++ b/src/stdio/remove.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "syscall.h" + +int remove(const char *path) +{ + return __syscall_unlink(path); +} diff --git a/src/stdio/rename.c b/src/stdio/rename.c new file mode 100644 index 00000000..4eced08a --- /dev/null +++ b/src/stdio/rename.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "syscall.h" + +int rename(const char *old, const char *new) +{ + return syscall2(__NR_rename, (long)old, (long)new); +} diff --git a/src/stdio/rewind.c b/src/stdio/rewind.c new file mode 100644 index 00000000..7944b434 --- /dev/null +++ b/src/stdio/rewind.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +void rewind(FILE *f) +{ + fseek(f, 0, SEEK_SET); +} diff --git a/src/stdio/scanf.c b/src/stdio/scanf.c new file mode 100644 index 00000000..a04a4402 --- /dev/null +++ b/src/stdio/scanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int scanf(const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vscanf(fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/setbuf.c b/src/stdio/setbuf.c new file mode 100644 index 00000000..205afcfe --- /dev/null +++ b/src/stdio/setbuf.c @@ -0,0 +1,6 @@ +#include "stdio_impl.h" + +void setbuf(FILE *f, char *buf) +{ + setvbuf(f, buf, buf ? _IOFBF : _IONBF, BUFSIZ); +} diff --git a/src/stdio/setvbuf.c b/src/stdio/setvbuf.c new file mode 100644 index 00000000..2985d3f1 --- /dev/null +++ b/src/stdio/setvbuf.c @@ -0,0 +1,22 @@ +#include "stdio_impl.h" + +/* This function makes no attempt to protect the user from his/her own + * stupidity. If called any time but when then ISO C standard specifically + * allows it, all hell can and will break loose, especially with threads! + * + * This implementation ignores all arguments except the buffering type, + * and uses the existing buffer allocated alongside the FILE object. + * In the case of stderr where the preexisting buffer is length 1, it + * is not possible to set line buffering or full buffering. */ + +int setvbuf(FILE *f, char *buf, int type, size_t size) +{ + f->lbf = EOF; + + if (type == _IONBF) + f->buf_size = 1; + else if (type == _IOLBF) + f->lbf = '\n'; + + return 0; +} diff --git a/src/stdio/snprintf.c b/src/stdio/snprintf.c new file mode 100644 index 00000000..4071c2f6 --- /dev/null +++ b/src/stdio/snprintf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> + +int snprintf(char *s, size_t n, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(s, n, fmt, ap); + va_end(ap); + return ret; +} + diff --git a/src/stdio/sprintf.c b/src/stdio/sprintf.c new file mode 100644 index 00000000..6b225409 --- /dev/null +++ b/src/stdio/sprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int sprintf(char *s, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsprintf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/sscanf.c b/src/stdio/sscanf.c new file mode 100644 index 00000000..a1cea699 --- /dev/null +++ b/src/stdio/sscanf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int sscanf(const char *s, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsscanf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/stderr.c b/src/stdio/stderr.c new file mode 100644 index 00000000..4a79d4e7 --- /dev/null +++ b/src/stdio/stderr.c @@ -0,0 +1,13 @@ +#include "stdio_impl.h" + +static unsigned char buf[1+UNGET]; +static FILE f = { + .buf = buf+UNGET, + .buf_size = 1, + .fd = 2, + .flags = F_PERM | F_NORD, + .write = __stdio_write, + .seek = __stdio_seek, + .close = __stdio_close, +}; +FILE *const stderr = &f; diff --git a/src/stdio/stdin.c b/src/stdio/stdin.c new file mode 100644 index 00000000..51c99232 --- /dev/null +++ b/src/stdio/stdin.c @@ -0,0 +1,13 @@ +#include "stdio_impl.h" + +static unsigned char buf[BUFSIZ+UNGET]; +static FILE f = { + .buf = buf+UNGET, + .buf_size = sizeof buf-UNGET, + .fd = 0, + .flags = F_PERM | F_NOWR, + .read = __stdio_read, + .seek = __stdio_seek, + .close = __stdio_close, +}; +FILE *const stdin = &f; diff --git a/src/stdio/stdout.c b/src/stdio/stdout.c new file mode 100644 index 00000000..bf6eea6c --- /dev/null +++ b/src/stdio/stdout.c @@ -0,0 +1,17 @@ +#include "stdio_impl.h" + +static unsigned char buf[BUFSIZ+UNGET]; +static FILE f = { + .buf = buf+UNGET, + .buf_size = sizeof buf-UNGET, + .fd = 1, + .flags = F_PERM | F_NORD, + .lbf = '\n', + .write = __stdio_write, + .seek = __stdio_seek, + .close = __stdio_close, +}; +FILE *const stdout = &f; + +/* overrides symbol in fflush.c, used for flushing NULL */ +FILE *const __stdout_to_flush = &f; diff --git a/src/stdio/swscanf.c b/src/stdio/swscanf.c new file mode 100644 index 00000000..b66ad03e --- /dev/null +++ b/src/stdio/swscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int swscanf(const wchar_t *s, const wchar_t *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vswscanf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdio/tempnam.c b/src/stdio/tempnam.c new file mode 100644 index 00000000..2cbcb864 --- /dev/null +++ b/src/stdio/tempnam.c @@ -0,0 +1,42 @@ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include "libc.h" + +char *tempnam(const char *dir, const char *pfx) +{ + static int lock; + static int index; + char *s; + int pid = getpid(); + int l; + + if (!dir) dir = P_tmpdir; + if (!pfx) pfx = "temp"; + + if (access(dir, R_OK|W_OK|X_OK) != 0) + return NULL; + + l = strlen(dir) + 1 + strlen(pfx) + 2 + sizeof(int)*3*2 + 1; + s = malloc(l); + if (!s) { + errno = ENOMEM; + return NULL; + } + + LOCK(&lock); + for (; index < TMP_MAX; index++) { + snprintf(s, l, "%s/%s-%d-%d", dir, pfx, pid, index); + if (access(s, F_OK) != 0) { + UNLOCK(&lock); + return s; + } + } + UNLOCK(&lock); + free(s); + return NULL; +} diff --git a/src/stdio/tmpfile.c b/src/stdio/tmpfile.c new file mode 100644 index 00000000..185025f1 --- /dev/null +++ b/src/stdio/tmpfile.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include "stdio_impl.h" + +FILE *tmpfile(void) +{ + char buf[L_tmpnam], *s; + int fd; + FILE *f; + for (;;) { + s = tmpnam(buf); + if (!s) return NULL; + fd = __syscall_open(s, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + f = __fdopen(fd, "w+"); + remove(s); + return f; + } + } +} + +LFS64(tmpfile); diff --git a/src/stdio/tmpnam.c b/src/stdio/tmpnam.c new file mode 100644 index 00000000..14d59220 --- /dev/null +++ b/src/stdio/tmpnam.c @@ -0,0 +1,38 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <unistd.h> +#include "libc.h" + +char *tmpnam(char *s) +{ + static int lock; + static int index; + static char *s2; + int pid = getpid(); + char *dir = getenv("TMPDIR"); + + if (!s) { + if (!s2) s2 = malloc(L_tmpnam); + s = s2; + } + + /* this interface is insecure anyway but at least we can try.. */ + if (!dir || strlen(dir) > L_tmpnam-32) + dir = P_tmpdir; + + if (access(dir, R_OK|W_OK|X_OK) != 0) + return NULL; + + LOCK(&lock); + for (index++; index < TMP_MAX; index++) { + snprintf(s, L_tmpnam, "%s/temp%d-%d", dir, pid, index); + if (access(s, F_OK) != 0) { + UNLOCK(&lock); + return s; + } + } + UNLOCK(&lock); + return NULL; +} diff --git a/src/stdio/ungetc.c b/src/stdio/ungetc.c new file mode 100644 index 00000000..07181684 --- /dev/null +++ b/src/stdio/ungetc.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +int ungetc(int c, FILE *f) +{ + if (c == EOF) return c; + + FLOCK(f); + + /* Fail if unreadable or writing and unable to flush */ + if ((f->flags & (F_ERR|F_NORD)) || (f->wpos && __oflow(f))) { + FUNLOCK(f); + return EOF; + } + + /* Clear write mode */ + f->wbase = f->wpos = f->wstop = f->wend = 0; + + /* Put the file in read mode */ + if (!f->rpos) f->rpos = f->rend = f->buf; + + /* If unget buffer is already full, fail. */ + if (f->rpos <= f->buf - UNGET) { + FUNLOCK(f); + return EOF; + } + + /* Put a byte back into the buffer */ + *--f->rpos = c; + f->flags &= ~F_EOF; + + FUNLOCK(f); + return c; +} diff --git a/src/stdio/ungetwc.c b/src/stdio/ungetwc.c new file mode 100644 index 00000000..f7cde2e0 --- /dev/null +++ b/src/stdio/ungetwc.c @@ -0,0 +1,45 @@ +#include "stdio_impl.h" + +wint_t ungetwc(wint_t c, FILE *f) +{ + unsigned char mbc[MB_LEN_MAX]; + int l=1; + + if (c == WEOF) return c; + + /* Try conversion early so we can fail without locking if invalid */ + if (!isascii(c) && (l = wctomb(mbc, c)) < 0) + return WEOF; + + FLOCK(f); + + f->mode |= f->mode+1; + + /* Fail if unreadable or writing and unable to flush */ + if ((f->flags & (F_ERR|F_NORD)) || (f->wpos && __oflow(f))) { + FUNLOCK(f); + return EOF; + } + + /* Clear write mode */ + f->wpos = f->wstop = f->wend = 0; + + /* Put the file in read mode */ + if (!f->rpos) f->rpos = f->rend = f->buf; + + /* If unget buffer is nonempty, fail. */ + if (f->rpos < f->buf) { + FUNLOCK(f); + return WEOF; + } + + /* Put character back into the buffer */ + if (isascii(c)) *--f->rpos = c; + else memcpy(f->rpos -= l, mbc, l); + + /* Clear EOF */ + f->flags &= ~F_EOF; + + FUNLOCK(f); + return c; +} diff --git a/src/stdio/vasprintf.c b/src/stdio/vasprintf.c new file mode 100644 index 00000000..f2bbc7aa --- /dev/null +++ b/src/stdio/vasprintf.c @@ -0,0 +1,27 @@ +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#define GUESS 240U + +int vasprintf(char **s, const char *fmt, va_list ap) +{ + va_list ap2; + char *a; + int l=GUESS; + + if (!(a=malloc(GUESS))) return -1; + + va_copy(ap2, ap); + l=vsnprintf(a, GUESS, fmt, ap2); + va_end(ap2); + + if (l<GUESS) { + char *b = realloc(a, l+1U); + *s = b ? b : a; + return l; + } + free(a); + if (l<0 || !(*s=malloc(l+1U))) return -1; + return vsnprintf(*s, l+1U, fmt, ap); +} diff --git a/src/stdio/vdprintf.c b/src/stdio/vdprintf.c new file mode 100644 index 00000000..bfb1b0a9 --- /dev/null +++ b/src/stdio/vdprintf.c @@ -0,0 +1,14 @@ +#include "stdio_impl.h" + +int vdprintf(int fd, const char *fmt, va_list ap) +{ + int r; + char buf[BUFSIZ]; + FILE f = { + .fd = fd, .lbf = EOF, .write = __stdio_write, + .buf = buf+UNGET, .buf_size = sizeof buf - UNGET + }; + r = vfprintf(&f, fmt, ap); + __oflow(&f); + return r; +} diff --git a/src/stdio/vfprintf.c b/src/stdio/vfprintf.c new file mode 100644 index 00000000..5e19acc5 --- /dev/null +++ b/src/stdio/vfprintf.c @@ -0,0 +1,640 @@ +#include "stdio_impl.h" + +/* Some useful macros */ + +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) +#define CONCAT2(x,y) x ## y +#define CONCAT(x,y) CONCAT2(x,y) + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM (1U<<'#'-' ') +#define ZERO_PAD (1U<<'0'-' ') +#define LEFT_ADJ (1U<<'-'-' ') +#define PAD_POS (1U<<' '-' ') +#define MARK_POS (1U<<'+'-' ') +#define GROUPED (1U<<'\''-' ') + +#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED) + +#if UINT_MAX == ULONG_MAX +#define LONG_IS_INT +#endif + +#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX +#define ODD_TYPES +#endif + +/* State machine to accept length modifiers + conversion specifiers. + * Result is 0 on failure, or an argument type to pop on success. */ + +enum { + BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE, + ZTPRE, JPRE, + STOP, + PTR, INT, UINT, ULLONG, +#ifndef LONG_IS_INT + LONG, ULONG, +#else +#define LONG INT +#define ULONG UINT +#endif + SHORT, USHORT, CHAR, UCHAR, +#ifdef ODD_TYPES + LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR, +#else +#define LLONG ULLONG +#define SIZET ULONG +#define IMAX LLONG +#define UMAX ULLONG +#define PDIFF LONG +#define UIPTR ULONG +#endif + DBL, LDBL, + NOARG, + MAXSTATE +}; + +#define S(x) [(x)-'A'] + +static const unsigned char states[]['z'-'A'+1] = { + { /* 0: bare types */ + S('d') = INT, S('i') = INT, + S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, + S('c') = CHAR, S('C') = INT, + S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR, + S('m') = NOARG, + S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE, + S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE, + }, { /* 1: l-prefixed */ + S('d') = LONG, S('i') = LONG, + S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, + S('c') = INT, S('s') = PTR, S('n') = PTR, + S('l') = LLPRE, + }, { /* 2: ll-prefixed */ + S('d') = LLONG, S('i') = LLONG, + S('o') = ULLONG, S('u') = ULLONG, + S('x') = ULLONG, S('X') = ULLONG, + S('n') = PTR, + }, { /* 3: h-prefixed */ + S('d') = SHORT, S('i') = SHORT, + S('o') = USHORT, S('u') = USHORT, + S('x') = USHORT, S('X') = USHORT, + S('n') = PTR, + S('h') = HHPRE, + }, { /* 4: hh-prefixed */ + S('d') = CHAR, S('i') = CHAR, + S('o') = UCHAR, S('u') = UCHAR, + S('x') = UCHAR, S('X') = UCHAR, + S('n') = PTR, + }, { /* 5: L-prefixed */ + S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL, + S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL, + S('n') = PTR, + }, { /* 6: z- or t-prefixed (assumed to be same size) */ + S('d') = PDIFF, S('i') = PDIFF, + S('o') = SIZET, S('u') = SIZET, + S('x') = SIZET, S('X') = SIZET, + S('n') = PTR, + }, { /* 7: j-prefixed */ + S('d') = IMAX, S('i') = IMAX, + S('o') = UMAX, S('u') = UMAX, + S('x') = UMAX, S('X') = UMAX, + S('n') = PTR, + } +}; + +#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A') + +union arg +{ + uintmax_t i; + long double f; + void *p; +}; + +static void pop_arg(union arg *arg, int type, va_list *ap) +{ + /* Give the compiler a hint for optimizing the switch. */ + if ((unsigned)type > MAXSTATE) return; + switch (type) { + case PTR: arg->p = va_arg(*ap, void *); + break; case INT: arg->i = va_arg(*ap, int); + break; case UINT: arg->i = va_arg(*ap, unsigned int); +#ifndef LONG_IS_INT + break; case LONG: arg->i = va_arg(*ap, long); + break; case ULONG: arg->i = va_arg(*ap, unsigned long); +#endif + break; case ULLONG: arg->i = va_arg(*ap, unsigned long long); + break; case SHORT: arg->i = (short)va_arg(*ap, int); + break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int); + break; case CHAR: arg->i = (signed char)va_arg(*ap, int); + break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int); +#ifdef ODD_TYPES + break; case LLONG: arg->i = va_arg(*ap, long long); + break; case SIZET: arg->i = va_arg(*ap, size_t); + break; case IMAX: arg->i = va_arg(*ap, intmax_t); + break; case UMAX: arg->i = va_arg(*ap, uintmax_t); + break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t); + break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *); +#endif + break; case DBL: arg->f = va_arg(*ap, double); + break; case LDBL: arg->f = va_arg(*ap, long double); + } +} + +static void out(FILE *f, const char *s, size_t l) +{ + __fwritex(s, l, f); +} + +static void pad(FILE *f, char c, int w, int l, int fl) +{ + char pad[256]; + if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; + l = w - l; + memset(pad, c, l>sizeof pad ? sizeof pad : l); + for (; l >= sizeof pad; l -= sizeof pad) + out(f, pad, sizeof pad); + out(f, pad, l); +} + +static const char xdigits[16] = { + "0123456789ABCDEF" +}; + +static char *fmt_x(uintmax_t x, char *s, int lower) +{ + for (; x; x>>=4) *--s = xdigits[(x&15)]|lower; + return s; +} + +static char *fmt_o(uintmax_t x, char *s) +{ + for (; x; x>>=3) *--s = '0' + (x&7); + return s; +} + +static char *fmt_u(uintmax_t x, char *s) +{ + unsigned long y; + for ( ; x>ULONG_MAX; x/=10) *--s = '0' + x%10; + for (y=x; y; y/=10) *--s = '0' + y%10; + return s; +} + +static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) +{ + uint32_t big[(LDBL_MAX_EXP+LDBL_MANT_DIG)/9+1]; + uint32_t *a, *d, *r, *z; + int e2=0, e, i, j, l; + char buf[9+LDBL_MANT_DIG/4], *s; + const char *prefix="-+ "; + int pl; + char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; + + pl=1; + if (y<0 || 1/y<0) { + y=-y; + } else if (fl & MARK_POS) { + prefix++; + } else if (fl & PAD_POS) { + prefix+=2; + } else pl=0; + + if (!isfinite(y)) { + char *s = (t&32)?"inf":"INF"; + if (y!=y) s=(t&32)?"nan":"NAN", pl=0; + pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); + out(f, prefix, pl); + out(f, s, 3); + pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); + return MAX(w, 3+pl); + } + + y = frexpl(y, &e2) * 2; + if (y) e2--; + + if ((t|32)=='a') { + long double round = 8.0; + int re; + + if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; + else re=LDBL_MANT_DIG/4-1-p; + + if (re) { + if (pl && *prefix=='-') y=-y; + while (re--) round*=16; + y+=round; + y-=round; + if (y<0) y=-y; + } + + estr=fmt_u(e2<0 ? -e2 : e2, ebuf); + if (estr==ebuf) *--estr='0'; + *--estr = (e2<0 ? '-' : '+'); + *--estr = t+('p'-'a'); + + s=buf; + *s++='0'; + *s++=t+('x'-'a'); + do { + int x=y; + *s++=xdigits[x]|(t&32); + y=16*(y-x); + if (s-buf==3 && (y||p>0||(fl&ALT_FORM))) *s++='.'; + } while (y); + + if (p<0) p = s-buf-4; + l = 1 + p + (p || (fl&ALT_FORM)) + ebuf-estr; + + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + out(f, buf, s-buf); + pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); + out(f, estr, ebuf-estr); + pad(f, '0', w, pl+l, fl^LEFT_ADJ); + return MAX(w, pl+l); + } + if (p<0) p=6; + + y *= 0x1p28; e2-=28; + + if (e2<0) a=r=z=big; + else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + + do { + *z = y; + y = 1000000000*(y-*z++); + } while (y); + + while (e2>0) { + uint32_t carry=0; + int sh=MIN(29,e2); + for (d=z-1; d>=a; d--) { + uint64_t x = ((uint64_t)*d<<sh)+carry; + *d = x % 1000000000; + carry = x / 1000000000; + } + if (!z[-1] && z>a) z--; + if (carry) *--a = carry; + e2-=sh; + } + while (e2<0) { + uint32_t carry=0, *z2; + int sh=MIN(9,-e2); + for (d=a; d<z; d++) { + uint32_t rm = *d & (1<<sh)-1; + *d = (*d>>sh) + carry; + carry = (1000000000>>sh) * rm; + } + if (!*a) a++; + if (carry) *z++ = carry; + /* Avoid (slow!) computation past requested precision */ + z2 = ((t|32)=='f' ? r : a) + 2 + p/9; + z = MIN(z, z2); + e2+=sh; + } + + if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++); + else e=0; + + /* Perform rounding: j is precision after the radix (possibly neg) */ + j = p - ((t|32)!='f')*e - ((t|32)=='g'); + if (j < 9*(z-r-1)) { + uint32_t x; + /* We avoid C's broken division of negative numbers */ + d = r + 1 + (j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP; + j += 9*LDBL_MAX_EXP; + j %= 9; + for (i=10, j++; j<9; i*=10, j++); + x = *d % i; + /* Are there any significant digits past j? */ + if (x || d+1!=z) { + long double round = CONCAT(0x1p,LDBL_MANT_DIG); + long double small; + if (x<i/2) small=0x01p-1; + else if (i==i/2 && d+1==z) small=0x10p-1; + else small=0x11p-1; + if (pl && *prefix=='-') round*=-1, small*=-1; + /* Decide whether to round by probing round+small */ + if (round+small != round) { + *d = *d - x + i; + while (*d > 999999999) { + *d--=0; + (*d)++; + } + if (d<a) a=d; + for (i=10, e=9*(r-a); *a>=i; i*=10, e++); + } + } + for (; !z[-1] && z>a; z--); + } + + if ((t|32)=='g') { + if (!p) p++; + if (p>e && e>=-4) { + t--; + p-=e+1; + } else { + t-=2; + p--; + } + if (!(fl&ALT_FORM)) { + /* Count trailing zeros in last place */ + if (z>a) for (i=10, j=0; z[-1]%i==0; i*=10, j++); + else j=9; + if ((t|32)=='f') + p = MIN(p,MAX(0,9*(z-r-1)-j)); + else + p = MIN(p,MAX(0,9*(z-r-1)+e-j)); + } + } + l = 1 + p + (p || (fl&ALT_FORM)); + if ((t|32)=='f') { + if (e>0) l+=e; + } else { + estr=fmt_u(e<0 ? -e : e, ebuf); + while(ebuf-estr<2) *--estr='0'; + *--estr = (e<0 ? '-' : '+'); + *--estr = t; + l += ebuf-estr; + } + + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + + if ((t|32)=='f') { + if (a>r) a=r; + for (d=a; d<=r; d++) { + char *s = fmt_u(*d, buf+9); + if (d!=a) while (s>buf) *--s='0'; + else if (s==buf+9) *--s='0'; + out(f, s, buf+9-s); + } + if (p || (fl&ALT_FORM)) out(f, ".", 1); + for (; d<z && p>0; d++, p-=9) { + char *s = fmt_u(*d, buf+9); + while (s>buf) *--s='0'; + out(f, s, MIN(9,p)); + } + pad(f, '0', p+9, 9, 0); + } else { + if (z<=a) z=a+1; + for (d=a; d<z && p>=0; d++) { + char *s = fmt_u(*d, buf+9); + if (s==buf+9) *--s='0'; + if (d!=a) while (s>buf) *--s='0'; + else { + out(f, s++, 1); + if (p>0||(fl&ALT_FORM)) out(f, ".", 1); + } + out(f, s, MIN(buf+9-s, p)); + p -= buf+9-s; + } + pad(f, '0', p+18, 18, 0); + out(f, estr, ebuf-estr); + } + + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + + return MAX(w, pl+l); +} + +static int getint(char **s) { + int i; + for (i=0; isdigit(**s); (*s)++) + i = 10*i + (**s-'0'); + return i; +} + +static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type) +{ + char *a, *z, *s=(char *)fmt; + unsigned l10n=0, litpct, fl; + int w, p; + union arg arg; + int argpos; + unsigned st, ps; + int cnt=0, l=0; + int i; + char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4]; + const char *prefix; + int t, pl; + wchar_t wc[2], *ws; + char mb[4]; + + for (;;) { + /* Update output count, end loop when fmt is exhausted */ + if (cnt >= 0) { + if (l > INT_MAX - cnt) { + if (!ferror(f)) errno = EOVERFLOW; + cnt = -1; + } else cnt += l; + } + if (!*s) break; + + /* Handle literal text and %% format specifiers */ + for (a=s; *s && *s!='%'; s++); + litpct = strspn(s, "%")/2; /* Optimize %%%% runs */ + z = s+litpct; + s += 2*litpct; + l = z-a; + if (f) out(f, a, l); + if (l) continue; + + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + argpos = s[1]-'0'; + s+=3; + } else { + argpos = -1; + s++; + } + + /* Read modifier flags */ + for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++) + fl |= 1U<<*s-' '; + + /* Read field width */ + if (*s=='*') { + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + nl_type[s[1]-'0'] = INT; + w = nl_arg[s[1]-'0'].i; + s+=3; + } else if (!l10n) { + w = f ? va_arg(*ap, int) : 0; + s++; + } else return -1; + if (w<0) fl|=LEFT_ADJ, w=-w; + } else if ((w=getint(&s))<0) return -1; + + /* Read precision */ + if (*s=='.' && s[1]=='*') { + if (isdigit(s[2]) && s[3]=='$') { + nl_type[s[2]-'0'] = INT; + p = nl_arg[s[2]-'0'].i; + s+=4; + } else if (!l10n) { + p = f ? va_arg(*ap, int) : 0; + s+=2; + } else return -1; + } else if (*s=='.') { + s++; + p = getint(&s); + } else p = -1; + + /* Format specifier state machine */ + st=0; + do { + if (OOB(*s)) return -1; + ps=st; + st=states[st]S(*s++); + } while (st-1<STOP); + if (!st) return -1; + + /* Check validity of argument type (nl/normal) */ + if (st==NOARG) { + if (argpos>=0) return -1; + else if (!f) continue; + } else { + if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos]; + else if (f) pop_arg(&arg, st, ap); + else return 0; + } + + if (!f) continue; + + z = buf + sizeof(buf); + prefix = "-+ 0X0x"; + pl = 0; + t = s[-1]; + + /* Transform ls,lc -> S,C */ + if (ps && (t&15)==3) t&=~32; + + /* - and 0 flags are mutually exclusive */ + if (fl & LEFT_ADJ) fl &= ~ZERO_PAD; + + switch(t) { + case 'n': + switch(ps) { + case BARE: *(int *)arg.p = l; + case LPRE: *(long *)arg.p = l; + case LLPRE: *(long long *)arg.p = l; + case HPRE: *(unsigned short *)arg.p = l; + case HHPRE: *(unsigned char *)arg.p = l; + case ZTPRE: *(size_t *)arg.p = l; + case JPRE: *(uintmax_t *)arg.p = l; + } + continue; + case 'p': + p = MAX(p, 2*sizeof(void*)); + t = 'x'; + fl |= ALT_FORM; + case 'x': case 'X': + a = fmt_x(arg.i, z, t&32); + if (fl & ALT_FORM) prefix+=(t>>4), pl=2; + if (0) { + case 'o': + a = fmt_o(arg.i, z); + if ((fl&ALT_FORM) && arg.i) prefix+=5, pl=1; + } if (0) { + case 'd': case 'i': + pl=1; + if (arg.i>INTMAX_MAX) { + arg.i=-arg.i; + } else if (fl & MARK_POS) { + prefix++; + } else if (fl & PAD_POS) { + prefix+=2; + } else pl=0; + case 'u': + a = fmt_u(arg.i, z); + } + if (!arg.i && !p) continue; + if (p>=0) fl &= ~ZERO_PAD; + p = MAX(p, z-a + !arg.i); + break; + case 'c': + *(a=z-(p=1))=arg.i; + fl &= ~ZERO_PAD; + break; + case 'm': + if (1) a = strerror(errno); else + case 's': + a = arg.p; + z = memchr(a, 0, p); + if (!z) z=a+p; + else p=z-a; + fl &= ~ZERO_PAD; + break; + case 'C': + wc[0] = arg.i; + wc[1] = 0; + arg.p = wc; + p = -1; + case 'S': + ws = arg.p; + for (i=0; *ws && (l=wctomb(mb, *ws++))>=0 && l<=0U+p-i; i+=l); + if (l<0) return -1; + p = i; + pad(f, ' ', w, p, fl); + ws = arg.p; + for (i=0; *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) + out(f, mb, l); + pad(f, ' ', w, p, fl^LEFT_ADJ); + l = w>p ? w : p; + continue; + case 'e': case 'f': case 'g': case 'a': + case 'E': case 'F': case 'G': case 'A': + l = fmt_fp(f, arg.f, w, p, fl, t); + continue; + } + + if (p < z-a) p = z-a; + if (w < pl+p) w = pl+p; + + pad(f, ' ', w, pl+p, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+p, fl^ZERO_PAD); + pad(f, '0', p, z-a, 0); + out(f, a, z-a); + pad(f, ' ', w, pl+p, fl^LEFT_ADJ); + + l = w; + } + + if (f) return cnt; + if (!l10n) return 0; + + for (i=1; i<=NL_ARGMAX && nl_type[i]; i++) + pop_arg(nl_arg+i, nl_type[i], ap); + for (; i<=NL_ARGMAX && !nl_type[i]; i++); + if (i<=NL_ARGMAX) return -1; + return 1; +} + +int vfprintf(FILE *f, const char *fmt, va_list ap) +{ + va_list ap2; + int nl_type[NL_ARGMAX] = {0}; + union arg nl_arg[NL_ARGMAX]; + int ret; + + va_copy(ap2, ap); + if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) return -1; + + FLOCK(f); + ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); + FUNLOCK(f); + va_end(ap2); + return ret; +} diff --git a/src/stdio/vfscanf.c b/src/stdio/vfscanf.c new file mode 100644 index 00000000..69f45081 --- /dev/null +++ b/src/stdio/vfscanf.c @@ -0,0 +1,43 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "stdio_impl.h" +#include "__scanf.h" + +static void f_read(rctx_t *r) +{ + FILE *f = r->opaque; + if ((r->c = __uflow(f)) >= 0) r->l++; +} + +int vfscanf(FILE *f, const char *fmt, va_list ap) +{ + size_t l = strlen(fmt), i, result; + rctx_t r = { f_read, (void *)f, 0, isspace }; + wchar_t fmt2[l+1]; + + if (l > 0x100000) { + errno = ENOMEM; + return -1; + } + for (i=0; i<=l; i++) fmt2[i] = (unsigned char)fmt[i]; + + FLOCK(f); + + result = __scanf(&r, fmt2, ap); + + if (r.u && r.c >= 0) { + /* This code takes care of the case where the caller performs + * a nonmatching scanf to leave a character in the unscan + * buffer, followed by an unget, followed by a scanf that + * matches zero characters. In this case the final 'unread' + * character must be returned to the unget buffer rather than + * the unscan buffer. */ + f->rpos--; + } + + FUNLOCK(f); + return result; +} diff --git a/src/stdio/vfwscanf.c b/src/stdio/vfwscanf.c new file mode 100644 index 00000000..491c1403 --- /dev/null +++ b/src/stdio/vfwscanf.c @@ -0,0 +1,28 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <wchar.h> +#include <wctype.h> + +#include "stdio_impl.h" +#include "__scanf.h" + +static void f_read(rctx_t *r) +{ + FILE *f = r->opaque; + if ((r->c = fgetwc(f)) >= 0) r->l++; +} + +int vfwscanf(FILE *f, const wchar_t *fmt, va_list ap) +{ + rctx_t r = { f_read, (void *)f, 1, iswspace }; + int result; + + result = __scanf(&r, fmt, ap); + + if (r.u && r.c >= 0) { + ungetwc(r.c, f); + } + + return result; +} diff --git a/src/stdio/vprintf.c b/src/stdio/vprintf.c new file mode 100644 index 00000000..67b38dac --- /dev/null +++ b/src/stdio/vprintf.c @@ -0,0 +1,6 @@ +#include <stdio.h> + +int vprintf(const char *fmt, va_list ap) +{ + return vfprintf(stdout, fmt, ap); +} diff --git a/src/stdio/vscanf.c b/src/stdio/vscanf.c new file mode 100644 index 00000000..6f55b1c3 --- /dev/null +++ b/src/stdio/vscanf.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <stdarg.h> + +int vscanf(const char *fmt, va_list ap) +{ + return vfscanf(stdin, fmt, ap); +} diff --git a/src/stdio/vsnprintf.c b/src/stdio/vsnprintf.c new file mode 100644 index 00000000..bda6b49b --- /dev/null +++ b/src/stdio/vsnprintf.c @@ -0,0 +1,33 @@ +#include "stdio_impl.h" + +static size_t sn_write(FILE *f, const unsigned char *s, size_t l) +{ + /* pretend to succeed, but discard data */ + return l; +} + +int vsnprintf(char *s, size_t n, const char *fmt, va_list ap) +{ + int r; + FILE f; + unsigned char buf[1]; + + memset(&f, 0, sizeof(FILE)); + f.lbf = EOF; + f.write = sn_write; + f.buf_size = 1; + f.buf = buf; + if (n > INT_MAX) { + errno = EOVERFLOW; + return -1; + } else if (n > 0) { + if (n > (char *)0+SIZE_MAX-s) n = (char *)0+SIZE_MAX-s; + f.wpos = s; + f.wbase = f.wend = s+n-1; + f.wstop = f.wend - 1; + } + r = vfprintf(&f, fmt, ap); + /* wpos points just after last byte written, or to s+n-1 (wbase) */ + *f.wpos = 0; + return r; +} diff --git a/src/stdio/vsprintf.c b/src/stdio/vsprintf.c new file mode 100644 index 00000000..7836ccb2 --- /dev/null +++ b/src/stdio/vsprintf.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <limits.h> + +int vsprintf(char *s, const char *fmt, va_list ap) +{ + return vsnprintf(s, INT_MAX, fmt, ap); +} diff --git a/src/stdio/vsscanf.c b/src/stdio/vsscanf.c new file mode 100644 index 00000000..fd48f709 --- /dev/null +++ b/src/stdio/vsscanf.c @@ -0,0 +1,21 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "__scanf.h" + +static void s_read(rctx_t *r) +{ + unsigned char *s = r->opaque; + if (!s[r->l]) r->c = -1; + else r->c = s[r->l++]; +} + +int vsscanf(const char *s, const char *fmt, va_list ap) +{ + size_t l = strlen(fmt), i; + wchar_t fmt2[l+1]; + rctx_t r = { s_read, (void *)s, 0, isspace }; + for (i=0; i<=l; i++) fmt2[i] = (unsigned char)fmt[i]; + return __scanf(&r, fmt2, ap); +} diff --git a/src/stdio/vswscanf.c b/src/stdio/vswscanf.c new file mode 100644 index 00000000..2c4ffbe0 --- /dev/null +++ b/src/stdio/vswscanf.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> + +#include "__scanf.h" + +static void s_read(rctx_t *r) +{ + wchar_t *s = r->opaque; + if (!s[r->l]) r->c = -1; + else r->c = s[r->l++]; +} + +int vswscanf(const wchar_t *s, const wchar_t *fmt, va_list ap) +{ + rctx_t r = { s_read, (void *)s, 1, iswspace }; + return __scanf(&r, fmt, ap); +} diff --git a/src/stdio/vwscanf.c b/src/stdio/vwscanf.c new file mode 100644 index 00000000..86da0457 --- /dev/null +++ b/src/stdio/vwscanf.c @@ -0,0 +1,8 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int vwscanf(const wchar_t *fmt, va_list ap) +{ + return vfwscanf(stdin, fmt, ap); +} diff --git a/src/stdio/wscanf.c b/src/stdio/wscanf.c new file mode 100644 index 00000000..34b58846 --- /dev/null +++ b/src/stdio/wscanf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> +#include <wchar.h> + +int wscanf(const wchar_t *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vwscanf(fmt, ap); + va_end(ap); + return ret; +} diff --git a/src/stdlib/abs.c b/src/stdlib/abs.c new file mode 100644 index 00000000..4806d629 --- /dev/null +++ b/src/stdlib/abs.c @@ -0,0 +1,4 @@ +int abs(int a) +{ + return a>0 ? a : -a; +} diff --git a/src/stdlib/atof.c b/src/stdlib/atof.c new file mode 100644 index 00000000..f7fcd826 --- /dev/null +++ b/src/stdlib/atof.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +double atof(const char *s) +{ + return strtod(s, 0); +} diff --git a/src/stdlib/atoi.c b/src/stdlib/atoi.c new file mode 100644 index 00000000..648b154f --- /dev/null +++ b/src/stdlib/atoi.c @@ -0,0 +1,15 @@ +#include <stdlib.h> +#include <ctype.h> + +int atoi(const char *s) +{ + int n=0, neg=0; + while (isspace(*s)) s++; + switch (*s) { + case '-': neg=1; + case '+': s++; + } + while (isdigit(*s)) + n = 10*n + *s++ - '0'; + return neg ? -n : n; +} diff --git a/src/stdlib/atol.c b/src/stdlib/atol.c new file mode 100644 index 00000000..9c91bba9 --- /dev/null +++ b/src/stdlib/atol.c @@ -0,0 +1,16 @@ +#include <stdlib.h> +#include <ctype.h> + +long atol(const char *s) +{ + long n=0; + int neg=0; + while (isspace(*s)) s++; + switch (*s) { + case '-': neg=1; + case '+': s++; + } + while (isdigit(*s)) + n = 10*n + *s++ - '0'; + return neg ? -n : n; +} diff --git a/src/stdlib/atoll.c b/src/stdlib/atoll.c new file mode 100644 index 00000000..0e03e0a1 --- /dev/null +++ b/src/stdlib/atoll.c @@ -0,0 +1,16 @@ +#include <stdlib.h> +#include <ctype.h> + +long long atoll(const char *s) +{ + long long n=0; + int neg=0; + while (isspace(*s)) s++; + switch (*s) { + case '-': neg=1; + case '+': s++; + } + while (isdigit(*s)) + n = 10*n + *s++ - '0'; + return neg ? -n : n; +} diff --git a/src/stdlib/bsearch.c b/src/stdlib/bsearch.c new file mode 100644 index 00000000..61d89367 --- /dev/null +++ b/src/stdlib/bsearch.c @@ -0,0 +1,20 @@ +#include <stdlib.h> + +void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *)) +{ + void *try; + int sign; + while (nel > 0) { + try = (char *)base + width*(nel/2); + sign = cmp(key, try); + if (!sign) return try; + else if (nel == 1) break; + else if (sign < 0) + nel /= 2; + else { + base = try; + nel -= nel/2; + } + } + return NULL; +} diff --git a/src/stdlib/div.c b/src/stdlib/div.c new file mode 100644 index 00000000..e42c1f14 --- /dev/null +++ b/src/stdlib/div.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +div_t div(int num, int den) +{ + return (div_t){ num/den, num%den }; +} diff --git a/src/stdlib/frexp.c b/src/stdlib/frexp.c new file mode 100644 index 00000000..ae82cb30 --- /dev/null +++ b/src/stdlib/frexp.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <inttypes.h> + +double frexp(double x, int *e) +{ + union { double d; uint64_t i; } y = { x }; + int ee = y.i>>52 & 0x7ff; + + if (!ee) { + if (x) { + x = frexp(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0x7ff) { + return x; + } + + *e = ee - 0x3fe; + y.i &= 0x800fffffffffffffull; + y.i |= 0x3fe0000000000000ull; + return y.d; +} diff --git a/src/stdlib/frexpf.c b/src/stdlib/frexpf.c new file mode 100644 index 00000000..ee5e910a --- /dev/null +++ b/src/stdlib/frexpf.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <inttypes.h> + +float frexpf(float x, int *e) +{ + union { float f; uint32_t i; } y = { x }; + int ee = y.i>>23 & 0xff; + + if (!ee) { + if (x) { + x = frexpf(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0xff) { + return x; + } + + *e = ee - 0x7e; + y.i &= 0x807ffffful; + y.i |= 0x3f000000ul; + return y.f; +} diff --git a/src/stdlib/frexpl.c b/src/stdlib/frexpl.c new file mode 100644 index 00000000..ecfff007 --- /dev/null +++ b/src/stdlib/frexpl.c @@ -0,0 +1,25 @@ +#include <math.h> +#include <inttypes.h> + +/* This version is for 80-bit little endian long double */ + +long double frexpl(long double x, int *e) +{ + union { long double ld; uint16_t hw[5]; } y = { x }; + int ee = y.hw[4]&0x7fff; + + if (!ee) { + if (x) { + x = frexpl(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0x7fff) { + return x; + } + + *e = ee - 0x3ffe; + y.hw[4] &= 0x8000; + y.hw[4] |= 0x3ffe; + return y.ld; +} diff --git a/src/stdlib/imaxabs.c b/src/stdlib/imaxabs.c new file mode 100644 index 00000000..81001819 --- /dev/null +++ b/src/stdlib/imaxabs.c @@ -0,0 +1,6 @@ +#include <inttypes.h> + +intmax_t imaxabs(intmax_t a) +{ + return a>0 ? a : -a; +} diff --git a/src/stdlib/imaxdiv.c b/src/stdlib/imaxdiv.c new file mode 100644 index 00000000..b2ce821f --- /dev/null +++ b/src/stdlib/imaxdiv.c @@ -0,0 +1,6 @@ +#include <inttypes.h> + +imaxdiv_t imaxdiv(intmax_t num, intmax_t den) +{ + return (imaxdiv_t){ num/den, num%den }; +} diff --git a/src/stdlib/labs.c b/src/stdlib/labs.c new file mode 100644 index 00000000..675b95b8 --- /dev/null +++ b/src/stdlib/labs.c @@ -0,0 +1,4 @@ +long labs(long a) +{ + return a>0 ? a : -a; +} diff --git a/src/stdlib/ldiv.c b/src/stdlib/ldiv.c new file mode 100644 index 00000000..36eb960b --- /dev/null +++ b/src/stdlib/ldiv.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +ldiv_t ldiv(long num, long den) +{ + return (ldiv_t){ num/den, num%den }; +} diff --git a/src/stdlib/llabs.c b/src/stdlib/llabs.c new file mode 100644 index 00000000..bec4a03d --- /dev/null +++ b/src/stdlib/llabs.c @@ -0,0 +1,4 @@ +long long llabs(long long a) +{ + return a>0 ? a : -a; +} diff --git a/src/stdlib/lldiv.c b/src/stdlib/lldiv.c new file mode 100644 index 00000000..7aaf7a0e --- /dev/null +++ b/src/stdlib/lldiv.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +lldiv_t lldiv(long long num, long long den) +{ + return (lldiv_t){ num/den, num%den }; +} diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c new file mode 100644 index 00000000..f5bf3d02 --- /dev/null +++ b/src/stdlib/qsort.c @@ -0,0 +1,50 @@ +#include <stdlib.h> +#include <string.h> + +/* A simple heap sort implementation.. only in-place O(nlogn) sort I know. */ + +#define MIN(a, b) ((a)<(b) ? (a) : (b)) + +static void swap(char *a, char *b, size_t len) +{ + char tmp[256]; + size_t l; + while (len) { + l = MIN(sizeof tmp, len); + memcpy(tmp, a, l); + memcpy(a, b, l); + memcpy(b, tmp, l); + a += l; + b += l; + len -= l; + } +} + +static void sift(char *base, size_t root, size_t nel, size_t width, int (*cmp)(const void *, const void *)) +{ + size_t max; + + while (2*root <= nel) { + max = 2*root; + if (max < nel && cmp(base+max*width, base+(max+1)*width) < 0) + max++; + if (cmp(base+root*width, base+max*width) < 0) { + swap(base+root*width, base+max*width, width); + root = max; + } else break; + } +} + +void qsort(void *_base, size_t nel, size_t width, int (*cmp)(const void *, const void *)) +{ + char *base = _base; + size_t i; + + if (!nel) return; + for (i=(nel+1)/2; i; i--) + sift(base, i-1, nel-1, width, cmp); + for (i=nel-1; i; i--) { + swap(base, base+i*width, width); + sift(base, 0, i-1, width, cmp); + } +} diff --git a/src/stdlib/strtod.c b/src/stdlib/strtod.c new file mode 100644 index 00000000..388058fe --- /dev/null +++ b/src/stdlib/strtod.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +double strtod(const char *s, char **p) +{ + return strtold(s, p); +} diff --git a/src/stdlib/strtof.c b/src/stdlib/strtof.c new file mode 100644 index 00000000..07b32df4 --- /dev/null +++ b/src/stdlib/strtof.c @@ -0,0 +1,6 @@ +#include <stdlib.h> + +float strtof(const char *s, char **p) +{ + return strtold(s, p); +} diff --git a/src/stdlib/strtoimax.c b/src/stdlib/strtoimax.c new file mode 100644 index 00000000..19691091 --- /dev/null +++ b/src/stdlib/strtoimax.c @@ -0,0 +1,25 @@ +#include <inttypes.h> +#include <errno.h> +#include <ctype.h> + +intmax_t strtoimax(const char *s1, char **p, int base) +{ + const unsigned char *s = s1; + int sign = 0; + uintmax_t x; + + /* Initial whitespace */ + for (; isspace(*s); s++); + + /* Optional sign */ + if (*s == '-') sign = *s++; + else if (*s == '+') s++; + + x = strtoumax(s, p, base); + if (x > INTMAX_MAX) { + if (!sign || -x != INTMAX_MIN) + errno = ERANGE; + return sign ? INTMAX_MIN : INTMAX_MAX; + } + return sign ? -x : x; +} diff --git a/src/stdlib/strtol.c b/src/stdlib/strtol.c new file mode 100644 index 00000000..ace820af --- /dev/null +++ b/src/stdlib/strtol.c @@ -0,0 +1,17 @@ +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +long strtol(const char *s, char **p, int base) +{ + intmax_t x = strtoimax(s, p, base); + if (x > LONG_MAX) { + errno = ERANGE; + return LONG_MAX; + } else if (x < LONG_MIN) { + errno = ERANGE; + return LONG_MIN; + } + return x; +} diff --git a/src/stdlib/strtold.c b/src/stdlib/strtold.c new file mode 100644 index 00000000..54f80469 --- /dev/null +++ b/src/stdlib/strtold.c @@ -0,0 +1,93 @@ +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> + +long double strtold(const char *s1, char **p) +{ + const unsigned char *s = s1; + long double x = 0; + long double frac; + int sign = 0; + int nonzero = 0; + int radix = '.'; + long e; + + if (!p) p = (char **)&s1; + + /* Initial whitespace */ + for (; isspace(*s); s++); + + /* Optional sign */ + if (*s == '-') sign = *s++; + else if (*s == '+') s++; + + /* Handle infinities and NaNs. */ + if ((s[0]|32)=='i' && (s[1]|32)=='n' && (s[2]|32)=='f') { + *p = (char *)s + 3; + return sign ? -1.0/0.0 : 1.0/0.0; + } else if ((s[0]|32)=='n' && (s[1]|32)=='a' && (s[2]|32)=='n') { + *p = (char *)s + 3; + return 0.0/0.0; + } + + /* Possible hex float */ + if (s[0]=='0' && (s[1]|32)=='x') { + /* Mantissa must be non-degenerate */ + if (!isxdigit(s[2]) && (s[2]!=radix || !isxdigit(s[3]))) { + /* Decimal float 0, 'x' extraneous */ + *p = (char *)++s; + return 0; + } + /* We have a real hex float */ + s += 2; + for (; isxdigit(*s); s++) { + x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'); + if (*s!='0') nonzero=1; + } + if (*s == radix) { + frac = 1.0/16.0; + for (s++; isxdigit(*s); s++) { + x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'); + frac *= 1.0/16.0; + if (*s!='0') nonzero=1; + } + } + if ((*s|32) == 'p') { + e = strtol(s+1, (void *)&s, 10); + for (; e>0; e--) x *= 2.0; + for (; e<0; e++) x *= 0.5; + } + if ((nonzero && !x) || !(1.0/x)) + errno = ERANGE; + *p = (char *)s; + return sign ? -x : x; + } + + /* Mantissa must be non-degenerate */ + if (!isdigit(s[0]) && (s[0]!=radix || !isdigit(s[1]))) { + *p = (char *)s1; + return 0; + } + + for (; isdigit(*s); s++) { + x = 10*x + *s-'0'; + if (*s!='0') nonzero=1; + } + if (*s == radix) { + frac = 10.0; + for (s++; isdigit(*s); s++) { + x += (*s-'0') / frac; + frac *= 10.0; + if (*s!='0') nonzero=1; + } + } + if ((*s|32)=='e') { + e = strtol(++s, (void *)&s, 10); + for (; e>0; e--) x *= 10.0; + for (; e<0; e++) x /= 10.0; + } + if ((nonzero && !x) || !(1.0/x)) + errno = ERANGE; + *p = (char*)s; + return sign ? -x : x; +} diff --git a/src/stdlib/strtoll.c b/src/stdlib/strtoll.c new file mode 100644 index 00000000..9ab66fd9 --- /dev/null +++ b/src/stdlib/strtoll.c @@ -0,0 +1,17 @@ +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +long long strtoll(const char *s, char **p, int base) +{ + intmax_t x = strtoimax(s, p, base); + if (x > LLONG_MAX) { + errno = ERANGE; + return LLONG_MAX; + } else if (x < LLONG_MIN) { + errno = ERANGE; + return LLONG_MIN; + } + return x; +} diff --git a/src/stdlib/strtoul.c b/src/stdlib/strtoul.c new file mode 100644 index 00000000..951d5e8c --- /dev/null +++ b/src/stdlib/strtoul.c @@ -0,0 +1,14 @@ +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +unsigned long strtoul(const char *s, char **p, int base) +{ + uintmax_t x = strtoumax(s, p, base); + if (x > ULONG_MAX) { + errno = ERANGE; + return ULONG_MAX; + } + return x; +} diff --git a/src/stdlib/strtoull.c b/src/stdlib/strtoull.c new file mode 100644 index 00000000..20aa7bde --- /dev/null +++ b/src/stdlib/strtoull.c @@ -0,0 +1,14 @@ +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +unsigned long long strtoull(const char *s, char **p, int base) +{ + uintmax_t x = strtoumax(s, p, base); + if (x > ULLONG_MAX) { + errno = ERANGE; + return ULLONG_MAX; + } + return x; +} diff --git a/src/stdlib/strtoumax.c b/src/stdlib/strtoumax.c new file mode 100644 index 00000000..a529f6e8 --- /dev/null +++ b/src/stdlib/strtoumax.c @@ -0,0 +1,123 @@ +#include <inttypes.h> +#include <stdlib.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> + +/* Lookup table for digit values. -1==255>=36 -> invalid */ +static const unsigned char digits[] = { +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, +-1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, +25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1, +-1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, +25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + +uintmax_t strtoumax(const char *s1, char **p, int base) +{ + const unsigned char *s = s1; + size_t x1, z1; + uintmax_t x, z=0; + int sign = 0; + int shift; + + if (!p) p = (char **)&s1; + + /* Initial whitespace */ + for (; isspace(*s); s++); + + /* Optional sign */ + if (*s == '-') sign = *s++; + else if (*s == '+') s++; + + /* Default base 8, 10, or 16 depending on prefix */ + if (base == 0) { + if (s[0] == '0') { + if ((s[1]|32) == 'x') base = 16; + else base = 8; + } else { + base = 10; + } + } + + if ((unsigned)base-2 > 36-2 || digits[*s]>=base) { + *p = (char *)s1; + errno = EINVAL; + return 0; + } + + /* Main loops. Only use big types if we have to. */ + if (base == 10) { + for (x1=0; isdigit(*s) && x1<=SIZE_MAX/10-10; s++) + x1 = 10*x1 + *s-'0'; + for (x=x1; isdigit(*s) && x<=UINTMAX_MAX/10-10; s++) + x = 10*x + *s-'0'; + if (isdigit(*s)) { + if (isdigit(s[1]) || 10*x>UINTMAX_MAX-(*s-'0')) + goto overflow; + x = 10*x + *s-'0'; + } + } else if (!(base & base/2)) { + if (base == 16) { + if (s[0]=='0' && (s[1]|32)=='x' && digits[s[2]]<16) + s+=2; + shift=4; + z1 = SIZE_MAX/16; + z = UINTMAX_MAX/16; + } else if (base == 8) { + shift=3; + z1 = SIZE_MAX/8; + z = UINTMAX_MAX/8; + } else if (base == 2) { + shift=1; + z1 = SIZE_MAX/2; + z = UINTMAX_MAX/2; + } else if (base == 4) { + shift=2; + z1 = SIZE_MAX/4; + z = UINTMAX_MAX/4; + } else /* if (base == 32) */ { + shift=5; + z1 = SIZE_MAX/32; + z = UINTMAX_MAX/32; + } + for (x1=0; digits[*s]<base && x1<=z1; s++) + x1 = (x1<<shift) + digits[*s]; + for (x=x1; digits[*s]<base && x<=z; s++) + x = (x<<shift) + digits[*s]; + if (digits[*s] < base) goto overflow; + } else { + z1 = SIZE_MAX/base-base; + for (x1=0; digits[*s]<base && x1<=z1; s++) + x1 = x1*base + digits[*s]; + if (digits[*s]<base) + z = UINTMAX_MAX/base-base; + for (x=x1; digits[*s]<base && x<=z; s++) + x = x*base + digits[*s]; + if (digits[*s] < base) { + if (digits[s[1]]<base || x*base>UINTMAX_MAX-digits[*s]) + goto overflow; + x = x*base + digits[*s]; + } + } + + *p = (char *)s; + return sign ? -x : x; + +overflow: + for (; digits[*s] < base; s++); + *p = (char *)s; + errno = ERANGE; + return UINTMAX_MAX; +} diff --git a/src/stdlib/wcstoimax.c b/src/stdlib/wcstoimax.c new file mode 100644 index 00000000..861fcb54 --- /dev/null +++ b/src/stdlib/wcstoimax.c @@ -0,0 +1,24 @@ +#include <wchar.h> +#include <inttypes.h> +#include <errno.h> + +intmax_t wcstoimax(const wchar_t *s, wchar_t **p, int base) +{ + int sign = 0; + uintmax_t x; + + /* Initial whitespace */ + for (; iswspace(*s); s++); + + /* Optional sign */ + if (*s == '-') sign = *s++; + else if (*s == '+') s++; + + x = wcstoumax(s, p, base); + if (x > INTMAX_MAX) { + if (!sign || -x != INTMAX_MIN) + errno = ERANGE; + return sign ? INTMAX_MIN : INTMAX_MAX; + } + return sign ? -x : x; +} diff --git a/src/stdlib/wcstol.c b/src/stdlib/wcstol.c new file mode 100644 index 00000000..aad62e5b --- /dev/null +++ b/src/stdlib/wcstol.c @@ -0,0 +1,18 @@ +#include <wchar.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +long wcstol(const wchar_t *s, wchar_t **p, int base) +{ + intmax_t x = wcstoimax(s, p, base); + if (x > LONG_MAX) { + errno = ERANGE; + return LONG_MAX; + } else if (x < LONG_MIN) { + errno = ERANGE; + return LONG_MIN; + } + return x; +} diff --git a/src/stdlib/wcstoll.c b/src/stdlib/wcstoll.c new file mode 100644 index 00000000..ddfea74b --- /dev/null +++ b/src/stdlib/wcstoll.c @@ -0,0 +1,18 @@ +#include <wchar.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +long long wcstoll(const wchar_t *s, wchar_t **p, int base) +{ + intmax_t x = wcstoimax(s, p, base); + if (x > LLONG_MAX) { + errno = ERANGE; + return LLONG_MAX; + } else if (x < LLONG_MIN) { + errno = ERANGE; + return LLONG_MIN; + } + return x; +} diff --git a/src/stdlib/wcstoul.c b/src/stdlib/wcstoul.c new file mode 100644 index 00000000..e39faafe --- /dev/null +++ b/src/stdlib/wcstoul.c @@ -0,0 +1,15 @@ +#include <wchar.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +unsigned long wcstoul(const wchar_t *s, wchar_t **p, int base) +{ + uintmax_t x = wcstoumax(s, p, base); + if (x > ULONG_MAX) { + errno = ERANGE; + return ULONG_MAX; + } + return x; +} diff --git a/src/stdlib/wcstoull.c b/src/stdlib/wcstoull.c new file mode 100644 index 00000000..e324dfb2 --- /dev/null +++ b/src/stdlib/wcstoull.c @@ -0,0 +1,15 @@ +#include <wchar.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <limits.h> + +unsigned long long wcstoull(const wchar_t *s, wchar_t **p, int base) +{ + uintmax_t x = wcstoumax(s, p, base); + if (x > ULLONG_MAX) { + errno = ERANGE; + return ULLONG_MAX; + } + return x; +} diff --git a/src/stdlib/wcstoumax.c b/src/stdlib/wcstoumax.c new file mode 100644 index 00000000..a8f4680f --- /dev/null +++ b/src/stdlib/wcstoumax.c @@ -0,0 +1,47 @@ +#include <wchar.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> + +uintmax_t wcstoumax(const wchar_t *s, wchar_t **p, int base) +{ + /* Large enough for largest value in binary */ + char buf[sizeof(uintmax_t)*8+2]; + int sign = 0, skipped=0; + + if (!p) p = (wchar_t **)&s; + + if (base && (unsigned)base-2 > 36-2) { + *p = (wchar_t *)s; + errno = EINVAL; + return 0; + } + + /* Initial whitespace */ + for (; iswspace(*s); s++); + + /* Optional sign */ + if (*s == '-') sign = *s++; + else if (*s == '+') s++; + + /* Skip leading zeros but don't allow leading zeros before "0x". */ + for (; s[0]=='0' && s[1]=='0'; s++) skipped=1; + if (skipped && (base==0 || base==16) && (s[1]|32)=='x') { + *p = (wchar_t *)(s+1); + return 0; + } + + /* Convert to normal char string so we can use strtoumax */ + buf[0] = sign; + if (wcstombs(buf+!!sign, s, sizeof buf-1) < 0) return 0; + buf[sizeof buf-1]=0; + + /* Compute final position */ + if (p) { + if ((base==0 || base==16) && s[0]=='0' && (s[1]|32)=='x' && iswxdigit(s[2])) s+=2; + for(;*s&&((unsigned)*s-'0'<base||((unsigned)*s|32)-'a'<base-10);s++); + *p = (wchar_t *)s; + } + + return strtoumax(buf, 0, base); +} diff --git a/src/string/bcmp.c b/src/string/bcmp.c new file mode 100644 index 00000000..5d6a388b --- /dev/null +++ b/src/string/bcmp.c @@ -0,0 +1,7 @@ +#include <string.h> +#include <strings.h> + +int bcmp(const void *s1, const void *s2, size_t n) +{ + return memcmp(s1, s2, n); +} diff --git a/src/string/bcopy.c b/src/string/bcopy.c new file mode 100644 index 00000000..e76272fc --- /dev/null +++ b/src/string/bcopy.c @@ -0,0 +1,7 @@ +#include <string.h> +#include <strings.h> + +void bcopy(const void *s1, void *s2, size_t n) +{ + memmove(s2, s1, n); +} diff --git a/src/string/bzero.c b/src/string/bzero.c new file mode 100644 index 00000000..0f98b4a5 --- /dev/null +++ b/src/string/bzero.c @@ -0,0 +1,7 @@ +#include <string.h> +#include <strings.h> + +void bzero(void *s, size_t n) +{ + memset(s, 0, n); +} diff --git a/src/string/index.c b/src/string/index.c new file mode 100644 index 00000000..dd611251 --- /dev/null +++ b/src/string/index.c @@ -0,0 +1,7 @@ +#include <string.h> +#include <strings.h> + +char *index(const char *s, int c) +{ + return strchr(s, c); +} diff --git a/src/string/memchr.c b/src/string/memchr.c new file mode 100644 index 00000000..a0472f78 --- /dev/null +++ b/src/string/memchr.c @@ -0,0 +1,24 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +#define SS (sizeof(size_t)) +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +void *memchr(const void *src, int c, size_t n) +{ + const unsigned char *s = src; + c = (unsigned char)c; + for (; ((uintptr_t)s & ALIGN) && n && *s != c; s++, n--); + if (n && *s != c) { + const size_t *w; + size_t k = ONES * c; + for (w = (const void *)s; n>=SS && !HASZERO(*w^k); w++, n-=SS); + for (s = (const void *)w; n && *s != c; s++, n--); + } + return n ? (void *)s : 0; +} diff --git a/src/string/memcmp.c b/src/string/memcmp.c new file mode 100644 index 00000000..bdbce9f0 --- /dev/null +++ b/src/string/memcmp.c @@ -0,0 +1,8 @@ +#include <string.h> + +int memcmp(const void *vl, const void *vr, size_t n) +{ + const unsigned char *l=vl, *r=vr; + for (; n && *l == *r; n--, l++, r++); + return n ? *l-*r : 0; +} diff --git a/src/string/memcpy.c b/src/string/memcpy.c new file mode 100644 index 00000000..02cb4694 --- /dev/null +++ b/src/string/memcpy.c @@ -0,0 +1,29 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +#define SS (sizeof(size_t)) +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) + +void *memcpy(void *dest, const void *src, size_t n) +{ + unsigned char *d = dest; + const unsigned char *s = src; + + if (((uintptr_t)d & ALIGN) != ((uintptr_t)s & ALIGN)) + goto misaligned; + + for (; ((uintptr_t)d & ALIGN) && n; n--) *d++ = *s++; + if (n) { + size_t *wd = (void *)d; + const size_t *ws = (const void *)s; + + for (; n>=SS; n-=SS) *wd++ = *ws++; + d = (void *)wd; + s = (const void *)ws; +misaligned: + for (; n; n--) *d++ = *s++; + } + return dest; +} diff --git a/src/string/memmove.c b/src/string/memmove.c new file mode 100644 index 00000000..22bb4b35 --- /dev/null +++ b/src/string/memmove.c @@ -0,0 +1,14 @@ +#include <string.h> + +void *memmove(void *dest, const void *src, size_t n) +{ + char *d = dest; + const char *s = src; + if (d==s) return d; + if ((size_t)(d-s) < n) { + while (n--) d[n] = s[n]; + return dest; + } + /* Assumes memcpy is overlap-safe when dest < src */ + return memcpy(d, s, n); +} diff --git a/src/string/mempcpy.c b/src/string/mempcpy.c new file mode 100644 index 00000000..e54251cd --- /dev/null +++ b/src/string/mempcpy.c @@ -0,0 +1,7 @@ +#include <string.h> + +void *mempcpy(void *dest, void *src, size_t n) +{ + memcpy(dest, src, n); + return (char *)dest + n; +} diff --git a/src/string/memset.c b/src/string/memset.c new file mode 100644 index 00000000..20e47c45 --- /dev/null +++ b/src/string/memset.c @@ -0,0 +1,21 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +#define SS (sizeof(size_t)) +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) + +void *memset(void *dest, int c, size_t n) +{ + unsigned char *s = dest; + c = (unsigned char)c; + for (; ((uintptr_t)s & ALIGN) && n; n--) *s++ = c; + if (n) { + size_t *w, k = ONES * c; + for (w = (void *)s; n>=SS; n-=SS, w++) *w = k; + for (s = (void *)w; n; n--, s++) *s = c; + } + return dest; +} diff --git a/src/string/rindex.c b/src/string/rindex.c new file mode 100644 index 00000000..17df2bf2 --- /dev/null +++ b/src/string/rindex.c @@ -0,0 +1,7 @@ +#include <string.h> +#include <strings.h> + +char *rindex(const char *s, int c) +{ + return strrchr(s, c); +} diff --git a/src/string/stpcpy.c b/src/string/stpcpy.c new file mode 100644 index 00000000..10ca4933 --- /dev/null +++ b/src/string/stpcpy.c @@ -0,0 +1,29 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include "libc.h" + +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +char *__stpcpy(char *d, const char *s) +{ + size_t *wd; + const size_t *ws; + + if (((uintptr_t)s & ALIGN) == ((uintptr_t)d & ALIGN)) { + for (; (*d=*s) && ((uintptr_t)s & ALIGN); s++, d++); + if (!*s) return d; + wd=(void *)d; ws=(const void *)s; + for (; !HASZERO(*ws); *wd++ = *ws++); + d=(void *)wd; s=(const void *)ws; + } + for (; (*d=*s); s++, d++); + + return d; +} + +weak_alias(__stpcpy, stpcpy); diff --git a/src/string/stpncpy.c b/src/string/stpncpy.c new file mode 100644 index 00000000..a877f5fe --- /dev/null +++ b/src/string/stpncpy.c @@ -0,0 +1,32 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include "libc.h" + +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +char *__stpncpy(char *d, const char *s, size_t n) +{ + size_t *wd; + const size_t *ws; + + if (((uintptr_t)s & ALIGN) != ((uintptr_t)d & ALIGN)) { + for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++); + if (!n || !*s) goto tail; + wd=(void *)d; ws=(const void *)s; + for (; n>=sizeof(size_t) && !HASZERO(*ws); + n-=sizeof(size_t), ws++, *wd++) *wd = *ws; + d=(void *)wd; s=(const void *)ws; + } + for (; n && (*d=*s); n--, s++, d++); +tail: + memset(d, 0, n); + return d; +} + +weak_alias(__stpncpy, stpncpy); + diff --git a/src/string/strcasecmp.c b/src/string/strcasecmp.c new file mode 100644 index 00000000..dd879052 --- /dev/null +++ b/src/string/strcasecmp.c @@ -0,0 +1,9 @@ +#include <strings.h> +#include <ctype.h> + +int strcasecmp(const char *_l, const char *_r) +{ + const unsigned char *l=_l, *r=_r; + for (; *l && *r && (*l == *r || tolower(*l) == tolower(*r)); l++, r++); + return tolower(*l) - tolower(*r); +} diff --git a/src/string/strcasestr.c b/src/string/strcasestr.c new file mode 100644 index 00000000..f1cb0e84 --- /dev/null +++ b/src/string/strcasestr.c @@ -0,0 +1,7 @@ +#include <string.h> + +char *strcasestr(const char *h, const char *n) +{ + //FIXME! + return strstr(h, n); +} diff --git a/src/string/strcat.c b/src/string/strcat.c new file mode 100644 index 00000000..29fdb611 --- /dev/null +++ b/src/string/strcat.c @@ -0,0 +1,7 @@ +#include <string.h> + +char *strcat(char *dest, const char *src) +{ + strcpy(dest + strlen(dest), src); + return dest; +} diff --git a/src/string/strchr.c b/src/string/strchr.c new file mode 100644 index 00000000..e606f4fe --- /dev/null +++ b/src/string/strchr.c @@ -0,0 +1,23 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +char *strchr(const char *s, int c) +{ + c = (char)c; + if (!c) return (char *)s + strlen(s); + for (; ((uintptr_t)s & ALIGN) && *s && *s != c; s++); + if (*s && *s != c) { + const size_t *w; + size_t k = ONES * c; + for (w = (const void *)s; !HASZERO(*w) && !HASZERO(*w^k); w++); + for (s = (const void *)w; *s && *s != c; s++); + } + return *s ? (char *)s : 0; +} diff --git a/src/string/strchrnul.c b/src/string/strchrnul.c new file mode 100644 index 00000000..5e0c1a1a --- /dev/null +++ b/src/string/strchrnul.c @@ -0,0 +1,7 @@ +#include <string.h> + +char *strchrnul(const char *s, int c) +{ + char *p = strchr(s, c); + return p ? p : (char *)s + strlen(s); +} diff --git a/src/string/strcmp.c b/src/string/strcmp.c new file mode 100644 index 00000000..91eb7404 --- /dev/null +++ b/src/string/strcmp.c @@ -0,0 +1,7 @@ +#include <string.h> + +int strcmp(const char *l, const char *r) +{ + for (; *l==*r && *l && *r; l++, r++); + return *(unsigned char *)l - *(unsigned char *)r; +} diff --git a/src/string/strcpy.c b/src/string/strcpy.c new file mode 100644 index 00000000..7675e9ce --- /dev/null +++ b/src/string/strcpy.c @@ -0,0 +1,16 @@ +#include <string.h> + +char *__stpcpy(char *, const char *); + +char *strcpy(char *dest, const char *src) +{ +#if 1 + __stpcpy(dest, src); + return dest; +#else + const unsigned char *s = src; + unsigned char *d = dest; + while ((*d++ = *s++)); + return dest; +#endif +} diff --git a/src/string/strcspn.c b/src/string/strcspn.c new file mode 100644 index 00000000..439b7be4 --- /dev/null +++ b/src/string/strcspn.c @@ -0,0 +1,20 @@ +#include <string.h> + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +size_t strcspn(const char *_s, const char *_c) +{ + const unsigned char *s = _s; + const unsigned char *c = _c; + const unsigned char *a = s; + size_t byteset[32/sizeof(size_t)]; + + if (!c[0]) return strlen(s); + if (!c[1]) return (s=strchr(s, *c)) ? s-a : strlen(a); + + memset(byteset, 0, sizeof byteset); + for (; *c && BITOP(byteset, *c, |=); c++); + for (; *s && !BITOP(byteset, *s, &); s++); + return s-a; +} diff --git a/src/string/strdup.c b/src/string/strdup.c new file mode 100644 index 00000000..dd5f80c1 --- /dev/null +++ b/src/string/strdup.c @@ -0,0 +1,13 @@ +#include <stdlib.h> +#include <string.h> +#include "libc.h" + +char *__strdup(const char *s) +{ + size_t l = strlen(s); + char *d = malloc(l+1); + if (!d) return NULL; + return memcpy(d, s, l+1); +} + +weak_alias(__strdup, strdup); diff --git a/src/string/strerror_r.c b/src/string/strerror_r.c new file mode 100644 index 00000000..6fdd4ce2 --- /dev/null +++ b/src/string/strerror_r.c @@ -0,0 +1,11 @@ +#include <string.h> +#include <errno.h> + +int strerror_r(int err, char *buf, size_t buflen) +{ + char *msg = strerror(err); + if (strlen(msg) >= buflen) + return ERANGE; + strcpy(buf, msg); + return 0; +} diff --git a/src/string/strlcat.c b/src/string/strlcat.c new file mode 100644 index 00000000..a6b94c4c --- /dev/null +++ b/src/string/strlcat.c @@ -0,0 +1,8 @@ +#include <string.h> + +size_t strlcat(char *d, const char *s, size_t n) +{ + size_t l = strnlen(d, n); + if (l == n) return l + strlen(s); + return l + strlcpy(d+l, s, n-l); +} diff --git a/src/string/strlcpy.c b/src/string/strlcpy.c new file mode 100644 index 00000000..bbebf1db --- /dev/null +++ b/src/string/strlcpy.c @@ -0,0 +1,32 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> +#include "libc.h" + +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +size_t strlcpy(char *d, const char *s, size_t n) +{ + char *d0 = d; + size_t *wd; + const size_t *ws; + + if (!n--) goto finish; + if (((uintptr_t)s & ALIGN) != ((uintptr_t)d & ALIGN)) { + for (; ((uintptr_t)s & ALIGN) && n && (*d=*s); n--, s++, d++); + if (n && *s) { + wd=(void *)d; ws=(const void *)s; + for (; n>=sizeof(size_t) && !HASZERO(*ws); + n-=sizeof(size_t), ws++, *wd++) *wd = *ws; + d=(void *)wd; s=(const void *)ws; + } + } + for (; n && (*d=*s); n--, s++, d++); + *d = 0; +finish: + return d-d0 + strlen(s); +} diff --git a/src/string/strlen.c b/src/string/strlen.c new file mode 100644 index 00000000..936fb5cf --- /dev/null +++ b/src/string/strlen.c @@ -0,0 +1,21 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <limits.h> + +#define ALIGN (sizeof(size_t)-1) +#define ONES ((size_t)-1/UCHAR_MAX) +#define HIGHS (ONES * (UCHAR_MAX/2+1)) +#define HASZERO(x) ((x)-ONES & ~(x) & HIGHS) + +size_t strlen(const char *s) +{ + const char *a = s; + const size_t *w; + for (; ((uintptr_t)s & ALIGN) && *s; s++); + if (*s) { + for (w = (const void *)s; !HASZERO(*w); w++); + for (s = (const void *)w; *s; s++); + } + return s-a; +} diff --git a/src/string/strncasecmp.c b/src/string/strncasecmp.c new file mode 100644 index 00000000..4f9230e1 --- /dev/null +++ b/src/string/strncasecmp.c @@ -0,0 +1,10 @@ +#include <strings.h> +#include <ctype.h> + +int strncasecmp(const char *_l, const char *_r, size_t n) +{ + const unsigned char *l=_l, *r=_r; + if (!n--) return 0; + for (; *l && *r && n && (*l == *r || tolower(*l) == tolower(*r)); l++, r++, n--); + return tolower(*l) - tolower(*r); +} diff --git a/src/string/strncat.c b/src/string/strncat.c new file mode 100644 index 00000000..255b7a72 --- /dev/null +++ b/src/string/strncat.c @@ -0,0 +1,10 @@ +#include <string.h> + +char *strncat(char *d, const char *s, size_t n) +{ + char *a = d; + d += strlen(d); + while (n && (*d++ = *s++)) n--; + *d++ = 0; + return a; +} diff --git a/src/string/strncmp.c b/src/string/strncmp.c new file mode 100644 index 00000000..52ba0323 --- /dev/null +++ b/src/string/strncmp.c @@ -0,0 +1,9 @@ +#include <string.h> + +int strncmp(const char *_l, const char *_r, size_t n) +{ + const unsigned char *l=_l, *r=_r; + if (!n--) return 0; + for (; *l && *r && n && *l == *r ; l++, r++, n--); + return *l - *r; +} diff --git a/src/string/strncpy.c b/src/string/strncpy.c new file mode 100644 index 00000000..c0cd7974 --- /dev/null +++ b/src/string/strncpy.c @@ -0,0 +1,9 @@ +#include <string.h> + +char *__stpncpy(char *, const char *, size_t); + +char *strncpy(char *d, const char *s, size_t n) +{ + __stpncpy(d, s, n); + return d; +} diff --git a/src/string/strndup.c b/src/string/strndup.c new file mode 100644 index 00000000..617d27ba --- /dev/null +++ b/src/string/strndup.c @@ -0,0 +1,12 @@ +#include <stdlib.h> +#include <string.h> + +char *strndup(const char *s, size_t n) +{ + size_t l = strnlen(s, n); + char *d = malloc(l+1); + if (!d) return NULL; + memcpy(d, s, l); + d[l] = 0; + return d; +} diff --git a/src/string/strnlen.c b/src/string/strnlen.c new file mode 100644 index 00000000..6442eb79 --- /dev/null +++ b/src/string/strnlen.c @@ -0,0 +1,7 @@ +#include <string.h> + +size_t strnlen(const char *s, size_t n) +{ + const char *p = memchr(s, 0, n); + return p ? p-s : n; +} diff --git a/src/string/strpbrk.c b/src/string/strpbrk.c new file mode 100644 index 00000000..55947c64 --- /dev/null +++ b/src/string/strpbrk.c @@ -0,0 +1,7 @@ +#include <string.h> + +char *strpbrk(const char *s, const char *b) +{ + s += strcspn(s, b); + return *s ? (char *)s : 0; +} diff --git a/src/string/strrchr.c b/src/string/strrchr.c new file mode 100644 index 00000000..31c8e0b8 --- /dev/null +++ b/src/string/strrchr.c @@ -0,0 +1,9 @@ +#include <string.h> + +char *strrchr(const char *s, int c) +{ + const char *p; + c = (char)c; + for (p=s+strlen(s); p>=s && *p!=c; p--); + return p>=s ? (char *)p : 0; +} diff --git a/src/string/strsep.c b/src/string/strsep.c new file mode 100644 index 00000000..1bfe1db1 --- /dev/null +++ b/src/string/strsep.c @@ -0,0 +1,12 @@ +#include <string.h> + +char *strsep(char **str, const char *sep) +{ + char *s = *str, *end; + if (!s) return NULL; + end = s + strcspn(s, sep); + if (*end) *end++ = 0; + else end = 0; + *str = end; + return s; +} diff --git a/src/string/strsignal.c b/src/string/strsignal.c new file mode 100644 index 00000000..72fba8d1 --- /dev/null +++ b/src/string/strsignal.c @@ -0,0 +1,98 @@ +#include <signal.h> + +#if (SIGHUP == 1) && (SIGINT == 2) && (SIGQUIT == 3) && (SIGILL == 4) \ + && (SIGTRAP == 5) && (SIGABRT == 6) && (SIGBUS == 7) && (SIGFPE == 8) \ + && (SIGKILL == 9) && (SIGUSR1 == 10) && (SIGSEGV == 11) && (SIGUSR2 == 12) \ + && (SIGPIPE == 13) && (SIGALRM == 14) && (SIGTERM == 15) && (SIGSTKFLT == 16) \ + && (SIGCHLD == 17) && (SIGCONT == 18) && (SIGSTOP == 19) && (SIGTSTP == 20) \ + && (SIGTTIN == 21) && (SIGTTOU == 22) && (SIGURG == 23) && (SIGXCPU == 24) \ + && (SIGXFSZ == 25) && (SIGVTALRM == 26) && (SIGPROF == 27) && (SIGWINCH == 28) \ + && (SIGPOLL == 29) && (SIGPWR == 30) && (SIGSYS == 31) + +#define sigmap(x) x + +#else + +static const char map[] = { + [SIGHUP] = 1, + [SIGINT] = 2, + [SIGQUIT] = 3, + [SIGILL] = 4, + [SIGTRAP] = 5, + [SIGABRT] = 6, + [SIGBUS] = 7, + [SIGFPE] = 8, + [SIGKILL] = 9, + [SIGUSR1] = 10, + [SIGSEGV] = 11, + [SIGUSR2] = 12, + [SIGPIPE] = 13, + [SIGALRM] = 14, + [SIGTERM] = 15, + [SIGSTKFLT] = 16, + [SIGCHLD] = 17, + [SIGCONT] = 18, + [SIGSTOP] = 19, + [SIGTSTP] = 20, + [SIGTTIN] = 21, + [SIGTTOU] = 22, + [SIGURG] = 23, + [SIGXCPU] = 24, + [SIGXFSZ] = 25, + [SIGVTALRM] = 26, + [SIGPROF] = 27, + [SIGWINCH] = 28, + [SIGPOLL] = 29, + [SIGPWR] = 30, + [SIGSYS] = 31 +}; + +#define sigmap(x) ((unsigned)(x) > sizeof map ? 0 : map[(unsigned)(x)]) + +#endif + +static const char strings[] = + "Unknown signal\0" + "Hangup\0" + "Interrupt\0" + "Quit\0" + "Illegal instruction\0" + "Trace/breakpoint trap\0" + "Aborted\0" + "Bus error\0" + "Floating point exception\0" + "Killed\0" + "User defined signal 1\0" + "Segmentation fault\0" + "User defined signal 2\0" + "Broken pipe\0" + "Alarm clock\0" + "Terminated\0" + "Stack fault\0" + "Child exited\0" + "Continued\0" + "Stopped (signal)\0" + "Stopped\0" + "Stopped (tty input)\0" + "Stopped (tty output)\0" + "Urgent I/O condition\0" + "CPU time limit exceeded\0" + "File size limit exceeded\0" + "Virtual timer expired\0" + "Profiling timer expired\0" + "Window changed\0" + "I/O possible\0" + "Power failure\0" + "Bad system call"; + +char *strsignal(int signum) +{ + char *s = (char *)strings; + + signum = sigmap(signum); + if ((unsigned)signum - 1 > 31) signum = 0; + + for (; signum--; s++) for (; *s; s++); + + return s; +} diff --git a/src/string/strspn.c b/src/string/strspn.c new file mode 100644 index 00000000..59b063e5 --- /dev/null +++ b/src/string/strspn.c @@ -0,0 +1,22 @@ +#include <string.h> + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +size_t strspn(const char *_s, const char *_c) +{ + const unsigned char *s = _s; + const unsigned char *c = _c; + const unsigned char *a = s; + size_t byteset[32/sizeof(size_t)] = { 0 }; + + if (!c[0]) return 0; + if (!c[1]) { + for (; *s == *c; s++); + return s-a; + } + + for (; *c && BITOP(byteset, *c, |=); c++); + for (; *s && BITOP(byteset, *s, &); s++); + return s-a; +} diff --git a/src/string/strstr.c b/src/string/strstr.c new file mode 100644 index 00000000..4d536a73 --- /dev/null +++ b/src/string/strstr.c @@ -0,0 +1,166 @@ +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +static char *twobyte_strstr(const unsigned char *h, const unsigned char *n) +{ + uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1]; + for (h++; *h && hw != nw; hw = hw<<8 | *++h); + return *h ? (char *)h-1 : 0; +} + +static char *threebyte_strstr(const unsigned char *h, const unsigned char *n) +{ + uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8; + uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8; + for (h+=2; *h && hw != nw; hw = (hw|*++h)<<8); + return *h ? (char *)h-2 : 0; +} + +static char *fourbyte_strstr(const unsigned char *h, const unsigned char *n) +{ + uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3]; + uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3]; + for (h+=3; *h && hw != nw; hw = hw<<8 | *++h); + return *h ? (char *)h-3 : 0; +} + +#if 0 +static char *naive_strstr(const char *h, const char *n) +{ + size_t i; + for (i=0; n[i] && h[i]; i++) + for ( ; n[i] != h[i]; h++, i=0); + return n[i] ? 0 : (char *)h; +} +#endif + +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) + +#define BITOP(a,b,op) \ + ((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a)))) + +static char *twoway_strstr(const unsigned char *h, const unsigned char *n) +{ + const unsigned char *z; + size_t l, ip, jp, k, p, ms, p0, mem, mem0; + size_t byteset[32 / sizeof(size_t)] = { 0 }; + size_t shift[256]; + + /* Computing length of needle and fill shift table */ + for (l=0; n[l] && h[l]; l++) + BITOP(byteset, n[l], |=), shift[n[l]] = l+1; + if (n[l]) return 0; /* hit the end of h */ + + /* Compute maximal suffix */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] > n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + ms = ip; + p0 = p; + + /* And with the opposite comparison */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] < n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + if (ip+1 > ms+1) ms = ip; + else p = p0; + + /* Periodic needle? */ + if (memcmp(n, n+p, ms+1)) { + mem0 = 0; + p = MAX(ms, l-ms-1) + 1; + } else mem0 = l-p; + mem = 0; + + /* Initialize incremental end-of-haystack pointer */ + z = h; + + /* Search loop */ + for (;;) { + /* Update incremental end-of-haystack pointer */ + if (z-h < l) { + /* Fast estimate for MIN(l,63) */ + size_t grow = l | 63; + const char *z2 = memchr(z, 0, grow); + if (z2) { + z = z2; + if (z-h < l) return 0; + } else z += grow; + } + + /* Check last byte first; advance by shift on mismatch */ + if (BITOP(byteset, h[l-1], &)) { + k = l-shift[h[l-1]]; + //printf("adv by %zu (on %c) at [%s] (%zu;l=%zu)\n", k, h[l-1], h, shift[h[l-1]], l); + if (k) { + if (mem0 && mem && k < p) k = l-p; + h += k; + mem = 0; + continue; + } + } else { + h += l; + mem = 0; + continue; + } + + /* Compare right half */ + for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++); + if (n[k]) { + h += k-ms; + mem = 0; + continue; + } + /* Compare left half */ + for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--); + if (k == mem) return (char *)h; + h += p; + mem = mem0; + } +} + +char *strstr(const char *h, const char *n) +{ + /* Return immediately on empty needle */ + if (!n[0]) return (char *)h; + + /* Use faster algorithms for short needles */ + h = strchr(h, *n); + if (!h || !n[1]) return (char *)h; + if (!h[1]) return 0; + if (!n[2]) return twobyte_strstr(h, n); + if (!h[2]) return 0; + if (!n[3]) return threebyte_strstr(h, n); + if (!h[3]) return 0; + if (!n[4]) return fourbyte_strstr(h, n); + + return twoway_strstr(h, n); +} diff --git a/src/string/strtok.c b/src/string/strtok.c new file mode 100644 index 00000000..1ba221cb --- /dev/null +++ b/src/string/strtok.c @@ -0,0 +1,13 @@ +#include <string.h> + +char *strtok(char *s, const char *sep) +{ + static char *p; + if (!s && !(s = p)) return NULL; + s += strspn(s, sep); + if (!*s) return p = 0; + p = s + strcspn(s, sep); + if (*p) *p++ = 0; + else p = 0; + return s; +} diff --git a/src/string/strtok_r.c b/src/string/strtok_r.c new file mode 100644 index 00000000..c763897a --- /dev/null +++ b/src/string/strtok_r.c @@ -0,0 +1,12 @@ +#include <string.h> + +char *strtok_r(char *s, const char *sep, char **p) +{ + if (!s && !(s = *p)) return NULL; + s += strspn(s, sep); + if (!*s) return *p = 0; + *p = s + strcspn(s, sep); + if (**p) *(*p)++ = 0; + else *p = 0; + return s; +} diff --git a/src/string/swab.c b/src/string/swab.c new file mode 100644 index 00000000..b2132884 --- /dev/null +++ b/src/string/swab.c @@ -0,0 +1,13 @@ +#include <unistd.h> + +void swab(const void *_src, void *_dest, ssize_t n) +{ + const char *src = _src; + char *dest = _dest; + for (; n>0; n-=2) { + dest[0] = src[1]; + dest[1] = src[0]; + dest += 2; + src += 2; + } +} diff --git a/src/string/wcscat.c b/src/string/wcscat.c new file mode 100644 index 00000000..946f16e2 --- /dev/null +++ b/src/string/wcscat.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +wchar_t *wcscat(wchar_t *dest, const wchar_t *src) +{ + wcscpy(dest + wcslen(dest), src); + return dest; +} diff --git a/src/string/wcschr.c b/src/string/wcschr.c new file mode 100644 index 00000000..8dfc2f31 --- /dev/null +++ b/src/string/wcschr.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcschr(const wchar_t *s, wchar_t c) +{ + if (!c) return (wchar_t *)s + wcslen(s); + for (; *s && *s != c; s++); + return *s ? (wchar_t *)s : 0; +} diff --git a/src/string/wcscmp.c b/src/string/wcscmp.c new file mode 100644 index 00000000..26eeee70 --- /dev/null +++ b/src/string/wcscmp.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +int wcscmp(const wchar_t *l, const wchar_t *r) +{ + for (; *l==*r && *l && *r; l++, r++); + return *l - *r; +} diff --git a/src/string/wcscpy.c b/src/string/wcscpy.c new file mode 100644 index 00000000..e0ac194f --- /dev/null +++ b/src/string/wcscpy.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcscpy(wchar_t *d, const wchar_t *s) +{ + wchar_t *a = d; + while ((*d++ = *s++)); + return a; +} diff --git a/src/string/wcscspn.c b/src/string/wcscspn.c new file mode 100644 index 00000000..c4e52722 --- /dev/null +++ b/src/string/wcscspn.c @@ -0,0 +1,10 @@ +#include <wchar.h> + +size_t wcscspn(const wchar_t *s, const wchar_t *c) +{ + const wchar_t *a; + if (!c[0]) return wcslen(s); + if (!c[1]) return (s=wcschr(a=s, *c)) ? s-a : wcslen(a); + for (a=s; *s && !wcschr(c, *s); s++); + return s-a; +} diff --git a/src/string/wcslen.c b/src/string/wcslen.c new file mode 100644 index 00000000..1b7b6655 --- /dev/null +++ b/src/string/wcslen.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +size_t wcslen(const wchar_t *s) +{ + const wchar_t *a; + for (a=s; *s; s++); + return s-a; +} diff --git a/src/string/wcsncat.c b/src/string/wcsncat.c new file mode 100644 index 00000000..b07abe45 --- /dev/null +++ b/src/string/wcsncat.c @@ -0,0 +1,10 @@ +#include <wchar.h> + +wchar_t *wcsncat(wchar_t *d, const wchar_t *s, size_t n) +{ + wchar_t *a = d; + d += wcslen(d); + while (n && (*d++ = *s++)) n--; + *d++ = 0; + return a; +} diff --git a/src/string/wcsncmp.c b/src/string/wcsncmp.c new file mode 100644 index 00000000..1b159f41 --- /dev/null +++ b/src/string/wcsncmp.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +int wcsncmp(const wchar_t *l, const wchar_t *r, size_t n) +{ + for (; n && *l==*r && *l && *r; l++, r++); + return n ? *l - *r : 0; +} diff --git a/src/string/wcsncpy.c b/src/string/wcsncpy.c new file mode 100644 index 00000000..0164208d --- /dev/null +++ b/src/string/wcsncpy.c @@ -0,0 +1,9 @@ +#include <wchar.h> + +wchar_t *wcsncpy(wchar_t *d, const wchar_t *s, size_t n) +{ + wchar_t *a = d; + while (n && (*d++ = *s++)) n--; + wmemset(d, 0, n); + return a; +} diff --git a/src/string/wcspbrk.c b/src/string/wcspbrk.c new file mode 100644 index 00000000..0c72c197 --- /dev/null +++ b/src/string/wcspbrk.c @@ -0,0 +1,7 @@ +#include <wchar.h> + +wchar_t *wcspbrk(const wchar_t *s, const wchar_t *b) +{ + s += wcscspn(s, b); + return *s ? (wchar_t *)s : NULL; +} diff --git a/src/string/wcsrchr.c b/src/string/wcsrchr.c new file mode 100644 index 00000000..7503475a --- /dev/null +++ b/src/string/wcsrchr.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +wchar_t *wcsrchr(const wchar_t *s, wint_t c) +{ + const wchar_t *p; + for (p=s+wcslen(s); p>=s && *p!=c; p--); + return p>=s ? (wchar_t *)p : 0; +} diff --git a/src/string/wcsspn.c b/src/string/wcsspn.c new file mode 100644 index 00000000..4320d8f6 --- /dev/null +++ b/src/string/wcsspn.c @@ -0,0 +1,8 @@ +#include <wchar.h> + +size_t wcsspn(const wchar_t *s, const wchar_t *c) +{ + const wchar_t *a; + for (a=s; *s && wcschr(c, *s); s++); + return s-a; +} diff --git a/src/string/wcsstr.c b/src/string/wcsstr.c new file mode 100644 index 00000000..966174f8 --- /dev/null +++ b/src/string/wcsstr.c @@ -0,0 +1,117 @@ +#include <wchar.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> + +static wchar_t *naive_wcsstr(const wchar_t *h, const wchar_t *n) +{ + size_t i; + for (i=0; n[i] && h[i]; i++) + for ( ; n[i] != h[i]; h++, i=0); + return n[i] ? 0 : (wchar_t *)h; +} + +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define MIN(a,b) ((a)<(b)?(a):(b)) + +static wchar_t *twoway_wcsstr(const wchar_t *h, const wchar_t *n) +{ + const wchar_t *z; + size_t l, ip, jp, k, p, ms, p0, mem, mem0; + + /* Computing length of needle */ + for (l=0; n[l] && h[l]; l++); + if (n[l]) return 0; /* hit the end of h */ + + /* Compute maximal suffix */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] > n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + ms = ip; + p0 = p; + + /* And with the opposite comparison */ + ip = -1; jp = 0; k = p = 1; + while (jp+k<l) { + if (n[ip+k] == n[jp+k]) { + if (k == p) { + jp += p; + k = 1; + } else k++; + } else if (n[ip+k] < n[jp+k]) { + jp += k; + k = 1; + p = jp - ip; + } else { + ip = jp++; + k = p = 1; + } + } + if (ip+1 > ms+1) ms = ip; + else p = p0; + + /* Periodic needle? */ + if (wmemcmp(n, n+p, ms+1)) { + mem0 = 0; + p = MAX(ms, l-ms-1) + 1; + } else mem0 = l-p; + mem = 0; + + /* Initialize incremental end-of-haystack pointer */ + z = h; + + /* Search loop */ + for (;;) { + /* Update incremental end-of-haystack pointer */ + if (z-h < l) { + /* Fast estimate for MIN(l,63) */ + size_t grow = l | 63; + const wchar_t *z2 = wmemchr(z, 0, grow); + if (z2) { + z = z2; + if (z-h < l) return 0; + } else z += grow; + } + + /* Compare right half */ + for (k=MAX(ms+1,mem); n[k] && n[k] == h[k]; k++); + if (n[k]) { + h += k-ms; + mem = 0; + continue; + } + /* Compare left half */ + for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--); + if (k == mem) return (wchar_t *)h; + h += p; + mem = mem0; + } +} + +wchar_t *wcsstr(const wchar_t *h, const wchar_t *n) +{ + /* Return immediately on empty needle or haystack */ + if (!n[0]) return (wchar_t *)h; + if (!h[0]) return 0; + + /* Use faster algorithms for short needles */ + h = wcschr(h, *n); + if (!h || !n[1]) return (wchar_t *)h; + if (!h[1]) return 0; + if (!n[2] || !n[3] || !n[4]) return naive_wcsstr(h, n); + + return twoway_wcsstr(h, n); +} diff --git a/src/string/wcswcs.c b/src/string/wcswcs.c new file mode 100644 index 00000000..9cfe4ac4 --- /dev/null +++ b/src/string/wcswcs.c @@ -0,0 +1,6 @@ +#include <wchar.h> + +wchar_t *wcswcs(const wchar_t *haystack, const wchar_t *needle) +{ + return wcsstr(haystack, needle); +} diff --git a/src/string/wmemchr.c b/src/string/wmemchr.c new file mode 100644 index 00000000..a3ee0e61 --- /dev/null +++ b/src/string/wmemchr.c @@ -0,0 +1,8 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n) +{ + for (; n && *s != c; s++); + return n ? (wchar_t *)s : 0; +} diff --git a/src/string/wmemcmp.c b/src/string/wmemcmp.c new file mode 100644 index 00000000..6788a383 --- /dev/null +++ b/src/string/wmemcmp.c @@ -0,0 +1,8 @@ +#include <string.h> +#include <wchar.h> + +int wmemcmp(const wchar_t *l, const wchar_t *r, size_t n) +{ + for (; n && *l==*r; n--, l++, r++); + return n ? *l-*r : 0; +} diff --git a/src/string/wmemcpy.c b/src/string/wmemcpy.c new file mode 100644 index 00000000..330e37c7 --- /dev/null +++ b/src/string/wmemcpy.c @@ -0,0 +1,9 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemcpy(wchar_t *d, const wchar_t *s, size_t n) +{ + wchar_t *a = d; + while (n--) *d++ = *s++; + return a; +} diff --git a/src/string/wmemmove.c b/src/string/wmemmove.c new file mode 100644 index 00000000..49608cae --- /dev/null +++ b/src/string/wmemmove.c @@ -0,0 +1,11 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemmove(wchar_t *d, const wchar_t *s, size_t n) +{ + if ((size_t)(d-s) < n) { + while (n--) d[n] = s[n]; + return d; + } + return wmemcpy(d, s, n); +} diff --git a/src/string/wmemset.c b/src/string/wmemset.c new file mode 100644 index 00000000..1a2a8618 --- /dev/null +++ b/src/string/wmemset.c @@ -0,0 +1,9 @@ +#include <string.h> +#include <wchar.h> + +wchar_t *wmemset(wchar_t *d, wchar_t c, size_t n) +{ + wchar_t *ret = d; + while (n--) *d++ = c; + return ret; +} diff --git a/src/stub/utmpx.c b/src/stub/utmpx.c new file mode 100644 index 00000000..32003969 --- /dev/null +++ b/src/stub/utmpx.c @@ -0,0 +1,30 @@ +#include <utmpx.h> +#include <stddef.h> + +void endutxent(void) +{ +} + +void setutxent(void) +{ +} + +struct utmpx *getutxent(void) +{ + return NULL; +} + +struct utmpx *getutxid(const struct utmpx *ut) +{ + return NULL; +} + +struct utmpx *getutxline(const struct utmpx *ut) +{ + return NULL; +} + +struct utmpx *pututxline(const struct utmpx *ut) +{ + return NULL; +} diff --git a/src/temp/mkdtemp.c b/src/temp/mkdtemp.c new file mode 100644 index 00000000..f8b067ec --- /dev/null +++ b/src/temp/mkdtemp.c @@ -0,0 +1,21 @@ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <sys/stat.h> +#include "libc.h" + +char *mkdtemp(char *template) +{ + for (;;) { + if (!mktemp(template)) return 0; + if (!mkdir(template, 0700)) return template; + if (errno != EEXIST) return 0; + /* this is safe because mktemp verified + * that we have a valid template string */ + strcpy(template+strlen(template)-6, "XXXXXX"); + } +} diff --git a/src/temp/mkstemp.c b/src/temp/mkstemp.c new file mode 100644 index 00000000..b1567191 --- /dev/null +++ b/src/temp/mkstemp.c @@ -0,0 +1,26 @@ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include "libc.h" + +int mkstemp(char *template) +{ + int fd; +retry: + if (!mktemp(template)) return -1; + fd = open(template, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) return fd; + if (errno == EEXIST) { + /* this is safe because mktemp verified + * that we have a valid template string */ + strcpy(template+strlen(template)-6, "XXXXXX"); + goto retry; + } + return -1; +} + +LFS64(mkstemp); diff --git a/src/temp/mktemp.c b/src/temp/mktemp.c new file mode 100644 index 00000000..8638e087 --- /dev/null +++ b/src/temp/mktemp.c @@ -0,0 +1,29 @@ +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> +#include "libc.h" + +char *mktemp(char *template) +{ + static int lock; + static int index; + int l = strlen(template); + + if (l < 6 || strcmp(template+l-6, "XXXXXX")) { + errno = EINVAL; + return NULL; + } + LOCK(&lock); + for (; index < 1000000; index++) { + snprintf(template+l-6, 6, "%06d", index); + if (access(template, F_OK) != 0) { + UNLOCK(&lock); + return template; + } + } + UNLOCK(&lock); + return NULL; +} diff --git a/src/termios/cfgetospeed.c b/src/termios/cfgetospeed.c new file mode 100644 index 00000000..0ebc198c --- /dev/null +++ b/src/termios/cfgetospeed.c @@ -0,0 +1,12 @@ +#include <termios.h> +#include <sys/ioctl.h> + +speed_t cfgetospeed(const struct termios *tio) +{ + return tio->c_cflag & CBAUD; +} + +speed_t cfgetispeed(const struct termios *tio) +{ + return cfgetospeed(tio); +} diff --git a/src/termios/cfsetospeed.c b/src/termios/cfsetospeed.c new file mode 100644 index 00000000..80c790f1 --- /dev/null +++ b/src/termios/cfsetospeed.c @@ -0,0 +1,22 @@ +#include <termios.h> +#include <sys/ioctl.h> +#include <errno.h> +#include "libc.h" + +int cfsetospeed(struct termios *tio, speed_t speed) +{ + if (speed & ~CBAUD) { + errno = EINVAL; + return -1; + } + tio->c_cflag &= ~CBAUD; + tio->c_cflag |= speed; + return 0; +} + +int cfsetispeed(struct termios *tio, speed_t speed) +{ + return speed ? cfsetospeed(tio, speed) : 0; +} + +weak_alias(cfsetospeed, cfsetspeed); diff --git a/src/termios/tcdrain.c b/src/termios/tcdrain.c new file mode 100644 index 00000000..c51dd401 --- /dev/null +++ b/src/termios/tcdrain.c @@ -0,0 +1,7 @@ +#include <termios.h> +#include <sys/ioctl.h> + +int tcdrain(int fd) +{ + return ioctl(fd, TCSBRK, 1); +} diff --git a/src/termios/tcflow.c b/src/termios/tcflow.c new file mode 100644 index 00000000..c7fc3fe2 --- /dev/null +++ b/src/termios/tcflow.c @@ -0,0 +1,7 @@ +#include <termios.h> +#include <sys/ioctl.h> + +int tcflow(int fd, int action) +{ + return ioctl(fd, TCXONC, action); +} diff --git a/src/termios/tcflush.c b/src/termios/tcflush.c new file mode 100644 index 00000000..50222669 --- /dev/null +++ b/src/termios/tcflush.c @@ -0,0 +1,7 @@ +#include <termios.h> +#include <sys/ioctl.h> + +int tcflush(int fd, int queue) +{ + return ioctl(fd, TCFLSH, queue); +} diff --git a/src/termios/tcgetattr.c b/src/termios/tcgetattr.c new file mode 100644 index 00000000..d9ce786e --- /dev/null +++ b/src/termios/tcgetattr.c @@ -0,0 +1,10 @@ +#include <termios.h> +#include <sys/ioctl.h> +#include <string.h> + +int tcgetattr(int fd, struct termios *tio) +{ + if (ioctl(fd, TCGETS, tio)) + return -1; + return 0; +} diff --git a/src/termios/tcgetsid.c b/src/termios/tcgetsid.c new file mode 100644 index 00000000..1053fd64 --- /dev/null +++ b/src/termios/tcgetsid.c @@ -0,0 +1,10 @@ +#include <termios.h> +#include <sys/ioctl.h> + +pid_t tcgetsid(int fd) +{ + int sid; + if (ioctl(fd, TIOCGSID, &sid) < 0) + return -1; + return sid; +} diff --git a/src/termios/tcsendbreak.c b/src/termios/tcsendbreak.c new file mode 100644 index 00000000..b6df0a23 --- /dev/null +++ b/src/termios/tcsendbreak.c @@ -0,0 +1,8 @@ +#include <termios.h> +#include <sys/ioctl.h> + +int tcsendbreak(int fd, int dur) +{ + /* nonzero duration is implementation-defined, so ignore it */ + return ioctl(fd, TCSBRK, 0); +} diff --git a/src/termios/tcsetattr.c b/src/termios/tcsetattr.c new file mode 100644 index 00000000..e9a168f3 --- /dev/null +++ b/src/termios/tcsetattr.c @@ -0,0 +1,13 @@ +#include <termios.h> +#include <sys/ioctl.h> +#include <string.h> +#include <errno.h> + +int tcsetattr(int fd, int act, const struct termios *tio) +{ + if (act < 0 || act > 2) { + errno = EINVAL; + return -1; + } + return ioctl(fd, TCSETS+act, tio); +} diff --git a/src/thread/__futex.c b/src/thread/__futex.c new file mode 100644 index 00000000..93352fa3 --- /dev/null +++ b/src/thread/__futex.c @@ -0,0 +1,8 @@ +#include "futex.h" +#include "syscall.h" + +int __futex(volatile int *addr, int op, int val, void *ts) +{ + return syscall4(__NR_futex, (long)addr, op, val, (long)ts); +} + diff --git a/src/thread/__lock.c b/src/thread/__lock.c new file mode 100644 index 00000000..557c6a62 --- /dev/null +++ b/src/thread/__lock.c @@ -0,0 +1,12 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __lock(volatile int *l) +{ + int spins=100000; + /* Do not use futexes because we insist that unlocking is a simple + * assignment to optimize non-pathological code with no contention. */ + while (a_xchg(l, 1)) + if (spins) spins--, a_spin(); + else syscall0(__NR_sched_yield); +} diff --git a/src/thread/__set_thread_area.c b/src/thread/__set_thread_area.c new file mode 100644 index 00000000..576d8b40 --- /dev/null +++ b/src/thread/__set_thread_area.c @@ -0,0 +1,9 @@ +#include "syscall.h" + +int __set_thread_area(unsigned long *desc) +{ + if (syscall1(__NR_set_thread_area, (long)desc) < 0) + return -1; + __asm__ __volatile__ ( "movw %w0,%%gs" : : "r"(desc[0]*8+3) ); + return 0; +} diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c new file mode 100644 index 00000000..354def2c --- /dev/null +++ b/src/thread/__timedwait.c @@ -0,0 +1,21 @@ +#include <time.h> +#include <errno.h> +#include "futex.h" +#define SYSCALL_RETURN_ERRNO +#include "syscall.h" +#include <stdio.h> +int __timedwait(volatile int *addr, int val, clockid_t clk, const struct timespec *at, int priv) +{ + struct timespec to; + if (at) { + clock_gettime(clk, &to); + to.tv_sec = at->tv_sec - to.tv_sec; + if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) { + to.tv_sec--; + to.tv_nsec += 1000000000; + } + if (to.tv_sec < 0) return ETIMEDOUT; + } + if (priv) priv = 128; priv=0; + return syscall4(__NR_futex, (long)addr, FUTEX_WAIT | priv, val, at ? (long)&to : 0); +} diff --git a/src/thread/__unmapself.c b/src/thread/__unmapself.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/thread/__unmapself.c diff --git a/src/thread/__wait.c b/src/thread/__wait.c new file mode 100644 index 00000000..8c249cd3 --- /dev/null +++ b/src/thread/__wait.c @@ -0,0 +1,16 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __wait(volatile int *addr, volatile int *waiters, int val, int priv) +{ + int spins=50000; + if (priv) priv = 128; priv=0; + while (spins--) { + if (*addr==val) a_spin(); + else return; + } + if (waiters) a_inc(waiters); + while (*addr==val) + syscall4(__NR_futex, (long)addr, FUTEX_WAIT|priv, val, 0); + if (waiters) a_dec(waiters); +} diff --git a/src/thread/__wake.c b/src/thread/__wake.c new file mode 100644 index 00000000..048ddcc0 --- /dev/null +++ b/src/thread/__wake.c @@ -0,0 +1,9 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +void __wake(volatile int *addr, int cnt, int priv) +{ + if (priv) priv = 128; priv=0; + if (cnt<0) cnt = INT_MAX; + syscall3(__NR_futex, (long)addr, FUTEX_WAKE | priv, cnt); +} diff --git a/src/thread/cancellation.c b/src/thread/cancellation.c new file mode 100644 index 00000000..e35ba824 --- /dev/null +++ b/src/thread/cancellation.c @@ -0,0 +1,22 @@ +#include "pthread_impl.h" + +void __pthread_register_cancel(struct __ptcb *cb) +{ + struct pthread *self = pthread_self(); + cb->__next = self->cancelbuf; + self->cancelbuf = cb; +} + +#define pthread_self __pthread_self + +void __pthread_unregister_cancel(struct __ptcb *cb) +{ + struct pthread *self = pthread_self(); + self->cancelbuf = self->cancelbuf->__next; +} + +void __pthread_unwind_next(struct __ptcb *cb) +{ + if (cb->__next) longjmp((void *)cb->__next->__jb, 1); + pthread_exit(PTHREAD_CANCELLED); +} diff --git a/src/thread/clone.c b/src/thread/clone.c new file mode 100644 index 00000000..971bfeed --- /dev/null +++ b/src/thread/clone.c @@ -0,0 +1,26 @@ +#if 0 + +int clone(int (*start)(void *), void *stack, int flags, void *arg, + pid_t *ptid, struct user_desc *tls, pid_t *ctid) +{ + int ret; + __asm__( + "andl $-16,%%ecx \n\t" + "xchgl %%ebx,%2 \n\t" + "movl %%ebx,(%%ecx) \n\t" + "int $0x80 \n\t" + "testl %%eax,%%eax \n\t" + "jnz 1f \n\t" + "xorl %%ebp,%%ebp \n\t" + "call *%%ebx \n\t" + "\n1: \n\t" + "xchgl %%ebx,%2 \n\t" + : "=a" (ret) + : "a" (__NR_clone), "m" (flags), "c"(stack), "d"(ptid), + "S" (tls), "D" (ctid) + : "memory" + ); + return __syscall_ret(ret); +} + +#endif diff --git a/src/thread/i386/__unmapself.s b/src/thread/i386/__unmapself.s new file mode 100644 index 00000000..5c674966 --- /dev/null +++ b/src/thread/i386/__unmapself.s @@ -0,0 +1,22 @@ +.text +.global __unmapself +.type __unmapself,%function +__unmapself: + call 1f + .long -1 + .long -1 +1: popl %ecx + xorl %ebx,%ebx + xorl %edx,%edx + movl $8,%esi + movl $175,%eax + int $128 + movl $91,%eax + movl 4(%esp),%ebx + movl 8(%esp),%ecx + int $128 + xorl %ebx,%ebx + movl $1,%eax + int $128 + +.size __unmapself,.-__unmapself diff --git a/src/thread/i386/clone.s b/src/thread/i386/clone.s new file mode 100644 index 00000000..4f33366c --- /dev/null +++ b/src/thread/i386/clone.s @@ -0,0 +1,35 @@ +.text +.global __clone +.type __clone,%function +__clone: + movl 8(%esp),%ecx + andl $0xfffffff0, %ecx + subl $28,%ecx + movl 16(%esp),%eax + movl %eax,12(%ecx) + movl 4(%esp),%eax + movl %eax,8(%ecx) + pushl %ebx + pushl %esi + pushl %edi + movl $120,%eax + movl 12+12(%esp),%ebx + movl 20+12(%esp),%edx + movl 24+12(%esp),%esi + movl 28+12(%esp),%edi + int $128 + popl %edi + popl %esi + popl %ebx + test %eax,%eax + jnz 1f + xorl %ebp,%ebp + call *%ebx + movl %eax, %ebx + movl $1, %eax + int $128 +1: + movl %eax, 4(%esp) + ret + +.size __clone,.-__clone diff --git a/src/thread/pthread_attr_destroy.c b/src/thread/pthread_attr_destroy.c new file mode 100644 index 00000000..b5845dd0 --- /dev/null +++ b/src/thread/pthread_attr_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_destroy(pthread_attr_t *a) +{ + return 0; +} diff --git a/src/thread/pthread_attr_getdetachstate.c b/src/thread/pthread_attr_getdetachstate.c new file mode 100644 index 00000000..9cd49536 --- /dev/null +++ b/src/thread/pthread_attr_getdetachstate.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getdetachstate(pthread_attr_t *a, int *state) +{ + *state = a->__detach; + return 0; +} diff --git a/src/thread/pthread_attr_getguardsize.c b/src/thread/pthread_attr_getguardsize.c new file mode 100644 index 00000000..3f089c8c --- /dev/null +++ b/src/thread/pthread_attr_getguardsize.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getguardsize(pthread_attr_t *a, size_t *size) +{ + *size = a->__guardsize + DEFAULT_GUARD_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_getscope.c b/src/thread/pthread_attr_getscope.c new file mode 100644 index 00000000..0cb224d3 --- /dev/null +++ b/src/thread/pthread_attr_getscope.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_getscope(pthread_attr_t *a, int *scope) +{ + return 0; +} diff --git a/src/thread/pthread_attr_getstacksize.c b/src/thread/pthread_attr_getstacksize.c new file mode 100644 index 00000000..40bbf186 --- /dev/null +++ b/src/thread/pthread_attr_getstacksize.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_getstacksize(pthread_attr_t *a, size_t *size) +{ + *size = a->__stacksize + DEFAULT_STACK_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_init.c b/src/thread/pthread_attr_init.c new file mode 100644 index 00000000..d91bf157 --- /dev/null +++ b/src/thread/pthread_attr_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_init(pthread_attr_t *a) +{ + memset(a, 0, sizeof *a); + return 0; +} diff --git a/src/thread/pthread_attr_setdetachstate.c b/src/thread/pthread_attr_setdetachstate.c new file mode 100644 index 00000000..d23b4778 --- /dev/null +++ b/src/thread/pthread_attr_setdetachstate.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_attr_setdetachstate(pthread_attr_t *a, int state) +{ + a->__detach = state; + return 0; +} diff --git a/src/thread/pthread_attr_setguardsize.c b/src/thread/pthread_attr_setguardsize.c new file mode 100644 index 00000000..e0e8d3fb --- /dev/null +++ b/src/thread/pthread_attr_setguardsize.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_attr_setguardsize(pthread_attr_t *a, size_t size) +{ + if (size > SIZE_MAX/8) return EINVAL; + a->__guardsize = size - DEFAULT_GUARD_SIZE; + return 0; +} diff --git a/src/thread/pthread_attr_setscope.c b/src/thread/pthread_attr_setscope.c new file mode 100644 index 00000000..a970a819 --- /dev/null +++ b/src/thread/pthread_attr_setscope.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_attr_setscope(pthread_attr_t *a, int scope) +{ + return 0; +} diff --git a/src/thread/pthread_attr_setstacksize.c b/src/thread/pthread_attr_setstacksize.c new file mode 100644 index 00000000..58f4bb18 --- /dev/null +++ b/src/thread/pthread_attr_setstacksize.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_attr_setstacksize(pthread_attr_t *a, size_t size) +{ + if (size-PAGE_SIZE > SIZE_MAX/4) return EINVAL; + a->__stacksize = size - DEFAULT_STACK_SIZE; + return 0; +} diff --git a/src/thread/pthread_barrier_destroy.c b/src/thread/pthread_barrier_destroy.c new file mode 100644 index 00000000..2898c41a --- /dev/null +++ b/src/thread/pthread_barrier_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_barrier_destroy(pthread_barrier_t *b) +{ + return 0; +} diff --git a/src/thread/pthread_barrier_init.c b/src/thread/pthread_barrier_init.c new file mode 100644 index 00000000..2cc67ed5 --- /dev/null +++ b/src/thread/pthread_barrier_init.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *a, unsigned count) +{ + if (!count) return EINVAL; + *b = (pthread_barrier_t){ .__limit = count-1 }; + return 0; +} diff --git a/src/thread/pthread_barrier_wait.c b/src/thread/pthread_barrier_wait.c new file mode 100644 index 00000000..02c252ad --- /dev/null +++ b/src/thread/pthread_barrier_wait.c @@ -0,0 +1,31 @@ +#include "pthread_impl.h" + +int pthread_barrier_wait(pthread_barrier_t *b) +{ + int cur; + + /* Trivial case: count was set at 1 */ + if (!b->__limit) return PTHREAD_BARRIER_SERIAL_THREAD; + + /* Wait for anyone still suspended at previous use of barrier */ + while ((cur=b->__left)) + __wait(&b->__left, &b->__waiters, cur, 0); + + /* If we are the last to reach barrier, reset it and wake others */ + if (a_fetch_add(&b->__count, 1) == b->__limit) { + b->__left = b->__limit; + b->__count = 0; + __wake(&b->__count, -1, 0); + return PTHREAD_BARRIER_SERIAL_THREAD; + } + + /* Wait for our peers to reach the barrier */ + while ((cur=b->__count)) + __wait(&b->__count, 0, cur, 0); + + /* If we're the last to wake up and barrier is awaiting reuse */ + if (a_fetch_add(&b->__left, -1) == 1 && b->__waiters) + __wake(&b->__left, -1, 0); + + return 0; +} diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c new file mode 100644 index 00000000..9397ffe9 --- /dev/null +++ b/src/thread/pthread_cancel.c @@ -0,0 +1,7 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +int pthread_cancel(pthread_t t) +{ + return syscall3(__NR_tgkill, t->pid, t->tid, SIGCANCEL); +} diff --git a/src/thread/pthread_cond_broadcast.c b/src/thread/pthread_cond_broadcast.c new file mode 100644 index 00000000..7a023b85 --- /dev/null +++ b/src/thread/pthread_cond_broadcast.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_cond_broadcast(pthread_cond_t *c) +{ + c->__block = 0; + __wake(&c->__block, -1, 0); + return 0; +} diff --git a/src/thread/pthread_cond_destroy.c b/src/thread/pthread_cond_destroy.c new file mode 100644 index 00000000..1d21a5a8 --- /dev/null +++ b/src/thread/pthread_cond_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_destroy(pthread_cond_t *c) +{ + return 0; +} diff --git a/src/thread/pthread_cond_init.c b/src/thread/pthread_cond_init.c new file mode 100644 index 00000000..33948606 --- /dev/null +++ b/src/thread/pthread_cond_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_cond_init(pthread_cond_t *c, const pthread_condattr_t *a) +{ + memset(c, 0, sizeof *c); + return 0; +} diff --git a/src/thread/pthread_cond_signal.c b/src/thread/pthread_cond_signal.c new file mode 100644 index 00000000..0dd9416b --- /dev/null +++ b/src/thread/pthread_cond_signal.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_cond_signal(pthread_cond_t *c) +{ + c->__block = 0; + __wake(&c->__block, 1, 0); + return 0; +} diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c new file mode 100644 index 00000000..b67dded4 --- /dev/null +++ b/src/thread/pthread_cond_timedwait.c @@ -0,0 +1,26 @@ +#include "pthread_impl.h" + +static void relock(void *m) +{ + pthread_mutex_lock(m); +} + +int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts) +{ + int r, e=0; + CANCELPT(0); + + pthread_cleanup_push(relock, m); + c->__block = 1; + if ((r=pthread_mutex_unlock(m))) return r; + + CANCELPT(1); + e = __timedwait(&c->__block, 1, CLOCK_REALTIME, ts, 0); + CANCELPT(0); + + pthread_cleanup_pop(0); + if ((r=pthread_mutex_lock(m))) return r; + + CANCELPT(0); + return e; +} diff --git a/src/thread/pthread_cond_wait.c b/src/thread/pthread_cond_wait.c new file mode 100644 index 00000000..eb70e5f7 --- /dev/null +++ b/src/thread/pthread_cond_wait.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_cond_wait(pthread_cond_t *c, pthread_mutex_t *m) +{ + return pthread_cond_timedwait(c, m, 0); +} diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c new file mode 100644 index 00000000..6fa484c7 --- /dev/null +++ b/src/thread/pthread_create.c @@ -0,0 +1,189 @@ +#include "pthread_impl.h" + +#define pthread_self __pthread_self + +static void docancel(struct pthread *self) +{ + struct __ptcb cb = { .__next = self->cancelbuf }; + __pthread_unwind_next(&cb); +} + +static void cancel_handler(int sig, siginfo_t *si, void *ctx) +{ + struct pthread *self = pthread_self(); + self->cancel = 1; + if (self->canceldisable || (!self->cancelasync && !self->cancelpoint)) + return; + docancel(self); +} + +/* "rsyscall" is a mechanism by which a thread can synchronously force all + * other threads to perform an arbitrary syscall. It is necessary to work + * around the non-conformant implementation of setuid() et al on Linux, + * which affect only the calling thread and not the whole process. This + * implementation performs some tricks with signal delivery to work around + * the fact that it does not keep any list of threads in userspace. */ + +static struct { + volatile int lock, hold, blocks, cnt; + unsigned long arg[6]; + int nr; + int err; +} rs; + +static void rsyscall_handler(int sig, siginfo_t *si, void *ctx) +{ + if (rs.cnt == libc.threads_minus_1) return; + + if (syscall6(rs.nr, rs.arg[0], rs.arg[1], rs.arg[2], + rs.arg[3], rs.arg[4], rs.arg[5]) < 0 && !rs.err) rs.err=errno; + + a_inc(&rs.cnt); + __wake(&rs.cnt, 1, 1); + while(rs.hold) + __wait(&rs.hold, 0, 1, 1); + a_dec(&rs.cnt); + if (!rs.cnt) __wake(&rs.cnt, 1, 1); +} + +static int rsyscall(int nr, long a, long b, long c, long d, long e, long f) +{ + int i, ret; + sigset_t set = { 0 }; + struct pthread *self = pthread_self(); + sigaddset(&set, SIGSYSCALL); + + LOCK(&rs.lock); + while ((i=rs.blocks)) + __wait(&rs.blocks, 0, i, 1); + + __libc_sigprocmask(SIG_BLOCK, &set, 0); + + rs.nr = nr; + rs.arg[0] = a; rs.arg[1] = b; + rs.arg[2] = c; rs.arg[3] = d; + rs.arg[4] = d; rs.arg[5] = f; + rs.hold = 1; + rs.err = 0; + rs.cnt = 0; + + /* Dispatch signals until all threads respond */ + for (i=libc.threads_minus_1; i; i--) + sigqueue(self->pid, SIGSYSCALL, (union sigval){0}); + while ((i=rs.cnt) < libc.threads_minus_1) { + sigqueue(self->pid, SIGSYSCALL, (union sigval){0}); + __wait(&rs.cnt, 0, i, 1); + } + + /* Handle any lingering signals with no-op */ + __libc_sigprocmask(SIG_UNBLOCK, &set, 0); + + /* Resume other threads' signal handlers and wait for them */ + rs.hold = 0; + __wake(&rs.hold, -1, 0); + while((i=rs.cnt)) __wait(&rs.cnt, 0, i, 1); + + if (rs.err) errno = rs.err, ret = -1; + else ret = syscall6(nr, a, b, c, d, e, f); + + UNLOCK(&rs.lock); + return ret; +} + +static void cancelpt(int x) +{ + struct pthread *self = pthread_self(); + if (self->canceldisable) return; + self->cancelpoint = x; + if (self->cancel) docancel(self); +} + +static void init_threads() +{ + struct sigaction sa = { .sa_flags = SA_SIGINFO | SA_RESTART }; + libc.lock = __lock; + libc.cancelpt = cancelpt; + libc.rsyscall = rsyscall; + sa.sa_sigaction = cancel_handler; + __libc_sigaction(SIGCANCEL, &sa, 0); + sigaddset(&sa.sa_mask, SIGSYSCALL); + sigaddset(&sa.sa_mask, SIGCANCEL); + sa.sa_sigaction = rsyscall_handler; + __libc_sigaction(SIGSYSCALL, &sa, 0); + sigprocmask(SIG_UNBLOCK, &sa.sa_mask, 0); +} + +static int start(void *p) +{ + struct pthread *self = p; + pthread_exit(self->start(self->start_arg)); + return 0; +} + +#undef pthread_self + +#define CLONE_MAGIC 0x7d0f00 +int __clone(int (*)(void *), void *, int, void *, pid_t *, void *, pid_t *); + +#define ROUND(x) (((x)+PAGE_SIZE-1)&-PAGE_SIZE) + +/* pthread_key_create.c overrides this */ +static const size_t dummy = 0; +weak_alias(dummy, __pthread_tsd_size); + +int pthread_create(pthread_t *res, const pthread_attr_t *attr, void *(*entry)(void *), void *arg) +{ + static int init; + int ret; + size_t size, guard; + struct pthread *self = pthread_self(), *new; + unsigned char *map, *stack, *tsd; + static const pthread_attr_t default_attr; + + if (!self) return errno = ENOSYS; + if (!init && ++init) init_threads(); + + if (!attr) attr = &default_attr; + guard = ROUND(attr->__guardsize + DEFAULT_GUARD_SIZE); + size = guard + ROUND(attr->__stacksize + DEFAULT_STACK_SIZE); + size += __pthread_tsd_size; + map = mmap(0, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0); + if (!map) return EAGAIN; + mprotect(map, guard, PROT_NONE); + + tsd = map + size - __pthread_tsd_size; + new = (void *)(tsd - sizeof *new - PAGE_SIZE%sizeof *new); + new->map_base = map; + new->map_size = size; + new->pid = self->pid; + new->errno_ptr = &new->errno_val; + new->start = entry; + new->start_arg = arg; + new->self = new; + new->tsd = (void *)tsd; + new->detached = attr->__detach; + new->attr = *attr; + memcpy(new->tlsdesc, self->tlsdesc, sizeof new->tlsdesc); + new->tlsdesc[1] = (uintptr_t)new; + stack = (void *)((uintptr_t)new-1 & ~(uintptr_t)15); + + /* We must synchronize new thread creation with rsyscall + * delivery. This looks to be the least expensive way: */ + a_inc(&rs.blocks); + while (rs.lock) __wait(&rs.lock, 0, 1, 1); + + a_inc(&libc.threads_minus_1); + ret = __clone(start, stack, CLONE_MAGIC, new, + &new->tid, &new->tlsdesc, &new->tid); + + a_dec(&rs.blocks); + if (rs.lock) __wake(&rs.blocks, 1, 1); + + if (ret < 0) { + a_dec(&libc.threads_minus_1); + munmap(map, size); + return -ret; + } + *res = new; + return 0; +} diff --git a/src/thread/pthread_detach.c b/src/thread/pthread_detach.c new file mode 100644 index 00000000..f0eae3e8 --- /dev/null +++ b/src/thread/pthread_detach.c @@ -0,0 +1,11 @@ +#include "pthread_impl.h" + +int pthread_detach(pthread_t t) +{ + /* Cannot detach a thread that's already exiting */ + if (a_xchg(&t->exitlock, 1)) + return pthread_join(t, 0); + t->detached = 1; + t->exitlock = 0; + return 0; +} diff --git a/src/thread/pthread_equal.c b/src/thread/pthread_equal.c new file mode 100644 index 00000000..a55d280c --- /dev/null +++ b/src/thread/pthread_equal.c @@ -0,0 +1,6 @@ +#include <pthread.h> + +int pthread_equal(pthread_t a, pthread_t b) +{ + return a==b; +} diff --git a/src/thread/pthread_exit.c b/src/thread/pthread_exit.c new file mode 100644 index 00000000..4966e234 --- /dev/null +++ b/src/thread/pthread_exit.c @@ -0,0 +1,25 @@ +#include "pthread_impl.h" + +#undef pthread_self + +void pthread_exit(void *result) +{ + int i; + struct pthread *self = pthread_self(); + self->result = result; + + a_dec(&libc.threads_minus_1); + if (libc.threads_minus_1 < 0) + exit(0); + + LOCK(&self->exitlock); + + if (self->tsd_used) for (i=0; i<PTHREAD_KEYS_MAX; i++) + if (self->tsd[i] && libc.tsd_keys[i]) + libc.tsd_keys[i](self->tsd[i]); + + if (self->detached && self->map_base) + __unmapself(self->map_base, self->map_size); + + __syscall_exit(0); +} diff --git a/src/thread/pthread_getspecific.c b/src/thread/pthread_getspecific.c new file mode 100644 index 00000000..a6ca27d0 --- /dev/null +++ b/src/thread/pthread_getspecific.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +void *pthread_getspecific(pthread_key_t k) +{ + struct pthread *self = pthread_self(); + if (!self->tsd) return 0; + return self->tsd[k]; +} diff --git a/src/thread/pthread_join.c b/src/thread/pthread_join.c new file mode 100644 index 00000000..5210ed48 --- /dev/null +++ b/src/thread/pthread_join.c @@ -0,0 +1,12 @@ +#include "pthread_impl.h" + +int pthread_join(pthread_t t, void **res) +{ + int tmp = t->tid; + CANCELPT_BEGIN; + if (tmp) __wait(&t->tid, 0, tmp, 1); + CANCELPT_END; + if (res) *res = t->result; + if (t->map_base) munmap(t->map_base, t->map_size); + return 0; +} diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c new file mode 100644 index 00000000..efc38046 --- /dev/null +++ b/src/thread/pthread_key_create.c @@ -0,0 +1,25 @@ +#include "pthread_impl.h" + +const size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX; + +static void nodtor(void *dummy) +{ +} + +int pthread_key_create(pthread_key_t *k, void (*dtor)(void *)) +{ + static void (*keys[PTHREAD_KEYS_MAX])(void *); + int i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX; + int j = i; + + libc.tsd_keys = keys; + if (!dtor) dtor = nodtor; + /* Cheap trick - &k cannot match any destructor pointer */ + while (a_cas_p(keys+j, 0, &k) + && (j=(j+1)%PTHREAD_KEYS_MAX) != i); + if (keys[j] != (void (*)(void *))&k) + return EAGAIN; + keys[j] = dtor; + *k = j; + return 0; +} diff --git a/src/thread/pthread_key_delete.c b/src/thread/pthread_key_delete.c new file mode 100644 index 00000000..4914ebb4 --- /dev/null +++ b/src/thread/pthread_key_delete.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_key_delete(pthread_key_t k) +{ + if (libc.tsd_keys) libc.tsd_keys[k] = 0; + return 0; +} diff --git a/src/thread/pthread_kill.c b/src/thread/pthread_kill.c new file mode 100644 index 00000000..9d85fa5b --- /dev/null +++ b/src/thread/pthread_kill.c @@ -0,0 +1,7 @@ +#define SYSCALL_RETURN_ERRNO +#include "pthread_impl.h" + +int pthread_kill(pthread_t t, int sig) +{ + return syscall3(__NR_tgkill, t->pid, t->tid, sig); +} diff --git a/src/thread/pthread_mutex_destroy.c b/src/thread/pthread_mutex_destroy.c new file mode 100644 index 00000000..6d49e689 --- /dev/null +++ b/src/thread/pthread_mutex_destroy.c @@ -0,0 +1,6 @@ +#include <pthread.h> + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + return 0; +} diff --git a/src/thread/pthread_mutex_init.c b/src/thread/pthread_mutex_init.c new file mode 100644 index 00000000..d453543d --- /dev/null +++ b/src/thread/pthread_mutex_init.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_mutex_init(pthread_mutex_t *m, const pthread_mutexattr_t *a) +{ + memset(m, 0, sizeof *m); + if (a) { + } + return 0; +} diff --git a/src/thread/pthread_mutex_lock.c b/src/thread/pthread_mutex_lock.c new file mode 100644 index 00000000..6696f17a --- /dev/null +++ b/src/thread/pthread_mutex_lock.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_mutex_lock(pthread_mutex_t *m) +{ + int r; + while ((r=pthread_mutex_trylock(m)) == EBUSY) + __wait(&m->__lock, &m->__waiters, 1, 0); + return r; +} diff --git a/src/thread/pthread_mutex_timedlock.c b/src/thread/pthread_mutex_timedlock.c new file mode 100644 index 00000000..5dfad94f --- /dev/null +++ b/src/thread/pthread_mutex_timedlock.c @@ -0,0 +1,15 @@ +#include "pthread_impl.h" + +int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *at) +{ + int r, w=0; + while ((r=pthread_mutex_trylock(m)) == EBUSY) { + if (!w) a_inc(&m->__waiters), w++; + if (__timedwait(&m->__lock, 1, CLOCK_REALTIME, at, 0) == ETIMEDOUT) { + if (w) a_dec(&m->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&m->__waiters); + return r; +} diff --git a/src/thread/pthread_mutex_trylock.c b/src/thread/pthread_mutex_trylock.c new file mode 100644 index 00000000..1e3817bb --- /dev/null +++ b/src/thread/pthread_mutex_trylock.c @@ -0,0 +1,28 @@ +#include "pthread_impl.h" + +int pthread_mutex_trylock(pthread_mutex_t *m) +{ + if (m->__type == PTHREAD_MUTEX_RECURSIVE) { + pthread_t self = pthread_self(); + if (m->__owner == self) { + if ((unsigned)m->__lock >= INT_MAX) return EAGAIN; + a_inc(&m->__lock); + return 0; + } + if (a_fetch_add(&m->__lock, 1)) { + if (a_fetch_add(&m->__lock, -1)==1 && m->__waiters) + __wake(&m->__lock, 1, 0); + return EBUSY; + } + m->__owner = self; + return 0; + } + + if (a_xchg(&m->__lock, 1)) + if (m->__type == PTHREAD_MUTEX_ERRORCHECK + && m->__owner == pthread_self()) return EDEADLK; + else return EBUSY; + if (m->__type == PTHREAD_MUTEX_ERRORCHECK) + m->__owner = pthread_self(); + return 0; +} diff --git a/src/thread/pthread_mutex_unlock.c b/src/thread/pthread_mutex_unlock.c new file mode 100644 index 00000000..23e64ac8 --- /dev/null +++ b/src/thread/pthread_mutex_unlock.c @@ -0,0 +1,19 @@ +#include "pthread_impl.h" + +int pthread_mutex_unlock(pthread_mutex_t *m) +{ + if (m->__type == PTHREAD_MUTEX_RECURSIVE) { + if (a_fetch_add(&m->__lock, -1)==1 && m->__waiters) + __wake(&m->__lock, 1, 0); + return 0; + } + + if (m->__type == PTHREAD_MUTEX_ERRORCHECK + && m->__owner != pthread_self()) + return EPERM; + + m->__owner = 0; + m->__lock = 0; + if (m->__waiters) __wake(&m->__lock, 1, 0); + return 0; +} diff --git a/src/thread/pthread_mutexattr_destroy.c b/src/thread/pthread_mutexattr_destroy.c new file mode 100644 index 00000000..9fd69747 --- /dev/null +++ b/src/thread/pthread_mutexattr_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_destroy(pthread_mutexattr_t *a) +{ + return 0; +} diff --git a/src/thread/pthread_mutexattr_gettype.c b/src/thread/pthread_mutexattr_gettype.c new file mode 100644 index 00000000..9edb16c6 --- /dev/null +++ b/src/thread/pthread_mutexattr_gettype.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *a, int *type) +{ + *type = *a & 3; + return 0; +} diff --git a/src/thread/pthread_mutexattr_init.c b/src/thread/pthread_mutexattr_init.c new file mode 100644 index 00000000..ea631069 --- /dev/null +++ b/src/thread/pthread_mutexattr_init.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_init(pthread_mutexattr_t *a) +{ + memset(a, 0, sizeof *a); + return 0; +} diff --git a/src/thread/pthread_mutexattr_settype.c b/src/thread/pthread_mutexattr_settype.c new file mode 100644 index 00000000..4e85950e --- /dev/null +++ b/src/thread/pthread_mutexattr_settype.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_mutexattr_settype(pthread_mutexattr_t *a, int type) +{ + if ((unsigned)type > 2) return EINVAL; + *a = (*a & ~3) | type; + return 0; +} diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c new file mode 100644 index 00000000..72230054 --- /dev/null +++ b/src/thread/pthread_once.c @@ -0,0 +1,38 @@ +#include "pthread_impl.h" + +static void undo(void *control) +{ + a_store(control, 0); + __wake(control, 1, 0); +} + +int pthread_once(pthread_once_t *control, void (*init)(void)) +{ + static int waiters; + + /* Return immediately if init finished before */ + if (*control == 2) return 0; + + /* Try to enter initializing state. Three possibilities: + * 0 - we're the first or the other cancelled; run init + * 1 - another thread is running init; wait + * 2 - another thread finished running init; just return */ + + for (;;) switch (a_swap(control, 1)) { + case 0: + break; + case 1: + __wait(control, &waiters, 1, 0); + continue; + case 2: + a_store(control, 2); + return 0; + } + + pthread_cleanup_push(undo, control); + init(); + pthread_cleanup_pop(0); + + if (waiters) __wake(control, -1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_destroy.c b/src/thread/pthread_rwlock_destroy.c new file mode 100644 index 00000000..49ecfbd0 --- /dev/null +++ b/src/thread/pthread_rwlock_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_rwlock_destroy(pthread_rwlock_t *rw) +{ + return 0; +} diff --git a/src/thread/pthread_rwlock_init.c b/src/thread/pthread_rwlock_init.c new file mode 100644 index 00000000..f87d566c --- /dev/null +++ b/src/thread/pthread_rwlock_init.c @@ -0,0 +1,9 @@ +#include "pthread_impl.h" + +int pthread_rwlock_init(pthread_rwlock_t *rw, const pthread_rwlockattr_t *a) +{ + memset(rw, 0, sizeof *rw); + if (a) { + } + return 0; +} diff --git a/src/thread/pthread_rwlock_rdlock.c b/src/thread/pthread_rwlock_rdlock.c new file mode 100644 index 00000000..6bcdb815 --- /dev/null +++ b/src/thread/pthread_rwlock_rdlock.c @@ -0,0 +1,8 @@ +#include "pthread_impl.h" + +int pthread_rwlock_rdlock(pthread_rwlock_t *rw) +{ + while (pthread_rwlock_tryrdlock(rw)) + __wait(&rw->__wrlock, &rw->__waiters, 1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_timedrdlock.c b/src/thread/pthread_rwlock_timedrdlock.c new file mode 100644 index 00000000..290327de --- /dev/null +++ b/src/thread/pthread_rwlock_timedrdlock.c @@ -0,0 +1,15 @@ +#include "pthread_impl.h" + +int pthread_rwlock_timedrdlock(pthread_rwlock_t *rw, const struct timespec *at) +{ + int w=0; + while (pthread_rwlock_tryrdlock(rw)) { + if (!w) a_inc(&rw->__waiters), w++; + if (__timedwait(&rw->__wrlock, 1, CLOCK_REALTIME, at, 0)==ETIMEDOUT) { + if (w) a_dec(&rw->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&rw->__waiters); + return 0; +} diff --git a/src/thread/pthread_rwlock_timedwrlock.c b/src/thread/pthread_rwlock_timedwrlock.c new file mode 100644 index 00000000..9f749648 --- /dev/null +++ b/src/thread/pthread_rwlock_timedwrlock.c @@ -0,0 +1,17 @@ +#include "pthread_impl.h" + +int pthread_rwlock_timedwrlock(pthread_rwlock_t *rw, const struct timespec *at) +{ + int nr, *p, w=0; + while (pthread_rwlock_trywrlock(rw)==EAGAIN) { + if (!w) a_inc(&rw->__waiters), w++; + if ((nr=rw->__readers)) p = &rw->__readers; + else nr=1, p = &rw->__wrlock; + if (__timedwait(p, nr, CLOCK_REALTIME, at, 0)==ETIMEDOUT) { + if (w) a_dec(&rw->__waiters); + return ETIMEDOUT; + } + } + if (w) a_dec(&rw->__waiters); + return 0; +} diff --git a/src/thread/pthread_rwlock_tryrdlock.c b/src/thread/pthread_rwlock_tryrdlock.c new file mode 100644 index 00000000..f59fbbdd --- /dev/null +++ b/src/thread/pthread_rwlock_tryrdlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw) +{ + a_inc(&rw->__readers); + if (rw->__wrlock) { + a_dec(&rw->__readers); + if (rw->__waiters && !rw->__readers) + __wake(&rw->__readers, 1, 0); + return EAGAIN; + } + return 0; +} diff --git a/src/thread/pthread_rwlock_trywrlock.c b/src/thread/pthread_rwlock_trywrlock.c new file mode 100644 index 00000000..c029b874 --- /dev/null +++ b/src/thread/pthread_rwlock_trywrlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_trywrlock(pthread_rwlock_t *rw) +{ + if (a_xchg(&rw->__wrlock, 1)) + return EAGAIN; + if (rw->__readers) { + a_store(&rw->__wrlock, 0); + return EAGAIN; + } + rw->__owner = pthread_self()->tid; + return 0; +} diff --git a/src/thread/pthread_rwlock_unlock.c b/src/thread/pthread_rwlock_unlock.c new file mode 100644 index 00000000..f39117e7 --- /dev/null +++ b/src/thread/pthread_rwlock_unlock.c @@ -0,0 +1,17 @@ +#include "pthread_impl.h" + +int pthread_rwlock_unlock(pthread_rwlock_t *rw) +{ + struct pthread *self = pthread_self(); + if (rw->__owner == self->tid) { + rw->__owner = 0; + a_store(&rw->__wrlock, 0); + if (rw->__waiters) + __wake(&rw->__wrlock, -1, 0); + return 0; + } + a_dec(&rw->__readers); + if (rw->__waiters && !rw->__readers) + __wake(&rw->__readers, 1, 0); + return 0; +} diff --git a/src/thread/pthread_rwlock_wrlock.c b/src/thread/pthread_rwlock_wrlock.c new file mode 100644 index 00000000..219e924a --- /dev/null +++ b/src/thread/pthread_rwlock_wrlock.c @@ -0,0 +1,13 @@ +#include "pthread_impl.h" + +int pthread_rwlock_wrlock(pthread_rwlock_t *rw) +{ + int nr; + while (pthread_rwlock_trywrlock(rw)==EAGAIN) { + if ((nr=rw->__readers)) + __wait(&rw->__readers, &rw->__waiters, nr, 0); + else + __wait(&rw->__wrlock, &rw->__waiters, 1, 0); + } + return 0; +} diff --git a/src/thread/pthread_self.c b/src/thread/pthread_self.c new file mode 100644 index 00000000..686d73d5 --- /dev/null +++ b/src/thread/pthread_self.c @@ -0,0 +1,39 @@ +#include "pthread_impl.h" + +static struct pthread main_thread; + +#undef errno +static int *errno_location() +{ + return pthread_self()->errno_ptr; +} + +static int init_main_thread() +{ + main_thread.tlsdesc[0] = -1; + main_thread.tlsdesc[1] = (long)&main_thread; + main_thread.tlsdesc[2] = SIZE_MAX/PAGE_SIZE; + main_thread.tlsdesc[3] = 0x51; + main_thread.self = &main_thread; + main_thread.errno_ptr = __errno_location(); + if (__set_thread_area(main_thread.tlsdesc) < 0) + return -1; + syscall1(__NR_set_tid_address, (long)&main_thread.tid); + libc.errno_location = errno_location; + main_thread.tid = main_thread.pid = getpid(); + return 0; +} + +#undef pthread_self + +pthread_t pthread_self() +{ + static int init, failed; + if (!init) { + if (failed) return 0; + if (init_main_thread() < 0) failed = 1; + if (failed) return 0; + init = 1; + } + return __pthread_self(); +} diff --git a/src/thread/pthread_setcancelstate.c b/src/thread/pthread_setcancelstate.c new file mode 100644 index 00000000..23c38851 --- /dev/null +++ b/src/thread/pthread_setcancelstate.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int pthread_setcancelstate(int new, int *old) +{ + struct pthread *self = pthread_self(); + if (old) *old = self->canceldisable; + if ((unsigned)new > 1) return EINVAL; + self->canceldisable = new; + return 0; +} diff --git a/src/thread/pthread_setcanceltype.c b/src/thread/pthread_setcanceltype.c new file mode 100644 index 00000000..c73db22f --- /dev/null +++ b/src/thread/pthread_setcanceltype.c @@ -0,0 +1,10 @@ +#include "pthread_impl.h" + +int pthread_setcanceltype(int new, int *old) +{ + struct pthread *self = pthread_self(); + if (old) *old = self->cancelasync; + if ((unsigned)new > 1) return EINVAL; + self->cancelasync = new; + return 0; +} diff --git a/src/thread/pthread_setspecific.c b/src/thread/pthread_setspecific.c new file mode 100644 index 00000000..171cef41 --- /dev/null +++ b/src/thread/pthread_setspecific.c @@ -0,0 +1,18 @@ +#include "pthread_impl.h" + +int pthread_setspecific(pthread_key_t k, const void *x) +{ + struct pthread *self = pthread_self(); + /* Handle the case of the main thread */ + if (!self->tsd) { + if (!x) return 0; + if (!(self->tsd = calloc(sizeof(void *), PTHREAD_KEYS_MAX))) + return ENOMEM; + } + /* Avoid unnecessary COW */ + if (self->tsd[k] != x) { + self->tsd[k] = (void *)x; + self->tsd_used = 1; + } + return 0; +} diff --git a/src/thread/pthread_spin_destroy.c b/src/thread/pthread_spin_destroy.c new file mode 100644 index 00000000..e65a820c --- /dev/null +++ b/src/thread/pthread_spin_destroy.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_destroy(pthread_spinlock_t *s) +{ + return 0; +} diff --git a/src/thread/pthread_spin_init.c b/src/thread/pthread_spin_init.c new file mode 100644 index 00000000..681881cf --- /dev/null +++ b/src/thread/pthread_spin_init.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_init(pthread_spinlock_t *s, int shared) +{ + return *s = 0; +} diff --git a/src/thread/pthread_spin_lock.c b/src/thread/pthread_spin_lock.c new file mode 100644 index 00000000..59fa6ea8 --- /dev/null +++ b/src/thread/pthread_spin_lock.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +int pthread_spin_lock(pthread_spinlock_t *s) +{ + while (a_xchg(s, 1)); + return 0; +} diff --git a/src/thread/pthread_spin_trylock.c b/src/thread/pthread_spin_trylock.c new file mode 100644 index 00000000..c12696b3 --- /dev/null +++ b/src/thread/pthread_spin_trylock.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_trylock(pthread_spinlock_t *s) +{ + return -a_xchg(s, 1) & EBUSY; +} diff --git a/src/thread/pthread_spin_unlock.c b/src/thread/pthread_spin_unlock.c new file mode 100644 index 00000000..a7eab334 --- /dev/null +++ b/src/thread/pthread_spin_unlock.c @@ -0,0 +1,6 @@ +#include "pthread_impl.h" + +int pthread_spin_unlock(pthread_spinlock_t *s) +{ + return *s = 0; +} diff --git a/src/thread/pthread_testcancel.c b/src/thread/pthread_testcancel.c new file mode 100644 index 00000000..774b7068 --- /dev/null +++ b/src/thread/pthread_testcancel.c @@ -0,0 +1,7 @@ +#include "pthread_impl.h" + +void pthread_testcancel() +{ + CANCELPT_BEGIN; + CANCELPT_END; +} diff --git a/src/time/__asctime.c b/src/time/__asctime.c new file mode 100644 index 00000000..18535802 --- /dev/null +++ b/src/time/__asctime.c @@ -0,0 +1,27 @@ +#include <time.h> +#include <stdio.h> +#include <langinfo.h> + +const char *__langinfo(nl_item); + +char *__asctime(const struct tm *tm, char *buf) +{ + /* FIXME: change __langinfo to __C_langinfo once we have locales */ + if (snprintf(buf, 26, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + __langinfo(ABDAY_1+tm->tm_wday), + __langinfo(ABMON_1+tm->tm_mon), + tm->tm_mday, tm->tm_hour, + tm->tm_min, tm->tm_sec, + 1900 + tm->tm_year) >= 26) + { + /* ISO C requires us to use the above format string, + * even if it will not fit in the buffer. Thus asctime_r + * is _supposed_ to crash if the fields in tm are too large. + * We follow this behavior and crash "gracefully" to warn + * application developers that they may not be so lucky + * on other implementations (e.g. stack smashing..). + */ + *(int*)0 = 0; + } + return buf; +} diff --git a/src/time/__time.h b/src/time/__time.h new file mode 100644 index 00000000..967e5180 --- /dev/null +++ b/src/time/__time.h @@ -0,0 +1,9 @@ +time_t __tm_to_time(struct tm *); +struct tm *__time_to_tm(time_t, struct tm *); +void __tzset(void); +struct tm *__dst_adjust(struct tm *tm); + +extern long __timezone; +extern int __daylight; +extern int __dst_offset; +extern char *__tzname[2]; diff --git a/src/time/__time_to_tm.c b/src/time/__time_to_tm.c new file mode 100644 index 00000000..a1ebc452 --- /dev/null +++ b/src/time/__time_to_tm.c @@ -0,0 +1,81 @@ +#include <time.h> + +/* C defines the rounding for division in a nonsensical way */ +#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) + +#define DAYS_PER_400Y (365*400 + 97) +#define DAYS_PER_100Y (365*100 + 24) +#define DAYS_PER_4Y (365*4 + 1) + +/* FIXME: use lldiv once it's fixed to compute quot,rem together */ +struct tm *__time_to_tm(time_t t, struct tm *tm) +{ + /* months are march-based */ + static const int days_thru_month[] = {31,61,92,122,153,184,214,245,275,306,337,366}; + long long bigday; + unsigned int day, year4, year100; + int year, year400; + int month; + int leap; + int hour, min, sec; + int wday, mday, yday; + + /* start from 2000-03-01 (multiple of 400 years) */ + t += -946684800 - 86400*(31+29); + + bigday = Q(t, 86400); + sec = t-bigday*86400; + + hour = sec/3600; + sec -= hour*3600; + min = sec/60; + sec -= min*60; + + /* 2000-03-01 was a wednesday */ + wday = (3+bigday)%7; + if (wday < 0) wday += 7; + + t = -946684800LL - 86400*(31+29) + 9000000; + + year400 = Q(bigday, DAYS_PER_400Y); + day = bigday-year400*DAYS_PER_400Y; + + year100 = day/DAYS_PER_100Y; + if (year100 == 4) year100--; + day -= year100*DAYS_PER_100Y; + + year4 = day/DAYS_PER_4Y; + if (year4 == 25) year4--; + day -= year4*DAYS_PER_4Y; + + year = day/365; + if (year == 4) year--; + day -= year*365; + + leap = !year && (year4 || !year100); + yday = day + 31+28 + leap; + if (yday >= 365+leap) yday -= 365+leap; + + year += 4*year4 + 100*year100 + 400*year400 + 2000-1900; + + for (month=0; days_thru_month[month] <= day; month++); + if (month) day -= days_thru_month[month-1]; + month += 2; + if (month >= 12) { + month -= 12; + year++; + } + + mday = day+1; + + tm->tm_sec = sec; + tm->tm_min = min; + tm->tm_hour= hour; + tm->tm_mday= mday; + tm->tm_mon = month; + tm->tm_year= year; + tm->tm_wday= wday; + tm->tm_yday= yday; + + return tm; +} diff --git a/src/time/__tm_to_time.c b/src/time/__tm_to_time.c new file mode 100644 index 00000000..3fa15fad --- /dev/null +++ b/src/time/__tm_to_time.c @@ -0,0 +1,33 @@ +#include <time.h> + +/* C defines the rounding for division in a nonsensical way */ +#define Q(a,b) ((a)>0 ? (a)/(b) : -(((b)-(a)-1)/(b))) + +time_t __tm_to_time(struct tm *tm) +{ + time_t year = tm->tm_year + -100; + int month = tm->tm_mon; + int day = tm->tm_mday; + int z4, z100, z400; + + /* normalize month */ + if (month >= 12) { + year += month/12; + month %= 12; + } else if (month < 0) { + year += month/12; + month %= 12; + if (month) { + month += 12; + year--; + } + } + z4 = Q(year - (month < 2), 4); + z100 = Q(z4, 25); + z400 = Q(z100, 4); + day += year*365 + z4 - z100 + z400 + + month[(int []){0,31,59,90,120,151,181,212,243,273,304,335}]; + return (long long)day*86400 + + tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec + - -946684800; /* the dawn of time :) */ +} diff --git a/src/time/asctime.c b/src/time/asctime.c new file mode 100644 index 00000000..3102eb87 --- /dev/null +++ b/src/time/asctime.c @@ -0,0 +1,9 @@ +#include <time.h> + +char *__asctime(const struct tm *, char *); + +char *asctime(const struct tm *tm) +{ + static char buf[26]; + return __asctime(tm, buf); +} diff --git a/src/time/asctime_r.c b/src/time/asctime_r.c new file mode 100644 index 00000000..e51b8804 --- /dev/null +++ b/src/time/asctime_r.c @@ -0,0 +1,8 @@ +#include <time.h> + +char *__asctime(const struct tm *, char *); + +char *asctime_r(const struct tm *tm, char *buf) +{ + return __asctime(tm, buf); +} diff --git a/src/time/clock.c b/src/time/clock.c new file mode 100644 index 00000000..2feddb36 --- /dev/null +++ b/src/time/clock.c @@ -0,0 +1,9 @@ +#include <time.h> +#include <sys/times.h> + +/* this function assumes 100 hz linux and corrects for it */ +clock_t clock() +{ + struct tms tms; + return (unsigned long)times(&tms)*10000; +} diff --git a/src/time/clock_gettime.c b/src/time/clock_gettime.c new file mode 100644 index 00000000..dab09d50 --- /dev/null +++ b/src/time/clock_gettime.c @@ -0,0 +1,7 @@ +#include <time.h> +#include "syscall.h" + +int clock_gettime(clockid_t clk, struct timespec *ts) +{ + return syscall2(__NR_clock_gettime, clk, (long)ts); +} diff --git a/src/time/ctime.c b/src/time/ctime.c new file mode 100644 index 00000000..185ec554 --- /dev/null +++ b/src/time/ctime.c @@ -0,0 +1,6 @@ +#include <time.h> + +char *ctime(const time_t *t) +{ + return asctime(localtime(t)); +} diff --git a/src/time/ctime_r.c b/src/time/ctime_r.c new file mode 100644 index 00000000..d2260a16 --- /dev/null +++ b/src/time/ctime_r.c @@ -0,0 +1,8 @@ +#include <time.h> + +char *ctime_r(const time_t *t, char *buf) +{ + struct tm tm; + localtime_r(t, &tm); + return asctime_r(&tm, buf); +} diff --git a/src/time/difftime.c b/src/time/difftime.c new file mode 100644 index 00000000..80a18cc0 --- /dev/null +++ b/src/time/difftime.c @@ -0,0 +1,6 @@ +#include <time.h> + +double difftime(time_t t1, time_t t0) +{ + return t1-t0; +} diff --git a/src/time/gettimeofday.c b/src/time/gettimeofday.c new file mode 100644 index 00000000..2b8a287d --- /dev/null +++ b/src/time/gettimeofday.c @@ -0,0 +1,9 @@ +#define SYSCALL_RETURN_ERRNO +#include <sys/time.h> +#include "syscall.h" + +int gettimeofday(struct timeval *tv, void *tz) +{ + syscall2(__NR_gettimeofday, (long)tv, 0); + return 0; +} diff --git a/src/time/gmtime.c b/src/time/gmtime.c new file mode 100644 index 00000000..d4d5d1f1 --- /dev/null +++ b/src/time/gmtime.c @@ -0,0 +1,11 @@ +#include <time.h> + +#include "__time.h" + +struct tm *gmtime(const time_t *t) +{ + static struct tm tm; + __time_to_tm(*t, &tm); + tm.tm_isdst = 0; + return &tm; +} diff --git a/src/time/gmtime_r.c b/src/time/gmtime_r.c new file mode 100644 index 00000000..5b565a65 --- /dev/null +++ b/src/time/gmtime_r.c @@ -0,0 +1,10 @@ +#include <time.h> + +#include "__time.h" + +struct tm *gmtime_r(const time_t *t, struct tm *result) +{ + __time_to_tm(*t, result); + result->tm_isdst = 0; + return result; +} diff --git a/src/time/localtime.c b/src/time/localtime.c new file mode 100644 index 00000000..abd5e84d --- /dev/null +++ b/src/time/localtime.c @@ -0,0 +1,12 @@ +#include <time.h> + +#include "__time.h" + +struct tm *localtime(const time_t *t) +{ + static struct tm tm; + __tzset(); + __time_to_tm(*t - __timezone, &tm); + tm.tm_isdst = -1; + return __dst_adjust(&tm); +} diff --git a/src/time/localtime_r.c b/src/time/localtime_r.c new file mode 100644 index 00000000..2bf10378 --- /dev/null +++ b/src/time/localtime_r.c @@ -0,0 +1,11 @@ +#include <time.h> + +#include "__time.h" + +struct tm *localtime_r(const time_t *t, struct tm *result) +{ + __tzset(); + __time_to_tm(*t - __timezone, result); + result->tm_isdst = -1; + return __dst_adjust(result); +} diff --git a/src/time/mktime.c b/src/time/mktime.c new file mode 100644 index 00000000..858cd50d --- /dev/null +++ b/src/time/mktime.c @@ -0,0 +1,24 @@ +#include <time.h> + +#include "__time.h" + +time_t mktime(struct tm *tm) +{ + int isdst = tm->tm_isdst; + time_t t, lt; + + __tzset(); + + tm->tm_sec += __timezone; + if (isdst > 0) tm->tm_sec += __dst_offset; + + t = __tm_to_time(tm); + + lt = t - __timezone; + if (isdst > 0) lt -= __dst_offset; + __time_to_tm(lt, tm); + + __dst_adjust(tm); + + return t; +} diff --git a/src/time/nanosleep.c b/src/time/nanosleep.c new file mode 100644 index 00000000..5ac4c359 --- /dev/null +++ b/src/time/nanosleep.c @@ -0,0 +1,13 @@ +#include <unistd.h> +#include <time.h> +#include "syscall.h" +#include "libc.h" + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + int ret; + CANCELPT_BEGIN; + ret = syscall2(__NR_nanosleep, (long)req, (long)rem); + CANCELPT_END; + return ret; +} diff --git a/src/time/strftime.c b/src/time/strftime.c new file mode 100644 index 00000000..f1b94631 --- /dev/null +++ b/src/time/strftime.c @@ -0,0 +1,172 @@ +#include <stdio.h> +#include <stdlib.h> +#include <langinfo.h> +#include <time.h> +#include "__time.h" + +// FIXME: integer overflows + +const char *__langinfo(nl_item); + +size_t strftime(char *s, size_t n, const char *f, const struct tm *tm) +{ + nl_item item; + int val; + const char *fmt; + size_t l; + for (l=0; *f && l<n; f++) { + if (*f == '%') { +do_fmt: + switch (*++f) { + case '%': + goto literal; + case 'E': + case 'O': + goto do_fmt; + case 'a': + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900+tm->tm_year) / 100; + fmt = "%02d"; + goto number; + case 'd': + val = tm->tm_mday; + fmt = "%02d"; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'e': + val = tm->tm_mday; + fmt = "%2d"; + goto number; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + // FIXME + val = 0; //week_based_year(tm)%100; + fmt = "%02d"; + goto number; + case 'G': + // FIXME + val = 0; //week_based_year(tm); + fmt = "%04d"; + goto number; + case 'H': + val = tm->tm_hour; + fmt = "%02d"; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + fmt = "%02d"; + goto number; + case 'j': + val = tm->tm_yday+1; + fmt = "%03d"; + goto number; + case 'm': + val = tm->tm_mon+1; + fmt = "%02d"; + goto number; + case 'M': + val = tm->tm_min; + fmt = "%02d"; + goto number; + case 'n': + s[l++] = '\n'; + continue; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 'S': + val = tm->tm_sec; + fmt = "%02d"; + goto number; + case 't': + s[l++] = '\t'; + continue; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + fmt = "%d"; + goto number; + case 'U': + case 'V': + case 'W': + // FIXME: week number mess.. + continue; + case 'w': + val = tm->tm_wday; + fmt = "%d"; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = tm->tm_year % 100; + fmt = "%02d"; + goto number; + case 'Y': + val = tm->tm_year + 1900; + fmt = "%04d"; + goto number; + case 'z': + if (tm->tm_isdst < 0) continue; + val = -__timezone - (tm->tm_isdst ? __dst_offset : 0); + l += snprintf(s+l, n-l, "%+.2d%.2d", val/3600, abs(val%3600)/60); + continue; + case 'Z': + if (tm->tm_isdst < 0 || !__tzname[0] || !__tzname[0][0]) + continue; + l += snprintf(s+l, n-l, "%s", __tzname[!!tm->tm_isdst]); + continue; + default: + return 0; + } + } +literal: + s[l++] = *f; + continue; +number: + l += snprintf(s+l, n-l, fmt, val); + continue; +nl_strcat: + l += snprintf(s+l, n-l, "%s", __langinfo(item)); + continue; +nl_strftime: + fmt = __langinfo(item); +recu_strftime: + l += strftime(s+l, n-l, fmt, tm); + } + if (l >= n) return 0; + s[l] = 0; + return l; +} diff --git a/src/time/strptime.c b/src/time/strptime.c new file mode 100644 index 00000000..db72e610 --- /dev/null +++ b/src/time/strptime.c @@ -0,0 +1,178 @@ +#include <stdio.h> +#include <stdlib.h> +#include <langinfo.h> +#include <time.h> + +const char *__langinfo(nl_item); + +char *strptime(const char *s, const char *f, struct tm *tm) +{ + return NULL; +} + +#if 0 + +char *strptime(const char *s, const char *f, struct tm *tm) +{ + nl_item item; + int *dest; + const char *fmt; + for (; *f; f++) { + if (isspace(*f)) goto whitespace; + if (*f == '%') { +do_fmt: + switch (*++f) { + case '%': + goto literal; + case 'E': + case 'O': + goto do_fmt; + case 'a': + item = ABDAY_1 + tm->tm_wday; + goto nl_strcat; + case 'A': + item = DAY_1 + tm->tm_wday; + goto nl_strcat; + case 'h': + case 'b': + item = ABMON_1 + tm->tm_mon; + goto nl_strcat; + case 'B': + item = MON_1 + tm->tm_mon; + goto nl_strcat; + case 'c': + item = D_T_FMT; + goto nl_strftime; + case 'C': + val = (1900+tm->tm_year) / 100; + fmt = "%02d"; + goto number; + case 'd': + val = tm->tm_mday; + fmt = "%02d"; + goto number; + case 'D': + fmt = "%m/%d/%y"; + goto recu_strftime; + case 'e': + val = tm->tm_mday; + fmt = "%2d"; + goto number; + case 'F': + fmt = "%Y-%m-%d"; + goto recu_strftime; + case 'g': + // FIXME + val = 0; //week_based_year(tm)%100; + fmt = "%02d"; + goto number; + case 'G': + // FIXME + val = 0; //week_based_year(tm); + fmt = "%04d"; + goto number; + case 'H': + val = tm->tm_hour; + fmt = "%02d"; + goto number; + case 'I': + val = tm->tm_hour; + if (!val) val = 12; + else if (val > 12) val -= 12; + fmt = "%02d"; + goto number; + case 'j': + val = tm->tm_yday+1; + fmt = "%03d"; + goto number; + case 'm': + val = tm->tm_mon+1; + fmt = "%02d"; + goto number; + case 'M': + val = tm->tm_min; + fmt = "%02d"; + goto number; + case 'n': + case 't': + goto whitespace; + case 'p': + item = tm->tm_hour >= 12 ? PM_STR : AM_STR; + goto nl_strcat; + case 'r': + item = T_FMT_AMPM; + goto nl_strftime; + case 'R': + fmt = "%H:%M"; + goto recu_strftime; + case 'S': + val = tm->tm_sec; + fmt = "%02d"; + goto number; + case 'T': + fmt = "%H:%M:%S"; + goto recu_strftime; + case 'u': + val = tm->tm_wday ? tm->tm_wday : 7; + fmt = "%d"; + goto number; + case 'U': + case 'V': + case 'W': + // FIXME: week number mess.. + continue; + case 'w': + val = tm->tm_wday; + fmt = "%d"; + goto number; + case 'x': + item = D_FMT; + goto nl_strftime; + case 'X': + item = T_FMT; + goto nl_strftime; + case 'y': + val = tm->tm_year % 100; + fmt = "%02d"; + goto number; + case 'Y': + val = tm->tm_year + 1900; + fmt = "%04d"; + goto number; + case 'z': + if (tm->tm_isdst < 0) continue; + val = timezone + (tm->tm_isdst) ? __dst_offset : 0; + l += snprintf(s+l, n-l, "%+02d%02d", val/60, abs(val%60)); + continue; + case 'Z': + if (tm->tm_isdst < 0 || !tzname[0] || !tzname[0][0]) + continue; + l += snprintf(s+l, n-l, "%s", tzname[!!tm->tm_isdst]); + continue; + } + default: + return NULL; + } +literal: + if (*s++ != *f) return NULL; + continue; +whitespace: + while(isspace(*s)) s++; + continue; +number: + l += snprintf(s+l, n-l, fmt, val); + continue; +nl_strcat: + l += snprintf(s+l, n-l, "%s", __langinfo(item)); + continue; +nl_strftime: + fmt = __langinfo(item); +recu_strftime: + l += strftime(s+l, n-l, fmt, tm); + } + if (l >= n) return 0; + s[l] = 0; + return l; +} + +#endif diff --git a/src/time/time.c b/src/time/time.c new file mode 100644 index 00000000..3457dade --- /dev/null +++ b/src/time/time.c @@ -0,0 +1,12 @@ +#define SYSCALL_RETURN_ERRNO +#include <time.h> +#include <sys/time.h> +#include "syscall.h" + +time_t time(time_t *t) +{ + struct timeval tv; + syscall2(__NR_gettimeofday, (long)&tv, 0); + if (t) *t = tv.tv_sec; + return tv.tv_sec; +} diff --git a/src/time/times.c b/src/time/times.c new file mode 100644 index 00000000..e9b5a823 --- /dev/null +++ b/src/time/times.c @@ -0,0 +1,7 @@ +#include <sys/times.h> +#include "syscall.h" + +clock_t times(struct tms *tms) +{ + return syscall1(__NR_times, (long)&tms); +} diff --git a/src/time/timezone.s b/src/time/timezone.s new file mode 100644 index 00000000..4e167b0b --- /dev/null +++ b/src/time/timezone.s @@ -0,0 +1,27 @@ +.data + +.global timezone +.global __timezone +.global daylight +.global __daylight +.global tzname +.global __tzname + +__timezone: +timezone: + .long 0 +.size timezone,.-timezone +.size __timezone,.-__timezone + +__daylight: +daylight: + .long 0 +.size daylight,.-daylight +.size __daylight,.-__daylight + +__tzname: +tzname: + .long 0 + .long 0 +.size tzname,.-tzname +.size __tzname,.-__tzname diff --git a/src/time/tzset.c b/src/time/tzset.c new file mode 100644 index 00000000..6d69957e --- /dev/null +++ b/src/time/tzset.c @@ -0,0 +1,173 @@ +#include <time.h> +#include <ctype.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include "libc.h" + +#include "__time.h" + +long __timezone = 0; +int __daylight = 0; +char *__tzname[2] = { 0, 0 }; +int __dst_offset = 0; + +weak_alias(__timezone, timezone); +weak_alias(__daylight, daylight); +weak_alias(__tzname, tzname); +weak_alias(__dst_offset, dst_offset); + +static char std_name[TZNAME_MAX+1]; +static char dst_name[TZNAME_MAX+1]; + +/* all elements are zero-based */ +static struct rule { + char month; + char week; + short day; + int time; +} __dst_start, __dst_end; + +static void zname(char *d, char **s) +{ + int i; + for (i=0; i<TZNAME_MAX && isalpha(d[i]=**s); i++, (*s)++); + d[i] = 0; +} + +static int hhmmss(char **s) +{ + int ofs = strtol(*s, s, 10)*3600; + if (ofs >= 0) { + if (**s == ':') ofs += strtol(*s+1, s, 10)*60; + if (**s == ':') ofs += strtol(*s+1, s, 10); + } else { + if (**s == ':') ofs -= strtol(*s+1, s, 10)*60; + if (**s == ':') ofs -= strtol(*s+1, s, 10); + } + return ofs; +} + +static int dstrule(struct rule *rule, char **s) +{ + if (**s != ',') return -1; + switch (*++*s) { + case 'J': + rule->month = 'J'; + rule->day = strtol(*s+1, s, 10)-1; + break; + case 'M': + rule->month = strtol(*s+1, s, 10)-1; + if (**s != '.' || rule->month < 0 || rule->month > 11) + return -1; + rule->week = strtol(*s+1, s, 10)-1; + if (**s != '.' || rule->week < 0 || rule->week > 4) + return -1; + rule->day = strtol(*s+1, s, 10); + if (rule->day < 0 || rule->day > 6) + return -1; + break; + default: + rule->month = 'L'; + rule->day = strtol(*s+1, s, 10); + break; + } + if (**s == '/') { + (*s)++; + rule->time = hhmmss(s); + } else rule->time = 7200; + return 0; +} + +void tzset(void) +{ + char *z, *a; + + strcpy(std_name, "GMT"); + strcpy(dst_name, "GMT"); + __tzname[0] = std_name; + __tzname[1] = dst_name; + __timezone = 0; + __daylight = 0; + + if (!(z = getenv("TZ")) || !isalpha(*z)) return; + + zname(std_name, &z); + __timezone = hhmmss(&z); + + zname(dst_name, &z); + if (dst_name[0]) __daylight=1; + a = z; + __dst_offset = hhmmss(&z) - __timezone; + if (z==a) __dst_offset = -3600; + + if (dstrule(&__dst_start, &z) || dstrule(&__dst_end, &z)) + __daylight = 0; +} + +void __tzset(void) +{ + static int lock, init; + if (init) return; + LOCK(&lock); + if (!init) tzset(); + init=1; + UNLOCK(&lock); +} + +static int is_leap(int year) +{ + year -= 100; + return !(year&3) && ((year%100) || !(year%400)); +} + +static int cutoff_yday(struct tm *tm, struct rule *rule) +{ + static const char days_in_month[] = {31,28,31,30,31,30,31,31,30,31,30,31}; + static const int first_day[] = {0,31,59,90,120,151,181,212,243,273,304,335}; + int yday, mday, leap; + + switch (rule->month) { + case 'J': + return rule->day + (tm->tm_mon > 1 && is_leap(tm->tm_year)); + case 'L': + return rule->day; + default: + yday = first_day[rule->month]; + leap = is_leap(tm->tm_year); + if (rule->month > 1 && leap) yday++; + mday = (rule->day - (yday + tm->tm_wday - tm->tm_yday) + 1400)%7 + 7*rule->week; + if (mday >= days_in_month[rule->month] + (leap && rule->month == 1)) + mday -= 7; + return mday + yday; + } +} + +struct tm *__dst_adjust(struct tm *tm) +{ + time_t t; + int start, end, secs; + int after_start, before_end; + + if (tm->tm_isdst >= 0) return tm; + if (!__daylight) { + tm->tm_isdst = 0; + return tm; + } + + secs = tm->tm_hour*3600 + tm->tm_min*60 + tm->tm_sec; + start = cutoff_yday(tm, &__dst_start); + end = cutoff_yday(tm, &__dst_end); + + after_start = (tm->tm_yday > start || (tm->tm_yday == start && secs >= __dst_start.time)); + before_end = (tm->tm_yday < end || (tm->tm_yday == end && secs < __dst_end.time)); + + if ((after_start && before_end) || ((end < start) && (after_start || before_end))) { + tm->tm_sec -= __dst_offset; + tm->tm_isdst = 1; + t = __tm_to_time(tm); + return __time_to_tm(t, tm); + } else tm->tm_isdst = 0; + + return tm; +} diff --git a/src/time/utime.c b/src/time/utime.c new file mode 100644 index 00000000..56e9e13a --- /dev/null +++ b/src/time/utime.c @@ -0,0 +1,12 @@ +#include <utime.h> +#include "syscall.h" + +int utime(const char *path, const struct utimbuf *times) +{ + long ktimes[2]; + if (times) { + ktimes[0] = times->actime; + ktimes[1] = times->modtime; + } + return syscall2(__NR_utime, (long)path, times ? (long)ktimes : 0); +} diff --git a/src/unistd/_exit.c b/src/unistd/_exit.c new file mode 100644 index 00000000..d2e84c4c --- /dev/null +++ b/src/unistd/_exit.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include <stdlib.h> + +void _exit(int status) +{ + _Exit(status); +} diff --git a/src/unistd/access.c b/src/unistd/access.c new file mode 100644 index 00000000..2c10e58c --- /dev/null +++ b/src/unistd/access.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int access(const char *filename, int amode) +{ + return syscall2(__NR_access, (long)filename, amode); +} diff --git a/src/unistd/alarm.c b/src/unistd/alarm.c new file mode 100644 index 00000000..bba444d8 --- /dev/null +++ b/src/unistd/alarm.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +unsigned alarm(unsigned seconds) +{ + return syscall1(__NR_alarm, seconds); +} diff --git a/src/unistd/chdir.c b/src/unistd/chdir.c new file mode 100644 index 00000000..c89bda38 --- /dev/null +++ b/src/unistd/chdir.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int chdir(const char *path) +{ + return syscall1(__NR_chdir, (long)path); +} diff --git a/src/unistd/chown.c b/src/unistd/chown.c new file mode 100644 index 00000000..6069a2fe --- /dev/null +++ b/src/unistd/chown.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int chown(const char *path, uid_t uid, gid_t gid) +{ + return syscall3(__NR_chown32, (long)path, uid, gid); +} diff --git a/src/unistd/close.c b/src/unistd/close.c new file mode 100644 index 00000000..97302f67 --- /dev/null +++ b/src/unistd/close.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int close(int fd) +{ + int ret = __syscall_close(fd); + CANCELPT_BEGIN; + CANCELPT_END; + return ret; +} diff --git a/src/unistd/confstr.c b/src/unistd/confstr.c new file mode 100644 index 00000000..4332f726 --- /dev/null +++ b/src/unistd/confstr.c @@ -0,0 +1,17 @@ +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +size_t confstr(int name, char *buf, size_t len) +{ + const char *s = ""; + if (!name) { + s = "/bin:/usr/bin"; + } else if ((name&~4U)!=1 && name-_CS_POSIX_V6_ILP32_OFF32_CFLAGS>31U) { + errno = EINVAL; + return 0; + } + // snprintf is overkill but avoid wasting code size to implement + // this completely useless function and its truncation semantics + return snprintf(buf, len, "%s", s); +} diff --git a/src/unistd/ctermid.c b/src/unistd/ctermid.c new file mode 100644 index 00000000..21b44ec8 --- /dev/null +++ b/src/unistd/ctermid.c @@ -0,0 +1,23 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <limits.h> + +char *ctermid(char *s) +{ + static char *s2; + int fd; + if (!s) { + if (!s2) s2 = malloc(L_ctermid); + s = s2; + } + fd = open("/dev/tty", O_WRONLY | O_NOCTTY); + if (fd < 0) + return strcpy(s, ""); + if (ttyname_r(fd, s, L_ctermid)) + strcpy(s, ""); + close(fd); + return s; +} diff --git a/src/unistd/dup.c b/src/unistd/dup.c new file mode 100644 index 00000000..b11cd715 --- /dev/null +++ b/src/unistd/dup.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int dup(int fd) +{ + return syscall1(__NR_dup, fd); +} diff --git a/src/unistd/dup2.c b/src/unistd/dup2.c new file mode 100644 index 00000000..93325446 --- /dev/null +++ b/src/unistd/dup2.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int dup2(int old, int new) +{ + return __syscall_dup2(old, new); +} diff --git a/src/unistd/faccessat.c b/src/unistd/faccessat.c new file mode 100644 index 00000000..99a93785 --- /dev/null +++ b/src/unistd/faccessat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int faccessat(int fd, const char *filename, int amode, int flag) +{ + return syscall4(__NR_faccessat, fd, (long)filename, amode, flag); +} diff --git a/src/unistd/fchdir.c b/src/unistd/fchdir.c new file mode 100644 index 00000000..b2acbc29 --- /dev/null +++ b/src/unistd/fchdir.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int fchdir(int fd) +{ + return syscall1(__NR_fchdir, fd); +} diff --git a/src/unistd/fchown.c b/src/unistd/fchown.c new file mode 100644 index 00000000..990f006d --- /dev/null +++ b/src/unistd/fchown.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int fchown(int fd, uid_t uid, gid_t gid) +{ + return syscall3(__NR_fchown32, fd, uid, gid); +} diff --git a/src/unistd/fchownat.c b/src/unistd/fchownat.c new file mode 100644 index 00000000..70626428 --- /dev/null +++ b/src/unistd/fchownat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int fchownat(int fd, const char *path, uid_t uid, gid_t gid, int flag) +{ + return syscall5(__NR_fchownat, fd, (long)path, uid, gid, flag); +} diff --git a/src/unistd/fdatasync.c b/src/unistd/fdatasync.c new file mode 100644 index 00000000..ef7c9a9d --- /dev/null +++ b/src/unistd/fdatasync.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int fdatasync(int fd) +{ + return 0; +} diff --git a/src/unistd/fsync.c b/src/unistd/fsync.c new file mode 100644 index 00000000..7cfedc98 --- /dev/null +++ b/src/unistd/fsync.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include "syscall.h" + +int fsync(int fd) +{ + //return syscall1(__NR_fsync, fd); + return 0; +} diff --git a/src/unistd/ftruncate.c b/src/unistd/ftruncate.c new file mode 100644 index 00000000..e0b2f4bb --- /dev/null +++ b/src/unistd/ftruncate.c @@ -0,0 +1,15 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int ftruncate(int fd, off_t length) +{ + if (sizeof(long) == 8) + return syscall2(__NR_ftruncate, fd, length); + else { + union { long long ll; long l[2]; } u = { length }; + return syscall3(__NR_ftruncate64, fd, u.l[0], u.l[1]); + } +} + +LFS64(ftruncate); diff --git a/src/unistd/getcwd.c b/src/unistd/getcwd.c new file mode 100644 index 00000000..4910f42c --- /dev/null +++ b/src/unistd/getcwd.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include <errno.h> +#include "syscall.h" + +char *getcwd(char *buf, size_t size) +{ + return syscall2(__NR_getcwd, (long)buf, size) < 0 ? NULL : buf; +} diff --git a/src/unistd/getegid.c b/src/unistd/getegid.c new file mode 100644 index 00000000..0e626b75 --- /dev/null +++ b/src/unistd/getegid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +gid_t getegid(void) +{ + return syscall0(__NR_getegid32); +} diff --git a/src/unistd/geteuid.c b/src/unistd/geteuid.c new file mode 100644 index 00000000..39d6ac7b --- /dev/null +++ b/src/unistd/geteuid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +uid_t geteuid(void) +{ + return syscall0(__NR_geteuid32); +} diff --git a/src/unistd/getgid.c b/src/unistd/getgid.c new file mode 100644 index 00000000..186635a2 --- /dev/null +++ b/src/unistd/getgid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +gid_t getgid(void) +{ + return syscall0(__NR_getgid32); +} diff --git a/src/unistd/getgroups.c b/src/unistd/getgroups.c new file mode 100644 index 00000000..6f19870e --- /dev/null +++ b/src/unistd/getgroups.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include "syscall.h" + +int getgroups(int count, gid_t list[]) +{ + /* this depends on our gid_t being 32bit */ + return syscall2(__NR_getgroups32, count, (long)list); +} diff --git a/src/unistd/gethostname.c b/src/unistd/gethostname.c new file mode 100644 index 00000000..a406c4eb --- /dev/null +++ b/src/unistd/gethostname.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include <sys/utsname.h> +#include <string.h> + +int gethostname(char *name, size_t len) +{ + size_t i; + struct utsname uts; + if (uname(&uts)) return -1; + if (len > sizeof uts.nodename) len = sizeof uts.nodename; + for (i=0; i<len && (name[i] = uts.nodename[i]); i++); + if (i==len) name[i-1] = 0; + return 0; +} diff --git a/src/unistd/getlogin.c b/src/unistd/getlogin.c new file mode 100644 index 00000000..06011913 --- /dev/null +++ b/src/unistd/getlogin.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include <stdlib.h> + +char *getlogin(void) +{ + return getenv("LOGNAME"); +} diff --git a/src/unistd/getlogin_r.c b/src/unistd/getlogin_r.c new file mode 100644 index 00000000..f04f71e5 --- /dev/null +++ b/src/unistd/getlogin_r.c @@ -0,0 +1,13 @@ +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +int getlogin_r(char *name, size_t size) +{ + char *logname = getlogin(); + if (!logname) return ENXIO; /* or...? */ + if (strlen(name) >= size) return ERANGE; + strcpy(name, logname); + return 0; +} diff --git a/src/unistd/getpgid.c b/src/unistd/getpgid.c new file mode 100644 index 00000000..50d716b5 --- /dev/null +++ b/src/unistd/getpgid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t getpgid(pid_t pid) +{ + return syscall1(__NR_getpgid, pid); +} diff --git a/src/unistd/getpgrp.c b/src/unistd/getpgrp.c new file mode 100644 index 00000000..2004630a --- /dev/null +++ b/src/unistd/getpgrp.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t getpgrp(void) +{ + return syscall0(__NR_getpgrp); +} diff --git a/src/unistd/getpid.c b/src/unistd/getpid.c new file mode 100644 index 00000000..31cbe1cb --- /dev/null +++ b/src/unistd/getpid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t getpid(void) +{ + return syscall0(__NR_getpid); +} diff --git a/src/unistd/getppid.c b/src/unistd/getppid.c new file mode 100644 index 00000000..a3241829 --- /dev/null +++ b/src/unistd/getppid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t getppid(void) +{ + return syscall0(__NR_getppid); +} diff --git a/src/unistd/getsid.c b/src/unistd/getsid.c new file mode 100644 index 00000000..064229cf --- /dev/null +++ b/src/unistd/getsid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t getsid(pid_t pid) +{ + return syscall1(__NR_getsid, pid); +} diff --git a/src/unistd/getuid.c b/src/unistd/getuid.c new file mode 100644 index 00000000..9d4e53f5 --- /dev/null +++ b/src/unistd/getuid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +uid_t getuid(void) +{ + return syscall0(__NR_getuid32); +} diff --git a/src/unistd/isatty.c b/src/unistd/isatty.c new file mode 100644 index 00000000..cff6e9fe --- /dev/null +++ b/src/unistd/isatty.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include <termios.h> + +int isatty(int fd) +{ + struct termios t; + return tcgetattr(fd, &t) == 0; +} diff --git a/src/unistd/lchown.c b/src/unistd/lchown.c new file mode 100644 index 00000000..30e83916 --- /dev/null +++ b/src/unistd/lchown.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int lchown(const char *path, uid_t uid, gid_t gid) +{ + return syscall3(__NR_lchown32, (long)path, uid, gid); +} diff --git a/src/unistd/link.c b/src/unistd/link.c new file mode 100644 index 00000000..f121bb9e --- /dev/null +++ b/src/unistd/link.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int link(const char *existing, const char *new) +{ + return syscall2(__NR_link, (long)existing, (long)new); +} diff --git a/src/unistd/linkat.c b/src/unistd/linkat.c new file mode 100644 index 00000000..0eb51221 --- /dev/null +++ b/src/unistd/linkat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int linkat(int fd1, const char *existing, int fd2, const char *new, int flag) +{ + return syscall5(__NR_linkat, fd1, (long)existing, fd2, (long)new, flag); +} diff --git a/src/unistd/lseek.c b/src/unistd/lseek.c new file mode 100644 index 00000000..0dab2679 --- /dev/null +++ b/src/unistd/lseek.c @@ -0,0 +1,16 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +off_t lseek(int fd, off_t offset, int whence) +{ + /* optimized away at compiletime */ + if (sizeof(long) == 8) + return syscall3(__NR_lseek, fd, offset, whence); + else { + off_t result; + return syscall5(__NR__llseek, fd, offset>>32, offset, (long)&result, whence) ? -1 : result; + } +} + +LFS64(lseek); diff --git a/src/unistd/nice.c b/src/unistd/nice.c new file mode 100644 index 00000000..4b28ef41 --- /dev/null +++ b/src/unistd/nice.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int nice(int inc) +{ + return syscall1(__NR_nice, inc); +} diff --git a/src/unistd/pause.c b/src/unistd/pause.c new file mode 100644 index 00000000..14720651 --- /dev/null +++ b/src/unistd/pause.c @@ -0,0 +1,12 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int pause(void) +{ + int r; + CANCELPT_BEGIN; + r = syscall0(__NR_pause); + CANCELPT_END; + return r; +} diff --git a/src/unistd/pipe.c b/src/unistd/pipe.c new file mode 100644 index 00000000..2dfc9c99 --- /dev/null +++ b/src/unistd/pipe.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int pipe(int fd[2]) +{ + return syscall1(__NR_pipe, (long)fd); +} diff --git a/src/unistd/pread.c b/src/unistd/pread.c new file mode 100644 index 00000000..029ba3d1 --- /dev/null +++ b/src/unistd/pread.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +ssize_t pread(int fd, void *buf, size_t size, off_t ofs) +{ + ssize_t r; + CANCELPT_BEGIN; + r = syscall5(__NR_pread64, fd, (long)buf, size, SYSCALL_LL(ofs)); + CANCELPT_END; + return r; +} + +LFS64(pread); diff --git a/src/unistd/pwrite.c b/src/unistd/pwrite.c new file mode 100644 index 00000000..8f23d1b9 --- /dev/null +++ b/src/unistd/pwrite.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +ssize_t pwrite(int fd, const void *buf, size_t size, off_t ofs) +{ + ssize_t r; + CANCELPT_BEGIN; + r = syscall5(__NR_pwrite64, fd, (long)buf, size, SYSCALL_LL(ofs)); + CANCELPT_END; + return r; +} + +LFS64(pwrite); diff --git a/src/unistd/read.c b/src/unistd/read.c new file mode 100644 index 00000000..87ff1f10 --- /dev/null +++ b/src/unistd/read.c @@ -0,0 +1,12 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +ssize_t read(int fd, void *buf, size_t count) +{ + ssize_t r; + CANCELPT_BEGIN; + r = __syscall_read(fd, buf, count); + CANCELPT_END; + return r; +} diff --git a/src/unistd/readlink.c b/src/unistd/readlink.c new file mode 100644 index 00000000..f6b1635c --- /dev/null +++ b/src/unistd/readlink.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int readlink(const char *path, char *buf, size_t bufsize) +{ + return syscall3(__NR_readlink, (long)path, (long)buf, bufsize); +} diff --git a/src/unistd/readlinkat.c b/src/unistd/readlinkat.c new file mode 100644 index 00000000..8171050d --- /dev/null +++ b/src/unistd/readlinkat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int readlinkat(int fd, const char *path, char *buf, size_t bufsize) +{ + return syscall4(__NR_readlinkat, fd, (long)path, (long)buf, bufsize); +} diff --git a/src/unistd/readv.c b/src/unistd/readv.c new file mode 100644 index 00000000..e311f9de --- /dev/null +++ b/src/unistd/readv.c @@ -0,0 +1,12 @@ +#include <sys/uio.h> +#include "syscall.h" +#include "libc.h" + +ssize_t readv(int fd, const struct iovec *iov, int count) +{ + ssize_t r; + CANCELPT_BEGIN; + r = syscall3(__NR_readv, fd, (long)iov, count); + CANCELPT_END; + return r; +} diff --git a/src/unistd/renameat.c b/src/unistd/renameat.c new file mode 100644 index 00000000..0dae9f11 --- /dev/null +++ b/src/unistd/renameat.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include "syscall.h" + +int renameat(int oldfd, const char *old, int newfd, const char *new) +{ + return syscall4(__NR_renameat, oldfd, (long)old, newfd, (long)new); +} diff --git a/src/unistd/rmdir.c b/src/unistd/rmdir.c new file mode 100644 index 00000000..8e18c7a8 --- /dev/null +++ b/src/unistd/rmdir.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int rmdir(const char *path) +{ + return syscall1(__NR_rmdir, (long)path); +} diff --git a/src/unistd/setegid.c b/src/unistd/setegid.c new file mode 100644 index 00000000..85348842 --- /dev/null +++ b/src/unistd/setegid.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +int setegid(gid_t egid) +{ + return setregid(-1, egid); +} diff --git a/src/unistd/seteuid.c b/src/unistd/seteuid.c new file mode 100644 index 00000000..0aaa86e0 --- /dev/null +++ b/src/unistd/seteuid.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +int seteuid(uid_t euid) +{ + return setreuid(-1, euid); +} diff --git a/src/unistd/setgid.c b/src/unistd/setgid.c new file mode 100644 index 00000000..00c7a03a --- /dev/null +++ b/src/unistd/setgid.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int setgid(gid_t gid) +{ + if (libc.rsyscall) return libc.rsyscall(__NR_setgid32, gid, 0, 0, 0, 0, 0); + return syscall1(__NR_setgid32, gid); +} diff --git a/src/unistd/setpgid.c b/src/unistd/setpgid.c new file mode 100644 index 00000000..748d2907 --- /dev/null +++ b/src/unistd/setpgid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t setpgid(pid_t pid, pid_t pgid) +{ + return syscall2(__NR_setpgid, pid, pgid); +} diff --git a/src/unistd/setpgrp.c b/src/unistd/setpgrp.c new file mode 100644 index 00000000..a2a37f65 --- /dev/null +++ b/src/unistd/setpgrp.c @@ -0,0 +1,6 @@ +#include <unistd.h> + +pid_t setpgrp(void) +{ + return setpgid(0, 0); +} diff --git a/src/unistd/setregid.c b/src/unistd/setregid.c new file mode 100644 index 00000000..d25dab76 --- /dev/null +++ b/src/unistd/setregid.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int setregid(gid_t rgid, gid_t egid) +{ + if (libc.rsyscall) return libc.rsyscall(__NR_setregid32, rgid, egid, 0, 0, 0, 0); + return syscall2(__NR_setregid32, rgid, egid); +} diff --git a/src/unistd/setreuid.c b/src/unistd/setreuid.c new file mode 100644 index 00000000..0ba2277b --- /dev/null +++ b/src/unistd/setreuid.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int setreuid(uid_t ruid, uid_t euid) +{ + if (libc.rsyscall) return libc.rsyscall(__NR_setreuid32, ruid, euid, 0, 0, 0, 0); + return syscall2(__NR_setreuid32, ruid, euid); +} diff --git a/src/unistd/setsid.c b/src/unistd/setsid.c new file mode 100644 index 00000000..e2c5690c --- /dev/null +++ b/src/unistd/setsid.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +pid_t setsid(void) +{ + return syscall0(__NR_setsid); +} diff --git a/src/unistd/setuid.c b/src/unistd/setuid.c new file mode 100644 index 00000000..53b74d88 --- /dev/null +++ b/src/unistd/setuid.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int setuid(uid_t uid) +{ + if (libc.rsyscall) return libc.rsyscall(__NR_setuid32, uid, 0, 0, 0, 0, 0); + return syscall1(__NR_setuid32, uid); +} diff --git a/src/unistd/sleep.c b/src/unistd/sleep.c new file mode 100644 index 00000000..d6450941 --- /dev/null +++ b/src/unistd/sleep.c @@ -0,0 +1,10 @@ +#include <unistd.h> +#include <time.h> + +unsigned sleep(unsigned seconds) +{ + struct timespec tv = { .tv_sec = seconds, .tv_nsec = 0 }; + if (nanosleep(&tv, &tv)) + return tv.tv_sec; + return 0; +} diff --git a/src/unistd/symlink.c b/src/unistd/symlink.c new file mode 100644 index 00000000..8d380d85 --- /dev/null +++ b/src/unistd/symlink.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int symlink(const char *existing, const char *new) +{ + return syscall2(__NR_symlink, (long)existing, (long)new); +} diff --git a/src/unistd/symlinkat.c b/src/unistd/symlinkat.c new file mode 100644 index 00000000..9693b226 --- /dev/null +++ b/src/unistd/symlinkat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int symlinkat(const char *existing, int fd, const char *new) +{ + return syscall3(__NR_symlinkat, (long)existing, fd, (long)new); +} diff --git a/src/unistd/sync.c b/src/unistd/sync.c new file mode 100644 index 00000000..a49808fd --- /dev/null +++ b/src/unistd/sync.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +void sync(void) +{ + syscall0(__NR_sync); +} diff --git a/src/unistd/tcgetpgrp.c b/src/unistd/tcgetpgrp.c new file mode 100644 index 00000000..50080c7e --- /dev/null +++ b/src/unistd/tcgetpgrp.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include <termios.h> +#include <sys/ioctl.h> + +pid_t tcgetpgrp(int fd) +{ + int pgrp; + if (ioctl(fd, TIOCGPGRP, &pgrp) < 0) + return -1; + return pgrp; +} diff --git a/src/unistd/tcsetpgrp.c b/src/unistd/tcsetpgrp.c new file mode 100644 index 00000000..67c38cb4 --- /dev/null +++ b/src/unistd/tcsetpgrp.c @@ -0,0 +1,9 @@ +#include <unistd.h> +#include <termios.h> +#include <sys/ioctl.h> + +int tcsetpgrp(int fd, pid_t pgrp) +{ + int pgrp_int = pgrp; + return ioctl(fd, TIOCSPGRP, &pgrp_int); +} diff --git a/src/unistd/truncate.c b/src/unistd/truncate.c new file mode 100644 index 00000000..f75e824e --- /dev/null +++ b/src/unistd/truncate.c @@ -0,0 +1,15 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +int truncate(const char *path, off_t length) +{ + if (sizeof(long) == 8) + return syscall2(__NR_truncate, (long)path, length); + else { + union { long long ll; long l[2]; } u = { length }; + return syscall3(__NR_truncate64, (long)path, u.l[0], u.l[1]); + } +} + +LFS64(truncate); diff --git a/src/unistd/ttyname.c b/src/unistd/ttyname.c new file mode 100644 index 00000000..0f3e1141 --- /dev/null +++ b/src/unistd/ttyname.c @@ -0,0 +1,14 @@ +#include <unistd.h> +#include <errno.h> +#include <limits.h> + +char *ttyname(int fd) +{ + static char buf[TTY_NAME_MAX]; + int result; + if ((result = ttyname_r(fd, buf, sizeof buf))) { + errno = result; + return NULL; + } + return buf; +} diff --git a/src/unistd/ttyname_r.c b/src/unistd/ttyname_r.c new file mode 100644 index 00000000..f86fbd9c --- /dev/null +++ b/src/unistd/ttyname_r.c @@ -0,0 +1,19 @@ +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +int ttyname_r(int fd, char *name, size_t size) +{ + char procname[sizeof "/proc/self/fd/" + 3*sizeof(int) + 2]; + ssize_t l; + + if (!isatty(fd)) return ENOTTY; + + snprintf(procname, sizeof procname, "/proc/self/fd/%d", fd); + l = readlink(procname, name, size); + + if (l < 0) return errno; + else if (l == size) return ERANGE; + else return 0; +} diff --git a/src/unistd/ualarm.c b/src/unistd/ualarm.c new file mode 100644 index 00000000..be853035 --- /dev/null +++ b/src/unistd/ualarm.c @@ -0,0 +1,8 @@ +#include <unistd.h> +#include "syscall.h" + +/* FIXME: ?? */ +useconds_t ualarm(useconds_t useconds, useconds_t interval) +{ + return -1; +} diff --git a/src/unistd/unlink.c b/src/unistd/unlink.c new file mode 100644 index 00000000..fb577920 --- /dev/null +++ b/src/unistd/unlink.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int unlink(const char *path) +{ + return __syscall_unlink(path); +} diff --git a/src/unistd/unlinkat.c b/src/unistd/unlinkat.c new file mode 100644 index 00000000..47fccc17 --- /dev/null +++ b/src/unistd/unlinkat.c @@ -0,0 +1,7 @@ +#include <unistd.h> +#include "syscall.h" + +int unlinkat(int fd, const char *path, int flag) +{ + return syscall3(__NR_unlinkat, fd, (long)path, flag); +} diff --git a/src/unistd/usleep.c b/src/unistd/usleep.c new file mode 100644 index 00000000..e3869017 --- /dev/null +++ b/src/unistd/usleep.c @@ -0,0 +1,11 @@ +#include <unistd.h> +#include <time.h> + +int usleep(useconds_t useconds) +{ + struct timespec tv = { + .tv_sec = useconds/1000000, + .tv_nsec = (useconds%1000000)*1000 + }; + return nanosleep(&tv, &tv); +} diff --git a/src/unistd/write.c b/src/unistd/write.c new file mode 100644 index 00000000..426cfc5b --- /dev/null +++ b/src/unistd/write.c @@ -0,0 +1,12 @@ +#include <unistd.h> +#include "syscall.h" +#include "libc.h" + +ssize_t write(int fd, const void *buf, size_t count) +{ + int r; + CANCELPT_BEGIN; + r = __syscall_write(fd, buf, count); + CANCELPT_END; + return r; +} diff --git a/src/unistd/writev.c b/src/unistd/writev.c new file mode 100644 index 00000000..a6a118af --- /dev/null +++ b/src/unistd/writev.c @@ -0,0 +1,12 @@ +#include <sys/uio.h> +#include "syscall.h" +#include "libc.h" + +ssize_t writev(int fd, const struct iovec *iov, int count) +{ + ssize_t r; + CANCELPT_BEGIN; + r = syscall3(__NR_writev, fd, (long)iov, count); + CANCELPT_END; + return r; +} |