/* SmoothWall libsmooth.
 *
 * This program is distributed under the terms of the GNU General Public
 * Licence.  See the file COPYING for details.
 *
 * (c) Lawrence Manning, 2001
 * Contains functions for manipulation files full of VAR=VAL pairs.
 * 
 * 2003-07-27 Robert Kerr - Added cooperative file locking to prevent any
 * clashes between setuid programs reading configuration and cgi scripts
 * trying to write it
 * 
 */
 
#include "libsmooth.h"

/* Sets up the list.  First entry is a dummy one to avoid having to special
 * case empty lists. */
struct keyvalue *initkeyvalues(void)
{
 	struct keyvalue *head = malloc(sizeof(struct keyvalue));
	
	strcpy(head->key, "KEY");
	strcpy(head->value, "VALUE");
	head->next = NULL;
	
	return head;
}

/* Splats all the entries in a list. */
void freekeyvalues(struct keyvalue *head)
{
	struct keyvalue *cur = head->next;
	struct keyvalue *next;
	
	while (cur)
	{
		next = cur->next;
		free(cur);
		cur = next;
	}
}

/* Reads from a file into a new list.  Uses appendkeyvalue to add entries.
 * Will bomb out on a error (eg bad format line). */
int readkeyvalues(struct keyvalue *head, char *filename)
{
 	FILE *file;
	char buffer[STRING_SIZE];
	char *temp;
	char *key, *value;
	
	if (!(file = fopen(filename, "r")))
		return 0;
		
	if (flock(fileno(file), LOCK_SH))
	{
		fclose(file);
		return 0;
	}
	
	while (fgets(buffer, STRING_SIZE, file))
	{
		temp = buffer;
		while (*temp)
		{
			if (*temp =='\n') *temp = '\0';
			temp++;
		}
		if (!strlen(buffer))
			continue;
		if (!(temp = strchr(buffer, '=')))
		{
			flock(fileno(file), LOCK_UN);
			fclose(file);
			return 0;
		}
		*temp = '\0';
		key = buffer; value = temp + 1;
		/* See if string is quoted.  If so, skip first quote, and
		 * nuke the one at the end. */
		if (value[0] == '\'')
		{
			value++;
			if ((temp = strrchr(value, '\'')))
				*temp = '\0';
			else
			{
				flock(fileno(file), LOCK_UN);
				fclose(file);
				return 0;
			}
		}
		if (strlen(key))
			appendkeyvalue(head, key, value);
	}
	
	flock(fileno(file), LOCK_UN);
	fclose(file);

	return 1;
}

/* Writes out a list to a file.  Easy. */
int writekeyvalues(struct keyvalue *head, char *filename)
{
	FILE *file;
	struct keyvalue *cur = head->next;
	
	if (!(file = fopen(filename, "w")))
		return 0;
		
	if (flock(fileno(file), LOCK_EX))
	{
		fclose(file);
		return 0;
	}
	
		
	while (cur)
	{
		/* No space in value?  If there is, we need to quote the value
		 * so the shell can read it. */
		if (!strchr(cur->value, ' '))
			fprintf(file, "%s=%s\n", cur->key, cur->value);
		else
			fprintf(file, "%s=\'%s\'\n", cur->key, cur->value);
		cur = cur->next;
	}
	flock(fileno(file), LOCK_UN);
	fclose(file);
	
	return 1;
}

/* Finds a key and copies the value back.  value must be at least STRING_SIZE
 * long. Would be nice to have a func that just returns a pointer to the value?
 */
int findkey(struct keyvalue *head, char *key, char *value)
{
	struct keyvalue *cur = head->next;

	while (cur)
	{
		if (strcmp(key, cur->key) == 0)
		{
			strncpy(value, cur->value, STRING_SIZE);
			value[STRING_SIZE-1] = '\0';
			return 1;
		}
		cur = cur->next;
	}

	return 0;
}

/* Appends a entry.  Not very efficent because it rescans the list looking
 * for the end.  Maybe fix this later. */
void appendkeyvalue(struct keyvalue *head, char *key, char *value)
{
	struct keyvalue *new = malloc(sizeof(struct keyvalue));
	struct keyvalue *cur = head->next;
	struct keyvalue *tail = head;

	strncpy(new->key, key, STRING_SIZE);
	strncpy(new->value, value, STRING_SIZE);
	new->key[STRING_SIZE-1] = '\0';
	new->value[STRING_SIZE-1] = '\0';
	new->next = NULL;

	while (cur)
	{
		tail = cur;
		cur = cur->next;
	}
	tail->next = new;
}

/* Otherwrites a key with a new value, or if it dosn't exist, appends it
 * on the end. */
void replacekeyvalue(struct keyvalue *head, char *key, char *value)
{
	struct keyvalue *cur = head->next;

	while (cur)
	{
		if (strcmp(cur->key, key) == 0)
		{
			strncpy(cur->value, value, STRING_SIZE);
			cur->value[STRING_SIZE-1] = '\0';
			return;
		}
		cur = cur->next;
	}

	appendkeyvalue(head, key, value);
}
