]> Pileus Git - ~andy/gtk/blob - gtk/updateiconcache.c
stylecontext: Do invalidation on first resize container
[~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 /**
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.
582  */
583 static void
584 replace_backslashes_with_slashes (gchar *path)
585 {
586   size_t i;
587   if (path == NULL)
588     return;
589   for (i = 0; path[i]; i++)
590     if (G_IS_DIR_SEPARATOR (path[i]))
591       path[i] = '/';
592 }
593
594 static GList *
595 scan_directory (const gchar *base_path, 
596                 const gchar *subdir, 
597                 GHashTable  *files, 
598                 GList       *directories,
599                 gint         depth)
600 {
601   GHashTable *dir_hash;
602   GDir *dir;
603   const gchar *name;
604   gchar *dir_path;
605   gboolean dir_added = FALSE;
606   guint dir_index = 0xffff;
607   
608   dir_path = g_build_path ("/", base_path, subdir, NULL);
609
610   /* FIXME: Use the gerror */
611   dir = g_dir_open (dir_path, 0, NULL);
612   
613   if (!dir)
614     return directories;
615   
616   dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
617
618   while ((name = g_dir_read_name (dir)))
619     {
620       gchar *path;
621       gboolean retval;
622       int flags = 0;
623       Image *image;
624       gchar *basename, *dot;
625
626       path = g_build_filename (dir_path, name, NULL);
627
628       retval = g_file_test (path, G_FILE_TEST_IS_DIR);
629       if (retval)
630         {
631           gchar *subsubdir;
632
633           if (subdir)
634             subsubdir = g_build_path ("/", subdir, name, NULL);
635           else
636             subsubdir = g_strdup (name);
637           directories = scan_directory (base_path, subsubdir, files, 
638                                         directories, depth + 1);
639           g_free (subsubdir);
640
641           continue;
642         }
643
644       /* ignore images in the toplevel directory */
645       if (subdir == NULL)
646         continue;
647
648       retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
649       if (retval)
650         {
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;
659           
660           if (flags == 0)
661             continue;
662           
663           basename = g_strdup (name);
664           dot = strrchr (basename, '.');
665           *dot = '\0';
666           
667           image = g_hash_table_lookup (dir_hash, basename);
668           if (!image)
669             {
670               if (!dir_added) 
671                 {
672                   dir_added = TRUE;
673                   if (subdir)
674                     {
675                       dir_index = g_list_length (directories);
676                       directories = g_list_append (directories, g_strdup (subdir));
677                     }
678                   else
679                     dir_index = 0xffff;
680                 }
681                 
682               image = g_new0 (Image, 1);
683               image->dir_index = dir_index;
684               g_hash_table_insert (dir_hash, g_strdup (basename), image);
685             }
686
687           image->flags |= flags;
688       
689           maybe_cache_image_data (image, path);
690           maybe_cache_icon_data (image, path);
691        
692           g_free (basename);
693         }
694
695       g_free (path);
696     }
697
698   g_dir_close (dir);
699
700   /* Move dir into the big file hash */
701   g_hash_table_foreach_remove (dir_hash, foreach_remove_func, files);
702   
703   g_hash_table_destroy (dir_hash);
704
705   return directories;
706 }
707
708 typedef struct _HashNode HashNode;
709
710 struct _HashNode
711 {
712   HashNode *next;
713   gchar *name;
714   GList *image_list;
715   gint offset;
716 };
717
718 static guint
719 icon_name_hash (gconstpointer key)
720 {
721   const signed char *p = key;
722   guint32 h = *p;
723
724   if (h)
725     for (p += 1; *p != '\0'; p++)
726       h = (h << 5) - h + *p;
727
728   return h;
729 }
730
731 typedef struct {
732   gint size;
733   HashNode **nodes;
734 } HashContext;
735
736 static gboolean
737 convert_to_hash (gpointer key, gpointer value, gpointer user_data)
738 {
739   HashContext *context = user_data;
740   guint hash;
741   HashNode *node;
742   
743   hash = icon_name_hash (key) % context->size;
744
745   node = g_new0 (HashNode, 1);
746   node->next = NULL;
747   node->name = key;
748   node->image_list = value;
749
750   if (context->nodes[hash] != NULL)
751     node->next = context->nodes[hash];
752
753   context->nodes[hash] = node;
754   
755   return TRUE;
756 }
757
758 static GHashTable *string_pool = NULL;
759  
760 static int
761 find_string (const gchar *n)
762 {
763   return GPOINTER_TO_INT (g_hash_table_lookup (string_pool, n));
764 }
765
766 static void
767 add_string (const gchar *n, int offset)
768 {
769   g_hash_table_insert (string_pool, (gpointer) n, GINT_TO_POINTER (offset));
770 }
771
772 static gboolean
773 write_string (FILE *cache, const gchar *n)
774 {
775   gchar *s;
776   int i, l;
777   
778   l = ALIGN_VALUE (strlen (n) + 1, 4);
779   
780   s = g_malloc0 (l);
781   strcpy (s, n);
782
783   i = fwrite (s, l, 1, cache);
784
785   g_free (s);
786
787   return i == 1;
788   
789 }
790
791 static gboolean
792 write_card16 (FILE *cache, guint16 n)
793 {
794   int i;
795
796   n = GUINT16_TO_BE (n);
797   
798   i = fwrite ((char *)&n, 2, 1, cache);
799
800   return i == 1;
801 }
802
803 static gboolean
804 write_card32 (FILE *cache, guint32 n)
805 {
806   int i;
807
808   n = GUINT32_TO_BE (n);
809   
810   i = fwrite ((char *)&n, 4, 1, cache);
811
812   return i == 1;
813 }
814
815
816 static gboolean
817 write_image_data (FILE *cache, ImageData *image_data, int offset)
818 {
819   guint8 *s;
820   guint len;
821   gint i;
822   GdkPixdata *pixdata = &image_data->pixdata;
823
824   /* Type 0 is GdkPixdata */
825   if (!write_card32 (cache, 0))
826     return FALSE;
827
828   s = gdk_pixdata_serialize (pixdata, &len);
829
830   if (!write_card32 (cache, len))
831     {
832       g_free (s);
833       return FALSE;
834     }
835
836   i = fwrite (s, len, 1, cache);
837   
838   g_free (s);
839
840   return i == 1;
841 }
842
843 static gboolean
844 write_icon_data (FILE *cache, IconData *icon_data, int offset)
845 {
846   int ofs = offset + 12;
847   int j;
848   int tmp, tmp2;
849
850   if (icon_data->has_embedded_rect)
851     {
852       if (!write_card32 (cache, ofs))
853         return FALSE;
854               
855        ofs += 8;
856     }         
857   else
858     {
859       if (!write_card32 (cache, 0))
860         return FALSE;
861     }
862               
863   if (icon_data->n_attach_points > 0)
864     {
865       if (!write_card32 (cache, ofs))
866         return FALSE;
867
868       ofs += 4 + 4 * icon_data->n_attach_points;
869     }
870   else
871     {
872       if (!write_card32 (cache, 0))
873         return FALSE;
874     }
875
876   if (icon_data->n_display_names > 0)
877     {
878       if (!write_card32 (cache, ofs))
879         return FALSE;
880     }
881   else
882     {
883       if (!write_card32 (cache, 0))
884         return FALSE;
885     }
886
887   if (icon_data->has_embedded_rect)
888     {
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))
893         return FALSE;
894     }
895
896   if (icon_data->n_attach_points > 0)
897     {
898       if (!write_card32 (cache, icon_data->n_attach_points))
899         return FALSE;
900                   
901       for (j = 0; j < 2 * icon_data->n_attach_points; j++)
902         {
903           if (!write_card16 (cache, icon_data->attach_points[j]))
904             return FALSE;
905         }                 
906     }
907
908   if (icon_data->n_display_names > 0)
909     {
910       if (!write_card32 (cache, icon_data->n_display_names))
911         return FALSE;
912
913       ofs += 4 + 8 * icon_data->n_display_names;
914
915       tmp = ofs;
916       for (j = 0; j < 2 * icon_data->n_display_names; j++)
917         {
918           tmp2 = find_string (icon_data->display_names[j]);
919           if (tmp2 == 0 || tmp2 == -1)
920             {
921               tmp2 = tmp;
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 
925                * the array.
926                */
927               add_string (icon_data->display_names[j], -tmp2);
928             }
929           else if (tmp2 < 0)
930             {
931               tmp2 = -tmp2;
932             }
933
934           if (!write_card32 (cache, tmp2))
935             return FALSE;
936
937         }
938
939       g_assert (ofs == ftell (cache));
940       for (j = 0; j < 2 * icon_data->n_display_names; j++)
941         {
942           tmp2 = find_string (icon_data->display_names[j]);
943           g_assert (tmp2 != 0 && tmp2 != -1);
944           if (tmp2 < 0)
945             {
946               tmp2 = -tmp2;
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]))
950                 return FALSE;
951             }
952         }
953     }        
954
955   return TRUE;
956 }
957
958 static gboolean
959 write_header (FILE *cache, guint32 dir_list_offset)
960 {
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));
965 }
966
967 static gint
968 get_image_meta_data_size (Image *image)
969 {
970   gint i;
971
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.
978    */
979   if (image->icon_data_size == 0)
980     {
981       if (image->icon_data && image->icon_data->size < 0)
982         {
983           IconData *data = image->icon_data;
984
985           data->size = 0;
986
987           if (data->has_embedded_rect ||
988               data->n_attach_points > 0 ||
989               data->n_display_names > 0)
990             data->size += 12;
991
992           if (data->has_embedded_rect)
993             data->size += 8;
994
995           if (data->n_attach_points > 0)
996             data->size += 4 + data->n_attach_points * 4;
997
998           if (data->n_display_names > 0)
999             {
1000               data->size += 4 + 8 * data->n_display_names;
1001
1002               for (i = 0; data->display_names[i]; i++)
1003                 { 
1004                   int poolv;
1005                   if ((poolv = find_string (data->display_names[i])) == 0)
1006                     {
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.
1013                        */
1014                       add_string (data->display_names[i], -1);
1015                     }
1016                 }
1017            } 
1018
1019           image->icon_data_size = data->size;
1020           data->size = 0;
1021         }
1022     }
1023
1024   g_assert (image->icon_data_size % 4 == 0);
1025
1026   return image->icon_data_size;
1027 }
1028
1029 static gint
1030 get_image_pixel_data_size (Image *image)
1031 {
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.
1038    */
1039   if (image->pixel_data_size == 0)
1040     {
1041       if (image->image_data && 
1042           image->image_data->has_pixdata)
1043         {
1044           image->pixel_data_size = image->image_data->size;
1045           image->image_data->size = 0;
1046         }
1047     }
1048
1049   g_assert (image->pixel_data_size % 4 == 0);
1050
1051   return image->pixel_data_size;
1052 }
1053
1054 static gint
1055 get_image_data_size (Image *image)
1056 {
1057   gint len;
1058   
1059   len = 0;
1060
1061   len += get_image_pixel_data_size (image);
1062   len += get_image_meta_data_size (image);
1063
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
1067    * are NULL.
1068    */
1069   if (len > 0 || image->image_data || image->icon_data)
1070     len += 8;
1071
1072   return len;
1073 }
1074
1075 static void
1076 get_single_node_size (HashNode *node, int *node_size, int *image_data_size)
1077 {
1078   GList *list;
1079
1080   /* Node pointers */
1081   *node_size = 12;
1082
1083   /* Name */
1084   if (find_string (node->name) == 0)
1085     {
1086       *node_size += ALIGN_VALUE (strlen (node->name) + 1, 4);
1087       add_string (node->name, -1);
1088     }
1089
1090   /* Image list */
1091   *node_size += 4 + g_list_length (node->image_list) * 8;
1092  
1093   /* Image data */
1094   *image_data_size = 0;
1095   for (list = node->image_list; list; list = list->next)
1096     {
1097       Image *image = list->data;
1098
1099       *image_data_size += get_image_data_size (image);
1100     }
1101 }
1102
1103 static gboolean
1104 write_bucket (FILE *cache, HashNode *node, int *offset)
1105 {
1106   while (node != NULL)
1107     {
1108       int node_size, image_data_size;
1109       int next_offset, image_data_offset;
1110       int data_offset;
1111       int name_offset;
1112       int name_size;
1113       int image_list_offset;
1114       int i, len;
1115       GList *list;
1116
1117       g_assert (*offset == ftell (cache));
1118
1119       node->offset = *offset;
1120
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;
1126       /* Chain offset */
1127       if (node->next != NULL)
1128         {
1129           if (!write_card32 (cache, next_offset))
1130             return FALSE;
1131         }
1132       else
1133         {
1134           if (!write_card32 (cache, 0xffffffff))
1135             return FALSE;
1136         }
1137
1138       name_size = 0;
1139       name_offset = find_string (node->name);
1140       if (name_offset <= 0)
1141         {
1142           name_offset = *offset + 12;
1143           name_size = ALIGN_VALUE (strlen (node->name) + 1, 4);
1144           add_string (node->name, name_offset);
1145         }
1146       if (!write_card32 (cache, name_offset))
1147         return FALSE;
1148
1149       image_list_offset = *offset + 12 + name_size;
1150       if (!write_card32 (cache, image_list_offset))
1151         return FALSE;
1152
1153       /* Icon name */
1154       if (name_size > 0)
1155         {
1156           if (!write_string (cache, node->name))
1157             return FALSE;
1158         }
1159
1160       /* Image list */
1161       len = g_list_length (node->image_list);
1162       if (!write_card32 (cache, len))
1163         return FALSE;
1164
1165       list = node->image_list;
1166       data_offset = image_data_offset;
1167       for (i = 0; i < len; i++)
1168         {
1169           Image *image = list->data;
1170           int image_data_size = get_image_data_size (image);
1171
1172           /* Directory index */
1173           if (!write_card16 (cache, image->dir_index))
1174             return FALSE;
1175
1176           /* Flags */
1177           if (!write_card16 (cache, image->flags))
1178             return FALSE;
1179
1180           /* Image data offset */
1181           if (image_data_size > 0)
1182             {
1183               if (!write_card32 (cache, data_offset))
1184                 return FALSE;
1185               data_offset += image_data_size;
1186             }
1187           else
1188             {
1189               if (!write_card32 (cache, 0))
1190                 return FALSE;
1191             }
1192
1193           list = list->next;
1194         }
1195
1196       /* Now write the image data */
1197       list = node->image_list;
1198       for (i = 0; i < len; i++, list = list->next)
1199         {
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);
1203
1204           if (get_image_data_size (image) == 0)
1205             continue;
1206
1207           /* Pixel data */
1208           if (pixel_data_size > 0)
1209             {
1210               image->image_data->offset = image_data_offset + 8;
1211               if (!write_card32 (cache, image->image_data->offset))
1212                 return FALSE;
1213             }
1214           else
1215             {
1216               if (!write_card32 (cache, (guint32) (image->image_data ? image->image_data->offset : 0)))
1217                 return FALSE;
1218             }
1219
1220           if (meta_data_size > 0)
1221             {
1222               image->icon_data->offset = image_data_offset + pixel_data_size + 8;
1223               if (!write_card32 (cache, image->icon_data->offset))
1224                 return FALSE;
1225             }
1226           else
1227             {
1228               if (!write_card32 (cache, image->icon_data ? image->icon_data->offset : 0))
1229                 return FALSE;
1230             }
1231
1232           if (pixel_data_size > 0)
1233             {
1234               if (!write_image_data (cache, image->image_data, image->image_data->offset))
1235                 return FALSE;
1236             }
1237
1238           if (meta_data_size > 0)
1239             {
1240               if (!write_icon_data (cache, image->icon_data, image->icon_data->offset))
1241                 return FALSE;
1242             }
1243
1244           image_data_offset += pixel_data_size + meta_data_size + 8;
1245         }
1246
1247       *offset = next_offset;
1248       node = node->next;
1249     }
1250
1251   return TRUE;
1252 }
1253
1254 static gboolean
1255 write_hash_table (FILE *cache, HashContext *context, int *new_offset)
1256 {
1257   int offset = HASH_OFFSET;
1258   int node_offset;
1259   int i;
1260
1261   if (!(write_card32 (cache, context->size)))
1262     return FALSE;
1263
1264   offset += 4;
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++)
1268     {
1269       if (!write_card32 (cache, 0))
1270         return FALSE;
1271     }
1272
1273   /* Now write the buckets */
1274   for (i = 0; i < context->size; i++)
1275     {
1276       if (!context->nodes[i])
1277         continue;
1278
1279       g_assert (node_offset % 4 == 0);
1280       if (!write_bucket (cache, context->nodes[i], &node_offset))
1281         return FALSE;
1282     }
1283
1284   *new_offset = node_offset;
1285
1286   /* Now write out the bucket offsets */
1287
1288   fseek (cache, offset, SEEK_SET);
1289
1290   for (i = 0; i < context->size; i++)
1291     {
1292       if (context->nodes[i] != NULL)
1293         node_offset = context->nodes[i]->offset;
1294       else
1295         node_offset = 0xffffffff;
1296       if (!write_card32 (cache, node_offset))
1297         return FALSE;
1298     }
1299
1300   fseek (cache, 0, SEEK_END);
1301
1302   return TRUE;
1303 }
1304
1305 static gboolean
1306 write_dir_index (FILE *cache, int offset, GList *directories)
1307 {
1308   int n_dirs;
1309   GList *d;
1310   char *dir;
1311   int tmp, tmp2;
1312
1313   n_dirs = g_list_length (directories);
1314
1315   if (!write_card32 (cache, n_dirs))
1316     return FALSE;
1317
1318   offset += 4 + n_dirs * 4;
1319
1320   tmp = offset;
1321   for (d = directories; d; d = d->next)
1322     {
1323       dir = d->data;
1324   
1325       tmp2 = find_string (dir);
1326     
1327       if (tmp2 == 0 || tmp2 == -1)
1328         {
1329           tmp2 = tmp;
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.
1335            */
1336           add_string (dir, -tmp2);
1337         }
1338       else if (tmp2 < 0)
1339         {
1340           tmp2 = -tmp2;
1341         }
1342
1343       if (!write_card32 (cache, tmp2))
1344         return FALSE;
1345     }
1346
1347   g_assert (offset == ftell (cache));
1348   for (d = directories; d; d = d->next)
1349     {
1350       dir = d->data;
1351
1352       tmp2 = find_string (dir);
1353       g_assert (tmp2 != 0 && tmp2 != -1);
1354       if (tmp2 < 0)
1355         {
1356           tmp2 = -tmp2;
1357           g_assert (tmp2 == ftell (cache));
1358           add_string (dir, tmp2); 
1359           if (!write_string (cache, dir))
1360             return FALSE;
1361         }
1362     }
1363   
1364   return TRUE;
1365 }
1366
1367 static gboolean
1368 write_file (FILE *cache, GHashTable *files, GList *directories)
1369 {
1370   HashContext context;
1371   int new_offset;
1372
1373   /* Convert the hash table into something looking a bit more
1374    * like what we want to write to disk.
1375    */
1376   context.size = g_spaced_primes_closest (g_hash_table_size (files) / 3);
1377   context.nodes = g_new0 (HashNode *, context.size);
1378   
1379   g_hash_table_foreach_remove (files, convert_to_hash, &context);
1380
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))
1385     {
1386       g_printerr (_("Failed to write header\n"));
1387       return FALSE;
1388     }
1389
1390   if (!write_hash_table (cache, &context, &new_offset))
1391     {
1392       g_printerr (_("Failed to write hash table\n"));
1393       return FALSE;
1394     }
1395
1396   if (!write_dir_index (cache, new_offset, directories))
1397     {
1398       g_printerr (_("Failed to write folder index\n"));
1399       return FALSE;
1400     }
1401   
1402   rewind (cache);
1403
1404   if (!write_header (cache, new_offset))
1405     {
1406       g_printerr (_("Failed to rewrite header\n"));
1407       return FALSE;
1408     }
1409     
1410   return TRUE;
1411 }
1412
1413 static gboolean
1414 validate_file (const gchar *file)
1415 {
1416   GMappedFile *map;
1417   CacheInfo info;
1418
1419   map = g_mapped_file_new (file, FALSE, NULL);
1420   if (!map)
1421     return FALSE;
1422
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;
1427
1428   if (!_gtk_icon_cache_validate (&info)) 
1429     {
1430       g_mapped_file_unref (map);
1431       return FALSE;
1432     }
1433   
1434   g_mapped_file_unref (map);
1435
1436   return TRUE;
1437 }
1438
1439 /**
1440  * safe_fclose:
1441  * @f: A FILE* stream, must have underlying fd
1442  *
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().
1446  *
1447  * Returns: %TRUE on success, %FALSE on failure, and will set errno()
1448  */
1449 static gboolean
1450 safe_fclose (FILE *f)
1451 {
1452   int fd = fileno (f);
1453   g_assert (fd >= 0);
1454   if (fflush (f) == EOF)
1455     return FALSE;
1456 #ifndef G_OS_WIN32
1457   if (fsync (fd) < 0)
1458     return FALSE;
1459 #endif
1460   if (fclose (f) == EOF)
1461     return FALSE;
1462   return TRUE;
1463 }
1464
1465 static void
1466 build_cache (const gchar *path)
1467 {
1468   gchar *cache_path, *tmp_cache_path;
1469 #ifdef G_OS_WIN32
1470   gchar *bak_cache_path = NULL;
1471 #endif
1472   GHashTable *files;
1473   FILE *cache;
1474   GStatBuf path_stat, cache_stat;
1475   struct utimbuf utime_buf;
1476   GList *directories = NULL;
1477   int fd;
1478   int retry_count = 0;
1479 #ifndef G_OS_WIN32
1480   mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
1481 #else
1482   int mode = _S_IWRITE | _S_IREAD;
1483 #endif
1484 #ifndef _O_BINARY
1485 #define _O_BINARY 0
1486 #endif
1487
1488   tmp_cache_path = g_build_filename (path, "."CACHE_NAME, NULL);
1489   cache_path = g_build_filename (path, CACHE_NAME, NULL);
1490
1491 opentmp:
1492   if ((fd = g_open (tmp_cache_path, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC | _O_BINARY, mode)) == -1)
1493     {
1494       if (force_update && retry_count == 0)
1495         {
1496           retry_count++;
1497           g_remove (tmp_cache_path);
1498           goto opentmp;
1499         }
1500       g_printerr (_("Failed to open file %s : %s\n"), tmp_cache_path, g_strerror (errno));
1501       exit (1);
1502     }
1503
1504   cache = fdopen (fd, "wb");
1505
1506   if (!cache)
1507     {
1508       g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1509       exit (1);
1510     }
1511   
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);
1516  
1517   directories = scan_directory (path, NULL, files, NULL, 0);
1518
1519   if (g_hash_table_size (files) == 0)
1520     {
1521       /* Empty table, just close and remove the file */
1522
1523       fclose (cache);
1524       g_unlink (tmp_cache_path);
1525       g_unlink (cache_path);
1526       exit (0);
1527     }
1528     
1529   /* FIXME: Handle failure */
1530   if (!write_file (cache, files, directories))
1531     {
1532       g_unlink (tmp_cache_path);
1533       exit (1);
1534     }
1535
1536   if (!safe_fclose (cache))
1537     {
1538       g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
1539       g_unlink (tmp_cache_path);
1540       exit (1);
1541     }
1542   cache = NULL;
1543
1544   g_list_free_full (directories, g_free);
1545
1546   if (!validate_file (tmp_cache_path))
1547     {
1548       g_printerr (_("The generated cache was invalid.\n"));
1549       /*g_unlink (tmp_cache_path);*/
1550       exit (1);
1551     }
1552
1553 #ifdef G_OS_WIN32
1554   if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
1555     {
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)
1559         {
1560           int errsv = errno;
1561
1562           g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
1563                       cache_path, bak_cache_path,
1564                       g_strerror (errsv),
1565                       cache_path);
1566           g_unlink (cache_path);
1567           bak_cache_path = NULL;
1568         }
1569     }
1570 #endif
1571
1572   if (g_rename (tmp_cache_path, cache_path) == -1)
1573     {
1574       int errsv = errno;
1575
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);
1580 #ifdef G_OS_WIN32
1581       if (bak_cache_path != NULL)
1582         if (g_rename (bak_cache_path, cache_path) == -1)
1583           {
1584             errsv = errno;
1585
1586             g_printerr (_("Could not rename %s back to %s: %s.\n"),
1587                         bak_cache_path, cache_path,
1588                         g_strerror (errsv));
1589           }
1590 #endif
1591       exit (1);
1592     }
1593 #ifdef G_OS_WIN32
1594   if (bak_cache_path != NULL)
1595     g_unlink (bak_cache_path);
1596 #endif
1597
1598   /* Update time */
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))
1602     exit (1);
1603
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);
1608 #else
1609   utime (path, &utime_buf);
1610 #endif
1611
1612   if (!quiet)
1613     g_printerr (_("Cache file created successfully.\n"));
1614 }
1615
1616 static void
1617 write_csource (const gchar *path)
1618 {
1619   gchar *cache_path;
1620   gchar *data;
1621   gsize len;
1622   gint i;
1623
1624   cache_path = g_build_filename (path, CACHE_NAME, NULL);
1625   if (!g_file_get_contents (cache_path, &data, &len, NULL))
1626     exit (1);
1627   
1628   g_printf ("#ifdef __SUNPRO_C\n");
1629   g_printf ("#pragma align 4 (%s)\n", var_name);   
1630   g_printf ("#endif\n");
1631   
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");
1637
1638   g_printf ("{\n");
1639   for (i = 0; i < len - 1; i++)
1640     {
1641       if (i %12 == 0)
1642         g_printf ("  ");
1643       g_printf ("0x%02x, ", (guint8)data[i]);
1644       if (i % 12 == 11)
1645         g_printf ("\n");
1646     }
1647   
1648   g_printf ("0x%02x\n};\n", (guint8)data[i]);
1649 }
1650
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 },
1658   { NULL }
1659 };
1660
1661 static void
1662 printerr_handler (const gchar *string)
1663 {
1664   const gchar *charset;
1665
1666   fputs (g_get_prgname (), stderr);
1667   fputs (": ", stderr);
1668   if (g_get_charset (&charset))
1669     fputs (string, stderr); /* charset is UTF-8 already */
1670   else
1671     {
1672       gchar *result;
1673
1674       result = g_convert_with_fallback (string, -1, charset, "UTF-8", "?", NULL, NULL, NULL);
1675       
1676       if (result)
1677         {
1678           fputs (result, stderr);
1679           g_free (result);
1680         }
1681    
1682       fflush (stderr);
1683     }
1684 }
1685
1686
1687 int
1688 main (int argc, char **argv)
1689 {
1690   gchar *path;
1691   GOptionContext *context;
1692
1693   if (argc < 2)
1694     return 0;
1695
1696   g_set_printerr_handler (printerr_handler);
1697   
1698   setlocale (LC_ALL, "");
1699
1700 #ifdef ENABLE_NLS
1701   bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
1702 #ifdef HAVE_BIND_TEXTDOMAIN_CODESET
1703   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
1704 #endif
1705 #endif
1706
1707   context = g_option_context_new ("ICONPATH");
1708   g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
1709
1710   g_option_context_parse (context, &argc, &argv, NULL);
1711   
1712   path = argv[1];
1713 #ifdef G_OS_WIN32
1714   path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
1715 #endif
1716   
1717   if (validate)
1718     {
1719        gchar *file = g_build_filename (path, CACHE_NAME, NULL);
1720
1721        if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
1722          {
1723             if (!quiet)
1724               g_printerr (_("File not found: %s\n"), file);
1725             exit (1);
1726          }
1727        if (!validate_file (file))
1728          {
1729            if (!quiet)
1730              g_printerr (_("Not a valid icon cache: %s\n"), file);
1731            exit (1);
1732          }
1733        else 
1734          {
1735            exit (0);
1736          }
1737     }
1738
1739   if (!ignore_theme_index && !has_theme_index (path))
1740     {
1741       if (path)
1742         {
1743           g_printerr (_("No theme index file.\n"));
1744         }
1745       else
1746         {
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);
1749         }
1750
1751       return 1;
1752     }
1753   
1754   if (!force_update && is_cache_up_to_date (path))
1755     return 0;
1756
1757   replace_backslashes_with_slashes (path);
1758   build_cache (path);
1759
1760   if (strcmp (var_name, "-") != 0)
1761     write_csource (path);
1762
1763   return 0;
1764 }