aboutsummaryrefslogtreecommitdiffstats
path: root/main/musl
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2014-09-20 10:20:54 +0300
committerTimo Teräs <timo.teras@iki.fi>2014-09-20 10:29:46 +0300
commit73ccb8f1afc77801f69c6b38164467fb21e8a3e1 (patch)
tree63f8a6c71c501b987d86ed32245f0854557ee9c8 /main/musl
parent4cf4058c180ac24d16c0facc784aa88d19de6b8a (diff)
downloadaports-73ccb8f1afc77801f69c6b38164467fb21e8a3e1.tar.bz2
aports-73ccb8f1afc77801f69c6b38164467fb21e8a3e1.tar.xz
main/musl: upgrade to git snapshot of 2014-09-20
Introduces C11 features, and multiple bug fixes. Notably fixes flockfile() corruption (regression) that caused claws-mail to crash often. Alignas patch was upstreamed so remove that.
Diffstat (limited to 'main/musl')
-rw-r--r--main/musl/0005-4674809b-to-3e936ce8.patch3400
-rw-r--r--main/musl/APKBUILD11
-rw-r--r--main/musl/alignas.patch17
3 files changed, 3406 insertions, 22 deletions
diff --git a/main/musl/0005-4674809b-to-3e936ce8.patch b/main/musl/0005-4674809b-to-3e936ce8.patch
new file mode 100644
index 0000000000..9180e7eab6
--- /dev/null
+++ b/main/musl/0005-4674809b-to-3e936ce8.patch
@@ -0,0 +1,3400 @@
+diff --git a/arch/arm/bits/alltypes.h.in b/arch/arm/bits/alltypes.h.in
+index 183c4c4..bbe3ce6 100644
+--- a/arch/arm/bits/alltypes.h.in
++++ b/arch/arm/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/i386/bits/alltypes.h.in b/arch/i386/bits/alltypes.h.in
+index 8ba8f6f..646922b 100644
+--- a/arch/i386/bits/alltypes.h.in
++++ b/arch/i386/bits/alltypes.h.in
+@@ -27,13 +27,19 @@ TYPEDEF long double float_t;
+ TYPEDEF long double double_t;
+ #endif
+
++#ifdef __cplusplus
++TYPEDEF struct { alignas(8) long long __ll; long double __ld; } max_align_t;
++#else
+ TYPEDEF struct { _Alignas(8) long long __ll; long double __ld; } max_align_t;
++#endif
+
+ TYPEDEF long time_t;
+ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/microblaze/bits/alltypes.h.in b/arch/microblaze/bits/alltypes.h.in
+index a03e1b8..95f1ebb 100644
+--- a/arch/microblaze/bits/alltypes.h.in
++++ b/arch/microblaze/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/mips/bits/alltypes.h.in b/arch/mips/bits/alltypes.h.in
+index a03e1b8..95f1ebb 100644
+--- a/arch/mips/bits/alltypes.h.in
++++ b/arch/mips/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/or1k/bits/alltypes.h.in b/arch/or1k/bits/alltypes.h.in
+index 183c4c4..bbe3ce6 100644
+--- a/arch/or1k/bits/alltypes.h.in
++++ b/arch/or1k/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/powerpc/bits/alltypes.h.in b/arch/powerpc/bits/alltypes.h.in
+index ee7f137..7446ab4 100644
+--- a/arch/powerpc/bits/alltypes.h.in
++++ b/arch/powerpc/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/sh/bits/alltypes.h.in b/arch/sh/bits/alltypes.h.in
+index ee7f137..7446ab4 100644
+--- a/arch/sh/bits/alltypes.h.in
++++ b/arch/sh/bits/alltypes.h.in
+@@ -20,6 +20,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[9]; unsigned __s[9]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[6]; volatile void *volatile __p[6]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[12]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[8]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[5]; void *__p[5]; } __u; } pthread_barrier_t;
+diff --git a/arch/x32/bits/alltypes.h.in b/arch/x32/bits/alltypes.h.in
+index 8e396c9..a469d5c 100644
+--- a/arch/x32/bits/alltypes.h.in
++++ b/arch/x32/bits/alltypes.h.in
+@@ -25,6 +25,8 @@ TYPEDEF long long suseconds_t;
+
+ TYPEDEF struct { union { int __i[14]; unsigned long __s[7]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[14]; void *__p[7]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[4]; } __u; } pthread_barrier_t;
+diff --git a/arch/x86_64/bits/alltypes.h.in b/arch/x86_64/bits/alltypes.h.in
+index 7b4f3e7..2ce8e4a 100644
+--- a/arch/x86_64/bits/alltypes.h.in
++++ b/arch/x86_64/bits/alltypes.h.in
+@@ -25,6 +25,8 @@ TYPEDEF long suseconds_t;
+
+ TYPEDEF struct { union { int __i[14]; unsigned long __s[7]; } __u; } pthread_attr_t;
+ TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } pthread_mutex_t;
++TYPEDEF struct { union { int __i[10]; volatile void *volatile __p[5]; } __u; } mtx_t;
+ TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } pthread_cond_t;
++TYPEDEF struct { union { int __i[12]; void *__p[6]; } __u; } cnd_t;
+ TYPEDEF struct { union { int __i[14]; void *__p[7]; } __u; } pthread_rwlock_t;
+ TYPEDEF struct { union { int __i[8]; void *__p[4]; } __u; } pthread_barrier_t;
+diff --git a/include/fcntl.h b/include/fcntl.h
+index 2d8fa6e..ff9fcb9 100644
+--- a/include/fcntl.h
++++ b/include/fcntl.h
+@@ -46,6 +46,10 @@ int posix_fallocate(int, off_t, off_t);
+ #define O_WRONLY 01
+ #define O_RDWR 02
+
++#define F_OFD_GETLK 36
++#define F_OFD_SETLK 37
++#define F_OFD_SETLKW 38
++
+ #define F_DUPFD_CLOEXEC 1030
+
+ #define F_RDLCK 0
+diff --git a/include/features.h b/include/features.h
+index 294c61d..3cc3e57 100644
+--- a/include/features.h
++++ b/include/features.h
+@@ -1,10 +1,14 @@
+ #ifndef _FEATURES_H
+ #define _FEATURES_H
+
+-#ifdef _ALL_SOURCE
++#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
+ #define _GNU_SOURCE 1
+ #endif
+
++#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
++#define _BSD_SOURCE 1
++#endif
++
+ #if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
+ && !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
+ && !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
+diff --git a/include/glob.h b/include/glob.h
+index 9fbbaa6..76f6c1c 100644
+--- a/include/glob.h
++++ b/include/glob.h
+@@ -39,6 +39,7 @@ void globfree(glob_t *);
+ #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
+ #define glob64 glob
+ #define globfree64 globfree
++#define glob64_t glob_t
+ #endif
+
+ #ifdef __cplusplus
+diff --git a/include/signal.h b/include/signal.h
+index c36e4d5..87301ba 100644
+--- a/include/signal.h
++++ b/include/signal.h
+@@ -196,7 +196,7 @@ void psignal(int, const char *);
+
+ #endif
+
+-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
++#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
+ int killpg(pid_t, int);
+ int sigaltstack(const stack_t *__restrict, stack_t *__restrict);
+ int sighold(int);
+diff --git a/include/stdlib.h b/include/stdlib.h
+index db569d9..97ce5a7 100644
+--- a/include/stdlib.h
++++ b/include/stdlib.h
+@@ -114,9 +114,6 @@ long int random (void);
+ void srandom (unsigned int);
+ char *initstate (unsigned int, char *, size_t);
+ char *setstate (char *);
+-#endif
+-
+-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
+ int putenv (char *);
+ int posix_openpt (int);
+ int grantpt (int);
+diff --git a/include/sys/stat.h b/include/sys/stat.h
+index c6abab5..82a6490 100644
+--- a/include/sys/stat.h
++++ b/include/sys/stat.h
+@@ -100,8 +100,9 @@ int lchmod(const char *, mode_t);
+ #define fstat64 fstat
+ #define lstat64 lstat
+ #define fstatat64 fstatat
+-#define blksize64_t blksize_t
+ #define blkcnt64_t blkcnt_t
++#define fsblkcnt64_t fsblkcnt_t
++#define fsfilcnt64_t fsfilcnt_t
+ #define ino64_t ino_t
+ #define off64_t off_t
+ #endif
+diff --git a/include/sys/types.h b/include/sys/types.h
+index f00db03..75e489c 100644
+--- a/include/sys/types.h
++++ b/include/sys/types.h
+@@ -73,7 +73,6 @@ typedef unsigned long long u_quad_t;
+ #endif
+
+ #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE)
+-#define blksize64_t blksize_t
+ #define blkcnt64_t blkcnt_t
+ #define fsblkcnt64_t fsblkcnt_t
+ #define fsfilcnt64_t fsfilcnt_t
+diff --git a/include/threads.h b/include/threads.h
+new file mode 100644
+index 0000000..0e5836c
+--- /dev/null
++++ b/include/threads.h
+@@ -0,0 +1,85 @@
++#ifndef _THREADS_H
++#define _THREADS_H
++
++#include <features.h>
++#include <time.h>
++
++#ifdef __cplusplus
++extern "C" {
++typedef unsigned long thrd_t;
++#else
++typedef struct __pthread *thrd_t;
++#define thread_local _Thread_local
++#endif
++
++typedef int once_flag;
++typedef unsigned tss_t;
++typedef int (*thrd_start_t)(void *);
++typedef void (*tss_dtor_t)(void *);
++
++#define __NEED_cnd_t
++#define __NEED_mtx_t
++
++#include <bits/alltypes.h>
++
++#define TSS_DTOR_ITERATIONS 4
++
++enum {
++ thrd_success = 0,
++ thrd_busy = 1,
++ thrd_error = 2,
++ thrd_nomem = 3,
++ thrd_timedout = 4,
++};
++
++enum {
++ mtx_plain = 0,
++ mtx_recursive = 1,
++ mtx_timed = 2,
++};
++
++#define ONCE_FLAG_INIT 0
++
++int thrd_create(thrd_t *, thrd_start_t, void *);
++_Noreturn void thrd_exit(int);
++
++int thrd_detach(thrd_t);
++int thrd_join(thrd_t, int *);
++
++int thrd_sleep(const struct timespec *, struct timespec *);
++void thrd_yield(void);
++
++thrd_t thrd_current(void);
++int thrd_equal(thrd_t, thrd_t);
++#define thrd_equal(A, B) ((A) == (B))
++
++void call_once(once_flag *, void (*)(void));
++
++int mtx_init(mtx_t *, int);
++void mtx_destroy(mtx_t *);
++
++int mtx_lock(mtx_t *);
++int mtx_timedlock(mtx_t *__restrict, const struct timespec *__restrict);
++int mtx_trylock(mtx_t *);
++int mtx_unlock(mtx_t *);
++
++int cnd_init(cnd_t *);
++void cnd_destroy(cnd_t *);
++
++int cnd_broadcast(cnd_t *);
++int cnd_signal(cnd_t *);
++
++int cnd_timedwait(cnd_t *__restrict, mtx_t *__restrict, const struct timespec *__restrict);
++int cnd_wait(cnd_t *, mtx_t *);
++
++int tss_create(tss_t *, tss_dtor_t);
++void tss_delete(tss_t key);
++
++int tss_set(tss_t, void *);
++void *tss_get(tss_t);
++
++#ifdef __cplusplus
++}
++#endif
++
++#endif
+diff --git a/include/time.h b/include/time.h
+index dc88070..16ec08a 100644
+--- a/include/time.h
++++ b/include/time.h
+@@ -17,11 +17,11 @@ extern "C" {
+ #define __NEED_size_t
+ #define __NEED_time_t
+ #define __NEED_clock_t
++#define __NEED_struct_timespec
+
+ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
+ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
+ || defined(_BSD_SOURCE)
+-#define __NEED_struct_timespec
+ #define __NEED_clockid_t
+ #define __NEED_timer_t
+ #define __NEED_pid_t
+@@ -59,9 +59,11 @@ struct tm *gmtime (const time_t *);
+ struct tm *localtime (const time_t *);
+ char *asctime (const struct tm *);
+ char *ctime (const time_t *);
++int timespec_get(struct timespec *, int);
+
+ #define CLOCKS_PER_SEC 1000000L
+
++#define TIME_UTC 1
+
+ #if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) \
+ || defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE) \
+@@ -114,7 +116,7 @@ int timer_getoverrun (timer_t);
+ #endif
+
+
+-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
++#if defined(_XOPEN_SOURCE) || defined(_BSD_SOURCE) || defined(_GNU_SOURCE)
+ char *strptime (const char *__restrict, const char *__restrict, struct tm *__restrict);
+ extern int daylight;
+ extern long timezone;
+diff --git a/include/unistd.h b/include/unistd.h
+index ac6055a..0fe75d5 100644
+--- a/include/unistd.h
++++ b/include/unistd.h
+@@ -140,9 +140,6 @@ int lockf(int, int, off_t);
+ long gethostid(void);
+ int nice(int);
+ void sync(void);
+-#endif
+-
+-#if defined(_XOPEN_SOURCE) || defined(_GNU_SOURCE)
+ pid_t setpgrp(void);
+ char *crypt(const char *, const char *);
+ void encrypt(char *, int);
+diff --git a/src/aio/aio_cancel.c b/src/aio/aio_cancel.c
+index 5a753b1..16fc431 100644
+--- a/src/aio/aio_cancel.c
++++ b/src/aio/aio_cancel.c
+@@ -1,6 +1,7 @@
+ #include <aio.h>
+ #include <pthread.h>
+ #include <errno.h>
++#include "libc.h"
+
+ int aio_cancel(int fd, struct aiocb *cb)
+ {
+@@ -14,3 +15,5 @@ int aio_cancel(int fd, struct aiocb *cb)
+ }
+ return cb->__err==EINPROGRESS ? AIO_NOTCANCELED : AIO_ALLDONE;
+ }
++
++LFS64(aio_cancel);
+diff --git a/src/aio/aio_error.c b/src/aio/aio_error.c
+index a780fd3..fd42ea1 100644
+--- a/src/aio/aio_error.c
++++ b/src/aio/aio_error.c
+@@ -1,6 +1,9 @@
+ #include <aio.h>
++#include "libc.h"
+
+ int aio_error(const struct aiocb *cb)
+ {
+ return cb->__err;
+ }
++
++LFS64(aio_error);
+diff --git a/src/aio/aio_fsync.c b/src/aio/aio_fsync.c
+index 0ac6ea8..6e1a70a 100644
+--- a/src/aio/aio_fsync.c
++++ b/src/aio/aio_fsync.c
+@@ -1,5 +1,6 @@
+ #include <aio.h>
+ #include <errno.h>
++#include "libc.h"
+
+ int aio_fsync(int op, struct aiocb *cb)
+ {
+@@ -7,3 +8,5 @@ int aio_fsync(int op, struct aiocb *cb)
+ errno = EINVAL;
+ return -1;
+ }
++
++LFS64(aio_fsync);
+diff --git a/src/aio/aio_readwrite.c b/src/aio/aio_readwrite.c
+index 2278226..8753ffd 100644
+--- a/src/aio/aio_readwrite.c
++++ b/src/aio/aio_readwrite.c
+@@ -105,3 +105,6 @@ int aio_write(struct aiocb *cb)
+ cb->aio_lio_opcode = LIO_WRITE;
+ return new_req(cb);
+ }
++
++LFS64(aio_read);
++LFS64(aio_write);
+diff --git a/src/aio/aio_return.c b/src/aio/aio_return.c
+index df10bdb..c1ce450 100644
+--- a/src/aio/aio_return.c
++++ b/src/aio/aio_return.c
+@@ -1,6 +1,9 @@
+ #include <aio.h>
++#include "libc.h"
+
+ ssize_t aio_return(struct aiocb *cb)
+ {
+ return cb->__ret;
+ }
++
++LFS64(aio_return);
+diff --git a/src/aio/aio_suspend.c b/src/aio/aio_suspend.c
+index 39a1d3a..dcdf601 100644
+--- a/src/aio/aio_suspend.c
++++ b/src/aio/aio_suspend.c
+@@ -1,6 +1,7 @@
+ #include <aio.h>
+ #include <errno.h>
+ #include "pthread_impl.h"
++#include "libc.h"
+
+ /* Due to the requirement that aio_suspend be async-signal-safe, we cannot
+ * use any locks, wait queues, etc. that would make it more efficient. The
+@@ -55,3 +56,5 @@ int aio_suspend(const struct aiocb *const cbs[], int cnt, const struct timespec
+ }
+ }
+ }
++
++LFS64(aio_suspend);
+diff --git a/src/aio/lio_listio.c b/src/aio/lio_listio.c
+index 75ed225..bd37767 100644
+--- a/src/aio/lio_listio.c
++++ b/src/aio/lio_listio.c
+@@ -141,3 +141,4 @@ int lio_listio(int mode, struct aiocb *restrict const *restrict cbs, int cnt, st
+ return 0;
+ }
+
++LFS64(lio_listio);
+diff --git a/src/conf/fpathconf.c b/src/conf/fpathconf.c
+index 28c4345..8eb037e 100644
+--- a/src/conf/fpathconf.c
++++ b/src/conf/fpathconf.c
+@@ -27,7 +27,7 @@ long fpathconf(int fd, int name)
+ [_PC_SYMLINK_MAX] = SYMLINK_MAX,
+ [_PC_2_SYMLINKS] = 1
+ };
+- if (name > sizeof(values)/sizeof(values[0])) {
++ if (name >= sizeof(values)/sizeof(values[0])) {
+ errno = EINVAL;
+ return -1;
+ }
+diff --git a/src/dirent/versionsort.c b/src/dirent/versionsort.c
+index 9769610..410cb70 100644
+--- a/src/dirent/versionsort.c
++++ b/src/dirent/versionsort.c
+@@ -1,8 +1,12 @@
+ #define _GNU_SOURCE
+ #include <string.h>
+ #include <dirent.h>
++#include "libc.h"
+
+ int versionsort(const struct dirent **a, const struct dirent **b)
+ {
+ return strverscmp((*a)->d_name, (*b)->d_name);
+ }
++
++#undef versionsort64
++LFS64(versionsort);
+diff --git a/src/internal/intscan.c b/src/internal/intscan.c
+index 69350ef..65d497e 100644
+--- a/src/internal/intscan.c
++++ b/src/internal/intscan.c
+@@ -83,6 +83,7 @@ unsigned long long __intscan(FILE *f, unsigned base, int pok, unsigned long long
+ for (; val[c]<base; c=shgetc(f));
+ errno = ERANGE;
+ y = lim;
++ if (lim&1) neg = 0;
+ }
+ done:
+ shunget(f);
+diff --git a/src/internal/pthread_impl.h b/src/internal/pthread_impl.h
+index 74c62cc..ae6e60b 100644
+--- a/src/internal/pthread_impl.h
++++ b/src/internal/pthread_impl.h
+@@ -128,4 +128,6 @@ void __restore_sigs(void *);
+ #define DEFAULT_STACK_SIZE 81920
+ #define DEFAULT_GUARD_SIZE PAGE_SIZE
+
++#define __ATTRP_C11_THREAD ((void*)(uintptr_t)-1)
++
+ #endif
+diff --git a/src/locale/__lctrans.c b/src/locale/__lctrans.c
+index 2769c08..15994c5 100644
+--- a/src/locale/__lctrans.c
++++ b/src/locale/__lctrans.c
+@@ -2,7 +2,7 @@
+ #include "locale_impl.h"
+ #include "libc.h"
+
+-const char *dummy(const char *msg, const struct __locale_map *lm)
++static const char *dummy(const char *msg, const struct __locale_map *lm)
+ {
+ return msg;
+ }
+diff --git a/src/math/__polevll.c b/src/math/__polevll.c
+index a272865..ce1a840 100644
+--- a/src/math/__polevll.c
++++ b/src/math/__polevll.c
+@@ -56,6 +56,8 @@
+
+ #include "libm.h"
+
++#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
++#else
+ /*
+ * Polynomial evaluator:
+ * P[0] x^n + P[1] x^(n-1) + ... + P[n]
+@@ -88,3 +90,4 @@ long double __p1evll(long double x, const long double *P, int n)
+
+ return y;
+ }
++#endif
+diff --git a/src/math/exp10.c b/src/math/exp10.c
+index 16d704a..9f5e3c2 100644
+--- a/src/math/exp10.c
++++ b/src/math/exp10.c
+@@ -1,5 +1,6 @@
+ #define _GNU_SOURCE
+ #include <math.h>
++#include <stdint.h>
+ #include "libc.h"
+
+ double exp10(double x)
+@@ -11,7 +12,9 @@ double exp10(double x)
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15
+ };
+ double n, y = modf(x, &n);
+- if (fabs(n) < 16) {
++ union {double f; uint64_t i;} u = {n};
++ /* fabs(n) < 16 without raising invalid on nan */
++ if ((u.i>>52 & 0x7ff) < 0x3ff+4) {
+ if (!y) return p10[(int)n+15];
+ y = exp2(3.32192809488736234787031942948939 * y);
+ return y * p10[(int)n+15];
+diff --git a/src/math/exp10f.c b/src/math/exp10f.c
+index 5fd1af9..7a8d447 100644
+--- a/src/math/exp10f.c
++++ b/src/math/exp10f.c
+@@ -1,5 +1,6 @@
+ #define _GNU_SOURCE
+ #include <math.h>
++#include <stdint.h>
+ #include "libc.h"
+
+ float exp10f(float x)
+@@ -9,7 +10,9 @@ float exp10f(float x)
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7
+ };
+ float n, y = modff(x, &n);
+- if (fabsf(n) < 8) {
++ union {float f; uint32_t i;} u = {n};
++ /* fabsf(n) < 8 without raising invalid on nan */
++ if ((u.i>>23 & 0xff) < 0x7f+3) {
+ if (!y) return p10[(int)n+7];
+ y = exp2f(3.32192809488736234787031942948939f * y);
+ return y * p10[(int)n+7];
+diff --git a/src/math/exp10l.c b/src/math/exp10l.c
+index 22a4636..b758ebf 100644
+--- a/src/math/exp10l.c
++++ b/src/math/exp10l.c
+@@ -1,7 +1,15 @@
+ #define _GNU_SOURCE
++#include <float.h>
+ #include <math.h>
+ #include "libc.h"
++#include "libm.h"
+
++#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
++long double exp10l(long double x)
++{
++ return exp10(x);
++}
++#elif (LDBL_MANT_DIG == 64 || LDBL_MANT_DIG == 113) && LDBL_MAX_EXP == 16384
+ long double exp10l(long double x)
+ {
+ static const long double p10[] = {
+@@ -11,12 +19,15 @@ long double exp10l(long double x)
+ 1e10, 1e11, 1e12, 1e13, 1e14, 1e15
+ };
+ long double n, y = modfl(x, &n);
+- if (fabsl(n) < 16) {
++ union ldshape u = {n};
++ /* fabsl(n) < 16 without raising invalid on nan */
++ if ((u.i.se & 0x7fff) < 0x3fff+4) {
+ if (!y) return p10[(int)n+15];
+ y = exp2l(3.32192809488736234787031942948939L * y);
+ return y * p10[(int)n+15];
+ }
+ return powl(10.0, x);
+ }
++#endif
+
+ weak_alias(exp10l, pow10l);
+diff --git a/src/mman/mprotect.c b/src/mman/mprotect.c
+index f488486..535787b 100644
+--- a/src/mman/mprotect.c
++++ b/src/mman/mprotect.c
+@@ -2,10 +2,12 @@
+ #include "libc.h"
+ #include "syscall.h"
+
+-int mprotect(void *addr, size_t len, int prot)
++int __mprotect(void *addr, size_t len, int prot)
+ {
+ size_t start, end;
+ start = (size_t)addr & -PAGE_SIZE;
+ end = (size_t)((char *)addr + len + PAGE_SIZE-1) & -PAGE_SIZE;
+ return syscall(SYS_mprotect, start, end-start, prot);
+ }
++
++weak_alias(__mprotect, mprotect);
+diff --git a/src/network/getnameinfo.c b/src/network/getnameinfo.c
+index 2ba66e3..3484fc6 100644
+--- a/src/network/getnameinfo.c
++++ b/src/network/getnameinfo.c
+@@ -96,7 +96,7 @@ static void reverse_services(char *buf, int port, int dgram)
+ if ((p=strchr(line, '#'))) *p++='\n', *p=0;
+
+ for (p=line; *p && !isspace(*p); p++);
+- if (!p) continue;
++ if (!*p) continue;
+ *p++ = 0;
+ svport = strtoul(p, &z, 10);
+
+diff --git a/src/network/lookup_serv.c b/src/network/lookup_serv.c
+index bf4cba0..a9be0f3 100644
+--- a/src/network/lookup_serv.c
++++ b/src/network/lookup_serv.c
+@@ -52,7 +52,6 @@ int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int pro
+
+ /* Skip past canonical name at beginning of line */
+ for (p=line; *p && !isspace(*p); p++);
+- if (!p) continue;
+
+ port = strtoul(p, &z, 10);
+ if (port > 65535 || z==p) continue;
+diff --git a/src/regex/regcomp.c b/src/regex/regcomp.c
+index d907627..4cdaa1e 100644
+--- a/src/regex/regcomp.c
++++ b/src/regex/regcomp.c
+@@ -34,6 +34,7 @@
+ #include <regex.h>
+ #include <limits.h>
+ #include <stdint.h>
++#include <ctype.h>
+
+ #include "tre.h"
+
+@@ -135,108 +136,88 @@ typedef struct {
+ 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);
+-
+-static tre_ast_node_t *
+-tre_ast_new_literal(tre_mem_t mem, int code_min, int code_max, int position);
+-
+-static tre_ast_node_t *
+-tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max,
+- int minimal);
+-
+-static tre_ast_node_t *
+-tre_ast_new_union(tre_mem_t mem, tre_ast_node_t *left, tre_ast_node_t *right);
+-
+-static tre_ast_node_t *
+-tre_ast_new_catenation(tre_mem_t mem, tre_ast_node_t *left,
+- tre_ast_node_t *right);
+-
+
+ static tre_ast_node_t *
+-tre_ast_new_node(tre_mem_t mem, tre_ast_type_t type, size_t size)
++tre_ast_new_node(tre_mem_t mem, int type, void *obj)
+ {
+- 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;
++ tre_ast_node_t *node = tre_mem_calloc(mem, sizeof *node);
++ if (!node || !obj)
++ return 0;
++ node->obj = obj;
++ 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;
++ tre_ast_node_t *node;
++ tre_literal_t *lit;
++
++ lit = tre_mem_calloc(mem, sizeof *lit);
++ node = tre_ast_new_node(mem, LITERAL, lit);
++ if (!node)
++ return 0;
++ 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,
+- int minimal)
++tre_ast_new_iter(tre_mem_t mem, tre_ast_node_t *arg, int min, int max, int minimal)
+ {
+- 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;
+- iter->minimal = minimal;
+- node->num_submatches = arg->num_submatches;
+-
+- return node;
++ tre_ast_node_t *node;
++ tre_iteration_t *iter;
++
++ iter = tre_mem_calloc(mem, sizeof *iter);
++ node = tre_ast_new_node(mem, ITERATION, iter);
++ if (!node)
++ return 0;
++ iter->arg = arg;
++ iter->min = min;
++ iter->max = max;
++ iter->minimal = minimal;
++ 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;
++ tre_ast_node_t *node;
++ tre_union_t *un;
++
++ if (!left)
++ return right;
++ un = tre_mem_calloc(mem, sizeof *un);
++ node = tre_ast_new_node(mem, UNION, un);
++ if (!node || !right)
++ return 0;
++ un->left = left;
++ un->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_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;
++ tre_ast_node_t *node;
++ tre_catenation_t *cat;
++
++ if (!left)
++ return right;
++ cat = tre_mem_calloc(mem, sizeof *cat);
++ node = tre_ast_new_node(mem, CATENATION, cat);
++ if (!node)
++ return 0;
++ cat->left = left;
++ cat->right = right;
++ node->num_submatches = left->num_submatches + right->num_submatches;
++ return node;
+ }
+
+
+@@ -412,1077 +393,654 @@ define_popf(voidptr, void *)
+
+ /* 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 char *re;
+- /* The first character of the entire regexp. */
+- const char *re_start;
+- /* 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;
+- /* This flag is set if the regexp uses approximate matching. */
+- int have_approx;
+- /* Compilation flags. */
+- int cflags;
+- /* If this flag is set the top-level submatch is not captured. */
+- int nofirstsub;
++ /* 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 parsed node after a parse function returns. */
++ tre_ast_node_t *n;
++ /* Position in the regexp pattern after a parse function returns. */
++ const char *s;
++ /* The first character of the regexp. */
++ const char *re;
++ /* 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;
+ } tre_parse_ctx_t;
+
+-/* Parses a wide character regexp pattern into a syntax tree. This parser
+- handles both syntaxes (BRE and ERE), including the TRE extensions. */
+-static reg_errcode_t
+-tre_parse(tre_parse_ctx_t *ctx);
+-
+-
+-/*
+- This parser is just a simple recursive descent parser for POSIX.2
+- regexps. The parser supports both the obsolete default syntax and
+- the "extended" syntax, and some nonstandard extensions.
+-*/
+-
+-/* Characters with special meanings in regexp syntax. */
+-#define CHAR_PIPE '|'
+-#define CHAR_LPAREN '('
+-#define CHAR_RPAREN ')'
+-#define CHAR_LBRACE '{'
+-#define CHAR_RBRACE '}'
+-#define CHAR_LBRACKET '['
+-#define CHAR_RBRACKET ']'
+-#define CHAR_MINUS '-'
+-#define CHAR_STAR '*'
+-#define CHAR_QUESTIONMARK '?'
+-#define CHAR_PLUS '+'
+-#define CHAR_PERIOD '.'
+-#define CHAR_COLON ':'
+-#define CHAR_EQUAL '='
+-#define CHAR_COMMA ','
+-#define CHAR_CARET '^'
+-#define CHAR_DOLLAR '$'
+-#define CHAR_BACKSLASH '\\'
+-#define CHAR_HASH '#'
+-#define CHAR_TILDE '~'
+-
+-
+ /* Some macros for expanding \w, \s, etc. */
+-static const struct tre_macro_struct {
+- const char c;
+- const char *expansion;
+-} tre_macros[] =
+- { {'t', "\t"}, {'n', "\n"}, {'r', "\r"},
+- {'f', "\f"}, {'a', "\a"}, {'e', "\033"},
+- {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
+- {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"},
+- { 0, NULL }
+- };
+-
++static const struct {
++ char c;
++ const char *expansion;
++} tre_macros[] = {
++ {'t', "\t"}, {'n', "\n"}, {'r', "\r"},
++ {'f', "\f"}, {'a', "\a"}, {'e', "\033"},
++ {'w', "[[:alnum:]_]"}, {'W', "[^[:alnum:]_]"}, {'s', "[[:space:]]"},
++ {'S', "[^[:space:]]"}, {'d', "[[:digit:]]"}, {'D', "[^[:digit:]]"},
++ { 0, 0 }
++};
+
+ /* Expands a macro delimited by `regex' and `regex_end' to `buf', which
+ must have at least `len' items. Sets buf[0] to zero if the there
+ is no match in `tre_macros'. */
+-static const char *
+-tre_expand_macro(const char *regex)
++static const char *tre_expand_macro(const char *s)
+ {
+- int i;
+-
+- if (!*regex)
+- return 0;
+-
+- for (i = 0; tre_macros[i].expansion && tre_macros[i].c != *regex; i++);
+- return tre_macros[i].expansion;
++ int i;
++ for (i = 0; tre_macros[i].c && tre_macros[i].c != *s; i++);
++ return tre_macros[i].expansion;
+ }
+
+-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)
++static int
++tre_compare_lit(const void *a, const void *b)
+ {
+- 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;
+- /* 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(*array) * *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;
++ const tre_literal_t *const *la = a;
++ const tre_literal_t *const *lb = b;
++ /* assumes the range of valid code_min is < INT_MAX */
++ return la[0]->code_min - lb[0]->code_min;
+ }
+
++struct literals {
++ tre_mem_t mem;
++ tre_literal_t **a;
++ int len;
++ int cap;
++};
+
+-static int
+-tre_compare_items(const void *a, const void *b)
++static tre_literal_t *tre_new_lit(struct literals *p)
+ {
+- const tre_ast_node_t *node_a = *(tre_ast_node_t * const *)a;
+- const tre_ast_node_t *node_b = *(tre_ast_node_t * const *)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;
++ tre_literal_t **a;
++ if (p->len >= p->cap) {
++ if (p->cap >= 1<<15)
++ return 0;
++ p->cap *= 2;
++ a = xrealloc(p->a, p->cap * sizeof *p->a);
++ if (!a)
++ return 0;
++ p->a = a;
++ }
++ a = p->a + p->len++;
++ *a = tre_mem_calloc(p->mem, sizeof **a);
++ return *a;
+ }
+
+-/* 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)
++static int add_icase_literals(struct literals *ls, int min, int max)
+ {
+- const char *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)
+- {
+- status = REG_EBRACK;
+- }
+- else if (*re == CHAR_RBRACKET && re > ctx->re)
+- {
+- re++;
+- break;
++ tre_literal_t *lit;
++ int b, e, c;
++ for (c=min; c<=max; ) {
++ /* assumes islower(c) and isupper(c) are exclusive
++ and toupper(c)!=c if islower(c).
++ multiple opposite case characters are not supported */
++ if (tre_islower(c)) {
++ b = e = tre_toupper(c);
++ for (c++, e++; c<=max; c++, e++)
++ if (tre_toupper(c) != e) break;
++ } else if (tre_isupper(c)) {
++ b = e = tre_tolower(c);
++ for (c++, e++; c<=max; c++, e++)
++ if (tre_tolower(c) != e) break;
++ } else {
++ c++;
++ continue;
++ }
++ lit = tre_new_lit(ls);
++ if (!lit)
++ return -1;
++ lit->code_min = b;
++ lit->code_max = e-1;
++ lit->position = -1;
+ }
+- else
+- {
+- tre_cint_t min = 0, max = 0;
+- wchar_t wc;
+- int clen = mbtowc(&wc, re, -1);
++ return 0;
++}
+
+- if (clen<0) clen=1, wc=WEOF;
+
+- class = (tre_ctype_t)0;
+- if (*(re + clen) == CHAR_MINUS && *(re + clen + 1) != CHAR_RBRACKET)
+- {
+- min = wc;
+- re += clen+1;
+- clen = mbtowc(&wc, re, -1);
+- if (clen<0) clen=1, wc=WEOF;
+- max = wc;
+- re += clen;
+- /* XXX - Should use collation order instead of encoding values
+- in character ranges. */
+- if (min > max)
+- status = REG_ERANGE;
+- }
+- else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_PERIOD)
+- status = REG_ECOLLATE;
+- else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_EQUAL)
+- status = REG_ECOLLATE;
+- else if (*re == CHAR_LBRACKET && *(re + 1) == CHAR_COLON)
+- {
+- char tmp_str[64];
+- const char *endptr = re + 2;
+- int len;
+- while (*endptr && *endptr != CHAR_COLON)
+- endptr++;
+- if (*endptr)
+- {
+- len = MIN(endptr - re - 2, 63);
+- strncpy(tmp_str, re + 2, len);
+- tmp_str[len] = '\0';
+- class = tre_ctype(tmp_str);
+- if (!class)
+- status = REG_ECTYPE;
+- re = endptr + 2;
+- }
+- else
+- status = REG_ECTYPE;
+- min = 0;
+- max = TRE_CHAR_MAX;
+- }
+- else
+- {
+- if (*re == CHAR_MINUS && *(re + 1) != CHAR_RBRACKET
+- && ctx->re != re)
+- /* Two ranges are not allowed to share and endpoint. */
+- status = REG_ERANGE;
+- min = max = wc;
+- re += clen;
+- }
++/* Maximum number of character classes in a negated bracket expression. */
++#define MAX_NEG_CLASSES 64
+
+- if (status != REG_OK)
+- break;
++struct neg {
++ int negate;
++ int len;
++ tre_ctype_t a[MAX_NEG_CLASSES];
++};
+
+- 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;
+- }
++// TODO: parse bracket into a set of non-overlapping [lo,hi] ranges
+
+- /* 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)
+- {
+- tre_cint_t cmin, ccurr;
++/*
++bracket grammar:
++Bracket = '[' List ']' | '[^' List ']'
++List = Term | List Term
++Term = Char | Range | Chclass | Eqclass
++Range = Char '-' Char | Char '-' '-'
++Char = Coll | coll_single
++Meta = ']' | '-'
++Coll = '[.' coll_single '.]' | '[.' coll_multi '.]' | '[.' Meta '.]'
++Eqclass = '[=' coll_single '=]' | '[=' coll_multi '=]'
++Chclass = '[:' class ':]'
++
++coll_single is a single char collating element but it can be
++ '-' only at the beginning or end of a List and
++ ']' only at the beginning of a List and
++ '^' anywhere except after the openning '['
++*/
+
+- 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;
++static reg_errcode_t parse_bracket_terms(tre_parse_ctx_t *ctx, const char *s, struct literals *ls, struct neg *neg)
++{
++ const char *start = s;
++ tre_ctype_t class;
++ int min, max;
++ wchar_t wc;
++ int len;
++
++ for (;;) {
++ class = 0;
++ len = mbtowc(&wc, s, -1);
++ if (len <= 0)
++ return *s ? REG_BADPAT : REG_EBRACK;
++ if (*s == ']' && s != start) {
++ ctx->s = s+1;
++ return REG_OK;
++ }
++ if (*s == '-' && s != start && s[1] != ']' &&
++ /* extension: [a-z--@] is accepted as [a-z]|[--@] */
++ (s[1] != '-' || s[2] == ']'))
++ return REG_ERANGE;
++ if (*s == '[' && (s[1] == '.' || s[1] == '='))
++ /* collating symbols and equivalence classes are not supported */
++ return REG_ECOLLATE;
++ if (*s == '[' && s[1] == ':') {
++ char tmp[CHARCLASS_NAME_MAX+1];
++ s += 2;
++ for (len=0; len < CHARCLASS_NAME_MAX && s[len]; len++) {
++ if (s[len] == ':') {
++ memcpy(tmp, s, len);
++ tmp[len] = 0;
++ class = tre_ctype(tmp);
++ break;
++ }
++ }
++ if (!class || s[len+1] != ']')
++ return REG_ECTYPE;
++ min = 0;
++ max = TRE_CHAR_MAX;
++ s += len+2;
++ } else {
++ min = max = wc;
++ s += len;
++ if (*s == '-' && s[1] != ']') {
++ s++;
++ len = mbtowc(&wc, s, -1);
++ max = wc;
++ /* XXX - Should use collation order instead of
++ encoding values in character ranges. */
++ if (len <= 0 || min > max)
++ return REG_ERANGE;
++ s += len;
++ }
++ }
++
++ if (class && neg->negate) {
++ if (neg->len >= MAX_NEG_CLASSES)
++ return REG_ESPACE;
++ neg->a[neg->len++] = class;
++ } else {
++ tre_literal_t *lit = tre_new_lit(ls);
++ if (!lit)
++ return REG_ESPACE;
++ lit->code_min = min;
++ lit->code_max = max;
++ lit->class = class;
++ lit->position = -1;
++
++ /* Add opposite-case codepoints if REG_ICASE is present.
++ It seems that POSIX requires that bracket negation
++ should happen before case-folding, but most practical
++ implementations do it the other way around. Changing
++ the order would need efficient representation of
++ case-fold ranges and bracket range sets even with
++ simple patterns so this is ok for now. */
++ if (ctx->cflags & REG_ICASE && !class)
++ if (add_icase_literals(ls, min, max))
++ return REG_ESPACE;
+ }
+- 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)
++static reg_errcode_t parse_bracket(tre_parse_ctx_t *ctx, const char *s)
+ {
+- 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 == CHAR_CARET)
+- {
+- 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, (unsigned)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;
+-
+- if (negate)
+- {
+- if (min < curr_max)
+- {
+- /* Overlap. */
+- curr_max = MAX(max + 1, curr_max);
+- l = NULL;
+- }
+- else
+- {
+- /* No overlap. */
+- curr_max = min - 1;
+- if (curr_max >= curr_min)
+- {
+- l->code_min = curr_min;
+- l->code_max = curr_max;
++ int i, max, min, negmax, negmin;
++ tre_ast_node_t *node = 0, *n;
++ tre_ctype_t *nc = 0;
++ tre_literal_t *lit;
++ struct literals ls;
++ struct neg neg;
++ reg_errcode_t err;
++
++ ls.mem = ctx->mem;
++ ls.len = 0;
++ ls.cap = 32;
++ ls.a = xmalloc(ls.cap * sizeof *ls.a);
++ if (!ls.a)
++ return REG_ESPACE;
++ neg.len = 0;
++ neg.negate = *s == '^';
++ if (neg.negate)
++ s++;
++
++ err = parse_bracket_terms(ctx, s, &ls, &neg);
++ if (err != REG_OK)
++ goto parse_bracket_done;
++
++ if (neg.negate) {
++ /* Sort the array if we need to negate it. */
++ qsort(ls.a, ls.len, sizeof *ls.a, tre_compare_lit);
++ /* extra lit for the last negated range */
++ lit = tre_new_lit(&ls);
++ if (!lit) {
++ err = REG_ESPACE;
++ goto parse_bracket_done;
+ }
+- else
+- {
+- l = NULL;
++ lit->code_min = TRE_CHAR_MAX+1;
++ lit->code_max = TRE_CHAR_MAX+1;
++ lit->position = -1;
++ /* negated classes */
++ if (neg.len) {
++ nc = tre_mem_alloc(ctx->mem, (neg.len+1)*sizeof *neg.a);
++ if (!nc) {
++ err = REG_ESPACE;
++ goto parse_bracket_done;
++ }
++ memcpy(nc, neg.a, neg.len*sizeof *neg.a);
++ nc[neg.len] = 0;
+ }
+- curr_min = curr_max = max + 1;
+- }
+ }
+
+- if (l != NULL)
+- {
+- int k;
+- 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;
++ /* Build a union of the items in the array, negated if necessary. */
++ negmax = negmin = 0;
++ for (i = 0; i < ls.len; i++) {
++ lit = ls.a[i];
++ min = lit->code_min;
++ max = lit->code_max;
++ if (neg.negate) {
++ if (min <= negmin) {
++ /* Overlap. */
++ negmin = MAX(max + 1, negmin);
++ continue;
++ }
++ negmax = min - 1;
++ lit->code_min = negmin;
++ lit->code_max = negmax;
++ negmin = max + 1;
+ }
+- 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;
+- 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;
++ lit->position = ctx->position;
++ lit->neg_classes = nc;
++ n = tre_ast_new_node(ctx->mem, LITERAL, lit);
++ node = tre_ast_new_union(ctx->mem, node, n);
++ if (!node) {
++ err = 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 = 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;
++parse_bracket_done:
++ xfree(ls.a);
++ ctx->position++;
++ ctx->n = node;
++ return err;
+ }
+
+-
+-/* Parses a positive decimal integer. Returns -1 if the string does not
+- contain a valid number. */
+-static int
+-tre_parse_int(const char **regex)
++static const char *parse_dup_count(const char *s, int *n)
+ {
+- int num = -1;
+- const char *r = *regex;
+- while (*r-'0'<10U)
+- {
+- if (num < 0)
+- num = 0;
+- num = num * 10 + *r - '0';
+- r++;
+- }
+- *regex = r;
+- return num;
++ *n = -1;
++ if (!isdigit(*s))
++ return s;
++ *n = 0;
++ for (;;) {
++ *n = 10 * *n + (*s - '0');
++ s++;
++ if (!isdigit(*s) || *n > RE_DUP_MAX)
++ break;
++ }
++ return s;
+ }
+
+-
+-static reg_errcode_t
+-tre_parse_bound(tre_parse_ctx_t *ctx, tre_ast_node_t **result)
++static reg_errcode_t parse_dup(tre_parse_ctx_t *ctx, const char *s)
+ {
+- int min, max;
+- const char *r = ctx->re;
+- int minimal = 0;
+-
+- /* Parse number (minimum repetition count). */
+- min = -1;
+- if (*r >= '0' && *r <= '9') {
+- min = tre_parse_int(&r);
+- }
+-
+- /* Parse comma and second number (maximum repetition count). */
+- max = min;
+- if (*r == CHAR_COMMA)
+- {
+- r++;
+- max = tre_parse_int(&r);
+- }
+-
+- /* Check that the repeat counts are sane. */
+- if ((max >= 0 && min > max) || max > RE_DUP_MAX)
+- return REG_BADBR;
+-
+- /* Missing }. */
+- if (!*r)
+- return REG_EBRACE;
+-
+- /* Empty contents of {}. */
+- if (r == ctx->re)
+- return REG_BADBR;
+-
+- /* Parse the ending '}' or '\}'.*/
+- if (ctx->cflags & REG_EXTENDED)
+- {
+- if (*r != CHAR_RBRACE)
+- return REG_BADBR;
+- r++;
+- }
+- else
+- {
+- if (*r != CHAR_BACKSLASH || *(r + 1) != CHAR_RBRACE)
+- 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, minimal);
+- if (!*result)
+- return REG_ESPACE;
+- }
+-
+- ctx->re = r;
+- return REG_OK;
++ int min, max;
++
++ s = parse_dup_count(s, &min);
++ if (*s == ',')
++ s = parse_dup_count(s+1, &max);
++ else
++ max = min;
++
++ if (
++ (max < min && max >= 0) ||
++ max > RE_DUP_MAX ||
++ min > RE_DUP_MAX ||
++ min < 0 ||
++ (!(ctx->cflags & REG_EXTENDED) && *s++ != '\\') ||
++ *s++ != '}'
++ )
++ return REG_BADBR;
++
++ if (min == 0 && max == 0)
++ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
++ else
++ ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0);
++ if (!ctx->n)
++ return REG_ESPACE;
++ ctx->s = s;
++ 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)
++static int hexval(unsigned c)
+ {
+- 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;
+- wchar_t wc;
+- int clen;
+-
+- if (!ctx->nofirstsub)
+- {
+- STACK_PUSH(stack, int, ctx->submatch_id);
+- STACK_PUSH(stack, int, PARSE_MARK_FOR_SUBMATCH);
+- ctx->submatch_id++;
+- }
+- STACK_PUSH(stack, int, PARSE_RE);
+- ctx->re_start = ctx->re;
+-
+-
+- /* 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_stack_pop_int(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, int, PARSE_UNION);
+- STACK_PUSHX(stack, int, 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, int, PARSE_CATENATION);
+- STACK_PUSHX(stack, int, 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, int, PARSE_POSTFIX);
+- STACK_PUSHX(stack, int, PARSE_ATOM);
+- break;
+-
+- case PARSE_CATENATION:
+- /* If the expression has not ended, parse another piece. */
+- {
+- tre_char_t c;
+- if (!*ctx->re)
+- break;
+- c = *ctx->re;
+- if (ctx->cflags & REG_EXTENDED && c == CHAR_PIPE)
+- break;
+- if ((ctx->cflags & REG_EXTENDED
+- && c == CHAR_RPAREN && depth > 0)
+- || (!(ctx->cflags & REG_EXTENDED)
+- && (c == CHAR_BACKSLASH
+- && *(ctx->re + 1) == CHAR_RPAREN)))
+- {
+- if (!(ctx->cflags & REG_EXTENDED) && depth == 0)
+- status = REG_EPAREN;
+- depth--;
+- if (!(ctx->cflags & REG_EXTENDED))
+- ctx->re += 2;
+- break;
+- }
+-
+- {
+- /* Default case, left associative concatenation. */
+- STACK_PUSHX(stack, int, PARSE_CATENATION);
+- STACK_PUSHX(stack, voidptr, result);
+- STACK_PUSHX(stack, int, PARSE_POST_CATENATION);
+- STACK_PUSHX(stack, int, PARSE_PIECE);
+- }
+- break;
+- }
+-
+- case PARSE_POST_CATENATION:
+- {
+- tre_ast_node_t *tree = tre_stack_pop_voidptr(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:
+- switch (*ctx->re)
+- {
+- case CHAR_PIPE:
+- STACK_PUSHX(stack, int, PARSE_UNION);
+- STACK_PUSHX(stack, voidptr, result);
+- STACK_PUSHX(stack, int, PARSE_POST_UNION);
+- STACK_PUSHX(stack, int, PARSE_BRANCH);
+- ctx->re++;
+- break;
+-
+- case CHAR_RPAREN:
+- ctx->re++;
+- break;
+-
+- default:
+- break;
+- }
+- break;
+-
+- case PARSE_POST_UNION:
+- {
+- tre_ast_node_t *tmp_node;
+- tre_ast_node_t *tree = tre_stack_pop_voidptr(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. */
+- switch (*ctx->re)
+- {
+- case CHAR_PLUS:
+- case CHAR_QUESTIONMARK:
+- if (!(ctx->cflags & REG_EXTENDED))
+- break;
+- /*FALLTHROUGH*/
+- case CHAR_STAR:
+- {
+- tre_ast_node_t *tmp_node;
+- int minimal = 0;
+- int rep_min = 0;
+- int rep_max = -1;
+-
+- if (*ctx->re == CHAR_PLUS)
+- rep_min = 1;
+- if (*ctx->re == CHAR_QUESTIONMARK)
+- rep_max = 1;
+-
+- ctx->re++;
+- tmp_node = tre_ast_new_iter(ctx->mem, result, rep_min, rep_max,
+- minimal);
+- if (tmp_node == NULL)
+- return REG_ESPACE;
+- result = tmp_node;
+- STACK_PUSHX(stack, int, PARSE_POSTFIX);
+- }
+- break;
+-
+- case CHAR_BACKSLASH:
+- /* "\{" is special without REG_EXTENDED */
+- if (!(ctx->cflags & REG_EXTENDED)
+- && *(ctx->re + 1) == CHAR_LBRACE)
+- {
+- ctx->re++;
+- goto parse_brace;
+- }
+- else
+- break;
+-
+- case CHAR_LBRACE:
+- /* "{" is literal without REG_EXTENDED */
+- if (!(ctx->cflags & REG_EXTENDED))
+- break;
+-
+- parse_brace:
+- ctx->re++;
++ if (c-'0'<10) return c-'0';
++ c |= 32;
++ if (c-'a'<6) return c-'a'+10;
++ return -1;
++}
+
+- status = tre_parse_bound(ctx, &result);
+- if (status != REG_OK)
+- return status;
+- STACK_PUSHX(stack, int, PARSE_POSTFIX);
+- break;
+- }
+- break;
++static reg_errcode_t marksub(tre_parse_ctx_t *ctx, tre_ast_node_t *node, int subid)
++{
++ if (node->submatch_id >= 0) {
++ tre_ast_node_t *n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
++ if (!n)
++ return REG_ESPACE;
++ n = tre_ast_new_catenation(ctx->mem, n, node);
++ if (!n)
++ return REG_ESPACE;
++ n->num_submatches = node->num_submatches;
++ node = n;
++ }
++ node->submatch_id = subid;
++ node->num_submatches++;
++ ctx->n = node;
++ return REG_OK;
++}
+
+- 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. */
++/*
++BRE grammar:
++Regex = Branch | '^' | '$' | '^$' | '^' Branch | Branch '$' | '^' Branch '$'
++Branch = Atom | Branch Atom
++Atom = char | quoted_char | '.' | Bracket | Atom Dup | '\(' Branch '\)' | back_ref
++Dup = '*' | '\{' Count '\}' | '\{' Count ',\}' | '\{' Count ',' Count '\}'
+
+- switch (*ctx->re)
+- {
+- case CHAR_LPAREN: /* parenthesized subexpression */
++(leading ^ and trailing $ in a sub expr may be an anchor or literal as well)
+
+- if (ctx->cflags & REG_EXTENDED)
+- {
+- lparen:
+- depth++;
+- {
+- ctx->re++;
+- /* First parse a whole RE, then mark the resulting tree
+- for submatching. */
+- STACK_PUSHX(stack, int, ctx->submatch_id);
+- STACK_PUSHX(stack, int, PARSE_MARK_FOR_SUBMATCH);
+- STACK_PUSHX(stack, int, PARSE_RE);
+- ctx->submatch_id++;
+- }
+- }
+- else
+- goto parse_literal;
+- break;
++ERE grammar:
++Regex = Branch | Regex '|' Branch
++Branch = Atom | Branch Atom
++Atom = char | quoted_char | '.' | Bracket | Atom Dup | '(' Regex ')' | '^' | '$'
++Dup = '*' | '+' | '?' | '{' Count '}' | '{' Count ',}' | '{' Count ',' Count '}'
+
+- case CHAR_LBRACKET: /* bracket expression */
+- ctx->re++;
+- status = tre_parse_bracket(ctx, &result);
+- if (status != REG_OK)
+- return status;
+- break;
++(a*+?, ^*, $+, \X, {, (|a) are unspecified)
++*/
+
+- case CHAR_BACKSLASH:
+- /* If this is "\(" or "\)" chew off the backslash and
+- try again. */
+- if (!(ctx->cflags & REG_EXTENDED) && *(ctx->re + 1) == CHAR_LPAREN)
+- {
+- ctx->re++;
+- goto lparen;
+- }
+- if (!(ctx->cflags & REG_EXTENDED) && *(ctx->re + 1) == CHAR_RPAREN)
+- {
+- goto empty_atom;
++static reg_errcode_t parse_atom(tre_parse_ctx_t *ctx, const char *s)
++{
++ int len, ere = ctx->cflags & REG_EXTENDED;
++ const char *p;
++ tre_ast_node_t *node;
++ wchar_t wc;
++ switch (*s) {
++ case '[':
++ return parse_bracket(ctx, s+1);
++ case '\\':
++ p = tre_expand_macro(s+1);
++ if (p) {
++ /* assume \X expansion is a single atom */
++ reg_errcode_t err = parse_atom(ctx, p);
++ ctx->s = s+2;
++ return err;
+ }
+-
+- /* If a macro is used, parse the expanded macro recursively. */
+- {
+- const char *buf = tre_expand_macro(ctx->re + 1);
+- if (buf)
+- {
+- tre_parse_ctx_t subctx;
+- memcpy(&subctx, ctx, sizeof(subctx));
+- subctx.re = buf;
+- subctx.nofirstsub = 1;
+- status = tre_parse(&subctx);
+- if (status != REG_OK)
+- return status;
+- ctx->re += 2;
+- ctx->position = subctx.position;
+- result = subctx.result;
+- break;
+- }
+- }
+-
+- if (!ctx->re[1])
+- /* Trailing backslash. */
+- return REG_EESCAPE;
+-
+- ctx->re++;
+- switch (*ctx->re)
+- {
++ /* extensions: \b, \B, \<, \>, \xHH \x{HHHH} */
++ switch (*++s) {
++ case 0:
++ return REG_EESCAPE;
+ case 'b':
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_WB, -1);
+- ctx->re++;
+- break;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB, -1);
++ break;
+ case 'B':
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_WB_NEG, -1);
+- ctx->re++;
+- break;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_WB_NEG, -1);
++ break;
+ case '<':
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_BOW, -1);
+- ctx->re++;
+- break;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOW, -1);
++ break;
+ case '>':
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_EOW, -1);
+- ctx->re++;
+- break;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOW, -1);
++ break;
+ case 'x':
+- ctx->re++;
+- if (ctx->re[0] != CHAR_LBRACE)
+- {
+- /* 8 bit hex char. */
+- char tmp[3] = {0, 0, 0};
+- long val;
+-
+- if (tre_isxdigit(ctx->re[0]))
+- {
+- tmp[0] = (char)ctx->re[0];
+- ctx->re++;
++ s++;
++ int i, v = 0, c;
++ len = 2;
++ if (*s == '{') {
++ len = 8;
++ s++;
+ }
+- if (tre_isxdigit(ctx->re[0]))
+- {
+- tmp[1] = (char)ctx->re[0];
+- ctx->re++;
++ for (i=0; i<len && v<0x110000; i++) {
++ c = hexval(s[i]);
++ if (c < 0) break;
++ v = 16*v + c;
+ }
+- val = strtol(tmp, NULL, 16);
+- result = tre_ast_new_literal(ctx->mem, (int)val,
+- (int)val, ctx->position);
+- ctx->position++;
+- break;
+- }
+- else if (*ctx->re)
+- {
+- /* Wide char. */
+- char tmp[32];
+- long val;
+- int i = 0;
+- ctx->re++;
+- while (*ctx->re && i < sizeof tmp)
+- {
+- if (ctx->re[0] == CHAR_RBRACE)
+- break;
+- if (tre_isxdigit(ctx->re[0]))
+- {
+- tmp[i] = (char)ctx->re[0];
+- i++;
+- ctx->re++;
+- continue;
+- }
+- return REG_EBRACE;
++ s += i;
++ if (len == 8) {
++ if (*s != '}')
++ return REG_EBRACE;
++ s++;
+ }
+- ctx->re++;
+- tmp[i] = 0;
+- val = strtol(tmp, NULL, 16);
+- result = tre_ast_new_literal(ctx->mem, (int)val, (int)val,
+- ctx->position);
+- ctx->position++;
+- break;
+- }
+- /*FALLTHROUGH*/
+-
++ node = tre_ast_new_literal(ctx->mem, v, v, ctx->position);
++ ctx->position++;
++ s--;
++ break;
+ default:
+- if (tre_isdigit(*ctx->re))
+- {
+- /* Back reference. */
+- int val = *ctx->re - '0';
+- 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. */
+- 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 CHAR_PERIOD: /* the any-symbol */
+- 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++;
++ if (isdigit(*s)) {
++ /* back reference */
++ int val = *s - '0';
++ node = tre_ast_new_literal(ctx->mem, BACKREF, val, ctx->position);
++ ctx->max_backref = MAX(val, ctx->max_backref);
++ } else {
++ /* extension: accept unknown escaped char
++ as a literal */
++ node = tre_ast_new_literal(ctx->mem, *s, *s, ctx->position);
++ }
++ ctx->position++;
+ }
+- ctx->re++;
+- break;
+-
+- case CHAR_CARET: /* beginning of line assertion */
+- /* '^' has a special meaning everywhere in EREs, and at
+- beginning of BRE. */
+- if (ctx->cflags & REG_EXTENDED
+- || ctx->re == ctx->re_start)
+- {
+- if (!(ctx->cflags & REG_EXTENDED))
+- STACK_PUSHX(stack, int, PARSE_CATENATION);
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_BOL, -1);
+- if (result == NULL)
+- return REG_ESPACE;
+- ctx->re++;
++ s++;
++ break;
++ case '.':
++ if (ctx->cflags & REG_NEWLINE) {
++ tre_ast_node_t *tmp1, *tmp2;
++ tmp1 = tre_ast_new_literal(ctx->mem, 0, '\n'-1, ctx->position++);
++ tmp2 = tre_ast_new_literal(ctx->mem, '\n'+1, TRE_CHAR_MAX, ctx->position++);
++ if (tmp1 && tmp2)
++ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
++ else
++ node = 0;
++ } else {
++ node = tre_ast_new_literal(ctx->mem, 0, TRE_CHAR_MAX, ctx->position++);
+ }
+- else
+- goto parse_literal;
+- break;
+-
+- case CHAR_DOLLAR: /* end of line assertion. */
+- /* '$' is special everywhere in EREs, and in the end of the
+- string in BREs. */
+- if (ctx->cflags & REG_EXTENDED
+- || !*(ctx->re + 1))
+- {
+- result = tre_ast_new_literal(ctx->mem, ASSERTION,
+- ASSERT_AT_EOL, -1);
+- if (result == NULL)
+- return REG_ESPACE;
+- ctx->re++;
++ s++;
++ break;
++ case '^':
++ /* '^' has a special meaning everywhere in EREs, and at beginning of BRE. */
++ if (!ere && s != ctx->re)
++ goto parse_literal;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_BOL, -1);
++ s++;
++ break;
++ case '$':
++ /* '$' is special everywhere in EREs, and in the end of the string in BREs. */
++ if (!ere && s[1])
++ goto parse_literal;
++ node = tre_ast_new_literal(ctx->mem, ASSERTION, ASSERT_AT_EOL, -1);
++ s++;
++ break;
++ case '*':
++ case '|':
++ case '{':
++ case '+':
++ case '?':
++ if (!ere)
++ goto parse_literal;
++ case 0:
++ node = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
++ break;
++ default:
++parse_literal:
++ len = mbtowc(&wc, s, -1);
++ if (len < 0)
++ return REG_BADPAT;
++ if (ctx->cflags & REG_ICASE && (tre_isupper(wc) || tre_islower(wc))) {
++ tre_ast_node_t *tmp1, *tmp2;
++ /* multiple opposite case characters are not supported */
++ tmp1 = tre_ast_new_literal(ctx->mem, tre_toupper(wc), tre_toupper(wc), ctx->position);
++ tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc), tre_tolower(wc), ctx->position);
++ if (tmp1 && tmp2)
++ node = tre_ast_new_union(ctx->mem, tmp1, tmp2);
++ else
++ node = 0;
++ } else {
++ node = tre_ast_new_literal(ctx->mem, wc, wc, ctx->position);
+ }
+- else
+- goto parse_literal;
+- break;
+-
+- case CHAR_RPAREN:
+- if (!depth)
+- goto parse_literal;
+- case CHAR_STAR:
+- case CHAR_PIPE:
+- case CHAR_LBRACE:
+- case CHAR_PLUS:
+- case CHAR_QUESTIONMARK:
+- if (!(ctx->cflags & REG_EXTENDED))
+- goto parse_literal;
+-
+- case 0:
+- empty_atom:
+- result = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
+- if (!result)
++ ctx->position++;
++ s += len;
++ break;
++ }
++ if (!node)
+ return REG_ESPACE;
+- break;
++ ctx->n = node;
++ ctx->s = s;
++ return REG_OK;
++}
+
+- default:
+- parse_literal:
++#define PUSHPTR(err, s, v) do { \
++ if ((err = tre_stack_push_voidptr(s, v)) != REG_OK) \
++ return err; \
++} while(0)
+
+- clen = mbtowc(&wc, ctx->re, -1);
+- if (clen<0) clen=1, wc=WEOF;
++#define PUSHINT(err, s, v) do { \
++ if ((err = tre_stack_push_int(s, v)) != REG_OK) \
++ return err; \
++} while(0)
+
+- /* 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(wc) || tre_islower(wc)))
+- {
+- 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(wc),
+- tre_toupper(wc),
+- ctx->position);
+- if (!tmp1)
+- return REG_ESPACE;
+- tmp2 = tre_ast_new_literal(ctx->mem, tre_tolower(wc),
+- tre_tolower(wc),
+- ctx->position);
+- if (!tmp2)
+- return REG_ESPACE;
+- result = tre_ast_new_union(ctx->mem, tmp1, tmp2);
+- if (!result)
+- return REG_ESPACE;
++static reg_errcode_t tre_parse(tre_parse_ctx_t *ctx)
++{
++ tre_ast_node_t *nbranch=0, *nunion=0;
++ int ere = ctx->cflags & REG_EXTENDED;
++ const char *s = ctx->re;
++ int subid = 0;
++ int depth = 0;
++ reg_errcode_t err;
++ tre_stack_t *stack = ctx->stack;
++
++ PUSHINT(err, stack, subid++);
++ for (;;) {
++ if ((!ere && *s == '\\' && s[1] == '(') ||
++ (ere && *s == '(')) {
++ PUSHPTR(err, stack, nunion);
++ PUSHPTR(err, stack, nbranch);
++ PUSHINT(err, stack, subid++);
++ s++;
++ if (!ere)
++ s++;
++ depth++;
++ nbranch = nunion = 0;
++ continue;
+ }
+- else
+- {
+- result = tre_ast_new_literal(ctx->mem, wc, wc,
+- ctx->position);
+- if (!result)
+- return REG_ESPACE;
++ if ((!ere && *s == '\\' && s[1] == ')') ||
++ (ere && *s == ')' && depth)) {
++ ctx->n = tre_ast_new_literal(ctx->mem, EMPTY, -1, -1);
++ if (!ctx->n)
++ return REG_ESPACE;
++ } else {
++ err = parse_atom(ctx, s);
++ if (err != REG_OK)
++ return err;
++ s = ctx->s;
+ }
+- ctx->position++;
+- ctx->re += clen;
+- break;
+- }
+- break;
+-
+- case PARSE_MARK_FOR_SUBMATCH:
+- {
+- int submatch_id = tre_stack_pop_int(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 = tre_stack_pop_int(stack);
+- break;
++ parse_iter:
++ /* extension: repetitions are accepted after an empty node
++ eg. (+), ^*, a$?, a|{2} */
++ switch (*s) {
++ case '+':
++ case '?':
++ if (!ere)
++ break;
++ /* fallthrough */
++ case '*':;
++ int min=0, max=-1;
++ if (*s == '+')
++ min = 1;
++ if (*s == '?')
++ max = 1;
++ s++;
++ ctx->n = tre_ast_new_iter(ctx->mem, ctx->n, min, max, 0);
++ if (!ctx->n)
++ return REG_ESPACE;
++ /* extension: multiple consecutive *+?{,} is unspecified,
++ but (a+)+ has to be supported so accepting a++ makes
++ sense, note however that the RE_DUP_MAX limit can be
++ circumvented: (a{255}){255} uses a lot of memory.. */
++ goto parse_iter;
++ case '\\':
++ if (ere || s[1] != '{')
++ break;
++ s++;
++ goto parse_brace;
++ case '{':
++ if (!ere)
++ break;
++ parse_brace:
++ err = parse_dup(ctx, s+1);
++ if (err != REG_OK)
++ return err;
++ s = ctx->s;
++ goto parse_iter;
++ }
+
+- default:
+- assert(0);
+- break;
++ nbranch = tre_ast_new_catenation(ctx->mem, nbranch, ctx->n);
++ if ((ere && *s == '|') ||
++ (ere && *s == ')' && depth) ||
++ (!ere && *s == '\\' && s[1] == ')') ||
++ !*s) {
++ /* extension: empty branch is unspecified (), (|a), (a|)
++ here they are not rejected but match on empty string */
++ int c = *s;
++ nunion = tre_ast_new_union(ctx->mem, nunion, nbranch);
++ nbranch = 0;
++ if (c != '|') {
++ if (c == '\\') {
++ if (!depth) return REG_EPAREN;
++ s+=2;
++ } else if (c == ')')
++ s++;
++ depth--;
++ err = marksub(ctx, nunion, tre_stack_pop_int(stack));
++ if (err != REG_OK)
++ return err;
++ if (!c && depth<0) {
++ ctx->submatch_id = subid;
++ return REG_OK;
++ }
++ if (!c || depth<0)
++ return REG_EPAREN;
++ nbranch = tre_stack_pop_voidptr(stack);
++ nunion = tre_stack_pop_voidptr(stack);
++ goto parse_iter;
++ }
++ s++;
++ }
+ }
+- }
+-
+- /* Check for missing closing parentheses. */
+- if (depth > 0)
+- return REG_EPAREN;
+-
+- if (status == REG_OK)
+- ctx->result = result;
+-
+- return status;
+ }
+
+
+-
+ /***********************************************************************
+ from tre-compile.c
+ ***********************************************************************/
+@@ -3122,12 +2680,7 @@ regcomp(regex_t *restrict preg, const char *restrict regex, int cflags)
+ if (errcode != REG_OK)
+ ERROR_EXIT(errcode);
+ preg->re_nsub = parse_ctx.submatch_id - 1;
+- tree = parse_ctx.result;
+-
+- /* Back references and approximate matching cannot currently be used
+- in the same regexp. */
+- if (parse_ctx.max_backref >= 0 && parse_ctx.have_approx)
+- ERROR_EXIT(REG_BADPAT);
++ tree = parse_ctx.n;
+
+ #ifdef TRE_DEBUG
+ tre_ast_print(tree);
+@@ -3142,7 +2695,7 @@ regcomp(regex_t *restrict preg, const char *restrict regex, int cflags)
+ if (tnfa == NULL)
+ ERROR_EXIT(REG_ESPACE);
+ tnfa->have_backrefs = parse_ctx.max_backref >= 0;
+- tnfa->have_approx = parse_ctx.have_approx;
++ tnfa->have_approx = 0;
+ tnfa->num_submatches = parse_ctx.submatch_id;
+
+ /* Set up tags for submatch addressing. If REG_NOSUB is set and the
+diff --git a/src/regex/regexec.c b/src/regex/regexec.c
+index 2e35b83..16c5d0a 100644
+--- a/src/regex/regexec.c
++++ b/src/regex/regexec.c
+@@ -52,7 +52,7 @@ tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags,
+ #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; \
++ if (pos_add_next < 0) { ret = REG_NOMATCH; goto error_exit; } \
+ else pos_add_next++; \
+ } \
+ str_byte += pos_add_next; \
+@@ -181,6 +181,7 @@ tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string,
+ int reg_notbol = eflags & REG_NOTBOL;
+ int reg_noteol = eflags & REG_NOTEOL;
+ int reg_newline = tnfa->cflags & REG_NEWLINE;
++ reg_errcode_t ret;
+
+ char *buf;
+ tre_tnfa_transition_t *trans_i;
+@@ -439,11 +440,11 @@ tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string,
+ reach_next_i->state = NULL;
+ }
+
+- if (buf)
+- xfree(buf);
+-
+ *match_end_ofs = match_eo;
+- return match_eo >= 0 ? REG_OK : REG_NOMATCH;
++ ret = match_eo >= 0 ? REG_OK : REG_NOMATCH;
++error_exit:
++ xfree(buf);
++ return ret;
+ }
+
+
+diff --git a/src/stdio/ftrylockfile.c b/src/stdio/ftrylockfile.c
+index 6f9a4b8..eb13c83 100644
+--- a/src/stdio/ftrylockfile.c
++++ b/src/stdio/ftrylockfile.c
+@@ -34,6 +34,7 @@ int ftrylockfile(FILE *f)
+ f->lockcount = 1;
+ f->prev_locked = 0;
+ f->next_locked = self->stdio_locks;
++ if (f->next_locked) f->next_locked->prev_locked = f;
+ self->stdio_locks = f;
+ return 0;
+ }
+diff --git a/src/thread/__timedwait.c b/src/thread/__timedwait.c
+index d6f1233..c9ec70c 100644
+--- a/src/thread/__timedwait.c
++++ b/src/thread/__timedwait.c
+@@ -4,6 +4,9 @@
+ #include "futex.h"
+ #include "syscall.h"
+
++int __pthread_setcancelstate(int, int *);
++int __clock_gettime(clockid_t, struct timespec *);
++
+ int __timedwait(volatile int *addr, int val,
+ clockid_t clk, const struct timespec *at,
+ void (*cleanup)(void *), void *arg, int priv)
+@@ -15,7 +18,7 @@ int __timedwait(volatile int *addr, int val,
+
+ if (at) {
+ if (at->tv_nsec >= 1000000000UL) return EINVAL;
+- if (clock_gettime(clk, &to)) return EINVAL;
++ if (__clock_gettime(clk, &to)) return EINVAL;
+ to.tv_sec = at->tv_sec - to.tv_sec;
+ if ((to.tv_nsec = at->tv_nsec - to.tv_nsec) < 0) {
+ to.tv_sec--;
+@@ -25,7 +28,7 @@ int __timedwait(volatile int *addr, int val,
+ top = &to;
+ }
+
+- if (!cleanup) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
++ if (!cleanup) __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
+ pthread_cleanup_push(cleanup, arg);
+
+ r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top);
+@@ -33,7 +36,7 @@ int __timedwait(volatile int *addr, int val,
+ if (r != EINTR && r != ETIMEDOUT) r = 0;
+
+ pthread_cleanup_pop(0);
+- if (!cleanup) pthread_setcancelstate(cs, 0);
++ if (!cleanup) __pthread_setcancelstate(cs, 0);
+
+ return r;
+ }
+diff --git a/src/thread/call_once.c b/src/thread/call_once.c
+new file mode 100644
+index 0000000..a7bc935
+--- /dev/null
++++ b/src/thread/call_once.c
+@@ -0,0 +1,8 @@
++#include <threads.h>
++
++int __pthread_once(once_flag *, void (*)(void));
++
++void call_once(once_flag *flag, void (*func)(void))
++{
++ __pthread_once(flag, func);
++}
+diff --git a/src/thread/cnd_broadcast.c b/src/thread/cnd_broadcast.c
+new file mode 100644
+index 0000000..85d4d3e
+--- /dev/null
++++ b/src/thread/cnd_broadcast.c
+@@ -0,0 +1,10 @@
++#include <threads.h>
++
++int __private_cond_signal(cnd_t *, int);
++
++int cnd_broadcast(cnd_t *c)
++{
++ /* This internal function never fails, and always returns zero,
++ * which matches the value thrd_success is defined with. */
++ return __private_cond_signal(c, -1);
++}
+diff --git a/src/thread/cnd_destroy.c b/src/thread/cnd_destroy.c
+new file mode 100644
+index 0000000..453c90b
+--- /dev/null
++++ b/src/thread/cnd_destroy.c
+@@ -0,0 +1,6 @@
++#include <threads.h>
++
++void cnd_destroy(cnd_t *c)
++{
++ /* For private cv this is a no-op */
++}
+diff --git a/src/thread/cnd_init.c b/src/thread/cnd_init.c
+new file mode 100644
+index 0000000..18c5085
+--- /dev/null
++++ b/src/thread/cnd_init.c
+@@ -0,0 +1,7 @@
++#include <threads.h>
++
++int cnd_init(cnd_t *c)
++{
++ *c = (cnd_t){ 0 };
++ return thrd_success;
++}
+diff --git a/src/thread/cnd_signal.c b/src/thread/cnd_signal.c
+new file mode 100644
+index 0000000..1211260
+--- /dev/null
++++ b/src/thread/cnd_signal.c
+@@ -0,0 +1,10 @@
++#include <threads.h>
++
++int __private_cond_signal(cnd_t *, int);
++
++int cnd_signal(cnd_t *c)
++{
++ /* This internal function never fails, and always returns zero,
++ * which matches the value thrd_success is defined with. */
++ return __private_cond_signal(c, 1);
++}
+diff --git a/src/thread/cnd_timedwait.c b/src/thread/cnd_timedwait.c
+new file mode 100644
+index 0000000..5997679
+--- /dev/null
++++ b/src/thread/cnd_timedwait.c
+@@ -0,0 +1,15 @@
++#include <threads.h>
++#include <errno.h>
++
++int __pthread_cond_timedwait(cnd_t *restrict, mtx_t *restrict, const struct timespec *restrict);
++
++int cnd_timedwait(cnd_t *restrict c, mtx_t *restrict m, const struct timespec *restrict ts)
++{
++ int ret = __pthread_cond_timedwait(c, m, ts);
++ switch (ret) {
++ /* May also return EINVAL or EPERM. */
++ default: return thrd_error;
++ case 0: return thrd_success;
++ case ETIMEDOUT: return thrd_timedout;
++ }
++}
+diff --git a/src/thread/cnd_wait.c b/src/thread/cnd_wait.c
+new file mode 100644
+index 0000000..602796f
+--- /dev/null
++++ b/src/thread/cnd_wait.c
+@@ -0,0 +1,9 @@
++#include <threads.h>
++
++int cnd_wait(cnd_t *c, mtx_t *m)
++{
++ /* Calling cnd_timedwait with a null pointer is an extension.
++ * It is convenient here to avoid duplication of the logic
++ * for return values. */
++ return cnd_timedwait(c, m, 0);
++}
+diff --git a/src/thread/mtx_destroy.c b/src/thread/mtx_destroy.c
+new file mode 100644
+index 0000000..40a0899
+--- /dev/null
++++ b/src/thread/mtx_destroy.c
+@@ -0,0 +1,5 @@
++#include <threads.h>
++
++void mtx_destroy(mtx_t *mtx)
++{
++}
+diff --git a/src/thread/mtx_init.c b/src/thread/mtx_init.c
+new file mode 100644
+index 0000000..4826f76
+--- /dev/null
++++ b/src/thread/mtx_init.c
+@@ -0,0 +1,10 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++int mtx_init(mtx_t *m, int type)
++{
++ *m = (mtx_t){
++ ._m_type = ((type&mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE : PTHREAD_MUTEX_NORMAL),
++ };
++ return thrd_success;
++}
+diff --git a/src/thread/mtx_lock.c b/src/thread/mtx_lock.c
+new file mode 100644
+index 0000000..5c2415c
+--- /dev/null
++++ b/src/thread/mtx_lock.c
+@@ -0,0 +1,12 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++int mtx_lock(mtx_t *m)
++{
++ if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY))
++ return thrd_success;
++ /* Calling mtx_timedlock with a null pointer is an extension.
++ * It is convenient, here to avoid duplication of the logic
++ * for return values. */
++ return mtx_timedlock(m, 0);
++}
+diff --git a/src/thread/mtx_timedlock.c b/src/thread/mtx_timedlock.c
+new file mode 100644
+index 0000000..bcc152c
+--- /dev/null
++++ b/src/thread/mtx_timedlock.c
+@@ -0,0 +1,14 @@
++#include <threads.h>
++#include <errno.h>
++
++int __pthread_mutex_timedlock(mtx_t *restrict, const struct timespec *restrict);
++
++int mtx_timedlock(mtx_t *restrict m, const struct timespec *restrict ts)
++{
++ int ret = __pthread_mutex_timedlock(m, ts);
++ switch (ret) {
++ default: return thrd_error;
++ case 0: return thrd_success;
++ case ETIMEDOUT: return thrd_timedout;
++ }
++}
+diff --git a/src/thread/mtx_trylock.c b/src/thread/mtx_trylock.c
+new file mode 100644
+index 0000000..61e7694
+--- /dev/null
++++ b/src/thread/mtx_trylock.c
+@@ -0,0 +1,17 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++int __pthread_mutex_trylock(mtx_t *);
++
++int mtx_trylock(mtx_t *m)
++{
++ if (m->_m_type == PTHREAD_MUTEX_NORMAL)
++ return (a_cas(&m->_m_lock, 0, EBUSY) & EBUSY) ? thrd_busy : thrd_success;
++
++ int ret = __pthread_mutex_trylock(m);
++ switch (ret) {
++ default: return thrd_error;
++ case 0: return thrd_success;
++ case EBUSY: return thrd_busy;
++ }
++}
+diff --git a/src/thread/mtx_unlock.c b/src/thread/mtx_unlock.c
+new file mode 100644
+index 0000000..5033ace
+--- /dev/null
++++ b/src/thread/mtx_unlock.c
+@@ -0,0 +1,11 @@
++#include <threads.h>
++
++int __pthread_mutex_unlock(mtx_t *);
++
++int mtx_unlock(mtx_t *mtx)
++{
++ /* The only cases where pthread_mutex_unlock can return an
++ * error are undefined behavior for C11 mtx_unlock, so we can
++ * assume it does not return an error and simply tail call. */
++ return __pthread_mutex_unlock(mtx);
++}
+diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c
+index 2d192b0..0a80d30 100644
+--- a/src/thread/pthread_cond_timedwait.c
++++ b/src/thread/pthread_cond_timedwait.c
+@@ -1,5 +1,9 @@
+ #include "pthread_impl.h"
+
++void __pthread_testcancel(void);
++int __pthread_mutex_lock(pthread_mutex_t *);
++int __pthread_mutex_unlock(pthread_mutex_t *);
++
+ /*
+ * struct waiter
+ *
+@@ -119,7 +123,7 @@ static void unwait(void *arg)
+ }
+ }
+
+-int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
++int __pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m, const struct timespec *restrict ts)
+ {
+ struct waiter node = { .cond = c, .mutex = m };
+ int e, seq, *fut, clock = c->_c_clock;
+@@ -130,7 +134,7 @@ int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict
+ if (ts && ts->tv_nsec >= 1000000000UL)
+ return EINVAL;
+
+- pthread_testcancel();
++ __pthread_testcancel();
+
+ if (c->_c_shared) {
+ node.shared = 1;
+@@ -151,7 +155,7 @@ int pthread_cond_timedwait(pthread_cond_t *restrict c, pthread_mutex_t *restrict
+ unlock(&c->_c_lock);
+ }
+
+- pthread_mutex_unlock(m);
++ __pthread_mutex_unlock(m);
+
+ do e = __timedwait(fut, seq, clock, ts, unwait, &node, !node.shared);
+ while (*fut==seq && (!e || e==EINTR));
+@@ -197,3 +201,5 @@ int __private_cond_signal(pthread_cond_t *c, int n)
+
+ return 0;
+ }
++
++weak_alias(__pthread_cond_timedwait, pthread_cond_timedwait);
+diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c
+index e441bda..1a47ed1 100644
+--- a/src/thread/pthread_create.c
++++ b/src/thread/pthread_create.c
+@@ -5,6 +5,10 @@
+ #include <sys/mman.h>
+ #include <string.h>
+
++void *__mmap(void *, size_t, int, int, int, off_t);
++int __munmap(void *, size_t);
++int __mprotect(void *, size_t, int);
++
+ static void dummy_0()
+ {
+ }
+@@ -14,7 +18,7 @@ weak_alias(dummy_0, __pthread_tsd_run_dtors);
+ weak_alias(dummy_0, __do_private_robust_list);
+ weak_alias(dummy_0, __do_orphaned_stdio_locks);
+
+-_Noreturn void pthread_exit(void *result)
++_Noreturn void __pthread_exit(void *result)
+ {
+ pthread_t self = __pthread_self();
+ sigset_t set;
+@@ -115,7 +119,15 @@ static int start(void *p)
+ if (self->unblock_cancel)
+ __syscall(SYS_rt_sigprocmask, SIG_UNBLOCK,
+ SIGPT_SET, 0, _NSIG/8);
+- pthread_exit(self->start(self->start_arg));
++ __pthread_exit(self->start(self->start_arg));
++ return 0;
++}
++
++static int start_c11(void *p)
++{
++ pthread_t self = p;
++ int (*start)(void*) = (int(*)(void*)) self->start;
++ __pthread_exit((void *)(uintptr_t)start(self->start_arg));
+ return 0;
+ }
+
+@@ -139,9 +151,9 @@ static void init_file_lock(FILE *f)
+
+ void *__copy_tls(unsigned char *);
+
+-int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
++int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp, void *(*entry)(void *), void *restrict arg)
+ {
+- int ret;
++ int ret, c11 = (attrp == __ATTRP_C11_THREAD);
+ size_t size, guard;
+ struct pthread *self, *new;
+ unsigned char *map = 0, *stack = 0, *tsd = 0, *stack_limit;
+@@ -163,7 +175,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
+ self->tsd = (void **)__pthread_tsd_main;
+ libc.threaded = 1;
+ }
+- if (attrp) attr = *attrp;
++ if (attrp && !c11) attr = *attrp;
+
+ __acquire_ptc();
+
+@@ -191,14 +203,14 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
+
+ if (!tsd) {
+ if (guard) {
+- map = mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
++ map = __mmap(0, size, PROT_NONE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto fail;
+- if (mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
+- munmap(map, size);
++ if (__mprotect(map+guard, size-guard, PROT_READ|PROT_WRITE)) {
++ __munmap(map, size);
+ goto fail;
+ }
+ } else {
+- map = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
++ map = __mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (map == MAP_FAILED) goto fail;
+ }
+ tsd = map + size - __pthread_tsd_size;
+@@ -230,7 +242,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
+ new->canary = self->canary;
+
+ a_inc(&libc.threads_minus_1);
+- ret = __clone(start, stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
++ ret = __clone((c11 ? start_c11 : start), stack, flags, new, &new->tid, TP_ADJ(new), &new->tid);
+
+ __release_ptc();
+
+@@ -240,7 +252,7 @@ int pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict attrp
+
+ if (ret < 0) {
+ a_dec(&libc.threads_minus_1);
+- if (map) munmap(map, size);
++ if (map) __munmap(map, size);
+ return EAGAIN;
+ }
+
+@@ -258,3 +270,6 @@ fail:
+ __release_ptc();
+ return EAGAIN;
+ }
++
++weak_alias(__pthread_exit, pthread_exit);
++weak_alias(__pthread_create, pthread_create);
+diff --git a/src/thread/pthread_detach.c b/src/thread/pthread_detach.c
+index 651c38e..ed77f74 100644
+--- a/src/thread/pthread_detach.c
++++ b/src/thread/pthread_detach.c
+@@ -1,11 +1,17 @@
+ #include "pthread_impl.h"
++#include <threads.h>
+
+-int pthread_detach(pthread_t t)
++int __pthread_join(pthread_t, void **);
++
++static int __pthread_detach(pthread_t t)
+ {
+ /* Cannot detach a thread that's already exiting */
+ if (a_swap(t->exitlock, 1))
+- return pthread_join(t, 0);
++ return __pthread_join(t, 0);
+ t->detached = 2;
+ __unlock(t->exitlock);
+ return 0;
+ }
++
++weak_alias(__pthread_detach, pthread_detach);
++weak_alias(__pthread_detach, thrd_detach);
+diff --git a/src/thread/pthread_equal.c b/src/thread/pthread_equal.c
+index 3e3df4f..7c31482 100644
+--- a/src/thread/pthread_equal.c
++++ b/src/thread/pthread_equal.c
+@@ -1,6 +1,11 @@
+ #include <pthread.h>
++#include <threads.h>
++#include "libc.h"
+
+-int (pthread_equal)(pthread_t a, pthread_t b)
++static int __pthread_equal(pthread_t a, pthread_t b)
+ {
+ return a==b;
+ }
++
++weak_alias(__pthread_equal, pthread_equal);
++weak_alias(__pthread_equal, thrd_equal);
+diff --git a/src/thread/pthread_getspecific.c b/src/thread/pthread_getspecific.c
+index b2a282c..d9342a5 100644
+--- a/src/thread/pthread_getspecific.c
++++ b/src/thread/pthread_getspecific.c
+@@ -1,7 +1,11 @@
+ #include "pthread_impl.h"
++#include <threads.h>
+
+-void *pthread_getspecific(pthread_key_t k)
++static void *__pthread_getspecific(pthread_key_t k)
+ {
+ struct pthread *self = __pthread_self();
+ return self->tsd[k];
+ }
++
++weak_alias(__pthread_getspecific, pthread_getspecific);
++weak_alias(__pthread_getspecific, tss_get);
+diff --git a/src/thread/pthread_join.c b/src/thread/pthread_join.c
+index abd2d66..19e6b54 100644
+--- a/src/thread/pthread_join.c
++++ b/src/thread/pthread_join.c
+@@ -1,16 +1,20 @@
+ #include "pthread_impl.h"
+ #include <sys/mman.h>
+
++int __munmap(void *, size_t);
++
+ static void dummy(void *p)
+ {
+ }
+
+-int pthread_join(pthread_t t, void **res)
++int __pthread_join(pthread_t t, void **res)
+ {
+ int tmp;
+ pthread_testcancel();
+ while ((tmp = t->tid)) __timedwait(&t->tid, tmp, 0, 0, dummy, 0, 0);
+ if (res) *res = t->result;
+- if (t->map_base) munmap(t->map_base, t->map_size);
++ if (t->map_base) __munmap(t->map_base, t->map_size);
+ return 0;
+ }
++
++weak_alias(__pthread_join, pthread_join);
+diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c
+index a9187f7..bfcd597 100644
+--- a/src/thread/pthread_key_create.c
++++ b/src/thread/pthread_key_create.c
+@@ -9,7 +9,7 @@ static void nodtor(void *dummy)
+ {
+ }
+
+-int pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
++int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
+ {
+ unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX;
+ unsigned j = i;
+@@ -31,7 +31,7 @@ int pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
+ return EAGAIN;
+ }
+
+-int pthread_key_delete(pthread_key_t k)
++int __pthread_key_delete(pthread_key_t k)
+ {
+ keys[k] = 0;
+ return 0;
+@@ -53,3 +53,6 @@ void __pthread_tsd_run_dtors()
+ }
+ }
+ }
++
++weak_alias(__pthread_key_delete, pthread_key_delete);
++weak_alias(__pthread_key_create, pthread_key_create);
+diff --git a/src/thread/pthread_mutex_lock.c b/src/thread/pthread_mutex_lock.c
+index 2a9a3aa..d0c93ca 100644
+--- a/src/thread/pthread_mutex_lock.c
++++ b/src/thread/pthread_mutex_lock.c
+@@ -1,10 +1,14 @@
+ #include "pthread_impl.h"
+
+-int pthread_mutex_lock(pthread_mutex_t *m)
++int __pthread_mutex_timedlock(pthread_mutex_t *restrict, const struct timespec *restrict);
++
++int __pthread_mutex_lock(pthread_mutex_t *m)
+ {
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+ && !a_cas(&m->_m_lock, 0, EBUSY))
+ return 0;
+
+- return pthread_mutex_timedlock(m, 0);
++ return __pthread_mutex_timedlock(m, 0);
+ }
++
++weak_alias(__pthread_mutex_lock, pthread_mutex_lock);
+diff --git a/src/thread/pthread_mutex_timedlock.c b/src/thread/pthread_mutex_timedlock.c
+index ae883f9..16241ee 100644
+--- a/src/thread/pthread_mutex_timedlock.c
++++ b/src/thread/pthread_mutex_timedlock.c
+@@ -1,6 +1,6 @@
+ #include "pthread_impl.h"
+
+-int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
++int __pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *restrict at)
+ {
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL
+ && !a_cas(&m->_m_lock, 0, EBUSY))
+@@ -30,3 +30,5 @@ int pthread_mutex_timedlock(pthread_mutex_t *restrict m, const struct timespec *
+ }
+ return r;
+ }
++
++weak_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock);
+diff --git a/src/thread/pthread_mutex_trylock.c b/src/thread/pthread_mutex_trylock.c
+index e851517..cb93565 100644
+--- a/src/thread/pthread_mutex_trylock.c
++++ b/src/thread/pthread_mutex_trylock.c
+@@ -50,9 +50,11 @@ int __pthread_mutex_trylock_owner(pthread_mutex_t *m)
+ return 0;
+ }
+
+-int pthread_mutex_trylock(pthread_mutex_t *m)
++int __pthread_mutex_trylock(pthread_mutex_t *m)
+ {
+ if ((m->_m_type&15) == PTHREAD_MUTEX_NORMAL)
+ return a_cas(&m->_m_lock, 0, EBUSY) & EBUSY;
+ return __pthread_mutex_trylock_owner(m);
+ }
++
++weak_alias(__pthread_mutex_trylock, pthread_mutex_trylock);
+diff --git a/src/thread/pthread_mutex_unlock.c b/src/thread/pthread_mutex_unlock.c
+index 46761d9..a7f39c7 100644
+--- a/src/thread/pthread_mutex_unlock.c
++++ b/src/thread/pthread_mutex_unlock.c
+@@ -3,7 +3,7 @@
+ void __vm_lock_impl(int);
+ void __vm_unlock_impl(void);
+
+-int pthread_mutex_unlock(pthread_mutex_t *m)
++int __pthread_mutex_unlock(pthread_mutex_t *m)
+ {
+ pthread_t self;
+ int waiters = m->_m_waiters;
+@@ -36,3 +36,5 @@ int pthread_mutex_unlock(pthread_mutex_t *m)
+ __wake(&m->_m_lock, 1, priv);
+ return 0;
+ }
++
++weak_alias(__pthread_mutex_unlock, pthread_mutex_unlock);
+diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c
+index 2eb0f93..05ebe69 100644
+--- a/src/thread/pthread_once.c
++++ b/src/thread/pthread_once.c
+@@ -6,7 +6,7 @@ static void undo(void *control)
+ __wake(control, 1, 1);
+ }
+
+-int pthread_once(pthread_once_t *control, void (*init)(void))
++int __pthread_once(pthread_once_t *control, void (*init)(void))
+ {
+ static int waiters;
+
+@@ -34,3 +34,5 @@ int pthread_once(pthread_once_t *control, void (*init)(void))
+ return 0;
+ }
+ }
++
++weak_alias(__pthread_once, pthread_once);
+diff --git a/src/thread/pthread_self.c b/src/thread/pthread_self.c
+index 5f9e651..241a620 100644
+--- a/src/thread/pthread_self.c
++++ b/src/thread/pthread_self.c
+@@ -1,6 +1,11 @@
+ #include "pthread_impl.h"
++#include <threads.h>
++#include "libc.h"
+
+-pthread_t pthread_self()
++static pthread_t __pthread_self_internal()
+ {
+ return __pthread_self();
+ }
++
++weak_alias(__pthread_self_internal, pthread_self);
++weak_alias(__pthread_self_internal, thrd_current);
+diff --git a/src/thread/pthread_setcancelstate.c b/src/thread/pthread_setcancelstate.c
+index 060bcdc..2268217 100644
+--- a/src/thread/pthread_setcancelstate.c
++++ b/src/thread/pthread_setcancelstate.c
+@@ -1,6 +1,6 @@
+ #include "pthread_impl.h"
+
+-int pthread_setcancelstate(int new, int *old)
++int __pthread_setcancelstate(int new, int *old)
+ {
+ if (new > 1U) return EINVAL;
+ if (!libc.has_thread_pointer) return ENOSYS;
+@@ -9,3 +9,5 @@ int pthread_setcancelstate(int new, int *old)
+ self->canceldisable = new;
+ return 0;
+ }
++
++weak_alias(__pthread_setcancelstate, pthread_setcancelstate);
+diff --git a/src/thread/pthread_testcancel.c b/src/thread/pthread_testcancel.c
+index ba5f7c6..ee48e6d 100644
+--- a/src/thread/pthread_testcancel.c
++++ b/src/thread/pthread_testcancel.c
+@@ -7,7 +7,9 @@ static void dummy()
+
+ weak_alias(dummy, __testcancel);
+
+-void pthread_testcancel()
++void __pthread_testcancel()
+ {
+ __testcancel();
+ }
++
++weak_alias(__pthread_testcancel, pthread_testcancel);
+diff --git a/src/thread/thrd_create.c b/src/thread/thrd_create.c
+new file mode 100644
+index 0000000..e033669
+--- /dev/null
++++ b/src/thread/thrd_create.c
+@@ -0,0 +1,14 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++int __pthread_create(pthread_t *restrict, const pthread_attr_t *restrict, void *(*)(void *), void *restrict);
++
++int thrd_create(thrd_t *thr, thrd_start_t func, void *arg)
++{
++ int ret = __pthread_create(thr, __ATTRP_C11_THREAD, (void *(*)(void *))func, arg);
++ switch (ret) {
++ case 0: return thrd_success;
++ case EAGAIN: return thrd_nomem;
++ default: return thrd_error;
++ }
++}
+diff --git a/src/thread/thrd_exit.c b/src/thread/thrd_exit.c
+new file mode 100644
+index 0000000..b66bd99
+--- /dev/null
++++ b/src/thread/thrd_exit.c
+@@ -0,0 +1,9 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++_Noreturn void __pthread_exit(void *);
++
++_Noreturn void thrd_exit(int result)
++{
++ __pthread_exit((void*)(intptr_t)result);
++}
+diff --git a/src/thread/thrd_join.c b/src/thread/thrd_join.c
+new file mode 100644
+index 0000000..ac66789
+--- /dev/null
++++ b/src/thread/thrd_join.c
+@@ -0,0 +1,12 @@
++#include <stdint.h>
++#include <threads.h>
++
++int __pthread_join(thrd_t, void**);
++
++int thrd_join(thrd_t t, int *res)
++{
++ void *pthread_res;
++ __pthread_join(t, &pthread_res);
++ if (res) *res = (int)(intptr_t)pthread_res;
++ return thrd_success;
++}
+diff --git a/src/thread/thrd_sleep.c b/src/thread/thrd_sleep.c
+new file mode 100644
+index 0000000..e8dfe40
+--- /dev/null
++++ b/src/thread/thrd_sleep.c
+@@ -0,0 +1,13 @@
++#include <threads.h>
++#include <errno.h>
++#include "syscall.h"
++
++int thrd_sleep(const struct timespec *req, struct timespec *rem)
++{
++ int ret = __syscall(SYS_nanosleep, req, rem);
++ switch (ret) {
++ case 0: return 0;
++ case -EINTR: return -1; /* value specified by C11 */
++ default: return -2;
++ }
++}
+diff --git a/src/thread/thrd_yield.c b/src/thread/thrd_yield.c
+new file mode 100644
+index 0000000..f7ad132
+--- /dev/null
++++ b/src/thread/thrd_yield.c
+@@ -0,0 +1,7 @@
++#include <threads.h>
++#include "syscall.h"
++
++void thrd_yield()
++{
++ __syscall(SYS_sched_yield);
++}
+diff --git a/src/thread/tss_create.c b/src/thread/tss_create.c
+new file mode 100644
+index 0000000..251d22b
+--- /dev/null
++++ b/src/thread/tss_create.c
+@@ -0,0 +1,11 @@
++#include <threads.h>
++
++int __pthread_key_create(tss_t *, void (*)(void *));
++
++int tss_create(tss_t *tss, tss_dtor_t dtor)
++{
++ /* Different error returns are possible. C glues them together into
++ * just failure notification. Can't be optimized to a tail call,
++ * unless thrd_error equals EAGAIN. */
++ return __pthread_key_create(tss, dtor) ? thrd_error : thrd_success;
++}
+diff --git a/src/thread/tss_delete.c b/src/thread/tss_delete.c
+new file mode 100644
+index 0000000..35db103
+--- /dev/null
++++ b/src/thread/tss_delete.c
+@@ -0,0 +1,8 @@
++#include <threads.h>
++
++int __pthread_key_delete(tss_t k);
++
++void tss_delete(tss_t key)
++{
++ __pthread_key_delete(key);
++}
+diff --git a/src/thread/tss_set.c b/src/thread/tss_set.c
+new file mode 100644
+index 0000000..70c4fb7
+--- /dev/null
++++ b/src/thread/tss_set.c
+@@ -0,0 +1,13 @@
++#include "pthread_impl.h"
++#include <threads.h>
++
++int tss_set(tss_t k, void *x)
++{
++ struct pthread *self = __pthread_self();
++ /* Avoid unnecessary COW */
++ if (self->tsd[k] != x) {
++ self->tsd[k] = x;
++ self->tsd_used = 1;
++ }
++ return thrd_success;
++}
+diff --git a/src/time/timespec_get.c b/src/time/timespec_get.c
+new file mode 100644
+index 0000000..03c5a77
+--- /dev/null
++++ b/src/time/timespec_get.c
+@@ -0,0 +1,12 @@
++#include <time.h>
++
++int __clock_gettime(clockid_t, struct timespec *);
++
++/* There is no other implemented value than TIME_UTC; all other values
++ * are considered erroneous. */
++int timespec_get(struct timespec * ts, int base)
++{
++ if (base != TIME_UTC) return 0;
++ int ret = __clock_gettime(CLOCK_REALTIME, ts);
++ return ret < 0 ? 0 : base;
++}
diff --git a/main/musl/APKBUILD b/main/musl/APKBUILD
index 6cf0c3143e..1998acee67 100644
--- a/main/musl/APKBUILD
+++ b/main/musl/APKBUILD
@@ -2,7 +2,7 @@
# Maintainer: Timo Teräs <timo.teras@iki.fi>
pkgname=musl
pkgver=1.1.4
-pkgrel=7
+pkgrel=8
pkgdesc="the musl c library (libc) implementation"
url="http://www.musl-libc.org/"
arch="all"
@@ -17,10 +17,11 @@ source="http://www.musl-libc.org/releases/musl-$pkgver.tar.gz
0002-d86af2a0-to-4992ace9.patch
0003-4992ace9-to-6e2bb7ac.patch
0004-6e2bb7ac-to-4674809b.patch
+ 0005-4674809b-to-3e936ce8.patch
+
1001-add-basic-dns-record-parsing-functions.patch
1002-check-lockcount-in-funlockfile.patch
1003-remove-ulimit-fiddling-from-setxid.patch
- alignas.patch
ldconfig
getopt_long.c
@@ -128,10 +129,10 @@ md5sums="f18f3bdbe088438cd64a5313c19a7312 musl-1.1.4.tar.gz
04eef5b44c0b56b79055340bee1febc2 0002-d86af2a0-to-4992ace9.patch
6710102c768f5a971b93100219de3fbb 0003-4992ace9-to-6e2bb7ac.patch
294d3d382098977ea0770731a4803322 0004-6e2bb7ac-to-4674809b.patch
+5e7769c1f65acbdb9decf3012a70b411 0005-4674809b-to-3e936ce8.patch
2371eb1ce057fcb709a0e6a81f0d356c 1001-add-basic-dns-record-parsing-functions.patch
8a763b1853ee16d034abe038a0c44641 1002-check-lockcount-in-funlockfile.patch
71b2a4dcc39c436a6b89173943424043 1003-remove-ulimit-fiddling-from-setxid.patch
-20014a2b6bbf5e1e7ad95de338a66562 alignas.patch
830d01f7821b978df770b06db3790921 ldconfig
61c6c1e84ed1df82abbe6d75e90cf21c getopt_long.c
0df687757221bbb0fc1aa67f1bd646f9 __stack_chk_fail_local.c
@@ -143,10 +144,10 @@ sha256sums="658c65ad3c3a9b281a96c5281e75720c758d91fcaae35ab987f2fdfb4f88f5cd mu
c7521464f3096befb5b44d303819e9169a43a6a5a3c93566ac1cdf523a6a389d 0002-d86af2a0-to-4992ace9.patch
7a716e03084724e3776300d8ed498e835b2cad4321b638a3eec5a9d8f28de8d9 0003-4992ace9-to-6e2bb7ac.patch
e727b50aa65886563960c10449f60e229ffb94dd3476df1098e877ce599358c4 0004-6e2bb7ac-to-4674809b.patch
+1672b904cf69c9c9b349e7b07f534663cd2083a878778db188f74104bfc051b5 0005-4674809b-to-3e936ce8.patch
75053a31f6b84a64846d92c0ec631c76d7f747a9c0dc92a6dc1aa1bddfe2ea76 1001-add-basic-dns-record-parsing-functions.patch
eebc0afce600cbe2f41055404c7735f1a7be6e262df602dbcbfde7321633bdd9 1002-check-lockcount-in-funlockfile.patch
fb542c2bd5081ff2f601c519edb3dac8f54ca5c888f44bc6cfb84e6565472025 1003-remove-ulimit-fiddling-from-setxid.patch
-91a20ddcbbeb1d14e805afe45d5914dda5dea51a945f9f53afd589a60a3ab9ba alignas.patch
b4a2c06db38742e8c42c3c9838b285a7d8cdac6c091ff3df5ff9a15f1e41b9c7 ldconfig
d9b644ec20bc33e81a7c52b9fcf7973d835923a69faf50f03db45534b811bd96 getopt_long.c
299a7d75a09de3e2e11e7fb4acc3182e4a14e868093d2f30938fce9bfcff13da __stack_chk_fail_local.c
@@ -158,10 +159,10 @@ sha512sums="a46fb1db23f518beaa959e9bebcb3bf0574e583c197792d30dcd52b3974e3c285594
0e892a0d0e2abf09cb52d27759769a4cdd39ebd87fb1af89f502da00a4c9453309291aeb69862d60d88a8d0a0c2517aabcc92b50214d67279b12113b3c5a0157 0002-d86af2a0-to-4992ace9.patch
af92b766d2ca9fcf58b57c705b65ec0b60ecd9af1d38f1b9697a6b999baa56d8cb0ecf94bc02267022a13763e4a1a13a3f73bc03fdf73c0cd8f80126c2949a13 0003-4992ace9-to-6e2bb7ac.patch
8aa879e31c3eb18f9b00ea98d25872d94cad9120c05a80419965dcf7d3a4e0a69c370b35e2871cb11b639bbcf606842a48d3ad64ec916bc34b2eb21d3c4a3efa 0004-6e2bb7ac-to-4674809b.patch
+7104eb3753135b5cd2a291552c6fbf36034d7169178ae45ef99d634a4055afa30a7e89ce7a01695b120c4a872ec51f1a837b34c4e83d69778b0248ca6c931d48 0005-4674809b-to-3e936ce8.patch
5b8ffa0a50419581adbf6ce2dae5797774022551c6331fa5aa2ff13635eb72b74eedd8a92cb478d45d73e1956af2f588669681ac414f3a255abd4d8ba8579448 1001-add-basic-dns-record-parsing-functions.patch
beb515f46fa7c18eafe72cc612c5e8877dd1fd083e05c427af3a2a4d4281df06c6c1727c39d136d8e3f267be8832ea133362f5088a2b6a26b33c9e184acf1884 1002-check-lockcount-in-funlockfile.patch
dae010b45419fcab64410568466f659cdc874e63113025e2cbc2fbab047b470fec23851ecbef08886505924482a069caf37c16b483b6922535fbd31832f1c4a3 1003-remove-ulimit-fiddling-from-setxid.patch
-3cba68979fddff66d4989290c989c51b1f1b1f45d371ab23ee8622c6caf8a12409eecb3a15ad88c1b1f4d5c9c62380e9c0073b6c45f117e0312c4f50df4a7ad8 alignas.patch
8d3a2d5315fc56fee7da9abb8b89bb38c6046c33d154c10d168fb35bfde6b0cf9f13042a3bceee34daf091bc409d699223735dcf19f382eeee1f6be34154f26f ldconfig
140f3f20d30bd95ebce8c41b8cc7f616c6cbedf4ea06c729c21014e74f6043796825cc40ebc5180620ea38173afdba23f09ebf6d8b11fa05440b14d23764fca9 getopt_long.c
062bb49fa54839010acd4af113e20f7263dde1c8a2ca359b5fb2661ef9ed9d84a0f7c3bc10c25dcfa10bb3c5a4874588dff636ac43d5dbb3d748d75400756d0b __stack_chk_fail_local.c
diff --git a/main/musl/alignas.patch b/main/musl/alignas.patch
deleted file mode 100644
index 61dd221cb7..0000000000
--- a/main/musl/alignas.patch
+++ /dev/null
@@ -1,17 +0,0 @@
-diff --git a/arch/i386/bits/alltypes.h.in b/arch/i386/bits/alltypes.h.in
-index a7882ed..c4ba2c7 100644
---- a/arch/i386/bits/alltypes.h.in
-+++ b/arch/i386/bits/alltypes.h.in
-@@ -27,7 +27,11 @@ TYPEDEF long double float_t;
- TYPEDEF long double double_t;
- #endif
-
-+#if __cplusplus >= 201103L
-+TYPEDEF struct { alignas(8) long long __ll; long double __ld; } max_align_t;
-+#else
- TYPEDEF struct { _Alignas(8) long long __ll; long double __ld; } max_align_t;
-+#endif
-
- TYPEDEF long time_t;
- TYPEDEF long suseconds_t;
-