/*
 * __rt_sigtimedwait() for uClibc
 *
 * Copyright (C) 2006 by Steven Hill <sjhill@realitydiluted.com>
 * Copyright (C) 2000-2004 by Erik Andersen <andersen@codepoet.org>
 *
 * GNU Library General Public License (LGPL) version 2 or later.
 */

#include <sys/syscall.h>

#if defined(__NR_rt_sigtimedwait) || (defined(__NR_rt_sigtimedwait_time64) && defined(__UCLIBC_USE_TIME64__))
# include <signal.h>
# include <cancel.h>
# ifdef __UCLIBC_HAS_THREADS_NATIVE__
#  include <pthreadP.h>	/* SIGCANCEL */
# endif
# ifdef SIGCANCEL
#  define __need_NULL
#  include <stddef.h>
#  include <string.h>
# endif

#if defined(__UCLIBC_USE_TIME64__)
#include "internal/time64_helpers.h"
#endif

int __NC(sigtimedwait)(const sigset_t *set, siginfo_t *info,
		       const struct timespec *timeout)
{
# if defined SI_TKILL && defined SI_USER
	int result;
# endif
# ifdef SIGCANCEL
	sigset_t tmpset;

	if (set != NULL && (unlikely (__sigismember (set, SIGCANCEL))
#  ifdef SIGSETXID
		|| unlikely (__sigismember (set, SIGSETXID))
#  endif
		))
	{
		/* Create a temporary mask without the bit for SIGCANCEL set.  */
		// We are not copying more than we have to.
		memcpy (&tmpset, set, _NSIG / 8);
		__sigdelset (&tmpset, SIGCANCEL);
#  ifdef SIGSETXID
		__sigdelset (&tmpset, SIGSETXID);
#  endif
		set = &tmpset;
	}
# endif

/* if this is enabled, enable the disabled section in sigwait.c */
# if defined SI_TKILL && defined SI_USER
	/* XXX The size argument hopefully will have to be changed to the
	   real size of the user-level sigset_t.  */
	/* on uClibc we use the kernel sigset_t size */
# if defined(__UCLIBC_USE_TIME64__) && defined(__NR_rt_sigtimedwait_time64)
	result = INLINE_SYSCALL(rt_sigtimedwait_time64, 4, set, info,
				    TO_TS64_P(timeout), __SYSCALL_SIGSET_T_SIZE);
# else
	result = INLINE_SYSCALL(rt_sigtimedwait, 4, set, info,
				    timeout, __SYSCALL_SIGSET_T_SIZE);
# endif

	/* The kernel generates a SI_TKILL code in si_code in case tkill is
	   used.  tkill is transparently used in raise().  Since having
	   SI_TKILL as a code is useful in general we fold the results
	   here.  */
	if (result != -1 && info != NULL && info->si_code == SI_TKILL)
		info->si_code = SI_USER;

	return result;
# else
	/* on uClibc we use the kernel sigset_t size */
# if defined(__UCLIBC_USE_TIME64__) && defined(__NR_rt_sigtimedwait_time64)
	return INLINE_SYSCALL(rt_sigtimedwait_time64, 4, set, info,
			      TO_TS64_P(timeout), __SYSCALL_SIGSET_T_SIZE);
# else
	return INLINE_SYSCALL(rt_sigtimedwait, 4, set, info,
			      timeout, __SYSCALL_SIGSET_T_SIZE);
# endif
# endif
}
CANCELLABLE_SYSCALL(int, sigtimedwait,
		    (const sigset_t *set, siginfo_t *info, const struct timespec *timeout),
		    (set, info, timeout))
lt_libc_hidden(sigtimedwait)
#endif
