text:
#include "pthread_impl.h"
#include "syscall.h"

void __cancel()
{
	pthread_t self = __pthread_self();
	self->canceldisable = 1;
	self->cancelasync = 0;
	pthread_exit(PTHREAD_CANCELED);
}

long __syscall_cp_asm(volatile void *, syscall_arg_t,
                      syscall_arg_t, syscall_arg_t, syscall_arg_t,
                      syscall_arg_t, syscall_arg_t, syscall_arg_t);

long __syscall_cp_c(syscall_arg_t nr,
                    syscall_arg_t u, syscall_arg_t v, syscall_arg_t w,
                    syscall_arg_t x, syscall_arg_t y, syscall_arg_t z)
{
	pthread_t self;
	long r;

	if (!libc.has_thread_pointer || (self = __pthread_self())->canceldisable)
		return __syscall(nr, u, v, w, x, y, z);

	r = __syscall_cp_asm(&self->cancel, nr, u, v, w, x, y, z);
	if (r==-EINTR && nr!=SYS_close && self->cancel && !self->canceldisable)
		__cancel();
	return r;
}

static void _sigaddset(sigset_t *set, int sig)
{
	unsigned s = sig-1;
	set->__bits[s/8/sizeof *set->__bits] |= 1UL<<(s&8*sizeof *set->__bits-1);
}

static void cancel_handler(int sig, siginfo_t *si, void *ctx)
{
	pthread_t self = __pthread_self();
	ucontext_t *uc = ctx;
	const char *ip = ((char **)&uc->uc_mcontext)[CANCEL_REG_IP];
	extern const char __cp_begin[1], __cp_end[1];

	if (!self->cancel || self->canceldisable) return;

	_sigaddset(&uc->uc_sigmask, SIGCANCEL);

	if (self->cancelasync || ip >= __cp_begin && ip < __cp_end) {
		self->canceldisable = 1;
		pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0);
		__cancel();
	}

	__syscall(SYS_tkill, self->tid, SIGCANCEL);
}

void __testcancel()
{
	if (!libc.has_thread_pointer) return;
	pthread_t self = __pthread_self();
	if (self->cancel && !self->canceldisable)
		__cancel();
}

static void init_cancellation()
{
	struct sigaction sa = {
		.sa_flags = SA_SIGINFO | SA_RESTART,
		.sa_sigaction = cancel_handler
	};
	sigfillset(&sa.sa_mask);
	__libc_sigaction(SIGCANCEL, &sa, 0);
}

int pthread_cancel(pthread_t t)
{
	static int init;
	if (!init) {
		init_cancellation();
		init = 1;
	}
	a_store(&t->cancel, 1);
	return pthread_kill(t, SIGCANCEL);
}


text:
#include "pthread_impl.h"

int pthread_setcanceltype(int new, int *old)
{
	struct pthread *self = __pthread_self();
	if (new > 1U) return EINVAL;
	if (old) *old = self->cancelasync;
	self->cancelasync = new;
	if (new) pthread_testcancel();
	return 0;
}

repo_language: C

mime_type: text/x-c


text:
#include "pthread_impl.h"

int pthread_cond_destroy(pthread_cond_t *c)
{
	if (c->_c_shared && c->_c_waiters) {
		int cnt;
		a_or(&c->_c_waiters, 0x80000000);
		a_inc(&c->_c_seq);
		__wake(&c->_c_seq, -1, 0);
		while ((cnt = c->_c_waiters) & 0x7fffffff)
			__wait(&c->_c_waiters, 0, cnt, 0);
	}
	return 0;
}


text:
#include <semaphore.h>
#include <limits.h>
#include <errno.h>

int sem_init(sem_t *sem, int pshared, unsigned value)
{
	if (value > SEM_VALUE_MAX) {
		errno = EINVAL;
		return -1;
	}
	sem->__val[0] = value;
	sem->__val[1] = 0;
	sem->__val[2] = pshared ? 0 : 128;
	return 0;
}


text:
#include "pthread_impl.h"

void __vm_lock(int), __vm_unlock(void);

int pthread_barrier_destroy(pthread_barrier_t *b)
{
	if (b->_b_limit < 0) {
		if (b->_b_lock) {
			int v;
			a_or(&b->_b_lock, INT_MIN);
			while ((v = b->_b_lock) & INT_MAX)
				__wait(&b->_b_lock, 0, v, 0);
		}
		__vm_lock(-1);
		__vm_unlock();
	}
	return 0;
}


text:
#include "pthread_impl.h"

void __wait(volatile int *addr, volatile int *waiters, int val, int priv)
{
	int spins=100;
	if (priv) priv = 128;
	while (spins-- && (!waiters || !*waiters)) {
		if (*addr==val) a_spin();
		else return;
	}
	if (waiters) a_inc(waiters);
	while (*addr==val) {
		__syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0) != -ENOSYS
		|| __syscall(SYS_futex, addr, FUTEX_WAIT, val, 0);
	}
	if (waiters) a_dec(waiters);
}


text:
#include "pthread_impl.h"

int pthread_getschedparam(pthread_t t, int *restrict policy, struct sched_param *restrict param)
{
	int r;
	__lock(t->killlock);
	if (t->dead) {
		r = ESRCH;
	} else {
		r = -__syscall(SYS_sched_getparam, t->tid, param);
		if (!r) {
			*policy = __syscall(SYS_sched_getscheduler, t->tid);
		}
	}
	__unlock(t->killlock);
	return r;
}


text:
#include "pthread_impl.h"
#include "libc.h"

static void dummy()
{
}

weak_alias(dummy, __testcancel);

void __pthread_testcancel()
{
	__testcancel();
}

weak_alias(__pthread_testcancel, pthread_testcancel);


text:
#include "pthread_impl.h"

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rw)
{
	int val, cnt;
	do {
		val = rw->_rw_lock;
		cnt = val & 0x7fffffff;
		if (cnt == 0x7fffffff) return EBUSY;
		if (cnt == 0x7ffffffe) return EAGAIN;
	} while (a_cas(&rw->_rw_lock, val, val+1) != val);
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_mutexattr_setpshared(pthread_mutexattr_t *a, int pshared)
{
	if (pshared > 1U) return EINVAL;
	a->__attr &= ~128U;
	a->__attr |= pshared<<7;
	return 0;
}


text:
#include "pthread_impl.h"

void __vm_lock_impl(int);
void __vm_unlock_impl(void);

static int pshared_barrier_wait(pthread_barrier_t *b)
{
	int limit = (b->_b_limit & INT_MAX) + 1;
	int ret = 0;
	int v, w;

	if (limit==1) return PTHREAD_BARRIER_SERIAL_THREAD;

	while ((v=a_cas(&b->_b_lock, 0, limit)))
		__wait(&b->_b_lock, &b->_b_waiters, v, 0);

	/* Wait for <limit> threads to get to the barrier */
	if (++b->_b_count == limit) {
		a_store(&b->_b_count, 0);
		ret = PTHREAD_BARRIER_SERIAL_THREAD;
		if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
	} else {
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 0);
		while ((v=b->_b_count)>0)
			__wait(&b->_b_count, &b->_b_waiters2, v, 0);
	}

	__vm_lock_impl(+1);

	/* Ensure all threads have a vm lock before proceeding */
	if (a_fetch_add(&b->_b_count, -1)==1-limit) {
		a_store(&b->_b_count, 0);
		if (b->_b_waiters2) __wake(&b->_b_count, -1, 0);
	} else {
		while ((v=b->_b_count))
			__wait(&b->_b_count, &b->_b_waiters2, v, 0);
	}

	/* Perform a recursive unlock suitable for self-sync'd destruction */
	do {
		v = b->_b_lock;
		w = b->_b_waiters;
	} while (a_cas(&b->_b_lock, v, v==INT_MIN+1 ? 0 : v-1) != v);

	/* Wake a thread waiting to reuse or destroy the barrier */
	if (v==INT_MIN+1 || (v==1 && w))
		__wake(&b->_b_lock, 1, 0);

	__vm_unlock_impl();

	return ret;
}

struct instance
{
	int count;
	int last;
	int waiters;
	int finished;
};

int pthread_barrier_wait(pthread_barrier_t *b)
{
	int limit = b->_b_limit;
	struct instance *inst;

	/* Trivial case: count was set at 1 */
	if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD;

	/* Process-shared barriers require a separate, inefficient wait */
	if (limit < 0) return pshared_barrier_wait(b);

	/* Otherwise we need a lock on the barrier object */
	while (a_swap(&b->_b_lock, 1))
		__wait(&b->_b_lock, &b->_b_waiters, 1, 1);
	inst = b->_b_inst;

	/* First thread to enter the barrier becomes the "instance owner" */
	if (!inst) {
		struct instance new_inst = { 0 };
		int spins = 200;
		b->_b_inst = inst = &new_inst;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		while (spins-- && !inst->finished)
			a_spin();
		a_inc(&inst->finished);
		while (inst->finished == 1)
			__syscall(SYS_futex,&inst->finished,FUTEX_WAIT|128,1,0) != -ENOSYS
			|| __syscall(SYS_futex,&inst->finished,FUTEX_WAIT,1,0);
		return PTHREAD_BARRIER_SERIAL_THREAD;
	}

	/* Last thread to enter the barrier wakes all non-instance-owners */
	if (++inst->count == limit) {
		b->_b_inst = 0;
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		a_store(&inst->last, 1);
		if (inst->waiters)
			__wake(&inst->last, -1, 1);
	} else {
		a_store(&b->_b_lock, 0);
		if (b->_b_waiters) __wake(&b->_b_lock, 1, 1);
		__wait(&inst->last, &inst->waiters, 0, 1);
	}

	/* Last thread to exit the barrier wakes the instance owner */
	if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1))
		__wake(&inst->finished, 1, 1);

	return 0;
}


text:
#include <threads.h>

void cnd_destroy(cnd_t *c)
{
	/* For private cv this is a no-op */
}


text:
#include "pthread_impl.h"

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

weak_alias(__pthread_mutex_lock, pthread_mutex_lock);


text:
#include "pthread_impl.h"

int pthread_getcpuclockid(pthread_t t, clockid_t *clockid)
{
	*clockid = (-t->tid-1)*8U + 6;
	return 0;
}


text:
#include <pthread.h>
#include <time.h>
#include <errno.h>
#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)
{
	int r, cs;
	struct timespec to, *top=0;

	if (priv) priv = 128;

	if (at) {
		if (at->tv_nsec >= 1000000000UL) 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--;
			to.tv_nsec += 1000000000;
		}
		if (to.tv_sec < 0) return ETIMEDOUT;
		top = &to;
	}

	if (!cleanup) __pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
	pthread_cleanup_push(cleanup, arg);

	r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT|priv, val, top);
	if (r == ENOSYS) r = -__syscall_cp(SYS_futex, addr, FUTEX_WAIT, val, top);
	if (r != EINTR && r != ETIMEDOUT) r = 0;

	pthread_cleanup_pop(0);
	if (!cleanup) __pthread_setcancelstate(cs, 0);

	return r;
}


text:
#include <threads.h>

void mtx_destroy(mtx_t *mtx)
{
}


text:
#include "pthread_impl.h"
#include <semaphore.h>
#include <unistd.h>

static struct chain {
	struct chain *next;
	sem_t sem, sem2;
} *head, *cur;

static void (*callback)(void *), *context;
static int chainlen;
static sem_t chainlock, chaindone;

static void handler(int sig, siginfo_t *si, void *ctx)
{
	struct chain ch;
	int old_errno = errno;

	if (chainlen == libc.threads_minus_1) return;

	sigqueue(getpid(), SIGSYNCCALL, (union sigval){0});

	sem_init(&ch.sem, 0, 0);
	sem_init(&ch.sem2, 0, 0);

	while (sem_wait(&chainlock));
	ch.next = head;
	head = &ch;
	if (++chainlen == libc.threads_minus_1) sem_post(&chaindone);
	sem_post(&chainlock);

	while (sem_wait(&ch.sem));
	callback(context);
	sem_post(&ch.sem2);
	while (sem_wait(&ch.sem));

	errno = old_errno;
}

void __synccall(void (*func)(void *), void *ctx)
{
	struct sigaction sa;
	struct chain *next;
	sigset_t oldmask;

	if (!libc.threads_minus_1) {
		func(ctx);
		return;
	}

	__inhibit_ptc();

	__block_all_sigs(&oldmask);

	sem_init(&chaindone, 0, 0);
	sem_init(&chainlock, 0, 1);
	chainlen = 0;
	head = 0;
	callback = func;
	context = ctx;

	sa.sa_flags = SA_SIGINFO | SA_RESTART;
	sa.sa_sigaction = handler;
	sigfillset(&sa.sa_mask);
	__libc_sigaction(SIGSYNCCALL, &sa, 0);

	sigqueue(getpid(), SIGSYNCCALL, (union sigval){0});
	while (sem_wait(&chaindone));

	sa.sa_flags = 0;
	sa.sa_handler = SIG_IGN;
	__libc_sigaction(SIGSYNCCALL, &sa, 0);

	for (cur=head; cur; cur=cur->next) {
		sem_post(&cur->sem);
		while (sem_wait(&cur->sem2));
	}
	func(ctx);

	for (cur=head; cur; cur=next) {
		next = cur->next;
		sem_post(&cur->sem);
	}

	__restore_sigs(&oldmask);

	__release_ptc();
}


text:
#include <semaphore.h>
#include "pthread_impl.h"

static void cleanup(void *p)
{
	a_dec(p);
}

int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at)
{
	pthread_testcancel();

	if (!sem_trywait(sem)) return 0;

	int spins = 100;
	while (spins-- && sem->__val[0] <= 0 && !sem->__val[1]) a_spin();

	while (sem_trywait(sem)) {
		int r;
		a_inc(sem->__val+1);
		a_cas(sem->__val, 0, -1);
		r = __timedwait(sem->__val, -1, CLOCK_REALTIME, at, cleanup, sem->__val+1, sem->__val[2]);
		a_dec(sem->__val+1);
		if (r) {
			errno = r;
			return -1;
		}
	}
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_cond_wait(pthread_cond_t *restrict c, pthread_mutex_t *restrict m)
{
	return pthread_cond_timedwait(c, m, 0);
}


text:
#include "futex.h"
#include "syscall.h"

int __futex(volatile int *addr, int op, int val, void *ts)
{
	return syscall(SYS_futex, addr, op, val, ts);
}


text:
#include "pthread_impl.h"

int pthread_attr_setstack(pthread_attr_t *a, void *addr, size_t size)
{
	if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
	a->_a_stackaddr = (size_t)addr + size;
	a->_a_stacksize = size - DEFAULT_STACK_SIZE;
	return 0;
}


text:
#include "pthread_impl.h"

volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;
void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 };

static void (*keys[PTHREAD_KEYS_MAX])(void *);

static void nodtor(void *dummy)
{
}

int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))
{
	unsigned i = (uintptr_t)&k / 16 % PTHREAD_KEYS_MAX;
	unsigned j = i;

	if (libc.has_thread_pointer) {
		pthread_t self = __pthread_self();
		/* This can only happen in the main thread before
		 * pthread_create has been called. */
		if (!self->tsd) self->tsd = __pthread_tsd_main;
	}

	if (!dtor) dtor = nodtor;
	do {
		if (!a_cas_p(keys+j, 0, (void *)dtor)) {
			*k = j;
			return 0;
		}
	} while ((j=(j+1)%PTHREAD_KEYS_MAX) != i);
	return EAGAIN;
}

int __pthread_key_delete(pthread_key_t k)
{
	keys[k] = 0;
	return 0;
}

void __pthread_tsd_run_dtors()
{
	pthread_t self = __pthread_self();
	int i, j, not_finished = self->tsd_used;
	for (j=0; not_finished && j<PTHREAD_DESTRUCTOR_ITERATIONS; j++) {
		not_finished = 0;
		for (i=0; i<PTHREAD_KEYS_MAX; i++) {
			if (self->tsd[i] && keys[i]) {
				void *tmp = self->tsd[i];
				self->tsd[i] = 0;
				keys[i](tmp);
				not_finished = 1;
			}
		}
	}
}

weak_alias(__pthread_key_delete, pthread_key_delete);
weak_alias(__pthread_key_create, pthread_key_create);


text:
#include "pthread_impl.h"
#include <threads.h>

_Noreturn void __pthread_exit(void *);

_Noreturn void thrd_exit(int result)
{
	__pthread_exit((void*)(intptr_t)result);
}


text:
#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;
}


text:
#include "pthread_impl.h"

int pthread_mutexattr_init(pthread_mutexattr_t *a)
{
	*a = (pthread_mutexattr_t){0};
	return 0;
}


text:
#include "pthread_impl.h"

static int vmlock[2];

void __vm_lock(int inc)
{
	for (;;) {
		int v = vmlock[0];
		if (inc*v < 0) __wait(vmlock, vmlock+1, v, 1);
		else if (a_cas(vmlock, v, v+inc)==v) break;
	}
}

void __vm_unlock(void)
{
	int inc = vmlock[0]>0 ? -1 : 1;
	if (a_fetch_add(vmlock, inc)==-inc && vmlock[1])
		__wake(vmlock, -1, 1);
}

weak_alias(__vm_lock, __vm_lock_impl);
weak_alias(__vm_unlock, __vm_unlock_impl);


text:
#include "pthread_impl.h"

int pthread_mutexattr_destroy(pthread_mutexattr_t *a)
{
	return 0;
}


text:
#include "pthread_impl.h"
#include <threads.h>
#include "libc.h"

static pthread_t __pthread_self_internal()
{
	return __pthread_self();
}

weak_alias(__pthread_self_internal, pthread_self);
weak_alias(__pthread_self_internal, thrd_current);


text:
#include "pthread_impl.h"

int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *a, int pshared)
{
	if (pshared > 1U) return EINVAL;
	a->__attr[0] = pshared;
	return 0;
}


text:
#include <errno.h>
#include "pthread_impl.h"

int __clone(int (*func)(void *), void *stack, int flags, void *arg, ...)
{
	return -ENOSYS;
}


text:
#include "pthread_impl.h"

int pthread_condattr_setpshared(pthread_condattr_t *a, int pshared)
{
	if (pshared > 1U) return EINVAL;
	a->__attr &= 0x7fffffff;
	a->__attr |= pshared<<31;
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_rwlock_wrlock(pthread_rwlock_t *rw)
{
	return pthread_rwlock_timedwrlock(rw, 0);
}


text:
#include "pthread_impl.h"

int pthread_spin_unlock(pthread_spinlock_t *s)
{
	a_store(s, 0);
	return 0;
}


text:
#include <semaphore.h>

int sem_getvalue(sem_t *restrict sem, int *restrict valp)
{
	int val = sem->__val[0];
	*valp = val < 0 ? 0 : val;
	return 0;
}


text:
#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);
}


text:
#include "pthread_impl.h"

int pthread_spin_destroy(pthread_spinlock_t *s)
{
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_barrierattr_setpshared(pthread_barrierattr_t *a, int pshared)
{
	a->__attr = pshared ? INT_MIN : 0;
	return 0;
}


text:
#include <pthread.h>
#include <threads.h>
#include "libc.h"

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


text:
#include "pthread_impl.h"

static void dummy(struct __ptcb *cb)
{
}
weak_alias(dummy, __do_cleanup_push);
weak_alias(dummy, __do_cleanup_pop);

void _pthread_cleanup_push(struct __ptcb *cb, void (*f)(void *), void *x)
{
	cb->__f = f;
	cb->__x = x;
	__do_cleanup_push(cb);
}

void _pthread_cleanup_pop(struct __ptcb *cb, int run)
{
	__do_cleanup_pop(cb);
	if (run) cb->__f(cb->__x);
}


text:
#include "pthread_impl.h"

int pthread_barrierattr_init(pthread_barrierattr_t *a)
{
	*a = (pthread_barrierattr_t){0};
	return 0;
}


text:
#include "pthread_impl.h"

int pthread_attr_setstacksize(pthread_attr_t *a, size_t size)
{
	if (size-PTHREAD_STACK_MIN > SIZE_MAX/4) return EINVAL;
	a->_a_stackaddr = 0;
	a->_a_stacksize = size - DEFAULT_STACK_SIZE;
	return 0;
}


text:
#include <pthread.h>

static pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;

void __inhibit_ptc()
{
	pthread_rwlock_wrlock(&lock);
}

void __acquire_ptc()
{
	pthread_rwlock_rdlock(&lock);
}

void __release_ptc()
{
	pthread_rwlock_unlock(&lock);
}


text:
#include <threads.h>

int cnd_init(cnd_t *c)
{
	*c = (cnd_t){ 0 };
	return thrd_success;
}


text:
#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;
	}
}


text:
#include <signal.h>
#include <errno.h>
#include "syscall.h"

int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict old)
{
	int ret;
	if ((unsigned)how - SIG_BLOCK > 2U) return EINVAL;
	ret = -__syscall(SYS_rt_sigprocmask, how, set, old, _NSIG/8);
	if (!ret && old) {
		if (sizeof old->__bits[0] == 8) {
			old->__bits[0] &= ~0x380000000ULL;
		} else {
			old->__bits[0] &= ~0x80000000UL;
			old->__bits[1] &= ~0x3UL;
		}
	}
	return ret;
}


text:
#r0: volatile. may be modified during linkage.
#r1: stack frame: 16 byte alignment.
#r2: tls/thread pointer on pp32
#r3,r4: return values, first args
#r5-r10: args
#r11-r12: volatile. may be modified during linkage
#r13: "small data area" pointer
#r14 - r30: local vars
#r31: local or environment pointer

#r1, r14-31: belong to the caller, must be saved and restored
#r0, r3-r12, ctr, xer: volatile, not preserved
#r0,r11,r12: may be altered by cross-module call,
#"a func cannot depend on that these regs have the values placed by the caller"

#the fields CR2,CR2,CR4 of the cond reg must be preserved
#LR (link reg) shall contain the funcs return address
	.text
	.global __syscall_cp_asm
	.type   __syscall_cp_asm,%function
__syscall_cp_asm:
	# at enter: r3 = pointer to self->cancel, r4: syscall no, r5: first arg, r6: 2nd, r7: 3rd, r8: 4th, r9: 5th, r10: 6th
	.global __cp_begin
__cp_begin:
	# r3 holds first argument, its a pointer to self->cancel.
	# we must compare the dereferenced value with 0 and jump to __cancel if its not

	lwz 0, 0(3) #deref pointer into r0

	cmpwi cr7, 0, 0 #compare r0 with 0, store result in cr7.
	beq+ cr7, 1f #jump to label 1 if r0 was 0

	b __cancel #else call cancel
	# (the return address is not needed, since __cancel never returns)
1:
	#ok, the cancel flag was not set
	# syscall: number goes to r0, the rest 3-8
	mr      0, 4                  # put the system call number into r0
	mr      3, 5                  # Shift the arguments: arg1
	mr      4, 6                  # arg2
	mr      5, 7                  # arg3
	mr      6, 8                  # arg4
	mr      7, 9                  # arg5
	mr      8, 10                  # arg6
	sc
	.global __cp_end
__cp_end:
	bnslr+ # return if no summary overflow.
	#else negate result.
	neg 3, 3
	blr


text:
.text
.global __set_thread_area
.type   __set_thread_area, %function
__set_thread_area:
	# mov pointer in reg3 into r2
	mr 2, 3
	# put 0 into return reg
	li 3, 0
	# return
	blr


text:
	.text
	.global __unmapself
	.type   __unmapself,%function
__unmapself:
	li      0, 91 # __NR_munmap
	sc
	li      0, 1 #__NR_exit
	sc
	blr


text:
.text
.global __clone
.type __clone, %function
__clone:
# int clone(fn, stack, flags, arg, ptid, tls, ctid)
#            a  b       c     d     e    f    g
#            3  4       5     6     7    8    9
# pseudo C code:
# tid = syscall(SYS_clone,c,b,e,f,g);
# if (!tid) syscall(SYS_exit, a(d));
# return tid;

# SYS_clone = 120
# SYS_exit = 1

# store non-volatile regs r30, r31 on stack in order to put our
# start func and its arg there
stwu 30, -16(1)
stw 31, 4(1)

# save r3 (func) into r30, and r6(arg) into r31
mr 30, 3
mr 31, 6

#move c into first arg
mr 3, 5
#mr 4, 4
mr 5, 7
mr 6, 8
mr 7, 9

# move syscall number into r0
li 0, 120

sc

# check for syscall error
bns+ 1f # jump to label 1 if no summary overflow.
#else
neg 3, 3 #negate the result (errno)
1:
# compare sc result with 0
cmpwi cr7, 3, 0

# if not 0, jump to end
bne cr7, 2f

#else: we're the child
#call funcptr: move arg (d) into r3
mr 3, 31
#move r30 (funcptr) into CTR reg
mtctr 30
# call CTR reg
bctrl
# mov SYS_exit into r0 (the exit param is already in r3)
li 0, 1
sc

2:

# restore stack
lwz 30, 0(1)
lwz 31, 4(1)
addi 1, 1, 16

blr


text:
.text
.global __syscall_cp_asm
.type   __syscall_cp_asm, @function
__syscall_cp_asm:

.global __cp_begin
__cp_begin:
	mov.l @r4, r4
	tst   r4, r4
	bt    2f

	mov.l L1, r0
	braf  r0
	 nop
1:

.align 2
L1:	.long __cancel@PLT-(1b-.)

2:	mov   r5, r3
	mov   r6, r4
	mov   r7, r5
	mov.l @r15, r6
	mov.l @(4,r15), r7
	mov.l @(8,r15), r0
	mov.l @(12,r15), r1
	trapa #22

.global __cp_end
__cp_end:
	! work around hardware bug
	or   r0, r0
	or   r0, r0
	or   r0, r0
	or   r0, r0
	or   r0, r0

	rts
	 nop


text:
.global __set_thread_area
.type   __set_thread_area, @function
__set_thread_area:
	rts
	 ldc r4, gbr


text:
.text
.global __unmapself
.type   __unmapself, @function
__unmapself:
	mov   #91, r3  ! SYS_munmap
	trapa #18

	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0

	mov   #1, r3   ! SYS_exit
	mov   #0, r4
	trapa #17

	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0
	or    r0, r0


text:
.text
.global __clone
.type   __clone, @function
__clone:
! incoming: fn stack flags arg ptid tls      ctid
!           r4 r5    r6    r7  @r15 @(4,r15) @(8,r15)

	mov   #-16, r0
	and   r0, r5

	mov   r4, r1         ! r1 = fn
	mov   r7, r2         ! r2 = arg

	mov   #120,     r3   ! r3 = __NR_clone
	mov   r6,       r4   ! r4 = flags
	!mov  r5,       r5   ! r5 = stack
	mov.l @r15,     r6   ! r6 = ptid
	mov.l @(8,r15), r7   ! r7 = ctid
	mov.l @(4,r15), r0   ! r0 = tls
	trapa #21

	or r0, r0
	or r0, r0
	or r0, r0
	or r0, r0
	or r0, r0

	cmp/eq #0, r0
	bt     1f

	! we are the parent, return
	rts
	 nop

1:	! we are the child, call fn(arg)
	jsr    @r1
	 mov   r2, r4

	mov   #1, r3   ! __NR_exit
	mov   r0, r4
	trapa #17

	or   r0, r0
	or   r0, r0
	or   r0, r0
	or   r0, r0
	or   r0, r0


text:
.global __syscall_cp_asm
.type   __syscall_cp_asm,@function
__syscall_cp_asm:
.global __cp_begin
__cp_begin:
	lwi     r5, r5, 0
	bnei    r5, __cancel@PLT
	addi    r12, r6, 0
	add     r5, r7, r0
	add     r6, r8, r0
	add     r7, r9, r0
	add     r8, r10, r0
	lwi     r9, r1, 28
	lwi     r10, r1, 32
	brki    r14, 0x8
.global __cp_end
__cp_end:
	rtsd    r15, 8
	nop


text:
.global __set_thread_area
.type   __set_thread_area,@function
__set_thread_area:
	ori      r21, r5, 0
	rtsd     r15, 8
	ori      r3, r0, 0


text:
.global __unmapself
.type   __unmapself,@function
__unmapself:
	ori     r12, r0, 91
	brki    r14, 0x8
	ori     r12, r0, 1
	brki    r14, 0x8
	nop


text:
.global __clone
.type   __clone,@function

# r5, r6, r7, r8, r9, r10, stack
# fn, st, fl, ar, pt, tl, ct
# fl, st, __, pt, ct, tl

__clone:
	andi    r6, r6, -16
	addi    r6, r6, -16
	swi     r5, r6, 0
	swi     r8, r6, 4

	ori     r5, r7, 0
	ori     r8, r9, 0
	lwi     r9, r1, 28
	ori     r12, r0, 120

	brki    r14, 8
	beqi	r3, 1f
	rtsd    r15, 8
	nop

1:	lwi     r3, r1, 0
	lwi     r5, r1, 4
	brald   r15, r3
	nop
	ori     r12, r0, 1
	brki    r14, 8


text:
.set    noreorder

.global __syscall_cp_asm
.type   __syscall_cp_asm,@function
__syscall_cp_asm:
.global __cp_begin
__cp_begin:
	lw      $4, 0($4)
	bne     $4, $0, 2f
	move    $2, $5
	move    $4, $6
	move    $5, $7
	lw      $6, 16($sp)
	lw      $7, 20($sp)
	lw      $8, 24($sp)
	lw      $9, 28($sp)
	lw      $10,32($sp)
	subu    $sp, $sp, 32
	sw      $8, 16($sp)
	sw      $9, 20($sp)
	sw      $10,24($sp)
	sw      $2, 28($sp)
	lw      $2, 28($sp)
	syscall
.global __cp_end
__cp_end:
	beq     $7, $0, 1f
	addu    $sp, $sp, 32
	subu    $2, $0, $2
1:	jr      $ra
	nop
2:	lw      $25, %call16(__cancel)($gp)
	jr      $25
	nop


text:
.set noreorder
.global __unmapself
.type   __unmapself,@function
__unmapself:
	li $2, 4091
	syscall
	li $4, 0
	li $2, 4001
	syscall


text:
.set noreorder
.global __clone
.type   __clone,@function
__clone:
	# Save function pointer and argument pointer on new thread stack
	and $5, $5, -8
	subu $5, $5, 16
	sw $4, 0($5)
	sw $7, 4($5)
	# Shuffle (fn,sp,fl,arg,ptid,tls,ctid) to (fl,sp,ptid,tls,ctid)
	move $4, $6
	lw $6, 16($sp)
	lw $7, 20($sp)
	lw $9, 24($sp)
	subu $sp, $sp, 16
	sw $9, 16($sp)
	li $2, 4120
	syscall
	beq $7, $0, 1f
	nop
	addu $sp, $sp, 16
	jr $ra
	subu $2, $0, $2
1:	beq $2, $0, 1f
	nop
	addu $sp, $sp, 16
	jr $ra
	nop
1:	lw $25, 0($sp)
	lw $4, 4($sp)
	jr $25
	nop


text:
.text
.global __syscall_cp_asm
.type   __syscall_cp_asm,@function
__syscall_cp_asm:
	mov 4(%esp),%ecx
	pushl %ebx
	pushl %esi
	pushl %edi
	pushl %ebp
.global __cp_begin
__cp_begin:
	movl (%ecx),%eax
	testl %eax,%eax
	jnz __cancel
	movl 24(%esp),%eax
	movl 28(%esp),%ebx
	movl 32(%esp),%ecx
	movl 36(%esp),%edx
	movl 40(%esp),%esi
	movl 44(%esp),%edi
	movl 48(%esp),%ebp
	int $128
.global __cp_end
__cp_end:
	popl %ebp
	popl %edi
	popl %esi
	popl %ebx
	ret


text:
.text
.global ___tls_get_addr
.type ___tls_get_addr,@function
___tls_get_addr:
	mov %gs:4,%edx
	mov (%eax),%ecx
	cmp %ecx,(%edx)
	jc 1f
	mov 4(%eax),%eax
	add (%edx,%ecx,4),%eax
	ret
1:	push %eax
	call __tls_get_addr
	pop %edx
	ret


text:
.text
.global __set_thread_area
.type   __set_thread_area,@function
__set_thread_area:
	push %ebx
	push $0x51
	push $0xfffff
	push 16(%esp)
	push $-1
	mov %esp,%ebx
	xor %eax,%eax
	mov $243,%al
	int $128
	testl %eax,%eax
	jnz 2f
	movl (%esp),%edx
	leal 3(,%edx,8),%edx
3:	movw %dx,%gs
1:
	addl $16,%esp
	popl %ebx
	ret
2:
	mov %ebx,%ecx
	xor %ebx,%ebx
	xor %edx,%edx
	mov %ebx,(%esp)
	mov $1,%bl
	mov $16,%dl
	mov $123,%al
	int $128
	testl %eax,%eax
	jnz 1b
	mov $7,%dl
	inc %al
	jmp 3b


text:
.text
.global __unmapself
.type   __unmapself,@function
__unmapself:
	movl $91,%eax
	movl 4(%esp),%ebx
	movl 8(%esp),%ecx
	int $128
	xorl %ebx,%ebx
	movl $1,%eax
	int $128


text:
.text
.global __clone
.type   __clone,@function
__clone:
	push %ebp
	mov %esp,%ebp
	push %ebx
	push %esi
	push %edi

	xor %eax,%eax
	push $0x51
	mov %gs,%ax
	push $0xfffff
	shr $3,%eax
	push 28(%ebp)
	push %eax
	mov $120,%al

	mov 12(%ebp),%ecx
	mov 16(%ebp),%ebx
	and $-16,%ecx
	sub $16,%ecx
	mov 20(%ebp),%edi
	mov %edi,(%ecx)
	mov 24(%ebp),%edx
	mov %esp,%esi
	mov 32(%ebp),%edi
	mov 8(%ebp),%ebp
	int $128
	test %eax,%eax
	jnz 1f

	mov %ebp,%eax
	xor %ebp,%ebp
	call *%eax
	mov %eax,%ebx
	xor %eax,%eax
	inc %eax
	int $128
	hlt

1:	add $16,%esp
	pop %edi
	pop %esi
	pop %ebx
	pop %ebp
	ret


text:
.text
.global __syscall_cp_internal
.type   __syscall_cp_internal,@function
__syscall_cp_internal:

.global __cp_begin
__cp_begin:
	mov (%rdi),%eax
	test %eax,%eax
	jnz __cancel
	mov %rdi,%r11
	mov %rsi,%rax
	mov %rdx,%rdi
	mov %rcx,%rsi
	mov %r8,%rdx
	mov %r9,%r10
	mov 8(%rsp),%r8
	mov 16(%rsp),%r9
	mov %r11,8(%rsp)
	syscall
.global __cp_end
__cp_end:
	ret


text:
/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.text
.global __set_thread_area
.type __set_thread_area,@function
__set_thread_area:
	mov %rdi,%rsi           /* shift for syscall */
	movl $0x1002,%edi       /* SET_FS register */
	movl $0x4000009e,%eax          /* set fs segment to */
	syscall                 /* arch_prctl(SET_FS, arg)*/
	ret


text:
/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.text
.global __unmapself
.type   __unmapself,@function
__unmapself:
	movl $0x4000000b,%eax   /* SYS_munmap */
	syscall         /* munmap(arg2,arg3) */
	xor %rdi,%rdi   /* exit() args: always return success */
	movl $0x4000003c,%eax   /* SYS_exit */
	syscall         /* exit(0) */


text:
.text
.global __clone
.type   __clone,@function
__clone:
	movl $0x40000038,%eax /* SYS_clone */
	mov %rdi,%r11
	mov %rdx,%rdi
	mov %r8,%rdx
	mov %r9,%r8
	mov 8(%rsp),%r10
	mov %r11,%r9
	and $-16,%rsi
	sub $8,%rsi
	mov %rcx,(%rsi)
	syscall
	test %eax,%eax
	jnz 1f
	xor %ebp,%ebp
	pop %rdi
	call *%r9
	mov %eax,%edi
	movl $0x4000003c,%eax /* SYS_exit */
	syscall
	hlt
1:	ret


text:
.global __syscall_cp_asm
.type __syscall_cp_asm,%function
__syscall_cp_asm:
	mov ip,sp
	stmfd sp!,{r4,r5,r6,r7,lr}
.global __cp_begin
__cp_begin:
	ldr r0,[r0]
	cmp r0,#0
	blne __cancel
	mov r7,r1
	mov r0,r2
	mov r1,r3
	ldmfd ip,{r2,r3,r4,r5,r6}
	svc 0
.global __cp_end
__cp_end:
	ldmfd sp!,{r4,r5,r6,r7,lr}
	tst lr,#1
	moveq pc,lr
	bx lr


text:
.global __aeabi_read_tp
.type __aeabi_read_tp,%function
__aeabi_read_tp:
	ldr pc,=0xffff0fe0


text:
.text
.global __set_thread_area
.type   __set_thread_area,%function
__set_thread_area:
	mov r1,r7
	mov r7,#0x0f0000
	add r7,r7,#5
	svc 0
	mov r7,r1
	tst lr,#1
	moveq pc,lr
	bx lr


text:
.text
.global __unmapself
.type   __unmapself,%function
__unmapself:
	mov r7,#91
	svc 0
	mov r7,#1
	svc 0


text:
.text
.global __clone
.type   __clone,%function
__clone:
	stmfd sp!,{r4,r5,r6,r7}
	mov r7,#120
	mov r6,r3
	mov r5,r0
	mov r0,r2
	and r1,r1,#-16
	ldr r2,[sp,#16]
	ldr r3,[sp,#20]
	ldr r4,[sp,#24]
	svc 0
	tst r0,r0
	beq 1f
	ldmfd sp!,{r4,r5,r6,r7}
	tst lr,#1
	moveq pc,lr
	bx lr

1:	mov r0,r6
	tst r5,#1
	bne 1f
	mov lr,pc
	mov pc,r5
2:	mov r7,#1
	svc 0

1:	mov lr,pc
	bx r5
	b 2b


text:
.global __syscall_cp_asm
.type   __syscall_cp_asm,@function
__syscall_cp_asm:
.global __cp_begin
__cp_begin:
	l.lwz	r3, 0(r3)
	l.sfeqi	r3, 0
	l.bnf	plt(__cancel)
	 l.ori	r11, r4, 0
	l.ori	r3, r5, 0
	l.ori	r4, r6, 0
	l.ori	r5, r7, 0
	l.ori	r6, r8, 0
	l.lwz	r7, 0(r1)
	l.lwz	r8, 4(r1)
	l.sys	1
.global __cp_end
__cp_end:
	l.jr	r9
	 l.nop


text:
.global __set_thread_area
.type   __set_thread_area,@function
__set_thread_area:
	l.ori	r10, r3, 0
	l.jr	r9
	 l.ori	r11, r0, 0


text:
.global __unmapself
.type   __unmapself,@function
__unmapself:
	l.ori	r11, r0, 215 /* __NR_munmap */
	l.sys	1
	l.ori	r3, r0, 0
	l.ori	r11, r0, 93 /* __NR_exit */
	l.sys	1


text:
/* int clone(fn, stack, flags, arg, ptid, tls, ctid)
 *           r3  r4     r5     r6   sp+0  sp+4 sp+8
 * sys_clone(flags, stack, ptid, ctid, tls)
 */
.global __clone
.type   __clone,@function
__clone:
	l.addi	r4, r4, -8
	l.sw	0(r4), r3
	l.sw	4(r4), r6
	/* (fn, st, fl, ar, pt, tl, ct) => (fl, st, pt, ct, tl) */
	l.ori	r3, r5, 0
	l.lwz	r5, 0(r1)
	l.lwz	r6, 8(r1)
	l.lwz	r7, 4(r1)
	l.ori	r11, r0, 220 /* __NR_clone */
	l.sys	1

	l.sfeqi	r11, 0
	l.bf	1f
	 l.nop
	l.jr	r9
	 l.nop

1:	l.lwz	r11, 0(r1)
	l.jalr	r11
	 l.lwz	r3, 4(r1)

	l.ori	r11, r0, 93 /* __NR_exit */
	l.sys	1


text:
.text
.global __syscall_cp_asm
.type   __syscall_cp_asm,@function
__syscall_cp_asm:

.global __cp_begin
__cp_begin:
	mov (%rdi),%eax
	test %eax,%eax
	jnz __cancel
	mov %rdi,%r11
	mov %rsi,%rax
	mov %rdx,%rdi
	mov %rcx,%rsi
	mov %r8,%rdx
	mov %r9,%r10
	mov 8(%rsp),%r8
	mov 16(%rsp),%r9
	mov %r11,8(%rsp)
	syscall
.global __cp_end
__cp_end:
	ret


text:
/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.text
.global __set_thread_area
.type __set_thread_area,@function
__set_thread_area:
	mov %rdi,%rsi           /* shift for syscall */
	movl $0x1002,%edi       /* SET_FS register */
	movl $158,%eax          /* set fs segment to */
	syscall                 /* arch_prctl(SET_FS, arg)*/
	ret


text:
/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.text
.global __unmapself
.type   __unmapself,@function
__unmapself:
	movl $11,%eax   /* SYS_munmap */
	syscall         /* munmap(arg2,arg3) */
	xor %rdi,%rdi   /* exit() args: always return success */
	movl $60,%eax   /* SYS_exit */
	syscall         /* exit(0) */


text:
.text
.global __clone
.type   __clone,@function
__clone:
	xor %eax,%eax
	mov $56,%al
	mov %rdi,%r11
	mov %rdx,%rdi
	mov %r8,%rdx
	mov %r9,%r8
	mov 8(%rsp),%r10
	mov %r11,%r9
	and $-16,%rsi
	sub $8,%rsi
	mov %rcx,(%rsi)
	syscall
	test %eax,%eax
	jnz 1f
	xor %ebp,%ebp
	pop %rdi
	call *%r9
	mov %eax,%edi
	xor %eax,%eax
	mov $60,%al
	syscall
	hlt
1:	ret


text:
#include <string.h>
#include <locale.h>
#include "locale_impl.h"
#include "libc.h"

/* collate only by code points */
size_t __strxfrm_l(char *restrict dest, const char *restrict src, size_t n, locale_t loc)
{
	size_t l = strlen(src);
	if (n > l) strcpy(dest, src);
	return l;
}

size_t strxfrm(char *restrict dest, const char *restrict src, size_t n)
{
	return __strxfrm_l(dest, src, n, CURRENT_LOCALE);
}

weak_alias(__strxfrm_l, strxfrm_l);


text:
#include <locale.h>

static const struct lconv posix_lconv = {
	.decimal_point = ".",
	.thousands_sep = "",
	.grouping = "",
	.int_curr_symbol = "",
	.currency_symbol = "",
	.mon_decimal_point = "",
	.mon_thousands_sep = "",
	.mon_grouping = "",
	.positive_sign = "",
	.negative_sign = "",
	.int_frac_digits = -1,
	.frac_digits = -1,
	.p_cs_precedes = -1,
	.p_sep_by_space = -1,
	.n_cs_precedes = -1,
	.n_sep_by_space = -1,
	.p_sign_posn = -1,
	.n_sign_posn = -1,
	.int_p_cs_precedes = -1,
	.int_p_sep_by_space = -1,
	.int_n_cs_precedes = -1,
	.int_n_sep_by_space = -1,
	.int_p_sign_posn = -1,
	.int_n_sign_posn = -1,
};

struct lconv *localeconv(void)
{
	return (void *)&posix_lconv;
}


text:
#include <locale.h>
#include <langinfo.h>
#include "locale_impl.h"
#include "libc.h"

static const char c_time[] =
	"Sun\0" "Mon\0" "Tue\0" "Wed\0" "Thu\0" "Fri\0" "Sat\0"
	"Sunday\0" "Monday\0" "Tuesday\0" "Wednesday\0"
	"Thursday\0" "Friday\0" "Saturday\0"
	"Jan\0" "Feb\0" "Mar\0" "Apr\0" "May\0" "Jun\0"
	"Jul\0" "Aug\0" "Sep\0" "Oct\0" "Nov\0" "Dec\0"
	"January\0"   "February\0" "March\0"    "April\0"
	"May\0"       "June\0"     "July\0"     "August\0"
	"September\0" "October\0"  "November\0" "December\0"
	"AM\0" "PM\0"
	"%a %b %e %T %Y\0"
	"%m/%d/%y\0"
	"%H:%M:%S\0"
	"%I:%M:%S %p\0"
	"\0"
	"\0"
	"%m/%d/%y\0"
	"0123456789\0"
	"%a %b %e %T %Y\0"
	"%H:%M:%S";

static const char c_messages[] = "^[yY]\0" "^[nN]\0" "yes\0" "no";
static const char c_numeric[] = ".\0" "";

char *__nl_langinfo_l(nl_item item, locale_t loc)
{
	int cat = item >> 16;
	int idx = item & 65535;
	const char *str;

	if (item == CODESET) return "UTF-8";

	switch (cat) {
	case LC_NUMERIC:
		if (idx > 1) return NULL;
		str = c_numeric;
		break;
	case LC_TIME:
		if (idx > 0x31) return NULL;
		str = c_time;
		break;
	case LC_MONETARY:
		if (idx > 0) return NULL;
		str = "";
		break;
	case LC_MESSAGES:
		if (idx > 3) return NULL;
		str = c_messages;
		break;
	default:
		return NULL;
	}

	for (; idx; idx--, str++) for (; *str; str++);
	if (cat != LC_NUMERIC && *str) str = LCTRANS(str, cat, loc);
	return (char *)str;
}

char *__nl_langinfo(nl_item item)
{
	return __nl_langinfo_l(item, CURRENT_LOCALE);
}

weak_alias(__nl_langinfo, nl_langinfo);
weak_alias(__nl_langinfo_l, nl_langinfo_l);


text:
"iso88591\0"
"latin1\0"
"\0\200"

"iso88592\0"
"\0\40"
"\0\330\20\313\32\0\244\21\10\0\0\34\122\310\42\240\0\100\212\50\0\334\60\13\33"
"\0\250\41\10\54\0\40\142\10\43\241\324\122\312\50\173\0\0\0\15\0\224\201\3\0"
"\76\0\200\4\0\112\0\0\0\20\102\264\21\7\0\0\334\1\0\0\177\124\2\300\45"
"\0\0\220\10\0\174\0\0\100\15\0\230\221\3\0\77\0\220\4\0\113\0\0\100\20"
"\103\270\41\7\0\0\340\1\0\0\200\130\2\0\46\0\0\240\210\54"

"iso88593\0"
"\0\40"
"\0\130\21\13\0\0\4\100\5\0\0\170\121\210\23\140\0\20\200\50\0\134\1\0\0"
"\0\0\120\5\0\0\174\141\310\23\141\0\20\300\50\0\0\0\100\0\0\360\240\3\0"
"\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\100\1\0\0\114\0\0\0\0\0\114\62\10\0"
"\0\0\0\100\0\0\364\260\3\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\0\0\104\1\0\0"
"\115\0\0\0\0\0\120\102\210\54"

"iso88594\0"
"\0\40"
"\0\330\100\106\37\0\140\161\6\0\0\34\102\204\24\215\0\100\12\0\0\334\60\213\37"
"\0\144\201\6\54\0\40\122\304\24\216\314\121\12\35\62\0\0\0\0\0\0\0\0\27"
"\76\0\200\4\0\106\0\0\200\26\102\274\121\207\30\0\0\0\0\0\0\144\2\0\0"
"\0\74\22\11\0\63\0\0\0\0\0\0\0\100\27\77\0\220\4\0\107\0\0\300\26"
"\103\300\141\307\30\0\0\0\0\0\0\150\2\0\0\0\100\42\211\54"

"iso88595\0"
"\0\40"
"\0\14\104\120\101\6\35\204\120\102\12\55\304\120\103\16\1\360\20\104"
"\21\111\64\21\105\25\131\164\21\106\31\151\264\21\107\35\171\364\21\110"
"\41\211\64\22\111\45\231\164\22\112\51\251\264\22\113\55\271\364\22\114"
"\61\311\64\23\115\65\331\164\23\116\71\351\264\23\117\75\371\364\23\120"
"\101\11\65\24\121\105\31\165\24\122\111\51\265\24\123\115\71\365\24\124"
"\127\106\45\325\124\124\125\145\325\125\130\145\245\325\126\134\15\320\225\127"

"iso88596\0"
"\0\40"
"\0\4\20\100\0\0\4\20\100\0\1\4\20\100\0\224\1\20\100\0\1\4\20\100\0"
"\1\4\20\100\0\1\4\20\100\145\1\4\20\200\145\1\134\206\131\146"
"\232\155\306\131\147\236\175\6\132\150\242\215\106\132\151\246\235\206\132\152"
"\252\255\306\132\153\256\275\6\133\0\1\4\20\100\0\261\311\66\33\155"
"\265\331\166\33\156\271\351\266\33\157\275\371\366\33\160\301\11\67\134\0"
"\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0"

"iso88597\0"
"\0\40"
"\0\24\151\44\0\124\126\11\0\0\0\0\260\13\0\0\0\20\300\220\0\0\0\0\0"
"\274\364\342\13\0\277\0\23\14\0\302\0\60\14\61\305\30\163\14\62"
"\311\50\263\14\63\315\70\363\14\64\321\110\63\15\65\325\130\23\300\65"
"\330\144\243\315\66\334\164\343\315\67\340\204\43\316\70\344\224\143\316\71"
"\350\244\243\316\72\354\264\343\316\73\360\304\43\317\74\364\324\143\317\75"
"\370\344\243\317\76\374\364\343\317\77\0\5\44\120\0"

"iso88598\0"
"\0\40"
"\0\4\0\0\0\0\0\0\0\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\0\0"
"\0\0\0\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0\1\4\20\100\0"
"\1\4\20\100\0\1\4\20\100\0\1\4\20\0\221\164\325\145\327\135"
"\170\345\245\327\136\174\365\345\327\137\200\5\46\330\140\204\25\146\330\141"
"\210\45\246\330\142\214\65\346\130\0\1\374\10\144\0"

"iso88599\0"
"\0\120"
"\116\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\170\121\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\117\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\174\141\10\0"

"iso885910\0"
"\0\40"
"\0\330\100\204\24\132\140\41\6\0\147\10\161\110\43\244\0\20\311\34"
"\0\334\120\304\24\133\144\61\6\0\150\14\201\210\43\245\14\51\11\35\62\0\0\0\0"
"\0\0\0\0\27\76\0\200\4\0\106\0\0\0\0\0\274\121\7\0\0\0\0\300\43\0\144\2\0\0"
"\0\0\0\0\0\63\0\0\0\0\0\0\0\100\27\77\0\220\4\0\107\0\0\0\0\0\300\141\7\0"
"\0\0\0\0\44\0\150\2\0\0\0\0\0\0\31"

"iso885911\0"
"tis620\0"
"\0\40"
"\0\100\27\235\164\323\121\127\235\165\327\141\227\235\166\333\161\327\235\167"
"\337\201\27\236\170\343\221\127\236\171\347\241\227\236\172"
"\353\261\327\236\173\357\301\27\237\174\363\321\127\237\175"
"\367\341\227\237\176\373\361\327\237\177\377\1\30\240\200\3\22\130\240\201"
"\7\42\230\140\0\1\4\20\200\202\13\62\330\240\203\17\102\30\241\204"
"\23\122\130\241\205\27\142\230\241\206\33\162\330\241\207\37\202\30\242\210"
"\43\222\130\242\211\1\4\20\100\0"

"iso885913\0"
"\0\40"
"\0\44\11\0\0\0\50\11\0\0\15\0\320\7\0\0\0\0\300\2\0\0\0\0\0\110\2\0\0\0"
"\20\0\340\7\0\0\0\0\200\3\66\160\41\3\16\0\0\200\4\21\76\0\0\212\21"
"\122\210\241\305\31\207\264\361\6\0\165\0\0\0\0\231\254\21\110\44"
"\0\210\102\12\0\67\164\61\103\16\0\0\220\104\21\77\0\20\312\21"
"\123\214\261\5\32\210\270\1\7\0\166\0\0\0\0\232\260\41\210\44"
"\0\214\122\212\221"

"iso885914\0"
"\0\40"
"\0\234\210\42\0\74\364\220\42\0\65\2\160\243\212\73\2\0\300\47"
"\53\262\10\105\24\55\272\10\300\213\66\302\210\143\214\74\346\250\243\214"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\233\0\0\0\0\0\0\0\300\214\0\0\0\0\0"
"\0\0\320\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\234\0\0\0\0\0\0\0\0\215"
"\0\0\0\0\0\0\0\340\11\0"

"iso885915\0"
"latin9\0"
"\0\44"
"\124\2\160\10\0\210\0\0\0\0\0\0\0\0\0\0\0\0\0\0\244\0\0\0\0\245\0\0\0\0"
"\171\350\361\11\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

"iso885916\0"
"\0\40"
"\0\330\160\303\32\124\52\171\10\0\210\0\260\12\0\240\0\20\212\50\0\0\340\3\33"
"\244\44\11\0\0\245\374\300\12\0\171\350\361\311\50\0\0\0\0\15\0\340\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\102\264\1\0\0\0\334\1\100\40\227\0\0\0\0\0\40\321\12\0"
"\0\0\0\100\15\0\344\0\0\0\0\0\0\0\0\0\0\0\0\0\103\270\1\0\0\0\340\1\200\40"
"\230\0\0\0\0\0\44\341\12\0"

"cp1250\0"
"windows1250\0"
"\0\0"
"\124\6\160\144\0\112\72\271\44\223\1\74\171\10\224\201\54\102\12\50"
"\1\24\151\44\222\111\66\31\244\220\1\140\211\110\224\202\60\122\112\50"
"\0\300\22\313\32\0\330\0\0\0\0\0\120\10\0\0\0\0\200\50\0\0\60\13\33\0\0\0\0\0"
"\0\334\140\10\0\151\324\242\306\50\173\0\0\0\15\0\224\201\3\0\76\0\200\4\0"
"\112\0\0\0\20\102\264\21\7\0\0\334\1\0\0\177\124\2\300\45\0\0\220\10\0"
"\174\0\0\100\15\0\230\221\3\0\77\0\220\4\0\113\0\0\100\20\103\270\41\7\0"
"\0\340\1\0\0\200\130\2\0\46\0\0\240\210\54"

"cp1251\0"
"windows1251\0"
"\0\0"
"\4\25\164\344\124\112\72\271\44\223\124\76\271\20\224\14\71\324\20\104"
"\122\25\151\44\222\111\66\31\244\220\1\140\231\125\224\132\161\265\225\127"
"\0\74\324\225\102\0\174\5\0\0\3\1\140\20\0\0\0\0\100\102\0\0\200\220\125"
"\140\1\0\0\0\121\135\111\25\0\130\35\124\325\125\21\111\64\21\105"
"\25\131\164\21\106\31\151\264\21\107\35\171\364\21\110\41\211\64\22\111"
"\45\231\164\22\112\51\251\264\22\113\55\271\364\22\114\61\311\64\23\115"
"\65\331\164\23\116\71\351\264\23\117\75\371\364\23\120\101\11\65\24\121"
"\105\31\165\24\122\111\51\265\24\123\115\71\365\24\124"

"cp1252\0"
"windows1252\0"
"\0\0"
"\124\6\160\244\51\112\72\271\44\223\257\74\171\10\224\171\4\100\112\0"
"\1\24\151\44\222\111\66\31\244\220\264\140\211\110\224\172\4\120\312\47"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"

"cp1253\0"
"windows1253\0"
"\0\0"
"\124\6\160\244\51\112\72\271\44\223\1\74\31\0\224\1\4\20\100\0\1\24\151\44\222"
"\111\66\31\244\220\1\140\31\100\224\1\4\20\100\0\0\364\342\13\0\0\0\0\0\0"
"\0\0\20\0\0\0\0\0\300\220\0\0\0\0\0\274\0\0\0\0\277\0\23\14\0\302\0\60\14\61"
"\305\30\163\14\62\311\50\263\14\63\315\70\363\14\64\321\110\63\15\65"
"\325\130\23\300\65\330\144\243\315\66\334\164\343\315\67\340\204\43\316\70"
"\344\224\143\316\71\350\244\243\316\72\354\264\343\316\73\360\304\43\317\74"
"\364\324\143\317\75\370\344\243\317\76\374\364\343\317\77\0\5\44\120\0"

"cp1254\0"
"windows1254\0"
"\0\0"
"\124\6\160\244\51\112\72\271\44\223\257\74\171\10\224\171\4\20\100\0"
"\1\24\151\44\222\111\66\31\244\220\264\140\211\110\224\172\4\20\300\47"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\116\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\170\121\10\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\117\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\0\0\174\141\10\0"

"cp1255\0"
"windows1255\0"
"\0\0"
"\124\6\160\244\51\112\72\271\44\223\257\74\31\0\224\1\4\20\100\0"
"\1\24\151\44\222\111\66\31\244\220\264\140\31\100\224\1\4\20\100\0\0\0\0\0\0"
"\122\2\0\0\0\0\0\300\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\360\0\0\0\0\0\0\0"
"\141\211\65\26\131\145\231\165\26\132\151\251\25\300\132\154\265\345\326\133"
"\160\305\45\327\134\217\101\26\231\144\223\5\20\100\0\1\4\20\100\0"
"\164\325\145\327\135\170\345\245\327\136\174\365\345\327\137\200\5\46\330\140"
"\204\25\146\330\141\210\45\246\330\142\214\65\346\130\0\1\374\10\144\0"

"cp1256\0"
"windows1256\0"
"\0\0"
"\124\26\167\244\51\112\72\271\44\223\257\74\111\34\224\171\30\227\334\161"
"\313\25\151\44\222\111\66\31\244\220\312\141\211\134\224\172\364\350\43\163"
"\0\120\6\0\0\0\0\0\0\0\0\0\320\34\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\120\31\0"
"\0\0\0\200\145\316\135\206\131\146\232\155\306\131\147\236\175\6\132\150"
"\242\215\106\132\151\246\235\206\132\152\252\255\306\32\0\255\271\366\32\154"
"\261\311\66\33\155\0\324\6\200\155\267\341\226\33\0\0\0\0\0\0\272\355\6\0\0"
"\274\365\346\333\157\0\0\27\34\0\302\1\60\34\0\0\374\10\344\163"

"cp1257\0"
"windows1257\0"
"\0\0"
"\124\6\160\144\0\112\72\271\44\223\1\74\31\0\224\1\20\0\213\2\1\24\151\44\222"
"\111\66\31\244\220\1\140\31\100\224\1\30\60\113\0\0\4\0\0\0\0\4\0\0\0"
"\15\0\320\7\0\0\0\0\300\2\0\0\0\0\0\0\0\0\0\0\20\0\340\7\0\0\0\0\200\3"
"\66\160\41\3\16\0\0\200\4\21\76\0\0\212\21\122\210\241\305\31\207\264\361\6\0"
"\165\0\0\0\0\231\254\21\110\44\0\210\102\12\0\67\164\61\103\16\0\0\220\104\21"
"\77\0\20\312\21\123\214\261\5\32\210\270\1\7\0\166\0\0\0\0\232\260\41\210\44"
"\0\214\122\212\54"

"cp1258\0"
"windows1258\0"
"\0\0"
"\124\6\160\244\51\112\72\271\44\223\257\74\31\0\224\171\4\20\100\0"
"\1\24\151\44\222\111\66\31\244\220\264\140\31\100\224\172\4\20\300\47"
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\0\15\0\0\0\0\0\0\0\0\0\0\266\0\0\0\0\102\0\220\13\0"
"\0\234\2\0\0\0\0\0\0\0\0\244\202\13\0\0\0\0\100\15\0\0\0\0\0\0\0\0\0\0"
"\267\0\0\0\0\103\0\240\13\0\0\240\2\0\0\0\0\0\0\0\0\250\62\45\0"

"koi8r\0"
"\0\0"
"\140\206\51\346\230\144\226\151\346\231\150\246\251\46\242\211\52\272\50\243"
"\215\72\372\250\227\220\146\251\345\226\134\166\51\300\227\7\40\220\300\3"
"\153\262\331\146\124\156\276\11\147\234\162\316\111\147\235"
"\166\336\211\147\236\172\356\311\347\100\175\372\371\47\240\201\12\72\50\241"
"\205\32\172\150\1\117\305\44\323\121\65\331\124\24\115\106\345\244\323\116"
"\74\365\344\323\117\100\101\25\224\120\103\21\165\323\114\115\61\205\123\122"
"\116\51\205\324\122\57\105\44\321\111\25\131\124\22\105\46\145\244\321\106"
"\34\165\344\321\107\40\301\24\222\110\43\221\164\321\104\55\261\204\121\112"
"\56\251\204\322\112"

"koi8u\0"
"\0\0"
"\140\206\51\346\230\144\226\151\346\231\150\246\251\46\242\211\52\272\50\243"
"\215\72\372\250\227\220\146\251\345\226\134\166\51\300\227\7\40\220\300\3"
"\153\262\331\146\124\124\275\151\325\125\162\316\111\147\235"
"\166\202\205\147\236\172\356\311\347\100\6\371\211\120\102\201\12\72\50\241"
"\205\176\165\150\1\117\305\44\323\121\65\331\124\24\115\106\345\244\323\116"
"\74\365\344\323\117\100\101\25\224\120\103\21\165\323\114\115\61\205\123\122"
"\116\51\205\324\122\57\105\44\321\111\25\131\124\22\105\46\145\244\321\106"
"\34\165\344\321\107\40\301\24\222\110\43\221\164\321\104\55\261\204\121\112"
"\56\251\204\322\112"

"cp437\0"
"\0\0"
"\27\300\100\202\7\37\164\0\202\10\45\230\60\102\12\50\234\100\101\5"
"\30\70\260\300\12\54\250\360\202\13\61\144\300\101\4\22\114\140\245\51"
"\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
"\215\72\372\150\230\147\362\331\147\234\160\372\311\246\234"
"\170\336\151\347\230\144\246\211\246\231\140\252\231\247\236"
"\165\276\111\150\240\173\256\171\250\240\203\376\11\50\235\163\266\351\246\241"
"\205\226\51\246\242\211\56\312\50\242\345\104\212\14\75\327\334\23\51\76"
"\332\64\323\15\72\221\352\223\116\244\221\106\332\45\227\136\176\371\300\226"
"\7\144\231\200\226\221\42\0\251\0"

"cp850\0"
"\0\0"
"\27\300\100\202\7\37\164\0\202\10\45\230\60\102\12\50\234\100\101\5"
"\30\70\260\300\12\54\250\360\202\13\61\144\300\1\4\22\64\300\200\51"
"\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
"\215\72\372\150\230\147\106\32\151\244\5\370\311\246\234\170\106\60\301\230"
"\144\246\211\246\231\140\252\31\151\244\165\276\111\150\240"
"\173\256\171\150\244\221\106\32\151\244\221\176\21\151\244\221\226\51\246\242"
"\211\106\32\51\242\221\106\32\151\244\221\106\32\151\244\221\106\32\151\244"
"\221\106\152\100\244\221\106\112\144\244\221\16\360\200\2\7\20\220\100\244"
"\221\42\0\251\0"


text:
#include <stdio.h>
#include <ctype.h>
#include <stdarg.h>
#include <monetary.h>
#include <errno.h>
#include "locale_impl.h"

static ssize_t vstrfmon_l(char *s, size_t n, locale_t loc, const char *fmt, va_list ap)
{
	size_t l;
	double x;
	int fill, nogrp, negpar, nosym, left, intl;
	int lp, rp, w, fw;
	char *s0=s;
	for (; n && *fmt; ) {
		if (*fmt != '%') {
		literal:
			*s++ = *fmt++;
			n--;
			continue;
		}
		fmt++;
		if (*fmt == '%') goto literal;

		fill = ' ';
		nogrp = 0;
		negpar = 0;
		nosym = 0;
		left = 0;
		for (; ; fmt++) {
			switch (*fmt) {
			case '=':
				fill = *++fmt;
				continue;
			case '^':
				nogrp = 1;
				continue;
			case '(':
				negpar = 1;
			case '+':
				continue;
			case '!':
				nosym = 1;
				continue;
			case '-':
				left = 1;
				continue;
			}
			break;
		}

		for (fw=0; isdigit(*fmt); fmt++)
			fw = 10*fw + (*fmt-'0');
		lp = 0;
		rp = 2;
		if (*fmt=='#') for (lp=0, fmt++; isdigit(*fmt); fmt++)
			lp = 10*lp + (*fmt-'0');
		if (*fmt=='.') for (rp=0, fmt++; isdigit(*fmt); fmt++)
			rp = 10*rp + (*fmt-'0');

		intl = *fmt++ == 'i';

		w = lp + 1 + rp;
		if (!left && fw>w) w = fw;

		x = va_arg(ap, double);
		l = snprintf(s, n, "%*.*f", w, rp, x);
		if (l >= n) {
			errno = E2BIG;
			return -1;
		}
		s += l;
		n -= l;
	}
	return s-s0;
}

ssize_t strfmon_l(char *restrict s, size_t n, locale_t loc, const char *restrict fmt, ...)
{
	va_list ap;
	ssize_t ret;

	va_start(ap, fmt);
	ret = vstrfmon_l(s, n, loc, fmt, ap);
	va_end(ap);

	return ret;
}

ssize_t strfmon(char *restrict s, size_t n, const char *restrict fmt, ...)
{
	va_list ap;
	ssize_t ret;

	va_start(ap, fmt);
	ret = vstrfmon_l(s, n, CURRENT_LOCALE, fmt, ap);
	va_end(ap);

	return ret;
}


text:
#include "locale_impl.h"
#include "pthread_impl.h"
#include "libc.h"

locale_t __uselocale(locale_t new)
{
	pthread_t self = __pthread_self();
	locale_t old = self->locale;
	locale_t global = &libc.global_locale;

	if (new == LC_GLOBAL_LOCALE) new = global;

	if (new && new != old) {
		int adj = 0;
		if (new == global) a_dec(&libc.uselocale_cnt);
		else if (!new->ctype_utf8) adj++;
		if (old == global) a_inc(&libc.uselocale_cnt);
		else if (!old->ctype_utf8) adj--;
		a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
		self->locale = new;
	}

	return old == global ? LC_GLOBAL_LOCALE : old;
}

weak_alias(__uselocale, uselocale);


text:
#include <libintl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include "libc.h"
#include "atomic.h"

static char *current_domain;

char *__gettextdomain()
{
	return current_domain ? current_domain : "messages";
}

char *textdomain(const char *domainname)
{
	if (!domainname) return __gettextdomain();

	size_t domlen = strlen(domainname);
	if (domlen > NAME_MAX) {
		errno = EINVAL;
		return 0;
	}

	if (!current_domain) {
		current_domain = malloc(NAME_MAX+1);
		if (!current_domain) return 0;
	}

	memcpy(current_domain, domainname, domlen+1);

	return current_domain;
}

char *gettext(const char *msgid)
{
	return dgettext(0, msgid);
}

char *ngettext(const char *msgid1, const char *msgid2, unsigned long int n)
{
	return dngettext(0, msgid1, msgid2, n);
}


text:
#include <wchar.h>
#include <locale.h>
#include "locale_impl.h"
#include "libc.h"

/* collate only by code points */
size_t __wcsxfrm_l(wchar_t *restrict dest, const wchar_t *restrict src, size_t n, locale_t loc)
{
	size_t l = wcslen(src);
	if (l < n) {
		wmemcpy(dest, src, l+1);
	} else if (n) {
		wmemcpy(dest, src, n-1);
		dest[n-1] = 0;
	}
	return l;
}

size_t wcsxfrm(wchar_t *restrict dest, const wchar_t *restrict src, size_t n)
{
	return __wcsxfrm_l(dest, src, n, CURRENT_LOCALE);
}

weak_alias(__wcsxfrm_l, wcsxfrm_l);


text:
#include <nl_types.h>

char *catgets (nl_catd catd, int set_id, int msg_id, const char *s)
{
	return (char *)s;
}


text:
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include "locale_impl.h"
#include "libc.h"
#include "atomic.h"

static char buf[2+4*(LOCALE_NAME_MAX+1)];

char *setlocale(int cat, const char *name)
{
	struct __locale_map *lm;
	int i, j;

	if (!libc.global_locale.messages_name) {
		libc.global_locale.messages_name =
			buf + 2 + 3*(LOCALE_NAME_MAX+1);
	}

	if ((unsigned)cat > LC_ALL) return 0;

	/* For LC_ALL, setlocale is required to return a string which
	 * encodes the current setting for all categories. The format of
	 * this string is unspecified, and only the following code, which
	 * performs both the serialization and deserialization, depends
	 * on the format, so it can easily be changed if needed. */
	if (cat == LC_ALL) {
		if (name) {
			char part[LOCALE_NAME_MAX+1];
			if (name[0] && name[1]==';'
			    && strlen(name) > 2 + 3*(LOCALE_NAME_MAX+1)) {
				part[0] = name[0];
				part[1] = 0;
				setlocale(LC_CTYPE, part);
				part[LOCALE_NAME_MAX] = 0;
				for (i=LC_TIME; i<LC_MESSAGES; i++) {
					memcpy(part, name + 2 + (i-2)*(LOCALE_NAME_MAX+1), LOCALE_NAME_MAX);
					for (j=LOCALE_NAME_MAX-1; j && part[j]==';'; j--)
						part[j] = 0;
					setlocale(i, part);
				}
				setlocale(LC_MESSAGES, name + 2 + 3*(LOCALE_NAME_MAX+1));
			} else {
				for (i=0; i<LC_ALL; i++)
					setlocale(i, name);
			}
		}
		memset(buf, ';', 2 + 3*(LOCALE_NAME_MAX+1));
		buf[0] = libc.global_locale.ctype_utf8 ? 'U' : 'C';
		for (i=LC_TIME; i<LC_MESSAGES; i++) {
			lm = libc.global_locale.cat[i-2];
			if (lm) memcpy(buf + 2 + (i-2)*(LOCALE_NAME_MAX+1),
				lm->name, strlen(lm->name));
		}
		return buf;
	}

	if (name) {
		int adj = libc.global_locale.ctype_utf8;
		__setlocalecat(&libc.global_locale, cat, name);
		adj -= libc.global_locale.ctype_utf8;
		if (adj) a_fetch_add(&libc.bytelocale_cnt_minus_1, adj);
	}

	switch (cat) {
	case LC_CTYPE:
		return libc.global_locale.ctype_utf8 ? "C.UTF-8" : "C";
	case LC_NUMERIC:
		return "C";
	case LC_MESSAGES:
		return libc.global_locale.messages_name[0]
			? libc.global_locale.messages_name : "C";
	default:
		lm = libc.global_locale.cat[cat-2];
		return lm ? lm->name : "C";
	}
}


text:
#include <wchar.h>
#include <locale.h>
#include "locale_impl.h"
#include "libc.h"

/* FIXME: stub */
int __wcscoll_l(const wchar_t *l, const wchar_t *r, locale_t locale)
{
	return wcscmp(l, r);
}

int wcscoll(const wchar_t *l, const wchar_t *r)
{
	return __wcscoll_l(l, r, CURRENT_LOCALE);
}

weak_alias(__wcscoll_l, wcscoll_l);


text:
#include <nl_types.h>

int catclose (nl_catd catd)
{
	return 0;
}


text:
#include <string.h>
#include <locale.h>
#include "locale_impl.h"
#include "libc.h"

int __strcoll_l(const char *l, const char *r, locale_t loc)
{
	return strcmp(l, r);
}

int strcoll(const char *l, const char *r)
{
	return __strcoll_l(l, r, CURRENT_LOCALE);
}

weak_alias(__strcoll_l, strcoll_l);


text:
#include <iconv.h>
#include <errno.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <stdint.h>

#define UTF_32BE    0300
#define UTF_16LE    0301
#define UTF_16BE    0302
#define UTF_32LE    0303
#define UCS2BE      0304
#define UCS2LE      0305
#define WCHAR_T     0306
#define US_ASCII    0307
#define UTF_8       0310
#define EUC_JP      0320
#define SHIFT_JIS   0321
#define GB18030     0330
#define GBK         0331
#define GB2312      0332
#define BIG5        0340
#define EUC_KR      0350

/* FIXME: these are not implemented yet
 * EUC:   A1-FE A1-FE
 * GBK:   81-FE 40-7E,80-FE
 * Big5:  A1-FE 40-7E,A1-FE
 */

/* Definitions of charmaps. Each charmap consists of:
 * 1. Empty-string-terminated list of null-terminated aliases.
 * 2. Special type code or number of elided entries.
 * 3. Character table (size determined by field 2). */

static const unsigned char charmaps[] =
"utf8\0\0\310"
"wchart\0\0\306"
"ucs2\0ucs2be\0\0\304"
"ucs2le\0\0\305"
"utf16\0utf16be\0\0\302"
"utf16le\0\0\301"
"ucs4\0ucs4be\0utf32\0utf32be\0\0\300"
"ucs4le\0utf32le\0\0\303"
"ascii\0usascii\0iso646\0iso646us\0\0\307"
"eucjp\0\0\320"
"shiftjis\0sjis\0\0\321"
"gb18030\0\0\330"
"gbk\0\0\331"
"gb2312\0\0\332"
"big5\0bigfive\0cp950\0big5hkscs\0\0\340"
"euckr\0ksc5601\0ksx1001\0cp949\0\0\350"
#include "codepages.h"
;

static const unsigned short legacy_chars[] = {
#include "legacychars.h"
};

static const unsigned short jis0208[84][94] = {
#include "jis0208.h"
};

static const unsigned short gb18030[126][190] = {
#include "gb18030.h"
};

static const unsigned short big5[89][157] = {
#include "big5.h"
};

static const unsigned short hkscs[] = {
#include "hkscs.h"
};

static const unsigned short ksc[93][94] = {
#include "ksc.h"
};

static int fuzzycmp(const unsigned char *a, const unsigned char *b)
{
	for (; *a && *b; a++, b++) {
		while (*a && (*a|32U)-'a'>26 && *a-'0'>10U) a++;
		if ((*a|32U) != *b) return 1;
	}
	return *a != *b;
}

static size_t find_charmap(const void *name)
{
	const unsigned char *s;
	for (s=charmaps; *s; ) {
		if (!fuzzycmp(name, s)) {
			for (; *s; s+=strlen((void *)s)+1);
			return s+1-charmaps;
		}
		s += strlen((void *)s)+1;
		if (!*s) {
			if (s[1] > 0200) s+=2;
			else s+=2+(128U-s[1])/4*5;
		}
	}
	return -1;
}

iconv_t iconv_open(const char *to, const char *from)
{
	size_t f, t;

	if ((t = find_charmap(to))==-1
	 || (f = find_charmap(from))==-1
	 || (charmaps[t] >= 0320)) {
		errno = EINVAL;
		return (iconv_t)-1;
	}

	return (void *)(f<<16 | t);
}

int iconv_close(iconv_t cd)
{
	return 0;
}

static unsigned get_16(const unsigned char *s, int e)
{
	e &= 1;
	return s[e]<<8 | s[1-e];
}

static void put_16(unsigned char *s, unsigned c, int e)
{
	e &= 1;
	s[e] = c>>8;
	s[1-e] = c;
}

static unsigned get_32(const unsigned char *s, int e)
{
	e &= 3;
	return s[e]+0U<<24 | s[e^1]<<16 | s[e^2]<<8 | s[e^3];
}

static void put_32(unsigned char *s, unsigned c, int e)
{
	e &= 3;
	s[e^0] = c>>24;
	s[e^1] = c>>16;
	s[e^2] = c>>8;
	s[e^3] = c;
}

/* Adapt as needed */
#define mbrtowc_utf8 mbrtowc
#define wctomb_utf8 wctomb

size_t iconv(iconv_t cd0, char **restrict in, size_t *restrict inb, char **restrict out, size_t *restrict outb)
{
	size_t x=0;
	unsigned long cd = (unsigned long)cd0;
	unsigned to = cd & 0xffff;
	unsigned from = cd >> 16;
	const unsigned char *map = charmaps+from+1;
	const unsigned char *tomap = charmaps+to+1;
	mbstate_t st = {0};
	wchar_t wc;
	unsigned c, d;
	size_t k, l;
	int err;
	unsigned char type = map[-1];
	unsigned char totype = tomap[-1];

	if (!in || !*in || !*inb) return 0;

	for (; *inb; *in+=l, *inb-=l) {
		c = *(unsigned char *)*in;
		l = 1;

		if (c >= 128 || type-UTF_32BE < 7U) switch (type) {
		case UTF_8:
			l = mbrtowc_utf8(&wc, *in, *inb, &st);
			if (!l) l++;
			else if (l == (size_t)-1) goto ilseq;
			else if (l == (size_t)-2) goto starved;
			c = wc;
			break;
		case US_ASCII:
			goto ilseq;
		case WCHAR_T:
			l = sizeof(wchar_t);
			if (*inb < l) goto starved;
			c = *(wchar_t *)*in;
			if (0) {
		case UTF_32BE:
		case UTF_32LE:
			l = 4;
			if (*inb < 4) goto starved;
			c = get_32((void *)*in, type);
			}
			if (c-0xd800u < 0x800u || c >= 0x110000u) goto ilseq;
			break;
		case UCS2BE:
		case UCS2LE:
		case UTF_16BE:
		case UTF_16LE:
			l = 2;
			if (*inb < 2) goto starved;
			c = get_16((void *)*in, type);
			if ((unsigned)(c-0xdc00) < 0x400) goto ilseq;
			if ((unsigned)(c-0xd800) < 0x400) {
				if (type-UCS2BE < 2U) goto ilseq;
				l = 4;
				if (*inb < 4) goto starved;
				d = get_16((void *)(*in + 2), type);
				if ((unsigned)(d-0xdc00) >= 0x400) goto ilseq;
				c = ((c-0xd7c0)<<10) + (d-0xdc00);
			}
			break;
		case SHIFT_JIS:
			if (c-0xa1 <= 0xdf-0xa1) {
				c += 0xff61-0xa1;
				break;
			}
			l = 2;
			if (*inb < 2) goto starved;
			d = *((unsigned char *)*in + 1);
			if (c-129 <= 159-129) c -= 129;
			else if (c-224 <= 239-224) c -= 193;
			else goto ilseq;
			c *= 2;
			if (d-64 <= 158-64) {
				if (d==127) goto ilseq;
				if (d>127) d--;
				d -= 64;
			} else if (d-159 <= 252-159) {
				c++;
				d -= 159;
			}
			c = jis0208[c][d];
			if (!c) goto ilseq;
			break;
		case EUC_JP:
			l = 2;
			if (*inb < 2) goto starved;
			d = *((unsigned char *)*in + 1);
			if (c==0x8e) {
				c = d;
				if (c-0xa1 > 0xdf-0xa1) goto ilseq;
				c += 0xff61 - 0xa1;
				break;
			}
			c -= 0xa1;
			d -= 0xa1;
			if (c >= 84 || d >= 94) goto ilseq;
			c = jis0208[c][d];
			if (!c) goto ilseq;
			break;
		case GB2312:
			if (c < 0xa1) goto ilseq;
		case GBK:
		case GB18030:
			c -= 0x81;
			if (c >= 126) goto ilseq;
			l = 2;
			if (*inb < 2) goto starved;
			d = *((unsigned char *)*in + 1);
			if (d < 0xa1 && type == GB2312) goto ilseq;
			if (d-0x40>=191 || d==127) {
				if (d-'0'>9 || type != GB18030)
					goto ilseq;
				l = 4;
				if (*inb < 4) goto starved;
				c = (10*c + d-'0') * 1260;
				d = *((unsigned char *)*in + 2);
				if (d-0x81>126) goto ilseq;
				c += 10*(d-0x81);
				d = *((unsigned char *)*in + 3);
				if (d-'0'>9) goto ilseq;
				c += d-'0';
				c += 128;
				for (d=0; d<=c; ) {
					k = 0;
					for (int i=0; i<126; i++)
						for (int j=0; j<190; j++)
							if (gb18030[i][j]-d <= c-d)
								k++;
					d = c+1;
					c += k;
				}
				break;
			}
			d -= 0x40;
			if (d>63) d--;
			c = gb18030[c][d];
			break;
		case BIG5:
			l = 2;
			if (*inb < 2) goto starved;
			d = *((unsigned char *)*in + 1);
			if (d-0x40>=0xff-0x40 || d-0x7f<0xa1-0x7f) goto ilseq;
			d -= 0x40;
			if (d > 0x3e) d -= 0x22;
			if (c-0xa1>=0xfa-0xa1) {
				if (c-0x87>=0xff-0x87) goto ilseq;
				if (c < 0xa1) c -= 0x87;
				else c -= 0x87 + (0xfa-0xa1);
				c = (hkscs[4867+(c*157+d)/16]>>(c*157+d)%16)%2<<17
					| hkscs[c*157+d];
				/* A few HKSCS characters map to pairs of UCS
				 * characters. These are mapped to surrogate
				 * range in the hkscs table then hard-coded
				 * here. Ugly, yes. */
				if (c/256 == 0xdc) {
					if (totype-0300U > 8) k = 2;
					else k = "\10\4\4\10\4\4\10\2\4"[totype-0300];
					if (k > *outb) goto toobig;
					x += iconv((iconv_t)(uintptr_t)to,
						&(char *){"\303\212\314\204"
						"\303\212\314\214"
						"\303\252\314\204"
						"\303\252\314\214"
						+c%256}, &(size_t){4},
						out, outb);
					continue;
				}
				if (!c) goto ilseq;
				break;
			}
			c -= 0xa1;
			c = big5[c][d]|(c==0x27&&(d==0x3a||d==0x3c||d==0x42))<<17;
			if (!c) goto ilseq;
			break;
		case EUC_KR:
			l = 2;
			if (*inb < 2) goto starved;
			d = *((unsigned char *)*in + 1);
			c -= 0xa1;
			d -= 0xa1;
			if (c >= 93 || d >= 94) {
				c += (0xa1-0x81);
				d += 0xa1;
				if (c >= 93 || c>=0xc6-0x81 && d>0x52)
					goto ilseq;
				if (d-'A'<26) d = d-'A';
				else if (d-'a'<26) d = d-'a'+26;
				else if (d-0x81<0xff-0x81) d = d-0x81+52;
				else goto ilseq;
				if (c < 0x20) c = 178*c + d;
				else c = 178*0x20 + 84*(c-0x20) + d;
				c += 0xac00;
				for (d=0xac00; d<=c; ) {
					k = 0;
					for (int i=0; i<93; i++)
						for (int j=0; j<94; j++)
							if (ksc[i][j]-d <= c-d)
								k++;
					d = c+1;
					c += k;
				}
				break;
			}
			c = ksc[c][d];
			if (!c) goto ilseq;
			break;
		default:
			if (c < 128+type) break;
			c -= 128+type;
			c = legacy_chars[ map[c*5/4]>>2*c%8 |
				map[c*5/4+1]<<8-2*c%8 & 1023 ];
			if (!c) c = *(unsigned char *)*in;
			if (c==1) goto ilseq;
		}

		switch (totype) {
		case WCHAR_T:
			if (*outb < sizeof(wchar_t)) goto toobig;
			*(wchar_t *)*out = c;
			*out += sizeof(wchar_t);
			*outb -= sizeof(wchar_t);
			break;
		case UTF_8:
			if (*outb < 4) {
				char tmp[4];
				k = wctomb_utf8(tmp, c);
				if (*outb < k) goto toobig;
				memcpy(*out, tmp, k);
			} else k = wctomb_utf8(*out, c);
			*out += k;
			*outb -= k;
			break;
		case US_ASCII:
			if (c > 0x7f) subst: x++, c='*';
		default:
			if (*outb < 1) goto toobig;
			if (c < 128+totype) {
			revout:
				*(*out)++ = c;
				*outb -= 1;
				break;
			}
			d = c;
			for (c=0; c<128-totype; c++) {
				if (d == legacy_chars[ tomap[c*5/4]>>2*c%8 |
					tomap[c*5/4+1]<<8-2*c%8 & 1023 ]) {
					c += 128;
					goto revout;
				}
			}
			goto subst;
		case UCS2BE:
		case UCS2LE:
		case UTF_16BE:
		case UTF_16LE:
			if (c < 0x10000 || type-UCS2BE < 2U) {
				if (c >= 0x10000) c = 0xFFFD;
				if (*outb < 2) goto toobig;
				put_16((void *)*out, c, totype);
				*out += 2;
				*outb -= 2;
				break;
			}
			if (*outb < 4) goto toobig;
			c -= 0x10000;
			put_16((void *)*out, (c>>10)|0xd800, totype);
			put_16((void *)(*out + 2), (c&0x3ff)|0xdc00, totype);
			*out += 4;
			*outb -= 4;
			break;
		case UTF_32BE:
		case UTF_32LE:
			if (*outb < 4) goto toobig;
			put_32((void *)*out, c, totype);
			*out += 4;
			*outb -= 4;
			break;
		}
	}
	return x;
ilseq:
	err = EILSEQ;
	x = -1;
	goto end;
toobig:
	err = E2BIG;
	x = -1;
	goto end;
starved:
	err = EINVAL;
	x = -1;
end:
	errno = err;
	return x;
}


text:
#include <stdlib.h>
#include <string.h>
#include "locale_impl.h"
#include "libc.h"

locale_t __newlocale(int mask, const char *name, locale_t loc)
{
	int i;

	if (!loc) {
		loc = calloc(1, sizeof *loc + LOCALE_NAME_MAX + 1);
		if (!loc) return 0;
		loc->messages_name = (void *)(loc+1);
		for (i=0; i<LC_ALL; i++)
			if (!(mask & (1<<i)))
				__setlocalecat(loc, i, "");
	}

	for (i=0; i<LC_ALL; i++)
		if (mask & (1<<i))
			__setlocalecat(loc, i, name);

	return loc;
}

weak_alias(__newlocale, newlocale);


text:
#include <stdint.h>
#include <string.h>

static inline uint32_t swapc(uint32_t x, int c)
{
	return c ? x>>24 | x>>8&0xff00 | x<<8&0xff0000 | x<<24 : x;
}

const char *__mo_lookup(const void *p, size_t size, const char *s)
{
	const uint32_t *mo = p;
	int sw = *mo - 0x950412de;
	uint32_t b = 0, n = swapc(mo[2], sw);
	uint32_t o = swapc(mo[3], sw);
	uint32_t t = swapc(mo[4], sw);
	if (n>=size/4 || o>=size-4*n || t>=size-4*n || ((o|t)%4))
		return 0;
	o/=4;
	t/=4;
	for (;;) {
		uint32_t ol = swapc(mo[o+2*(b+n/2)], sw);
		uint32_t os = swapc(mo[o+2*(b+n/2)+1], sw);
		if (os >= size || ol >= size-os || ((char *)p)[os+ol])
			return 0;
		int sign = strcmp(s, (char *)p + os);
		if (!sign) {
			uint32_t tl = swapc(mo[t+2*(b+n/2)], sw);
			uint32_t ts = swapc(mo[t+2*(b+n/2)+1], sw);
			if (ts >= size || tl >= size-ts || ((char *)p)[ts+tl])
				return 0;
			return (char *)p + ts;
		}
		else if (n == 1) return 0;
		else if (sign < 0)
			n /= 2;
		else {
			b += n/2;
			n -= n/2;
		}
	}
	return 0;
}


text:
#include <stdlib.h>
#include <ctype.h>

/*
grammar:

Start = Expr ';'
Expr  = Or | Or '?' Expr ':' Expr
Or    = And | Or '||' And
And   = Eq | And '&&' Eq
Eq    = Rel | Eq '==' Rel | Eq '!=' Rel
Rel   = Add | Rel '<=' Add | Rel '>=' Add | Rel '<' Add | Rel '>' Add
Add   = Mul | Add '+' Mul | Add '-' Mul
Mul   = Prim | Mul '*' Prim | Mul '/' Prim | Mul '%' Prim
Prim  = '(' Expr ')' | '!' Prim | decimal | 'n'

internals:

recursive descent expression evaluator with stack depth limit.
for binary operators an operator-precedence parser is used.
eval* functions store the result of the parsed subexpression
and return a pointer to the next non-space character.
*/

struct st {
	unsigned long r;
	unsigned long n;
	int op;
};

static const char *skipspace(const char *s)
{
	while (isspace(*s)) s++;
	return s;
}

static const char *evalexpr(struct st *st, const char *s, int d);

static const char *evalprim(struct st *st, const char *s, int d)
{
	char *e;
	if (--d < 0) return "";
	s = skipspace(s);
	if (isdigit(*s)) {
		st->r = strtoul(s, &e, 10);
		if (e == s || st->r == -1) return "";
		return skipspace(e);
	}
	if (*s == 'n') {
		st->r = st->n;
		return skipspace(s+1);
	}
	if (*s == '(') {
		s = evalexpr(st, s+1, d);
		if (*s != ')') return "";
		return skipspace(s+1);
	}
	if (*s == '!') {
		s = evalprim(st, s+1, d);
		st->r = !st->r;
		return s;
	}
	return "";
}

static int binop(struct st *st, int op, unsigned long left)
{
	unsigned long a = left, b = st->r;
	switch (op) {
	case 0: st->r = a||b; return 0;
	case 1: st->r = a&&b; return 0;
	case 2: st->r = a==b; return 0;
	case 3: st->r = a!=b; return 0;
	case 4: st->r = a>=b; return 0;
	case 5: st->r = a<=b; return 0;
	case 6: st->r = a>b; return 0;
	case 7: st->r = a<b; return 0;
	case 8: st->r = a+b; return 0;
	case 9: st->r = a-b; return 0;
	case 10: st->r = a*b; return 0;
	case 11: if (b) {st->r = a%b; return 0;} return 1;
	case 12: if (b) {st->r = a/b; return 0;} return 1;
	}
	return 1;
}

static const char *parseop(struct st *st, const char *s)
{
	static const char opch[11] = "|&=!><+-*%/";
	static const char opch2[6] = "|&====";
	int i;
	for (i=0; i<11; i++)
		if (*s == opch[i]) {
			/* note: >,< are accepted with or without = */
			if (i<6 && s[1] == opch2[i]) {
				st->op = i;
				return s+2;
			}
			if (i>=4) {
				st->op = i+2;
				return s+1;
			}
			break;
		}
	st->op = 13;
	return s;
}

static const char *evalbinop(struct st *st, const char *s, int minprec, int d)
{
	static const char prec[14] = {1,2,3,3,4,4,4,4,5,5,6,6,6,0};
	unsigned long left;
	int op;
	d--;
	s = evalprim(st, s, d);
	s = parseop(st, s);
	for (;;) {
		/*
		st->r (left hand side value) and st->op are now set,
		get the right hand side or back out if op has low prec,
		if op was missing then prec[op]==0
		*/
		op = st->op;
		if (prec[op] <= minprec)
			return s;
		left = st->r;
		s = evalbinop(st, s, prec[op], d);
		if (binop(st, op, left))
			return "";
	}
}

static const char *evalexpr(struct st *st, const char *s, int d)
{
	unsigned long a, b;
	if (--d < 0)
		return "";
	s = evalbinop(st, s, 0, d);
	if (*s != '?')
		return s;
	a = st->r;
	s = evalexpr(st, s+1, d);
	if (*s != ':')
		return "";
	b = st->r;
	s = evalexpr(st, s+1, d);
	st->r = a ? b : st->r;
	return s;
}

unsigned long __pleval(const char *s, unsigned long n)
{
	struct st st;
	st.n = n;
	s = evalexpr(&st, s, 100);
	return *s == ';' ? st.r : -1;
}


text:
#include <libintl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/stat.h>
#include <ctype.h>
#include "locale_impl.h"
#include "libc.h"
#include "atomic.h"

struct binding {
	struct binding *next;
	int dirlen;
	int active;
	char *domainname;
	char *dirname;
	char buf[];
};

static void *volatile bindings;

static char *gettextdir(const char *domainname, size_t *dirlen)
{
	struct binding *p;
	for (p=bindings; p; p=p->next) {
		if (!strcmp(p->domainname, domainname) && p->active) {
			*dirlen = p->dirlen;
			return (char *)p->dirname;
		}
	}
	return 0;
}

char *bindtextdomain(const char *domainname, const char *dirname)
{
	static int lock[2];
	struct binding *p, *q;

	if (!domainname) return 0;
	if (!dirname) return gettextdir(domainname, &(size_t){0});

	size_t domlen = strlen(domainname);
	size_t dirlen = strlen(dirname);
	if (domlen > NAME_MAX || dirlen >= PATH_MAX) {
		errno = EINVAL;
		return 0;
	}

	LOCK(lock);

	for (p=bindings; p; p=p->next) {
		if (!strcmp(p->domainname, domainname) &&
		    !strcmp(p->dirname, dirname)) {
			break;
		}
	}

	if (!p) {
		p = malloc(sizeof *p + domlen + dirlen + 2);
		if (!p) {
			UNLOCK(lock);
			return 0;
		}
		p->next = bindings;
		p->dirlen = dirlen;
		p->domainname = p->buf;
		p->dirname = p->buf + domlen + 1;
		memcpy(p->domainname, domainname, domlen+1);
		memcpy(p->dirname, dirname, dirlen+1);
		a_cas_p(&bindings, bindings, p);
	}

	a_store(&p->active, 1);

	for (q=bindings; q; q=q->next) {
		if (!strcmp(p->domainname, domainname) && q != p)
			a_store(&q->active, 0);
	}

	UNLOCK(lock);

	return (char *)p->dirname;
}

static const char catnames[][12] = {
	"LC_TIME",
	"LC_COLLATE",
	"LC_MONETARY",
	"LC_MESSAGES",
};

static const char catlens[] = { 7, 10, 11, 11 };

struct msgcat {
	struct msgcat *next;
	const void *map;
	size_t map_size;
	void *plural_rule;
	int nplurals;
	char name[];
};

static char *dummy_gettextdomain()
{
	return "messages";
}

weak_alias(dummy_gettextdomain, __gettextdomain);

const unsigned char *__map_file(const char *, size_t *);
int __munmap(void *, size_t);
unsigned long __pleval(const char *, unsigned long);

char *dcngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n, int category)
{
	static struct msgcat *volatile cats;
	struct msgcat *p;
	struct __locale_struct *loc = CURRENT_LOCALE;
	struct __locale_map *lm;
	const char *dirname, *locname, *catname;
	size_t dirlen, loclen, catlen, domlen;

	if (!domainname) domainname = __gettextdomain();

	domlen = strlen(domainname);
	if (domlen > NAME_MAX) goto notrans;

	dirname = gettextdir(domainname, &dirlen);
	if (!dirname) goto notrans;

	switch (category) {
	case LC_MESSAGES:
		locname = loc->messages_name;
		if (!*locname) goto notrans;
		break;
	case LC_TIME:
	case LC_MONETARY:
	case LC_COLLATE:
		lm = loc->cat[category-2];
		if (!lm) goto notrans;
		locname = lm->name;
		break;
	default:
notrans:
		return (char *) ((n == 1) ? msgid1 : msgid2);
	}

	catname = catnames[category-2];
	catlen = catlens[category-2];
	loclen = strlen(locname);

	size_t namelen = dirlen+1 + loclen+1 + catlen+1 + domlen+3;
	char name[namelen+1], *s = name;

	memcpy(s, dirname, dirlen);
	s[dirlen] = '/';
	s += dirlen + 1;
	memcpy(s, locname, loclen);
	s[loclen] = '/';
	s += loclen + 1;
	memcpy(s, catname, catlen);
	s[catlen] = '/';
	s += catlen + 1;
	memcpy(s, domainname, domlen);
	s[domlen] = '.';
	s[domlen+1] = 'm';
	s[domlen+2] = 'o';
	s[domlen+3] = 0;

	for (p=cats; p; p=p->next)
		if (!strcmp(p->name, name))
			break;

	if (!p) {
		void *old_cats;
		size_t map_size;
		const void *map = __map_file(name, &map_size);
		if (!map) goto notrans;
		p = malloc(sizeof *p + namelen + 1);
		if (!p) {
			__munmap((void *)map, map_size);
			goto notrans;
		}
		p->map = map;
		p->map_size = map_size;
		memcpy(p->name, name, namelen+1);
		do {
			old_cats = cats;
			p->next = old_cats;
		} while (a_cas_p(&cats, old_cats, p) != old_cats);
	}

	const char *trans = __mo_lookup(p->map, p->map_size, msgid1);
	if (!trans) goto notrans;

	/* Non-plural-processing gettext forms pass a null pointer as
	 * msgid2 to request that dcngettext suppress plural processing. */
	if (!msgid2) return (char *)trans;

	if (!p->plural_rule) {
		const char *rule = "n!=1;";
		unsigned long np = 2;
		const char *r = __mo_lookup(p->map, p->map_size, "");
		char *z;
		while (r && strncmp(r, "Plural-Forms:", 13)) {
			z = strchr(r, '\n');
			r = z ? z+1 : 0;
		}
		if (r) {
			r += 13;
			while (isspace(*r)) r++;
			if (!strncmp(r, "nplurals=", 9)) {
				np = strtoul(r+9, &z, 10);
				r = z;
			}
			while (*r && *r != ';') r++;
			if (*r) {
				r++;
				while (isspace(*r)) r++;
				if (!strncmp(r, "plural=", 7))
					rule = r+7;
			}
		}
		a_store(&p->nplurals, np);
		a_cas_p(&p->plural_rule, 0, (void *)rule);
	}
	if (p->nplurals) {
		unsigned long plural = __pleval(p->plural_rule, n);
		if (plural > p->nplurals) goto notrans;
		while (plural--) {
			size_t rem = p->map_size - (trans - (char *)p->map);
			size_t l = strnlen(trans, rem);
			if (l+1 >= rem)
				goto notrans;
			trans += l+1;
		}
	}
	return (char *)trans;
}

char *dcgettext(const char *domainname, const char *msgid, int category)
{
	return dcngettext(domainname, msgid, 0, 1, category);
}

char *dngettext(const char *domainname, const char *msgid1, const char *msgid2, unsigned long int n)
{
	return dcngettext(domainname, msgid1, msgid2, n, LC_MESSAGES);
}

char *dgettext(const char *domainname, const char *msgid)
{
	return dcngettext(domainname, msgid, 0, 1, LC_MESSAGES);
}


text:
#include <libintl.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

char *bind_textdomain_codeset(const char *domainname, const char *codeset)
{
	if (codeset && strcasecmp(codeset, "UTF-8"))
		errno = EINVAL;
	return NULL;
}


text:
#include <nl_types.h>

nl_catd catopen (const char *name, int oflag)
{
	return (nl_catd)-1;
}


text:
#include <stdlib.h>
#include "locale_impl.h"
#include "libc.h"

void freelocale(locale_t l)
{
	free(l);
}

weak_alias(freelocale, __freelocale);


text:
#include <stdlib.h>
#include <string.h>
#include "locale_impl.h"
#include "libc.h"

locale_t __duplocale(locale_t old)
{
	locale_t new = calloc(1, sizeof *new + LOCALE_NAME_MAX + 1);
	if (!new) return 0;
	new->messages_name = (void *)(new+1);

	if (old == LC_GLOBAL_LOCALE) old = &libc.global_locale;
	new->ctype_utf8 = old->ctype_utf8;
	if (old->messages_name)
		strcpy(new->messages_name, old->messages_name);

	if (new && old != LC_GLOBAL_LOCALE) memcpy(new, old, sizeof *new);
	return new;
}

weak_alias(__duplocale, duplocale);


text:
#include <locale.h>
#include <string.h>
#include "locale_impl.h"
#include "libc.h"
#include "atomic.h"

const char *__lctrans_impl(const char *msg, const struct __locale_map *lm)
{
	const char *trans = 0;
	if (lm) trans = __mo_lookup(lm->map, lm->map_size, msg);
	return trans ? trans : msg;
}

const unsigned char *__map_file(const char *, size_t *);
int __munmap(void *, size_t);
char *__strchrnul(const char *, int);

static struct __locale_map *findlocale(const char *name, size_t n)
{
	static void *loc_head;
	struct __locale_map *p, *new, *old_head;
	const char *path = 0, *z;
	char buf[256];
	size_t l;
	const void *map;
	size_t map_size;

	for (p=loc_head; p; p=p->next)
		if (!strcmp(name, p->name)) return p;

	if (!libc.secure) path = getenv("MUSL_LOCPATH");
	/* FIXME: add a default path? */
	if (!path) return 0;

	for (; *path; path=z+!!*z) {
		z = __strchrnul(path, ':');
		l = z - path - !!*z;
		if (l >= sizeof buf - n - 2) continue;
		memcpy(buf, path, l);
		buf[l] = '/';
		memcpy(buf+l+1, name, n);
		buf[l+1+n] = 0;
		map = __map_file(buf, &map_size);
		if (map) {
			new = malloc(sizeof *new);
			if (!new) {
				__munmap((void *)map, map_size);
				return 0;
			}
			new->map = map;
			new->map_size = map_size;
			memcpy(new->name, name, n);
			new->name[n] = 0;
			do {
				old_head = loc_head;
				new->next = old_head;
			} while (a_cas_p(&loc_head, old_head, new) != old_head);
			return new;
		}
	}
	return 0;
}

static const char envvars[][12] = {
	"LC_CTYPE",
	"LC_NUMERIC",
	"LC_TIME",
	"LC_COLLATE",
	"LC_MONETARY",
	"LC_MESSAGES",
};

int __setlocalecat(locale_t loc, int cat, const char *val)
{
	if (!*val) {
		(val = getenv("LC_ALL")) && *val ||
		(val = getenv(envvars[cat])) && *val ||
		(val = getenv("LANG")) && *val ||
		(val = "C.UTF-8");
	}

	size_t n;
	for (n=0; n<LOCALE_NAME_MAX && val[n] && val[n]!='/'; n++);
	if (val[0]=='.' || val[n]) val = "C.UTF-8";
	int builtin = (val[0]=='C' && !val[1])
		|| !strcmp(val, "C.UTF-8")
		|| !strcmp(val, "POSIX");
	struct __locale_map *data, *old;

	switch (cat) {
	case LC_CTYPE:
		a_store(&loc->ctype_utf8, !builtin || val[1]=='.');
		break;
	case LC_MESSAGES:
		if (builtin) {
			loc->messages_name[0] = 0;
		} else {
			memcpy(loc->messages_name, val, n);
			loc->messages_name[n] = 0;
		}
		/* fall through */
	default:
		data = builtin ? 0 : findlocale(val, n);
		if (data == loc->cat[cat-2]) break;
		do old = loc->cat[cat-2];
		while (a_cas_p(&loc->cat[cat-2], old, data) != old);
	case LC_NUMERIC:
		break;
	}
	return 0;
}


text:
#include <locale.h>
#include "locale_impl.h"
#include "libc.h"

static const char *dummy(const char *msg, const struct __locale_map *lm)
{
	return msg;
}

weak_alias(dummy, __lctrans_impl);

const char *__lctrans(const char *msg, const struct __locale_map *lm)
{
	return __lctrans_impl(msg, lm);
}

const char *__lctrans_cur(const char *msg)
{
	return __lctrans_impl(msg, CURRENT_LOCALE->cat[LC_MESSAGES-2]);
}


text:
#include <netinet/in.h>
#include <byteswap.h>

uint32_t htonl(uint32_t n)
{
	union { int i; char c; } u = { 1 };
	return u.c ? bswap_32(n) : n;
}


text:
#define _GNU_SOURCE
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include "syscall.h"
#include "libc.h"

int accept4(int fd, struct sockaddr *restrict addr, socklen_t *restrict len, int flg)
{
	if (!flg) return accept(fd, addr, len);
	int ret = socketcall_cp(accept4, fd, addr, len, flg, 0, 0);
	if (ret>=0 || (errno != ENOSYS && errno != EINVAL)) return ret;
	ret = accept(fd, addr, len);
	if (ret<0) return ret;
	if (flg & SOCK_CLOEXEC)
		__syscall(SYS_fcntl, ret, F_SETFD, FD_CLOEXEC);
	if (flg & SOCK_NONBLOCK)
		__syscall(SYS_fcntl, ret, F_SETFL, O_NONBLOCK);
	return ret;
}


text:
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <syscall.h>
#include <net/if.h>
#include <netinet/in.h>
#include "netlink.h"

#define IFADDRS_HASH_SIZE 64

/* getifaddrs() reports hardware addresses with PF_PACKET that implies
 * struct sockaddr_ll.  But e.g. Infiniband socket address length is
 * longer than sockaddr_ll.ssl_addr[8] can hold. Use this hack struct
 * to extend ssl_addr - callers should be able to still use it. */
struct sockaddr_ll_hack {
	unsigned short sll_family, sll_protocol;
	int sll_ifindex;
	unsigned short sll_hatype;
	unsigned char sll_pkttype, sll_halen;
	unsigned char sll_addr[24];
};

union sockany {
	struct sockaddr sa;
	struct sockaddr_ll_hack ll;
	struct sockaddr_in v4;
	struct sockaddr_in6 v6;
};

struct ifaddrs_storage {
	struct ifaddrs ifa;
	struct ifaddrs_storage *hash_next;
	union sockany addr, netmask, ifu;
	unsigned int index;
	char name[IFNAMSIZ+1];
};

struct ifaddrs_ctx {
	struct ifaddrs_storage *first;
	struct ifaddrs_storage *last;
	struct ifaddrs_storage *hash[IFADDRS_HASH_SIZE];
};

void freeifaddrs(struct ifaddrs *ifp)
{
	struct ifaddrs *n;
	while (ifp) {
		n = ifp->ifa_next;
		free(ifp);
		ifp = n;
	}
}

static void copy_addr(struct sockaddr **r, int af, union sockany *sa, void *addr, size_t addrlen, int ifindex)
{
	uint8_t *dst;
	int len;

	switch (af) {
	case AF_INET:
		dst = (uint8_t*) &sa->v4.sin_addr;
		len = 4;
		break;
	case AF_INET6:
		dst = (uint8_t*) &sa->v6.sin6_addr;
		len = 16;
		if (IN6_IS_ADDR_LINKLOCAL(addr) || IN6_IS_ADDR_MC_LINKLOCAL(addr))
			sa->v6.sin6_scope_id = ifindex;
		break;
	default:
		return;
	}
	if (addrlen < len) return;
	sa->sa.sa_family = af;
	memcpy(dst, addr, len);
	*r = &sa->sa;
}

static void gen_netmask(struct sockaddr **r, int af, union sockany *sa, int prefixlen)
{
	uint8_t addr[16] = {0};
	int i;

	if (prefixlen > 8*sizeof(addr)) prefixlen = 8*sizeof(addr);
	i = prefixlen / 8;
	memset(addr, 0xff, i);
	if (i < sizeof(addr)) addr[i++] = 0xff << (8 - (prefixlen % 8));
	copy_addr(r, af, sa, addr, sizeof(addr), 0);
}

static void copy_lladdr(struct sockaddr **r, union sockany *sa, void *addr, size_t addrlen, int ifindex, unsigned short hatype)
{
	if (addrlen > sizeof(sa->ll.sll_addr)) return;
	sa->ll.sll_family = AF_PACKET;
	sa->ll.sll_ifindex = ifindex;
	sa->ll.sll_hatype = hatype;
	sa->ll.sll_halen = addrlen;
	memcpy(sa->ll.sll_addr, addr, addrlen);
	*r = &sa->sa;
}

static int netlink_msg_to_ifaddr(void *pctx, struct nlmsghdr *h)
{
	struct ifaddrs_ctx *ctx = pctx;
	struct ifaddrs_storage *ifs, *ifs0;
	struct ifinfomsg *ifi = NLMSG_DATA(h);
	struct ifaddrmsg *ifa = NLMSG_DATA(h);
	struct rtattr *rta;
	int stats_len = 0;

	if (h->nlmsg_type == RTM_NEWLINK) {
		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
			if (rta->rta_type != IFLA_STATS) continue;
			stats_len = RTA_DATALEN(rta);
			break;
		}
	} else {
		for (ifs0 = ctx->hash[ifa->ifa_index % IFADDRS_HASH_SIZE]; ifs0; ifs0 = ifs0->hash_next)
			if (ifs0->index == ifa->ifa_index)
				break;
		if (!ifs0) return 0;
	}

	ifs = calloc(1, sizeof(struct ifaddrs_storage) + stats_len);
	if (ifs == 0) return -1;

	if (h->nlmsg_type == RTM_NEWLINK) {
		ifs->index = ifi->ifi_index;
		ifs->ifa.ifa_flags = ifi->ifi_flags;

		for (rta = NLMSG_RTA(h, sizeof(*ifi)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
			switch (rta->rta_type) {
			case IFLA_IFNAME:
				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
					ifs->ifa.ifa_name = ifs->name;
				}
				break;
			case IFLA_ADDRESS:
				copy_lladdr(&ifs->ifa.ifa_addr, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
				break;
			case IFLA_BROADCAST:
				copy_lladdr(&ifs->ifa.ifa_broadaddr, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifi->ifi_index, ifi->ifi_type);
				break;
			case IFLA_STATS:
				ifs->ifa.ifa_data = (void*)(ifs+1);
				memcpy(ifs->ifa.ifa_data, RTA_DATA(rta), RTA_DATALEN(rta));
				break;
			}
		}
		if (ifs->ifa.ifa_name) {
			unsigned int bucket = ifs->index % IFADDRS_HASH_SIZE;
			ifs->hash_next = ctx->hash[bucket];
			ctx->hash[bucket] = ifs;
		}
	} else {
		ifs->ifa.ifa_name = ifs0->ifa.ifa_name;
		ifs->ifa.ifa_flags = ifs0->ifa.ifa_flags;
		for (rta = NLMSG_RTA(h, sizeof(*ifa)); NLMSG_RTAOK(rta, h); rta = RTA_NEXT(rta)) {
			switch (rta->rta_type) {
			case IFA_ADDRESS:
				copy_addr(&ifs->ifa.ifa_addr, ifa->ifa_family, &ifs->addr, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
				break;
			case IFA_BROADCAST:
				/* For point-to-point links this is peer, but ifa_broadaddr
				 * and ifa_dstaddr are union, so this works for both.  */
				copy_addr(&ifs->ifa.ifa_broadaddr, ifa->ifa_family, &ifs->ifu, RTA_DATA(rta), RTA_DATALEN(rta), ifa->ifa_index);
				break;
			case IFA_LABEL:
				if (RTA_DATALEN(rta) < sizeof(ifs->name)) {
					memcpy(ifs->name, RTA_DATA(rta), RTA_DATALEN(rta));
					ifs->ifa.ifa_name = ifs->name;
				}
				break;
			}
		}
		if (ifs->ifa.ifa_addr)
			gen_netmask(&ifs->ifa.ifa_netmask, ifa->ifa_family, &ifs->netmask, ifa->ifa_prefixlen);
	}

	if (ifs->ifa.ifa_name) {
		if (!ctx->first) ctx->first = ifs;
		if (ctx->last) ctx->last->ifa.ifa_next = &ifs->ifa;
		ctx->last = ifs;
	} else {
		free(ifs);
	}
	return 0;
}

int getifaddrs(struct ifaddrs **ifap)
{
	struct ifaddrs_ctx _ctx, *ctx = &_ctx;
	int r;
	memset(ctx, 0, sizeof *ctx);
	r = __rtnetlink_enumerate(AF_UNSPEC, AF_UNSPEC, netlink_msg_to_ifaddr, ctx);
	if (r == 0) *ifap = &ctx->first->ifa;
	else freeifaddrs(&ctx->first->ifa);
	return r;
}


text:
#include <ctype.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "libc.h"

int __inet_aton(const char *s0, struct in_addr *dest)
{
	const char *s = s0;
	unsigned char *d = (void *)dest;
	unsigned long a[4] = { 0 };
	char *z;
	int i;

	for (i=0; i<4; i++) {
		a[i] = strtoul(s, &z, 0);
		if (z==s || (*z && *z != '.') || !isdigit(*s))
			return 0;
		if (!*z) break;
		s=z+1;
	}
	if (i==4) return 0;
	switch (i) {
	case 0:
		a[1] = a[0] & 0xffffff;
		a[0] >>= 24;
	case 1:
		a[2] = a[1] & 0xffff;
		a[1] >>= 16;
	case 2:
		a[3] = a[2] & 0xff;
		a[2] >>= 8;
	}
	for (i=0; i<4; i++) {
		if (a[i] > 255) return 0;
		d[i] = a[i];
	}
	return 1;
}

weak_alias(__inet_aton, inet_aton);


text:
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>

static int hexval(unsigned c)
{
	if (c-'0'<10) return c-'0';
	c |= 32;
	if (c-'a'<6) return c-'a'+10;
	return -1;
}

int inet_pton(int af, const char *restrict s, void *restrict a0)
{
	uint16_t ip[8];
	unsigned char *a = a0;
	int i, j, v, d, brk=-1, need_v4=0;

	if (af==AF_INET) {
		for (i=0; i<4; i++) {
			for (v=j=0; j<3 && isdigit(s[j]); j++)
				v = 10*v + s[j]-'0';
			if (j==0 || (j>1 && s[0]=='0') || v>255) return 0;
			a[i] = v;
			if (s[j]==0 && i==3) return 1;
			if (s[j]!='.') return 0;
			s += j+1;
		}
		return 0;
	} else if (af!=AF_INET6) {
		errno = EAFNOSUPPORT;
		return -1;
	}

	if (*s==':' && *++s!=':') return 0;

	for (i=0; ; i++) {
		if (s[0]==':' && brk<0) {
			brk=i;
			ip[i]=0;
			if (!*++s) break;
			continue;
		}
		for (v=j=0; j<4 && (d=hexval(s[j]))>=0; j++)
			v=16*v+d;
		if (j==0) return 0;
		ip[i] = v;
		if (!s[j] && (brk>=0 || i==7)) break;
		if (i==7) return 0;
		if (s[j]!=':') {
			if (s[j]!='.' || (i<6 && brk<0)) return 0;
			need_v4=1;
			i++;
			break;
		}
		s += j+1;
	}
	if (brk>=0) {
		memmove(ip+brk+7-i, ip+brk, 2*(i+1-brk));
		for (j=0; j<7-i; j++) ip[brk+j] = 0;
	}
	for (j=0; j<8; j++) {
		*a++ = ip[j]>>8;
		*a++ = ip[j];
	}
	if (need_v4 && inet_pton(AF_INET, (void *)s, a-4) <= 0) return 0;
	return 1;
}


text:
#include <stdint.h>

/* linux/netlink.h */

#define NETLINK_ROUTE 0

struct nlmsghdr {
	uint32_t	nlmsg_len;
	uint16_t	nlmsg_type;
	uint16_t	nlmsg_flags;
	uint32_t	nlmsg_seq;
	uint32_t	nlmsg_pid;
};

#define NLM_F_REQUEST	1
#define NLM_F_MULTI	2
#define NLM_F_ACK	4

#define NLM_F_ROOT	0x100
#define NLM_F_MATCH	0x200
#define NLM_F_ATOMIC	0x400
#define NLM_F_DUMP	(NLM_F_ROOT|NLM_F_MATCH)

#define NLMSG_NOOP	0x1
#define NLMSG_ERROR	0x2
#define NLMSG_DONE	0x3
#define NLMSG_OVERRUN	0x4

/* linux/rtnetlink.h */

#define RTM_NEWLINK	16
#define RTM_GETLINK	18
#define RTM_NEWADDR	20
#define RTM_GETADDR	22

struct rtattr {
	unsigned short	rta_len;
	unsigned short	rta_type;
};

struct rtgenmsg {
	unsigned char	rtgen_family;
};

struct ifinfomsg {
	unsigned char	ifi_family;
	unsigned char	__ifi_pad;
	unsigned short	ifi_type;
	int		ifi_index;
	unsigned	ifi_flags;
	unsigned	ifi_change;
};

/* linux/if_link.h */

#define IFLA_ADDRESS	1
#define IFLA_BROADCAST	2
#define IFLA_IFNAME	3
#define IFLA_STATS	7

/* linux/if_addr.h */

struct ifaddrmsg {
	uint8_t		ifa_family;
	uint8_t		ifa_prefixlen;
	uint8_t		ifa_flags;
	uint8_t		ifa_scope;
	uint32_t	ifa_index;
};

#define IFA_ADDRESS	1
#define IFA_LOCAL	2
#define IFA_LABEL	3
#define IFA_BROADCAST	4

/* musl */

#define NETLINK_ALIGN(len)	(((len)+3) & ~3)
#define NLMSG_DATA(nlh)		((void*)((char*)(nlh)+sizeof(struct nlmsghdr)))
#define NLMSG_DATALEN(nlh)	((nlh)->nlmsg_len-sizeof(struct nlmsghdr))
#define NLMSG_DATAEND(nlh)	((char*)(nlh)+(nlh)->nlmsg_len)
#define NLMSG_NEXT(nlh)		(struct nlmsghdr*)((char*)(nlh)+NETLINK_ALIGN((nlh)->nlmsg_len))
#define NLMSG_OK(nlh,end)	((char*)(end)-(char*)(nlh) >= sizeof(struct nlmsghdr))

#define RTA_DATA(rta)		((void*)((char*)(rta)+sizeof(struct rtattr)))
#define RTA_DATALEN(rta)	((rta)->rta_len-sizeof(struct rtattr))
#define RTA_DATAEND(rta)	((char*)(rta)+(rta)->rta_len)
#define RTA_NEXT(rta)		(struct rtattr*)((char*)(rta)+NETLINK_ALIGN((rta)->rta_len))
#define RTA_OK(nlh,end)		((char*)(end)-(char*)(rta) >= sizeof(struct rtattr))

#define NLMSG_RTA(nlh,len)	((void*)((char*)(nlh)+sizeof(struct nlmsghdr)+NETLINK_ALIGN(len)))
#define NLMSG_RTAOK(rta,nlh)	RTA_OK(rta,NLMSG_DATAEND(nlh))

int __rtnetlink_enumerate(int link_af, int addr_af, int (*cb)(void *ctx, struct nlmsghdr *h), void *ctx);


text:
#define _GNU_SOURCE
#include <netdb.h>

struct servent *getservbyname(const char *name, const char *prots)
{
	static struct servent se;
	static char *buf[2];
	struct servent *res;
	if (getservbyname_r(name, prots, &se, (void *)buf, sizeof buf, &res))
		return 0;
	return &se;
}


text:
#include <sys/socket.h>
#include "syscall.h"

int getsockopt(int fd, int level, int optname, void *restrict optval, socklen_t *restrict optlen)
{
	return socketcall(getsockopt, fd, level, optname, optval, optlen, 0);
}


text:
#include <resolv.h>
#include <string.h>
#include <time.h>
#include "libc.h"

int __res_mkquery(int op, const char *dname, int class, int type,
	const unsigned char *data, int datalen,
	const unsigned char *newrr, unsigned char *buf, int buflen)
{
	int id, i, j;
	unsigned char q[280];
	struct timespec ts;
	size_t l = strnlen(dname, 255);
	int n;

	if (l && dname[l-1]=='.') l--;
	n = 17+l+!!l;
	if (l>253 || buflen<n || op>15u || class>255u || type>255u)
		return -1;

	/* Construct query template - ID will be filled later */
	memset(q, 0, n);
	q[2] = op*8 + 1;
	q[5] = 1;
	memcpy((char *)q+13, dname, l);
	for (i=13; q[i]; i=j+1) {
		for (j=i; q[j] && q[j] != '.'; j++);
		if (j-i-1u > 62u) return -1;
		q[i-1] = j-i;
	}
	q[i+1] = type;
	q[i+3] = class;

	/* Make a reasonably unpredictable id */
	clock_gettime(CLOCK_REALTIME, &ts);
	id = ts.tv_nsec + ts.tv_nsec/65536UL & 0xffff;
	q[0] = id/256;
	q[1] = id;

	memcpy(buf, q, n);
	return n;
}

weak_alias(__res_mkquery, res_mkquery);


text:
#include <sys/socket.h>
#include "syscall.h"

int bind(int fd, const struct sockaddr *addr, socklen_t len)
{
	return socketcall(bind, fd, addr, len, 0, 0, 0);
}


text:
#include <netdb.h>
#include <string.h>

/* do we really need all these?? */

static int idx;
static const unsigned char protos[][8] = {
	"\000ip",
	"\001icmp",
	"\002igmp",
	"\003ggp",
	"\006tcp",
	"\014pup",
	"\021udp",
	"\026idp",
	"\051ipv6",
	"\057gre",
	"\062esp",
	"\063ah",
	"\072icmpv6",
	"\131ospf",
	"\136ipip",
	"\147pim",
	"\377raw",
	"\0\0"
};

void endprotoent(void)
{
	idx = 0;
}

void setprotoent(int stayopen)
{
	idx = 0;
}

struct protoent *getprotoent(void)
{
	static struct protoent p;
	static const char *aliases;
	if (!protos[idx][1]) return NULL;
	p.p_proto = protos[idx][0];
	p.p_name = (char *)protos[idx++]+1;
	p.p_aliases = (char **)&aliases;
	return &p;
}

struct protoent *getprotobyname(const char *name)
{
	struct protoent *p;
	endprotoent();
	do p = getprotoent();
	while (p && strcmp(name, p->p_name));
	return p;
}

struct protoent *getprotobynumber(int num)
{
	struct protoent *p;
	endprotoent();
	do p = getprotoent();
	while (p && p->p_proto != num);
	return p;
}


text:
#define _GNU_SOURCE

#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <inttypes.h>

int gethostbyaddr_r(const void *a, socklen_t l, int af,
	struct hostent *h, char *buf, size_t buflen,
	struct hostent **res, int *err)
{
	union {
		struct sockaddr_in sin;
		struct sockaddr_in6 sin6;
	} sa = { .sin.sin_family = af };
	socklen_t sl = af==AF_INET6 ? sizeof sa.sin6 : sizeof sa.sin;
	int i;

	*res = 0;

	/* Load address argument into sockaddr structure */
	if (af==AF_INET6 && l==16) memcpy(&sa.sin6.sin6_addr, a, 16);
	else if (af==AF_INET && l==4) memcpy(&sa.sin.sin_addr, a, 4);
	else {
		*err = NO_RECOVERY;
		return EINVAL;
	}

	/* Align buffer and check for space for pointers and ip address */
	i = (uintptr_t)buf & sizeof(char *)-1;
	if (!i) i = sizeof(char *);
	if (buflen <= 5*sizeof(char *)-i + l) return ERANGE;
	buf += sizeof(char *)-i;
	buflen -= 5*sizeof(char *)-i + l;

	h->h_addr_list = (void *)buf;
	buf += 2*sizeof(char *);
	h->h_aliases = (void *)buf;
	buf += 2*sizeof(char *);

	h->h_addr_list[0] = buf;
	memcpy(h->h_addr_list[0], a, l);
	buf += l;
	h->h_addr_list[1] = 0;
	h->h_aliases[0] = buf;
	h->h_aliases[1] = 0;

	switch (getnameinfo((void *)&sa, sl, buf, buflen, 0, 0, 0)) {
	case EAI_AGAIN:
		*err = TRY_AGAIN;
		return EAGAIN;
	case EAI_OVERFLOW:
		return ERANGE;
	default:
	case EAI_MEMORY:
	case EAI_SYSTEM:
	case EAI_FAIL:
		*err = NO_RECOVERY;
		return errno;
	case 0:
		break;
	}

	h->h_addrtype = af;
	h->h_name = h->h_aliases[0];
	*res = h;
	return 0;
}


text:
#include "libc.h"

void sethostent(int x)
{
}

void *gethostent()
{
	return 0;
}

void endhostent(void)
{
}

weak_alias(sethostent, setnetent);
weak_alias(gethostent, getnetent);
weak_alias(endhostent, endnetent);


text:
#include <sys/socket.h>
#include <limits.h>
#include "syscall.h"
#include "libc.h"

ssize_t recvmsg(int fd, struct msghdr *msg, int flags)
{
	ssize_t r;
#if LONG_MAX > INT_MAX
	struct msghdr h, *orig = msg;
	if (msg) {
		h = *msg;
		h.__pad1 = h.__pad2 = 0;
		msg = &h;
	}
#endif
	r = socketcall_cp(recvmsg, fd, msg, flags, 0, 0, 0);
#if LONG_MAX > INT_MAX
	if (orig) *orig = h;
#endif
	return r;
}


text:
#include <netdb.h>

void endservent(void)
{
}

void setservent(int stayopen)
{
}

struct servent *getservent(void)
{
	return 0;
}


text:
#ifndef LOOKUP_H
#define LOOKUP_H

#include <stdint.h>

struct address {
	int family;
	unsigned scopeid;
	uint8_t addr[16];
	int sortkey;
};

struct service {
	uint16_t port;
	char proto;
};

/* The limit of 48 results is a non-sharp bound on the number of addresses
 * that can fit in one 512-byte DNS packet full of v4 results and a second
 * packet full of v6 results. Due to headers, the actual limit is lower. */
#define MAXADDRS 48
#define MAXSERVS 2

int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int proto, int flags);
int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags);
int __lookup_ipliteral(struct address buf[static 1], const char *name, int family);

#endif


text:
#include <sys/socket.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include "syscall.h"
#include "libc.h"

ssize_t sendmsg(int fd, const struct msghdr *msg, int flags)
{
#if LONG_MAX > INT_MAX
	struct msghdr h;
	struct cmsghdr chbuf[1024/sizeof(struct cmsghdr)+1], *c;
	if (msg) {
		h = *msg;
		h.__pad1 = h.__pad2 = 0;
		msg = &h;
		if (h.msg_controllen) {
			if (h.msg_controllen > 1024) {
				errno = ENOMEM;
				return -1;
			}
			memcpy(chbuf, h.msg_control, h.msg_controllen);
			h.msg_control = chbuf;
			for (c=CMSG_FIRSTHDR(&h); c; c=CMSG_NXTHDR(&h,c))
				c->__pad1 = 0;
		}
	}
#endif
	return socketcall_cp(sendmsg, fd, msg, flags, 0, 0, 0);
}


text:
#define _GNU_SOURCE

#include <sys/socket.h>
#include <netdb.h>

int gethostbyname_r(const char *name,
	struct hostent *h, char *buf, size_t buflen,
	struct hostent **res, int *err)
{
	return gethostbyname2_r(name, AF_INET, h, buf, buflen, res, err);
}


text:
#include <netinet/in.h>
#include <byteswap.h>

uint16_t ntohs(uint16_t n)
{
	union { int i; char c; } u = { 1 };
	return u.c ? bswap_16(n) : n;
}


text:
#include <netinet/in.h>

const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;


text:
#define _GNU_SOURCE
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include "lookup.h"

#define ALIGN (sizeof(struct { char a; char *b; }) - sizeof(char *))

int getservbyname_r(const char *name, const char *prots,
	struct servent *se, char *buf, size_t buflen, struct servent **res)
{
	struct service servs[MAXSERVS];
	int cnt, proto, align;

	/* Align buffer */
	align = -(uintptr_t)buf & ALIGN-1;
	if (buflen < 2*sizeof(char *)+align)
		return ERANGE;
	buf += align;

	if (!prots) proto = 0;
	else if (!strcmp(prots, "tcp")) proto = IPPROTO_TCP;
	else if (!strcmp(prots, "udp")) proto = IPPROTO_UDP;
	else return EINVAL;

	cnt = __lookup_serv(servs, name, proto, 0);
	if (cnt<0) switch (cnt) {
	case EAI_MEMORY:
	case EAI_SYSTEM:
		return ENOMEM;
	default:
		return ENOENT;
	}

	se->s_name = (char *)name;
	se->s_aliases = (void *)buf;
	se->s_aliases[0] = se->s_name;
	se->s_aliases[1] = 0;
	se->s_port = htons(servs[0].port);
	se->s_proto = servs[0].proto == IPPROTO_TCP ? "tcp" : "udp";

	*res = se;
	return 0;
}


text:
#define _GNU_SOURCE
#include <stdio.h>
#include <netdb.h>

void herror(const char *msg)
{
	fprintf(stderr, "%s%s%s", msg?msg:"", msg?": ":"", hstrerror(h_errno));
}


text:
#include <sys/socket.h>
#include <fcntl.h>
#include <errno.h>
#include "syscall.h"

int socketpair(int domain, int type, int protocol, int fd[2])
{
	int r = socketcall(socketpair, domain, type, protocol, fd, 0, 0);
	if (r<0 && (errno==EINVAL || errno==EPROTONOSUPPORT)
	    && (type&(SOCK_CLOEXEC|SOCK_NONBLOCK))) {
		r = socketcall(socketpair, domain,
			type & ~(SOCK_CLOEXEC|SOCK_NONBLOCK),
			protocol, fd, 0, 0);
		if (r < 0) return r;
		if (type & SOCK_CLOEXEC) {
			__syscall(SYS_fcntl, fd[0], F_SETFD, FD_CLOEXEC);
			__syscall(SYS_fcntl, fd[1], F_SETFD, FD_CLOEXEC);
		}
		if (type & SOCK_NONBLOCK) {
			__syscall(SYS_fcntl, fd[0], F_SETFL, O_NONBLOCK);
			__syscall(SYS_fcntl, fd[1], F_SETFL, O_NONBLOCK);
		}
	}
	return r;
}