Bug Summary

File:ctk/updateiconcache.c
Warning:line 811, column 7
Value of 'errno' was not checked and may be overwritten by function 'fwrite'

Annotated Source Code

Press '?' to see keyboard shortcuts

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