/*
**  Copyright 2000-2004 University of Illinois Board of Trustees
**  Copyright 2000-2004 Mark D. Roth
**  All rights reserved.
**
**  options.c - libfget option code
**
**  Mark D. Roth <roth@feep.net>
*/

#include <internal.h>

#include <stdio.h>

#ifdef STDC_HEADERS
# include <stdarg.h>
#endif


/******************************************************************************
***  boolean type
******************************************************************************/

/*
** (ab)uses field_offset to indicate the option's bit value
** within the ftp_flags bitmask
*/

static void
_opt_get_boolean(FTP *ftp, int option,
		 size_t field_offset, size_t field_size, void *arg_ptr)
{
	unsigned short us;

	us = (BIT_ISSET(ftp->ftp_flags, field_offset)
	      ? 1
	      : 0);

	memcpy(arg_ptr, &us, sizeof(us));
}


static void
_opt_set_boolean(FTP *ftp, int option, size_t field_offset, va_list *argsp)
{
	unsigned int ui;

	/*
	** NOTE: we use int instead of short when calling va_arg(),
	** because short will be promoted to int when passed to a
	** varags function
	*/
	ui = va_arg(*argsp, unsigned int);

#ifdef DEBUG
	printf("==> _opt_set_boolean(option=%d, field_offset=%d, value=%u)\n",
	       option, (int)field_offset, ui);
#endif

	if (ui)
		BIT_SET(ftp->ftp_flags, field_offset);
	else
		BIT_CLEAR(ftp->ftp_flags, field_offset);
}


/******************************************************************************
***  time_t type
******************************************************************************/

static void
_opt_set_time_t(FTP *ftp, int option, size_t field_offset, va_list *argsp)
{
	time_t *ptr;

	ptr = (time_t *)((char *)ftp + field_offset);
	*ptr = va_arg(*argsp, time_t);
}


/******************************************************************************
***  long type
******************************************************************************/

static void
_opt_set_long(FTP *ftp, int option, size_t field_offset, va_list *argsp)
{
	long *ptr;

	ptr = (long *)((char *)ftp + field_offset);
	*ptr = va_arg(*argsp, long);
}


/******************************************************************************
***  pointer type
******************************************************************************/

static void
_opt_set_ptr(FTP *ftp, int option, size_t field_offset, va_list *argsp)
{
	void **ptr;

	ptr = (void **)((char *)ftp + field_offset);
	*ptr = va_arg(*argsp, void *);
}


/******************************************************************************
***  generic field get function
******************************************************************************/

static void
_opt_get_field(FTP *ftp, int option,
	       size_t field_offset, size_t field_size, void *arg_ptr)
{
	void *opt_ptr;

	opt_ptr = (void *)((char *)ftp + field_offset);

	memcpy(arg_ptr, opt_ptr, field_size);
}


/******************************************************************************
***  option description information
******************************************************************************/

/*
** finds the offset within the FTP handle of a given field
*/
#define ftp_field_offset(field) \
	((unsigned int) (((char *) (&(((FTP *)NULL)->field))) - ((char *)NULL)))

/*
** typedefs for get/set functions
*/
typedef void (*option_get_func_t)(FTP *, int, size_t, size_t, void *);
typedef void (*option_set_func_t)(FTP *, int, size_t, va_list *);

/*
** description of an option
*/
struct ftp_option_description
{
	int o_option;			/* option number */
	option_get_func_t o_get_func;	/* get function */
	option_set_func_t o_set_func;	/* set function */
	size_t o_offset;		/* offset of field into FTP structure */
	size_t o_size;			/* size of field's data type */
};
typedef struct ftp_option_description ftp_option_description_t;


/*
** here's the actual table of option descriptions
*/
static const ftp_option_description_t ftp_option_descriptions[] = {
	{
		FTP_OPT_PASSIVE,
		_opt_get_boolean,
		_opt_set_boolean,
		FTP_FLAG_USE_PASV,
		0
	},
	{
		FTP_OPT_USE_MLST,
		_opt_get_boolean,
		_opt_set_boolean,
		FTP_FLAG_USE_MLST,
		0
	},
	{
		FTP_OPT_USE_ABOR,
		_opt_get_boolean,
		_opt_set_boolean,
		FTP_FLAG_USE_ABOR,
		0
	},
	{
		FTP_OPT_IO_TIMEOUT,
		_opt_get_field,
		_opt_set_time_t,
		ftp_field_offset(ftp_io_timeout),
		sizeof(time_t)
	},
	{
		FTP_OPT_CACHE_MAXSIZE,
		_opt_get_field,
		_opt_set_long,
		ftp_field_offset(ftp_cache_maxsize),
		sizeof(long)
	},
	{
		FTP_OPT_CACHE_EXPIRE,
		_opt_get_field,
		_opt_set_long,
		ftp_field_offset(ftp_cache_expire),
		sizeof(long)
	},
	{
		FTP_OPT_SEND_HOOK,
		_opt_get_field,
		_opt_set_ptr,
		ftp_field_offset(ftp_send_hook),
		sizeof(ftp_hookfunc_t)
	},
	{
		FTP_OPT_RECV_HOOK,
		_opt_get_field,
		_opt_set_ptr,
		ftp_field_offset(ftp_recv_hook),
		sizeof(ftp_hookfunc_t)
	},
	{
		FTP_OPT_HOOK_DATA,
		_opt_get_field,
		_opt_set_ptr,
		ftp_field_offset(ftp_hook_data),
		sizeof(void *)
	},
	{
		0,
		NULL,
		NULL,
		0,
		0
	}
};


/*
** utility function for finding the right option description in the table
*/
static const ftp_option_description_t *
_find_option_description(int option)
{
	int i;

	for (i = 0; ftp_option_descriptions[i].o_option != 0; i++)
	{
		if (ftp_option_descriptions[i].o_option == option)
			return &(ftp_option_descriptions[i]);
	}

	return NULL;
}


/******************************************************************************
***  code for setting options
******************************************************************************/

void
_vftp_set_options(FTP *ftp, va_list args)
{
	const ftp_option_description_t *opt_desc;
	int option;

	while ((option = va_arg(args, int)) != 0)
	{
#ifdef DEBUG
		printf("    _vftp_set_options(): option=%d\n", option);
#endif
		opt_desc = _find_option_description(option);
		if (opt_desc == NULL)
			continue;

		(*(opt_desc->o_set_func))(ftp, option,
					  opt_desc->o_offset, &args);
	}
}


/*
** set options
*/
void
ftp_set_options(FTP *ftp, ...)
{
	va_list args;

	va_start(args, ftp);
	_vftp_set_options(ftp, args);
	va_end(args);
}


/******************************************************************************
***  code for getting options
******************************************************************************/

static void
_vftp_get_options(FTP *ftp, va_list args)
{
	const ftp_option_description_t *opt_desc;
	int option;
	void *arg_ptr;

	while ((option = va_arg(args, int)) != 0)
	{
		opt_desc = _find_option_description(option);
		if (opt_desc == NULL)
			return;

		arg_ptr = va_arg(args, void *);

		(*(opt_desc->o_get_func))(ftp, option,
					  opt_desc->o_offset,
					  opt_desc->o_size,
					  arg_ptr);
	}
}


/*
** get options
*/
void
ftp_get_options(FTP *ftp, ...)
{
	va_list args;

	va_start(args, ftp);
	_vftp_get_options(ftp, args);
	va_end(args);
}


/******************************************************************************
***  initialize options to default values
******************************************************************************/

/*
** set option defaults
*/
void
_ftp_init_options(FTP *ftp)
{
#ifdef DEBUG
	printf("==> _ftp_init_options(ftp=%p)\n", ftp);
#endif

	/*
	** ftp_connect() uses calloc(3) to create the FTP handle,
	** so we only need to set those options that have a non-zero
	** default value
	*/
	ftp_set_options(
		ftp,
		FTP_OPT_PASSIVE,	(unsigned short)1,
		FTP_OPT_IO_TIMEOUT,	(time_t)-1,
		FTP_OPT_CACHE_MAXSIZE,	(long)-1,
		FTP_OPT_CACHE_EXPIRE,	(long)-1,
		0
	);

#ifdef DEBUG
	printf("<== _ftp_init_options()\n");
#endif
}




syntax highlighted by Code2HTML, v. 0.9.1