/*
 * Machinery to run routines off timers.
 */
#include "common.h"

#ifdef TIMERS
#ifndef lint
static char rcsid[] =
    "@(#) $Header: timer.c,v 1.2 90/12/27 22:16:27 sob Exp $ (NNTP with TIMERS)";
#endif
#else
#ifndef lint
static char rcsid[] =
    "@(#) $Header: timer.c,v 1.2 90/12/27 22:16:27 sob Exp $ (NNTP without TIMERS)";
#endif
#endif

#ifdef TIMERS
#include <sys/time.h>
#include "timer.h"
#ifndef USG
#ifndef FD_SETSIZE
/* Forward compatability */
#define FD_SET(n, p)    ((p)->fds_bits[0] |= (1<<(n)))
#define FD_CLR(n, p)    ((p)->fds_bits[0] &= ~(1<<(n)))
#define FD_ISSET(n, p)  ((p)->fds_bits[0] & (1<<(n)))
#define FD_ZERO(p)      ((p)->fds_bits[0] = 0)
#endif
#endif
/* non-portable */
#ifdef LINUX
#define BUFFERED_DATA(f) (((f)->_gptr - (f)->_egptr) > 0)
#else
#define BUFFERED_DATA(f) ((f)->_cnt > 0)
#endif

static long lastsecs;

/*
 * Should be called before first call to timer_sleep()
 */
void
timer_init(timers, ntimer)
	register struct timer *timers;
	register int ntimer;
{
	register int i;
	register struct timer *tp;

#ifdef SYSLOG
	if (ntimer <= 0)
		syslog(LOG_ERR,
		    "timer_init(): configuration error, %d timers\n", ntimer);
#endif

	/* Reset all timers */
	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
		tp->left = tp->seconds;

	/* Start clock */
	lastsecs = time((long *)0);
}

/*
 * Sleep until input or next timer needs to be run and then run any
 * expired timers. Returns true if input is available to be read.
 */
int
timer_sleep(timers, ntimer)
	register struct timer *timers;
	register int ntimer;
{
	register int i, n;
	register struct timer *tp;
	register long secs;
#ifdef USG
	long timeout;
	long readfds;
#else
	register struct timeval *timeoutp;
	struct timeval timeout;
	fd_set readfds;
#endif

	/* No need to do the select if there are characters in the buffer */
	if (BUFFERED_DATA(stdin))
		return(1);

	/* Length of next timeout is minimum of all "timers" */
#ifdef USG
	timeout = -1;
	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
		if (tp->left >= 0 &&
		    (tp->left < timeout || timeout < 0))
			timeout = tp->left;

	/* If active timeouts (this can easily happen), block until input */
	if (timeout < 0)
		timeout = 0;
#ifdef EXCELAN
	readfds = 1<<(fileno(stdin));
	timeout = timeout * 1000;     /* timeout needs to be in milliseconds */
#endif /* EXCELAN */
#else
	timeout.tv_sec = -1;
	timeout.tv_usec = 0;
	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
		if (tp->left >= 0 &&
		    (tp->left < timeout.tv_sec || timeout.tv_sec < 0))
			timeout.tv_sec = tp->left;

	/* If active timeouts (this can easily happen), block until input */
	if (timeout.tv_sec < 0)
		timeoutp = 0;
	else
		timeoutp = &timeout;

	/* Do select */
	FD_ZERO(&readfds);
	FD_SET(fileno(stdin), &readfds);
#endif /* !USG */
	errno = 0;
#ifdef EXCELAN
	n = select(fileno(stdin) + 1, &readfds, (long*)0, timeout);
#else
	n = select(fileno(stdin) + 1,
	    &readfds, (fd_set*)0, (fd_set*)0, timeoutp);
#endif
	/* "Interrupted system call" isn't a real error */
	if (n < 0 && errno != EINTR) {
#ifdef SYSLOG
		syslog(LOG_ERR, "%s read select: %m", hostname);
#endif
		exit(1);
	}

	/* Calculate off seconds since last time */
	secs = time((long *)0) - lastsecs;
	if (secs < 0)
		secs = 0;

	/* Subtract time from "timers" that have time remaining */
	for (i = ntimer, tp = timers; i > 0; --i, ++tp)
		if (tp->left > 0 && (tp->left -= secs) < 0)
			tp->left = 0;

	/* Update lastsecs */
	lastsecs += secs;

	/* If we have input, reset clock on guys that like it that way */
	if (n > 0)
		for (i = ntimer, tp = timers; i > 0; --i, ++tp)
			if (tp->resetoninput)
				tp->left = tp->seconds;

	/* Process "timers" that have timed out */
	for (i = ntimer, tp = timers; i > 0; --i, ++tp) {
		if (tp->left == 0) {
			(tp->subr)();
			/* resetoninput guys only get "reset on input" */
			if (tp->resetoninput)
				tp->left = -1;
			else
				tp->left = tp->seconds;
		}
	}

	/* Indicate no input */
	if (n <= 0)
		return(0);
	return(1);
	
}
#endif
