Add a note about casting the results of g_new() and g_new0().
[platform/upstream/glib.git] / glib / gkeyfile.c
index 49462c9..7a8a783 100644 (file)
@@ -3,6 +3,7 @@
  *  Copyright 2004  Red Hat, Inc.  
  *
  * Written by Ray Strode <rstrode@redhat.com>
+ *            Matthias Clasen <mclasen@redhat.com>
  *
  * GLib is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
@@ -21,6 +22,7 @@
  */
 
 #include "config.h"
+
 #include "gkeyfile.h"
 
 #include <errno.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
+#ifdef G_OS_WIN32
+#include <io.h>
+
+#ifndef S_ISREG
+#define S_ISREG(mode) ((mode)&_S_IFREG)
+#endif
+
+#endif  /* G_OS_WIN23 */
 
 #include "gconvert.h"
 #include "gdataset.h"
 #include "gslist.h"
 #include "gmem.h"
 #include "gmessages.h"
+#include "gstdio.h"
 #include "gstring.h"
 #include "gstrfuncs.h"
 #include "gutils.h"
 
+#include "galias.h"
+
 typedef struct _GKeyFileGroup GKeyFileGroup;
 
 struct _GKeyFile
 {
   GList *groups;
 
-  gchar  *start_group_name;
-
+  GKeyFileGroup *start_group;
   GKeyFileGroup *current_group;
 
   GString *parse_buffer; /* Holds up to one line of not-yet-parsed data */
@@ -68,10 +82,14 @@ struct _GKeyFile
   GKeyFileFlags flags;
 };
 
+typedef struct _GKeyFileKeyValuePair GKeyFileKeyValuePair;
+
 struct _GKeyFileGroup
 {
   const gchar *name;  /* NULL for above first group (which will be comments) */
 
+  GKeyFileKeyValuePair *comment; /* Special comment that is stuck to the top of a group */
+
   GList *key_value_pairs; 
 
   /* Used in parallel with key_value_pairs for
@@ -80,72 +98,87 @@ struct _GKeyFileGroup
   GHashTable *lookup_map;
 };
 
-typedef struct
+struct _GKeyFileKeyValuePair
 {
   gchar *key;  /* NULL for comments */
   gchar *value;
-} GKeyFileKeyValuePair;
+};
 
 static gint                  find_file_in_data_dirs            (const gchar            *file,
                                                                gchar                 **output_file,
                                                                gchar                ***data_dirs,
                                                                GError                **error);
-static void                  g_key_file_load_from_fd           (GKeyFile               *key_file,
+static gboolean              g_key_file_load_from_fd           (GKeyFile               *key_file,
                                                                gint                    fd,
                                                                GKeyFileFlags           flags,
                                                                GError                **error);
+static GList                *g_key_file_lookup_group_node      (GKeyFile               *key_file,
+                                                               const gchar            *group_name);
 static GKeyFileGroup        *g_key_file_lookup_group           (GKeyFile               *key_file,
                                                                const gchar            *group_name);
-static GKeyFileKeyValuePair *g_key_file_lookup_key_value_pair  (GKeyFile               *key_file,
+
+static GList                *g_key_file_lookup_key_value_pair_node  (GKeyFile       *key_file,
+                                                                    GKeyFileGroup  *group,
+                                                                     const gchar    *key);
+static GKeyFileKeyValuePair *g_key_file_lookup_key_value_pair       (GKeyFile       *key_file,
+                                                                     GKeyFileGroup  *group,
+                                                                     const gchar    *key);
+
+static void                  g_key_file_remove_group_node          (GKeyFile      *key_file,
+                                                                   GList         *group_node);
+static void                  g_key_file_remove_key_value_pair_node (GKeyFile      *key_file,
+                                                                    GKeyFileGroup *group,
+                                                                    GList         *pair_node);
+
+static void                  g_key_file_add_key                (GKeyFile               *key_file,
                                                                GKeyFileGroup          *group,
-                                                               const gchar            *key);
-static void                  g_key_file_remove_group_node      (GKeyFile               *entry,
-                                                               GList                  *group_node);
-static void                  g_key_file_add_key                (GKeyFile               *entry,
-                                                               const gchar            *group_name,
                                                                const gchar            *key,
                                                                const gchar            *value);
-static void                  g_key_file_add_group              (GKeyFile               *entry,
+static void                  g_key_file_add_group              (GKeyFile               *key_file,
                                                                const gchar            *group_name);
 static void                  g_key_file_key_value_pair_free    (GKeyFileKeyValuePair   *pair);
 static gboolean              g_key_file_line_is_comment        (const gchar            *line);
 static gboolean              g_key_file_line_is_group          (const gchar            *line);
 static gboolean              g_key_file_line_is_key_value_pair (const gchar            *line);
-static gchar                *g_key_file_parse_value_as_string  (GKeyFile               *entry,
+static gchar                *g_key_file_parse_value_as_string  (GKeyFile               *key_file,
                                                                const gchar            *value,
                                                                GSList                **separators,
                                                                GError                **error);
-static gchar                *g_key_file_parse_string_as_value  (GKeyFile               *entry,
+static gchar                *g_key_file_parse_string_as_value  (GKeyFile               *key_file,
                                                                const gchar            *string,
                                                                gboolean                escape_separator);
-static gint                  g_key_file_parse_value_as_integer (GKeyFile               *entry,
+static gint                  g_key_file_parse_value_as_integer (GKeyFile               *key_file,
                                                                const gchar            *value,
                                                                GError                **error);
-static gchar                *g_key_file_parse_integer_as_value (GKeyFile               *entry,
+static gchar                *g_key_file_parse_integer_as_value (GKeyFile               *key_file,
                                                                gint                    value);
-static gboolean              g_key_file_parse_value_as_boolean (GKeyFile               *entry,
+static gboolean              g_key_file_parse_value_as_boolean (GKeyFile               *key_file,
                                                                const gchar            *value,
                                                                GError                **error);
-static gchar                *g_key_file_parse_boolean_as_value (GKeyFile               *entry,
+static gchar                *g_key_file_parse_boolean_as_value (GKeyFile               *key_file,
                                                                gboolean                value);
-static void                  g_key_file_parse_key_value_pair   (GKeyFile               *entry,
+static gchar                *g_key_file_parse_value_as_comment (GKeyFile               *key_file,
+                                                                const gchar            *value);
+static gchar                *g_key_file_parse_comment_as_value (GKeyFile               *key_file,
+                                                                const gchar            *comment);
+static void                  g_key_file_parse_key_value_pair   (GKeyFile               *key_file,
                                                                const gchar            *line,
                                                                gsize                   length,
                                                                GError                **error);
-static void                  g_key_file_parse_comment          (GKeyFile               *entry,
+static void                  g_key_file_parse_comment          (GKeyFile               *key_file,
                                                                const gchar            *line,
                                                                gsize                   length,
                                                                GError                **error);
-static void                  g_key_file_parse_group            (GKeyFile               *entry,
+static void                  g_key_file_parse_group            (GKeyFile               *key_file,
                                                                const gchar            *line,
                                                                gsize                   length,
                                                                GError                **error);
 static gchar                *key_get_locale                    (const gchar            *key);
-static void                  g_key_file_parse_data             (GKeyFile               *entry,
+static void                  g_key_file_parse_data             (GKeyFile               *key_file,
                                                                const gchar            *data,
                                                                gsize                   length,
                                                                GError                **error);
-static void                  g_key_file_flush_parse_buffer     (GKeyFile               *entry,
+static void                  g_key_file_flush_parse_buffer     (GKeyFile               *key_file,
                                                                GError                **error);
 
 
@@ -165,7 +198,7 @@ g_key_file_init (GKeyFile *key_file)
 {  
   key_file->current_group = g_new0 (GKeyFileGroup, 1);
   key_file->groups = g_list_prepend (NULL, key_file->current_group);
-  key_file->start_group_name = NULL;
+  key_file->start_group = NULL;
   key_file->parse_buffer = g_string_sized_new (128);
   key_file->approximate_size = 0;
   key_file->list_separator = ';';
@@ -180,8 +213,6 @@ g_key_file_clear (GKeyFile *key_file)
   if (key_file->parse_buffer)
     g_string_free (key_file->parse_buffer, TRUE);
 
-  g_free (key_file->start_group_name);
-
   tmp = key_file->groups;
   while (tmp != NULL)
     {
@@ -196,7 +227,6 @@ g_key_file_clear (GKeyFile *key_file)
 
 /**
  * g_key_file_new:
- * @flags: flags from #GKeyFileFlags
  *
  * Creates a new empty #GKeyFile object. Use g_key_file_load_from_file(),
  * g_key_file_load_from_data() or g_key_file_load_from_data_dirs() to
@@ -250,9 +280,7 @@ find_file_in_data_dirs (const gchar   *file,
 {
   gchar **data_dirs, *data_dir, *path;
   gint fd;
-  GError *file_error;
 
-  file_error = NULL;
   path = NULL;
   fd = -1;
 
@@ -274,18 +302,13 @@ find_file_in_data_dirs (const gchar   *file,
           path = g_build_filename (data_dir, sub_dir,
                                    candidate_file, NULL);
 
-          fd = open (path, O_RDONLY);
-
-          if (output_file != NULL)
-            *output_file = g_strdup (path);
-
-          g_free (path);
+          fd = g_open (path, O_RDONLY, 0);
 
-          if (fd < 0 && file_error == NULL)
-            file_error = g_error_new (G_KEY_FILE_ERROR,
-                                      G_KEY_FILE_ERROR_NOT_FOUND,
-                                      _("Valid key file could not be "
-                                        "found in data dirs")); 
+          if (fd < 0)
+            {
+              g_free (path);
+              path = NULL;
+            }
 
           candidate_file = strchr (candidate_file, '-');
 
@@ -309,51 +332,63 @@ find_file_in_data_dirs (const gchar   *file,
 
   *dirs = data_dirs;
 
-  if (file_error)
+  if (fd < 0)
     {
-      if (fd >= 0)
-        g_error_free (file_error);
-      else
-        g_propagate_error (error, file_error);
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_NOT_FOUND,
+                   _("Valid key file could not be "
+                     "found in data dirs")); 
     }
 
-  if (output_file && fd < 0)
-    {
-      g_free (*output_file);
-      *output_file = NULL;
-    }
+  if (output_file != NULL && fd > 0)
+    *output_file = g_strdup (path);
+
+  g_free (path);
 
   return fd;
 }
 
-static void
+static gboolean
 g_key_file_load_from_fd (GKeyFile       *key_file,
                         gint            fd,
                         GKeyFileFlags   flags,
                         GError        **error)
 {
   GError *key_file_error = NULL;
-  gsize bytes_read;
+  gssize bytes_read;
   struct stat stat_buf;
   gchar read_buf[4096];
+  
+  if (fstat (fd, &stat_buf) < 0)
+    {
+      g_set_error (error, G_FILE_ERROR,
+                   g_file_error_from_errno (errno),
+                   "%s", g_strerror (errno));
+      return FALSE;
+    }
 
-  if (key_file->approximate_size > 0)
+  if (!S_ISREG (stat_buf.st_mode))
     {
-      g_key_file_clear (key_file);
-      g_key_file_init (key_file);
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_PARSE,
+                   _("Not a regular file"));
+      return FALSE;
     }
-  key_file->flags = flags;
 
-  fstat (fd, &stat_buf);
   if (stat_buf.st_size == 0)
     {
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_PARSE,
                    _("File is empty"));
-      return;
+      return FALSE;
     }
 
-  key_file->start_group_name = NULL;
+  if (key_file->approximate_size > 0)
+    {
+      g_key_file_clear (key_file);
+      g_key_file_init (key_file);
+    }
+  key_file->flags = flags;
 
   bytes_read = 0;
   do
@@ -365,15 +400,13 @@ g_key_file_load_from_fd (GKeyFile       *key_file,
 
       if (bytes_read < 0)
         {
-          if (errno == EINTR)
+          if (errno == EINTR || errno == EAGAIN)
             continue;
 
           g_set_error (error, G_FILE_ERROR,
                        g_file_error_from_errno (errno),
-                       _("Failed to read from file: %s"),
-                       g_strerror (errno));
-          close (fd);
-          return;
+                       "%s", g_strerror (errno));
+          return FALSE;
         }
 
       g_key_file_parse_data (key_file, 
@@ -382,12 +415,10 @@ g_key_file_load_from_fd (GKeyFile       *key_file,
     }
   while (!key_file_error);
 
-  close (fd);
-
   if (key_file_error)
     {
       g_propagate_error (error, key_file_error);
-      return;
+      return FALSE;
     }
 
   g_key_file_flush_parse_buffer (key_file, &key_file_error);
@@ -395,14 +426,16 @@ g_key_file_load_from_fd (GKeyFile       *key_file,
   if (key_file_error)
     {
       g_propagate_error (error, key_file_error);
-      return;
+      return FALSE;
     }
+
+  return TRUE;
 }
 
 /**
  * g_key_file_load_from_file:
  * @key_file: an empty #GKeyFile struct
- * @file: the path of a filename to load
+ * @file: the path of a filename to load, in the GLib file name encoding
  * @flags: flags from #GKeyFileFlags
  * @error: return location for a #GError, or %NULL
  *
@@ -410,9 +443,10 @@ g_key_file_load_from_fd (GKeyFile       *key_file,
  * If the file could not be loaded then %error is set to 
  * either a #GFileError or #GKeyFileError.
  *
+ * Return value: %TRUE if a key file could be loaded, %FALSE othewise
  * Since: 2.6
  **/
-void
+gboolean
 g_key_file_load_from_file (GKeyFile       *key_file,
                           const gchar    *file,
                           GKeyFileFlags   flags,
@@ -421,30 +455,29 @@ g_key_file_load_from_file (GKeyFile       *key_file,
   GError *key_file_error = NULL;
   gint fd;
 
-  fd = open (file, O_RDONLY);
+  g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (file != NULL, FALSE);
+
+  fd = g_open (file, O_RDONLY, 0);
 
   if (fd < 0)
     {
       g_set_error (error, G_FILE_ERROR,
                    g_file_error_from_errno (errno),
-                   _("Failed to open file '%s': %s"),
-                   file, g_strerror (errno));
-      return;
-    }
-
-  if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
-    {
-      g_set_error (error, G_FILE_ERROR,
-                   G_FILE_ERROR_ISDIR,
-                   _("Not a regular file: '%s'"),
-                   file);
-      return;
+                   "%s", g_strerror (errno));
+      return FALSE;
     }
 
   g_key_file_load_from_fd (key_file, fd, flags, &key_file_error);
+  close (fd);
 
   if (key_file_error)
-    g_propagate_error (error, key_file_error);
+    {
+      g_propagate_error (error, key_file_error);
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
 /**
@@ -455,12 +488,14 @@ g_key_file_load_from_file (GKeyFile       *key_file,
  * @flags: flags from #GKeyFileFlags
  * @error: return location for a #GError, or %NULL
  *
- * Loads a key file from memory into an empty #GKeyFile structure.  
- * If the object cannot be created then %error is set to a #GKeyFileError.
+ * Loads a key file from memory into an empty #GKeyFile structure.  If
+ * the object cannot be created then %error is set to a
+ * #GKeyFileError. 
  *
+ * Return value: %TRUE if a key file could be loaded, %FALSE othewise
  * Since: 2.6
  **/
-void
+gboolean
 g_key_file_load_from_data (GKeyFile       *key_file,
                           const gchar    *data,
                           gsize           length,
@@ -469,8 +504,12 @@ g_key_file_load_from_data (GKeyFile       *key_file,
 {
   GError *key_file_error = NULL;
 
-  g_return_if_fail (data != NULL);
-  g_return_if_fail (length != 0);
+  g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (data != NULL, FALSE);
+  g_return_val_if_fail (length != 0, FALSE);
+
+  if (length == (gsize)-1)
+    length = strlen (data);
 
   if (key_file->approximate_size > 0)
     {
@@ -484,13 +523,18 @@ g_key_file_load_from_data (GKeyFile       *key_file,
   if (key_file_error)
     {
       g_propagate_error (error, key_file_error);
-      return;
+      return FALSE;
     }
 
   g_key_file_flush_parse_buffer (key_file, &key_file_error);
   
   if (key_file_error)
-    g_propagate_error (error, key_file_error);
+    {
+      g_propagate_error (error, key_file_error);
+      return FALSE;
+    }
+
+  return TRUE;
 }
 
 /**
@@ -508,9 +552,10 @@ g_key_file_load_from_data (GKeyFile       *key_file,
  * @full_path.  If the file could not be loaded then an %error is
  * set to either a #GFileError or #GKeyFileError.
  *
+ * Return value: %TRUE if a key file could be loaded, %FALSE othewise
  * Since: 2.6
  **/
-void
+gboolean
 g_key_file_load_from_data_dirs (GKeyFile       *key_file,
                                const gchar    *file,
                                gchar         **full_path,
@@ -520,16 +565,18 @@ g_key_file_load_from_data_dirs (GKeyFile       *key_file,
   GError *key_file_error = NULL;
   gchar **all_data_dirs, **data_dirs;
   const gchar * user_data_dir;
-  const const gchar * const * system_data_dirs;
+  const gchar * const * system_data_dirs;
   gsize i, j;
   gchar *output_path;
   gint fd;
+  gboolean found_file;
   
-  g_return_if_fail (!g_path_is_absolute (file));
+  g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (!g_path_is_absolute (file), FALSE);
 
   user_data_dir = g_get_user_data_dir ();
   system_data_dirs = g_get_system_data_dirs ();
-  all_data_dirs = g_new (gchar *, g_strv_length ((gchar **)system_data_dirs) + 1);
+  all_data_dirs = g_new0 (gchar *, g_strv_length ((gchar **)system_data_dirs) + 2);
 
   i = 0;
   all_data_dirs[i++] = g_strdup (user_data_dir);
@@ -538,9 +585,13 @@ g_key_file_load_from_data_dirs (GKeyFile       *key_file,
   while (system_data_dirs[j] != NULL)
     all_data_dirs[i++] = g_strdup (system_data_dirs[j++]);
 
+  found_file = FALSE;
   data_dirs = all_data_dirs;
-  while (*data_dirs != NULL)
+  output_path = NULL;
+  while (*data_dirs != NULL && !found_file)
     {
+      g_free (output_path);
+
       fd = find_file_in_data_dirs (file, &output_path, &data_dirs, 
                                    &key_file_error);
       
@@ -548,26 +599,28 @@ g_key_file_load_from_data_dirs (GKeyFile       *key_file,
         {
           if (key_file_error)
             g_propagate_error (error, key_file_error);
-
-         break;
+         break;
         }
 
-      g_key_file_load_from_fd (key_file, fd, flags,
-                              &key_file_error);
+      found_file = g_key_file_load_from_fd (key_file, fd, flags,
+                                           &key_file_error);
+      close (fd);
       
       if (key_file_error)
         {
          g_propagate_error (error, key_file_error);
-          g_free (output_path);
-
          break;
         }
-      
-      if (full_path)
-       *full_path = output_path;
     }
 
+  if (found_file && full_path)
+    *full_path = output_path;
+  else 
+    g_free (output_path);
+
   g_strfreev (all_data_dirs);
+
+  return found_file;
 }
 
 /**
@@ -594,7 +647,7 @@ static gboolean
 g_key_file_locale_is_interesting (GKeyFile    *key_file,
                                  const gchar *locale)
 {
-  const const gchar * const * current_locales;
+  const gchar * const * current_locales;
   gsize i;
 
   if (key_file->flags & G_KEY_FILE_KEEP_TRANSLATIONS)
@@ -658,11 +711,11 @@ g_key_file_parse_comment (GKeyFile     *key_file,
 {
   GKeyFileKeyValuePair *pair;
   
-  if (!key_file->flags & G_KEY_FILE_KEEP_COMMENTS)
+  if (!(key_file->flags & G_KEY_FILE_KEEP_COMMENTS))
     return;
   
   g_assert (key_file->current_group != NULL);
-  
+
   pair = g_new0 (GKeyFileKeyValuePair, 1);
   
   pair->key = NULL;
@@ -692,9 +745,6 @@ g_key_file_parse_group (GKeyFile     *key_file,
   group_name = g_strndup (group_name_start, 
                           group_name_end - group_name_start);
   
-  if (key_file->start_group_name == NULL)
-    key_file->start_group_name = g_strdup (group_name);
-  
   g_key_file_add_group (key_file, group_name);
   g_free (group_name);
 }
@@ -743,21 +793,11 @@ g_key_file_parse_key_value_pair (GKeyFile     *key_file,
 
   value = g_strndup (value_start, value_len);
 
-  if (!g_utf8_validate (value, -1, NULL))
-    {
-      g_set_error (error, G_KEY_FILE_ERROR,
-                   G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
-                   _("Key file contains line '%s' "
-                     "which is not UTF-8"), line);
-
-      g_free (key);
-      g_free (value);
-      return;
-    }
+  g_assert (key_file->start_group != NULL);
 
   if (key_file->current_group
       && key_file->current_group->name
-      && strcmp (g_key_file_get_start_group (key_file),
+      && strcmp (key_file->start_group->name,
                  key_file->current_group->name) == 0
       && strcmp (key, "Encoding") == 0)
     {
@@ -778,7 +818,7 @@ g_key_file_parse_key_value_pair (GKeyFile     *key_file,
   locale = key_get_locale (key);
 
   if (locale == NULL || g_key_file_locale_is_interesting (key_file, locale))
-    g_key_file_add_key (key_file, key_file->current_group->name, key, value);
+    g_key_file_add_key (key_file, key_file->current_group, key, value);
 
   g_free (locale);
   g_free (key);
@@ -811,6 +851,7 @@ g_key_file_parse_data (GKeyFile     *key_file,
   gsize i;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (data != NULL);
 
   parse_error = NULL;
 
@@ -818,6 +859,11 @@ g_key_file_parse_data (GKeyFile     *key_file,
     {
       if (data[i] == '\n')
         {
+         if (i > 0 && data[i - 1] == '\r')
+           g_string_erase (key_file->parse_buffer,
+                           key_file->parse_buffer->len - 1,
+                           1);
+           
           /* When a newline is encountered flush the parse buffer so that the
            * line can be parsed.  Note that completely blank lines won't show
            * up in the parse buffer, so they get parsed directly.
@@ -900,6 +946,8 @@ g_key_file_to_data (GKeyFile  *key_file,
 
       group = (GKeyFileGroup *) group_node->data;
 
+      if (group->comment != NULL)
+        g_string_append_printf (data_string, "%s\n", group->comment->value);
       if (group->name != NULL)
         g_string_append_printf (data_string, "[%s]\n", group->name);
 
@@ -931,13 +979,15 @@ g_key_file_to_data (GKeyFile  *key_file,
 /**
  * g_key_file_get_keys:
  * @key_file: a #GKeyFile
- * @group_name: a group name, or %NULL
+ * @group_name: a group name
  * @length: return location for the number of keys returned, or %NULL
  * @error: return location for a #GError, or %NULL
  *
- * Returns all keys for the group name @group_name. If @group_name is
- * %NULL, the start group is used. The array of returned keys will be 
- * %NULL-terminated, so @length may optionally be %NULL.
+ * Returns all keys for the group name @group_name.  The array of
+ * returned keys will be %NULL-terminated, so @length may
+ * optionally be %NULL. In the event that the @group_name cannot
+ * be found, %NULL is returned and @error is set to
+ * #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
  *
  * Return value: a newly-allocated %NULL-terminated array of
  * strings. Use g_strfreev() to free it.
@@ -956,9 +1006,7 @@ g_key_file_get_keys (GKeyFile     *key_file,
   gsize i, num_keys;
   
   g_return_val_if_fail (key_file != NULL, NULL);
-  
-  if (group_name == NULL)
-    group_name = (const gchar *) key_file->start_group_name;
+  g_return_val_if_fail (group_name != NULL, NULL);
   
   group = g_key_file_lookup_group (key_file, group_name);
   
@@ -967,25 +1015,38 @@ g_key_file_get_keys (GKeyFile     *key_file,
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
                    _("Key file does not have group '%s'"),
-                   group_name);
+                   group_name ? group_name : "(null)");
       return NULL;
     }
 
-  num_keys = g_list_length (group->key_value_pairs);
+  num_keys = 0;
+  for (tmp = group->key_value_pairs; tmp; tmp = tmp->next)
+    {
+      GKeyFileKeyValuePair *pair;
+
+      pair = (GKeyFileKeyValuePair *) tmp->data;
+
+      if (pair->key)
+       num_keys++;
+    }
   
-  keys = (gchar **) g_new (gchar **, num_keys + 1);
+  keys = g_new0 (gchar *, num_keys + 1);
 
-  tmp = group->key_value_pairs;
-  for (i = 0; i < num_keys; i++)
+  i = num_keys - 1;
+  for (tmp = group->key_value_pairs; tmp; tmp = tmp->next)
     {
       GKeyFileKeyValuePair *pair;
 
       pair = (GKeyFileKeyValuePair *) tmp->data;
-      keys[i] = g_strdup (pair->key);
 
-      tmp = tmp->next;
+      if (pair->key)
+       {
+         keys[i] = g_strdup (pair->key);
+         i--;
+       }
     }
-  keys[i] = NULL;
+
+  keys[num_keys] = NULL;
 
   if (length)
     *length = num_keys;
@@ -1008,10 +1069,10 @@ g_key_file_get_start_group (GKeyFile *key_file)
 {
   g_return_val_if_fail (key_file != NULL, NULL);
 
-  if (key_file->start_group_name)
-    return g_strdup (key_file->start_group_name);
-  else
-    return NULL;
+  if (key_file->start_group)
+    return g_strdup (key_file->start_group->name);
+
+  return NULL;
 }
 
 /**
@@ -1031,29 +1092,44 @@ gchar **
 g_key_file_get_groups (GKeyFile *key_file,
                       gsize    *length)
 {
-  GList *tmp;
+  GList *group_node;
   gchar **groups;
   gsize i, num_groups;
 
   g_return_val_if_fail (key_file != NULL, NULL);
 
   num_groups = g_list_length (key_file->groups);
-  groups = (gchar **) g_new (gchar **, num_groups + 1);
 
-  tmp = key_file->groups;
-  for (i = 0; i < num_groups; i++)
+  g_assert (num_groups > 0);
+
+  /* Only need num_groups instead of num_groups + 1
+   * because the first group of the file (last in the
+   * list) is always the comment group at the top,
+   * which we skip
+   */
+  groups = g_new0 (gchar *, num_groups);
+
+  group_node = g_list_last (key_file->groups);
+  
+  g_assert (((GKeyFileGroup *) group_node->data)->name == NULL);
+
+  i = 0;
+  for (group_node = group_node->prev;
+       group_node != NULL;
+       group_node = group_node->prev)
     {
       GKeyFileGroup *group;
 
-      group = (GKeyFileGroup *) tmp->data;
-      groups[i] = g_strdup (group->name);
+      group = (GKeyFileGroup *) group_node->data;
 
-      tmp = tmp->next;
+      g_assert (group->name != NULL);
+
+      groups[i++] = g_strdup (group->name);
     }
   groups[i] = NULL;
 
   if (length)
-    *length = num_groups;
+    *length = i;
 
   return groups;
 }
@@ -1063,16 +1139,17 @@ g_key_file_get_groups (GKeyFile *key_file,
  * @key_file: a #GKeyFile
  * @group_name: a group name
  * @key: a key
- * @error: return location for a #GError, or #NULL
+ * @error: return location for a #GError, or %NULL
  *
  * Returns the value associated with @key under @group_name.  
+ *
  * In the event the key cannot be found, %NULL is returned and 
  * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND.  In the 
  * event that the @group_name cannot be found, %NULL is returned 
  * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
  *
- * Return value: a string or %NULL if the specified key cannot be
- * found.
+ * Return value: a newly allocated string or %NULL if the specified 
+ *  key cannot be found.
  *
  * Since: 2.6
  **/
@@ -1086,8 +1163,9 @@ g_key_file_get_value (GKeyFile     *key_file,
   GKeyFileKeyValuePair *pair;
   gchar *value = NULL;
 
-  if (group_name == NULL)
-    group_name = (const gchar *) key_file->start_group_name;
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
   
   group = g_key_file_lookup_group (key_file, group_name);
 
@@ -1096,7 +1174,7 @@ g_key_file_get_value (GKeyFile     *key_file,
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
                    _("Key file does not have group '%s'"),
-                   group_name);
+                   group_name ? group_name : "(null)");
       return NULL;
     }
 
@@ -1119,9 +1197,9 @@ g_key_file_get_value (GKeyFile     *key_file,
  * @key: a key
  * @value: a string
  *
- * Associates a new value with @key under @group_name.
- * If @key cannot be found then it is created. If @group_name
- * cannot be found then it is created as well.
+ * Associates a new value with @key under @group_name.  If @key
+ * cannot be found then it is created. If @group_name cannot be
+ * found then it is created.
  *
  * Since: 2.6
  **/
@@ -1134,19 +1212,31 @@ g_key_file_set_value (GKeyFile    *key_file,
   GKeyFileGroup *group;
   GKeyFileKeyValuePair *pair;
 
-  pair = NULL;
+  g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
+  g_return_if_fail (key != NULL);
+  g_return_if_fail (value != NULL);
 
-  if (group_name == NULL)
-    group_name = (const gchar *) key_file->start_group_name;
+  group = g_key_file_lookup_group (key_file, group_name);
+
+  if (!group)
+    {
+      g_key_file_add_group (key_file, group_name);
+      group = (GKeyFileGroup *) key_file->groups->data;
 
-  if (!g_key_file_has_key (key_file, group_name, key, NULL))
-    g_key_file_add_key (key_file, group_name, key, value);
+      g_key_file_add_key (key_file, group, key, value);
+    }
   else
     {
-      group = g_key_file_lookup_group (key_file, group_name);
       pair = g_key_file_lookup_key_value_pair (key_file, group, key);
-      g_free (pair->value);
-      pair->value = g_strdup (value);
+
+      if (!pair)
+        g_key_file_add_key (key_file, group, key, value);
+      else
+        {
+          g_free (pair->value);
+          pair->value = g_strdup (value);
+        }
     }
 }
 
@@ -1155,16 +1245,17 @@ g_key_file_set_value (GKeyFile    *key_file,
  * @key_file: a #GKeyFile
  * @group_name: a group name
  * @key: a key
- * @error: return location for a #GError, or #NULL
+ * @error: return location for a #GError, or %NULL
  *
  * Returns the value associated with @key under @group_name.  
+ *
  * In the event the key cannot be found, %NULL is returned and 
  * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND.  In the 
  * event that the @group_name cannot be found, %NULL is returned 
  * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
  *
- * Return value: a string or %NULL if the specified key cannot be
- * found.
+ * Return value: a newly allocated string or %NULL if the specified 
+ *   key cannot be found.
  *
  * Since: 2.6
  **/
@@ -1178,6 +1269,7 @@ g_key_file_get_string (GKeyFile     *key_file,
   GError *key_file_error;
 
   g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
   g_return_val_if_fail (key != NULL, NULL);
 
   key_file_error = NULL;
@@ -1190,6 +1282,16 @@ g_key_file_get_string (GKeyFile     *key_file,
       return NULL;
     }
 
+  if (!g_utf8_validate (value, -1, NULL))
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
+                   _("Key file contains key '%s' with value '%s' "
+                     "which is not UTF-8"), key, value);
+      g_free (value);
+      return NULL;
+    }
+
   string_value = g_key_file_parse_value_as_string (key_file, value, NULL,
                                                   &key_file_error);
   g_free (value);
@@ -1221,9 +1323,9 @@ g_key_file_get_string (GKeyFile     *key_file,
  * @key: a key
  * @string: a string
  *
- * Associates a new string value with @key under @group_name.
- * If @key cannot be found then it is created. If @group_name
- * cannot be found then it is created as well.
+ * Associates a new string value with @key under @group_name.  If
+ * @key cannot be found then it is created.  If @group_name
+ * cannot be found then it is created.
  *
  * Since: 2.6
  **/
@@ -1236,8 +1338,10 @@ g_key_file_set_string (GKeyFile    *key_file,
   gchar *value;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
-  
+  g_return_if_fail (string != NULL);
+
   value = g_key_file_parse_string_as_value (key_file, string, FALSE);
   g_key_file_set_value (key_file, group_name, key, value);
   g_free (value);
@@ -1252,6 +1356,7 @@ g_key_file_set_string (GKeyFile    *key_file,
  * @error: return location for a #GError, or %NULL
  *
  * Returns the values associated with @key under @group_name.
+ *
  * In the event the key cannot be found, %NULL is returned and
  * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND.  In the
  * event that the @group_name cannot be found, %NULL is returned
@@ -1274,6 +1379,10 @@ g_key_file_get_string_list (GKeyFile     *key_file,
   gint i, len;
   GSList *p, *pieces = NULL;
 
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
   value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
 
   if (key_file_error)
@@ -1282,6 +1391,16 @@ g_key_file_get_string_list (GKeyFile     *key_file,
       return NULL;
     }
 
+  if (!g_utf8_validate (value, -1, NULL))
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_UNKNOWN_ENCODING,
+                   _("Key file contains key '%s' with value '%s' "
+                     "which is not UTF-8"), key, value);
+      g_free (value);
+      return NULL;
+    }
+
   string_value = g_key_file_parse_value_as_string (key_file, value, &pieces, &key_file_error);
   g_free (value);
   g_free (string_value);
@@ -1304,7 +1423,7 @@ g_key_file_get_string_list (GKeyFile     *key_file,
     }
 
   len = g_slist_length (pieces);
-  values = g_new (gchar *, len + 1); 
+  values = g_new0 (gchar *, len + 1); 
   for (p = pieces, i = 0; p; p = p->next)
     values[i++] = p->data;
   values[len] = NULL;
@@ -1326,7 +1445,8 @@ g_key_file_get_string_list (GKeyFile     *key_file,
  * @length: number of locale string values in @list
  *
  * Associates a list of string values for @key under @group_name.
- * If the @key cannot be found then it is created.
+ * If @key cannot be found then it is created.  If @group_name 
+ * cannot be found then it is created.
  *
  * Since: 2.6
  **/
@@ -1341,7 +1461,9 @@ g_key_file_set_string_list (GKeyFile            *key_file,
   gsize i;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
+  g_return_if_fail (list != NULL);
 
   value_list = g_string_sized_new (length * 128);
   for (i = 0; list[i] != NULL && i < length; i++)
@@ -1383,7 +1505,10 @@ g_key_file_set_locale_string (GKeyFile     *key_file,
   gchar *full_key, *value;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
+  g_return_if_fail (locale != NULL);
+  g_return_if_fail (string != NULL);
 
   value = g_key_file_parse_string_as_value (key_file, string, FALSE);
   full_key = g_strdup_printf ("%s[%s]", key, locale);
@@ -1404,18 +1529,16 @@ extern GSList *_g_compute_locale_variants (const gchar *locale);
  *
  * Returns the value associated with @key under @group_name
  * translated in the given @locale if available.  If @locale is
- * %NULL then the current locale is assumed. If @key cannot be
- * found then %NULL is returned and @error is set to
+ * %NULL then the current locale is assumed. 
+ *
+ * If @key cannot be found then %NULL is returned and @error is set to
  * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. If the value associated
  * with @key cannot be interpreted or no suitable translation can
- * be found then the untranslated value is returned and @error is
- * set to #G_KEY_FILE_ERROR_INVALID_VALUE and
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND, respectively. In the
- * event that the @group_name cannot be found, %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
+ * be found then the untranslated value is returned.
+ *
+ * Return value: a newly allocated string or %NULL if the specified 
+ *   key cannot be found.
  *
- * Return value: a string or %NULL if the specified key cannot be
- *               found.
  * Since: 2.6
  **/
 gchar *
@@ -1431,19 +1554,14 @@ g_key_file_get_locale_string (GKeyFile     *key_file,
   gboolean free_languages = FALSE;
   gint i;
 
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
   candidate_key = NULL;
   translated_value = NULL;
   key_file_error = NULL;
 
-  if (!g_key_file_has_group (key_file, group_name))
-    {
-      g_set_error (error, G_KEY_FILE_ERROR,
-                   G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
-                   _("Key file does not have group '%s'"),
-                   group_name);
-      return NULL;
-    }
-
   if (locale)
     {
       GSList *l, *list;
@@ -1473,20 +1591,12 @@ g_key_file_get_locale_string (GKeyFile     *key_file,
                                                candidate_key, NULL);
       g_free (candidate_key);
 
-      if (translated_value)
+      if (translated_value && g_utf8_validate (translated_value, -1, NULL))
        break;
-   }
 
-  if (translated_value && !g_utf8_validate (translated_value, -1, NULL))
-    {
-      g_set_error (error, G_KEY_FILE_ERROR,
-                  G_KEY_FILE_ERROR_INVALID_VALUE,
-                  _("Key file contains key '%s' "
-                    "which has value that cannot be interpreted."),
-                  candidate_key);
       g_free (translated_value);
       translated_value = NULL;
-  }
+   }
 
   /* Fallback to untranslated key
    */
@@ -1497,12 +1607,6 @@ g_key_file_get_locale_string (GKeyFile     *key_file,
       
       if (!translated_value)
         g_propagate_error (error, key_file_error);
-      else
-        g_set_error (error, G_KEY_FILE_ERROR,
-                     G_KEY_FILE_ERROR_KEY_NOT_FOUND,
-                     _("Key file contains no translated value "
-                       "for key '%s' with locale '%s'."),
-                     key, locale);
     }
 
   if (free_languages)
@@ -1522,15 +1626,12 @@ g_key_file_get_locale_string (GKeyFile     *key_file,
  *
  * Returns the values associated with @key under @group_name
  * translated in the given @locale if available.  If @locale is
- * %NULL then the current locale is assumed. If @key cannot be
- * found then %NULL is returned and @error is set to
+ * %NULL then the current locale is assumed.
+
+ * If @key cannot be found then %NULL is returned and @error is set to
  * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. If the values associated
  * with @key cannot be interpreted or no suitable translations
- * can be found then the untranslated values are returned and
- * @error is set to #G_KEY_FILE_ERROR_INVALID_VALUE and
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND, respectively. In the
- * event that the @group_name cannot be found, %NULL is returned
- * and @error is set to #G_KEY_FILE_ERROR_GROUP_NOT_FOUND.
+ * can be found then the untranslated values are returned.
  * The returned array is %NULL-terminated, so @length may optionally be %NULL.
  *
  * Return value: a newly allocated %NULL-terminated string array
@@ -1550,6 +1651,10 @@ g_key_file_get_locale_string_list (GKeyFile     *key_file,
   GError *key_file_error;
   gchar **values, *value;
 
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
   key_file_error = NULL;
 
   value = g_key_file_get_locale_string (key_file, group_name, 
@@ -1586,7 +1691,7 @@ g_key_file_get_locale_string_list (GKeyFile     *key_file,
  *
  * Associates a list of string values for @key and @locale under
  * @group_name.  If the translation for @key cannot be found then
- * it is created.
+ * it is created. 
  *
  * Since: 2.6
  **/
@@ -1603,7 +1708,10 @@ g_key_file_set_locale_string_list (GKeyFile            *key_file,
   gsize i;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
+  g_return_if_fail (locale != NULL);
+  g_return_if_fail (length != 0);
 
   value_list = g_string_sized_new (length * 128);
   for (i = 0; list[i] != NULL && i < length; i++)
@@ -1632,11 +1740,12 @@ g_key_file_set_locale_string_list (GKeyFile            *key_file,
  * @error: return location for a #GError
  *
  * Returns the value associated with @key under @group_name as a
- * boolean.  If @key cannot be found then the return value is
- * undefined and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the value
- * associated with @key cannot be interpreted as a boolean then
- * the return value is also undefined and @error is set to
+ * boolean. 
+ *
+ * If @key cannot be found then the return value is undefined and
+ * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if
+ * the value associated with @key cannot be interpreted as a boolean
+ * then the return value is also undefined and @error is set to
  * #G_KEY_FILE_ERROR_INVALID_VALUE.
  *
  * Return value: the value associated with the key as a boolean
@@ -1652,6 +1761,10 @@ g_key_file_get_boolean (GKeyFile     *key_file,
   gchar *value;
   gboolean bool_value;
 
+  g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (group_name != NULL, FALSE);
+  g_return_val_if_fail (key != NULL, FALSE);
+
   value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
 
   if (!value)
@@ -1692,7 +1805,7 @@ g_key_file_get_boolean (GKeyFile     *key_file,
  * @value: %TRUE or %FALSE
  *
  * Associates a new boolean value with @key under @group_name.
- * If @key cannot be found then it is created.
+ * If @key cannot be found then it is created. 
  *
  * Since: 2.6
  **/
@@ -1705,6 +1818,7 @@ g_key_file_set_boolean (GKeyFile    *key_file,
   gchar *result;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
 
   result = g_key_file_parse_boolean_as_value (key_file, value);
@@ -1721,14 +1835,17 @@ g_key_file_set_boolean (GKeyFile    *key_file,
  * @error: return location for a #GError
  *
  * Returns the values associated with @key under @group_name as
- * booleans.  If @key cannot be found then the return value is
- * undefined and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the values
- * associated with @key cannot be interpreted as booleans then
- * the return value is also undefined and @error is set to
+ * booleans. If @group_name is %NULL, the start_group is used.
+ *
+ * If @key cannot be found then the return value is undefined and
+ * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if
+ * the values associated with @key cannot be interpreted as booleans
+ * then the return value is also undefined and @error is set to
  * #G_KEY_FILE_ERROR_INVALID_VALUE.
  *
  * Return value: the values associated with the key as a boolean
+ * 
+ * Since: 2.6
  **/
 gboolean *
 g_key_file_get_boolean_list (GKeyFile     *key_file,
@@ -1742,6 +1859,10 @@ g_key_file_get_boolean_list (GKeyFile     *key_file,
   gboolean *bool_values;
   gsize i, num_bools;
 
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
   key_file_error = NULL;
 
   values = g_key_file_get_string_list (key_file, group_name, key,
@@ -1753,7 +1874,7 @@ g_key_file_get_boolean_list (GKeyFile     *key_file,
   if (!values)
     return NULL;
 
-  bool_values = g_new (gboolean, num_bools);
+  bool_values = g_new0 (gboolean, num_bools);
 
   for (i = 0; i < num_bools; i++)
     {
@@ -1788,6 +1909,7 @@ g_key_file_get_boolean_list (GKeyFile     *key_file,
  *
  * Associates a list of boolean values with @key under
  * @group_name.  If @key cannot be found then it is created.
+ * If @group_name is %NULL, the start_group is used.
  *
  * Since: 2.6
  **/
@@ -1802,7 +1924,9 @@ g_key_file_set_boolean_list (GKeyFile    *key_file,
   gsize i;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
+  g_return_if_fail (list != NULL);
 
   value_list = g_string_sized_new (length * 8);
   for (i = 0; i < length; i++)
@@ -1829,14 +1953,16 @@ g_key_file_set_boolean_list (GKeyFile    *key_file,
  * @error: return location for a #GError
  *
  * Returns the value associated with @key under @group_name as an
- * integer. If @key cannot be found then the return value is
- * undefined and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the value
- * associated with @key cannot be interpreted as an integer then
- * the return value is also undefined and @error is set to
+ * integer. If @group_name is %NULL, the start_group is used.
+ *
+ * If @key cannot be found then the return value is undefined and
+ * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if
+ * the value associated with @key cannot be interpreted as an integer
+ * then the return value is also undefined and @error is set to
  * #G_KEY_FILE_ERROR_INVALID_VALUE.
  *
  * Return value: the value associated with the key as an integer.
+ *
  * Since: 2.6
  **/
 gint
@@ -1849,6 +1975,10 @@ g_key_file_get_integer (GKeyFile     *key_file,
   gchar *value;
   gint int_value;
 
+  g_return_val_if_fail (key_file != NULL, -1);
+  g_return_val_if_fail (group_name != NULL, -1);
+  g_return_val_if_fail (key != NULL, -1);
+
   key_file_error = NULL;
 
   value = g_key_file_get_value (key_file, group_name, key, &key_file_error);
@@ -1904,6 +2034,7 @@ g_key_file_set_integer (GKeyFile    *key_file,
   gchar *result;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
 
   result = g_key_file_parse_integer_as_value (key_file, value);
@@ -1920,14 +2051,16 @@ g_key_file_set_integer (GKeyFile    *key_file,
  * @error: return location for a #GError
  *
  * Returns the values associated with @key under @group_name as
- * integers.  If @key cannot be found then the return value is
- * undefined and @error is set to
- * #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if the values
- * associated with @key cannot be interpreted as integers then
- * the return value is also undefined and @error is set to
+ * integers. 
+ *
+ * If @key cannot be found then the return value is undefined and
+ * @error is set to #G_KEY_FILE_ERROR_KEY_NOT_FOUND. Likewise, if
+ * the values associated with @key cannot be interpreted as integers
+ * then the return value is also undefined and @error is set to
  * #G_KEY_FILE_ERROR_INVALID_VALUE.
  *
  * Return value: the values associated with the key as a integer
+ *
  * Since: 2.6
  **/
 gint *
@@ -1942,6 +2075,10 @@ g_key_file_get_integer_list (GKeyFile     *key_file,
   gint *int_values;
   gsize i, num_ints;
 
+  g_return_val_if_fail (key_file != NULL, NULL);
+  g_return_val_if_fail (group_name != NULL, NULL);
+  g_return_val_if_fail (key != NULL, NULL);
+
   values = g_key_file_get_string_list (key_file, group_name, key,
                                       &num_ints, &key_file_error);
 
@@ -1951,7 +2088,7 @@ g_key_file_get_integer_list (GKeyFile     *key_file,
   if (!values)
     return NULL;
 
-  int_values = g_new (gint, num_ints);
+  int_values = g_new0 (gint, num_ints);
 
   for (i = 0; i < num_ints; i++)
     {
@@ -2000,7 +2137,9 @@ g_key_file_set_integer_list (GKeyFile     *key_file,
   gsize i;
 
   g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
   g_return_if_fail (key != NULL);
+  g_return_if_fail (list != NULL);
 
   values = g_string_sized_new (length * 16);
   for (i = 0; i < length; i++)
@@ -2019,35 +2158,458 @@ g_key_file_set_integer_list (GKeyFile     *key_file,
   g_string_free (values, TRUE);
 }
 
-/**
- * g_key_file_has_group:
- * @key_file: a #GKeyFile
- * @group_name: a group name
- *
- * Looks whether the key file has the group @group_name.
- *
- * Return value: %TRUE if @group_name is a part of @key_file, %FALSE
- * otherwise.
- * Since: 2.6
- **/
-gboolean
-g_key_file_has_group (GKeyFile    *key_file,
-                     const gchar *group_name)
+static void
+g_key_file_set_key_comment (GKeyFile             *key_file,
+                            const gchar          *group_name,
+                            const gchar          *key,
+                            const gchar          *comment,
+                            GError              **error)
 {
-  GList *tmp;
   GKeyFileGroup *group;
+  GKeyFileKeyValuePair *pair;
+  GList *key_node, *comment_node, *tmp;
+  
+  group = g_key_file_lookup_group (key_file, group_name);
+  if (!group)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                   _("Key file does not have group '%s'"),
+                   group_name ? group_name : "(null)");
 
-  g_return_val_if_fail (key_file != NULL, FALSE);
-  g_return_val_if_fail (group_name != NULL, FALSE);
+      return;
+    }
 
-  for (tmp = key_file->groups; tmp != NULL; tmp = tmp->next)
+  /* First find the key the comments are supposed to be
+   * associated with
+   */
+  key_node = g_key_file_lookup_key_value_pair_node (key_file, group, key);
+
+  if (key_node == NULL)
     {
-      group = (GKeyFileGroup *) tmp->data;
-      if (group && group->name && (strcmp (group->name, group_name) == 0))
-        return TRUE;
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_KEY_NOT_FOUND,
+                   _("Key file does not have key '%s' in group '%s'"),
+                   key, group->name);
+      return;
     }
 
-  return FALSE;
+  /* Then find all the comments already associated with the
+   * key and free them
+   */
+  tmp = key_node->next;
+  while (tmp != NULL)
+    {
+      GKeyFileKeyValuePair *pair;
+
+      pair = (GKeyFileKeyValuePair *) tmp->data;
+
+      if (pair->key != NULL)
+        break;
+
+      comment_node = tmp;
+      tmp = tmp->next;
+      g_key_file_remove_key_value_pair_node (key_file, group,
+                                             comment_node); 
+    }
+
+  if (comment == NULL)
+    return;
+
+  /* Now we can add our new comment
+   */
+  pair = g_new0 (GKeyFileKeyValuePair, 1);
+  
+  pair->key = NULL;
+  pair->value = g_key_file_parse_comment_as_value (key_file, comment);
+  
+  key_node = g_list_insert (key_node, pair, 1);
+}
+
+static void
+g_key_file_set_group_comment (GKeyFile             *key_file,
+                              const gchar          *group_name,
+                              const gchar          *comment,
+                              GError              **error)
+{
+  GKeyFileGroup *group;
+  
+  group = g_key_file_lookup_group (key_file, group_name);
+  if (!group)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                   _("Key file does not have group '%s'"),
+                   group_name ? group_name : "(null)");
+
+      return;
+    }
+
+  /* First remove any existing comment
+   */
+  if (group->comment)
+    {
+      g_key_file_key_value_pair_free (group->comment);
+      group->comment = NULL;
+    }
+
+  if (comment == NULL)
+    return;
+
+  /* Now we can add our new comment
+   */
+  group->comment = g_new0 (GKeyFileKeyValuePair, 1);
+  
+  group->comment->key = NULL;
+  group->comment->value = g_key_file_parse_comment_as_value (key_file, comment);
+}
+
+static void
+g_key_file_set_top_comment (GKeyFile             *key_file,
+                            const gchar          *comment,
+                            GError              **error)
+{
+  GList *group_node;
+  GKeyFileGroup *group;
+  GKeyFileKeyValuePair *pair;
+
+  /* The last group in the list should be the top (comments only)
+   * group in the file
+   */
+  g_assert (key_file->groups != NULL);
+  group_node = g_list_last (key_file->groups);
+  group = (GKeyFileGroup *) group_node->data;
+  g_assert (group->name == NULL);
+
+  /* Note all keys must be comments at the top of
+   * the file, so we can just free it all.
+   */
+  if (group->key_value_pairs != NULL)
+    {
+      g_list_foreach (group->key_value_pairs, 
+                      (GFunc) g_key_file_key_value_pair_free, 
+                      NULL);
+      g_list_free (group->key_value_pairs);
+      group->key_value_pairs = NULL;
+    }
+
+  if (comment == NULL)
+     return;
+
+  pair = g_new0 (GKeyFileKeyValuePair, 1);
+  
+  pair->key = NULL;
+  pair->value = g_key_file_parse_comment_as_value (key_file, comment);
+  
+  group->key_value_pairs =
+    g_list_prepend (group->key_value_pairs, pair);
+}
+
+/**
+ * g_key_file_set_comment:
+ * @key_file: a #GKeyFile
+ * @group_name: a group name, or %NULL
+ * @key: a key
+ * @comment: a comment
+ * @error: return location for a #GError
+ *
+ * Places a comment above @key from @group_name.
+ * @group_name. If @key is %NULL then @comment will
+ * be written above @group_name.  If both @key
+ * and @group_name are NULL, then @comment will
+ * be written above the first group in the file.
+ *
+ * Since: 2.6
+ **/
+void
+g_key_file_set_comment (GKeyFile             *key_file,
+                        const gchar          *group_name,
+                        const gchar          *key,
+                        const gchar          *comment,
+                        GError              **error)
+{
+  g_return_if_fail (key_file != NULL);
+
+  if (group_name != NULL && key != NULL)
+    g_key_file_set_key_comment (key_file, group_name, key, comment, error);
+  else if (group_name != NULL)
+    g_key_file_set_group_comment (key_file, group_name, comment, error);
+  else
+    g_key_file_set_top_comment (key_file, comment, error);
+
+  if (comment != NULL)
+    key_file->approximate_size += strlen (comment);
+}
+
+static gchar *
+g_key_file_get_key_comment (GKeyFile             *key_file,
+                            const gchar          *group_name,
+                            const gchar          *key,
+                            GError              **error)
+{
+  GKeyFileGroup *group;
+  GKeyFileKeyValuePair *pair;
+  GList *key_node, *tmp;
+  GString *string;
+  gchar *comment;
+
+  group = g_key_file_lookup_group (key_file, group_name);
+  if (!group)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                   _("Key file does not have group '%s'"),
+                   group_name ? group_name : "(null)");
+
+      return NULL;
+    }
+
+  /* First find the key the comments are supposed to be
+   * associated with
+   */
+  key_node = g_key_file_lookup_key_value_pair_node (key_file, group, key);
+
+  if (key_node == NULL)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_KEY_NOT_FOUND,
+                   _("Key file does not have key '%s' in group '%s'"),
+                   key, group->name);
+      return NULL;
+    }
+
+  string = NULL;
+
+  /* Then find all the comments already associated with the
+   * key and concatentate them.
+   */
+  tmp = key_node->next;
+  if (!key_node->next)
+    return NULL;
+
+  pair = (GKeyFileKeyValuePair *) tmp->data;
+  if (pair->key != NULL)
+    return NULL;
+
+  while (tmp->next)
+    {
+      pair = (GKeyFileKeyValuePair *) tmp->next->data;
+      
+      if (pair->key != NULL)
+        break;
+
+      tmp = tmp->next;
+    }
+
+  while (tmp != key_node)
+    {
+      GKeyFileKeyValuePair *pair;
+      
+      pair = (GKeyFileKeyValuePair *) tmp->data;
+      
+      if (string == NULL)
+       string = g_string_sized_new (512);
+      
+      comment = g_key_file_parse_value_as_comment (key_file, pair->value);
+      g_string_append (string, comment);
+      g_free (comment);
+      
+      tmp = tmp->prev;
+    }
+
+  if (string != NULL)
+    {
+      comment = string->str;
+      g_string_free (string, FALSE);
+    }
+  else
+    comment = NULL;
+
+  return comment;
+}
+
+static gchar *
+get_group_comment (GKeyFile       *key_file,
+                  GKeyFileGroup  *group,
+                  GError        **error)
+{
+  GString *string;
+  GList *tmp;
+  gchar *comment;
+
+  string = NULL;
+
+  tmp = group->key_value_pairs;
+  while (tmp)
+    {
+      GKeyFileKeyValuePair *pair;
+
+      pair = (GKeyFileKeyValuePair *) tmp->data;
+
+      if (pair->key != NULL)
+       {
+         tmp = tmp->prev;
+         break;
+       }
+
+      if (tmp->next == NULL)
+       break;
+
+      tmp = tmp->next;
+    }
+  
+  while (tmp != NULL)
+    {
+      GKeyFileKeyValuePair *pair;
+
+      pair = (GKeyFileKeyValuePair *) tmp->data;
+
+      if (string == NULL)
+        string = g_string_sized_new (512);
+
+      comment = g_key_file_parse_value_as_comment (key_file, pair->value);
+      g_string_append (string, comment);
+      g_free (comment);
+
+      tmp = tmp->prev;
+    }
+
+  if (string != NULL)
+    return g_string_free (string, FALSE);
+
+  return NULL;
+}
+
+static gchar *
+g_key_file_get_group_comment (GKeyFile             *key_file,
+                              const gchar          *group_name,
+                              GError              **error)
+{
+  GList *group_node;
+  GKeyFileGroup *group;
+  
+  group_node = g_key_file_lookup_group_node (key_file, group_name);
+  group = (GKeyFileGroup *)group_node->data;
+  if (!group)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                   G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                   _("Key file does not have group '%s'"),
+                   group_name ? group_name : "(null)");
+
+      return NULL;
+    }
+
+  if (group->comment)
+    return g_strdup (group->comment->value);
+  
+  group_node = group_node->next;
+  group = (GKeyFileGroup *)group_node->data;  
+  return get_group_comment (key_file, group, error);
+}
+
+static gchar *
+g_key_file_get_top_comment (GKeyFile             *key_file,
+                            GError              **error)
+{
+  GList *group_node;
+  GKeyFileGroup *group;
+
+  /* The last group in the list should be the top (comments only)
+   * group in the file
+   */
+  g_assert (key_file->groups != NULL);
+  group_node = g_list_last (key_file->groups);
+  group = (GKeyFileGroup *) group_node->data;
+  g_assert (group->name == NULL);
+
+  return get_group_comment (key_file, group, error);
+}
+
+/**
+ * g_key_file_get_comment:
+ * @key_file: a #GKeyFile
+ * @group_name: a group name, or %NULL
+ * @key: a key
+ * @error: return location for a #GError
+ *
+ * Retrieves a comment above @key from @group_name.
+ * @group_name. If @key is %NULL then @comment will
+ * be read from above @group_name.  If both @key
+ * and @group_name are NULL, then @comment will
+ * be read from above the first group in the file.
+ *
+ * Returns: a comment that should be freed with g_free()
+ *
+ * Since: 2.6
+ **/
+gchar * 
+g_key_file_get_comment (GKeyFile             *key_file,
+                        const gchar          *group_name,
+                        const gchar          *key,
+                        GError              **error)
+{
+  g_return_val_if_fail (key_file != NULL, NULL);
+
+  if (group_name != NULL && key != NULL)
+    return g_key_file_get_key_comment (key_file, group_name, key, error);
+  else if (group_name != NULL)
+    return g_key_file_get_group_comment (key_file, group_name, error);
+  else
+    return g_key_file_get_top_comment (key_file, error);
+}
+
+/**
+ * g_key_file_remove_comment:
+ * @key_file: a #GKeyFile
+ * @group_name: a group name, or %NULL
+ * @key: a key
+ * @error: return location for a #GError
+ *
+ * Removes a comment above @key from @group_name.
+ * @group_name. If @key is %NULL then @comment will
+ * be written above @group_name.  If both @key
+ * and @group_name are NULL, then @comment will
+ * be written above the first group in the file.
+ *
+ * Since: 2.6
+ **/
+
+void
+g_key_file_remove_comment (GKeyFile             *key_file,
+                           const gchar          *group_name,
+                           const gchar          *key,
+                           GError              **error)
+{
+  g_return_if_fail (key_file != NULL);
+
+  if (group_name != NULL && key != NULL)
+    g_key_file_set_key_comment (key_file, group_name, key, NULL, error);
+  else if (group_name != NULL)
+    g_key_file_set_group_comment (key_file, group_name, NULL, error);
+  else
+    g_key_file_set_top_comment (key_file, NULL, error);
+}
+
+/**
+ * g_key_file_has_group:
+ * @key_file: a #GKeyFile
+ * @group_name: a group name
+ *
+ * Looks whether the key file has the group @group_name.
+ *
+ * Return value: %TRUE if @group_name is a part of @key_file, %FALSE
+ * otherwise.
+ * Since: 2.6
+ **/
+gboolean
+g_key_file_has_group (GKeyFile    *key_file,
+                     const gchar *group_name)
+{
+  g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (group_name != NULL, FALSE);
+
+  return g_key_file_lookup_group_node (key_file, group_name) != NULL;
 }
 
 /**
@@ -2058,10 +2620,11 @@ g_key_file_has_group (GKeyFile    *key_file,
  * @error: return location for a #GError
  *
  * Looks whether the key file has the key @key in the group
- * @group_name.
+ * @group_name. 
  *
  * Return value: %TRUE if @key is a part of @group_name, %FALSE
  * otherwise.
+ *
  * Since: 2.6
  **/
 gboolean
@@ -2074,13 +2637,9 @@ g_key_file_has_key (GKeyFile     *key_file,
   GKeyFileGroup *group;
 
   g_return_val_if_fail (key_file != NULL, FALSE);
+  g_return_val_if_fail (group_name != NULL, FALSE);
   g_return_val_if_fail (key != NULL, FALSE);
 
-  pair = NULL;
-
-  if (group_name == NULL)
-    group_name = (const gchar *) key_file->start_group_name;
-
   group = g_key_file_lookup_group (key_file, group_name);
 
   if (!group)
@@ -2088,7 +2647,7 @@ g_key_file_has_key (GKeyFile     *key_file,
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
                    _("Key file does not have group '%s'"),
-                   group_name);
+                   group_name ? group_name : "(null)");
 
       return FALSE;
     }
@@ -2106,7 +2665,7 @@ g_key_file_add_group (GKeyFile    *key_file,
 
   g_return_if_fail (key_file != NULL);
   g_return_if_fail (group_name != NULL);
-  g_return_if_fail (g_key_file_lookup_group (key_file, group_name) == NULL);
+  g_return_if_fail (g_key_file_lookup_group_node (key_file, group_name) == NULL);
 
   group = g_new0 (GKeyFileGroup, 1);
   group->name = g_strdup (group_name);
@@ -2115,11 +2674,10 @@ g_key_file_add_group (GKeyFile    *key_file,
   key_file->approximate_size += strlen (group_name) + 3;
   key_file->current_group = group;
 
-  if (key_file->start_group_name == NULL)
-    key_file->start_group_name = g_strdup (group_name);
+  if (key_file->start_group == NULL)
+    key_file->start_group = group;
 }
 
-
 static void
 g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair)
 {
@@ -2131,27 +2689,80 @@ g_key_file_key_value_pair_free (GKeyFileKeyValuePair *pair)
     }
 }
 
+/* Be careful not to call this function on a node with data in the
+ * lookup map without removing it from the lookup map, first.
+ *
+ * Some current cases where this warning is not a concern are
+ * when:
+ *   - the node being removed is a comment node
+ *   - the entire lookup map is getting destroyed soon after
+ *     anyway.
+ */ 
+static void
+g_key_file_remove_key_value_pair_node (GKeyFile      *key_file,
+                                       GKeyFileGroup *group,
+                                      GList         *pair_node)
+{
+
+  GKeyFileKeyValuePair *pair;
+
+  pair = (GKeyFileKeyValuePair *) pair_node->data;
+
+  group->key_value_pairs = g_list_remove_link (group->key_value_pairs, pair_node);
+
+  if (pair->key != NULL)
+    key_file->approximate_size -= strlen (pair->key) + 1;
+
+  g_assert (pair->value != NULL);
+  key_file->approximate_size -= strlen (pair->value);
+
+  g_key_file_key_value_pair_free (pair);
+
+  g_list_free_1 (pair_node);
+}
+
 static void
 g_key_file_remove_group_node (GKeyFile *key_file,
                              GList    *group_node)
 {
   GKeyFileGroup *group;
+  GList *tmp;
 
   group = (GKeyFileGroup *) group_node->data;
 
-  /* If the current group gets deleted make the current group the first
-   * group.
+  /* If the current group gets deleted make the current group the last
+   * added group.
    */
   if (key_file->current_group == group)
     {
-      GList *first_group;
+      /* groups should always contain at least the top comment group,
+       * unless g_key_file_clear has been called
+       */
+      if (key_file->groups)
+        key_file->current_group = (GKeyFileGroup *) key_file->groups->data;
+      else
+        key_file->current_group = NULL;
+    }
 
-      first_group = key_file->groups;
+  /* If the start group gets deleted make the start group the first
+   * added group.
+   */
+  if (key_file->start_group == group)
+    {
+      tmp = g_list_last (key_file->groups);
+      while (tmp != NULL)
+       {
+         if (tmp != group_node &&
+             ((GKeyFileGroup *) tmp->data)->name != NULL)
+           break;
 
-      if (first_group)
-        key_file->current_group = (GKeyFileGroup *) first_group->data;
+         tmp = tmp->prev;
+       }
+
+      if (tmp)
+        key_file->start_group = (GKeyFileGroup *) tmp->data;
       else
-        key_file->current_group = NULL;
+        key_file->start_group = NULL;
     }
 
   key_file->groups = g_list_remove_link (key_file->groups, group_node);
@@ -2159,13 +2770,17 @@ g_key_file_remove_group_node (GKeyFile *key_file,
   if (group->name != NULL)
     key_file->approximate_size -= strlen (group->name) + 3;
 
-  /* FIXME: approximate_size isn't getting updated for the
-   * removed keys in group.  
-   */
-  g_list_foreach (group->key_value_pairs, 
-                  (GFunc) g_key_file_key_value_pair_free, NULL);
-  g_list_free (group->key_value_pairs);
-  group->key_value_pairs = NULL;
+  tmp = group->key_value_pairs;
+  while (tmp != NULL)
+    {
+      GList *pair_node;
+
+      pair_node = tmp;
+      tmp = tmp->next;
+      g_key_file_remove_key_value_pair_node (key_file, group, pair_node);
+    }
+
+  g_assert (group->key_value_pairs == NULL);
 
   if (group->lookup_map)
     {
@@ -2194,45 +2809,37 @@ g_key_file_remove_group (GKeyFile     *key_file,
                         const gchar  *group_name,
                         GError      **error)
 {
-  GKeyFileGroup *group;
   GList *group_node;
 
   g_return_if_fail (key_file != NULL);
   g_return_if_fail (group_name != NULL);
 
-  group = g_key_file_lookup_group (key_file, group_name);
+  group_node = g_key_file_lookup_group_node (key_file, group_name);
 
-  if (!group)
-    g_set_error (error, G_KEY_FILE_ERROR,
-                G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
-                _("Key file does not have group '%s'"),
-                group_name);
+  if (!group_node)
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                  G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
+                  _("Key file does not have group '%s'"),
+                  group_name);
+      return;
+    }
 
-  group_node = g_list_find (key_file->groups, group);
-  g_key_file_remove_group_node (key_file, group_node);
+    g_key_file_remove_group_node (key_file, group_node);
 }
 
 static void
-g_key_file_add_key (GKeyFile    *key_file,
-                   const gchar *group_name,
-                   const gchar *key,
-                   const gchar *value)
+g_key_file_add_key (GKeyFile      *key_file,
+                   GKeyFileGroup *group,
+                   const gchar   *key,
+                   const gchar   *value)
 {
-  GKeyFileGroup *group;
   GKeyFileKeyValuePair *pair;
 
-  group = g_key_file_lookup_group (key_file, group_name);
-
-  if (!group)
-    {
-      g_key_file_add_group (key_file, group_name);
-      group = (GKeyFileGroup *) key_file->groups->data;
-    }
-
   pair = g_new0 (GKeyFileKeyValuePair, 1);
 
   pair->key = g_strdup (key);
-  pair->value =  g_strdup (value);
+  pair->value = g_strdup (value);
 
   g_hash_table_replace (group->lookup_map, pair->key, pair);
   group->key_value_pairs = g_list_prepend (group->key_value_pairs, pair);
@@ -2259,42 +2866,43 @@ g_key_file_remove_key (GKeyFile     *key_file,
   GKeyFileGroup *group;
   GKeyFileKeyValuePair *pair;
 
-  pair = NULL;
+  g_return_if_fail (key_file != NULL);
+  g_return_if_fail (group_name != NULL);
+  g_return_if_fail (key != NULL);
 
-  if (group_name == NULL)
-    group = key_file->current_group;
-  else
-    group = g_key_file_lookup_group (key_file, group_name);
+  pair = NULL;
 
+  group = g_key_file_lookup_group (key_file, group_name);
   if (!group)
     {
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
                    _("Key file does not have group '%s'"),
-                   group_name);
+                   group_name ? group_name : "(null)");
       return;
     }
 
-  group->key_value_pairs = g_list_remove (group->key_value_pairs, key_file);
   pair = g_key_file_lookup_key_value_pair (key_file, group, key);
 
   if (!pair)
     {
       g_set_error (error, G_KEY_FILE_ERROR,
                    G_KEY_FILE_ERROR_KEY_NOT_FOUND,
-                   _("Key file does not have key '%s'"), key);
+                   _("Key file does not have key '%s' in group '%s'"),
+                  key, group->name);
       return;
     }
 
-  g_hash_table_remove (group->lookup_map, pair->key);
-
   key_file->approximate_size -= strlen (pair->key) + strlen (pair->value) + 2;
+
+  group->key_value_pairs = g_list_remove (group->key_value_pairs, pair);
+  g_hash_table_remove (group->lookup_map, pair->key);  
   g_key_file_key_value_pair_free (pair);
 }
 
-static GKeyFileGroup *
-g_key_file_lookup_group (GKeyFile    *key_file,
-                        const gchar *group_name)
+static GList *
+g_key_file_lookup_group_node (GKeyFile    *key_file,
+                             const gchar *group_name)
 {
   GKeyFileGroup *group;
   GList *tmp;
@@ -2310,7 +2918,43 @@ g_key_file_lookup_group (GKeyFile    *key_file,
       group = NULL;
     }
 
-  return group;
+  return tmp;
+}
+
+static GKeyFileGroup *
+g_key_file_lookup_group (GKeyFile    *key_file,
+                        const gchar *group_name)
+{
+  GList *group_node;
+
+  group_node = g_key_file_lookup_group_node (key_file, group_name);
+
+  if (group_node != NULL)
+    return (GKeyFileGroup *) group_node->data; 
+
+  return NULL;
+}
+
+static GList *
+g_key_file_lookup_key_value_pair_node (GKeyFile       *key_file,
+                                      GKeyFileGroup  *group,
+                                       const gchar    *key)
+{
+  GList *key_node;
+
+  for (key_node = group->key_value_pairs;
+       key_node != NULL;
+       key_node = key_node->next)
+    {
+      GKeyFileKeyValuePair *pair;
+
+      pair = (GKeyFileKeyValuePair *) key_node->data; 
+
+      if (pair->key && strcmp (pair->key, key) == 0)
+        break;
+    }
+
+  return key_node;
 }
 
 static GKeyFileKeyValuePair *
@@ -2345,14 +2989,9 @@ g_key_file_line_is_group (const gchar *line)
 
   p = g_utf8_next_char (p);
 
-  if (!*p)
-    return FALSE;
-
-  p = g_utf8_next_char (p);
-
   /* Group name must be non-empty
    */
-  if (*p == ']')
+  if (!*p || *p == ']')
     return FALSE;
 
   while (*p && *p != ']')
@@ -2388,10 +3027,9 @@ g_key_file_parse_value_as_string (GKeyFile     *key_file,
                                  GSList      **pieces,
                                  GError      **error)
 {
-  GError *parse_error = NULL;
   gchar *string_value, *p, *q0, *q;
 
-  string_value = g_new (gchar, strlen (value) + 1);
+  string_value = g_new0 (gchar, strlen (value) + 1);
 
   p = (gchar *) value;
   q0 = q = string_value;
@@ -2423,6 +3061,13 @@ g_key_file_parse_value_as_string (GKeyFile     *key_file,
               *q = '\\';
               break;
 
+           case '\0':
+             g_set_error (error, G_KEY_FILE_ERROR,
+                          G_KEY_FILE_ERROR_INVALID_VALUE,
+                          _("Key file contains escape character "
+                            "at end of line"));
+             break;
+
             default:
              if (pieces && *p == key_file->list_separator)
                *q = key_file->list_separator;
@@ -2431,7 +3076,7 @@ g_key_file_parse_value_as_string (GKeyFile     *key_file,
                  *q++ = '\\';
                  *q = *p;
                  
-                 if (parse_error == NULL)
+                 if (*error == NULL)
                    {
                      gchar sequence[3];
                      
@@ -2458,15 +3103,13 @@ g_key_file_parse_value_as_string (GKeyFile     *key_file,
            }
        }
 
+      if (*p == '\0')
+       break;
+
       q++;
       p++;
     }
 
-  if (p[-1] == '\\' && error == NULL)
-    g_set_error (error, G_KEY_FILE_ERROR,
-                G_KEY_FILE_ERROR_INVALID_VALUE,
-                _("Key file contains escape character at end of line"));
-
   *q = '\0';
   if (pieces)
   {
@@ -2485,16 +3128,18 @@ g_key_file_parse_string_as_value (GKeyFile    *key_file,
 {
   gchar *value, *p, *q;
   gsize length;
+  gboolean parsing_leading_space;
 
   length = strlen (string) + 1;
 
   /* Worst case would be that every character needs to be escaped.
    * In other words every character turns to two characters
    */
-  value = g_new (gchar, 2 * length);
+  value = g_new0 (gchar, 2 * length);
 
   p = (gchar *) string;
   q = value;
+  parsing_leading_space = TRUE;
   while (p < (string + length - 1))
     {
       gchar escaped_character[3] = { '\\', 0, 0 };
@@ -2502,20 +3147,36 @@ g_key_file_parse_string_as_value (GKeyFile    *key_file,
       switch (*p)
         {
         case ' ':
-          escaped_character[1] = 's';
-          strcpy (q, escaped_character);
-          q += 2;
+          if (parsing_leading_space)
+            {
+              escaped_character[1] = 's';
+              strcpy (q, escaped_character);
+              q += 2;
+            }
+          else
+            {
+             *q = *p;
+             q++;
+            }
+          break;
+        case '\t':
+          if (parsing_leading_space)
+            {
+              escaped_character[1] = 't';
+              strcpy (q, escaped_character);
+              q += 2;
+            }
+          else
+            {
+             *q = *p;
+             q++;
+            }
           break;
         case '\n':
           escaped_character[1] = 'n';
           strcpy (q, escaped_character);
           q += 2;
           break;
-        case '\t':
-          escaped_character[1] = 't';
-          strcpy (q, escaped_character);
-          q += 2;
-          break;
         case '\r':
           escaped_character[1] = 'r';
           strcpy (q, escaped_character);
@@ -2525,6 +3186,7 @@ g_key_file_parse_string_as_value (GKeyFile    *key_file,
           escaped_character[1] = '\\';
           strcpy (q, escaped_character);
           q += 2;
+          parsing_leading_space = FALSE;
           break;
         default:
          if (escape_separator && *p == key_file->list_separator)
@@ -2532,11 +3194,13 @@ g_key_file_parse_string_as_value (GKeyFile    *key_file,
              escaped_character[1] = key_file->list_separator;
              strcpy (q, escaped_character);
              q += 2;
+              parsing_leading_space = TRUE;
            }
          else 
            {
              *q = *p;
              q++;
+              parsing_leading_space = FALSE;
            }
           break;
         }
@@ -2553,15 +3217,30 @@ g_key_file_parse_value_as_integer (GKeyFile     *key_file,
                                   GError      **error)
 {
   gchar *end_of_valid_int;
-  gint int_value = 0;
+  glong long_value;
+  gint int_value;
 
-  int_value = strtol (value, &end_of_valid_int, 0);
+  errno = 0;
+  long_value = strtol (value, &end_of_valid_int, 10);
 
-  if (*end_of_valid_int != '\0')
-    g_set_error (error, G_KEY_FILE_ERROR,
-                G_KEY_FILE_ERROR_INVALID_VALUE,
-                _("Value '%s' cannot be interpreted as a number."), value);
+  if (*value == '\0' || *end_of_valid_int != '\0')
+    {
+      g_set_error (error, G_KEY_FILE_ERROR,
+                  G_KEY_FILE_ERROR_INVALID_VALUE,
+                  _("Value '%s' cannot be interpreted as a number."), value);
+      return 0;
+    }
 
+  int_value = long_value;
+  if (int_value != long_value || errno == ERANGE)
+    {
+      g_set_error (error,
+                  G_KEY_FILE_ERROR, 
+                  G_KEY_FILE_ERROR_INVALID_VALUE,
+                  _("Integer value '%s' out of range"), value);
+      return 0;
+    }
+  
   return int_value;
 }
 
@@ -2602,3 +3281,58 @@ g_key_file_parse_boolean_as_value (GKeyFile *key_file,
   else
     return g_strdup ("false");
 }
+
+static gchar *
+g_key_file_parse_value_as_comment (GKeyFile    *key_file,
+                                   const gchar *value)
+{
+  GString *string;
+  gchar **lines, *comment;
+  gsize i;
+
+  string = g_string_sized_new (512);
+
+  lines = g_strsplit (value, "\n", 0);
+
+  for (i = 0; lines[i] != NULL; i++)
+    {
+        if (lines[i][0] != '#')
+           g_string_append_printf (string, "%s\n", lines[i]);
+        else 
+           g_string_append_printf (string, "%s\n", lines[i] + 1);
+    }
+  g_strfreev (lines);
+
+  comment = string->str;
+
+  g_string_free (string, FALSE);
+
+  return comment;
+}
+
+static gchar *
+g_key_file_parse_comment_as_value (GKeyFile      *key_file,
+                                   const gchar   *comment)
+{
+  GString *string;
+  gchar **lines, *value;
+  gsize i;
+
+  string = g_string_sized_new (512);
+
+  lines = g_strsplit (comment, "\n", 0);
+
+  for (i = 0; lines[i] != NULL; i++)
+    g_string_append_printf (string, "#%s%s", lines[i], 
+                            lines[i + 1] == NULL? "" : "\n");
+  g_strfreev (lines);
+
+  value = string->str;
+
+  g_string_free (string, FALSE);
+
+  return value;
+}
+
+#define __G_KEY_FILE_C__
+#include "galiasdef.c"