2 * Copyright (C) 2004 Anders Carlsson <andersca@gnome.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
33 #include <sys/utime.h>
39 #include <glib/gstdio.h>
40 #include <gdk-pixbuf/gdk-pixdata.h>
41 #include <glib/gi18n.h>
42 #include "gtkiconcachevalidator.h"
44 static gboolean force_update = FALSE;
45 static gboolean ignore_theme_index = FALSE;
46 static gboolean quiet = FALSE;
47 static gboolean index_only = FALSE;
48 static gboolean validate = FALSE;
49 static gchar *var_name = "-";
51 /* Quite ugly - if we just add the c file to the
52 * list of sources in Makefile.am, libtool complains.
54 #include "gtkiconcachevalidator.c"
56 #define CACHE_NAME "icon-theme.cache"
58 #define HAS_SUFFIX_XPM (1 << 0)
59 #define HAS_SUFFIX_SVG (1 << 1)
60 #define HAS_SUFFIX_PNG (1 << 2)
61 #define HAS_ICON_FILE (1 << 3)
63 #define MAJOR_VERSION 1
64 #define MINOR_VERSION 0
65 #define HASH_OFFSET 12
67 #define ALIGN_VALUE(this, boundary) \
68 (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
74 static GStatBuf cache_stat;
75 static gboolean cache_up_to_date;
77 static int check_dir_mtime (const char *dir,
81 if (tf != FTW_NS && sb->st_mtime > cache_stat.st_mtime)
83 cache_up_to_date = FALSE;
92 is_cache_up_to_date (const gchar *path)
97 cache_path = g_build_filename (path, CACHE_NAME, NULL);
98 retval = g_stat (cache_path, &cache_stat);
103 /* Cache file not found */
107 cache_up_to_date = TRUE;
109 ftw (path, check_dir_mtime, 20);
111 return cache_up_to_date;
114 #else /* !HAVE_FTW_H */
117 is_cache_up_to_date (const gchar *path)
119 GStatBuf path_stat, cache_stat;
123 retval = g_stat (path, &path_stat);
127 /* We can't stat the path,
128 * assume we have a updated cache */
132 cache_path = g_build_filename (path, CACHE_NAME, NULL);
133 retval = g_stat (cache_path, &cache_stat);
138 /* Cache file not found */
143 return cache_stat.st_mtime >= path_stat.st_mtime;
146 #endif /* !HAVE_FTW_H */
149 has_theme_index (const gchar *path)
154 index_path = g_build_filename (path, "index.theme", NULL);
156 result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
167 gboolean has_pixdata;
174 int has_embedded_rect;
181 char **display_names;
187 static GHashTable *image_data_hash = NULL;
188 static GHashTable *icon_data_hash = NULL;
195 ImageData *image_data;
196 guint pixel_data_size;
199 guint icon_data_size;
204 foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
206 Image *image = (Image *)value;
207 GHashTable *files = user_data;
209 gboolean free_key = FALSE;
211 if (image->flags == HAS_ICON_FILE)
213 /* just a .icon file, throw away */
220 list = g_hash_table_lookup (files, key);
224 list = g_list_prepend (list, value);
225 g_hash_table_insert (files, key, list);
234 load_icon_data (const char *path)
243 GError *error = NULL;
248 icon_file = g_key_file_new ();
249 g_key_file_set_list_separator (icon_file, ',');
250 g_key_file_load_from_file (icon_file, path, G_KEY_FILE_KEEP_TRANSLATIONS, &error);
253 g_error_free (error);
254 g_key_file_free (icon_file);
259 data = g_new0 (IconData, 1);
261 ivalues = g_key_file_get_integer_list (icon_file,
262 "Icon Data", "EmbeddedTextRectangle",
268 data->has_embedded_rect = TRUE;
269 data->x0 = ivalues[0];
270 data->y0 = ivalues[1];
271 data->x1 = ivalues[2];
272 data->y1 = ivalues[3];
278 str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
281 split = g_strsplit (str, "|", -1);
283 data->n_attach_points = g_strv_length (split);
284 data->attach_points = g_new (int, 2 * data->n_attach_points);
287 while (split[i] != NULL && i < data->n_attach_points)
289 split_point = strchr (split[i], ',');
294 data->attach_points[2 * i] = atoi (split[i]);
295 data->attach_points[2 * i + 1] = atoi (split_point);
304 keys = g_key_file_get_keys (icon_file, "Icon Data", &n_keys, &error);
305 data->display_names = g_new0 (gchar *, 2 * n_keys + 1);
306 data->n_display_names = 0;
308 for (i = 0; i < n_keys; i++)
312 if (g_str_has_prefix (keys[i], "DisplayName"))
314 gchar *open, *close = NULL;
316 open = strchr (keys[i], '[');
319 close = strchr (open, ']');
323 lang = g_strndup (open + 1, close - open - 1);
324 name = g_key_file_get_locale_string (icon_file,
325 "Icon Data", "DisplayName",
330 lang = g_strdup ("C");
331 name = g_key_file_get_string (icon_file,
332 "Icon Data", "DisplayName",
336 data->display_names[2 * data->n_display_names] = lang;
337 data->display_names[2 * data->n_display_names + 1] = name;
338 data->n_display_names++;
344 g_key_file_free (icon_file);
346 /* -1 means not computed yet, the real value depends
347 * on string pool state, and will be computed
356 * This function was copied from gtkfilesystemunix.c, it should
357 * probably go to GLib
360 canonicalize_filename (gchar *filename)
363 gboolean last_was_slash = FALSE;
370 if (*p == G_DIR_SEPARATOR)
373 *q++ = G_DIR_SEPARATOR;
375 last_was_slash = TRUE;
379 if (last_was_slash && *p == '.')
381 if (*(p + 1) == G_DIR_SEPARATOR ||
384 if (*(p + 1) == '\0')
389 else if (*(p + 1) == '.' &&
390 (*(p + 2) == G_DIR_SEPARATOR ||
393 if (q > filename + 1)
396 while (q > filename + 1 &&
397 *(q - 1) != G_DIR_SEPARATOR)
401 if (*(p + 2) == '\0')
409 last_was_slash = FALSE;
415 last_was_slash = FALSE;
422 if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
429 follow_links (const gchar *path)
435 path2 = g_strdup (path);
436 while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
438 target = g_file_read_link (path2, NULL);
442 if (g_path_is_absolute (target))
446 d = g_path_get_dirname (path2);
447 s = g_build_filename (d, target, NULL);
458 if (strcmp (path, path2) == 0)
468 maybe_cache_image_data (Image *image,
471 if (!index_only && !image->image_data &&
472 (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
478 idata = g_hash_table_lookup (image_data_hash, path);
479 path2 = follow_links (path);
485 canonicalize_filename (path2);
487 idata2 = g_hash_table_lookup (image_data_hash, path2);
489 if (idata && idata2 && idata != idata2)
490 g_error ("different idatas found for symlinked '%s' and '%s'\n",
493 if (idata && !idata2)
494 g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
496 if (!idata && idata2)
498 g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
505 idata = g_new0 (ImageData, 1);
506 g_hash_table_insert (image_data_hash, g_strdup (path), idata);
508 g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
511 if (!idata->has_pixdata)
513 pixbuf = gdk_pixbuf_new_from_file (path, NULL);
517 gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
518 idata->size = idata->pixdata.length + 8;
519 idata->has_pixdata = TRUE;
523 image->image_data = idata;
530 maybe_cache_icon_data (Image *image,
533 if (g_str_has_suffix (path, ".icon"))
535 IconData *idata = NULL;
538 idata = g_hash_table_lookup (icon_data_hash, path);
539 path2 = follow_links (path);
545 canonicalize_filename (path2);
547 idata2 = g_hash_table_lookup (icon_data_hash, path2);
549 if (idata && idata2 && idata != idata2)
550 g_error ("different idatas found for symlinked '%s' and '%s'\n",
553 if (idata && !idata2)
554 g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
556 if (!idata && idata2)
558 g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
565 idata = load_icon_data (path);
566 g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
568 g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
571 image->icon_data = idata;
578 * Finds all dir separators and replaces them with '/'.
579 * This makes sure that only /-separated paths are written in cache files,
580 * maintaining compatibility with theme index files that use slashes as
581 * directory separators on all platforms.
584 replace_backslashes_with_slashes (gchar *path)
589 for (i = 0; path[i]; i++)
590 if (G_IS_DIR_SEPARATOR (path[i]))
595 scan_directory (const gchar *base_path,
601 GHashTable *dir_hash;
605 gboolean dir_added = FALSE;
606 guint dir_index = 0xffff;
608 dir_path = g_build_path ("/", base_path, subdir, NULL);
610 /* FIXME: Use the gerror */
611 dir = g_dir_open (dir_path, 0, NULL);
616 dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
618 while ((name = g_dir_read_name (dir)))
624 gchar *basename, *dot;
626 path = g_build_filename (dir_path, name, NULL);
628 retval = g_file_test (path, G_FILE_TEST_IS_DIR);
634 subsubdir = g_build_path ("/", subdir, name, NULL);
636 subsubdir = g_strdup (name);
637 directories = scan_directory (base_path, subsubdir, files,
638 directories, depth + 1);
644 /* ignore images in the toplevel directory */
648 retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
651 if (g_str_has_suffix (name, ".png"))
652 flags |= HAS_SUFFIX_PNG;
653 else if (g_str_has_suffix (name, ".svg"))
654 flags |= HAS_SUFFIX_SVG;
655 else if (g_str_has_suffix (name, ".xpm"))
656 flags |= HAS_SUFFIX_XPM;
657 else if (g_str_has_suffix (name, ".icon"))
658 flags |= HAS_ICON_FILE;
663 basename = g_strdup (name);
664 dot = strrchr (basename, '.');
667 image = g_hash_table_lookup (dir_hash, basename);
675 dir_index = g_list_length (directories);
676 directories = g_list_append (directories, g_strdup (subdir));
682 image = g_new0 (Image, 1);
683 image->dir_index = dir_index;
684 g_hash_table_insert (dir_hash, g_strdup (basename), image);
687 image->flags |= flags;
689 maybe_cache_image_data (image, path);
690 maybe_cache_icon_data (image, path);
700 /* Move dir into the big file hash */
701 g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
703 g_hash_table_destroy (dir_hash);
708 typedef struct _HashNode HashNode;
719 icon_name_hash (gconstpointer key)
721 const signed char *p = key;
725 for (p += 1; *p != '\0'; p++)
726 h = (h << 5) - h + *p;
737 convert_to_hash (gpointer key, gpointer value, gpointer user_data)
739 HashContext *context = user_data;
743 hash = icon_name_hash (key) % context->size;
745 node = g_new0 (HashNode, 1);
748 node->image_list = value;
750 if (context->nodes[hash] != NULL)
751 node->next = context->nodes[hash];
753 context->nodes[hash] = node;
758 static GHashTable *string_pool = NULL;
761 find_string (const gchar *n)
763 return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
767 add_string (const gchar *n, int offset)
769 g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
773 write_string (FILE *cache, const gchar *n)
778 l = ALIGN_VALUE (strlen (n) + 1, 4);
783 i = fwrite (s, l, 1, cache);
792 write_card16 (FILE *cache, guint16 n)
796 n = GUINT16_TO_BE (n);
798 i = fwrite ((char *)&n, 2, 1, cache);
804 write_card32 (FILE *cache, guint32 n)
808 n = GUINT32_TO_BE (n);
810 i = fwrite ((char *)&n, 4, 1, cache);
817 write_image_data (FILE *cache, ImageData *image_data, int offset)
822 GdkPixdata *pixdata = &image_data->pixdata;
824 /* Type 0 is GdkPixdata */
825 if (!write_card32 (cache, 0))
828 s = gdk_pixdata_serialize (pixdata, &len);
830 if (!write_card32 (cache, len))
836 i = fwrite (s, len, 1, cache);
844 write_icon_data (FILE *cache, IconData *icon_data, int offset)
846 int ofs = offset + 12;
850 if (icon_data->has_embedded_rect)
852 if (!write_card32 (cache, ofs))
859 if (!write_card32 (cache, 0))
863 if (icon_data->n_attach_points > 0)
865 if (!write_card32 (cache, ofs))
868 ofs += 4 + 4 * icon_data->n_attach_points;
872 if (!write_card32 (cache, 0))
876 if (icon_data->n_display_names > 0)
878 if (!write_card32 (cache, ofs))
883 if (!write_card32 (cache, 0))
887 if (icon_data->has_embedded_rect)
889 if (!write_card16 (cache, icon_data->x0) ||
890 !write_card16 (cache, icon_data->y0) ||
891 !write_card16 (cache, icon_data->x1) ||
892 !write_card16 (cache, icon_data->y1))
896 if (icon_data->n_attach_points > 0)
898 if (!write_card32 (cache, icon_data->n_attach_points))
901 for (j = 0; j < 2 * icon_data->n_attach_points; j++)
903 if (!write_card16 (cache, icon_data->attach_points[j]))
908 if (icon_data->n_display_names > 0)
910 if (!write_card32 (cache, icon_data->n_display_names))
913 ofs += 4 + 8 * icon_data->n_display_names;
916 for (j = 0; j < 2 * icon_data->n_display_names; j++)
918 tmp2 = find_string (icon_data->display_names[j]);
919 if (tmp2 == 0 || tmp2 == -1)
922 tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
923 /* We're playing a little game with negative
924 * offsets here to handle duplicate strings in
927 add_string (icon_data->display_names[j], -tmp2);
934 if (!write_card32 (cache, tmp2))
939 g_assert (ofs == ftell (cache));
940 for (j = 0; j < 2 * icon_data->n_display_names; j++)
942 tmp2 = find_string (icon_data->display_names[j]);
943 g_assert (tmp2 != 0 && tmp2 != -1);
947 g_assert (tmp2 == ftell (cache));
948 add_string (icon_data->display_names[j], tmp2);
949 if (!write_string (cache, icon_data->display_names[j]))
959 write_header (FILE *cache, guint32 dir_list_offset)
961 return (write_card16 (cache, MAJOR_VERSION) &&
962 write_card16 (cache, MINOR_VERSION) &&
963 write_card32 (cache, HASH_OFFSET) &&
964 write_card32 (cache, dir_list_offset));
968 get_image_meta_data_size (Image *image)
972 /* The complication with storing the size in both
973 * IconData and Image is necessary since we attribute
974 * the size of the IconData only to the first Image
975 * using it (at which time it is written out in the
976 * cache). Later Images just refer to the written out
977 * IconData via the offset.
979 if (image->icon_data_size == 0)
981 if (image->icon_data && image->icon_data->size < 0)
983 IconData *data = image->icon_data;
987 if (data->has_embedded_rect ||
988 data->n_attach_points > 0 ||
989 data->n_display_names > 0)
992 if (data->has_embedded_rect)
995 if (data->n_attach_points > 0)
996 data->size += 4 + data->n_attach_points * 4;
998 if (data->n_display_names > 0)
1000 data->size += 4 + 8 * data->n_display_names;
1002 for (i = 0; data->display_names[i]; i++)
1005 if ((poolv = find_string (data->display_names[i])) == 0)
1007 data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
1008 /* Adding the string to the pool with -1
1009 * to indicate that it hasn't been written out
1010 * to the cache yet. We still need it in the
1011 * pool in case the same string occurs twice
1012 * during a get_single_node_size() calculation.
1014 add_string (data->display_names[i], -1);
1019 image->icon_data_size = data->size;
1024 g_assert (image->icon_data_size % 4 == 0);
1026 return image->icon_data_size;
1030 get_image_pixel_data_size (Image *image)
1032 /* The complication with storing the size in both
1033 * ImageData and Image is necessary since we attribute
1034 * the size of the ImageData only to the first Image
1035 * using it (at which time it is written out in the
1036 * cache). Later Images just refer to the written out
1037 * ImageData via the offset.
1039 if (image->pixel_data_size == 0)
1041 if (image->image_data &&
1042 image->image_data->has_pixdata)
1044 image->pixel_data_size = image->image_data->size;
1045 image->image_data->size = 0;
1049 g_assert (image->pixel_data_size % 4 == 0);
1051 return image->pixel_data_size;
1055 get_image_data_size (Image *image)
1061 len += get_image_pixel_data_size (image);
1062 len += get_image_meta_data_size (image);
1064 /* Even if len is zero, we need to reserve space to
1065 * write the ImageData, unless this is an .svg without
1066 * .icon, in which case both image_data and icon_data
1069 if (len > 0 || image->image_data || image->icon_data)
1076 get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
1084 if (find_string (node->name) == 0)
1086 *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
1087 add_string (node->name, -1);
1091 *node_size += 4 + g_list_length (node->image_list) * 8;
1094 *image_data_size = 0;
1095 for (list = node->image_list; list; list = list->next)
1097 Image *image = list->data;
1099 *image_data_size += get_image_data_size (image);
1104 write_bucket (FILE *cache, HashNode *node, int *offset)
1106 while (node != NULL)
1108 int node_size, image_data_size;
1109 int next_offset, image_data_offset;
1113 int image_list_offset;
1117 g_assert (*offset == ftell (cache));
1119 node->offset = *offset;
1121 get_single_node_size (node, &node_size, &image_data_size);
1122 g_assert (node_size % 4 == 0);
1123 g_assert (image_data_size % 4 == 0);
1124 image_data_offset = *offset + node_size;
1125 next_offset = *offset + node_size + image_data_size;
1127 if (node->next != NULL)
1129 if (!write_card32 (cache, next_offset))
1134 if (!write_card32 (cache, 0xffffffff))
1139 name_offset = find_string (node->name);
1140 if (name_offset <= 0)
1142 name_offset = *offset + 12;
1143 name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
1144 add_string (node->name, name_offset);
1146 if (!write_card32 (cache, name_offset))
1149 image_list_offset = *offset + 12 + name_size;
1150 if (!write_card32 (cache, image_list_offset))
1156 if (!write_string (cache, node->name))
1161 len = g_list_length (node->image_list);
1162 if (!write_card32 (cache, len))
1165 list = node->image_list;
1166 data_offset = image_data_offset;
1167 for (i = 0; i < len; i++)
1169 Image *image = list->data;
1170 int image_data_size = get_image_data_size (image);
1172 /* Directory index */
1173 if (!write_card16 (cache, image->dir_index))
1177 if (!write_card16 (cache, image->flags))
1180 /* Image data offset */
1181 if (image_data_size > 0)
1183 if (!write_card32 (cache, data_offset))
1185 data_offset += image_data_size;
1189 if (!write_card32 (cache, 0))
1196 /* Now write the image data */
1197 list = node->image_list;
1198 for (i = 0; i < len; i++, list = list->next)
1200 Image *image = list->data;
1201 int pixel_data_size = get_image_pixel_data_size (image);
1202 int meta_data_size = get_image_meta_data_size (image);
1204 if (get_image_data_size (image) == 0)
1208 if (pixel_data_size > 0)
1210 image->image_data->offset = image_data_offset + 8;
1211 if (!write_card32 (cache, image->image_data->offset))
1216 if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
1220 if (meta_data_size > 0)
1222 image->icon_data->offset = image_data_offset + pixel_data_size + 8;
1223 if (!write_card32 (cache, image->icon_data->offset))
1228 if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
1232 if (pixel_data_size > 0)
1234 if (!write_image_data (cache, image->image_data, image->image_data->offset))
1238 if (meta_data_size > 0)
1240 if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
1244 image_data_offset += pixel_data_size + meta_data_size + 8;
1247 *offset = next_offset;
1255 write_hash_table (FILE *cache, HashContext *context, int *new_offset)
1257 int offset = HASH_OFFSET;
1261 if (!(write_card32 (cache, context->size)))
1265 node_offset = offset + context->size * 4;
1266 /* Just write zeros here, we will rewrite this later */
1267 for (i = 0; i < context->size; i++)
1269 if (!write_card32 (cache, 0))
1273 /* Now write the buckets */
1274 for (i = 0; i < context->size; i++)
1276 if (!context->nodes[i])
1279 g_assert (node_offset % 4 == 0);
1280 if (!write_bucket (cache, context->nodes[i], &node_offset))
1284 *new_offset = node_offset;
1286 /* Now write out the bucket offsets */
1288 fseek (cache, offset, SEEK_SET);
1290 for (i = 0; i < context->size; i++)
1292 if (context->nodes[i] != NULL)
1293 node_offset = context->nodes[i]->offset;
1295 node_offset = 0xffffffff;
1296 if (!write_card32 (cache, node_offset))
1300 fseek (cache, 0, SEEK_END);
1306 write_dir_index (FILE *cache, int offset, GList *directories)
1313 n_dirs = g_list_length (directories);
1315 if (!write_card32 (cache, n_dirs))
1318 offset += 4 + n_dirs * 4;
1321 for (d = directories; d; d = d->next)
1325 tmp2 = find_string (dir);
1327 if (tmp2 == 0 || tmp2 == -1)
1330 tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
1331 /* We're playing a little game with negative
1332 * offsets here to handle duplicate strings in
1333 * the array, even though that should not
1334 * really happen for the directory index.
1336 add_string (dir, -tmp2);
1343 if (!write_card32 (cache, tmp2))
1347 g_assert (offset == ftell (cache));
1348 for (d = directories; d; d = d->next)
1352 tmp2 = find_string (dir);
1353 g_assert (tmp2 != 0 && tmp2 != -1);
1357 g_assert (tmp2 == ftell (cache));
1358 add_string (dir, tmp2);
1359 if (!write_string (cache, dir))
1368 write_file (FILE *cache, GHashTable *files, GList *directories)
1370 HashContext context;
1373 /* Convert the hash table into something looking a bit more
1374 * like what we want to write to disk.
1376 context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
1377 context.nodes = g_new0 (HashNode *, context.size);
1379 g_hash_table_foreach_remove (files, convert_to_hash, &context);
1381 /* Now write the file */
1382 /* We write 0 as the directory list offset and go
1383 * back and change it later */
1384 if (!write_header (cache, 0))
1386 g_printerr (_("Failed to write header\n"));
1390 if (!write_hash_table (cache, &context, &new_offset))
1392 g_printerr (_("Failed to write hash table\n"));
1396 if (!write_dir_index (cache, new_offset, directories))
1398 g_printerr (_("Failed to write folder index\n"));
1404 if (!write_header (cache, new_offset))
1406 g_printerr (_("Failed to rewrite header\n"));
1414 validate_file (const gchar *file)
1419 map = g_mapped_file_new (file, FALSE, NULL);
1423 info.cache = g_mapped_file_get_contents (map);
1424 info.cache_size = g_mapped_file_get_length (map);
1425 info.n_directories = 0;
1426 info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
1428 if (!_gtk_icon_cache_validate (&info))
1430 g_mapped_file_unref (map);
1434 g_mapped_file_unref (map);
1441 * @f: A FILE* stream, must have underlying fd
1443 * Unix defaults for data preservation after system crash
1444 * are unspecified, and many systems will eat your data
1445 * in this situation unless you explicitly fsync().
1447 * Returns: %TRUE on success, %FALSE on failure, and will set errno()
1450 safe_fclose (FILE *f)
1452 int fd = fileno (f);
1454 if (fflush (f) == EOF)
1460 if (fclose (f) == EOF)
1466 build_cache (const gchar *path)
1468 gchar *cache_path, *tmp_cache_path;
1470 gchar *bak_cache_path = NULL;
1474 GStatBuf path_stat, cache_stat;
1475 struct utimbuf utime_buf;
1476 GList *directories = NULL;
1478 int retry_count = 0;
1480 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1482 int mode = _S_IWRITE | _S_IREAD;
1488 tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
1489 cache_path = g_build_filename (path, CACHE_NAME, NULL);
1492 if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
1494 if (force_update && retry_count == 0)
1497 g_remove (tmp_cache_path);
1500 g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
1504 cache = fdopen (fd, "wb");
1508 g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1512 files = g_hash_table_new (g_str_hash, g_str_equal);
1513 image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
1514 icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
1515 string_pool = g_hash_table_new (g_str_hash, g_str_equal);
1517 directories = scan_directory (path, NULL, files, NULL, 0);
1519 if (g_hash_table_size (files) == 0)
1521 /* Empty table, just close and remove the file */
1524 g_unlink (tmp_cache_path);
1525 g_unlink (cache_path);
1529 /* FIXME: Handle failure */
1530 if (!write_file (cache, files, directories))
1532 g_unlink (tmp_cache_path);
1536 if (!safe_fclose (cache))
1538 g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1539 g_unlink (tmp_cache_path);
1544 g_list_free_full (directories, g_free);
1546 if (!validate_file (tmp_cache_path))
1548 g_printerr (_("The generated cache was invalid.\n"));
1549 /*g_unlink (tmp_cache_path);*/
1554 if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
1556 bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
1557 g_unlink (bak_cache_path);
1558 if (g_rename (cache_path, bak_cache_path) == -1)
1562 g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
1563 cache_path, bak_cache_path,
1566 g_unlink (cache_path);
1567 bak_cache_path = NULL;
1572 if (g_rename (tmp_cache_path, cache_path) == -1)
1576 g_printerr (_("Could not rename %s to %s: %s\n"),
1577 tmp_cache_path, cache_path,
1578 g_strerror (errsv));
1579 g_unlink (tmp_cache_path);
1581 if (bak_cache_path != NULL)
1582 if (g_rename (bak_cache_path, cache_path) == -1)
1586 g_printerr (_("Could not rename %s back to %s: %s.\n"),
1587 bak_cache_path, cache_path,
1588 g_strerror (errsv));
1594 if (bak_cache_path != NULL)
1595 g_unlink (bak_cache_path);
1599 /* FIXME: What do do if an error occurs here? */
1600 if (g_stat (path, &path_stat) < 0 ||
1601 g_stat (cache_path, &cache_stat))
1604 utime_buf.actime = path_stat.st_atime;
1605 utime_buf.modtime = cache_stat.st_mtime;
1606 #if GLIB_CHECK_VERSION (2, 17, 1)
1607 g_utime (path, &utime_buf);
1609 utime (path, &utime_buf);
1613 g_printerr (_("Cache file created successfully.\n"));
1617 write_csource (const gchar *path)
1624 cache_path = g_build_filename (path, CACHE_NAME, NULL);
1625 if (!g_file_get_contents (cache_path, &data, &len, NULL))
1628 g_printf ("#ifdef __SUNPRO_C\n");
1629 g_printf ("#pragma align 4 (%s)\n", var_name);
1630 g_printf ("#endif\n");
1632 g_printf ("#ifdef __GNUC__\n");
1633 g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
1634 g_printf ("#else\n");
1635 g_printf ("static const guint8 %s[] = \n", var_name);
1636 g_printf ("#endif\n");
1639 for (i = 0; i < len - 1; i++)
1643 g_printf ("0x%02x, ", (guint8)data[i]);
1648 g_printf ("0x%02x\n};\n", (guint8)data[i]);
1651 static GOptionEntry args[] = {
1652 { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
1653 { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don't check for the existence of index.theme"), NULL },
1654 { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL },
1655 { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
1656 { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
1657 { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
1662 printerr_handler (const gchar *string)
1664 const gchar *charset;
1666 fputs (g_get_prgname (), stderr);
1667 fputs (": ", stderr);
1668 if (g_get_charset (&charset))
1669 fputs (string, stderr); /* charset is UTF-8 already */
1674 result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
1678 fputs (result, stderr);
1688 main (int argc, char **argv)
1691 GOptionContext *context;
1696 g_set_printerr_handler (printerr_handler);
1698 setlocale (LC_ALL, "");
1701 bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
1702 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1703 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1707 context = g_option_context_new ("ICONPATH");
1708 g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
1710 g_option_context_parse (context, &argc, &argv, NULL);
1714 path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
1719 gchar *file = g_build_filename (path, CACHE_NAME, NULL);
1721 if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
1724 g_printerr (_("File not found: %s\n"), file);
1727 if (!validate_file (file))
1730 g_printerr (_("Not a valid icon cache: %s\n"), file);
1739 if (!ignore_theme_index && !has_theme_index (path))
1743 g_printerr (_("No theme index file.\n"));
1747 g_printerr (_("No theme index file in '%s'.\n"
1748 "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
1754 if (!force_update && is_cache_up_to_date (path))
1757 replace_backslashes_with_slashes (path);
1760 if (strcmp (var_name, "-") != 0)
1761 write_csource (path);