/*
 *	$Source: /mit/tytso/src/gfinger/RCS/dofinger.c,v $
 *	$Author: tytso $
 *	$Header: dofinger.c,v 1.1 88/05/24 17:47:32 tytso Exp $
 */

#ifndef lint
static char *rcsid_dofinger_c = "$Header: dofinger.c,v 1.1 88/05/24 17:47:32 tytso Exp $";
#endif lint

#define SELECT_TIME_OUT	1
#define TIMEOUT_HOSTS_LOOP

#include <std.h>
#include <stddef.h>
extern "C" {
#include <sys/file.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netdb.h>
#include <sys/errno.h>
}

#include "gfinger.h"

static	fd_set	rchkfds, wchkfds;
static	char	*(*iter_proc)();
static	void	(*output_proc)();

HF	*get_new_hf();

HF	*top;			/* Top of linked list */
HF	*timed_out;		/* List of timed out hosts */

/*
 * Query the finger daemon for all hosts returned by the procedure
 * i_proc()
 */
do_finger(char *(*i_proc)(), void (*o_proc)())
{
	register HF	*q, *hf, *next;
	int	nfound, nfds = 0;
	struct timeval	timeout;
	char	*p;
	int	num = 0;	/* Number of queries left */

	iter_proc = i_proc;
	output_proc = o_proc;
	timed_out = NULL;
	
	FD_ZERO(&rchkfds);
	FD_ZERO(&wchkfds);
	num = 0;
	q = NULL;
	while ((num < max_hosts) && (hf = get_new_hf())) {
		hf_insert(&top, q, hf);
		q = hf;
		nfds = max(nfds, hf->fd);
		num++;
	}
	nfds++;

	while (top) {
		fd_set	readfds, writefds;
		
		readfds = rchkfds;
		writefds = wchkfds;
		timeout.tv_sec = SELECT_TIME_OUT;
		timeout.tv_usec = 0;
		nfound = select(nfds, &readfds, &writefds, NULL, &timeout);
		if (debug)
			printf("Select: nfound = %d, readfds = %d, writefds = %d\n",
			       nfound, readfds.fds_bits[0],
			       writefds.fds_bits[0]);
		if (nfound == -1) {
			if (errno == EINTR)
				continue;
			perror("select");
			error_abort(10);
		}
#ifndef TIMEOUT_HOSTS_LOOP
		if (nfound == 0) 
			timeout_hosts();
#endif
		for (hf = top; hf; hf = next) {
			next = hf->next;
			if (FD_ISSET(hf->fd, &writefds)) {
				hf_write(hf);
				if (hf->state = STATE_READING) {
					FD_CLR(hf->fd, &wchkfds);
					FD_SET(hf->fd, &rchkfds);
				}
			}
			if (FD_ISSET(hf->fd, &readfds))
				handle_read_request(hf);
		}
#ifdef TIMEOUT_HOSTS_LOOP
		timeout_hosts();
#endif
	}
	return(0);
}


handle_read_request(HF *hf)
{
	char		*host;
	register HF	*new_hf;
	
	if (!hf_read(hf))
		return;
	/* We've found an EOF */
	FD_CLR(hf->fd, &rchkfds);
	if (output_proc)
		(*output_proc)(hf);

	if (new_hf = get_new_hf())
		hf_insert(&top, hf, new_hf);
	hf_delete(&top, hf);
	hf_free(hf);
}


timeout_hosts()
{
	register HF	*hf, *next, *new_hf;
	long		timenow;
	
	if (debug)
		printf("Scanning for dead hosts....\n");
	timenow = time(0);
	for (hf = top; hf; hf = next) {
		next = hf->next;
		if ((timenow - hf->time) > ((hf->state == STATE_CONNECT) ?
					    connect_time_out :
					    host_time_out)) {
			if (debug)
				printf("Timing out host %s\n", hf->host);
			hf->state = STATE_TIMED_OUT;
			close(hf->fd);
			FD_CLR(hf->fd, &wchkfds);
			FD_CLR(hf->fd, &rchkfds);
			if (new_hf = get_new_hf())
				hf_insert(&top, hf, new_hf);
			hf_delete(&top, hf);
			hf_insert(&timed_out, NULL, hf);
		}
	}
}


HF *get_new_hf()
{
	register HF	*hf;
	char		*host;

	while (host = (*iter_proc)()) {
		if (hf = hf_startup(user_name, host, finger_port, large)) {
			FD_SET(hf->fd, &wchkfds);
			return(hf);
		}
	}
	return(NULL);
}

