]> Pileus Git - ~andy/gtk/blob - gtk/updateiconcache.c
immodule: Fix context ID lookup on module with multiple contexts
[~andy/gtk] / gtk / updateiconcache.c
1 /* updateiconcache.c
2  * Copyright (C) 2004  Anders Carlsson <andersca@gnome.org>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include "config.h"
19
20 #include <locale.h>
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #ifdef HAVE_UNISTD_H
28 #include <unistd.h>
29 #endif
30 #include <errno.h>
31 #ifdef _MSC_VER
32 #include <io.h>
33 #include <sys/utime.h>
34 #else
35 #include <utime.h>
36 #endif
37
38 #include <glib.h>
39 #include <glib/gstdio.h>
40 #include <gdk-pixbuf/gdk-pixdata.h>
41 #include <glib/gi18n.h>
42 #include "gtkiconcachevalidator.h"
43
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 = "-";
50
51 /* Quite ugly - if we just add the c file to the
52  * list of sources in Makefile.am, libtool complains.
53  */
54 #include "gtkiconcachevalidator.c"
55
56 #define CACHE_NAME "icon-theme.cache"
57
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)
62
63 #define MAJOR_VERSION 1
64 #define MINOR_VERSION 0
65 #define HASH_OFFSET 12
66
67 #define ALIGN_VALUE(this, boundary) \
68   (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
69
70 #ifdef HAVE_FTW_H
71
72 #include <ftw.h>
73
74 static GStatBuf cache_stat;
75 static gboolean cache_up_to_date;
76
77 static int check_dir_mtime (const char        *dir, 
78                             const GStatBuf    *sb,
79                             int                tf)
80 {
81   if (tf != FTW_NS && sb->st_mtime > cache_stat.st_mtime)
82     {
83       cache_up_to_date = FALSE;
84       /* stop tree walk */
85       return 1;
86     }
87
88   return 0;
89 }
90
91 static gboolean
92 is_cache_up_to_date (const gchar *path)
93 {
94   gchar *cache_path;
95   gint retval;
96
97   cache_path = g_build_filename (path, CACHE_NAME, NULL);
98   retval = g_stat (cache_path, &cache_stat);
99   g_free (cache_path);
100   
101   if (retval < 0)
102     {
103       /* Cache file not found */
104       return FALSE;
105     }
106
107   cache_up_to_date = TRUE;
108
109   ftw (path, check_dir_mtime, 20);
110
111   return cache_up_to_date;
112 }
113
114 #else  /* !HAVE_FTW_H */
115
116 gboolean
117 is_cache_up_to_date (const gchar *path)
118 {
119   GStatBuf path_stat, cache_stat;
120   gchar *cache_path;
121   int retval; 
122   
123   retval = g_stat (path, &path_stat);
124
125   if (retval < 0) 
126     {
127       /* We can't stat the path,
128        * assume we have a updated cache */
129       return TRUE;
130     }
131
132   cache_path = g_build_filename (path, CACHE_NAME, NULL);
133   retval = g_stat (cache_path, &cache_stat);
134   g_free (cache_path);
135   
136   if (retval < 0)
137     {
138       /* Cache file not found */
139       return FALSE;
140     }
141
142   /* Check mtime */
143   return cache_stat.st_mtime >= path_stat.st_mtime;
144 }
145
146 #endif  /* !HAVE_FTW_H */
147
148 static gboolean
149 has_theme_index (const gchar *path)
150 {
151   gboolean result;
152   gchar *index_path;
153
154   index_path = g_build_filename (path, "index.theme", NULL);
155
156   result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
157   
158   g_free (index_path);
159
160   return result;
161 }
162
163
164 typedef struct 
165 {
166   GdkPixdata pixdata;
167   gboolean has_pixdata;
168   guint32 offset;
169   guint size;
170 } ImageData;
171
172 typedef struct 
173 {
174   int has_embedded_rect;
175   int x0, y0, x1, y1;
176   
177   int n_attach_points;
178   int *attach_points;
179   
180   int n_display_names;
181   char **display_names;
182
183   guint32 offset;
184   gint size;
185 } IconData;
186
187 static GHashTable *image_data_hash = NULL;
188 static GHashTable *icon_data_hash = NULL;
189
190 typedef struct
191 {
192   int flags;
193   int dir_index;
194
195   ImageData *image_data;
196   guint pixel_data_size;
197
198   IconData *icon_data;
199   guint icon_data_size;
200 } Image;
201
202
203 static gboolean
204 foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
205 {
206   Image *image = (Image *)value;
207   GHashTable *files = user_data;
208   GList *list;
209   gboolean free_key = FALSE;  
210
211   if (image->flags == HAS_ICON_FILE)
212     {
213       /* just a .icon file, throw away */
214       g_free (key);
215       g_free (image);
216
217       return TRUE;
218     }
219
220   list = g_hash_table_lookup (files, key);
221   if (list)
222     free_key = TRUE;
223   
224   list = g_list_prepend (list, value);
225   g_hash_table_insert (files, key, list);
226   
227   if (free_key)
228     g_free (key);
229   
230   return TRUE;
231 }
232
233 static IconData *
234 load_icon_data (const char *path)
235 {
236   GKeyFile *icon_file;
237   char **split;
238   gsize length;
239   char *str;
240   char *split_point;
241   int i;
242   gint *ivalues;
243   GError *error = NULL;
244   gchar **keys;
245   gsize n_keys;
246   IconData *data;
247   
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);
251   if (error)
252     {
253       g_error_free (error);
254       g_key_file_free (icon_file);
255
256       return NULL;
257     }
258
259   data = g_new0 (IconData, 1);
260
261   ivalues = g_key_file_get_integer_list (icon_file, 
262                                          "Icon Data", "EmbeddedTextRectangle",
263                                          &length, NULL);
264   if (ivalues)
265     {
266       if (length == 4)
267         {
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];
273         }
274       
275       g_free (ivalues);
276     }
277       
278   str = g_key_file_get_string (icon_file, "Icon Data", "AttachPoints", NULL);
279   if (str)
280     {
281       split = g_strsplit (str, "|", -1);
282       
283       data->n_attach_points = g_strv_length (split);
284       data->attach_points = g_new (int, 2 * data->n_attach_points);
285
286       i = 0;
287       while (split[i] != NULL && i < data->n_attach_points)
288         {
289           split_point = strchr (split[i], ',');
290           if (split_point)
291             {
292               *split_point = 0;
293               split_point++;
294               data->attach_points[2 * i] = atoi (split[i]);
295               data->attach_points[2 * i + 1] = atoi (split_point);
296             }
297           i++;
298         }
299       
300       g_strfreev (split);
301       g_free (str);
302     }
303       
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;
307   
308   for (i = 0; i < n_keys; i++)
309     {
310       gchar *lang, *name;
311       
312       if (g_str_has_prefix (keys[i], "DisplayName"))
313         {
314           gchar *open, *close = NULL;
315           
316           open = strchr (keys[i], '[');
317
318           if (open)
319             close = strchr (open, ']');
320
321           if (open && close)
322             {
323               lang = g_strndup (open + 1, close - open - 1);
324               name = g_key_file_get_locale_string (icon_file, 
325                                                    "Icon Data", "DisplayName",
326                                                    lang, NULL);
327             }
328           else
329             {
330               lang = g_strdup ("C");
331               name = g_key_file_get_string (icon_file, 
332                                             "Icon Data", "DisplayName",
333                                             NULL);
334             }
335           
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++;
339         }
340     }
341
342   g_strfreev (keys);
343   
344   g_key_file_free (icon_file);
345
346   /* -1 means not computed yet, the real value depends
347    * on string pool state, and will be computed 
348    * later 
349    */
350   data->size = -1;
351
352   return data;
353 }
354
355 /*
356  * This function was copied from gtkfilesystemunix.c, it should
357  * probably go to GLib
358  */
359 static void
360 canonicalize_filename (gchar *filename)
361 {
362   gchar *p, *q;
363   gboolean last_was_slash = FALSE;
364
365   p = filename;
366   q = filename;
367
368   while (*p)
369     {
370       if (*p == G_DIR_SEPARATOR)
371         {
372           if (!last_was_slash)
373             *q++ = G_DIR_SEPARATOR;
374
375           last_was_slash = TRUE;
376         }
377       else
378         {
379           if (last_was_slash && *p == '.')
380             {
381               if (*(p + 1) == G_DIR_SEPARATOR ||
382                   *(p + 1) == '\0')
383                 {
384                   if (*(p + 1) == '\0')
385                     break;
386
387                   p += 1;
388                 }
389               else if (*(p + 1) == '.' &&
390                        (*(p + 2) == G_DIR_SEPARATOR ||
391                         *(p + 2) == '\0'))
392                 {
393                   if (q > filename + 1)
394                     {
395                       q--;
396                       while (q > filename + 1 &&
397                              *(q - 1) != G_DIR_SEPARATOR)
398                         q--;
399                     }
400
401                   if (*(p + 2) == '\0')
402                     break;
403
404                   p += 2;
405                 }
406               else
407                 {
408                   *q++ = *p;
409                   last_was_slash = FALSE;
410                 }
411             }
412           else
413             {
414               *q++ = *p;
415               last_was_slash = FALSE;
416             }
417         }
418
419       p++;
420     }
421
422   if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
423     q--;
424
425   *q = '\0';
426 }
427
428 static gchar *
429 follow_links (const gchar *path)
430 {
431   gchar *target;
432   gchar *d, *s;
433   gchar *path2 = NULL;
434
435   path2 = g_strdup (path);
436   while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
437     {
438       target = g_file_read_link (path2, NULL);
439       
440       if (target)
441         {
442           if (g_path_is_absolute (target))
443             path2 = target;
444           else
445             {
446               d = g_path_get_dirname (path2);
447               s = g_build_filename (d, target, NULL);
448               g_free (d);
449               g_free (target);
450               g_free (path2);
451               path2 = s;
452             }
453         }
454       else
455         break;
456     }
457
458   if (strcmp (path, path2) == 0)
459     {
460       g_free (path2);
461       path2 = NULL;
462     }
463
464   return path2;
465 }
466
467 static void
468 maybe_cache_image_data (Image       *image, 
469                         const gchar *path)
470 {
471   if (!index_only && !image->image_data && 
472       (g_str_has_suffix (path, ".png") || g_str_has_suffix (path, ".xpm")))
473     {
474       GdkPixbuf *pixbuf;
475       ImageData *idata;
476       gchar *path2;
477
478       idata = g_hash_table_lookup (image_data_hash, path);
479       path2 = follow_links (path);
480
481       if (path2)
482         {
483           ImageData *idata2;
484
485           canonicalize_filename (path2);
486   
487           idata2 = g_hash_table_lookup (image_data_hash, path2);
488
489           if (idata && idata2 && idata != idata2)
490             g_error ("different idatas found for symlinked '%s' and '%s'\n",
491                      path, path2);
492
493           if (idata && !idata2)
494             g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
495
496           if (!idata && idata2)
497             {
498               g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
499               idata = idata2;
500             }
501         }
502       
503       if (!idata)
504         {
505           idata = g_new0 (ImageData, 1);
506           g_hash_table_insert (image_data_hash, g_strdup (path), idata);
507           if (path2)
508             g_hash_table_insert (image_data_hash, g_strdup (path2), idata);  
509         }
510
511       if (!idata->has_pixdata)
512         {
513           pixbuf = gdk_pixbuf_new_from_file (path, NULL);
514           
515           if (pixbuf) 
516             {
517               gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
518               idata->size = idata->pixdata.length + 8;
519               idata->has_pixdata = TRUE;
520             }
521         }
522
523       image->image_data = idata;
524
525       g_free (path2);
526     }
527 }
528
529 static void
530 maybe_cache_icon_data (Image       *image,
531                        const gchar *path)
532 {
533   if (g_str_has_suffix (path, ".icon"))
534     {
535       IconData *idata = NULL;
536       gchar *path2 = NULL;
537
538       idata = g_hash_table_lookup (icon_data_hash, path);
539       path2 = follow_links (path);
540
541       if (path2)
542         {
543           IconData *idata2;
544
545           canonicalize_filename (path2);
546   
547           idata2 = g_hash_table_lookup (icon_data_hash, path2);
548
549           if (idata && idata2 && idata != idata2)
550             g_error ("different idatas found for symlinked '%s' and '%s'\n",
551                      path, path2);
552
553           if (idata && !idata2)
554             g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);
555
556           if (!idata && idata2)
557             {
558               g_hash_table_insert (icon_data_hash, g_strdup (path), idata2);
559               idata = idata2;
560             }
561         }
562       
563       if (!idata)
564         {
565           idata = load_icon_data (path);
566           g_hash_table_insert (icon_data_hash, g_strdup (path), idata);
567           if (path2)
568             g_hash_table_insert (icon_data_hash, g_strdup (path2), idata);  
569         }
570
571       image->icon_data = idata;
572
573       g_free (path2);
574     }
575 }
576
577 static GList *
578 scan_directory (const gchar *base_path, 
579                 const gchar *subdir, 
580                 GHashTable  *files, 
581                 GList       *directories,
582                 gint         depth)
583 {
584   GHashTable *dir_hash;
585   GDir *dir;
586   const gchar *name;
587   gchar *dir_path;
588   gboolean dir_added = FALSE;
589   guint dir_index = 0xffff;
590   
591   dir_path = g_build_filename (base_path, subdir, NULL);
592
593   /* FIXME: Use the gerror */
594   dir = g_dir_open (dir_path, 0, NULL);
595   
596   if (!dir)
597     return directories;
598   
599   dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
600
601   while ((name = g_dir_read_name (dir)))
602     {
603       gchar *path;
604       gboolean retval;
605       int flags = 0;
606       Image *image;
607       gchar *basename, *dot;
608
609       path = g_build_filename (dir_path, name, NULL);
610       retval = g_file_test (path, G_FILE_TEST_IS_DIR);
611       if (retval)
612         {
613           gchar *subsubdir;
614
615           if (subdir)
616             subsubdir = g_build_filename (subdir, name, NULL);
617           else
618             subsubdir = g_strdup (name);
619           directories = scan_directory (base_path, subsubdir, files, 
620                                         directories, depth + 1);
621           g_free (subsubdir);
622
623           continue;
624         }
625
626       /* ignore images in the toplevel directory */
627       if (subdir == NULL)
628         continue;
629
630       retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
631       if (retval)
632         {
633           if (g_str_has_suffix (name, ".png"))
634             flags |= HAS_SUFFIX_PNG;
635           else if (g_str_has_suffix (name, ".svg"))
636             flags |= HAS_SUFFIX_SVG;
637           else if (g_str_has_suffix (name, ".xpm"))
638             flags |= HAS_SUFFIX_XPM;
639           else if (g_str_has_suffix (name, ".icon"))
640             flags |= HAS_ICON_FILE;
641           
642           if (flags == 0)
643             continue;
644           
645           basename = g_strdup (name);
646           dot = strrchr (basename, '.');
647           *dot = '\0';
648           
649           image = g_hash_table_lookup (dir_hash, basename);
650           if (!image)
651             {
652               if (!dir_added) 
653                 {
654                   dir_added = TRUE;
655                   if (subdir)
656                     {
657                       dir_index = g_list_length (directories);
658                       directories = g_list_append (directories, g_strdup (subdir));
659                     }
660                   else
661                     dir_index = 0xffff;
662                 }
663                 
664               image = g_new0 (Image, 1);
665               image->dir_index = dir_index;
666               g_hash_table_insert (dir_hash, g_strdup (basename), image);
667             }
668
669           image->flags |= flags;
670       
671           maybe_cache_image_data (image, path);
672           maybe_cache_icon_data (image, path);
673        
674           g_free (basename);
675         }
676
677       g_free (path);
678     }
679
680   g_dir_close (dir);
681
682   /* Move dir into the big file hash */
683   g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
684   
685   g_hash_table_destroy (dir_hash);
686
687   return directories;
688 }
689
690 typedef struct _HashNode HashNode;
691
692 struct _HashNode
693 {
694   HashNode *next;
695   gchar *name;
696   GList *image_list;
697   gint offset;
698 };
699
700 static guint
701 icon_name_hash (gconstpointer key)
702 {
703   const signed char *p = key;
704   guint32 h = *p;
705
706   if (h)
707     for (p += 1; *p != '\0'; p++)
708       h = (h << 5) - h + *p;
709
710   return h;
711 }
712
713 typedef struct {
714   gint size;
715   HashNode **nodes;
716 } HashContext;
717
718 static gboolean
719 convert_to_hash (gpointer key, gpointer value, gpointer user_data)
720 {
721   HashContext *context = user_data;
722   guint hash;
723   HashNode *node;
724   
725   hash = icon_name_hash (key) % context->size;
726
727   node = g_new0 (HashNode, 1);
728   node->next = NULL;
729   node->name = key;
730   node->image_list = value;
731
732   if (context->nodes[hash] != NULL)
733     node->next = context->nodes[hash];
734
735   context->nodes[hash] = node;
736   
737   return TRUE;
738 }
739
740 static GHashTable *string_pool = NULL;
741  
742 static int
743 find_string (const gchar *n)
744 {
745   return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
746 }
747
748 static void
749 add_string (const gchar *n, int offset)
750 {
751   g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
752 }
753
754 static gboolean
755 write_string (FILE *cache, const gchar *n)
756 {
757   gchar *s;
758   int i, l;
759   
760   l = ALIGN_VALUE (strlen (n) + 1, 4);
761   
762   s = g_malloc0 (l);
763   strcpy (s, n);
764
765   i = fwrite (s, l, 1, cache);
766
767   g_free (s);
768
769   return i == 1;
770   
771 }
772
773 static gboolean
774 write_card16 (FILE *cache, guint16 n)
775 {
776   int i;
777
778   n = GUINT16_TO_BE (n);
779   
780   i = fwrite ((char *)&n, 2, 1, cache);
781
782   return i == 1;
783 }
784
785 static gboolean
786 write_card32 (FILE *cache, guint32 n)
787 {
788   int i;
789
790   n = GUINT32_TO_BE (n);
791   
792   i = fwrite ((char *)&n, 4, 1, cache);
793
794   return i == 1;
795 }
796
797
798 static gboolean
799 write_image_data (FILE *cache, ImageData *image_data, int offset)
800 {
801   guint8 *s;
802   guint len;
803   gint i;
804   GdkPixdata *pixdata = &image_data->pixdata;
805
806   /* Type 0 is GdkPixdata */
807   if (!write_card32 (cache, 0))
808     return FALSE;
809
810   s = gdk_pixdata_serialize (pixdata, &len);
811
812   if (!write_card32 (cache, len))
813     {
814       g_free (s);
815       return FALSE;
816     }
817
818   i = fwrite (s, len, 1, cache);
819   
820   g_free (s);
821
822   return i == 1;
823 }
824
825 static gboolean
826 write_icon_data (FILE *cache, IconData *icon_data, int offset)
827 {
828   int ofs = offset + 12;
829   int j;
830   int tmp, tmp2;
831
832   if (icon_data->has_embedded_rect)
833     {
834       if (!write_card32 (cache, ofs))
835         return FALSE;
836               
837        ofs += 8;
838     }         
839   else
840     {
841       if (!write_card32 (cache, 0))
842         return FALSE;
843     }
844               
845   if (icon_data->n_attach_points > 0)
846     {
847       if (!write_card32 (cache, ofs))
848         return FALSE;
849
850       ofs += 4 + 4 * icon_data->n_attach_points;
851     }
852   else
853     {
854       if (!write_card32 (cache, 0))
855         return FALSE;
856     }
857
858   if (icon_data->n_display_names > 0)
859     {
860       if (!write_card32 (cache, ofs))
861         return FALSE;
862     }
863   else
864     {
865       if (!write_card32 (cache, 0))
866         return FALSE;
867     }
868
869   if (icon_data->has_embedded_rect)
870     {
871       if (!write_card16 (cache, icon_data->x0) ||
872           !write_card16 (cache, icon_data->y0) ||
873           !write_card16 (cache, icon_data->x1) ||
874           !write_card16 (cache, icon_data->y1))
875         return FALSE;
876     }
877
878   if (icon_data->n_attach_points > 0)
879     {
880       if (!write_card32 (cache, icon_data->n_attach_points))
881         return FALSE;
882                   
883       for (j = 0; j < 2 * icon_data->n_attach_points; j++)
884         {
885           if (!write_card16 (cache, icon_data->attach_points[j]))
886             return FALSE;
887         }                 
888     }
889
890   if (icon_data->n_display_names > 0)
891     {
892       if (!write_card32 (cache, icon_data->n_display_names))
893         return FALSE;
894
895       ofs += 4 + 8 * icon_data->n_display_names;
896
897       tmp = ofs;
898       for (j = 0; j < 2 * icon_data->n_display_names; j++)
899         {
900           tmp2 = find_string (icon_data->display_names[j]);
901           if (tmp2 == 0 || tmp2 == -1)
902             {
903               tmp2 = tmp;
904               tmp += ALIGN_VALUE (strlen (icon_data->display_names[j]) + 1, 4);
905               /* We're playing a little game with negative
906                * offsets here to handle duplicate strings in 
907                * the array.
908                */
909               add_string (icon_data->display_names[j], -tmp2);
910             }
911           else if (tmp2 < 0)
912             {
913               tmp2 = -tmp2;
914             }
915
916           if (!write_card32 (cache, tmp2))
917             return FALSE;
918
919         }
920
921       g_assert (ofs == ftell (cache));
922       for (j = 0; j < 2 * icon_data->n_display_names; j++)
923         {
924           tmp2 = find_string (icon_data->display_names[j]);
925           g_assert (tmp2 != 0 && tmp2 != -1);
926           if (tmp2 < 0)
927             {
928               tmp2 = -tmp2;
929               g_assert (tmp2 == ftell (cache));
930               add_string (icon_data->display_names[j], tmp2);
931               if (!write_string (cache, icon_data->display_names[j]))
932                 return FALSE;
933             }
934         }
935     }        
936
937   return TRUE;
938 }
939
940 static gboolean
941 write_header (FILE *cache, guint32 dir_list_offset)
942 {
943   return (write_card16 (cache, MAJOR_VERSION) &&
944           write_card16 (cache, MINOR_VERSION) &&
945           write_card32 (cache, HASH_OFFSET) &&
946           write_card32 (cache, dir_list_offset));
947 }
948
949 static gint
950 get_image_meta_data_size (Image *image)
951 {
952   gint i;
953
954   /* The complication with storing the size in both
955    * IconData and Image is necessary since we attribute
956    * the size of the IconData only to the first Image
957    * using it (at which time it is written out in the 
958    * cache). Later Images just refer to the written out
959    * IconData via the offset.
960    */
961   if (image->icon_data_size == 0)
962     {
963       if (image->icon_data && image->icon_data->size < 0)
964         {
965           IconData *data = image->icon_data;
966
967           data->size = 0;
968
969           if (data->has_embedded_rect ||
970               data->n_attach_points > 0 ||
971               data->n_display_names > 0)
972             data->size += 12;
973
974           if (data->has_embedded_rect)
975             data->size += 8;
976
977           if (data->n_attach_points > 0)
978             data->size += 4 + data->n_attach_points * 4;
979
980           if (data->n_display_names > 0)
981             {
982               data->size += 4 + 8 * data->n_display_names;
983
984               for (i = 0; data->display_names[i]; i++)
985                 { 
986                   int poolv;
987                   if ((poolv = find_string (data->display_names[i])) == 0)
988                     {
989                       data->size += ALIGN_VALUE (strlen (data->display_names[i]) + 1, 4);
990                       /* Adding the string to the pool with -1
991                        * to indicate that it hasn't been written out
992                        * to the cache yet. We still need it in the
993                        * pool in case the same string occurs twice
994                        * during a get_single_node_size() calculation.
995                        */
996                       add_string (data->display_names[i], -1);
997                     }
998                 }
999            } 
1000
1001           image->icon_data_size = data->size;
1002           data->size = 0;
1003         }
1004     }
1005
1006   g_assert (image->icon_data_size % 4 == 0);
1007
1008   return image->icon_data_size;
1009 }
1010
1011 static gint
1012 get_image_pixel_data_size (Image *image)
1013 {
1014   /* The complication with storing the size in both
1015    * ImageData and Image is necessary since we attribute
1016    * the size of the ImageData only to the first Image
1017    * using it (at which time it is written out in the 
1018    * cache). Later Images just refer to the written out
1019    * ImageData via the offset.
1020    */
1021   if (image->pixel_data_size == 0)
1022     {
1023       if (image->image_data && 
1024           image->image_data->has_pixdata)
1025         {
1026           image->pixel_data_size = image->image_data->size;
1027           image->image_data->size = 0;
1028         }
1029     }
1030
1031   g_assert (image->pixel_data_size % 4 == 0);
1032
1033   return image->pixel_data_size;
1034 }
1035
1036 static gint
1037 get_image_data_size (Image *image)
1038 {
1039   gint len;
1040   
1041   len = 0;
1042
1043   len += get_image_pixel_data_size (image);
1044   len += get_image_meta_data_size (image);
1045
1046   /* Even if len is zero, we need to reserve space to
1047    * write the ImageData, unless this is an .svg without 
1048    * .icon, in which case both image_data and icon_data
1049    * are NULL.
1050    */
1051   if (len > 0 || image->image_data || image->icon_data)
1052     len += 8;
1053
1054   return len;
1055 }
1056
1057 static void
1058 get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
1059 {
1060   GList *list;
1061
1062   /* Node pointers */
1063   *node_size = 12;
1064
1065   /* Name */
1066   if (find_string (node->name) == 0)
1067     {
1068       *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
1069       add_string (node->name, -1);
1070     }
1071
1072   /* Image list */
1073   *node_size += 4 + g_list_length (node->image_list) * 8;
1074  
1075   /* Image data */
1076   *image_data_size = 0;
1077   for (list = node->image_list; list; list = list->next)
1078     {
1079       Image *image = list->data;
1080
1081       *image_data_size += get_image_data_size (image);
1082     }
1083 }
1084
1085 static gboolean
1086 write_bucket (FILE *cache, HashNode *node, int *offset)
1087 {
1088   while (node != NULL)
1089     {
1090       int node_size, image_data_size;
1091       int next_offset, image_data_offset;
1092       int data_offset;
1093       int name_offset;
1094       int name_size;
1095       int image_list_offset;
1096       int i, len;
1097       GList *list;
1098
1099       g_assert (*offset == ftell (cache));
1100
1101       node->offset = *offset;
1102
1103       get_single_node_size (node, &node_size, &image_data_size);
1104       g_assert (node_size % 4 == 0);
1105       g_assert (image_data_size % 4 == 0);
1106       image_data_offset = *offset + node_size;
1107       next_offset = *offset + node_size + image_data_size;
1108       /* Chain offset */
1109       if (node->next != NULL)
1110         {
1111           if (!write_card32 (cache, next_offset))
1112             return FALSE;
1113         }
1114       else
1115         {
1116           if (!write_card32 (cache, 0xffffffff))
1117             return FALSE;
1118         }
1119
1120       name_size = 0;
1121       name_offset = find_string (node->name);
1122       if (name_offset <= 0)
1123         {
1124           name_offset = *offset + 12;
1125           name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
1126           add_string (node->name, name_offset);
1127         }
1128       if (!write_card32 (cache, name_offset))
1129         return FALSE;
1130
1131       image_list_offset = *offset + 12 + name_size;
1132       if (!write_card32 (cache, image_list_offset))
1133         return FALSE;
1134
1135       /* Icon name */
1136       if (name_size > 0)
1137         {
1138           if (!write_string (cache, node->name))
1139             return FALSE;
1140         }
1141
1142       /* Image list */
1143       len = g_list_length (node->image_list);
1144       if (!write_card32 (cache, len))
1145         return FALSE;
1146
1147       list = node->image_list;
1148       data_offset = image_data_offset;
1149       for (i = 0; i < len; i++)
1150         {
1151           Image *image = list->data;
1152           int image_data_size = get_image_data_size (image);
1153
1154           /* Directory index */
1155           if (!write_card16 (cache, image->dir_index))
1156             return FALSE;
1157
1158           /* Flags */
1159           if (!write_card16 (cache, image->flags))
1160             return FALSE;
1161
1162           /* Image data offset */
1163           if (image_data_size > 0)
1164             {
1165               if (!write_card32 (cache, data_offset))
1166                 return FALSE;
1167               data_offset += image_data_size;
1168             }
1169           else
1170             {
1171               if (!write_card32 (cache, 0))
1172                 return FALSE;
1173             }
1174
1175           list = list->next;
1176         }
1177
1178       /* Now write the image data */
1179       list = node->image_list;
1180       for (i = 0; i < len; i++, list = list->next)
1181         {
1182           Image *image = list->data;
1183           int pixel_data_size = get_image_pixel_data_size (image);
1184           int meta_data_size = get_image_meta_data_size (image);
1185
1186           if (get_image_data_size (image) == 0)
1187             continue;
1188
1189           /* Pixel data */
1190           if (pixel_data_size > 0)
1191             {
1192               image->image_data->offset = image_data_offset + 8;
1193               if (!write_card32 (cache, image->image_data->offset))
1194                 return FALSE;
1195             }
1196           else
1197             {
1198               if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
1199                 return FALSE;
1200             }
1201
1202           if (meta_data_size > 0)
1203             {
1204               image->icon_data->offset = image_data_offset + pixel_data_size + 8;
1205               if (!write_card32 (cache, image->icon_data->offset))
1206                 return FALSE;
1207             }
1208           else
1209             {
1210               if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
1211                 return FALSE;
1212             }
1213
1214           if (pixel_data_size > 0)
1215             {
1216               if (!write_image_data (cache, image->image_data, image->image_data->offset))
1217                 return FALSE;
1218             }
1219
1220           if (meta_data_size > 0)
1221             {
1222               if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
1223                 return FALSE;
1224             }
1225
1226           image_data_offset += pixel_data_size + meta_data_size + 8;
1227         }
1228
1229       *offset = next_offset;
1230       node = node->next;
1231     }
1232
1233   return TRUE;
1234 }
1235
1236 static gboolean
1237 write_hash_table (FILE *cache, HashContext *context, int *new_offset)
1238 {
1239   int offset = HASH_OFFSET;
1240   int node_offset;
1241   int i;
1242
1243   if (!(write_card32 (cache, context->size)))
1244     return FALSE;
1245
1246   offset += 4;
1247   node_offset = offset + context->size * 4;
1248   /* Just write zeros here, we will rewrite this later */  
1249   for (i = 0; i < context->size; i++)
1250     {
1251       if (!write_card32 (cache, 0))
1252         return FALSE;
1253     }
1254
1255   /* Now write the buckets */
1256   for (i = 0; i < context->size; i++)
1257     {
1258       if (!context->nodes[i])
1259         continue;
1260
1261       g_assert (node_offset % 4 == 0);
1262       if (!write_bucket (cache, context->nodes[i], &node_offset))
1263         return FALSE;
1264     }
1265
1266   *new_offset = node_offset;
1267
1268   /* Now write out the bucket offsets */
1269
1270   fseek (cache, offset, SEEK_SET);
1271
1272   for (i = 0; i < context->size; i++)
1273     {
1274       if (context->nodes[i] != NULL)
1275         node_offset = context->nodes[i]->offset;
1276       else
1277         node_offset = 0xffffffff;
1278       if (!write_card32 (cache, node_offset))
1279         return FALSE;
1280     }
1281
1282   fseek (cache, 0, SEEK_END);
1283
1284   return TRUE;
1285 }
1286
1287 static gboolean
1288 write_dir_index (FILE *cache, int offset, GList *directories)
1289 {
1290   int n_dirs;
1291   GList *d;
1292   char *dir;
1293   int tmp, tmp2;
1294
1295   n_dirs = g_list_length (directories);
1296
1297   if (!write_card32 (cache, n_dirs))
1298     return FALSE;
1299
1300   offset += 4 + n_dirs * 4;
1301
1302   tmp = offset;
1303   for (d = directories; d; d = d->next)
1304     {
1305       dir = d->data;
1306   
1307       tmp2 = find_string (dir);
1308     
1309       if (tmp2 == 0 || tmp2 == -1)
1310         {
1311           tmp2 = tmp;
1312           tmp += ALIGN_VALUE (strlen (dir) + 1, 4);
1313           /* We're playing a little game with negative
1314            * offsets here to handle duplicate strings in 
1315            * the array, even though that should not 
1316            * really happen for the directory index.
1317            */
1318           add_string (dir, -tmp2);
1319         }
1320       else if (tmp2 < 0)
1321         {
1322           tmp2 = -tmp2;
1323         }
1324
1325       if (!write_card32 (cache, tmp2))
1326         return FALSE;
1327     }
1328
1329   g_assert (offset == ftell (cache));
1330   for (d = directories; d; d = d->next)
1331     {
1332       dir = d->data;
1333
1334       tmp2 = find_string (dir);
1335       g_assert (tmp2 != 0 && tmp2 != -1);
1336       if (tmp2 < 0)
1337         {
1338           tmp2 = -tmp2;
1339           g_assert (tmp2 == ftell (cache));
1340           add_string (dir, tmp2); 
1341           if (!write_string (cache, dir))
1342             return FALSE;
1343         }
1344     }
1345   
1346   return TRUE;
1347 }
1348
1349 static gboolean
1350 write_file (FILE *cache, GHashTable *files, GList *directories)
1351 {
1352   HashContext context;
1353   int new_offset;
1354
1355   /* Convert the hash table into something looking a bit more
1356    * like what we want to write to disk.
1357    */
1358   context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
1359   context.nodes = g_new0 (HashNode *, context.size);
1360   
1361   g_hash_table_foreach_remove (files, convert_to_hash, &context);
1362
1363   /* Now write the file */
1364   /* We write 0 as the directory list offset and go
1365    * back and change it later */
1366   if (!write_header (cache, 0))
1367     {
1368       g_printerr (_("Failed to write header\n"));
1369       return FALSE;
1370     }
1371
1372   if (!write_hash_table (cache, &context, &new_offset))
1373     {
1374       g_printerr (_("Failed to write hash table\n"));
1375       return FALSE;
1376     }
1377
1378   if (!write_dir_index (cache, new_offset, directories))
1379     {
1380       g_printerr (_("Failed to write folder index\n"));
1381       return FALSE;
1382     }
1383   
1384   rewind (cache);
1385
1386   if (!write_header (cache, new_offset))
1387     {
1388       g_printerr (_("Failed to rewrite header\n"));
1389       return FALSE;
1390     }
1391     
1392   return TRUE;
1393 }
1394
1395 static gboolean
1396 validate_file (const gchar *file)
1397 {
1398   GMappedFile *map;
1399   CacheInfo info;
1400
1401   map = g_mapped_file_new (file, FALSE, NULL);
1402   if (!map)
1403     return FALSE;
1404
1405   info.cache = g_mapped_file_get_contents (map);
1406   info.cache_size = g_mapped_file_get_length (map);
1407   info.n_directories = 0;
1408   info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
1409
1410   if (!_gtk_icon_cache_validate (&info)) 
1411     {
1412       g_mapped_file_unref (map);
1413       return FALSE;
1414     }
1415   
1416   g_mapped_file_unref (map);
1417
1418   return TRUE;
1419 }
1420
1421 /**
1422  * safe_fclose:
1423  * @f: A FILE* stream, must have underlying fd
1424  *
1425  * Unix defaults for data preservation after system crash
1426  * are unspecified, and many systems will eat your data
1427  * in this situation unless you explicitly fsync().
1428  *
1429  * Returns: %TRUE on success, %FALSE on failure, and will set errno()
1430  */
1431 static gboolean
1432 safe_fclose (FILE *f)
1433 {
1434   int fd = fileno (f);
1435   g_assert (fd >= 0);
1436   if (fflush (f) == EOF)
1437     return FALSE;
1438 #ifndef G_OS_WIN32
1439   if (fsync (fd) < 0)
1440     return FALSE;
1441 #endif
1442   if (fclose (f) == EOF)
1443     return FALSE;
1444   return TRUE;
1445 }
1446
1447 static void
1448 build_cache (const gchar *path)
1449 {
1450   gchar *cache_path, *tmp_cache_path;
1451 #ifdef G_OS_WIN32
1452   gchar *bak_cache_path = NULL;
1453 #endif
1454   GHashTable *files;
1455   FILE *cache;
1456   GStatBuf path_stat, cache_stat;
1457   struct utimbuf utime_buf;
1458   GList *directories = NULL;
1459   int fd;
1460   int retry_count = 0;
1461 #ifndef G_OS_WIN32
1462   mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1463 #else
1464   int mode = _S_IWRITE | _S_IREAD;
1465 #endif
1466 #ifndef _O_BINARY
1467 #define _O_BINARY 0
1468 #endif
1469
1470   tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
1471   cache_path = g_build_filename (path, CACHE_NAME, NULL);
1472
1473 opentmp:
1474   if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
1475     {
1476       if (force_update && retry_count == 0)
1477         {
1478           retry_count++;
1479           g_remove (tmp_cache_path);
1480           goto opentmp;
1481         }
1482       g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
1483       exit (1);
1484     }
1485
1486   cache = fdopen (fd, "wb");
1487
1488   if (!cache)
1489     {
1490       g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1491       exit (1);
1492     }
1493   
1494   files = g_hash_table_new (g_str_hash, g_str_equal);
1495   image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
1496   icon_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
1497   string_pool = g_hash_table_new (g_str_hash, g_str_equal);
1498  
1499   directories = scan_directory (path, NULL, files, NULL, 0);
1500
1501   if (g_hash_table_size (files) == 0)
1502     {
1503       /* Empty table, just close and remove the file */
1504
1505       fclose (cache);
1506       g_unlink (tmp_cache_path);
1507       g_unlink (cache_path);
1508       exit (0);
1509     }
1510     
1511   /* FIXME: Handle failure */
1512   if (!write_file (cache, files, directories))
1513     {
1514       g_unlink (tmp_cache_path);
1515       exit (1);
1516     }
1517
1518   if (!safe_fclose (cache))
1519     {
1520       g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1521       g_unlink (tmp_cache_path);
1522       exit (1);
1523     }
1524   cache = NULL;
1525
1526   g_list_free_full (directories, g_free);
1527
1528   if (!validate_file (tmp_cache_path))
1529     {
1530       g_printerr (_("The generated cache was invalid.\n"));
1531       /*g_unlink (tmp_cache_path);*/
1532       exit (1);
1533     }
1534
1535 #ifdef G_OS_WIN32
1536   if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
1537     {
1538       bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
1539       g_unlink (bak_cache_path);
1540       if (g_rename (cache_path, bak_cache_path) == -1)
1541         {
1542           int errsv = errno;
1543
1544           g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
1545                       cache_path, bak_cache_path,
1546                       g_strerror (errsv),
1547                       cache_path);
1548           g_unlink (cache_path);
1549           bak_cache_path = NULL;
1550         }
1551     }
1552 #endif
1553
1554   if (g_rename (tmp_cache_path, cache_path) == -1)
1555     {
1556       int errsv = errno;
1557
1558       g_printerr (_("Could not rename %s to %s: %s\n"),
1559                   tmp_cache_path, cache_path,
1560                   g_strerror (errsv));
1561       g_unlink (tmp_cache_path);
1562 #ifdef G_OS_WIN32
1563       if (bak_cache_path != NULL)
1564         if (g_rename (bak_cache_path, cache_path) == -1)
1565           {
1566             errsv = errno;
1567
1568             g_printerr (_("Could not rename %s back to %s: %s.\n"),
1569                         bak_cache_path, cache_path,
1570                         g_strerror (errsv));
1571           }
1572 #endif
1573       exit (1);
1574     }
1575 #ifdef G_OS_WIN32
1576   if (bak_cache_path != NULL)
1577     g_unlink (bak_cache_path);
1578 #endif
1579
1580   /* Update time */
1581   /* FIXME: What do do if an error occurs here? */
1582   if (g_stat (path, &path_stat) < 0 ||
1583       g_stat (cache_path, &cache_stat))
1584     exit (1);
1585
1586   utime_buf.actime = path_stat.st_atime;
1587   utime_buf.modtime = cache_stat.st_mtime;
1588 #if GLIB_CHECK_VERSION (2, 17, 1)
1589   g_utime (path, &utime_buf);
1590 #else
1591   utime (path, &utime_buf);
1592 #endif
1593
1594   if (!quiet)
1595     g_printerr (_("Cache file created successfully.\n"));
1596 }
1597
1598 static void
1599 write_csource (const gchar *path)
1600 {
1601   gchar *cache_path;
1602   gchar *data;
1603   gsize len;
1604   gint i;
1605
1606   cache_path = g_build_filename (path, CACHE_NAME, NULL);
1607   if (!g_file_get_contents (cache_path, &data, &len, NULL))
1608     exit (1);
1609   
1610   g_printf ("#ifdef __SUNPRO_C\n");
1611   g_printf ("#pragma align 4 (%s)\n", var_name);   
1612   g_printf ("#endif\n");
1613   
1614   g_printf ("#ifdef __GNUC__\n");
1615   g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
1616   g_printf ("#else\n");
1617   g_printf ("static const guint8 %s[] = \n", var_name);
1618   g_printf ("#endif\n");
1619
1620   g_printf ("{\n");
1621   for (i = 0; i < len - 1; i++)
1622     {
1623       if (i %12 == 0)
1624         g_printf ("  ");
1625       g_printf ("0x%02x, ", (guint8)data[i]);
1626       if (i % 12 == 11)
1627         g_printf ("\n");
1628     }
1629   
1630   g_printf ("0x%02x\n};\n", (guint8)data[i]);
1631 }
1632
1633 static GOptionEntry args[] = {
1634   { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
1635   { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don't check for the existence of index.theme"), NULL },
1636   { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL },
1637   { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
1638   { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
1639   { "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
1640   { NULL }
1641 };
1642
1643 static void
1644 printerr_handler (const gchar *string)
1645 {
1646   const gchar *charset;
1647
1648   fputs (g_get_prgname (), stderr);
1649   fputs (": ", stderr);
1650   if (g_get_charset (&charset))
1651     fputs (string, stderr); /* charset is UTF-8 already */
1652   else
1653     {
1654       gchar *result;
1655
1656       result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
1657       
1658       if (result)
1659         {
1660           fputs (result, stderr);
1661           g_free (result);
1662         }
1663    
1664       fflush (stderr);
1665     }
1666 }
1667
1668
1669 int
1670 main (int argc, char **argv)
1671 {
1672   gchar *path;
1673   GOptionContext *context;
1674
1675   if (argc < 2)
1676     return 0;
1677
1678   g_set_printerr_handler (printerr_handler);
1679   
1680   setlocale (LC_ALL, "");
1681
1682 #ifdef ENABLE_NLS
1683   bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
1684 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1685   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1686 #endif
1687 #endif
1688
1689   context = g_option_context_new ("ICONPATH");
1690   g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
1691
1692   g_option_context_parse (context, &argc, &argv, NULL);
1693   
1694   path = argv[1];
1695 #ifdef G_OS_WIN32
1696   path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
1697 #endif
1698   
1699   if (validate)
1700     {
1701        gchar *file = g_build_filename (path, CACHE_NAME, NULL);
1702
1703        if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
1704          {
1705             if (!quiet)
1706               g_printerr (_("File not found: %s\n"), file);
1707             exit (1);
1708          }
1709        if (!validate_file (file))
1710          {
1711            if (!quiet)
1712              g_printerr (_("Not a valid icon cache: %s\n"), file);
1713            exit (1);
1714          }
1715        else 
1716          {
1717            exit (0);
1718          }
1719     }
1720
1721   if (!ignore_theme_index && !has_theme_index (path))
1722     {
1723       if (path)
1724         {
1725           g_printerr (_("No theme index file.\n"));
1726         }
1727       else
1728         {
1729           g_printerr (_("No theme index file in '%s'.\n"
1730                     "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
1731         }
1732
1733       return 1;
1734     }
1735   
1736   if (!force_update && is_cache_up_to_date (path))
1737     return 0;
1738
1739   build_cache (path);
1740
1741   if (strcmp (var_name, "-") != 0)
1742     write_csource (path);
1743
1744   return 0;
1745 }