Bug Summary

File:ctk/updateiconcache.c
Warning:line 1023, column 24
Although the value stored to 'poolv' is used in the enclosing expression, the value is never actually read from 'poolv'

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 -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -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-16/lib/clang/16/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 -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fgnuc-version=4.2.1 -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.core.SizeofPtr -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-09-19-171836-43636-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);
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) &&
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)
Although the value stored to 'poolv' is used in the enclosing expression, the value is never actually read from 'poolv'
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))
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))
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))
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);
1421
1422 if (!write_header (cache, new_offset))
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)
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)
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)
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))
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)
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)
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 && !has_theme_index (path))
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 && is_cache_up_to_date (path))
1770 return 0;
1771
1772 replace_backslashes_with_slashes (path);
1773 build_cache (path);
1774
1775 if (strcmp (var_name, "-") != 0)
1776 write_csource (path);
1777
1778 return 0;
1779}