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 +#include + +#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 + +#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 #include #include +#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 +#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 #include +#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 +#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 #include #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 #include +#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] +#include #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 +#include #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 #include #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 #include #include +#include #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; imem, (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 + +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 + +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 + +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 + +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 + +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 +#include + +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 + +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 + +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 + +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 + +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 +#include + +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 + +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 + +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 #include +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 -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 +#include +#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 -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 +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 +#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 + +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 + +_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 +#include + +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 +#include +#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 +#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 + +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 + +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 + +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 + +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; +}