1 /* parasitelist.c: Copyright 1998 Jay Cox <jaycox@gimp.org>
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include "config.h"
18
19 #include <string.h>
20
21 #include <gio/gio.h>
22 #include <gegl.h>
23
24 #include "libgimpbase/gimpbase.h"
25 #include "libgimpconfig/gimpconfig.h"
26
27 #include "core-types.h"
28
29 #include "gimp-memsize.h"
30 #include "gimpmarshal.h"
31 #include "gimpparasitelist.h"
32
33
34 enum
35 {
36 ADD,
37 REMOVE,
38 LAST_SIGNAL
39 };
40
41
42 static void gimp_parasite_list_finalize (GObject *object);
43 static gint64 gimp_parasite_list_get_memsize (GimpObject *object,
44 gint64 *gui_size);
45
46 static void gimp_parasite_list_config_iface_init (gpointer iface,
47 gpointer iface_data);
48 static gboolean gimp_parasite_list_serialize (GimpConfig *list,
49 GimpConfigWriter *writer,
50 gpointer data);
51 static gboolean gimp_parasite_list_deserialize (GimpConfig *list,
52 GScanner *scanner,
53 gint nest_level,
54 gpointer data);
55
56 static void parasite_serialize (const gchar *key,
57 GimpParasite *parasite,
58 GimpConfigWriter *writer);
59 static void parasite_copy (const gchar *key,
60 GimpParasite *parasite,
61 GimpParasiteList *list);
62 static gboolean parasite_free (const gchar *key,
63 GimpParasite *parasite,
64 gpointer unused);
65 static void parasite_count_if_persistent (const gchar *key,
66 GimpParasite *parasite,
67 gint *count);
68
69
70 G_DEFINE_TYPE_WITH_CODE (GimpParasiteList, gimp_parasite_list, GIMP_TYPE_OBJECT,
71 G_IMPLEMENT_INTERFACE (GIMP_TYPE_CONFIG,
72 gimp_parasite_list_config_iface_init))
73
74 #define parent_class gimp_parasite_list_parent_class
75
76 static guint parasite_list_signals[LAST_SIGNAL] = { 0 };
77 static const gchar parasite_symbol[] = "parasite";
78
79
80 static void
gimp_parasite_list_class_init(GimpParasiteListClass * klass)81 gimp_parasite_list_class_init (GimpParasiteListClass *klass)
82 {
83 GObjectClass *object_class = G_OBJECT_CLASS (klass);
84 GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
85
86 parasite_list_signals[ADD] =
87 g_signal_new ("add",
88 G_TYPE_FROM_CLASS (klass),
89 G_SIGNAL_RUN_FIRST,
90 G_STRUCT_OFFSET (GimpParasiteListClass, add),
91 NULL, NULL,
92 gimp_marshal_VOID__POINTER,
93 G_TYPE_NONE, 1,
94 G_TYPE_POINTER);
95
96 parasite_list_signals[REMOVE] =
97 g_signal_new ("remove",
98 G_TYPE_FROM_CLASS (klass),
99 G_SIGNAL_RUN_FIRST,
100 G_STRUCT_OFFSET (GimpParasiteListClass, remove),
101 NULL, NULL,
102 gimp_marshal_VOID__POINTER,
103 G_TYPE_NONE, 1,
104 G_TYPE_POINTER);
105
106 object_class->finalize = gimp_parasite_list_finalize;
107
108 gimp_object_class->get_memsize = gimp_parasite_list_get_memsize;
109
110 klass->add = NULL;
111 klass->remove = NULL;
112 }
113
114 static void
gimp_parasite_list_config_iface_init(gpointer iface,gpointer iface_data)115 gimp_parasite_list_config_iface_init (gpointer iface,
116 gpointer iface_data)
117 {
118 GimpConfigInterface *config_iface = (GimpConfigInterface *) iface;
119
120 config_iface->serialize = gimp_parasite_list_serialize;
121 config_iface->deserialize = gimp_parasite_list_deserialize;
122 }
123
124 static void
gimp_parasite_list_init(GimpParasiteList * list)125 gimp_parasite_list_init (GimpParasiteList *list)
126 {
127 list->table = NULL;
128 }
129
130 static void
gimp_parasite_list_finalize(GObject * object)131 gimp_parasite_list_finalize (GObject *object)
132 {
133 GimpParasiteList *list = GIMP_PARASITE_LIST (object);
134
135 if (list->table)
136 {
137 g_hash_table_foreach_remove (list->table, (GHRFunc) parasite_free, NULL);
138 g_hash_table_destroy (list->table);
139 list->table = NULL;
140 }
141
142 G_OBJECT_CLASS (parent_class)->finalize (object);
143 }
144
145 static gint64
gimp_parasite_list_get_memsize(GimpObject * object,gint64 * gui_size)146 gimp_parasite_list_get_memsize (GimpObject *object,
147 gint64 *gui_size)
148 {
149 GimpParasiteList *list = GIMP_PARASITE_LIST (object);
150 gint64 memsize = 0;
151
152 memsize += gimp_g_hash_table_get_memsize_foreach (list->table,
153 (GimpMemsizeFunc)
154 gimp_parasite_get_memsize,
155 gui_size);
156
157 return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
158 gui_size);
159 }
160
161 static gboolean
gimp_parasite_list_serialize(GimpConfig * list,GimpConfigWriter * writer,gpointer data)162 gimp_parasite_list_serialize (GimpConfig *list,
163 GimpConfigWriter *writer,
164 gpointer data)
165 {
166 if (GIMP_PARASITE_LIST (list)->table)
167 g_hash_table_foreach (GIMP_PARASITE_LIST (list)->table,
168 (GHFunc) parasite_serialize,
169 writer);
170
171 return TRUE;
172 }
173
174 static gboolean
gimp_parasite_list_deserialize(GimpConfig * list,GScanner * scanner,gint nest_level,gpointer data)175 gimp_parasite_list_deserialize (GimpConfig *list,
176 GScanner *scanner,
177 gint nest_level,
178 gpointer data)
179 {
180 GTokenType token;
181
182 g_scanner_scope_add_symbol (scanner, 0,
183 parasite_symbol, (gpointer) parasite_symbol);
184
185 token = G_TOKEN_LEFT_PAREN;
186
187 while (g_scanner_peek_next_token (scanner) == token)
188 {
189 token = g_scanner_get_next_token (scanner);
190
191 switch (token)
192 {
193 case G_TOKEN_LEFT_PAREN:
194 token = G_TOKEN_SYMBOL;
195 break;
196
197 case G_TOKEN_SYMBOL:
198 if (scanner->value.v_symbol == parasite_symbol)
199 {
200 gchar *parasite_name = NULL;
201 gint parasite_flags = 0;
202 guint8 *parasite_data = NULL;
203 gint parasite_data_size = 0;
204 GimpParasite *parasite;
205
206 token = G_TOKEN_STRING;
207
208 if (g_scanner_peek_next_token (scanner) != token)
209 break;
210
211 if (! gimp_scanner_parse_string (scanner, ¶site_name))
212 break;
213
214 token = G_TOKEN_INT;
215
216 if (g_scanner_peek_next_token (scanner) != token)
217 goto cleanup;
218
219 if (! gimp_scanner_parse_int (scanner, ¶site_flags))
220 goto cleanup;
221
222 token = G_TOKEN_INT;
223
224 if (g_scanner_peek_next_token (scanner) != token)
225 {
226 /* old format -- plain string */
227
228 gchar *str;
229
230 if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
231 goto cleanup;
232
233 if (! gimp_scanner_parse_string (scanner, &str))
234 goto cleanup;
235
236 parasite_data_size = strlen (str);
237 parasite_data = (guint8 *) str;
238 }
239 else
240 {
241 /* new format -- properly encoded binary data */
242
243 if (! gimp_scanner_parse_int (scanner, ¶site_data_size))
244 goto cleanup;
245
246 token = G_TOKEN_STRING;
247
248 if (g_scanner_peek_next_token (scanner) != token)
249 goto cleanup;
250
251 if (! gimp_scanner_parse_data (scanner, parasite_data_size,
252 ¶site_data))
253 goto cleanup;
254 }
255
256 parasite = gimp_parasite_new (parasite_name,
257 parasite_flags,
258 parasite_data_size,
259 parasite_data);
260 gimp_parasite_list_add (GIMP_PARASITE_LIST (list),
261 parasite); /* adds a copy */
262 gimp_parasite_free (parasite);
263
264 token = G_TOKEN_RIGHT_PAREN;
265
266 g_free (parasite_data);
267 cleanup:
268 g_free (parasite_name);
269 }
270 break;
271
272 case G_TOKEN_RIGHT_PAREN:
273 token = G_TOKEN_LEFT_PAREN;
274 break;
275
276 default: /* do nothing */
277 break;
278 }
279 }
280
281 return gimp_config_deserialize_return (scanner, token, nest_level);
282 }
283
284 GimpParasiteList *
gimp_parasite_list_new(void)285 gimp_parasite_list_new (void)
286 {
287 GimpParasiteList *list;
288
289 list = g_object_new (GIMP_TYPE_PARASITE_LIST, NULL);
290
291 return list;
292 }
293
294 GimpParasiteList *
gimp_parasite_list_copy(GimpParasiteList * list)295 gimp_parasite_list_copy (GimpParasiteList *list)
296 {
297 GimpParasiteList *newlist;
298
299 g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), NULL);
300
301 newlist = gimp_parasite_list_new ();
302
303 if (list->table)
304 g_hash_table_foreach (list->table, (GHFunc) parasite_copy, newlist);
305
306 return newlist;
307 }
308
309 void
gimp_parasite_list_add(GimpParasiteList * list,const GimpParasite * parasite)310 gimp_parasite_list_add (GimpParasiteList *list,
311 const GimpParasite *parasite)
312 {
313 GimpParasite *copy;
314
315 g_return_if_fail (GIMP_IS_PARASITE_LIST (list));
316 g_return_if_fail (parasite != NULL);
317 g_return_if_fail (parasite->name != NULL);
318
319 if (list->table == NULL)
320 list->table = g_hash_table_new (g_str_hash, g_str_equal);
321
322 gimp_parasite_list_remove (list, parasite->name);
323 copy = gimp_parasite_copy (parasite);
324 g_hash_table_insert (list->table, copy->name, copy);
325
326 g_signal_emit (list, parasite_list_signals[ADD], 0, copy);
327 }
328
329 void
gimp_parasite_list_remove(GimpParasiteList * list,const gchar * name)330 gimp_parasite_list_remove (GimpParasiteList *list,
331 const gchar *name)
332 {
333 g_return_if_fail (GIMP_IS_PARASITE_LIST (list));
334
335 if (list->table)
336 {
337 GimpParasite *parasite;
338
339 parasite = (GimpParasite *) gimp_parasite_list_find (list, name);
340
341 if (parasite)
342 {
343 g_hash_table_remove (list->table, name);
344
345 g_signal_emit (list, parasite_list_signals[REMOVE], 0, parasite);
346
347 gimp_parasite_free (parasite);
348 }
349 }
350 }
351
352 gint
gimp_parasite_list_length(GimpParasiteList * list)353 gimp_parasite_list_length (GimpParasiteList *list)
354 {
355 g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), 0);
356
357 if (! list->table)
358 return 0;
359
360 return g_hash_table_size (list->table);
361 }
362
363 gint
gimp_parasite_list_persistent_length(GimpParasiteList * list)364 gimp_parasite_list_persistent_length (GimpParasiteList *list)
365 {
366 gint len = 0;
367
368 g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), 0);
369
370 if (! list->table)
371 return 0;
372
373 gimp_parasite_list_foreach (list,
374 (GHFunc) parasite_count_if_persistent, &len);
375
376 return len;
377 }
378
379 void
gimp_parasite_list_foreach(GimpParasiteList * list,GHFunc function,gpointer user_data)380 gimp_parasite_list_foreach (GimpParasiteList *list,
381 GHFunc function,
382 gpointer user_data)
383 {
384 g_return_if_fail (GIMP_IS_PARASITE_LIST (list));
385
386 if (! list->table)
387 return;
388
389 g_hash_table_foreach (list->table, function, user_data);
390 }
391
392 const GimpParasite *
gimp_parasite_list_find(GimpParasiteList * list,const gchar * name)393 gimp_parasite_list_find (GimpParasiteList *list,
394 const gchar *name)
395 {
396 g_return_val_if_fail (GIMP_IS_PARASITE_LIST (list), NULL);
397
398 if (list->table)
399 return (GimpParasite *) g_hash_table_lookup (list->table, name);
400
401 return NULL;
402 }
403
404
405 static void
parasite_serialize(const gchar * key,GimpParasite * parasite,GimpConfigWriter * writer)406 parasite_serialize (const gchar *key,
407 GimpParasite *parasite,
408 GimpConfigWriter *writer)
409 {
410 if (! gimp_parasite_is_persistent (parasite))
411 return;
412
413 gimp_config_writer_open (writer, parasite_symbol);
414
415 gimp_config_writer_printf (writer, "\"%s\" %lu %lu",
416 gimp_parasite_name (parasite),
417 gimp_parasite_flags (parasite),
418 gimp_parasite_data_size (parasite));
419
420 gimp_config_writer_data (writer,
421 gimp_parasite_data_size (parasite),
422 gimp_parasite_data (parasite));
423
424 gimp_config_writer_close (writer);
425 gimp_config_writer_linefeed (writer);
426 }
427
428 static void
parasite_copy(const gchar * key,GimpParasite * parasite,GimpParasiteList * list)429 parasite_copy (const gchar *key,
430 GimpParasite *parasite,
431 GimpParasiteList *list)
432 {
433 gimp_parasite_list_add (list, parasite);
434 }
435
436 static gboolean
parasite_free(const gchar * key,GimpParasite * parasite,gpointer unused)437 parasite_free (const gchar *key,
438 GimpParasite *parasite,
439 gpointer unused)
440 {
441 gimp_parasite_free (parasite);
442
443 return TRUE;
444 }
445
446 static void
parasite_count_if_persistent(const gchar * key,GimpParasite * parasite,gint * count)447 parasite_count_if_persistent (const gchar *key,
448 GimpParasite *parasite,
449 gint *count)
450 {
451 if (gimp_parasite_is_persistent (parasite))
452 *count = *count + 1;
453 }
454