+static IconData *
+load_icon_data (const char *path)
+{
+ GKeyFile *icon_file;
+ char **split;
+ gsize length;
+ char *str;
+ char *split_point;
+ int i;
+ gint *ivalues;
+ GError *error = NULL;
+ gchar **keys;
+ gsize n_keys;
+ IconData *data;
+
+ icon_file = g_key_file_new ();
+ g_key_file_set_list_separator (icon_file, ',');
+ g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
+ if (error)
+ {
+ g_error_free (error);
+ g_key_file_free (icon_file);
+
+ return NULL;
+ }
+
+ data = g_new0 (IconData, 1);
+
+ ivalues = g_key_file_get_integer_list (icon_file,
+ "Icon Data", "EmbeddedTextRectangle",
+ &length, NULL);
+ if (ivalues)
+ {
+ if (length == 4)
+ {
+ data->has_embedded_rect = TRUE;
+ data->x0 = ivalues[0];
+ data->y0 = ivalues[1];
+ data->x1 = ivalues[2];
+ data->y1 = ivalues[3];
+ }
+
+ g_free (ivalues);
+ }
+
+ str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
+ if (str)
+ {
+ split = g_strsplit (str, "|", -1);
+
+ data->n_attach_points = g_strv_length (split);
+ data->attach_points = g_new (int, 2 * data->n_attach_points);
+
+ i = 0;
+ while (split[i] != NULL && i < data->n_attach_points)
+ {
+ split_point = strchr (split[i], ',');
+ if (split_point)
+ {
+ *split_point = 0;
+ split_point++;
+ data->attach_points[2 * i] = atoi (split[i]);
+ data->attach_points[2 * i + 1] = atoi (split_point);
+ }
+ i++;
+ }
+
+ g_strfreev (split);
+ g_free (str);
+ }
+
+ keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
+ data->display_names = g_new0 (gchar *, 2 * n_keys + 1);
+ data->n_display_names = 0;
+
+ for (i = 0; i < n_keys; i++)
+ {
+ gchar *lang, *name;
+
+ if (g_str_has_prefix (keys[i], "DisplayName"))
+ {
+ gchar *open, *close = NULL;
+
+ open = strchr (keys[i], '[');
+
+ if (open)
+ close = strchr (open, ']');
+
+ if (open && close)
+ {
+ lang = g_strndup (open + 1, close - open - 1);
+ name = g_key_file_get_locale_string (icon_file,
+ "Icon Data", "DisplayName",
+ lang, NULL);
+ }
+ else
+ {
+ lang = g_strdup ("C");
+ name = g_key_file_get_string (icon_file,
+ "Icon Data", "DisplayName",
+ NULL);
+ }
+
+ data->display_names[2 * data->n_display_names] = lang;
+ data->display_names[2 * data->n_display_names + 1] = name;
+ data->n_display_names++;
+ }
+ }
+
+ g_strfreev (keys);
+
+ g_key_file_free (icon_file);
+
+ /* -1 means not computed yet, the real value depends
+ * on string pool state, and will be computed
+ * later
+ */
+ data->size = -1;
+
+ return data;
+}
+
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
+static void
+canonicalize_filename (gchar *filename)
+{
+ gchar *p, *q;
+ gboolean last_was_slash = FALSE;
+
+ p = filename;
+ q = filename;
+
+ while (*p)
+ {
+ if (*p == G_DIR_SEPARATOR)
+ {
+ if (!last_was_slash)
+ *q++ = G_DIR_SEPARATOR;
+
+ last_was_slash = TRUE;
+ }
+ else
+ {
+ if (last_was_slash && *p == '.')
+ {
+ if (*(p + 1) == G_DIR_SEPARATOR ||
+ *(p + 1) == '\0')
+ {
+ if (*(p + 1) == '\0')
+ break;
+
+ p += 1;
+ }
+ else if (*(p + 1) == '.' &&
+ (*(p + 2) == G_DIR_SEPARATOR ||
+ *(p + 2) == '\0'))
+ {
+ if (q > filename + 1)
+ {
+ q--;
+ while (q > filename + 1 &&
+ *(q - 1) != G_DIR_SEPARATOR)
+ q--;
+ }
+
+ if (*(p + 2) == '\0')
+ break;
+
+ p += 2;
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+ else
+ {
+ *q++ = *p;
+ last_was_slash = FALSE;
+ }
+ }
+
+ p++;
+ }
+
+ if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+ q--;
+
+ *q = '\0';
+}
+
+static gchar *
+follow_links (const gchar *path)
+{
+ gchar *target;
+ gchar *d, *s;
+ gchar *path2 = NULL;
+
+ path2 = g_strdup (path);
+ while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+ {
+ target = g_file_read_link (path2, NULL);
+
+ if (target)
+ {
+ if (g_path_is_absolute (target))
+ path2 = target;
+ else
+ {
+ d = g_path_get_dirname (path2);
+ s = g_build_filename (d, target, NULL);
+ g_free (d);
+ g_free (target);
+ g_free (path2);
+ path2 = s;
+ }
+ }
+ else
+ break;
+ }
+
+ if (strcmp (path, path2) == 0)
+ {
+ g_free (path2);
+ path2 = NULL;
+ }
+
+ return path2;
+}
+
+static void
+maybe_cache_image_data (Image *image,
+ const gchar *path)
+{
+ if (!index_only && !image->image_data &&
+ (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
+ {
+ GdkPixbuf *pixbuf;
+ ImageData *idata;
+ gchar *path2;
+
+ idata = g_hash_table_lookup (image_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ ImageData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = g_new0 (ImageData, 1);
+ g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+ }
+
+ if (!idata->has_pixdata)
+ {
+ pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+
+ if (pixbuf)
+ {
+ gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+ idata->size = idata->pixdata.length + 8;
+ idata->has_pixdata = TRUE;
+ }
+ }
+
+ image->image_data = idata;
+
+ g_free (path2);
+ }
+}
+
+static void
+maybe_cache_icon_data (Image *image,
+ const gchar *path)
+{
+ if (g_str_has_suffix (path, ".icon"))
+ {
+ IconData *idata = NULL;
+ gchar *path2 = NULL;
+
+ idata = g_hash_table_lookup (icon_data_hash, path);
+ path2 = follow_links (path);
+
+ if (path2)
+ {
+ IconData *idata2;
+
+ canonicalize_filename (path2);
+
+ idata2 = g_hash_table_lookup (icon_data_hash, path2);
+
+ if (idata && idata2 && idata != idata2)
+ g_error ("different idatas found for symlinked '%s' and '%s'\n",
+ path, path2);
+
+ if (idata && !idata2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+
+ if (!idata && idata2)
+ {
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
+ idata = idata2;
+ }
+ }
+
+ if (!idata)
+ {
+ idata = load_icon_data (path);
+ g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
+ if (path2)
+ g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
+ }
+
+ image->icon_data = idata;
+
+ g_free (path2);
+ }
+}
+
+/**
+ * Finds all dir separators and replaces them with '/'.
+ * This makes sure that only /-separated paths are written in cache files,
+ * maintaining compatibility with theme index files that use slashes as
+ * directory separators on all platforms.
+ */
+static void
+replace_backslashes_with_slashes (gchar *path)
+{
+ size_t i;
+ if (path == NULL)
+ return;
+ for (i = 0; path[i]; i++)
+ if (G_IS_DIR_SEPARATOR (path[i]))
+ path[i] = '/';
+}
+
+static GList *