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, &parasite_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, &parasite_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, &parasite_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                                                  &parasite_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