/*
 * Copyright (c)2004 The DragonFly Project.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 
 *   Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 * 
 *   Neither the name of the DragonFly Project nor the names of its
 *   contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission. 
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

/*
 * confed.c
 * Functions for working with configuration files.
 * Inspired by (but not derived from) sysinstall's variable.c
 * $Id: confed.c,v 1.1 2004/07/09 23:25:40 cpressey Exp $
 */

#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <dfui/mem.h>

#include "confed.h"

/*
 * Create a new, empty set of in-memory config variable settings.
 */
struct config_vars *
config_vars_new(void)
{
	struct config_vars *cvs;

	DFUI_MALLOC(cvs, config_vars);

	cvs->head = NULL;
	cvs->tail = NULL;

	return(cvs);
}

/*
 * Deallocate the memory used by a set of config variable settings.
 */
void
config_vars_free(struct config_vars *cvs)
{
	struct config_var *cv, *next;

	if (cvs == NULL)
		return;

	for (cv = cvs->head; cv != NULL; cv = next) {
		next = cv->next;
		config_var_free(cv);
	}

	DFUI_FREE(cvs, config_vars);
}

/*
 * Create a new configuration variable with the given name and value,
 * independent of any set of settings.  This is typically not called
 * directly by client code.
 */
struct config_var *
config_var_new(char *name, char *value)
{
	struct config_var *cv;

	DFUI_MALLOC(cv, config_var);

	cv->name = dfui_strdup(name);
	cv->value = dfui_strdup(value);
	cv->next = NULL;
	cv->prev = NULL;

	return(cv);
}

/*
 * Deallocate a configuration variable.
 * Typically not called directly by client code.
 */
void
config_var_free(struct config_var *cv)
{
	if (cv == NULL)
		return;
	if (cv->name != NULL)
		free(cv->name);
	if (cv->value != NULL)
		free(cv->value);
	DFUI_FREE(cv, config_var);
}

/*
 * Find a configuration variable with the given name in the given
 * set of settings and return a pointer to it, or NULL if not found.
 */
struct config_var *
config_var_find(struct config_vars *cvs, char *name)
{
	struct config_var *cv;

	for (cv = cvs->head; cv != NULL; cv = cv->next) {
		if (strcmp(cv->name, name) == 0)
			return(cv);
	}

	return(NULL);
}

/*
 * Get the value of a configuration variable in a set of settings
 * and return it, or a (constant) 0-length string if not found.
 */
char *
config_var_get(struct config_vars *cvs, char *name)
{
	struct config_var *cv;

	if ((cv = config_var_find(cvs, name)) == NULL) {
		return("");
	} else {
		return(cv->value);
	}
}

/*
 * Set the value of a configuration variable.  If the named variable
 * does not exist within the given set, a new one is created.
 */
struct config_var *
config_var_set(struct config_vars *cvs, char *name, char *value)
{
	struct config_var *cv;

	if ((cv = config_var_find(cvs, name)) == NULL) {
		if ((cv = config_var_new(name, value)) == NULL)
			return(NULL);

		cv->next = NULL;
		if (cvs->head == NULL) {
			cvs->head = cv;
		} else {
			cvs->tail->next = cv;
		}
		cv->prev = cvs->tail;
		cvs->tail = cv;

		return(cv);
	} else {
		if (cv->value != NULL)
			free(cv->value);
		cv->value = dfui_strdup(value);
		return(cv);
	}
}

/*
 * Write a set of configuration variable settings to a file.
 */
int
config_vars_write(struct config_vars *cvs, int config_type, char *fmt, ...)
{
	FILE *f;
	struct config_var *cv;
	va_list args;
	char *filename;
	char tstr[256];
	time_t t;

	va_start(args, fmt);
	vasprintf(&filename, fmt, args);
	va_end(args);

	if ((f = fopen(filename, "a")) == NULL)
		return(0);

	time(&t);
	strlcpy(tstr, ctime(&t), 256);
	if (strlen(tstr) > 0)
		tstr[strlen(tstr) - 1] = '\0';

	switch (config_type) {
	case CONFIG_TYPE_SH:
		fprintf(f, "\n");
		fprintf(f, "# -- BEGIN DragonFlyBSD Installer "
			   "automatically generated configuration  -- #\n");
		fprintf(f, "# -- Written on %s -- #\n", tstr);
		for (cv = cvs->head; cv != NULL; cv = cv->next) {
			fprintf(f, "%s=\"%s\"\n", cv->name, cv->value);
		}
		fprintf(f, "# -- END of DragonFlyBSD Installer "
			   "automatically generated configuration -- #\n");
		break;
	case CONFIG_TYPE_RESOLV:
		for (cv = cvs->head; cv != NULL; cv = cv->next) {
			fprintf(f, "%s\t\t%s\n", cv->name, cv->value);
		}
		break;
	default:
		fclose(f);
		return(0);
	}
	
	fclose(f);
	return(1);
}
