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 | |
46 | struct _GtkIconCache { |
47 | int ref_count; |
48 | |
49 | GMappedFile *map; |
50 | char *buffer; |
51 | |
52 | guint32 last_chain_offset; |
53 | }; |
54 | |
55 | GtkIconCache * |
56 | gtk_icon_cache_ref (GtkIconCache *cache) |
57 | { |
58 | cache->ref_count++; |
59 | return cache; |
60 | } |
61 | |
62 | void |
63 | gtk_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 | |
77 | GtkIconCache * |
78 | gtk_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 (path: path, statbuf: &path_st) < 0) |
93 | goto done; |
94 | |
95 | if (g_stat (path: cache_filename, statbuf: &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 | |
143 | GtkIconCache * |
144 | gtk_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 | |
156 | static int |
157 | get_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 | |
179 | GHashTable * |
180 | gtk_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 | |