/*
 *  Profile
 *  copyright (c) 2002-2003 Kazuki IWAMOTO http://www.maid.org/ iwm@maid.org

 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 *  2003-10-26 Takuro Ashie <ashie@homa.ne.jp>
 *      Copy section and key string before delete it.
 *
 *  2003-09-03 Takuro Ashie <ashie@homa.ne.jp>
 *      Added Kz and kz_ prefix.
 *      GObjectize.
 *      Remove also space befor a section when delete it.
 *      Added const keyword for value field of KzPrrofileList.
 *
 *  2003-08-28 Takuro Ashie <ashie@homa.ne.jp>
 *      Translated comments into English.
 *
 *  2003-08-27 Takuro Ashie <ashie@homa.ne.jp>
 *      Aggregated related codes into this file.
 *      Modified coding style.
 *      Simplize profile_open().
 */

#include "kz-profile.h"

#include <stdio.h>
#include <string.h>
#include <glib/gstdio.h>
#include "glib-utils.h"
#include "kz-marshalers.h"


enum {
	SECTION_ADDED_SIGNAL,
	SECTION_DELETED_SIGNAL,
	KEY_ADDED_SIGNAL,
	KEY_DELETED_SIGNAL,
	CHANGED_SIGNAL,
	LAST_SIGNAL
};

static void dispose    (GObject        *object);

static gint kz_profile_signals[LAST_SIGNAL] = {0};

G_DEFINE_TYPE(KzProfile, kz_profile, G_TYPE_OBJECT)

static void
kz_profile_class_init (KzProfileClass *klass)
{
	GObjectClass *object_class;

	object_class = (GObjectClass *) klass;

	object_class->dispose = dispose;

	klass->section_added   = NULL;
	klass->section_deleted = NULL;
	klass->key_added       = NULL;
	klass->key_deleted     = NULL;
	klass->changed         = NULL;

	kz_profile_signals[SECTION_ADDED_SIGNAL]
		= g_signal_new ("section-added",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				G_STRUCT_OFFSET (KzProfileClass, section_added),
				NULL, NULL,
				g_cclosure_marshal_VOID__STRING,
				G_TYPE_NONE, 1,
				G_TYPE_STRING);

	kz_profile_signals[SECTION_DELETED_SIGNAL]
		= g_signal_new ("section-deleted",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				G_STRUCT_OFFSET (KzProfileClass, section_deleted),
				NULL, NULL,
				g_cclosure_marshal_VOID__STRING,
				G_TYPE_NONE, 1,
				G_TYPE_STRING);

	kz_profile_signals[KEY_ADDED_SIGNAL]
		= g_signal_new ("key-added",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				G_STRUCT_OFFSET (KzProfileClass, key_added),
				NULL, NULL,
				_kz_marshal_VOID__STRING_STRING,
				G_TYPE_NONE, 2,
				G_TYPE_STRING, G_TYPE_STRING);

	kz_profile_signals[KEY_DELETED_SIGNAL]
		= g_signal_new ("key-deleted",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				G_STRUCT_OFFSET (KzProfileClass, key_deleted),
				NULL, NULL,
				_kz_marshal_VOID__STRING_STRING,
				G_TYPE_NONE, 2,
				G_TYPE_STRING, G_TYPE_STRING);

	kz_profile_signals[CHANGED_SIGNAL]
		= g_signal_new ("changed",
				G_TYPE_FROM_CLASS (klass),
				G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED,
				G_STRUCT_OFFSET (KzProfileClass, changed),
				NULL, NULL,
				_kz_marshal_VOID__STRING_STRING_STRING,
				G_TYPE_NONE, 3,
				G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
}


static void
kz_profile_init (KzProfile *profile)
{
	profile->save_eachtime = TRUE;
	
	profile->edit    = FALSE;
	profile->file    = NULL;
	profile->subfile = NULL;
	profile->list    = NULL;
	profile->sublist = NULL;
}


static void
dispose (GObject *object)
{
	KzProfile *profile = KZ_PROFILE(object);
	KzProfileList *p, *q;

	if (profile->file)
	{
		g_free(profile->file);
		profile->file = NULL;
	}

	if (profile->subfile)
	{
		g_free(profile->subfile);
		profile->subfile = NULL;
	}

	for (p = profile->list; p; p = q)
	{
		q = p->next;
		g_free(p->data);
		g_free(p->section);
		g_free(p->key);
		g_free(p);
	}
	profile->list = NULL;

	for (p = profile->sublist; p; p = q)
	{
		q = p->next;
		g_free(p->data);
		g_free(p->section);
		g_free(p->key);
		g_free(p);
	}
	profile->sublist = NULL;

	if (G_OBJECT_CLASS (kz_profile_parent_class)->dispose)
		G_OBJECT_CLASS (kz_profile_parent_class)->dispose(object);
}


KzProfile *
kz_profile_new (void)
{
	KzProfile *profile = g_object_new(KZ_TYPE_PROFILE, NULL);
	return profile;
}


/******************************************************************************
*                                                                             *
* Converting between numerical value and string functions                     *
*                                                                             *
******************************************************************************/
const static gchar hex[16]={'0', '1', '2', '3', '4', '5', '6', '7',
			    '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

/*
 * string -> value
 * value,value
 * str,string
 * radix,radix
 * flag,TRUE:signed,FALSE:unsigned
 * RET,TRUE:normal exit,FALSE:error
 */
static gboolean
misc_str_to_val(gint *value,const gchar *str,const gint radix,
		const gboolean flag)
{
	gchar c;
	gint i,j,t;

	*value = 0;
	for (i = 0; str[i] != '\0' && str[i] == ' '; i++);
	if (str[i] == '\0')
		return FALSE;
	if (flag && str[i] == '-')
	{
		i++;
		while (str[i] != '\0')
		{
			t = *value;
			*value *= radix;
			c = g_ascii_toupper(str[i]);
			for (j = 0; j < radix; j++)
				if (hex[j] == c)
					break;
			*value += j;
			if (j == radix || *value < t)
			{
				*value = t;
				return FALSE;
			}
			i++;
		}
		if (*value < 0)
		{
			*value = 0;
			return FALSE;
		}
		*value =- *value;
	}
	else
	{
		while (str[i] != '\0')
		{
			t = *value;
			*value *= radix;
			c = g_ascii_toupper(str[i]);
			for (j = 0; j < radix; j++)
				if (hex[j] == c)
					break;
			*value += j;
			if (j == radix || *value < t)
			{
				*value = t;
				return FALSE;
			}
			i++;
		}
	}
	return TRUE;
}


/*
 * string -> array of value
 * size,bytes
 * text,string
 * radix,radix
 * flag,TRUE:signed,FALSE:unsigned
 * RET,array of value
 */
static guint8 *
misc_str_to_array(gint *size,const gchar *text,
		  const gint radix,const gboolean flag)
{
	gchar *p;
	guint8 *array = NULL;
	gint i, j, value;

	*size = 0;
	if (!text)
		return NULL;
	p = g_malloc((g_strlen(text) + 2) * sizeof(gchar));
	strcpy(p, text);
	for (i = 0; p[i] != '\0'; i++)
	{
		for (j = 0; j < radix; j++)
			if (p[i] == hex[j])
				break;
		if (j >= radix)
			p[i] = '\0';
	}
	p[i+1] = '\0';
	i = 0;
	while (p[i] != '\0')
	{
		if (!misc_str_to_val(&value, p+i, radix, flag))
		{
			g_free(p);
			g_free(array);
			*size = 0;
			return NULL;
		}
		array = g_realloc(array, *size + 1);
		array[(*size)++] = value;
		i += g_strlen(p + i) + 1;
	}
	g_free(p);
	return array;
}

/******************************************************************************
*                                                                             *
* Initialize file related functions                                           *
*                                                                             *
******************************************************************************/
/*
 * listize the file.
 * file,file name
 * RET,list
 */
static KzProfileList *
kz_profile_list (const gchar *file)
{
	gchar buf[256], *data = NULL, *tmp, *section = NULL;
	gint n;
	FILE *fio;
	KzProfileList *p = NULL, *q, *r;

	fio = g_fopen(file, "rt");
	if (!fio)
		return NULL;
	while (fgets (buf, 256, fio))
	{
		if (!data)
		{
			data = g_strdup(buf);
		}
		else
		{
			tmp = data;
			data = g_strconcat(tmp, buf, NULL);
			g_free(tmp);
		}
		n = data ? g_strlen(data) : 0;
		if (n > 0 && data[n - 1] == '\n')
		{
			data[n - 1] = '\0';
			q = g_malloc0(sizeof(KzProfileList));
			q->data = data;
			q->prev = p;
			if (p)
				p->next = q;
			p = q;
			data = NULL;
		}
	}
	if (fclose(fio) != 0 || !p)
	{
		while (p)
		{
			q = p->prev;
			g_free(p->data);
			g_free(p);
			p = q;
		}
		return NULL;
	}
	while (p->prev)
		p = p->prev;
	r = p;
	/* arrange the list */
	while (p)
	{
		data = p->data ? g_strstrip(g_strdup(p->data)) : NULL;
		n = data ? g_strlen(data) : 0;
		if (n <= 0)
		{
			p->type = KZ_PROFILE_DATA_TYPE_SPACE;
		}
		else if (data[0] == '#' || data[0] == ';')
		{
			p->type = KZ_PROFILE_DATA_TYPE_COMMENT;
		}
		else if (data[0] == '[' && data[n - 1] == ']')
		{
			p->type = KZ_PROFILE_DATA_TYPE_SECTION;
			g_free (section);
			section = g_strdup(data + 1);
			section[n - 2] = '\0';
			for (q = p->prev; q && q->type == KZ_PROFILE_DATA_TYPE_SPACE;
			     q = q->prev)
			{
				g_free(q->section);
				q->section = NULL;
			}
		}
		else if (strchr(data, '='))
		{
			p->type = KZ_PROFILE_DATA_TYPE_KEY;
			p->key = g_strdup(data);
			*strchr(p->key, '=') = '\0';
			p->value = strchr(p->data, '=') + 1;
		}
		else
		{
			p->type = KZ_PROFILE_DATA_TYPE_UNKNOW;
		}
		p->section = g_strdup(section);
		g_free(data);
		p = p->next;
	}
	g_free (section);
	return r;
}


/*
 * Open the initialize file.
 * file,file name
 * RET,the KzProfile struct
 */
KzProfile *
kz_profile_open (const gchar *file, const gchar *subfile)
{
	KzProfile *profile;

	profile = kz_profile_new();

	profile->subfile = subfile ? g_strdup(subfile) : NULL;
	profile->sublist = profile->subfile
		? kz_profile_list(profile->subfile) : NULL;

	profile->file = file ? g_strdup(file) : NULL;
	profile->list = profile->file
		? kz_profile_list(profile->file) : NULL;

	return profile;
}


/*
 * Close the initialize file.
 * profile,the KzProfile struct
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_close (KzProfile *profile)
{
	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	kz_profile_save (profile);
	g_object_run_dispose(G_OBJECT(profile));

	return TRUE;
}


/*
 * Save the initialize file.
 * profile,the KzProfile struct
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_save (KzProfile *profile)
{
	FILE *fio;
	KzProfileList *p;
	
	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	if (!profile->edit) return TRUE;
	if (!profile->file) return FALSE;

	fio = g_fopen(profile->file, "wt");
	if (!fio) return FALSE;

	/* when the profile was modified */
	for (p = profile->list; p; p = p->next)
	{
		if (p->data)
			fputs(p->data, fio);
		fputc('\n', fio);
	}
	fclose(fio);

	profile->edit = FALSE;

	return TRUE;
}


/*
 * Get a string from the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * key,name of the key
 * RET,string,NULL:error
 */
gchar *
kz_profile_get_string (KzProfile *profile,
		       const gchar *section,const gchar *key)
{
	KzProfileList *p;
	
	g_return_val_if_fail(KZ_IS_PROFILE(profile), NULL);

	if (!profile || !section || !key)
		return NULL;
	for (p = profile->list; p; p = p->next)
	{
		if (p->type == KZ_PROFILE_DATA_TYPE_KEY 
		    && g_strcmp(p->section, section) == 0
		    && g_strcmp(p->key, key) == 0)
		{
			return g_strdup(p->value);
		}
	}
	for (p = profile->sublist; p; p = p->next)
	{
		if (p->type == KZ_PROFILE_DATA_TYPE_KEY
		    && g_strcmp(p->section, section) == 0
		    && g_strcmp(p->key, key) == 0)
		{
			return g_strdup(p->value);
		}
	}
	return NULL;
}


/*
 * Get size of a value.
 * profile,the KzProfile struct
 * section,name of the section
 * key,name of the key
 * type,value type
 * RET,bytes,0:error
 */
gint
kz_profile_get_size (KzProfile *profile,
		     const gchar *section,const gchar *key,const guint type)
{
	guint8 *array;
	gint n;
	KzProfileList *p;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), 0);

	if (!section || !key)
		return 0;
	for (p = profile->list; p; p = p->next)
	{
		if (p->type == KZ_PROFILE_DATA_TYPE_KEY
		    && g_strcmp(p->section, section) == 0
		    && g_strcmp(p->key, key) == 0)
		{
			break;
		}
	}
	if (!p)
	{
		for (p = profile->sublist; p; p = p->next)
			if (p->type == KZ_PROFILE_DATA_TYPE_KEY
			    && g_strcmp(p->section, section) == 0
			    && g_strcmp(p->key, key) == 0)
			{
				break;
			}
	}
	if (!p)
		return 0;
	switch (type)
	{
	case KZ_PROFILE_VALUE_TYPE_BOOL:
		return g_strcmp(p->value, "true") == 0
			|| g_strcmp(p->value, "false") == 0 ? sizeof(gboolean) : 0;
	case KZ_PROFILE_VALUE_TYPE_INT:
		return sizeof(gint);
	case KZ_PROFILE_VALUE_TYPE_STRING:
		return g_strlen(p->value) + 1;
	case KZ_PROFILE_VALUE_TYPE_ARRAY:
		if (!(array=misc_str_to_array(&n, p->value, 10, FALSE)))
			return 0;
		g_free(array);
		return n;
	}
	return 0;
}


/*
 * Get a value from the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * key,name of the key
 * value,buffer to store value.
 * size,size of buffer to store value.
 * type,value type
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_get_value (KzProfile *profile,
		      const gchar *section,const gchar *key,
		      gpointer value,const gint size,const guint type)
{
	guint8 *array;
	gint n;
	KzProfileList *p;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	if (!section || !key || !value)
		return FALSE;
	for (p = profile->list; p; p = p->next)
	{
		if (p->type == KZ_PROFILE_DATA_TYPE_KEY
		    && g_strcmp(p->section, section) == 0
		    && g_strcmp(p->key, key) == 0)
		{
			break;
		}
	}
	if (!p)
	{
		for (p = profile->sublist; p; p = p->next)
			if (p->type == KZ_PROFILE_DATA_TYPE_KEY
			    && g_strcmp(p->section, section) == 0
			    && g_strcmp(p->key, key) == 0)
			{
				break;
			}
	}
	if (!p)
		return FALSE;
	switch (type)
	{
	case KZ_PROFILE_VALUE_TYPE_BOOL:
		if (size < sizeof(gboolean))
			return FALSE;
		if (g_ascii_strcasecmp (p->value, "true") == 0)
			*((gboolean *)value) = TRUE;
		else if (g_ascii_strcasecmp (p->value, "false") == 0)
			*((gboolean *)value) = FALSE;
		else
			return FALSE;
		break;
	case KZ_PROFILE_VALUE_TYPE_INT:
		if (size < sizeof(gint))
			return FALSE;
		misc_str_to_val((gint *)value, p->value, 10, TRUE);
		break;
	case KZ_PROFILE_VALUE_TYPE_STRING:
		if (size < g_strlen(p->value) + 1)
			return FALSE;
		strcpy((gchar *)value,p->value);
		break;
	case KZ_PROFILE_VALUE_TYPE_ARRAY:
		if (!(array = misc_str_to_array(&n, p->value, 10, FALSE)))
			return FALSE;
		if (size <= n)
			g_memmove(value, array, size);
		g_free(array);
		if (n < size)
			return FALSE;
		break;
	default:
		return FALSE;
	}
	return TRUE;
}


/*
 * Set a value into the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * key,name of the key
 * value,buffer which store the value.
 * size,size of buffer which store the value.
 * type,value type
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_set_value (KzProfile *profile,
		      const gchar *section,const gchar *key,
		      gconstpointer value,const size_t size,const guint type)
{
	gchar *old_value = NULL;
	gint i;
	KzProfileList *p,*q = NULL;
	GQuark quark;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	if (!section || !key || !value)
		return FALSE;

	quark = g_quark_from_string(section);

	for (p = profile->list; p; q = p, p = p->next)
	{
		if (p->type == KZ_PROFILE_DATA_TYPE_KEY
		    && g_strcmp(p->section, section) == 0
		    && g_strcmp(p->key, key) == 0)
		{
			break;
		}
	}

	if (p)
	{
		if (p->data)
			old_value = g_strdup(p->data);
	}
	else
	{
		for (p = q; p; p = p->prev)
		{
			if (p->section && g_strcmp(p->section, section) == 0)
				break;
		}
		if (!p)
		{
			if (q)
			{
				/* insert space between previous data and current data */
				p = g_malloc(sizeof(KzProfileList));
				p->type=KZ_PROFILE_DATA_TYPE_SPACE;
				p->value = p->data = p->section = p->key = NULL;
				p->prev = q;
				p->next = q->next;
				q->next = p;
				q = p;
			}
			/* create a section if neither section and key aren't exist */
			p = g_malloc(sizeof(KzProfileList));
			p->type = KZ_PROFILE_DATA_TYPE_SECTION;
			p->data = g_strdup_printf("[%s]", section);
			p->section = g_strdup(section);
			p->value = p->key = NULL;
			p->prev = q;
			if (q)
			{
				p->next = q->next;
				q->next = p;
			}
			else
			{
				p->next = NULL;
				profile->list = p;
			}
			g_signal_emit(profile,
				      kz_profile_signals[SECTION_ADDED_SIGNAL],
				      quark, p->section);
		}
		q = p;
		while (q->type == KZ_PROFILE_DATA_TYPE_SPACE && q->section
		       && g_strcmp(p->section, section) == 0 && q->prev)
			q = q->prev;
		/* create a key into last of the section */
		p = g_malloc(sizeof(KzProfileList));
		p->type = KZ_PROFILE_DATA_TYPE_KEY;
		p->data = g_strdup_printf("%s=", key);
		p->section = g_strdup(section);
		p->key = g_strdup(key);
		p->value = strchr(p->data,'=')+1;
		p->prev = q;
		p->next = q->next;
		q->next = p;
		if (p->next)
			p->next->prev = p;
		g_signal_emit(profile,
			      kz_profile_signals[KEY_ADDED_SIGNAL],
			      quark, p->section, p->key);
	}

	switch (type)
	{
	case KZ_PROFILE_VALUE_TYPE_BOOL:
		g_free(p->data);
		p->data = g_strdup_printf("%s=%s",
					  p->key, *((gboolean *)value) ? "true" : "false");
		break;
	case KZ_PROFILE_VALUE_TYPE_INT:
		g_free(p->data);
		p->data = g_strdup_printf("%s=%d", p->key, *((gint *)value));
		break;
	case KZ_PROFILE_VALUE_TYPE_STRING:
		g_free(p->data);
		p->data = g_strdup_printf("%s=%s", p->key, (gchar *)value);
		break;
	case KZ_PROFILE_VALUE_TYPE_ARRAY:
		g_free(p->data);
		p->data = g_strdup_printf("%s=%u", p->key, ((guint8 *)value)[0]);
		for (i = 1; i < size; i++)
		{
			gchar *data;
			data = g_strdup_printf("%s %u", p->data, ((guint8 *)value)[i]);
			g_free(p->data);
			p->data = data;
		}
		break;
	default:
		g_free(old_value);
		return FALSE;
	}

	p->value = strchr(p->data,'=') + 1;
	profile->edit = TRUE;

	g_signal_emit(profile, kz_profile_signals[CHANGED_SIGNAL],
		      quark, p->section, p->key, old_value);

	g_free(old_value);

	if (profile->save_eachtime)
		kz_profile_save (profile);

	return TRUE;
}


static void
kz_profile_list_free (KzProfile *profile, KzProfileList *p)
{
	if (!p) return;

	if (p == profile->list)
		profile->list = p->next;
	if (p->prev)
		p->prev->next = p->next;
	if (p->next)
		p->next->prev = p->prev;

	g_free(p->data);
	g_free(p->section);
	g_free(p->key);
	g_free(p);
}


/*
 * Delete a section from the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_delete_section (KzProfile *profile, const gchar *section)
{
	gboolean result = FALSE;
	KzProfileList *p,*q;
	gchar *section_tmp;
	GQuark quark;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	if (!section)
		return FALSE;

	/*
	 * section and p->section may be same memory chunk.
	 */
	section_tmp = g_strdup(section);

	for (p = profile->list; p; p = q)
	{
		q = p->next;
		if (p->section && g_strcmp(p->section, section_tmp) == 0)
		{
			if (p->prev && p->prev->type == KZ_PROFILE_DATA_TYPE_SPACE)
				kz_profile_list_free(profile, p->prev);
			kz_profile_list_free(profile, p);
			profile->edit = TRUE;
			result = TRUE;
		}
	}

	quark = g_quark_from_string(section_tmp);
	g_signal_emit(profile,
		      kz_profile_signals[SECTION_DELETED_SIGNAL],
		      quark, section_tmp);

	g_free(section_tmp);

	if (profile->save_eachtime)
		kz_profile_save (profile);

	return result;
}


/*
 * Delete a key from the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * key,key
 * RET,TRUE:normal exit,FALSE:error
 */
gboolean
kz_profile_delete_key (KzProfile *profile,
		       const gchar *section, const gchar *key)
{
	gboolean result = FALSE;
	KzProfileList *p,*q;
	gchar *section_tmp, *key_tmp;
	GQuark quark;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	if (!section || !key)
		return FALSE;

	/*
	 * section and p->section (also key and p->key) may be same memory
	 * chunk.
	 */
	section_tmp = g_strdup(section);
	key_tmp = g_strdup(key);

	for (p = profile->list; p; p = q)
	{
		q = p->next;
		if (p->section && p->key
		    && g_strcmp(p->section, section_tmp) == 0
		    && g_strcmp(p->key, key_tmp) == 0)
		{
			kz_profile_list_free(profile, p);
			profile->edit = TRUE;
			result = TRUE;
		}
	}

	quark = g_quark_from_string(section_tmp);
	g_signal_emit(profile,
		      kz_profile_signals[KEY_DELETED_SIGNAL],
		      quark, section_tmp, key_tmp);

	g_free(section_tmp);
	g_free(key_tmp);

	if (profile->save_eachtime)
		kz_profile_save (profile);

	return result;
}


/*
 * Enumerate sections in the initialize file.
 * profile,the KzProfile struct
 * RET,list of sections,NULL:error
 */
GList *
kz_profile_enum_section (KzProfile *profile)
{
	GList *glist = NULL;
	KzProfileList *p;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	for (p = profile->list; p; p = p->next)
	{
		if (p->section
		    && (!glist || !g_list_find_custom(glist, p->section,
						      (GCompareFunc)strcmp)))
		{
			glist = g_list_insert_sorted(glist, p->section,
						     (GCompareFunc)strcmp);
		}
	}
	for (p = profile->sublist; p; p = p->next)
	{
		if (p->section
		    && (!glist || !g_list_find_custom(glist, p->section,
						      (GCompareFunc)strcmp)))
		{
			glist = g_list_insert_sorted(glist, p->section,
						     (GCompareFunc)strcmp);
		}
	}
	return glist;
}


static gint
compare_enum_key (gconstpointer a, gconstpointer b)
{
	const gchar *one = (const gchar *)a;
	const gchar *two = (const gchar *)b;
	
	if (strlen(one) != strlen(two))
		return strlen(one) - strlen(two);
	return strcmp(one, two);
}


/*
 * Enumelate keys in the initialize file.
 * profile,the KzProfile struct
 * section,name of the section
 * RET,list of keys,NULL:error
*/
GList *
kz_profile_enum_key (KzProfile *profile, 
		     const gchar *section,
		     gboolean users_only)
{
	GList *glist = NULL;
	KzProfileList *p;

	g_return_val_if_fail(KZ_IS_PROFILE(profile), FALSE);

	for (p = profile->list; p; p = p->next)
	{
		if (p->section && p->key
		    && g_strcmp(p->section, section) == 0
		    && (!glist || !g_list_find_custom(glist, p->key,
						     (GCompareFunc)compare_enum_key)))
		{
			glist = g_list_insert_sorted(glist, p->key,
						     (GCompareFunc)compare_enum_key);
		}
	}

	/* We need only user's setting if it exists */
	
	if (glist && users_only)
		return glist;
	
	for (p = profile->sublist; p; p = p->next)
	{
		if (p->section && p->key
		    && g_strcmp (p->section, section) == 0
		    && (!glist || !g_list_find_custom(glist, p->key,
						      (GCompareFunc)compare_enum_key)))
		{
			glist = g_list_insert_sorted(glist, p->key,
						     (GCompareFunc)compare_enum_key);
		}
	}
	return glist;
}

void
kz_profile_set_save_each_time (KzProfile *profile, gboolean save)
{
	profile->save_eachtime = save;
}

