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