1 /* GConf
2  * Copyright (C) 1999, 2000 Red Hat Inc.
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, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #include <config.h>
21 #include "gconf-changeset.h"
22 #include "gconf-internals.h"
23 
24 typedef enum {
25   CHANGE_INVALID,
26   CHANGE_SET,
27   CHANGE_UNSET
28 } ChangeType;
29 
30 typedef struct _Change Change;
31 
32 struct _Change {
33   gchar* key;
34   ChangeType type;
35   GConfValue* value;
36 };
37 
38 static Change* change_new    (const gchar* key);
39 static void    change_set    (Change* c, GConfValue* value);
40 static void    change_unset  (Change* c);
41 static void    change_destroy(Change* c);
42 
43 struct _GConfChangeSet {
44   guint refcount;
45   GHashTable* hash;
46   gint in_foreach;
47   gpointer user_data;
48   GDestroyNotify dnotify;
49 };
50 
51 GType
gconf_change_set_get_type(void)52 gconf_change_set_get_type (void)
53 {
54   static GType our_type = 0;
55 
56   if (our_type == 0)
57     our_type = g_boxed_type_register_static ("GConfChangeSet",
58 					     (GBoxedCopyFunc) gconf_change_set_ref,
59 					     (GBoxedFreeFunc) gconf_change_set_unref);
60 
61   return our_type;
62 }
63 
64 GConfChangeSet*
gconf_change_set_new(void)65 gconf_change_set_new      (void)
66 {
67   GConfChangeSet* cs;
68 
69   cs = g_new(GConfChangeSet, 1);
70 
71   cs->refcount = 1;
72   cs->hash = g_hash_table_new(g_str_hash, g_str_equal);
73   cs->in_foreach = 0;
74   cs->user_data = NULL;
75   cs->dnotify = NULL;
76 
77   return cs;
78 }
79 
80 GConfChangeSet*
gconf_change_set_ref(GConfChangeSet * cs)81 gconf_change_set_ref      (GConfChangeSet* cs)
82 {
83   g_return_val_if_fail(cs != NULL, NULL);
84 
85   cs->refcount += 1;
86 
87   return cs;
88 }
89 
90 void
gconf_change_set_unref(GConfChangeSet * cs)91 gconf_change_set_unref    (GConfChangeSet* cs)
92 {
93   g_return_if_fail(cs != NULL);
94   g_return_if_fail(cs->refcount > 0);
95 
96   cs->refcount -= 1;
97 
98   if (cs->refcount == 0)
99     {
100       if (cs->in_foreach > 0)
101         g_warning("GConfChangeSet refcount reduced to 0 during a foreach");
102 
103       gconf_change_set_clear(cs);
104 
105       g_hash_table_destroy(cs->hash);
106 
107       g_free(cs);
108     }
109 }
110 
111 /**
112  * gconf_change_set_set_user_data: (skip)
113  * @cs: a #GConfChangeSet.
114  * @data: a #gpointer.
115  * @dnotify: a pointer to the function to be called during destroy.
116  *
117  * Sets the user_data and the destroy notification function fields of the
118  * #GConfChangeSet.
119  */
120 void
gconf_change_set_set_user_data(GConfChangeSet * cs,gpointer data,GDestroyNotify dnotify)121 gconf_change_set_set_user_data (GConfChangeSet *cs,
122                                 gpointer        data,
123                                 GDestroyNotify  dnotify)
124 {
125   if (cs->dnotify)
126     (* cs->dnotify) (cs->user_data);
127 
128   cs->user_data = data;
129   cs->dnotify = dnotify;
130 }
131 
132 /**
133  * gconf_change_set_get_user_data: (skip)
134  * @cs: a #GConfChangeSet.
135  *
136  * Returns the user_data field of the #GConfChangeSet.
137  *
138  * Return value: a pointer to the user_data.
139  */
140 gpointer
gconf_change_set_get_user_data(GConfChangeSet * cs)141 gconf_change_set_get_user_data (GConfChangeSet *cs)
142 {
143   return cs->user_data;
144 }
145 
146 static Change*
get_change_unconditional(GConfChangeSet * cs,const gchar * key)147 get_change_unconditional (GConfChangeSet* cs,
148                           const gchar* key)
149 {
150   Change* c;
151 
152   c = g_hash_table_lookup(cs->hash, key);
153 
154   if (c == NULL)
155     {
156       c = change_new(key);
157 
158       g_hash_table_insert(cs->hash, c->key, c);
159     }
160 
161   return c;
162 }
163 
164 static gboolean
destroy_foreach(gpointer key,gpointer value,gpointer user_data)165 destroy_foreach (gpointer key, gpointer value, gpointer user_data)
166 {
167   Change* c = value;
168 
169   g_assert(c != NULL);
170 
171   change_destroy(c);
172 
173   return TRUE; /* remove from hash */
174 }
175 
176 void
gconf_change_set_clear(GConfChangeSet * cs)177 gconf_change_set_clear    (GConfChangeSet* cs)
178 {
179   g_return_if_fail(cs != NULL);
180 
181   g_hash_table_foreach_remove (cs->hash, destroy_foreach, NULL);
182 }
183 
184 guint
gconf_change_set_size(GConfChangeSet * cs)185 gconf_change_set_size     (GConfChangeSet* cs)
186 {
187   g_return_val_if_fail(cs != NULL, 0);
188 
189   return g_hash_table_size(cs->hash);
190 }
191 
192 void
gconf_change_set_remove(GConfChangeSet * cs,const gchar * key)193 gconf_change_set_remove   (GConfChangeSet* cs,
194                            const gchar* key)
195 {
196   Change* c;
197 
198   g_return_if_fail(cs != NULL);
199   g_return_if_fail(cs->in_foreach == 0);
200 
201   c = g_hash_table_lookup(cs->hash, key);
202 
203   if (c != NULL)
204     {
205       g_hash_table_remove(cs->hash, c->key);
206       change_destroy(c);
207     }
208 }
209 
210 
211 struct ForeachData {
212   GConfChangeSet* cs;
213   GConfChangeSetForeachFunc func;
214   gpointer user_data;
215 };
216 
217 static void
foreach(gpointer key,gpointer value,gpointer user_data)218 foreach(gpointer key, gpointer value, gpointer user_data)
219 {
220   Change* c;
221   struct ForeachData* fd = user_data;
222 
223   c = value;
224 
225   /* assumes that an UNSET change has a NULL value */
226   (* fd->func) (fd->cs, c->key, c->value, fd->user_data);
227 }
228 
229 /**
230  * gconf_change_set_foreach:
231  * @cs: a #GConfChangeSet.
232  * @func: (scope call): function to call for each change in the change set.
233  * @user_data: user data to pass to the #GConfChangeSetForeachFunc.
234  *
235  * Iterates over a #GConfChangeSet by calling a
236  * #GConfChangeSetForeachFunc for each change in the set. See the
237  * description of #GConfChangeSetForeachFunc for details.  You may not
238  * call gconf_change_set_remove() during the iteration, because you'll
239  * confuse the internal data structures and cause memory corruption.
240  */
241 void
gconf_change_set_foreach(GConfChangeSet * cs,GConfChangeSetForeachFunc func,gpointer user_data)242 gconf_change_set_foreach  (GConfChangeSet* cs,
243                            GConfChangeSetForeachFunc func,
244                            gpointer user_data)
245 {
246   struct ForeachData fd;
247 
248   g_return_if_fail(cs != NULL);
249   g_return_if_fail(func != NULL);
250 
251   fd.cs = cs;
252   fd.func = func;
253   fd.user_data = user_data;
254 
255   gconf_change_set_ref(cs);
256 
257   cs->in_foreach += 1;
258 
259   g_hash_table_foreach(cs->hash, foreach, &fd);
260 
261   cs->in_foreach -= 1;
262 
263   gconf_change_set_unref(cs);
264 }
265 
266 gboolean
gconf_change_set_check_value(GConfChangeSet * cs,const gchar * key,GConfValue ** value_retloc)267 gconf_change_set_check_value   (GConfChangeSet* cs, const gchar* key,
268                                 GConfValue** value_retloc)
269 {
270   Change* c;
271 
272   g_return_val_if_fail(cs != NULL, FALSE);
273 
274   c = g_hash_table_lookup(cs->hash, key);
275 
276   if (c == NULL)
277     return FALSE;
278   else
279     {
280       if (value_retloc != NULL)
281         *value_retloc = c->value;
282 
283       return TRUE;
284     }
285 }
286 
287 void
gconf_change_set_set_nocopy(GConfChangeSet * cs,const gchar * key,GConfValue * value)288 gconf_change_set_set_nocopy  (GConfChangeSet* cs, const gchar* key,
289                               GConfValue* value)
290 {
291   Change* c;
292 
293   g_return_if_fail(cs != NULL);
294   g_return_if_fail(value != NULL);
295 
296   c = get_change_unconditional(cs, key);
297 
298   change_set(c, value);
299 }
300 
301 void
gconf_change_set_set(GConfChangeSet * cs,const gchar * key,GConfValue * value)302 gconf_change_set_set (GConfChangeSet* cs, const gchar* key,
303                       GConfValue* value)
304 {
305   g_return_if_fail(value != NULL);
306 
307   gconf_change_set_set_nocopy(cs, key, gconf_value_copy(value));
308 }
309 
310 void
gconf_change_set_unset(GConfChangeSet * cs,const gchar * key)311 gconf_change_set_unset      (GConfChangeSet* cs, const gchar* key)
312 {
313   Change* c;
314 
315   g_return_if_fail(cs != NULL);
316 
317   c = get_change_unconditional(cs, key);
318 
319   change_unset(c);
320 }
321 
322 void
gconf_change_set_set_float(GConfChangeSet * cs,const gchar * key,gdouble val)323 gconf_change_set_set_float   (GConfChangeSet* cs, const gchar* key,
324                               gdouble val)
325 {
326   GConfValue* value;
327 
328   g_return_if_fail(cs != NULL);
329 
330   value = gconf_value_new(GCONF_VALUE_FLOAT);
331   gconf_value_set_float(value, val);
332 
333   gconf_change_set_set_nocopy(cs, key, value);
334 }
335 
336 void
gconf_change_set_set_int(GConfChangeSet * cs,const gchar * key,gint val)337 gconf_change_set_set_int     (GConfChangeSet* cs, const gchar* key,
338                               gint val)
339 {
340   GConfValue* value;
341 
342   g_return_if_fail(cs != NULL);
343 
344   value = gconf_value_new(GCONF_VALUE_INT);
345   gconf_value_set_int(value, val);
346 
347   gconf_change_set_set_nocopy(cs, key, value);
348 }
349 
350 void
gconf_change_set_set_string(GConfChangeSet * cs,const gchar * key,const gchar * val)351 gconf_change_set_set_string  (GConfChangeSet* cs, const gchar* key,
352                               const gchar* val)
353 {
354   GConfValue* value;
355 
356   g_return_if_fail(cs != NULL);
357   g_return_if_fail(key != NULL);
358   g_return_if_fail(val != NULL);
359 
360   value = gconf_value_new(GCONF_VALUE_STRING);
361   gconf_value_set_string(value, val);
362 
363   gconf_change_set_set_nocopy(cs, key, value);
364 }
365 
366 void
gconf_change_set_set_bool(GConfChangeSet * cs,const gchar * key,gboolean val)367 gconf_change_set_set_bool    (GConfChangeSet* cs, const gchar* key,
368                               gboolean val)
369 {
370   GConfValue* value;
371 
372   g_return_if_fail(cs != NULL);
373 
374   value = gconf_value_new(GCONF_VALUE_BOOL);
375   gconf_value_set_bool(value, val);
376 
377   gconf_change_set_set_nocopy(cs, key, value);
378 }
379 
380 void
gconf_change_set_set_schema(GConfChangeSet * cs,const gchar * key,GConfSchema * val)381 gconf_change_set_set_schema  (GConfChangeSet* cs, const gchar* key,
382                               GConfSchema* val)
383 {
384   GConfValue* value;
385 
386   g_return_if_fail(cs != NULL);
387 
388   value = gconf_value_new(GCONF_VALUE_SCHEMA);
389   gconf_value_set_schema(value, val);
390 
391   gconf_change_set_set_nocopy(cs, key, value);
392 }
393 
394 void
gconf_change_set_set_list(GConfChangeSet * cs,const gchar * key,GConfValueType list_type,GSList * list)395 gconf_change_set_set_list    (GConfChangeSet* cs, const gchar* key,
396                               GConfValueType list_type,
397                               GSList* list)
398 {
399   GConfValue* value_list;
400 
401   g_return_if_fail(cs != NULL);
402   g_return_if_fail(key != NULL);
403   g_return_if_fail(list_type != GCONF_VALUE_INVALID);
404   g_return_if_fail(list_type != GCONF_VALUE_LIST);
405   g_return_if_fail(list_type != GCONF_VALUE_PAIR);
406 
407   value_list = gconf_value_list_from_primitive_list (list_type, list, NULL);
408 
409   gconf_change_set_set_nocopy(cs, key, value_list);
410 }
411 
412 
413 void
gconf_change_set_set_pair(GConfChangeSet * cs,const gchar * key,GConfValueType car_type,GConfValueType cdr_type,gconstpointer address_of_car,gconstpointer address_of_cdr)414 gconf_change_set_set_pair    (GConfChangeSet* cs, const gchar* key,
415                               GConfValueType car_type, GConfValueType cdr_type,
416                               gconstpointer address_of_car,
417                               gconstpointer address_of_cdr)
418 {
419   GConfValue* pair;
420 
421   g_return_if_fail(cs != NULL);
422   g_return_if_fail(key != NULL);
423   g_return_if_fail(car_type != GCONF_VALUE_INVALID);
424   g_return_if_fail(car_type != GCONF_VALUE_LIST);
425   g_return_if_fail(car_type != GCONF_VALUE_PAIR);
426   g_return_if_fail(cdr_type != GCONF_VALUE_INVALID);
427   g_return_if_fail(cdr_type != GCONF_VALUE_LIST);
428   g_return_if_fail(cdr_type != GCONF_VALUE_PAIR);
429   g_return_if_fail(address_of_car != NULL);
430   g_return_if_fail(address_of_cdr != NULL);
431 
432   pair = gconf_value_pair_from_primitive_pair (car_type, cdr_type,
433                                                address_of_car, address_of_cdr,
434                                                NULL);
435 
436   gconf_change_set_set_nocopy(cs, key, pair);
437 }
438 
439 
440 /*
441  * Change
442  */
443 
444 static Change*
change_new(const gchar * key)445 change_new    (const gchar* key)
446 {
447   Change* c;
448 
449   c = g_new(Change, 1);
450 
451   c->key  = g_strdup(key);
452   c->type = CHANGE_INVALID;
453   c->value = NULL;
454 
455   return c;
456 }
457 
458 static void
change_destroy(Change * c)459 change_destroy(Change* c)
460 {
461   g_return_if_fail(c != NULL);
462 
463   g_free(c->key);
464 
465   if (c->value)
466     gconf_value_free(c->value);
467 
468   g_free(c);
469 }
470 
471 static void
change_set(Change * c,GConfValue * value)472 change_set    (Change* c, GConfValue* value)
473 {
474   g_return_if_fail(value == NULL ||
475                    GCONF_VALUE_TYPE_VALID(value->type));
476 
477   c->type = CHANGE_SET;
478 
479   if (value == c->value)
480     return;
481 
482   if (c->value)
483     gconf_value_free(c->value);
484 
485   c->value = value;
486 }
487 
488 static void
change_unset(Change * c)489 change_unset  (Change* c)
490 {
491   c->type = CHANGE_UNSET;
492 
493   if (c->value)
494     gconf_value_free(c->value);
495 
496   c->value = NULL;
497 }
498 
499 /*
500  * Actually send it upstream
501  */
502 
503 struct CommitData {
504   GConfEngine* conf;
505   GError* error;
506   GSList* remove_list;
507   gboolean remove_committed;
508 };
509 
510 static void
commit_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)511 commit_foreach (GConfChangeSet* cs,
512                 const gchar* key,
513                 GConfValue* value,
514                 gpointer user_data)
515 {
516   struct CommitData* cd = user_data;
517 
518   g_assert(cd != NULL);
519 
520   if (cd->error != NULL)
521     return;
522 
523   if (value)
524     gconf_engine_set   (cd->conf, key, value, &cd->error);
525   else
526     gconf_engine_unset (cd->conf, key, &cd->error);
527 
528   if (cd->error == NULL && cd->remove_committed)
529     {
530       /* Bad bad bad; we keep the key reference, knowing that it's
531          valid until we modify the change set, to avoid string copies.  */
532       cd->remove_list = g_slist_prepend(cd->remove_list, (gchar*)key);
533     }
534 }
535 
536 gboolean
gconf_engine_commit_change_set(GConfEngine * conf,GConfChangeSet * cs,gboolean remove_committed,GError ** err)537 gconf_engine_commit_change_set   (GConfEngine* conf,
538                            GConfChangeSet* cs,
539                            gboolean remove_committed,
540                            GError** err)
541 {
542   struct CommitData cd;
543   GSList* tmp;
544 
545   g_return_val_if_fail(conf != NULL, FALSE);
546   g_return_val_if_fail(cs != NULL, FALSE);
547   g_return_val_if_fail(err == NULL || *err == NULL, FALSE);
548 
549   cd.conf = conf;
550   cd.error = NULL;
551   cd.remove_list = NULL;
552   cd.remove_committed = remove_committed;
553 
554   /* Because the commit could have lots of side
555      effects, this makes it safer */
556   gconf_change_set_ref(cs);
557   gconf_engine_ref(conf);
558 
559   gconf_change_set_foreach(cs, commit_foreach, &cd);
560 
561   tmp = cd.remove_list;
562   while (tmp != NULL)
563     {
564       const gchar* key = tmp->data;
565 
566       gconf_change_set_remove(cs, key);
567 
568       /* key is now invalid due to our little evil trick */
569 
570       tmp = g_slist_next(tmp);
571     }
572 
573   g_slist_free(cd.remove_list);
574 
575   gconf_change_set_unref(cs);
576   gconf_engine_unref(conf);
577 
578   if (cd.error != NULL)
579     {
580       if (err != NULL)
581         *err = cd.error;
582       else
583         g_error_free(cd.error);
584 
585       return FALSE;
586     }
587   else
588     {
589       return TRUE;
590     }
591 }
592 
593 struct RevertData {
594   GConfEngine* conf;
595   GError* error;
596   GConfChangeSet* revert_set;
597 };
598 
599 static void
revert_foreach(GConfChangeSet * cs,const gchar * key,GConfValue * value,gpointer user_data)600 revert_foreach (GConfChangeSet* cs,
601                 const gchar* key,
602                 GConfValue* value,
603                 gpointer user_data)
604 {
605   struct RevertData* rd = user_data;
606   GConfValue* old_value;
607   GError* error = NULL;
608 
609   g_assert(rd != NULL);
610 
611   if (rd->error != NULL)
612     return;
613 
614   old_value = gconf_engine_get_without_default(rd->conf, key, &error);
615 
616   if (error != NULL)
617     {
618       /* FIXME */
619       g_warning("error creating revert set: %s", error->message);
620       g_error_free(error);
621       error = NULL;
622     }
623 
624   if (old_value == NULL &&
625       value == NULL)
626     return; /* this commit will have no effect. */
627 
628   if (old_value == NULL)
629     gconf_change_set_unset(rd->revert_set, key);
630   else
631     gconf_change_set_set_nocopy(rd->revert_set, key, old_value);
632 }
633 
634 
635 GConfChangeSet*
gconf_engine_reverse_change_set(GConfEngine * conf,GConfChangeSet * cs,GError ** err)636 gconf_engine_reverse_change_set  (GConfEngine* conf,
637                                   GConfChangeSet* cs,
638                                   GError** err)
639 {
640   struct RevertData rd;
641 
642   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
643 
644   rd.error = NULL;
645   rd.conf = conf;
646   rd.revert_set = gconf_change_set_new();
647 
648   gconf_change_set_foreach(cs, revert_foreach, &rd);
649 
650   if (rd.error != NULL)
651     {
652       if (err != NULL)
653         *err = rd.error;
654       else
655         g_error_free(rd.error);
656     }
657 
658   return rd.revert_set;
659 }
660 
661 GConfChangeSet*
gconf_engine_change_set_from_currentv(GConfEngine * conf,const gchar ** keys,GError ** err)662 gconf_engine_change_set_from_currentv (GConfEngine* conf,
663                                        const gchar** keys,
664                                        GError** err)
665 {
666   GConfValue* old_value;
667   GConfChangeSet* new_set;
668   const gchar** keyp;
669 
670   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
671 
672   new_set = gconf_change_set_new();
673 
674   keyp = keys;
675 
676   while (*keyp != NULL)
677     {
678       GError* error = NULL;
679       const gchar* key = *keyp;
680 
681       old_value = gconf_engine_get_without_default(conf, key, &error);
682 
683       if (error != NULL)
684         {
685           /* FIXME */
686           g_warning("error creating change set from current keys: %s", error->message);
687           g_error_free(error);
688           error = NULL;
689         }
690 
691       if (old_value == NULL)
692         gconf_change_set_unset(new_set, key);
693       else
694         gconf_change_set_set_nocopy(new_set, key, old_value);
695 
696       ++keyp;
697     }
698 
699   return new_set;
700 }
701 
702 GConfChangeSet*
gconf_engine_change_set_from_current(GConfEngine * conf,GError ** err,const gchar * first_key,...)703 gconf_engine_change_set_from_current (GConfEngine* conf,
704                                       GError** err,
705                                       const gchar* first_key,
706                                       ...)
707 {
708   GSList* keys = NULL;
709   va_list args;
710   const gchar* arg;
711   const gchar** vec;
712   GConfChangeSet* retval;
713   GSList* tmp;
714   guint i;
715 
716   g_return_val_if_fail(err == NULL || *err == NULL, NULL);
717 
718   va_start (args, first_key);
719 
720   arg = first_key;
721 
722   while (arg != NULL)
723     {
724       keys = g_slist_prepend(keys, (/*not-const*/gchar*)arg);
725 
726       arg = va_arg (args, const gchar*);
727     }
728 
729   va_end (args);
730 
731   vec = g_new0(const gchar*, g_slist_length(keys) + 1);
732 
733   i = 0;
734   tmp = keys;
735 
736   while (tmp != NULL)
737     {
738       vec[i] = tmp->data;
739 
740       ++i;
741       tmp = g_slist_next(tmp);
742     }
743 
744   g_slist_free(keys);
745 
746   retval = gconf_engine_change_set_from_currentv(conf, vec, err);
747 
748   g_free(vec);
749 
750   return retval;
751 }
752