/* COPYING ******************************************************************
For copyright and licensing terms, see the file named COPYING.
// **************************************************************************
*/

#include <sys/types.h>
#include <sys/wait.h>
#include <cerrno>
#include <csignal>
#include "utils.h"

#if !defined(WSTOPPED)
#define WSTOPPED WUNTRACED
#endif

/* Waiting for child processes **********************************************
// **************************************************************************
// 
// This form of the library uses the "new" SVR4 waitid() function.
// That separates process group IDs from process IDs.
// It also makes WEXITED explicit.
*/

static inline
void
make_status (
	const siginfo_t & si,
	int & status,
	int & code
) {
	switch (si.si_code) {
		case CLD_EXITED:	status = WAIT_STATUS_EXITED; code = si.si_status; break;
		case CLD_KILLED:	status = WAIT_STATUS_SIGNALLED; code = si.si_status; break;
		case CLD_DUMPED:	status = WAIT_STATUS_SIGNALLED_CORE; code = si.si_status; break;
		case CLD_STOPPED:	status = WAIT_STATUS_PAUSED; code = si.si_status; break;
		case CLD_TRAPPED:	status = WAIT_STATUS_PAUSED; code = si.si_status; break;
		default:
		case CLD_CONTINUED:	status = WAIT_STATUS_RUNNING; code = 0; break;
	}
}

static inline
int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_for_anychild (
	pid_t & child,
	int & status,
	int & code,
	int flags
) {
	for (;;) {
		siginfo_t si;
		// The Windows NT Linux subsystem needs this; actual Linux does not, despite the manual.
		si.si_pid = si.si_signo = 0;
		const int rc(waitid(P_ALL, -1, &si, flags));
		if (0 > rc) {
			if (EINTR != errno) return -1;
		} else 
		if (0 == si.si_pid || 0 == si.si_signo) 
			return 0;
		else
		{
			make_status(si, status, code);
			child = si.si_pid;
			return 1;
		}
	}
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_anychild_stopcontexit (
	pid_t & child,
	int & status,
	int & code
) {
	return wait_for_anychild(child, status, code, WNOHANG|WSTOPPED|WCONTINUED|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_anychild_stopexit (
	pid_t & child,
	int & status,
	int & code
) {
	return wait_for_anychild(child, status, code, WNOHANG|WSTOPPED|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_anychild_exit (
	pid_t & child,
	int & status,
	int & code
) {
	return wait_for_anychild(child, status, code, WNOHANG|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_blocking_for_anychild_exit (
	pid_t & child,
	int & status,
	int & code
) {
	return wait_for_anychild(child, status, code, WEXITED);
}

static inline
int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_for_event_of (
	const pid_t child,
	int & status,
	int & code,
	int flags
) {
	for (;;) {
		siginfo_t si;
		const int rc(waitid(P_PID, child, &si, flags));
		if (0 > rc) {
			if (EINTR != errno) return -1;
		} else 
		if (0 == si.si_pid || 0 == si.si_signo) 
			return 0;
		else
		{
			make_status(si, status, code);
			return 1;
		}
	}
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_stopcontexit_of (
	const pid_t child,
	int & status,
	int & code
) {
	return wait_for_event_of(child, status, code, WNOHANG|WSTOPPED|WCONTINUED|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_stopexit_of (
	const pid_t child,
	int & status,
	int & code
) {
	return wait_for_event_of(child, status, code, WNOHANG|WSTOPPED|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_nonblocking_for_exit_of (
	const pid_t child,
	int & status,
	int & code
) {
	return wait_for_event_of(child, status, code, WNOHANG|WEXITED);
}

int	/// \retval -1 error \retval 0 no child \retval >0 found child
wait_blocking_for_exit_of (
	const pid_t child,
	int & status,
	int & code
) {
	return wait_for_event_of(child, status, code, WEXITED);
}
