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