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

#include <internal.h>

#include <errno.h>

#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif

#include <strings.h>


int
ph_is_optionlist_cached(PH *ph)
{
	return (ph->ph_options != NULL ? 1 : 0);
}


/* ph_free_optionlist() - free option list. */
static void
ph_free_optionlist(struct ph_option *opt)
{
	int i;

	for (i = 0; opt[i].po_option != NULL; i++)
	{
		free(opt[i].po_option);
		free(opt[i].po_setting);
	}

	free(opt);
}


/*
** ph_retrieve_options() - obtain options from server.
**	0				success
**	-1				error (sets errno)
*/
int
ph_retrieve_options(PH *ph)
{
	int code, numopts = 0, save_errno;
	char *option, *setting;
	char buf[PH_BUF_SIZE];
	void *ptr;
	struct ph_option *opt;
	ph_memblock_t *blockp;

	/* don't load if already cached */
	if (ph_is_optionlist_cached(ph))
		return 0;

	if (ph_send_command(ph, "set") == -1)
		return -1;

	if (ph_mmgr_malloc(ph->ph_mmgr, ph_MMGR_ARRAY,
			   0, sizeof(struct ph_option),
			   (ph_free_func_t)ph_free_optionlist,
			   &blockp) == -1)
		return -1;
	opt = (struct ph_option *)ph_mmgr_ptr(blockp);

	while (ph_get_response(ph, &code, buf, sizeof(buf)) == 0)
	{
		if (code < LR_OK && code > 0)
			continue;
		if (code == LR_OK)
		{
			ph->ph_options = opt;
			ph_mmgr_free(blockp, 0);
			return 0;
		}
		if (code != -(LR_OK))
		{
			ph_mmgr_free(blockp, 1);
			errno = EINVAL;
			return -1;
		}

		if (ph_parse_response(buf, NULL, &option, &setting) == -1)
		{
			ph_mmgr_free(blockp, 1);
			errno = EINVAL;
			return -1;
		}

		if (ph_mmgr_array_add(blockp, numopts + 2) == -1)
		{
			save_errno = errno;
			ph_mmgr_free(blockp, 1);
			errno = save_errno;
			return -1;
		}
		opt = (struct ph_option *)ph_mmgr_ptr(blockp);

		opt[numopts].po_option = strdup(option);
		opt[numopts].po_setting = strdup(setting);
		if (opt[numopts].po_option == NULL
		    || opt[numopts].po_setting == NULL)
		{
			save_errno = errno;
			ph_mmgr_free(blockp, 1);
			errno = save_errno;
			return -1;
		}

		numopts++;
	}

	return 0;
}


/*
** ph_set_option() - set an option on the server.
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_DATAERR			no such option
*/
int
ph_set_option(PH *ph, char *option, char *setting)
{
	int code, i;

	if (ph_send_command(ph, "set %s%s%s", option, (setting ? "=" : ""),
			    (setting ? setting : "")) == -1)
		return -1;

	do
	{
		if (ph_get_response(ph, &code, NULL, 0) == -1)
			return -1;
	}
	while (code < LR_OK);

	if (code == LR_OPTION)
		return PH_ERR_DATAERR;
	if (code != LR_OK)
	{
		errno = EINVAL;
		return -1;
	}

	/* if the option list is stored in the local PH handle, update it */
	if (ph_is_optionlist_cached(ph))
		for (i = 0; ph->ph_options[i].po_option != NULL; i++)
			if (strcasecmp(ph->ph_options[i].po_option,
				       option) == 0)
			{
				free(ph->ph_options[i].po_setting);
				ph->ph_options[i].po_setting = strdup(setting
								      ? setting
								      : "on");
				if (ph->ph_options[i].po_setting == NULL)
					return -1;
				break;
			}

	return 0;
}


/*
** ph_get_option() - obtain the current setting of a given option.
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_DATAERR			no such option
*/
int
ph_get_option(PH *ph, char *option, char **setting)
{
	int i;

	if (!ph_is_optionlist_cached(ph)
	    && ph_retrieve_options(ph) == -1)
		return -1;

	for (i = 0; ph->ph_options[i].po_option != NULL; i++)
		if (strcasecmp(ph->ph_options[i].po_option, option) == 0)
		{
			*setting = ph->ph_options[i].po_setting;
			return 0;
		}

	return PH_ERR_DATAERR;
}


/*
** ph_option_iterate() - iterate through server option list.
** returns:
**	1				data returned
**	0				no more data
**	-1				error (sets errno)
*/
int
ph_option_iterate(PH *ph, struct ph_option **svropt)
{
	if (!ph_is_optionlist_cached(ph)
	    && ph_retrieve_options(ph) == -1)
		return -1;

	if (*svropt == NULL)
	{
		*svropt = &(ph->ph_options[0]);
		return 1;
	}

	/* get next element */
	(*svropt)++;
	if ((*svropt)->po_option != NULL)
		return 1;

	/* end of list */
	*svropt = NULL;
	return 0;
}


/*
** ph_free_options() - free option list associated with PH handle ph.
*/
void
ph_free_options(PH *ph)
{
	if (ph->ph_options != NULL)
		ph_free_optionlist(ph->ph_options);
	ph->ph_options = NULL;
}




syntax highlighted by Code2HTML, v. 0.9.1