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