1/* gtkiconcache.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 "gtkdebug.h"
21#include "gtkiconcacheprivate.h"
22#include "gtkiconcachevalidatorprivate.h"
23
24#include <glib/gstdio.h>
25#include <gdk-pixbuf/gdk-pixdata.h>
26
27#ifdef HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30#ifdef G_OS_WIN32
31#include <io.h>
32#endif
33#include <fcntl.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <string.h>
37
38
39#ifndef _O_BINARY
40#define _O_BINARY 0
41#endif
42
43#define GET_UINT16(cache, offset) (GUINT16_FROM_BE (*(guint16 *)((cache) + (offset))))
44#define GET_UINT32(cache, offset) (GUINT32_FROM_BE (*(guint32 *)((cache) + (offset))))
45
46struct _GtkIconCache {
47 int ref_count;
48
49 GMappedFile *map;
50 char *buffer;
51
52 guint32 last_chain_offset;
53};
54
55GtkIconCache *
56gtk_icon_cache_ref (GtkIconCache *cache)
57{
58 cache->ref_count++;
59 return cache;
60}
61
62void
63gtk_icon_cache_unref (GtkIconCache *cache)
64{
65 cache->ref_count --;
66
67 if (cache->ref_count == 0)
68 {
69 GTK_NOTE (ICONTHEME, g_message ("unmapping icon cache"));
70
71 if (cache->map)
72 g_mapped_file_unref (file: cache->map);
73 g_free (mem: cache);
74 }
75}
76
77GtkIconCache *
78gtk_icon_cache_new_for_path (const char *path)
79{
80 GtkIconCache *cache = NULL;
81 GMappedFile *map;
82
83 char *cache_filename;
84 GStatBuf st;
85 GStatBuf path_st;
86
87 /* Check if we have a cache file */
88 cache_filename = g_build_filename (first_element: path, "icon-theme.cache", NULL);
89
90 GTK_NOTE (ICONTHEME, g_message ("look for icon cache in %s", path));
91
92 if (g_stat (file: path, buf: &path_st) < 0)
93 goto done;
94
95 if (g_stat (file: cache_filename, buf: &st) < 0 || st.st_size < 4)
96 goto done;
97
98 /* Verify cache is up-to-date */
99 if (st.st_mtime < path_st.st_mtime)
100 {
101 GTK_NOTE (ICONTHEME, g_message ("icon cache outdated"));
102 goto done;
103 }
104
105 map = g_mapped_file_new (filename: cache_filename, FALSE, NULL);
106
107 if (!map)
108 goto done;
109
110#ifdef G_ENABLE_DEBUG
111 if (GTK_DEBUG_CHECK (ICONTHEME))
112 {
113 CacheInfo info;
114
115 info.cache = g_mapped_file_get_contents (file: map);
116 info.cache_size = g_mapped_file_get_length (file: map);
117 info.n_directories = 0;
118 info.flags = CHECK_OFFSETS|CHECK_STRINGS;
119
120 if (!gtk_icon_cache_validate (info: &info))
121 {
122 g_mapped_file_unref (file: map);
123 g_warning ("Icon cache '%s' is invalid", cache_filename);
124
125 goto done;
126 }
127 }
128#endif
129
130 GTK_NOTE (ICONTHEME, g_message ("found icon cache for %s", path));
131
132 cache = g_new0 (GtkIconCache, 1);
133 cache->ref_count = 1;
134 cache->map = map;
135 cache->buffer = g_mapped_file_get_contents (file: map);
136
137 done:
138 g_free (mem: cache_filename);
139
140 return cache;
141}
142
143GtkIconCache *
144gtk_icon_cache_new (const char *data)
145{
146 GtkIconCache *cache;
147
148 cache = g_new0 (GtkIconCache, 1);
149 cache->ref_count = 1;
150 cache->map = NULL;
151 cache->buffer = (char *)data;
152
153 return cache;
154}
155
156static int
157get_directory_index (GtkIconCache *cache,
158 const char *directory)
159{
160 guint32 dir_list_offset;
161 int n_dirs;
162 int i;
163
164 dir_list_offset = GET_UINT32 (cache->buffer, 8);
165
166 n_dirs = GET_UINT32 (cache->buffer, dir_list_offset);
167
168 for (i = 0; i < n_dirs; i++)
169 {
170 guint32 name_offset = GET_UINT32 (cache->buffer, dir_list_offset + 4 + 4 * i);
171 char *name = cache->buffer + name_offset;
172 if (strcmp (s1: name, s2: directory) == 0)
173 return i;
174 }
175
176 return -1;
177}
178
179GHashTable *
180gtk_icon_cache_list_icons_in_directory (GtkIconCache *cache,
181 const char *directory,
182 GtkStringSet *set)
183{
184 int directory_index;
185 guint32 hash_offset, n_buckets;
186 guint32 chain_offset;
187 guint32 image_list_offset, n_images;
188 int i, j;
189 GHashTable *icons = NULL;
190
191 directory_index = get_directory_index (cache, directory);
192
193 if (directory_index == -1)
194 return NULL;
195
196 hash_offset = GET_UINT32 (cache->buffer, 4);
197 n_buckets = GET_UINT32 (cache->buffer, hash_offset);
198
199 for (i = 0; i < n_buckets; i++)
200 {
201 chain_offset = GET_UINT32 (cache->buffer, hash_offset + 4 + 4 * i);
202 while (chain_offset != 0xffffffff)
203 {
204 guint32 flags = 0;
205
206 image_list_offset = GET_UINT32 (cache->buffer, chain_offset + 8);
207 n_images = GET_UINT32 (cache->buffer, image_list_offset);
208
209 for (j = 0; j < n_images; j++)
210 {
211 if (GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j) ==
212 directory_index)
213 {
214 flags = GET_UINT16 (cache->buffer, image_list_offset + 4 + 8 * j + 2);
215 break;
216 }
217 }
218
219 if (flags != 0)
220 {
221 guint32 name_offset = GET_UINT32 (cache->buffer, chain_offset + 4);
222 const char *name = cache->buffer + name_offset;
223 const char *interned_name;
224 guint32 hash_flags = 0;
225
226 /* Icons named foo.symbolic.png are stored in the cache as "foo.symbolic" with ICON_CACHE_FLAG_PNG,
227 * but we convert it internally to ICON_CACHE_FLAG_SYMBOLIC_PNG.
228 * Otherwise we use the same enum values and names as on disk. */
229 if (g_str_has_suffix (str: name, suffix: ".symbolic") && (flags & ICON_CACHE_FLAG_PNG_SUFFIX) != 0)
230 {
231 char *converted_name = g_strndup (str: name, n: strlen (s: name) - 9);
232 interned_name = gtk_string_set_add (set, string: converted_name);
233 g_free (mem: converted_name);
234 flags |= ICON_CACHE_FLAG_SYMBOLIC_PNG_SUFFIX;
235 flags &= ~ICON_CACHE_FLAG_PNG_SUFFIX;
236 }
237 else
238 interned_name = gtk_string_set_add (set, string: name);
239
240 if (!icons)
241 icons = g_hash_table_new_full (hash_func: g_direct_hash, key_equal_func: g_direct_equal, NULL, NULL);
242
243 hash_flags = GPOINTER_TO_INT (g_hash_table_lookup (icons, interned_name));
244 g_hash_table_replace (hash_table: icons, key: (char *)interned_name, GUINT_TO_POINTER (hash_flags|flags));
245 }
246
247 chain_offset = GET_UINT32 (cache->buffer, chain_offset);
248 }
249 }
250
251 return icons;
252}
253

source code of gtk/gtk/gtkiconcache.c