1 /* GConf
2 * Copyright (C) 2002 Red Hat Inc.
3 * Copyright (C) 2005 Mark McLoughlin
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include <config.h>
22 #include <glib.h>
23 #include "gconf/gconf-internals.h"
24 #include "gconf/gconf-schema.h"
25 #include "markup-tree.h"
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <unistd.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <time.h>
36
37 #ifdef G_OS_WIN32
38 #include <io.h>
39 #include <conio.h>
40 #define _WIN32_WINNT 0x0500
41 #include <windows.h>
42
43 static int
fsync(int fd)44 fsync (int fd)
45 {
46 HANDLE h = (HANDLE) _get_osfhandle (fd);
47 DWORD err;
48
49 if (h == INVALID_HANDLE_VALUE)
50 {
51 errno = EBADF;
52 return -1;
53 }
54
55 if (!FlushFileBuffers (h))
56 {
57 err = GetLastError ();
58 switch (err)
59 {
60 case ERROR_INVALID_HANDLE:
61 errno = EINVAL;
62 break;
63
64 default:
65 errno = EIO;
66 }
67 return -1;
68 }
69
70 return 0;
71 }
72 #endif
73
74 typedef struct
75 {
76 char *locale;
77 char *short_desc;
78 char *long_desc;
79 GConfValue *default_value;
80 } LocalSchemaInfo;
81
82 struct _MarkupEntry
83 {
84 MarkupDir *dir;
85 char *name;
86 GConfValue *value;
87 /* list of LocalSchemaInfo */
88 GSList *local_schemas;
89 char *schema_name;
90 char *mod_user;
91 GTime mod_time;
92 };
93
94 static LocalSchemaInfo* local_schema_info_new (void);
95 static void local_schema_info_free (LocalSchemaInfo *info);
96
97 static MarkupDir* markup_dir_new (MarkupTree *tree,
98 MarkupDir *parent,
99 const char *name);
100 static void markup_dir_free (MarkupDir *dir);
101 static gboolean markup_dir_needs_sync (MarkupDir *dir);
102 static gboolean markup_dir_sync (MarkupDir *dir);
103 static char* markup_dir_build_path (MarkupDir *dir,
104 gboolean filesystem_path,
105 gboolean with_data_file,
106 gboolean subtree_data_file,
107 const char *locale);
108 static void markup_dir_set_entries_need_save (MarkupDir *dir);
109 static void markup_dir_setup_as_subtree_root (MarkupDir *dir);
110
111 static MarkupEntry* markup_entry_new (MarkupDir *dir,
112 const char *name);
113 static void markup_entry_free (MarkupEntry *entry);
114
115 static void parse_tree (MarkupDir *root,
116 gboolean parse_subtree,
117 const char *locale,
118 GError **err);
119 static void save_tree (MarkupDir *root,
120 gboolean save_as_subtree,
121 guint file_mode,
122 GError **err);
123
124
125 struct _MarkupTree
126 {
127 char *dirname;
128 guint dir_mode;
129 guint file_mode;
130
131 MarkupDir *root;
132
133 guint refcount;
134
135 guint merged : 1;
136 };
137
138 static GHashTable *trees_by_root_dir = NULL;
139
140 MarkupTree*
markup_tree_get(const char * root_dir,guint dir_mode,guint file_mode,gboolean merged)141 markup_tree_get (const char *root_dir,
142 guint dir_mode,
143 guint file_mode,
144 gboolean merged)
145 {
146 MarkupTree *tree = NULL;
147
148 if (trees_by_root_dir == NULL)
149 trees_by_root_dir = g_hash_table_new (g_str_hash, g_str_equal);
150 else
151 tree = g_hash_table_lookup (trees_by_root_dir, root_dir);
152
153 if (tree != NULL)
154 {
155 tree->refcount += 1;
156 if (merged && !tree->merged)
157 tree->merged = TRUE;
158 return tree;
159 }
160
161 tree = g_new0 (MarkupTree, 1);
162
163 tree->dirname = g_strdup (root_dir);
164 tree->dir_mode = dir_mode;
165 tree->file_mode = file_mode;
166 tree->merged = merged != FALSE;
167
168 tree->root = markup_dir_new (tree, NULL, "/");
169
170 tree->refcount = 1;
171
172 g_hash_table_insert (trees_by_root_dir, tree->dirname, tree);
173
174 return tree;
175 }
176
177 void
markup_tree_unref(MarkupTree * tree)178 markup_tree_unref (MarkupTree *tree)
179 {
180 g_return_if_fail (tree != NULL);
181 g_return_if_fail (tree->refcount > 0);
182
183 if (tree->refcount > 1)
184 {
185 tree->refcount -= 1;
186 return;
187 }
188
189 g_hash_table_remove (trees_by_root_dir, tree->dirname);
190 if (g_hash_table_size (trees_by_root_dir) == 0)
191 {
192 g_hash_table_destroy (trees_by_root_dir);
193 trees_by_root_dir = NULL;
194 }
195
196 markup_dir_free (tree->root);
197 tree->root = NULL;
198
199 g_free (tree->dirname);
200
201 g_free (tree);
202 }
203
204 void
markup_tree_rebuild(MarkupTree * tree)205 markup_tree_rebuild (MarkupTree *tree)
206 {
207 g_return_if_fail (!markup_dir_needs_sync (tree->root));
208
209 markup_dir_free (tree->root);
210 tree->root = markup_dir_new (tree, NULL, "/");
211 }
212
213 struct _MarkupDir
214 {
215 MarkupTree *tree;
216 MarkupDir *parent;
217 MarkupDir *subtree_root;
218 char *name;
219
220 GSList *entries;
221 GSList *subdirs;
222
223 /* Available %gconf-tree-$(locale).xml files */
224 GHashTable *available_local_descs;
225
226 /* Have read the existing XML file */
227 guint entries_loaded : 1;
228 /* Need to rewrite the XML file since we changed
229 * it
230 */
231 guint entries_need_save : 1;
232
233 /* Have read the existing directories */
234 guint subdirs_loaded : 1;
235
236 /* Child needs sync */
237 guint some_subdir_needs_sync : 1;
238
239 /* We are pretty sure the filesystem dir exists */
240 guint filesystem_dir_probably_exists : 1;
241
242 /* Not represented by an actual filesystem dir */
243 guint not_in_filesystem : 1;
244
245 /* Save to %gconf-tree.xml when syncing */
246 guint save_as_subtree : 1;
247
248 /* We've loaded all locales in @available_local_descs */
249 guint all_local_descs_loaded : 1;
250
251 /* This is a temporary directory used only during parsing */
252 guint is_parser_dummy : 1;
253
254 /* Temporary flag used only when writing */
255 guint is_dir_empty : 1;
256 };
257
258 static MarkupDir*
markup_dir_new(MarkupTree * tree,MarkupDir * parent,const char * name)259 markup_dir_new (MarkupTree *tree,
260 MarkupDir *parent,
261 const char *name)
262 {
263 MarkupDir *dir;
264
265 dir = g_new0 (MarkupDir, 1);
266
267 dir->name = g_strdup (name);
268 dir->tree = tree;
269 dir->parent = parent;
270
271 if (parent)
272 {
273 dir->subtree_root = parent->subtree_root;
274 parent->subdirs = g_slist_prepend (parent->subdirs, dir);
275 }
276 else
277 {
278 markup_dir_setup_as_subtree_root (dir);
279 }
280
281 return dir;
282 }
283
284 static void
markup_dir_free(MarkupDir * dir)285 markup_dir_free (MarkupDir *dir)
286 {
287 GSList *tmp;
288
289 if (dir->available_local_descs != NULL)
290 {
291 g_hash_table_destroy (dir->available_local_descs);
292 dir->available_local_descs = NULL;
293 }
294
295 tmp = dir->entries;
296 while (tmp)
297 {
298 MarkupEntry *entry = tmp->data;
299
300 markup_entry_free (entry);
301
302 tmp = tmp->next;
303 }
304 g_slist_free (dir->entries);
305
306 tmp = dir->subdirs;
307 while (tmp)
308 {
309 MarkupDir *subdir = tmp->data;
310
311 markup_dir_free (subdir);
312
313 tmp = tmp->next;
314 }
315 g_slist_free (dir->subdirs);
316
317 g_free (dir->name);
318
319 g_free (dir);
320 }
321
322 static void
markup_dir_queue_sync(MarkupDir * dir)323 markup_dir_queue_sync (MarkupDir *dir)
324 {
325 MarkupDir *iter;
326
327 iter = dir->parent;
328 while (iter != NULL) /* exclude root dir */
329 {
330 iter->some_subdir_needs_sync = TRUE;
331 iter = iter->parent;
332 }
333 }
334
335 static inline char *
markup_dir_build_file_path(MarkupDir * dir,gboolean subtree_data_file,const char * locale)336 markup_dir_build_file_path (MarkupDir *dir,
337 gboolean subtree_data_file,
338 const char *locale)
339 {
340 return markup_dir_build_path (dir, TRUE, TRUE, subtree_data_file, locale);
341 }
342
343 static inline char *
markup_dir_build_dir_path(MarkupDir * dir,gboolean filesystem_path)344 markup_dir_build_dir_path (MarkupDir *dir,
345 gboolean filesystem_path)
346 {
347 return markup_dir_build_path (dir, filesystem_path, FALSE, FALSE, NULL);
348 }
349
350 static MarkupDir*
markup_tree_get_dir_internal(MarkupTree * tree,const char * full_key,gboolean create_if_not_found,GError ** err)351 markup_tree_get_dir_internal (MarkupTree *tree,
352 const char *full_key,
353 gboolean create_if_not_found,
354 GError **err)
355 {
356 char **components;
357 int i;
358 MarkupDir *dir;
359
360 g_return_val_if_fail (*full_key == '/', NULL);
361
362 /* Split without leading '/' */
363 components = g_strsplit (full_key + 1, "/", -1);
364
365 dir = tree->root;
366
367 if (components) /* if components == NULL the root dir was requested */
368 {
369 i = 0;
370 while (components[i])
371 {
372 MarkupDir *subdir;
373 GError *tmp_err;
374
375 tmp_err = NULL;
376
377 if (create_if_not_found)
378 subdir = markup_dir_ensure_subdir (dir, components[i], &tmp_err);
379 else
380 subdir = markup_dir_lookup_subdir (dir, components[i], &tmp_err);
381
382 if (tmp_err != NULL)
383 {
384 dir = NULL;
385 g_propagate_error (err, tmp_err);
386 goto out;
387 }
388
389 if (subdir)
390 {
391 /* Descend one level */
392 dir = subdir;
393 }
394 else
395 {
396 dir = NULL;
397 goto out;
398 }
399
400 ++i;
401 }
402 }
403
404 out:
405 g_strfreev (components);
406
407 return dir;
408 }
409
410 MarkupDir*
markup_tree_lookup_dir(MarkupTree * tree,const char * full_key,GError ** err)411 markup_tree_lookup_dir (MarkupTree *tree,
412 const char *full_key,
413 GError **err)
414
415 {
416 return markup_tree_get_dir_internal (tree, full_key, FALSE, err);
417 }
418
419 MarkupDir*
markup_tree_ensure_dir(MarkupTree * tree,const char * full_key,GError ** err)420 markup_tree_ensure_dir (MarkupTree *tree,
421 const char *full_key,
422 GError **err)
423 {
424 return markup_tree_get_dir_internal (tree, full_key, TRUE, err);
425 }
426
427 gboolean
markup_tree_sync(MarkupTree * tree,GError ** err)428 markup_tree_sync (MarkupTree *tree,
429 GError **err)
430 {
431 if (markup_dir_needs_sync (tree->root))
432 {
433 if (!markup_dir_sync (tree->root))
434 {
435 g_set_error (err, GCONF_ERROR,
436 GCONF_ERROR_FAILED,
437 _("Failed to write some configuration data to disk\n"));
438 return FALSE;
439 }
440 }
441
442 return TRUE;
443 }
444
445 static void
markup_dir_setup_as_subtree_root(MarkupDir * dir)446 markup_dir_setup_as_subtree_root (MarkupDir *dir)
447 {
448 if (dir->subtree_root != dir)
449 {
450 dir->subtree_root = dir;
451
452 dir->available_local_descs = g_hash_table_new_full (g_str_hash,
453 g_str_equal,
454 g_free,
455 NULL);
456 dir->all_local_descs_loaded = TRUE;
457 }
458 }
459
460 static void
markup_dir_list_available_local_descs(MarkupDir * dir)461 markup_dir_list_available_local_descs (MarkupDir *dir)
462 {
463 #define LOCALE_FILE_PREFIX "%gconf-tree-"
464 #define LOCALE_FILE_SUFFIX ".xml"
465 #define LOCALE_FILE_PREFIX_LEN (sizeof (LOCALE_FILE_PREFIX) - 1)
466 #define LOCALE_FILE_SUFFIX_LEN (sizeof (LOCALE_FILE_SUFFIX) - 1)
467
468 GDir *dp;
469 char *dir_path;
470 const char *dent;
471
472 dir_path = markup_dir_build_dir_path (dir, TRUE);
473
474 if ((dp = g_dir_open (dir_path, 0, NULL)) == NULL)
475 {
476 /* This is debug-only since it usually happens when creating a
477 * new directory
478 */
479 gconf_log (GCL_DEBUG,
480 "Could not open directory \"%s\": %s\n",
481 dir_path, g_strerror (errno));
482 g_free (dir_path);
483 return;
484 }
485
486 g_assert (dir->available_local_descs != NULL);
487 g_assert (g_hash_table_size (dir->available_local_descs) == 0);
488
489 while ((dent = g_dir_read_name (dp)) != NULL)
490 {
491 gsize dent_len;
492 char *locale;
493
494 dent_len = strlen (dent);
495
496 if (dent_len <= LOCALE_FILE_PREFIX_LEN + LOCALE_FILE_SUFFIX_LEN)
497 continue;
498
499 if (strncmp (dent, LOCALE_FILE_PREFIX, LOCALE_FILE_PREFIX_LEN) != 0)
500 continue;
501
502 if (strcmp (dent + dent_len - LOCALE_FILE_SUFFIX_LEN, LOCALE_FILE_SUFFIX) != 0)
503 continue;
504
505 locale = g_strndup (dent + LOCALE_FILE_PREFIX_LEN,
506 dent_len - LOCALE_FILE_PREFIX_LEN - LOCALE_FILE_SUFFIX_LEN);
507
508 g_hash_table_replace (dir->available_local_descs,
509 locale,
510 GINT_TO_POINTER (FALSE));
511 }
512
513 if (g_hash_table_size (dir->available_local_descs) != 0)
514 dir->all_local_descs_loaded = FALSE;
515
516 /* if this fails, we really can't do a thing about it
517 * and it's not a meaningful error
518 */
519 g_dir_close (dp);
520
521 g_free (dir_path);
522
523 #undef LOCALE_FILE_SUFFIX_LEN
524 #undef LOCALE_FILE_PREFIX_LEN
525 #undef LOCALE_FILE_SUFFIX
526 #undef LOCALE_FILE_PREFIX
527 }
528
529 static gboolean
load_subtree(MarkupDir * dir)530 load_subtree (MarkupDir *dir)
531 {
532 /* Load subtree from %gconf-tree.xml if exists */
533
534 GError *tmp_err = NULL;
535 char *markup_file;
536
537 markup_file = markup_dir_build_file_path (dir, TRUE, NULL);
538 if (!g_file_test (markup_file, G_FILE_TEST_EXISTS))
539 {
540 g_free (markup_file);
541 return FALSE;
542 }
543
544 dir->subdirs_loaded = TRUE;
545 dir->entries_loaded = TRUE;
546 dir->save_as_subtree = TRUE;
547
548 markup_dir_setup_as_subtree_root (dir);
549 markup_dir_list_available_local_descs (dir);
550
551 parse_tree (dir, TRUE, NULL, &tmp_err);
552 if (tmp_err)
553 {
554 /* note that tmp_err may be a G_MARKUP_ERROR while only
555 * GCONF_ERROR could be returned, so if we decide to return this
556 * error someday we need to fix it up first
557 */
558
559 /* this message is debug-only because it usually happens
560 * when creating a new directory
561 */
562 gconf_log (GCL_DEBUG,
563 "Failed to load file \"%s\": %s",
564 markup_file, tmp_err->message);
565 g_error_free (tmp_err);
566 }
567
568 g_free (markup_file);
569
570 return TRUE;
571 }
572
573 static gboolean
load_entries(MarkupDir * dir)574 load_entries (MarkupDir *dir)
575 {
576 /* Load the entries in this directory */
577
578 if (dir->entries_loaded)
579 return TRUE;
580
581 /* We mark it loaded even if the next stuff
582 * fails, because we don't want to keep trying and
583 * failing, plus we have invariants
584 * that assume entries_loaded is TRUE once we've
585 * called load_entries()
586 */
587 dir->entries_loaded = TRUE;
588
589 if (!load_subtree (dir))
590 {
591 GError *tmp_err = NULL;
592
593 parse_tree (dir, FALSE, NULL, &tmp_err);
594 if (tmp_err)
595 {
596 char *markup_file;
597
598 /* note that tmp_err may be a G_MARKUP_ERROR while only
599 * GCONF_ERROR could be returned, so if we decide to return this
600 * error someday we need to fix it up first
601 */
602
603 /* this message is debug-only because it usually happens
604 * when creating a new directory
605 */
606 markup_file = markup_dir_build_file_path (dir, FALSE, NULL);
607 gconf_log (GCL_DEBUG,
608 "Failed to load file \"%s\": %s",
609 markup_file, tmp_err->message);
610 g_error_free (tmp_err);
611 g_free (markup_file);
612 }
613 }
614
615 return TRUE;
616 }
617
618 static gboolean
load_subdirs(MarkupDir * dir)619 load_subdirs (MarkupDir *dir)
620 {
621 GDir* dp;
622 const char* dent;
623 struct stat statbuf;
624 gchar* fullpath;
625 gchar* fullpath_end;
626 guint len;
627 guint subdir_len;
628 char *markup_dir;
629
630 if (dir->subdirs_loaded)
631 return TRUE;
632
633 /* We mark it loaded even if the next stuff
634 * fails, because we don't want to keep trying and
635 * failing, plus we have invariants
636 * that assume subdirs_loaded is TRUE once we've
637 * called load_subdirs()
638 */
639 dir->subdirs_loaded = TRUE;
640
641 g_assert (dir->subdirs == NULL);
642
643 if (load_subtree (dir))
644 return TRUE;
645
646 markup_dir = markup_dir_build_dir_path (dir, TRUE);
647
648 dp = g_dir_open (markup_dir, 0, NULL);
649
650 if (dp == NULL)
651 {
652 /* This is debug-only since it usually happens when creating a
653 * new directory
654 */
655 gconf_log (GCL_DEBUG,
656 "Could not open directory \"%s\": %s\n",
657 markup_dir, g_strerror (errno));
658 g_free (markup_dir);
659 return FALSE;
660 }
661
662 len = strlen (markup_dir);
663
664 subdir_len = PATH_MAX - len;
665
666 fullpath = g_new0 (char, subdir_len + len + 2); /* ensure null termination */
667 strcpy (fullpath, markup_dir);
668
669 fullpath_end = fullpath + len;
670 if (*(fullpath_end - 1) != '/')
671 {
672 *fullpath_end = '/';
673 ++fullpath_end;
674 }
675
676 while ((dent = g_dir_read_name (dp)) != NULL)
677 {
678 /* ignore all dot-files */
679 if (dent[0] == '.')
680 continue;
681
682 /* ignore stuff starting with % as it's an invalid gconf
683 * dir name, and probably %gconf.xml
684 */
685 if (dent[0] == '%')
686 continue;
687
688 len = strlen (dent);
689
690 if (len < subdir_len)
691 {
692 strcpy (fullpath_end, dent);
693 strncpy (fullpath_end+len, "/%gconf.xml", subdir_len - len);
694 }
695 else
696 continue; /* Shouldn't ever happen since PATH_MAX is available */
697
698 if (g_stat (fullpath, &statbuf) < 0)
699 {
700 strncpy (fullpath_end+len, "/%gconf-tree.xml", subdir_len - len);
701 if (g_stat (fullpath, &statbuf) < 0)
702 {
703 /* This is some kind of cruft, not an XML directory */
704 continue;
705 }
706 }
707
708 markup_dir_new (dir->tree, dir, dent);
709 }
710
711 /* if this fails, we really can't do a thing about it
712 * and it's not a meaningful error
713 */
714 g_dir_close (dp);
715
716 g_free (fullpath);
717 g_free (markup_dir);
718
719 return TRUE;
720 }
721
722 MarkupEntry*
markup_dir_lookup_entry(MarkupDir * dir,const char * relative_key,GError ** err)723 markup_dir_lookup_entry (MarkupDir *dir,
724 const char *relative_key,
725 GError **err)
726 {
727 GSList *tmp;
728
729 load_entries (dir);
730
731 tmp = dir->entries;
732 while (tmp != NULL)
733 {
734 MarkupEntry *entry = tmp->data;
735
736 if (strcmp (relative_key, entry->name) == 0)
737 return entry;
738
739 tmp = tmp->next;
740 }
741
742 return NULL;
743 }
744
745 MarkupEntry*
markup_dir_ensure_entry(MarkupDir * dir,const char * relative_key,GError ** err)746 markup_dir_ensure_entry (MarkupDir *dir,
747 const char *relative_key,
748 GError **err)
749 {
750 MarkupEntry *entry;
751 GError *tmp_err;
752
753 tmp_err = NULL;
754 entry = markup_dir_lookup_entry (dir, relative_key, &tmp_err);
755 if (tmp_err != NULL)
756 {
757 g_propagate_error (err, tmp_err);
758 return NULL;
759 }
760
761 if (entry != NULL)
762 return entry;
763
764 g_return_val_if_fail (dir->entries_loaded, NULL);
765
766 /* Create a new entry */
767 entry = markup_entry_new (dir, relative_key);
768
769 /* Need to save this */
770 markup_dir_set_entries_need_save (dir);
771 markup_dir_queue_sync (dir);
772
773 return entry;
774 }
775
776 MarkupDir*
markup_dir_lookup_subdir(MarkupDir * dir,const char * relative_key,GError ** err)777 markup_dir_lookup_subdir (MarkupDir *dir,
778 const char *relative_key,
779 GError **err)
780 {
781 GSList *tmp;
782
783 load_subdirs (dir);
784
785 tmp = dir->subdirs;
786 while (tmp != NULL)
787 {
788 MarkupDir *subdir = tmp->data;
789
790 if (strcmp (subdir->name, relative_key) == 0)
791 return subdir;
792
793 tmp = tmp->next;
794 }
795
796 return NULL;
797 }
798
799 MarkupDir*
markup_dir_ensure_subdir(MarkupDir * dir,const char * relative_key,GError ** err)800 markup_dir_ensure_subdir (MarkupDir *dir,
801 const char *relative_key,
802 GError **err)
803 {
804 MarkupDir *subdir;
805 GError *tmp_err;
806
807 tmp_err = NULL;
808 subdir = markup_dir_lookup_subdir (dir, relative_key, &tmp_err);
809 if (tmp_err != NULL)
810 {
811 g_propagate_error (err, tmp_err);
812 return NULL;
813 }
814
815 if (subdir != NULL)
816 return subdir;
817
818 g_return_val_if_fail (dir->subdirs_loaded, NULL);
819
820 subdir = markup_dir_new (dir->tree, dir, relative_key);
821 markup_dir_set_entries_need_save (subdir); /* so we save empty %gconf.xml */
822
823 /* we don't need to load stuff, since we know the dir didn't exist */
824 subdir->entries_loaded = TRUE;
825 subdir->subdirs_loaded = TRUE;
826
827 return subdir;
828 }
829
830 GSList*
markup_dir_list_entries(MarkupDir * dir,GError ** err)831 markup_dir_list_entries (MarkupDir *dir,
832 GError **err)
833 {
834 load_entries (dir);
835
836 return dir->entries;
837 }
838
839 GSList*
markup_dir_list_subdirs(MarkupDir * dir,GError ** err)840 markup_dir_list_subdirs (MarkupDir *dir,
841 GError **err)
842 {
843 load_subdirs (dir);
844
845 return dir->subdirs;
846 }
847
848 const char*
markup_dir_get_name(MarkupDir * dir)849 markup_dir_get_name (MarkupDir *dir)
850 {
851 return dir->name;
852 }
853
854 static gboolean
markup_dir_needs_sync(MarkupDir * dir)855 markup_dir_needs_sync (MarkupDir *dir)
856 {
857 return dir->entries_need_save || dir->some_subdir_needs_sync;
858 }
859
860 static void
markup_dir_set_entries_need_save(MarkupDir * dir)861 markup_dir_set_entries_need_save (MarkupDir *dir)
862 {
863 dir->entries_need_save = TRUE;
864
865 if (dir->not_in_filesystem)
866 {
867 /* root must be a filesystem dir */
868 g_assert (dir->parent);
869
870 markup_dir_set_entries_need_save (dir->parent);
871 }
872 }
873
874 /* Get rid of any local_schema that no longer apply */
875 static void
clean_old_local_schemas(MarkupEntry * entry)876 clean_old_local_schemas (MarkupEntry *entry)
877 {
878 GSList *tmp;
879 GSList *kept_schemas;
880
881 kept_schemas = NULL;
882
883 tmp = entry->local_schemas;
884 while (tmp != NULL)
885 {
886 LocalSchemaInfo *local_schema = tmp->data;
887 gboolean dead = FALSE;
888
889 local_schema = tmp->data;
890
891 if (entry->value &&
892 entry->value->type != GCONF_VALUE_SCHEMA)
893 dead = TRUE;
894 else if (local_schema->default_value &&
895 entry->value &&
896 gconf_value_get_schema (entry->value) &&
897 gconf_schema_get_type (gconf_value_get_schema (entry->value)) !=
898 local_schema->default_value->type)
899 {
900 dead = TRUE;
901 }
902
903 if (dead)
904 {
905 local_schema_info_free (local_schema);
906 }
907 else
908 {
909 kept_schemas = g_slist_prepend (kept_schemas, local_schema);
910 }
911
912 tmp = tmp->next;
913 }
914
915 g_slist_free (entry->local_schemas);
916
917 entry->local_schemas = g_slist_reverse (kept_schemas);
918 }
919
920 static void
clean_old_local_schemas_recurse(MarkupDir * dir,gboolean recurse)921 clean_old_local_schemas_recurse (MarkupDir *dir,
922 gboolean recurse)
923 {
924 GSList *tmp;
925
926 if (recurse)
927 {
928 tmp = dir->subdirs;
929 while (tmp)
930 {
931 MarkupDir *subdir = tmp->data;
932
933 clean_old_local_schemas_recurse (subdir, TRUE);
934
935 tmp = tmp->next;
936 }
937 }
938
939 tmp = dir->entries;
940 while (tmp != NULL)
941 {
942 MarkupEntry *entry = tmp->data;
943
944 clean_old_local_schemas (entry);
945
946 tmp = tmp->next;
947 }
948 }
949
950 static gboolean
create_filesystem_dir(const char * name,guint dir_mode)951 create_filesystem_dir (const char *name,
952 guint dir_mode)
953 {
954 if (g_mkdir (name, dir_mode) < 0)
955 {
956 if (errno == EEXIST)
957 return TRUE;
958
959 gconf_log (GCL_WARNING,
960 _("Could not make directory \"%s\": %s"),
961 name, g_strerror (errno));
962
963 return FALSE;
964 }
965
966 return TRUE;
967 }
968
969 static gboolean
delete_useless_subdirs(MarkupDir * dir)970 delete_useless_subdirs (MarkupDir *dir)
971 {
972 GSList *tmp;
973 GSList *kept_subdirs;
974 gboolean some_deleted;
975
976 some_deleted = FALSE;
977 kept_subdirs = NULL;
978
979 tmp = dir->subdirs;
980 while (tmp != NULL)
981 {
982 MarkupDir *subdir = tmp->data;
983
984 if (subdir->entries_loaded && subdir->entries == NULL &&
985 subdir->subdirs_loaded && subdir->subdirs == NULL)
986 {
987 if (!subdir->not_in_filesystem)
988 {
989 char *fs_dirname;
990 char *fs_filename;
991
992 fs_dirname = markup_dir_build_dir_path (subdir, TRUE);
993 fs_filename = markup_dir_build_file_path (subdir,
994 subdir->save_as_subtree,
995 NULL);
996
997 if (g_unlink (fs_filename) < 0)
998 {
999 gconf_log (GCL_WARNING,
1000 _("Could not remove \"%s\": %s\n"),
1001 fs_filename, g_strerror (errno));
1002 }
1003
1004 if (g_rmdir (fs_dirname) < 0)
1005 {
1006 gconf_log (GCL_WARNING,
1007 _("Could not remove \"%s\": %s\n"),
1008 fs_dirname, g_strerror (errno));
1009 }
1010
1011 g_free (fs_dirname);
1012 g_free (fs_filename);
1013 }
1014
1015 markup_dir_free (subdir);
1016
1017 some_deleted = TRUE;
1018 }
1019 else
1020 {
1021 kept_subdirs = g_slist_prepend (kept_subdirs, subdir);
1022 }
1023
1024 tmp = tmp->next;
1025 }
1026
1027 g_slist_free (dir->subdirs);
1028 dir->subdirs = g_slist_reverse (kept_subdirs);
1029
1030 return some_deleted;
1031 }
1032
1033 static gboolean
delete_useless_subdirs_recurse(MarkupDir * dir)1034 delete_useless_subdirs_recurse (MarkupDir *dir)
1035 {
1036 GSList *tmp;
1037 gboolean retval = FALSE;
1038
1039 tmp = dir->subdirs;
1040 while (tmp)
1041 {
1042 MarkupDir *subdir = tmp->data;
1043
1044 if (delete_useless_subdirs_recurse (subdir))
1045 retval = TRUE;
1046
1047 tmp = tmp->next;
1048 }
1049
1050 if (delete_useless_subdirs (dir))
1051 retval = TRUE;
1052
1053 return retval;
1054 }
1055
1056 static gboolean
delete_useless_entries(MarkupDir * dir)1057 delete_useless_entries (MarkupDir *dir)
1058 {
1059 GSList *tmp;
1060 GSList *kept_entries;
1061 gboolean some_deleted;
1062
1063 some_deleted = FALSE;
1064
1065 kept_entries = NULL;
1066
1067 tmp = dir->entries;
1068 while (tmp != NULL)
1069 {
1070 MarkupEntry *entry = tmp->data;
1071
1072 /* mod_user and mod_time don't keep an entry alive */
1073
1074 if (entry->value == NULL &&
1075 entry->local_schemas == NULL &&
1076 entry->schema_name == NULL)
1077 {
1078 markup_entry_free (entry);
1079 some_deleted = TRUE;
1080 }
1081 else
1082 {
1083 kept_entries = g_slist_prepend (kept_entries, entry);
1084 }
1085
1086 tmp = tmp->next;
1087 }
1088
1089 g_slist_free (dir->entries);
1090 dir->entries = g_slist_reverse (kept_entries);
1091
1092 return some_deleted;
1093 }
1094
1095 static gboolean
delete_useless_entries_recurse(MarkupDir * dir)1096 delete_useless_entries_recurse (MarkupDir *dir)
1097 {
1098 GSList *tmp;
1099 gboolean retval = FALSE;
1100
1101 tmp = dir->subdirs;
1102 while (tmp)
1103 {
1104 MarkupDir *subdir = tmp->data;
1105
1106 if (delete_useless_entries_recurse (subdir))
1107 retval = TRUE;
1108
1109 tmp = tmp->next;
1110 }
1111
1112 if (delete_useless_entries (dir))
1113 retval = TRUE;
1114
1115 return retval;
1116 }
1117
1118 static void
recursively_load_subtree(MarkupDir * dir)1119 recursively_load_subtree (MarkupDir *dir)
1120 {
1121 GSList *tmp;
1122
1123 load_entries (dir);
1124 load_subdirs (dir);
1125
1126 tmp = dir->subdirs;
1127 while (tmp != NULL)
1128 {
1129 MarkupDir *subdir = tmp->data;
1130
1131 recursively_load_subtree (subdir);
1132 subdir->not_in_filesystem = TRUE;
1133
1134 tmp = tmp->next;
1135 }
1136 }
1137
1138 static gboolean
markup_dir_sync(MarkupDir * dir)1139 markup_dir_sync (MarkupDir *dir)
1140 {
1141 char *fs_dirname;
1142 char *fs_filename;
1143 char *fs_subtree;
1144 gboolean some_useless_entries;
1145 gboolean some_useless_subdirs;
1146
1147 some_useless_entries = FALSE;
1148 some_useless_subdirs = FALSE;
1149
1150 /* We assume our parent directories have all been synced, before
1151 * we are synced. So we don't need to mkdir() parent directories.
1152 */
1153
1154 /* We must have been synced already */
1155 if (dir->not_in_filesystem)
1156 return TRUE;
1157
1158 /* Sanitize the entries */
1159 clean_old_local_schemas_recurse (dir, dir->save_as_subtree);
1160
1161 if (!dir->save_as_subtree && dir->tree->merged)
1162 {
1163 dir->save_as_subtree = TRUE;
1164 recursively_load_subtree (dir);
1165 }
1166
1167 fs_dirname = markup_dir_build_dir_path (dir, TRUE);
1168 fs_filename = markup_dir_build_file_path (dir, FALSE, NULL);
1169 fs_subtree = markup_dir_build_file_path (dir, TRUE, NULL);
1170
1171 /* For a dir to be loaded as a subdir, it must have a
1172 * %gconf.xml file, even if it has no entries in that
1173 * file. Thus when creating a new dir, we set dir->entries_need_save
1174 * even though the entry list is initially empty.
1175 */
1176
1177 if (dir->entries_need_save ||
1178 (dir->some_subdir_needs_sync && dir->save_as_subtree))
1179 {
1180 GError *err;
1181
1182 g_return_val_if_fail (dir->entries_loaded, FALSE);
1183
1184 if (!dir->save_as_subtree)
1185 {
1186 if (delete_useless_entries (dir))
1187 some_useless_entries = TRUE;
1188 }
1189 else
1190 {
1191 if (delete_useless_entries_recurse (dir))
1192 some_useless_entries = TRUE;
1193 }
1194
1195 /* Be sure the directory exists */
1196 if (!dir->filesystem_dir_probably_exists)
1197 {
1198 if (create_filesystem_dir (fs_dirname, dir->tree->dir_mode))
1199 dir->filesystem_dir_probably_exists = TRUE;
1200 }
1201
1202 /* Now write the file */
1203 err = NULL;
1204 save_tree (dir, dir->save_as_subtree, dir->tree->file_mode, &err);
1205 if (err != NULL)
1206 {
1207 gconf_log (GCL_WARNING,
1208 _("Failed to write \"%s\": %s\n"),
1209 !dir->save_as_subtree ? fs_filename : fs_subtree, err->message);
1210
1211 g_error_free (err);
1212 }
1213 else
1214 {
1215 dir->entries_need_save = FALSE;
1216 if (dir->save_as_subtree)
1217 dir->some_subdir_needs_sync = FALSE;
1218 }
1219 }
1220
1221 if (dir->some_subdir_needs_sync && !dir->save_as_subtree)
1222 {
1223 GSList *tmp;
1224 gboolean one_failed;
1225
1226 g_return_val_if_fail (dir->subdirs_loaded, FALSE);
1227
1228 one_failed = FALSE;
1229
1230 tmp = dir->subdirs;
1231 while (tmp != NULL)
1232 {
1233 MarkupDir *subdir = tmp->data;
1234
1235 if (markup_dir_needs_sync (subdir))
1236 {
1237 /* Be sure the directory exists (may not have
1238 * had to save entries, if not we won't have done
1239 * this there)
1240 */
1241 if (!dir->filesystem_dir_probably_exists)
1242 {
1243 if (create_filesystem_dir (fs_dirname, dir->tree->dir_mode))
1244 dir->filesystem_dir_probably_exists = TRUE;
1245 }
1246
1247 if (!markup_dir_sync (subdir))
1248 one_failed = TRUE;
1249 }
1250
1251 tmp = tmp->next;
1252 }
1253
1254 if (!one_failed)
1255 dir->some_subdir_needs_sync = FALSE;
1256 }
1257
1258 /* Now if we've synced everything and some subdirs have no entries
1259 * and no subdirs, they have become useless - so we can delete
1260 * them. Note that this happens _after_ recursing
1261 * subdirectories, so the deletion happens first on
1262 * the leaves, and then on the root. Also note that since
1263 * we're deleting our subdirs, the root dir (tree->root)
1264 * never gets deleted - this is intentional.
1265 */
1266
1267 if (!dir->save_as_subtree)
1268 {
1269 if (delete_useless_subdirs (dir))
1270 some_useless_subdirs = TRUE;
1271 }
1272 else
1273 {
1274 /* We haven't recursively synced subdirs so we need
1275 * to now recursively delete useless subdirs.
1276 */
1277 if (delete_useless_subdirs_recurse (dir))
1278 some_useless_subdirs = TRUE;
1279 }
1280
1281 g_free (fs_dirname);
1282 g_free (fs_filename);
1283 g_free (fs_subtree);
1284
1285 /* If we deleted an entry or subdir from this directory, and hadn't
1286 * fully loaded this directory, we now don't know whether the entry
1287 * or subdir was the last thing making the directory worth keeping
1288 * around. So we need to load so we can be established as useless if
1289 * necessary.
1290 */
1291 if (some_useless_entries && !dir->subdirs_loaded)
1292 {
1293 g_assert (dir->entries_loaded);
1294 load_subdirs (dir);
1295 }
1296 if (some_useless_subdirs && !dir->entries_loaded)
1297 {
1298 g_assert (dir->subdirs_loaded);
1299 load_entries (dir);
1300 }
1301
1302 return !markup_dir_needs_sync (dir);
1303 }
1304
1305 static char*
markup_dir_build_path(MarkupDir * dir,gboolean filesystem_path,gboolean with_data_file,gboolean subtree_data_file,const char * locale)1306 markup_dir_build_path (MarkupDir *dir,
1307 gboolean filesystem_path,
1308 gboolean with_data_file,
1309 gboolean subtree_data_file,
1310 const char *locale)
1311 {
1312 GString *name;
1313 GSList *components;
1314 GSList *tmp;
1315 MarkupDir *iter;
1316
1317 g_assert (filesystem_path || !with_data_file);
1318
1319 components = NULL;
1320 iter = dir;
1321 while (iter->parent != NULL) /* exclude root dir */
1322 {
1323 components = g_slist_prepend (components, iter->name);
1324 iter = iter->parent;
1325 }
1326
1327 if (filesystem_path)
1328 name = g_string_new (dir->tree->dirname);
1329 else
1330 name = g_string_new (components ? NULL : "/");
1331
1332 tmp = components;
1333 while (tmp != NULL)
1334 {
1335 const char *comp = tmp->data;
1336
1337 g_string_append_c (name, '/');
1338 g_string_append (name, comp);
1339
1340 tmp = tmp->next;
1341 }
1342
1343 g_slist_free (components);
1344
1345 if (with_data_file)
1346 {
1347 if (locale == NULL)
1348 {
1349 g_string_append (name,
1350 subtree_data_file ? "/%gconf-tree.xml" : "/%gconf.xml");
1351 }
1352 else
1353 {
1354 g_assert (subtree_data_file);
1355
1356 g_string_append_printf (name, "/%%gconf-tree-%s.xml", locale);
1357 }
1358 }
1359
1360 return g_string_free (name, FALSE);
1361 }
1362
1363 /*
1364 * MarkupEntry
1365 */
1366
1367 static MarkupEntry*
markup_entry_new(MarkupDir * dir,const char * name)1368 markup_entry_new (MarkupDir *dir,
1369 const char *name)
1370 {
1371 MarkupEntry *entry;
1372
1373 entry = g_new0 (MarkupEntry, 1);
1374
1375 entry->name = g_strdup (name);
1376
1377 entry->dir = dir;
1378 dir->entries = g_slist_prepend (dir->entries, entry);
1379
1380 return entry;
1381 }
1382
1383 static void
markup_entry_free(MarkupEntry * entry)1384 markup_entry_free (MarkupEntry *entry)
1385 {
1386 g_free (entry->name);
1387 if (entry->value)
1388 gconf_value_free (entry->value);
1389 g_free (entry->schema_name);
1390 g_free (entry->mod_user);
1391
1392 g_slist_foreach (entry->local_schemas,
1393 (GFunc) local_schema_info_free,
1394 NULL);
1395
1396 g_slist_free (entry->local_schemas);
1397
1398 g_free (entry);
1399 }
1400
1401 static void
load_schema_descs_for_locale(MarkupDir * dir,const char * locale)1402 load_schema_descs_for_locale (MarkupDir *dir,
1403 const char *locale)
1404 {
1405 GError *error;
1406
1407 error = NULL;
1408 parse_tree (dir, TRUE, locale, &error);
1409 if (error != NULL)
1410 {
1411 char *markup_file;
1412
1413 markup_file = markup_dir_build_file_path (dir, TRUE, locale);
1414
1415 gconf_log (GCL_ERR,
1416 _("Failed to load file \"%s\": %s"),
1417 markup_file,
1418 error->message);
1419
1420 g_free (markup_file);
1421 g_error_free (error);
1422 }
1423
1424 g_hash_table_replace (dir->available_local_descs,
1425 g_strdup (locale),
1426 GINT_TO_POINTER (TRUE));
1427 }
1428
1429 static void
load_schema_descs_foreach(const char * locale,gpointer value,MarkupDir * dir)1430 load_schema_descs_foreach (const char *locale,
1431 gpointer value,
1432 MarkupDir *dir)
1433 {
1434 if (value != NULL)
1435 return; /* already loaded */
1436
1437 load_schema_descs_for_locale (dir, locale);
1438 }
1439
1440 static gboolean
find_unloaded_locale(const char * locale,gpointer value,gboolean * any_unloaded)1441 find_unloaded_locale (const char *locale,
1442 gpointer value,
1443 gboolean *any_unloaded)
1444 {
1445 if (value != NULL)
1446 return FALSE;
1447
1448 *any_unloaded = TRUE;
1449
1450 return TRUE;
1451 }
1452
1453 static void
ensure_schema_descs_loaded(MarkupEntry * entry,const char * locale)1454 ensure_schema_descs_loaded (MarkupEntry *entry,
1455 const char *locale)
1456 {
1457 MarkupDir *subtree_root;
1458
1459 subtree_root = entry->dir->subtree_root;
1460
1461 if (subtree_root->all_local_descs_loaded)
1462 return;
1463
1464 if (locale == NULL)
1465 {
1466 g_hash_table_foreach (subtree_root->available_local_descs,
1467 (GHFunc) load_schema_descs_foreach,
1468 subtree_root);
1469
1470 subtree_root->all_local_descs_loaded = TRUE;
1471
1472 return;
1473 }
1474 else
1475 {
1476 gpointer value;
1477 gboolean any_unloaded;
1478
1479 value = NULL;
1480 if (!g_hash_table_lookup_extended (subtree_root->available_local_descs,
1481 locale,
1482 NULL,
1483 &value))
1484 return; /* locale isn't available */
1485
1486 if (value != NULL)
1487 return; /* already loaded */
1488
1489 load_schema_descs_for_locale (subtree_root, locale);
1490
1491 any_unloaded = FALSE;
1492 g_hash_table_find (subtree_root->available_local_descs,
1493 (GHRFunc) find_unloaded_locale,
1494 &any_unloaded);
1495
1496 if (!any_unloaded)
1497 subtree_root->all_local_descs_loaded = TRUE;
1498 }
1499 }
1500
1501 void
markup_entry_set_value(MarkupEntry * entry,const GConfValue * value)1502 markup_entry_set_value (MarkupEntry *entry,
1503 const GConfValue *value)
1504 {
1505 /* We have to have loaded entries, because
1506 * someone called ensure_entry to get this
1507 * entry.
1508 */
1509 g_return_if_fail (entry->dir != NULL);
1510 g_return_if_fail (entry->dir->entries_loaded);
1511 g_return_if_fail (value != NULL);
1512
1513 if (value->type != GCONF_VALUE_SCHEMA)
1514 {
1515 if (entry->value == value)
1516 return;
1517
1518 if (entry->value)
1519 gconf_value_free (entry->value);
1520
1521 entry->value = gconf_value_copy (value);
1522
1523 /* Dump these if they exist, we aren't a schema anymore */
1524 if (entry->local_schemas)
1525 {
1526 g_slist_foreach (entry->local_schemas,
1527 (GFunc) local_schema_info_free,
1528 NULL);
1529 g_slist_free (entry->local_schemas);
1530 entry->local_schemas = NULL;
1531 }
1532 }
1533 else
1534 {
1535 /* For schema entries, we put the localized info
1536 * in a LocalSchemaInfo, and the other info
1537 * in the schema in the GConfValue
1538 */
1539 GSList *tmp;
1540 LocalSchemaInfo *local_schema;
1541 GConfSchema *schema;
1542 const char *locale;
1543 GConfSchema *current_schema;
1544 GConfValue *def_value;
1545
1546 schema = gconf_value_get_schema (value);
1547 g_assert (schema);
1548
1549 locale = gconf_schema_get_locale (schema);
1550 if (locale == NULL)
1551 locale = "C";
1552
1553 ensure_schema_descs_loaded (entry, locale);
1554
1555 local_schema = NULL;
1556 tmp = entry->local_schemas;
1557 while (tmp != NULL)
1558 {
1559 LocalSchemaInfo *lsi;
1560
1561 lsi = tmp->data;
1562
1563 if (strcmp (lsi->locale, locale) == 0)
1564 {
1565 local_schema = lsi;
1566 break;
1567 }
1568
1569 tmp = tmp->next;
1570 }
1571
1572 if (local_schema == NULL)
1573 {
1574 /* Didn't find a value for locale, make a new entry in the list */
1575 local_schema = local_schema_info_new ();
1576 local_schema->locale = g_strdup (locale);
1577 entry->local_schemas =
1578 g_slist_prepend (entry->local_schemas, local_schema);
1579 }
1580
1581 g_free (local_schema->short_desc);
1582 g_free (local_schema->long_desc);
1583 if (local_schema->default_value)
1584 gconf_value_free (local_schema->default_value);
1585
1586 local_schema->short_desc = g_strdup (gconf_schema_get_short_desc (schema));
1587 local_schema->long_desc = g_strdup (gconf_schema_get_long_desc (schema));
1588 def_value = gconf_schema_get_default_value (schema);
1589 if (def_value)
1590 local_schema->default_value = gconf_value_copy (def_value);
1591 else
1592 local_schema->default_value = NULL;
1593
1594 /* When saving, we will check that the type of default_value is
1595 * consistent with the type in the entry->value schema, so that
1596 * we don't save something we can't load. We'll drop any
1597 * LocalSchemaInfo with the wrong type default value at that
1598 * time, more efficient than dropping it now.
1599 */
1600 if (entry->value && entry->value->type != GCONF_VALUE_SCHEMA)
1601 {
1602 gconf_value_free (entry->value);
1603 entry->value = NULL;
1604 }
1605
1606 if (entry->value == NULL)
1607 {
1608 entry->value = gconf_value_new (GCONF_VALUE_SCHEMA);
1609 current_schema = gconf_schema_new ();
1610 gconf_value_set_schema_nocopy (entry->value, current_schema);
1611 }
1612 else
1613 {
1614 current_schema = gconf_value_get_schema (entry->value);
1615 }
1616
1617 /* Don't save localized info in the main schema */
1618 gconf_schema_set_locale (current_schema, NULL);
1619 gconf_schema_set_short_desc (current_schema, NULL);
1620 gconf_schema_set_long_desc (current_schema, NULL);
1621
1622 /* But everything else goes in the main schema */
1623 gconf_schema_set_list_type (current_schema,
1624 gconf_schema_get_list_type (schema));
1625 gconf_schema_set_car_type (current_schema,
1626 gconf_schema_get_car_type (schema));
1627 gconf_schema_set_cdr_type (current_schema,
1628 gconf_schema_get_cdr_type (schema));
1629 gconf_schema_set_type (current_schema,
1630 gconf_schema_get_type (schema));
1631 gconf_schema_set_owner (current_schema,
1632 gconf_schema_get_owner (schema));
1633 }
1634
1635 /* Update mod time */
1636 entry->mod_time = time (NULL);
1637
1638 /* Need to save to disk */
1639 markup_dir_set_entries_need_save (entry->dir);
1640 markup_dir_queue_sync (entry->dir);
1641 }
1642
1643 void
markup_entry_unset_value(MarkupEntry * entry,const char * locale)1644 markup_entry_unset_value (MarkupEntry *entry,
1645 const char *locale)
1646 {
1647 /* We have to have loaded entries, because
1648 * someone called ensure_entry to get this
1649 * entry.
1650 */
1651 g_return_if_fail (entry->dir != NULL);
1652 g_return_if_fail (entry->dir->entries_loaded);
1653
1654 if (entry->value == NULL)
1655 {
1656 /* nothing to do */
1657 return;
1658 }
1659 else if (entry->value->type == GCONF_VALUE_SCHEMA)
1660 {
1661 if (locale == NULL)
1662 {
1663 /* blow it all away */
1664 gconf_value_free (entry->value);
1665 entry->value = NULL;
1666
1667 ensure_schema_descs_loaded (entry, NULL);
1668
1669 g_slist_foreach (entry->local_schemas,
1670 (GFunc) local_schema_info_free,
1671 NULL);
1672
1673 g_slist_free (entry->local_schemas);
1674
1675 entry->local_schemas = NULL;
1676 }
1677 else
1678 {
1679 /* Just blow away any matching local schema */
1680 GSList *tmp;
1681
1682 ensure_schema_descs_loaded (entry, locale);
1683
1684 tmp = entry->local_schemas;
1685 while (tmp != NULL)
1686 {
1687 LocalSchemaInfo *local_schema = tmp->data;
1688
1689 if (strcmp (local_schema->locale, locale) == 0)
1690 {
1691 entry->local_schemas =
1692 g_slist_remove (entry->local_schemas,
1693 local_schema);
1694
1695 local_schema_info_free (local_schema);
1696 break;
1697 }
1698
1699 tmp = tmp->next;
1700 }
1701 }
1702 }
1703 else
1704 {
1705 gconf_value_free (entry->value);
1706 entry->value = NULL;
1707 }
1708
1709 /* Update mod time */
1710 entry->mod_time = time (NULL);
1711
1712 /* Need to save to disk */
1713 markup_dir_set_entries_need_save (entry->dir);
1714 markup_dir_queue_sync (entry->dir);
1715 }
1716
1717 void
markup_entry_set_schema_name(MarkupEntry * entry,const char * schema_name)1718 markup_entry_set_schema_name (MarkupEntry *entry,
1719 const char *schema_name)
1720 {
1721 /* We have to have loaded entries, because
1722 * someone called ensure_entry to get this
1723 * entry.
1724 */
1725 g_return_if_fail (entry->dir != NULL);
1726 g_return_if_fail (entry->dir->entries_loaded);
1727
1728 /* schema_name may be NULL to unset it */
1729
1730 g_free (entry->schema_name);
1731 entry->schema_name = g_strdup (schema_name);
1732
1733 /* Update mod time */
1734 entry->mod_time = time (NULL);
1735
1736 /* Need to save to disk */
1737 markup_dir_set_entries_need_save (entry->dir);
1738 markup_dir_queue_sync (entry->dir);
1739 }
1740
1741 GConfValue*
markup_entry_get_value(MarkupEntry * entry,const char ** locales)1742 markup_entry_get_value (MarkupEntry *entry,
1743 const char **locales)
1744 {
1745 /* We have to have loaded entries, because
1746 * someone called ensure_entry to get this
1747 * entry.
1748 */
1749 g_return_val_if_fail (entry->dir != NULL, NULL);
1750 g_return_val_if_fail (entry->dir->entries_loaded, NULL);
1751
1752 if (entry->value == NULL)
1753 {
1754 return NULL;
1755 }
1756 else if (entry->value->type != GCONF_VALUE_SCHEMA)
1757 {
1758 return gconf_value_copy (entry->value);
1759 }
1760 else
1761 {
1762 GConfValue *retval;
1763 GConfSchema *schema;
1764 static const char *fallback_locales[2] = {
1765 "C", NULL
1766 };
1767 LocalSchemaInfo *best;
1768 LocalSchemaInfo *c_local_schema;
1769 int i;
1770
1771 retval = gconf_value_copy (entry->value);
1772 schema = gconf_value_get_schema (retval);
1773 g_return_val_if_fail (schema != NULL, NULL);
1774
1775 /* Find the best local schema */
1776
1777 if (locales == NULL || locales[0] == NULL)
1778 locales = fallback_locales;
1779
1780 best = NULL;
1781 c_local_schema = NULL;
1782
1783 i = 0;
1784 while (locales[i] != NULL)
1785 {
1786 GSList *tmp;
1787
1788 ensure_schema_descs_loaded (entry, locales[i]);
1789
1790 tmp = entry->local_schemas;
1791 while (tmp != NULL)
1792 {
1793 LocalSchemaInfo *lsi = tmp->data;
1794
1795 if (c_local_schema == NULL &&
1796 strcmp (lsi->locale, "C") == 0)
1797 {
1798 c_local_schema = lsi;
1799 if (best != NULL)
1800 break;
1801 }
1802
1803 if (best == NULL &&
1804 strcmp (locales[i], lsi->locale) == 0)
1805 {
1806 best = lsi;
1807 if (c_local_schema != NULL)
1808 break;
1809 }
1810
1811 tmp = tmp->next;
1812 }
1813
1814 /* Quit as soon as we have the best possible locale */
1815 if (best != NULL && c_local_schema != NULL)
1816 break;
1817
1818 ++i;
1819 }
1820
1821 /* If we found localized info, add it to the return value,
1822 * fall back to C locale if we can
1823 */
1824
1825 if (best && best->locale)
1826 gconf_schema_set_locale (schema, best->locale);
1827 else
1828 gconf_schema_set_locale (schema, "C");
1829
1830 if (best && best->default_value)
1831 gconf_schema_set_default_value (schema, best->default_value);
1832 else if (c_local_schema && c_local_schema->default_value)
1833 gconf_schema_set_default_value (schema, c_local_schema->default_value);
1834
1835 if (best && best->short_desc)
1836 gconf_schema_set_short_desc (schema, best->short_desc);
1837 else if (c_local_schema && c_local_schema->short_desc)
1838 gconf_schema_set_short_desc (schema, c_local_schema->short_desc);
1839
1840 if (best && best->long_desc)
1841 gconf_schema_set_long_desc (schema, best->long_desc);
1842 else if (c_local_schema && c_local_schema->long_desc)
1843 gconf_schema_set_long_desc (schema, c_local_schema->long_desc);
1844
1845 return retval;
1846 }
1847 }
1848
1849 const char*
markup_entry_get_name(MarkupEntry * entry)1850 markup_entry_get_name (MarkupEntry *entry)
1851 {
1852 g_return_val_if_fail (entry->dir != NULL, NULL);
1853 g_return_val_if_fail (entry->dir->entries_loaded, NULL);
1854
1855 return entry->name;
1856 }
1857
1858 const char*
markup_entry_get_schema_name(MarkupEntry * entry)1859 markup_entry_get_schema_name (MarkupEntry *entry)
1860 {
1861 g_return_val_if_fail (entry->dir != NULL, NULL);
1862 g_return_val_if_fail (entry->dir->entries_loaded, NULL);
1863
1864 return entry->schema_name;
1865 }
1866
1867 const char*
markup_entry_get_mod_user(MarkupEntry * entry)1868 markup_entry_get_mod_user (MarkupEntry *entry)
1869 {
1870 g_return_val_if_fail (entry->dir != NULL, NULL);
1871 g_return_val_if_fail (entry->dir->entries_loaded, NULL);
1872
1873 return entry->mod_user;
1874 }
1875
1876 GTime
markup_entry_get_mod_time(MarkupEntry * entry)1877 markup_entry_get_mod_time (MarkupEntry *entry)
1878 {
1879 g_return_val_if_fail (entry->dir != NULL, 0);
1880 g_return_val_if_fail (entry->dir->entries_loaded, 0);
1881
1882 return entry->mod_time;
1883 }
1884
1885 static void
markup_entry_set_mod_user(MarkupEntry * entry,const char * muser)1886 markup_entry_set_mod_user (MarkupEntry *entry,
1887 const char *muser)
1888 {
1889 if (muser == entry->mod_user)
1890 return;
1891
1892 g_free (entry->mod_user);
1893 entry->mod_user = g_strdup (muser);
1894 }
1895
1896 static void
markup_entry_set_mod_time(MarkupEntry * entry,GTime mtime)1897 markup_entry_set_mod_time (MarkupEntry *entry,
1898 GTime mtime)
1899 {
1900 entry->mod_time = mtime;
1901 }
1902
1903 /*
1904 * Parser
1905 */
1906
1907
1908 /* The GConf XML format is on a lot of crack. When I wrote it,
1909 * I didn't know what I was doing, and now we're stuck with it.
1910 * Apologies.
1911 */
1912
1913 typedef enum
1914 {
1915 STATE_START,
1916 STATE_GCONF,
1917 STATE_DIR,
1918 STATE_ENTRY,
1919 STATE_STRINGVALUE,
1920 STATE_LONGDESC,
1921
1922 STATE_LOCAL_SCHEMA,
1923
1924 /* these all work just like <entry> in storing a value but have no
1925 * name/muser/mtime/owner and in the case of car/cdr/li can only
1926 * store primitive values.
1927 */
1928
1929 STATE_DEFAULT,
1930 STATE_CAR,
1931 STATE_CDR,
1932 STATE_LI
1933 } ParseState;
1934
1935 typedef struct
1936 {
1937 GSList *states;
1938
1939 MarkupDir *root;
1940 GSList *dir_stack;
1941
1942 MarkupEntry *current_entry;
1943 GSList *value_stack;
1944 GSList *value_freelist;
1945
1946 /* Collected while parsing a schema entry */
1947 GSList *local_schemas;
1948
1949 char *locale;
1950
1951 guint allow_subdirs : 1;
1952 guint parsing_local_descs : 1;
1953 } ParseInfo;
1954
1955 static void set_error (GError **err,
1956 GMarkupParseContext *context,
1957 int error_code,
1958 const char *format,
1959 ...) G_GNUC_PRINTF (4, 5);
1960
1961 static void dir_stack_push (ParseInfo *info,
1962 MarkupDir *dir);
1963
1964 static void start_element_handler (GMarkupParseContext *context,
1965 const gchar *element_name,
1966 const gchar **attribute_names,
1967 const gchar **attribute_values,
1968 gpointer user_data,
1969 GError **error);
1970 static void end_element_handler (GMarkupParseContext *context,
1971 const gchar *element_name,
1972 gpointer user_data,
1973 GError **error);
1974 static void text_handler (GMarkupParseContext *context,
1975 const gchar *text,
1976 gsize text_len,
1977 gpointer user_data,
1978 GError **error);
1979
1980 static GMarkupParser gconf_parser = {
1981 start_element_handler,
1982 end_element_handler,
1983 text_handler,
1984 NULL,
1985 NULL
1986 };
1987
1988 static void
set_error(GError ** err,GMarkupParseContext * context,int error_code,const char * format,...)1989 set_error (GError **err,
1990 GMarkupParseContext *context,
1991 int error_code,
1992 const char *format,
1993 ...)
1994 {
1995 int line, ch;
1996 va_list args;
1997 char *str;
1998
1999 g_markup_parse_context_get_position (context, &line, &ch);
2000
2001 va_start (args, format);
2002 str = g_strdup_vprintf (format, args);
2003 va_end (args);
2004
2005 g_set_error (err, GCONF_ERROR, error_code,
2006 _("Line %d character %d: %s"),
2007 line, ch, str);
2008
2009 g_free (str);
2010 }
2011
2012 static void
parse_info_init(ParseInfo * info,MarkupDir * root,gboolean allow_subdirs,const char * locale)2013 parse_info_init (ParseInfo *info,
2014 MarkupDir *root,
2015 gboolean allow_subdirs,
2016 const char *locale)
2017 {
2018 info->states = g_slist_prepend (NULL, GINT_TO_POINTER (STATE_START));
2019
2020 info->root = root;
2021 info->dir_stack = NULL;
2022
2023 info->current_entry = NULL;
2024 info->value_stack = NULL;
2025 info->value_freelist = NULL;
2026
2027 info->local_schemas = NULL;
2028
2029 info->locale = g_strdup (locale);
2030
2031 info->allow_subdirs = allow_subdirs != FALSE;
2032 info->parsing_local_descs = info->locale != NULL;
2033
2034 dir_stack_push (info, root);
2035 }
2036
2037 static void
parse_info_free(ParseInfo * info)2038 parse_info_free (ParseInfo *info)
2039 {
2040 g_free (info->locale);
2041
2042 g_slist_free (info->dir_stack);
2043
2044 /* Don't free current_entry - always owned by tree */
2045
2046 g_slist_foreach (info->local_schemas,
2047 (GFunc) local_schema_info_free,
2048 NULL);
2049 g_slist_free (info->local_schemas);
2050
2051 /* only free values on the freelist, not those on the stack,
2052 * but all values in the freelist are also in the stack.
2053 */
2054 g_slist_foreach (info->value_freelist, (GFunc) gconf_value_free, NULL);
2055 g_slist_free (info->value_freelist);
2056 g_slist_free (info->value_stack);
2057
2058 g_slist_free (info->states);
2059 }
2060
2061 static void
push_state(ParseInfo * info,ParseState state)2062 push_state (ParseInfo *info,
2063 ParseState state)
2064 {
2065 info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state));
2066 }
2067
2068 static void
pop_state(ParseInfo * info)2069 pop_state (ParseInfo *info)
2070 {
2071 g_return_if_fail (info->states != NULL);
2072
2073 info->states = g_slist_remove (info->states, info->states->data);
2074 }
2075
2076 static ParseState
peek_state(ParseInfo * info)2077 peek_state (ParseInfo *info)
2078 {
2079 g_return_val_if_fail (info->states != NULL, STATE_START);
2080
2081 return GPOINTER_TO_INT (info->states->data);
2082 }
2083
2084
2085 /* add_to_freelist means that if the parse is aborted
2086 * while the value is on the stack, free that value
2087 */
2088 static void
value_stack_push(ParseInfo * info,GConfValue * value,gboolean add_to_freelist)2089 value_stack_push (ParseInfo *info,
2090 GConfValue *value,
2091 gboolean add_to_freelist)
2092 {
2093 info->value_stack = g_slist_prepend (info->value_stack, value);
2094 if (add_to_freelist)
2095 info->value_freelist = g_slist_prepend (info->value_freelist, value);
2096 }
2097
2098 static GConfValue*
value_stack_peek(ParseInfo * info)2099 value_stack_peek (ParseInfo *info)
2100 {
2101 return info->value_stack ? info->value_stack->data : NULL;
2102 }
2103
2104 static GConfValue*
value_stack_pop(ParseInfo * info)2105 value_stack_pop (ParseInfo *info)
2106 {
2107 GConfValue *retval;
2108
2109 if (!info->value_stack)
2110 return NULL;
2111
2112 retval = info->value_stack->data;
2113
2114 info->value_freelist = g_slist_remove (info->value_freelist, retval);
2115 info->value_stack = g_slist_remove (info->value_stack, retval);
2116
2117 return retval;
2118 }
2119
2120 static void
dir_stack_push(ParseInfo * info,MarkupDir * dir)2121 dir_stack_push (ParseInfo *info,
2122 MarkupDir *dir)
2123 {
2124 info->dir_stack = g_slist_prepend (info->dir_stack, dir);
2125 }
2126
2127 static MarkupDir*
dir_stack_peek(ParseInfo * info)2128 dir_stack_peek (ParseInfo *info)
2129 {
2130 g_return_val_if_fail (info->dir_stack != NULL, NULL);
2131
2132 return info->dir_stack->data;
2133 }
2134
2135 static MarkupDir*
dir_stack_pop(ParseInfo * info)2136 dir_stack_pop (ParseInfo *info)
2137 {
2138 MarkupDir *retval;
2139
2140 g_return_val_if_fail (info->dir_stack != NULL, NULL);
2141
2142 retval = info->dir_stack->data;
2143 info->dir_stack = g_slist_remove (info->dir_stack, retval);
2144
2145 return retval;
2146 }
2147
2148 #define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0)
2149
2150 typedef struct
2151 {
2152 const char *name;
2153 const char **retloc;
2154 } LocateAttr;
2155
2156 static gboolean
locate_attributes(GMarkupParseContext * context,const char * element_name,const char ** attribute_names,const char ** attribute_values,GError ** error,const char * first_attribute_name,const char ** first_attribute_retloc,...)2157 locate_attributes (GMarkupParseContext *context,
2158 const char *element_name,
2159 const char **attribute_names,
2160 const char **attribute_values,
2161 GError **error,
2162 const char *first_attribute_name,
2163 const char **first_attribute_retloc,
2164 ...)
2165 {
2166 va_list args;
2167 const char *name;
2168 const char **retloc;
2169 int n_attrs;
2170 #define MAX_ATTRS 24
2171 LocateAttr attrs[MAX_ATTRS];
2172 gboolean retval;
2173 int i;
2174
2175 g_return_val_if_fail (first_attribute_name != NULL, FALSE);
2176 g_return_val_if_fail (first_attribute_retloc != NULL, FALSE);
2177
2178 n_attrs = 1;
2179 attrs[0].name = first_attribute_name;
2180 attrs[0].retloc = first_attribute_retloc;
2181 *first_attribute_retloc = NULL;
2182
2183 va_start (args, first_attribute_retloc);
2184
2185 name = va_arg (args, const char*);
2186 retloc = va_arg (args, const char**);
2187
2188 while (name != NULL)
2189 {
2190 g_return_val_if_fail (retloc != NULL, FALSE);
2191
2192 g_assert (n_attrs < MAX_ATTRS);
2193
2194 attrs[n_attrs].name = name;
2195 attrs[n_attrs].retloc = retloc;
2196 n_attrs += 1;
2197 *retloc = NULL;
2198
2199 name = va_arg (args, const char*);
2200 retloc = va_arg (args, const char**);
2201 }
2202
2203 va_end (args);
2204
2205 retval = TRUE;
2206
2207 for (i = 0; attribute_names[i]; i++)
2208 {
2209 int j;
2210
2211 for (j = 0; j < n_attrs; j++)
2212 {
2213 /* already matched */
2214 if (attrs[j].name == NULL)
2215 continue;
2216
2217 if (strcmp (attrs[j].name, attribute_names[i]) == 0)
2218 {
2219 retloc = attrs[j].retloc;
2220 attrs[j].name = NULL;
2221
2222 /* if this fails we passed the same retloc twice */
2223 g_assert (*retloc == NULL);
2224
2225 *retloc = attribute_values[i];
2226 break;
2227 }
2228 }
2229
2230 if (j >= n_attrs)
2231 {
2232 set_error (error, context,
2233 GCONF_ERROR_PARSE_ERROR,
2234 _("Attribute \"%s\" is invalid on <%s> element in this context"),
2235 attribute_names[i], element_name);
2236 retval = FALSE;
2237 break;
2238 }
2239 }
2240
2241 return retval;
2242 }
2243
2244 static gboolean
check_no_attributes(GMarkupParseContext * context,const char * element_name,const char ** attribute_names,const char ** attribute_values,GError ** error)2245 check_no_attributes (GMarkupParseContext *context,
2246 const char *element_name,
2247 const char **attribute_names,
2248 const char **attribute_values,
2249 GError **error)
2250 {
2251 if (attribute_names[0] != NULL)
2252 {
2253 set_error (error, context,
2254 GCONF_ERROR_PARSE_ERROR,
2255 _("Attribute \"%s\" is invalid on <%s> element in this context"),
2256 attribute_names[0], element_name);
2257 return FALSE;
2258 }
2259
2260 return TRUE;
2261 }
2262
2263 static gboolean
int_from_string(GMarkupParseContext * context,const char * str,int * val,GError ** error)2264 int_from_string (GMarkupParseContext *context,
2265 const char *str,
2266 int *val,
2267 GError **error)
2268 {
2269 char* endptr = NULL;
2270 glong result;
2271
2272 *val = 0;
2273
2274 errno = 0;
2275 result = strtol (str, &endptr, 10);
2276
2277 if (endptr == str)
2278 {
2279 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2280 _("Didn't understand `%s' (expected integer)"),
2281 str);
2282 return FALSE;
2283 }
2284 else if (errno == ERANGE || result < G_MININT || result > G_MAXINT)
2285 {
2286 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2287 _("Integer `%s' is too large or small"),
2288 str);
2289 return FALSE;
2290 }
2291 else
2292 {
2293 *val = result;
2294 return TRUE;
2295 }
2296 }
2297
2298 static gboolean
bool_from_string(GMarkupParseContext * context,const char * str,gboolean * val,GError ** error)2299 bool_from_string (GMarkupParseContext *context,
2300 const char *str,
2301 gboolean *val,
2302 GError **error)
2303 {
2304 if (strcmp (str, "true") == 0)
2305 {
2306 *val = TRUE;
2307 return TRUE;
2308 }
2309 else if (strcmp (str, "false") == 0)
2310 {
2311 *val = FALSE;
2312 return TRUE;
2313 }
2314 else
2315 {
2316 *val = FALSE;
2317
2318 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2319 _("Didn't understand `%s' (expected true or false)"),
2320 str);
2321 return FALSE;
2322 }
2323 }
2324
2325
2326 static gboolean
float_from_string(GMarkupParseContext * context,const char * str,double * val,GError ** error)2327 float_from_string (GMarkupParseContext *context,
2328 const char *str,
2329 double *val,
2330 GError **error)
2331 {
2332 double num;
2333
2334 if (gconf_string_to_double (str, &num))
2335 {
2336 *val = num;
2337 return TRUE;
2338 }
2339 else
2340 {
2341 *val = 0.0;
2342 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2343 _("Didn't understand `%s' (expected real number)"),
2344 str);
2345 return FALSE;
2346 }
2347 }
2348
2349 static void
parse_value_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,GConfValue ** retval,GError ** error)2350 parse_value_element (GMarkupParseContext *context,
2351 const gchar *element_name,
2352 const gchar **attribute_names,
2353 const gchar **attribute_values,
2354 GConfValue **retval,
2355 GError **error)
2356 {
2357 const char *type;
2358 const char *stype;
2359 const char *car_type;
2360 const char *cdr_type;
2361 const char *value;
2362 /* check out the crack; "ltype" is for nodes storing a list,
2363 * and "list_type" is for nodes storing a schema
2364 */
2365 const char *ltype;
2366 const char *list_type;
2367 const char *owner;
2368 GConfValueType vtype;
2369 const char *dummy1, *dummy2, *dummy3, *dummy4;
2370
2371 #if 0
2372 g_assert (ELEMENT_IS ("entry") ||
2373 ELEMENT_IS ("default") ||
2374 ELEMENT_IS ("cdr") ||
2375 ELEMENT_IS ("car") ||
2376 ELEMENT_IS ("li"));
2377 #endif
2378
2379 *retval = NULL;
2380
2381 value = NULL;
2382 type = NULL;
2383 stype = NULL;
2384 ltype = NULL;
2385 list_type = NULL;
2386 car_type = NULL;
2387 cdr_type = NULL;
2388 owner = NULL;
2389
2390 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
2391 error,
2392 "value", &value,
2393 "type", &type,
2394 "stype", &stype,
2395 "ltype", <ype,
2396 "list_type", &list_type,
2397 "car_type", &car_type,
2398 "cdr_type", &cdr_type,
2399 "owner", &owner,
2400
2401 /* And these are just to eat any error messages */
2402 "name", &dummy1,
2403 "muser", &dummy2,
2404 "mtime", &dummy3,
2405 "schema", &dummy4,
2406
2407 NULL))
2408 return;
2409
2410 if (type == NULL)
2411 {
2412 /* in fact this is a rather common case */
2413 /* set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2414 _("No \"%s\" attribute on element <%s>"),
2415 "type", element_name); */
2416 return;
2417 }
2418
2419 vtype = gconf_value_type_from_string (type);
2420 if (vtype == GCONF_VALUE_INVALID)
2421 {
2422 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2423 _("Unknown value \"%s\" for \"%s\" attribute on element <%s>"),
2424 type, "type", element_name);
2425 return;
2426 }
2427
2428 switch (vtype)
2429 {
2430 case GCONF_VALUE_STRING:
2431 {
2432 *retval = gconf_value_new (GCONF_VALUE_STRING);
2433 }
2434 break;
2435
2436 case GCONF_VALUE_LIST:
2437 {
2438 GConfValueType lvtype;
2439
2440 if (ltype == NULL)
2441 {
2442 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2443 _("No \"%s\" attribute on element <%s>"),
2444 "ltype", element_name);
2445 return;
2446 }
2447
2448 lvtype = gconf_value_type_from_string (ltype);
2449
2450 switch (lvtype)
2451 {
2452 case GCONF_VALUE_INVALID:
2453 case GCONF_VALUE_LIST:
2454 case GCONF_VALUE_PAIR:
2455 case GCONF_VALUE_SCHEMA:
2456 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2457 _("Invalid ltype \"%s\" on <%s>"),
2458 ltype, element_name);
2459 return;
2460 break;
2461 default:
2462 break;
2463 }
2464
2465 *retval = gconf_value_new (GCONF_VALUE_LIST);
2466
2467 gconf_value_set_list_type (*retval,
2468 lvtype);
2469 }
2470 break;
2471
2472 case GCONF_VALUE_SCHEMA:
2473 {
2474 GConfValueType schema_vtype;
2475 GConfSchema *schema;
2476 GConfValueType car_vtype;
2477 GConfValueType cdr_vtype;
2478 GConfValueType list_vtype;
2479
2480 if (stype == NULL)
2481 {
2482 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2483 _("No \"%s\" attribute on element <%s>"),
2484 "stype", element_name);
2485 return;
2486 }
2487
2488 /* init for compiler warnings */
2489 car_vtype = GCONF_VALUE_INVALID;
2490 cdr_vtype = GCONF_VALUE_INVALID;
2491 list_vtype = GCONF_VALUE_INVALID;
2492
2493 schema_vtype = gconf_value_type_from_string (stype);
2494
2495 if (schema_vtype == GCONF_VALUE_PAIR)
2496 {
2497
2498 #if 0
2499 /* We have to allow missing car_type/cdr_type because
2500 * old versions of gconf would write it out that way
2501 * (if a schema was provided by an app and the
2502 * schema was missing these fields)
2503 */
2504 if (car_type == NULL)
2505 {
2506 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2507 _("No \"%s\" attribute on element <%s>"),
2508 "car_type", element_name);
2509 return;
2510 }
2511
2512 if (cdr_type == NULL)
2513 {
2514 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2515 _("No \"%s\" attribute on element <%s>"),
2516 "cdr_type", element_name);
2517 return;
2518 }
2519 #endif
2520
2521 if (car_type)
2522 car_vtype = gconf_value_type_from_string (car_type);
2523 else
2524 car_vtype = GCONF_VALUE_INVALID;
2525
2526 if (cdr_type)
2527 cdr_vtype = gconf_value_type_from_string (cdr_type);
2528 else
2529 cdr_vtype = GCONF_VALUE_INVALID;
2530
2531 switch (car_vtype)
2532 {
2533 case GCONF_VALUE_LIST:
2534 case GCONF_VALUE_PAIR:
2535 case GCONF_VALUE_SCHEMA:
2536 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2537 _("Invalid first-element type \"%s\" on <%s>"),
2538 car_type, element_name);
2539 return;
2540 break;
2541 default:
2542 break;
2543 }
2544
2545 switch (cdr_vtype)
2546 {
2547 case GCONF_VALUE_LIST:
2548 case GCONF_VALUE_PAIR:
2549 case GCONF_VALUE_SCHEMA:
2550 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2551 _("Invalid cdr_type \"%s\" on <%s>"),
2552 cdr_type, element_name);
2553 return;
2554 break;
2555 default:
2556 break;
2557 }
2558 }
2559 else if (schema_vtype == GCONF_VALUE_LIST)
2560 {
2561 #if 0
2562 /* We have to allow missing list_type because
2563 * old versions of gconf would write it out that way
2564 * (if a schema was provided by an app and the
2565 * schema was missing these fields)
2566 */
2567 if (list_type == NULL)
2568 {
2569 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2570 _("No \"%s\" attribute on element <%s>"),
2571 "list_type", element_name);
2572 return;
2573 }
2574 #endif
2575
2576 if (list_type)
2577 list_vtype = gconf_value_type_from_string (list_type);
2578 else
2579 list_vtype = GCONF_VALUE_INVALID;
2580
2581 switch (list_vtype)
2582 {
2583 case GCONF_VALUE_LIST:
2584 case GCONF_VALUE_PAIR:
2585 case GCONF_VALUE_SCHEMA:
2586 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2587 _("Invalid list_type \"%s\" on <%s>"),
2588 list_type, element_name);
2589 return;
2590 break;
2591 default:
2592 break;
2593 }
2594 }
2595
2596 *retval = gconf_value_new (GCONF_VALUE_SCHEMA);
2597
2598 schema = gconf_schema_new ();
2599 gconf_schema_set_type (schema, schema_vtype);
2600
2601 if (schema_vtype == GCONF_VALUE_PAIR)
2602 {
2603 gconf_schema_set_car_type (schema, car_vtype);
2604 gconf_schema_set_cdr_type (schema, cdr_vtype);
2605 }
2606 else if (schema_vtype == GCONF_VALUE_LIST)
2607 {
2608 gconf_schema_set_list_type (schema, list_vtype);
2609 }
2610
2611 if (owner)
2612 gconf_schema_set_owner (schema, owner);
2613
2614 gconf_value_set_schema_nocopy (*retval, schema);
2615 }
2616 break;
2617
2618 case GCONF_VALUE_PAIR:
2619 {
2620 *retval = gconf_value_new (GCONF_VALUE_PAIR);
2621 }
2622 break;
2623
2624 case GCONF_VALUE_INT:
2625 case GCONF_VALUE_BOOL:
2626 case GCONF_VALUE_FLOAT:
2627 {
2628 double fval = 0.0;
2629 gboolean bval = FALSE;
2630 int ival = 0;
2631
2632 if (value == NULL)
2633 {
2634 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2635 _("No \"%s\" attribute on element <%s>"),
2636 "value", element_name);
2637 return;
2638 }
2639
2640 switch (vtype)
2641 {
2642 case GCONF_VALUE_INT:
2643 if (!int_from_string (context, value, &ival, error))
2644 return;
2645 break;
2646
2647 case GCONF_VALUE_BOOL:
2648 if (!bool_from_string (context, value, &bval, error))
2649 return;
2650 break;
2651
2652 case GCONF_VALUE_FLOAT:
2653 if (!float_from_string (context, value, &fval, error))
2654 return;
2655 break;
2656
2657 default:
2658 g_assert_not_reached ();
2659 }
2660
2661 *retval = gconf_value_new (vtype);
2662
2663 switch (vtype)
2664 {
2665 case GCONF_VALUE_INT:
2666 gconf_value_set_int (*retval, ival);
2667 break;
2668
2669 case GCONF_VALUE_BOOL:
2670 gconf_value_set_bool (*retval, bval);
2671 break;
2672
2673 case GCONF_VALUE_FLOAT:
2674 gconf_value_set_float (*retval, fval);
2675 break;
2676
2677 default:
2678 g_assert_not_reached ();
2679 }
2680 }
2681 break;
2682
2683 case GCONF_VALUE_INVALID:
2684 g_assert_not_reached ();
2685 break;
2686 }
2687 }
2688
2689 static void
parse_entry_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)2690 parse_entry_element (GMarkupParseContext *context,
2691 const gchar *element_name,
2692 const gchar **attribute_names,
2693 const gchar **attribute_values,
2694 ParseInfo *info,
2695 GError **error)
2696 {
2697 MarkupEntry *entry;
2698
2699 g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR);
2700 g_return_if_fail (ELEMENT_IS ("entry"));
2701 g_return_if_fail (info->current_entry == NULL);
2702
2703 push_state (info, STATE_ENTRY);
2704
2705 if (!info->parsing_local_descs)
2706 {
2707 const char *name;
2708 const char *muser;
2709 const char *mtime;
2710 const char *schema;
2711 const char *type;
2712 const char *dummy1, *dummy2, *dummy3, *dummy4;
2713 const char *dummy5, *dummy6, *dummy7;
2714 GConfValue *value;
2715 GError *tmp_err;
2716
2717 name = NULL;
2718 muser = NULL;
2719 mtime = NULL;
2720 schema = NULL;
2721 type = NULL;
2722
2723 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
2724 error,
2725 "name", &name,
2726 "muser", &muser,
2727 "mtime", &mtime,
2728 "schema", &schema,
2729 "type", &type,
2730
2731 /* These are allowed but we don't use them until
2732 * parse_value_element
2733 */
2734 "value", &dummy1,
2735 "stype", &dummy2,
2736 "ltype", &dummy3,
2737 "list_type", &dummy4,
2738 "car_type", &dummy5,
2739 "cdr_type", &dummy6,
2740 "owner", &dummy7,
2741 NULL))
2742 return;
2743
2744 if (name == NULL)
2745 {
2746 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2747 _("No \"%s\" attribute on element <%s>"),
2748 "name", element_name);
2749 return;
2750 }
2751
2752 /* Entries can exist just for the schema name,
2753 * lacking a value element. But if the entry has a type
2754 * attribute, it's supposed to have a value.
2755 */
2756 value = NULL;
2757 tmp_err = NULL;
2758 parse_value_element (context, element_name, attribute_names,
2759 attribute_values, &value,
2760 &tmp_err);
2761
2762 if (tmp_err)
2763 {
2764 if (type != NULL)
2765 {
2766 g_propagate_error (error, tmp_err);
2767 return;
2768 }
2769 else
2770 g_error_free (tmp_err);
2771 }
2772
2773 entry = markup_entry_new (dir_stack_peek (info), name);
2774 if (value != NULL)
2775 {
2776 entry->value = value;
2777 value_stack_push (info, value, FALSE); /* FALSE since entry owns it */
2778 }
2779
2780 if (muser)
2781 markup_entry_set_mod_user (entry, muser);
2782
2783 if (mtime)
2784 {
2785 GTime vmtime;
2786
2787 vmtime = gconf_string_to_gulong (mtime);
2788
2789 markup_entry_set_mod_time (entry, vmtime);
2790 }
2791
2792 /* don't use markup_entry_set_schema_name because it would
2793 * mess up the modtime
2794 */
2795 if (schema)
2796 entry->schema_name = g_strdup (schema);
2797 }
2798 else
2799 {
2800 MarkupDir *dir;
2801 GSList *tmp;
2802 const char *name;
2803
2804 name = NULL;
2805
2806 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
2807 error,
2808 "name", &name,
2809 NULL))
2810 return;
2811
2812 if (name == NULL)
2813 {
2814 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2815 _("No \"%s\" attribute on element <%s>"),
2816 "name", element_name);
2817 return;
2818 }
2819
2820 dir = dir_stack_peek (info);
2821
2822 entry = NULL;
2823
2824 tmp = dir->entries;
2825 while (tmp != NULL)
2826 {
2827 entry = tmp->data;
2828
2829 if (strcmp (entry->name, name) == 0)
2830 break;
2831 else
2832 entry = NULL;
2833
2834 tmp = tmp->next;
2835 }
2836
2837 /* Note: entry can be NULL here, in which case we'll discard
2838 * the LocalSchemaInfo once we've finished parsing this entry
2839 */
2840 }
2841
2842 info->current_entry = entry;
2843 }
2844
2845 static void
parse_dir_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)2846 parse_dir_element (GMarkupParseContext *context,
2847 const gchar *element_name,
2848 const gchar **attribute_names,
2849 const gchar **attribute_values,
2850 ParseInfo *info,
2851 GError **error)
2852 {
2853 MarkupDir *parent;
2854 MarkupDir *dir;
2855 const char *name;
2856
2857 g_return_if_fail (peek_state (info) == STATE_GCONF || peek_state (info) == STATE_DIR);
2858 g_return_if_fail (ELEMENT_IS ("dir"));
2859
2860 push_state (info, STATE_DIR);
2861
2862 name = NULL;
2863
2864 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
2865 error,
2866 "name", &name,
2867 NULL))
2868 return;
2869
2870 if (name == NULL)
2871 {
2872 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
2873 _("No \"%s\" attribute on element <%s>"),
2874 "name", element_name);
2875 return;
2876 }
2877
2878 dir = NULL;
2879 parent = dir_stack_peek (info);
2880
2881 if (!info->parsing_local_descs)
2882 {
2883 dir = markup_dir_new (info->root->tree, parent, name);
2884
2885 dir->not_in_filesystem = TRUE;
2886 dir->entries_loaded = TRUE;
2887 dir->subdirs_loaded = TRUE;
2888 }
2889 else
2890 {
2891 GSList *tmp;
2892
2893 tmp = parent->subdirs;
2894 while (tmp != NULL)
2895 {
2896 dir = tmp->data;
2897
2898 if (strcmp (dir->name, name) == 0)
2899 break;
2900 else
2901 dir = NULL;
2902
2903 tmp = tmp->next;
2904 }
2905
2906 if (dir == NULL)
2907 {
2908 dir = markup_dir_new (info->root->tree, parent, name);
2909
2910 /* This is a dummy directory which will be deleted when
2911 * we've finised parsing the contents of this element.
2912 */
2913 dir->is_parser_dummy = TRUE;
2914 }
2915 }
2916
2917 g_assert (dir != NULL);
2918
2919 dir_stack_push (info, dir);
2920 }
2921
2922 static void
parse_local_schema_child_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)2923 parse_local_schema_child_element (GMarkupParseContext *context,
2924 const gchar *element_name,
2925 const gchar **attribute_names,
2926 const gchar **attribute_values,
2927 ParseInfo *info,
2928 GError **error)
2929 {
2930 LocalSchemaInfo *local_schema;
2931
2932 g_return_if_fail (peek_state (info) == STATE_LOCAL_SCHEMA);
2933
2934 local_schema = info->local_schemas->data;
2935
2936 if (ELEMENT_IS ("default") && !info->parsing_local_descs)
2937 {
2938 GConfValue *value;
2939
2940 push_state (info, STATE_DEFAULT);
2941
2942 value = NULL;
2943 parse_value_element (context, element_name, attribute_names,
2944 attribute_values, &value,
2945 error);
2946 if (value == NULL)
2947 return;
2948
2949 if (local_schema->default_value != NULL)
2950 {
2951 gconf_value_free (value);
2952 set_error (error, context,
2953 GCONF_ERROR_PARSE_ERROR,
2954 _("Two <default> elements below a <local_schema>"));
2955 return;
2956 }
2957
2958 local_schema->default_value = value;
2959 value_stack_push (info, value, FALSE); /* local_schema owns it */
2960 }
2961 else if (ELEMENT_IS ("longdesc"))
2962 {
2963 push_state (info, STATE_LONGDESC);
2964
2965 if (local_schema->long_desc != NULL)
2966 {
2967 set_error (error, context,
2968 GCONF_ERROR_PARSE_ERROR,
2969 _("Two <longdesc> elements below a <local_schema>"));
2970 }
2971 }
2972 else
2973 {
2974 set_error (error, context,
2975 GCONF_ERROR_PARSE_ERROR,
2976 _("Element <%s> is not allowed below <%s>"),
2977 element_name, "local_schema");
2978 }
2979 }
2980
2981 static void
parse_local_schema_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)2982 parse_local_schema_element (GMarkupParseContext *context,
2983 const gchar *element_name,
2984 const gchar **attribute_names,
2985 const gchar **attribute_values,
2986 ParseInfo *info,
2987 GError **error)
2988 {
2989 const char *locale;
2990 const char *short_desc;
2991 LocalSchemaInfo *local_schema;
2992
2993 g_return_if_fail (ELEMENT_IS ("local_schema"));
2994
2995 if (!info->parsing_local_descs &&
2996 (info->current_entry == NULL ||
2997 info->current_entry->value == NULL ||
2998 info->current_entry->value->type != GCONF_VALUE_SCHEMA))
2999 {
3000 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3001 _("<%s> provided but current element does not have type %s"),
3002 "local_schema", "schema");
3003 return;
3004 }
3005
3006 push_state (info, STATE_LOCAL_SCHEMA);
3007
3008 locale = NULL;
3009 short_desc = NULL;
3010
3011 if (!info->parsing_local_descs)
3012 {
3013 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
3014 error,
3015 "locale", &locale,
3016 "short_desc", &short_desc,
3017 NULL))
3018 return;
3019
3020 if (locale == NULL)
3021 {
3022 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3023 _("No \"%s\" attribute on element <%s>"),
3024 "locale", element_name);
3025 return;
3026 }
3027 }
3028 else
3029 {
3030 if (!locate_attributes (context, element_name, attribute_names, attribute_values,
3031 error,
3032 "short_desc", &short_desc,
3033 NULL))
3034 return;
3035
3036 locale = info->locale;
3037 }
3038
3039 local_schema = local_schema_info_new ();
3040 local_schema->locale = g_strdup (locale);
3041 local_schema->short_desc = g_strdup (short_desc);
3042
3043 info->local_schemas = g_slist_prepend (info->local_schemas,
3044 local_schema);
3045 }
3046
3047 static void
parse_car_or_cdr_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)3048 parse_car_or_cdr_element (GMarkupParseContext *context,
3049 const gchar *element_name,
3050 const gchar **attribute_names,
3051 const gchar **attribute_values,
3052 ParseInfo *info,
3053 GError **error)
3054 {
3055 ParseState current_state;
3056 GConfValue *value;
3057 GConfValue *pair;
3058
3059 current_state = ELEMENT_IS ("car") ? STATE_CAR : STATE_CDR;
3060 push_state (info, current_state);
3061
3062 value = NULL;
3063 parse_value_element (context, element_name, attribute_names,
3064 attribute_values, &value,
3065 error);
3066 if (value == NULL)
3067 return;
3068
3069 pair = value_stack_peek (info);
3070
3071 if (pair->type == GCONF_VALUE_PAIR)
3072 {
3073 if (current_state == STATE_CAR)
3074 {
3075 if (gconf_value_get_car (pair) == NULL)
3076 {
3077 gconf_value_set_car_nocopy (pair, value);
3078 value_stack_push (info, value, FALSE); /* pair owns it */
3079 }
3080 else
3081 {
3082 gconf_value_free (value);
3083 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3084 _("Two <car> elements given for same pair"));
3085 }
3086 }
3087 else
3088 {
3089 if (gconf_value_get_cdr (pair) == NULL)
3090 {
3091 gconf_value_set_cdr_nocopy (pair, value);
3092 value_stack_push (info, value, FALSE); /* pair owns it */
3093 }
3094 else
3095 {
3096 gconf_value_free (value);
3097 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3098 _("Two <cdr> elements given for same pair"));
3099 }
3100 }
3101 }
3102 else
3103 {
3104 gconf_value_free (value);
3105 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3106 _("<%s> provided but current element does not have type %s"),
3107 current_state == STATE_CAR ? "car" : "cdr", "pair");
3108 }
3109 }
3110
3111 static void
parse_li_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)3112 parse_li_element (GMarkupParseContext *context,
3113 const gchar *element_name,
3114 const gchar **attribute_names,
3115 const gchar **attribute_values,
3116 ParseInfo *info,
3117 GError **error)
3118 {
3119 ParseState current_state;
3120 GConfValue *value;
3121 GConfValue *list;
3122
3123 current_state = peek_state (info);
3124
3125 push_state (info, STATE_LI);
3126
3127 value = NULL;
3128 parse_value_element (context, element_name, attribute_names,
3129 attribute_values, &value,
3130 error);
3131 if (value == NULL)
3132 return;
3133
3134 list = value_stack_peek (info);
3135
3136 if (list->type == GCONF_VALUE_LIST)
3137 {
3138 if (value->type == gconf_value_get_list_type (list))
3139 {
3140 GSList *slist;
3141
3142 slist = gconf_value_steal_list (list);
3143 slist = g_slist_append (slist, value);
3144 gconf_value_set_list_nocopy (list, slist);
3145
3146 value_stack_push (info, value, FALSE); /* FALSE since list owns it */
3147 }
3148 else
3149 {
3150 gconf_value_free (value);
3151 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3152 _("<li> has wrong type %s"),
3153 gconf_value_type_to_string (value->type));
3154 }
3155 }
3156 else
3157 {
3158 gconf_value_free (value);
3159 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3160 _("<%s> provided but current element does not have type %s"),
3161 "li", "list");
3162 }
3163 }
3164
3165 static void
parse_value_child_element(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,ParseInfo * info,GError ** error)3166 parse_value_child_element (GMarkupParseContext *context,
3167 const gchar *element_name,
3168 const gchar **attribute_names,
3169 const gchar **attribute_values,
3170 ParseInfo *info,
3171 GError **error)
3172 {
3173 ParseState current_state;
3174
3175 current_state = peek_state (info);
3176
3177 if (!info->parsing_local_descs)
3178 {
3179 if (current_state == STATE_ENTRY &&
3180 info->current_entry->value == NULL)
3181 {
3182 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3183 _("<%s> provided but parent <entry> does not have a value"),
3184 element_name);
3185 return;
3186 }
3187 else if (current_state == STATE_ENTRY)
3188 {
3189 g_assert (info->current_entry->value == value_stack_peek (info));
3190 }
3191 }
3192
3193 if (ELEMENT_IS ("stringvalue") && !info->parsing_local_descs)
3194 {
3195 GConfValue *value;
3196
3197 value = value_stack_peek (info);
3198
3199 if (value->type == GCONF_VALUE_STRING)
3200 {
3201 push_state (info, STATE_STRINGVALUE);
3202
3203 /* Put in an empty string, since <stringvalue></stringvalue>
3204 * doesn't result in a text_handler callback
3205 */
3206 gconf_value_set_string (value, "");
3207 }
3208 else
3209 {
3210 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3211 _("<%s> provided but current element does not have type %s"),
3212 "stringvalue", "string");
3213 }
3214 }
3215 else if (ELEMENT_IS ("local_schema"))
3216 {
3217 switch (current_state)
3218 {
3219 case STATE_CAR:
3220 case STATE_CDR:
3221 case STATE_LI:
3222 case STATE_DEFAULT:
3223 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3224 _("Element <%s> is not allowed inside current element"),
3225 element_name);
3226 break;
3227 case STATE_ENTRY:
3228 parse_local_schema_element (context, element_name,
3229 attribute_names, attribute_values,
3230 info, error);
3231 break;
3232 default:
3233 g_assert_not_reached ();
3234 break;
3235 }
3236 }
3237 else if ((ELEMENT_IS ("car") ||
3238 ELEMENT_IS ("cdr")) &&
3239 !info->parsing_local_descs)
3240 {
3241 switch (current_state)
3242 {
3243 case STATE_CAR:
3244 case STATE_CDR:
3245 case STATE_LI:
3246 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3247 _("Element <%s> is not allowed inside current element"),
3248 element_name);
3249 break;
3250 case STATE_DEFAULT:
3251 case STATE_ENTRY:
3252 parse_car_or_cdr_element (context, element_name,
3253 attribute_names, attribute_values,
3254 info, error);
3255 break;
3256 default:
3257 g_assert_not_reached ();
3258 break;
3259 }
3260 }
3261 else if (ELEMENT_IS ("li") && !info->parsing_local_descs)
3262 {
3263 switch (current_state)
3264 {
3265 case STATE_CAR:
3266 case STATE_CDR:
3267 case STATE_LI:
3268 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3269 _("Element <%s> is not allowed inside current element"),
3270 element_name);
3271 break;
3272 case STATE_DEFAULT:
3273 case STATE_ENTRY:
3274 parse_li_element (context, element_name,
3275 attribute_names, attribute_values,
3276 info, error);
3277 break;
3278 default:
3279 g_assert_not_reached ();
3280 break;
3281 }
3282 }
3283 else
3284 {
3285 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3286 _("Element <%s> is not allowed inside current element"),
3287 element_name);
3288 }
3289 }
3290
3291 static void
start_element_handler(GMarkupParseContext * context,const gchar * element_name,const gchar ** attribute_names,const gchar ** attribute_values,gpointer user_data,GError ** error)3292 start_element_handler (GMarkupParseContext *context,
3293 const gchar *element_name,
3294 const gchar **attribute_names,
3295 const gchar **attribute_values,
3296 gpointer user_data,
3297 GError **error)
3298 {
3299 ParseInfo *info = user_data;
3300 ParseState current_state;
3301
3302 current_state = peek_state (info);
3303
3304 switch (current_state)
3305 {
3306 case STATE_START:
3307 if (ELEMENT_IS ("gconf"))
3308 {
3309 if (!check_no_attributes (context, element_name,
3310 attribute_names, attribute_values,
3311 error))
3312 return;
3313
3314 push_state (info, STATE_GCONF);
3315 }
3316 else
3317 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3318 _("Outermost element in menu file must be <gconf> not <%s>"),
3319 element_name);
3320 break;
3321
3322 case STATE_GCONF:
3323 case STATE_DIR:
3324 if (ELEMENT_IS ("entry"))
3325 {
3326 parse_entry_element (context, element_name,
3327 attribute_names, attribute_values,
3328 info, error);
3329 }
3330 else if (ELEMENT_IS ("dir") && info->allow_subdirs)
3331 {
3332 parse_dir_element (context, element_name,
3333 attribute_names, attribute_values,
3334 info, error);
3335 }
3336 else
3337 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3338 _("Element <%s> is not allowed inside a <%s> element"),
3339 element_name, current_state == STATE_GCONF ? "gconf" : "dir");
3340 break;
3341
3342 case STATE_ENTRY:
3343 case STATE_DEFAULT:
3344 case STATE_CAR:
3345 case STATE_CDR:
3346 case STATE_LI:
3347 parse_value_child_element (context, element_name,
3348 attribute_names, attribute_values,
3349 info, error);
3350 break;
3351
3352 case STATE_LOCAL_SCHEMA:
3353 parse_local_schema_child_element (context, element_name,
3354 attribute_names, attribute_values,
3355 info, error);
3356 break;
3357
3358 case STATE_STRINGVALUE:
3359 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3360 _("Element <%s> is not allowed inside a <%s> element"),
3361 element_name, "stringvalue");
3362 break;
3363 case STATE_LONGDESC:
3364 set_error (error, context, GCONF_ERROR_PARSE_ERROR,
3365 _("Element <%s> is not allowed inside a <%s> element"),
3366 element_name, "longdesc");
3367 break;
3368 }
3369 }
3370
3371 static void
end_element_handler(GMarkupParseContext * context,const gchar * element_name,gpointer user_data,GError ** error)3372 end_element_handler (GMarkupParseContext *context,
3373 const gchar *element_name,
3374 gpointer user_data,
3375 GError **error)
3376 {
3377 ParseInfo *info = user_data;
3378
3379 switch (peek_state (info))
3380 {
3381 case STATE_START:
3382 break;
3383
3384 case STATE_ENTRY:
3385 if (!info->parsing_local_descs)
3386 {
3387 g_assert (info->current_entry);
3388 g_assert (info->current_entry->local_schemas == NULL);
3389
3390 info->current_entry->local_schemas = g_slist_reverse (info->local_schemas);
3391 info->local_schemas = NULL;
3392
3393 if (info->current_entry->value != NULL)
3394 value_stack_pop (info);
3395 }
3396 else if (info->local_schemas != NULL)
3397 {
3398 LocalSchemaInfo *local_schema;
3399
3400 g_assert (g_slist_length (info->local_schemas) == 1);
3401
3402 local_schema = info->local_schemas->data;
3403
3404 g_slist_free (info->local_schemas);
3405 info->local_schemas = NULL;
3406
3407 if (info->current_entry != NULL &&
3408 info->current_entry->value != NULL &&
3409 info->current_entry->value->type == GCONF_VALUE_SCHEMA)
3410 {
3411 GSList *tmp;
3412
3413 tmp = info->current_entry->local_schemas;
3414 while (tmp != NULL)
3415 {
3416 LocalSchemaInfo *lsi = tmp->data;
3417
3418 if (strcmp (local_schema->locale, lsi->locale) == 0)
3419 {
3420 g_free (lsi->short_desc);
3421 lsi->short_desc = local_schema->short_desc;
3422 local_schema->short_desc = NULL;
3423
3424 g_free (lsi->long_desc);
3425 lsi->long_desc = local_schema->short_desc;
3426 local_schema->long_desc = NULL;
3427
3428 local_schema_info_free (local_schema);
3429
3430 break;
3431 }
3432
3433 tmp = tmp->next;
3434 }
3435
3436 if (tmp == NULL)
3437 {
3438 info->current_entry->local_schemas =
3439 g_slist_append (info->current_entry->local_schemas,
3440 local_schema);
3441 }
3442 }
3443 else
3444 {
3445 local_schema_info_free (local_schema);
3446 }
3447 }
3448
3449 info->current_entry = NULL;
3450
3451 pop_state (info);
3452 break;
3453
3454 case STATE_DEFAULT:
3455 {
3456 GConfValue *value;
3457 LocalSchemaInfo *local_schema;
3458
3459 local_schema = info->local_schemas->data;
3460
3461 /* Default should already be in a LocalSchemaInfo */
3462 value = value_stack_peek (info);
3463
3464 g_assert (value == local_schema->default_value);
3465
3466 value_stack_pop (info);
3467
3468 pop_state (info);
3469 }
3470 break;
3471
3472 case STATE_CAR:
3473 case STATE_CDR:
3474 case STATE_LI:
3475 value_stack_pop (info);
3476 pop_state (info);
3477 break;
3478
3479 case STATE_DIR:
3480 case STATE_GCONF:
3481 {
3482 MarkupDir *dir;
3483
3484 dir = dir_stack_pop (info);
3485
3486 if (!info->parsing_local_descs)
3487 {
3488 dir->entries = g_slist_reverse (dir->entries);
3489 dir->subdirs = g_slist_reverse (dir->subdirs);
3490 }
3491 else if (dir->is_parser_dummy)
3492 {
3493 dir->parent->subdirs = g_slist_remove (dir->parent->subdirs, dir);
3494 markup_dir_free (dir);
3495 }
3496
3497 pop_state (info);
3498 }
3499 break;
3500
3501 case STATE_LOCAL_SCHEMA:
3502 case STATE_LONGDESC:
3503 case STATE_STRINGVALUE:
3504 pop_state (info);
3505 break;
3506 }
3507 }
3508
3509 #define NO_TEXT(element_name) set_error (error, context, GCONF_ERROR_PARSE_ERROR, _("No text is allowed inside element <%s>"), element_name)
3510
3511 static gboolean
all_whitespace(const char * text,int text_len)3512 all_whitespace (const char *text,
3513 int text_len)
3514 {
3515 const char *p;
3516 const char *end;
3517
3518 p = text;
3519 end = text + text_len;
3520
3521 while (p != end)
3522 {
3523 if (G_UNLIKELY (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\r'))
3524 return FALSE;
3525 p++;
3526 }
3527
3528 return TRUE;
3529 }
3530
3531 static void
text_handler(GMarkupParseContext * context,const gchar * text,gsize text_len,gpointer user_data,GError ** error)3532 text_handler (GMarkupParseContext *context,
3533 const gchar *text,
3534 gsize text_len,
3535 gpointer user_data,
3536 GError **error)
3537 {
3538 ParseInfo *info = user_data;
3539
3540 if (all_whitespace (text, text_len))
3541 return;
3542
3543 /* FIXME http://bugzilla.gnome.org/show_bug.cgi?id=70448 would
3544 * allow a nice cleanup here.
3545 */
3546
3547 switch (peek_state (info))
3548 {
3549 case STATE_START:
3550 g_assert_not_reached (); /* gmarkup shouldn't do this */
3551 break;
3552 case STATE_STRINGVALUE:
3553 {
3554 GConfValue *value;
3555
3556 value = value_stack_peek (info);
3557 g_assert (value->type == GCONF_VALUE_STRING);
3558
3559 gconf_value_set_string_nocopy (value,
3560 g_strndup (text, text_len));
3561 }
3562 break;
3563 case STATE_LONGDESC:
3564 {
3565 LocalSchemaInfo *local_schema;
3566
3567 local_schema = info->local_schemas->data;
3568
3569 local_schema->long_desc = g_strndup (text, text_len);
3570 }
3571 break;
3572 case STATE_GCONF:
3573 NO_TEXT ("gconf");
3574 break;
3575 case STATE_DIR:
3576 NO_TEXT ("dir");
3577 break;
3578 case STATE_ENTRY:
3579 NO_TEXT ("entry");
3580 break;
3581 case STATE_LOCAL_SCHEMA:
3582 NO_TEXT ("local_schema");
3583 break;
3584 case STATE_DEFAULT:
3585 NO_TEXT ("default");
3586 break;
3587 case STATE_CAR:
3588 NO_TEXT ("car");
3589 break;
3590 case STATE_CDR:
3591 NO_TEXT ("cdr");
3592 break;
3593 case STATE_LI:
3594 NO_TEXT ("li");
3595 break;
3596 }
3597 }
3598
3599 static void
parse_tree(MarkupDir * root,gboolean parse_subtree,const char * locale,GError ** err)3600 parse_tree (MarkupDir *root,
3601 gboolean parse_subtree,
3602 const char *locale,
3603 GError **err)
3604 {
3605 GMarkupParseContext *context = NULL;
3606 GError *error;
3607 ParseInfo info;
3608 char *filename;
3609 FILE *f;
3610
3611 if (!parse_subtree)
3612 g_assert (locale == NULL);
3613
3614 filename = markup_dir_build_file_path (root, parse_subtree, locale);
3615
3616 parse_info_init (&info, root, parse_subtree, locale);
3617
3618 error = NULL;
3619
3620 f = g_fopen (filename, "rb");
3621 if (f == NULL)
3622 {
3623 char *str;
3624
3625 str = g_strdup_printf (_("Failed to open \"%s\": %s\n"),
3626 filename, g_strerror (errno));
3627 error = g_error_new_literal (GCONF_ERROR,
3628 GCONF_ERROR_FAILED,
3629 str);
3630 g_free (str);
3631
3632 goto out;
3633 }
3634
3635 context = g_markup_parse_context_new (&gconf_parser,
3636 0, &info, NULL);
3637
3638 while (!feof (f))
3639 {
3640 char text[4096];
3641 gsize n_bytes;
3642
3643 n_bytes = fread (text, 1, sizeof (text), f);
3644 if (n_bytes > 0)
3645 {
3646 error = NULL;
3647 if (!g_markup_parse_context_parse (context, text, n_bytes, &error))
3648 goto out;
3649 }
3650
3651 if (ferror (f))
3652 {
3653 char *str;
3654
3655 str = g_strdup_printf (_("Error reading \"%s\": %s\n"),
3656 filename, g_strerror (errno));
3657 error = g_error_new_literal (GCONF_ERROR,
3658 GCONF_ERROR_FAILED,
3659 str);
3660 g_free (str);
3661
3662 goto out;
3663 }
3664 }
3665
3666 error = NULL;
3667 if (!g_markup_parse_context_end_parse (context, &error))
3668 goto out;
3669
3670 out:
3671
3672 if (context)
3673 g_markup_parse_context_free (context);
3674 g_free (filename);
3675
3676 if (f != NULL)
3677 fclose (f);
3678
3679 parse_info_free (&info);
3680
3681 if (error)
3682 g_propagate_error (err, error);
3683 }
3684
3685 /*
3686 * Save
3687 */
3688
3689 #define INDENT_SPACES 1
3690
3691 static gboolean write_list_children (GConfValue *value,
3692 FILE *f,
3693 int indent);
3694 static gboolean write_pair_children (GConfValue *value,
3695 FILE *f,
3696 int indent);
3697 static gboolean write_schema_children (GConfValue *value,
3698 FILE *f,
3699 int indent,
3700 GSList *local_schemas,
3701 gboolean save_as_subtree);
3702
3703 /* the common case - before we start interning */
3704 static const char write_indents_static[] =
3705 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t" /* 16 */
3706 "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; /* 32 */
3707
make_whitespace(int indent)3708 static const char *make_whitespace (int indent)
3709 {
3710 int idx = MAX (sizeof (write_indents_static) - 1 - indent, 0);
3711 return &write_indents_static[idx];
3712 }
3713
3714 static gboolean
write_value_element(GConfValue * value,const char * closing_element,FILE * f,int indent,GSList * local_schemas,gboolean save_as_subtree)3715 write_value_element (GConfValue *value,
3716 const char *closing_element,
3717 FILE *f,
3718 int indent,
3719 GSList *local_schemas,
3720 gboolean save_as_subtree)
3721 {
3722 gboolean single_element = FALSE;
3723 /* We are at the "<foo bar="whatever"" stage here,
3724 * <foo> still missing the closing >
3725 */
3726
3727 if (fprintf (f, " type=\"%s\"",
3728 gconf_value_type_to_string (value->type)) < 0)
3729 return FALSE;
3730
3731 switch (value->type)
3732 {
3733 case GCONF_VALUE_LIST:
3734 if (fprintf (f, " ltype=\"%s\"",
3735 gconf_value_type_to_string (gconf_value_get_list_type (value))) < 0)
3736 return FALSE;
3737 break;
3738
3739 case GCONF_VALUE_SCHEMA:
3740 {
3741 GConfSchema *schema;
3742 GConfValueType stype;
3743 const char *owner;
3744
3745 schema = gconf_value_get_schema (value);
3746
3747 stype = gconf_schema_get_type (schema);
3748
3749 if (fprintf (f, " stype=\"%s\"",
3750 gconf_value_type_to_string (stype)) < 0)
3751 return FALSE;
3752
3753 owner = gconf_schema_get_owner (schema);
3754
3755 if (owner)
3756 {
3757 char *s;
3758
3759 s = g_markup_escape_text (owner, -1);
3760
3761 if (fprintf (f, " owner=\"%s\"", s) < 0)
3762 {
3763 g_free (s);
3764 return FALSE;
3765 }
3766
3767 g_free (s);
3768 }
3769
3770 if (stype == GCONF_VALUE_LIST)
3771 {
3772 GConfValueType list_type = gconf_schema_get_list_type (schema);
3773
3774 if (list_type != GCONF_VALUE_INVALID)
3775 {
3776 if (fprintf (f, " list_type=\"%s\"",
3777 gconf_value_type_to_string (list_type)) < 0)
3778 return FALSE;
3779 }
3780 }
3781
3782 if (stype == GCONF_VALUE_PAIR)
3783 {
3784 GConfValueType car_type;
3785 GConfValueType cdr_type;
3786
3787 car_type = gconf_schema_get_car_type (schema);
3788 cdr_type = gconf_schema_get_cdr_type (schema);
3789
3790 if (car_type != GCONF_VALUE_INVALID)
3791 {
3792 if (fprintf (f, " car_type=\"%s\"",
3793 gconf_value_type_to_string (car_type)) < 0)
3794 return FALSE;
3795 }
3796
3797 if (cdr_type != GCONF_VALUE_INVALID)
3798 {
3799 if (fprintf (f, " cdr_type=\"%s\"",
3800 gconf_value_type_to_string (cdr_type)) < 0)
3801 return FALSE;
3802 }
3803 }
3804 }
3805 break;
3806
3807 case GCONF_VALUE_INT:
3808 if (fprintf (f, " value=\"%d\"",
3809 gconf_value_get_int (value)) < 0)
3810 return FALSE;
3811 break;
3812
3813 case GCONF_VALUE_BOOL:
3814 if (fprintf (f, " value=\"%s\"",
3815 gconf_value_get_bool (value) ? "true" : "false") < 0)
3816 return FALSE;
3817 break;
3818
3819 case GCONF_VALUE_FLOAT:
3820 {
3821 char *s;
3822
3823 s = gconf_double_to_string (gconf_value_get_float (value));
3824 if (fprintf (f, " value=\"%s\"", s) < 0)
3825 {
3826 g_free (s);
3827 return FALSE;
3828 }
3829 g_free (s);
3830 }
3831 break;
3832
3833 case GCONF_VALUE_INVALID:
3834 case GCONF_VALUE_STRING:
3835 case GCONF_VALUE_PAIR:
3836 break;
3837 }
3838
3839 switch (value->type)
3840 {
3841 case GCONF_VALUE_STRING:
3842 {
3843 char *s;
3844
3845 s = g_markup_escape_text (gconf_value_get_string (value),
3846 -1);
3847 if (fprintf (f, ">\n%s<stringvalue>%s</stringvalue>\n",
3848 make_whitespace (indent + INDENT_SPACES), s) < 0)
3849 {
3850 g_free (s);
3851 return FALSE;
3852 }
3853
3854 g_free (s);
3855 }
3856 break;
3857
3858 case GCONF_VALUE_LIST:
3859 if (fputs (">\n", f) < 0)
3860 return FALSE;
3861 if (!write_list_children (value, f, indent + INDENT_SPACES))
3862 return FALSE;
3863 break;
3864
3865 case GCONF_VALUE_PAIR:
3866 if (fputs (">\n", f) < 0)
3867 return FALSE;
3868 if (!write_pair_children (value, f, indent + INDENT_SPACES))
3869 return FALSE;
3870 break;
3871
3872 case GCONF_VALUE_SCHEMA:
3873 if (fputs (">\n", f) < 0)
3874 return FALSE;
3875 if (!write_schema_children (value,
3876 f,
3877 indent + INDENT_SPACES,
3878 local_schemas,
3879 save_as_subtree))
3880 return FALSE;
3881 break;
3882
3883 case GCONF_VALUE_INT:
3884 case GCONF_VALUE_BOOL:
3885 case GCONF_VALUE_FLOAT:
3886 case GCONF_VALUE_INVALID:
3887 if (fputs ("/>\n", f) < 0)
3888 return FALSE;
3889 single_element = TRUE;
3890 break;
3891 }
3892
3893 if (!single_element)
3894 if (fprintf (f, "%s</%s>\n", make_whitespace (indent), closing_element) < 0)
3895 return FALSE;
3896
3897 return TRUE;
3898 }
3899
3900 static gboolean
write_list_children(GConfValue * value,FILE * f,int indent)3901 write_list_children (GConfValue *value,
3902 FILE *f,
3903 int indent)
3904 {
3905 GSList *tmp;
3906 gboolean retval = FALSE;
3907
3908 tmp = gconf_value_get_list (value);
3909 while (tmp != NULL)
3910 {
3911 GConfValue *li = tmp->data;
3912
3913 if (fputs (make_whitespace (indent), f) < 0)
3914 goto out;
3915
3916 if (fputs ("<li", f) < 0)
3917 goto out;
3918
3919 if (!write_value_element (li, "li", f, indent, NULL, FALSE))
3920 goto out;
3921
3922 tmp = tmp->next;
3923 }
3924
3925 retval = TRUE;
3926
3927 out:
3928
3929 return retval;
3930 }
3931
3932 static gboolean
write_pair_children(GConfValue * value,FILE * f,int indent)3933 write_pair_children (GConfValue *value,
3934 FILE *f,
3935 int indent)
3936 {
3937 GConfValue *child;
3938 gboolean retval = FALSE;
3939
3940 child = gconf_value_get_car (value);
3941
3942 if (child != NULL)
3943 {
3944 if (fputs (make_whitespace (indent), f) < 0)
3945 goto out;
3946
3947 if (fputs ("<car", f) < 0)
3948 goto out;
3949
3950 if (!write_value_element (child, "car", f, indent, NULL, FALSE))
3951 goto out;
3952 }
3953
3954 child = gconf_value_get_cdr (value);
3955
3956 if (child != NULL)
3957 {
3958 if (fputs (make_whitespace (indent), f) < 0)
3959 goto out;
3960
3961 if (fputs ("<cdr", f) < 0)
3962 goto out;
3963
3964 if (!write_value_element (child, "cdr", f, indent, NULL, FALSE))
3965 goto out;
3966 }
3967
3968 retval = TRUE;
3969
3970 out:
3971
3972 return retval;
3973 }
3974
3975 static gboolean
write_local_schema_info(LocalSchemaInfo * local_schema,FILE * f,int indent,gboolean is_locale_file,gboolean write_descs)3976 write_local_schema_info (LocalSchemaInfo *local_schema,
3977 FILE *f,
3978 int indent,
3979 gboolean is_locale_file,
3980 gboolean write_descs)
3981 {
3982 gboolean retval;
3983 const char *whitespace1, *whitespace2;
3984 char *s;
3985
3986 if (!write_descs && local_schema->default_value == NULL)
3987 return TRUE;
3988
3989 retval = FALSE;
3990
3991 whitespace1 = make_whitespace (indent);
3992 whitespace2 = make_whitespace (indent + INDENT_SPACES);
3993
3994 if (fputs (whitespace1, f) < 0)
3995 goto out;
3996
3997 if (fputs ("<local_schema", f) < 0)
3998 goto out;
3999
4000 if (!is_locale_file)
4001 {
4002 g_assert (local_schema->locale);
4003
4004 s = g_markup_escape_text (local_schema->locale, -1);
4005
4006 if (fprintf (f, " locale=\"%s\"", s) < 0)
4007 {
4008 g_free (s);
4009 goto out;
4010 }
4011
4012 g_free (s);
4013 }
4014
4015 if (write_descs && local_schema->short_desc)
4016 {
4017 s = g_markup_escape_text (local_schema->short_desc, -1);
4018
4019 if (fprintf (f, " short_desc=\"%s\"", s) < 0)
4020 {
4021 g_free (s);
4022 goto out;
4023 }
4024
4025 g_free (s);
4026 }
4027
4028 if (fputs (">\n", f) < 0)
4029 goto out;
4030
4031 if (!is_locale_file && local_schema->default_value)
4032 {
4033 if (fputs (whitespace2, f) < 0)
4034 goto out;
4035
4036 if (fputs ("<default", f) < 0)
4037 goto out;
4038
4039 if (!write_value_element (local_schema->default_value,
4040 "default",
4041 f,
4042 indent + INDENT_SPACES,
4043 NULL,
4044 FALSE))
4045 goto out;
4046 }
4047
4048 if (write_descs && local_schema->long_desc)
4049 {
4050 if (fprintf (f, "%s<longdesc>", whitespace2) < 0)
4051 goto out;
4052
4053 s = g_markup_escape_text (local_schema->long_desc, -1);
4054
4055 if (fputs (s, f) < 0)
4056 {
4057 g_free (s);
4058 goto out;
4059 }
4060
4061 g_free (s);
4062
4063 if (fputs ("</longdesc>\n", f) < 0)
4064 goto out;
4065 }
4066
4067 if (fputs (whitespace1, f) < 0)
4068 goto out;
4069
4070 if (fputs ("</local_schema>\n", f) < 0)
4071 goto out;
4072
4073 retval = TRUE;
4074
4075 out:
4076
4077 return retval;
4078 }
4079
4080 static gboolean
write_schema_children(GConfValue * value,FILE * f,int indent,GSList * local_schemas,gboolean save_as_subtree)4081 write_schema_children (GConfValue *value,
4082 FILE *f,
4083 int indent,
4084 GSList *local_schemas,
4085 gboolean save_as_subtree)
4086 {
4087 /* Here we write each local_schema, in turn a local_schema can
4088 * contain <default> and <longdesc> and have locale and short_desc
4089 * attributes
4090 */
4091 GSList *tmp;
4092
4093 tmp = local_schemas;
4094 while (tmp != NULL)
4095 {
4096 LocalSchemaInfo *local_schema = tmp->data;
4097 gboolean write_descs;
4098
4099 write_descs = TRUE;
4100
4101 if (save_as_subtree &&
4102 strcmp (local_schema->locale, "C") != 0)
4103 write_descs = FALSE;
4104
4105 if (!write_local_schema_info (local_schema,
4106 f,
4107 indent,
4108 FALSE,
4109 write_descs))
4110 return FALSE;
4111
4112 tmp = tmp->next;
4113 }
4114
4115 return TRUE;
4116 }
4117
4118 static void
get_non_c_desc_locales(MarkupEntry * entry,GHashTable * non_c_desc_locales)4119 get_non_c_desc_locales (MarkupEntry *entry,
4120 GHashTable *non_c_desc_locales)
4121 {
4122 GSList *tmp;
4123
4124 tmp = entry->local_schemas;
4125 while (tmp != NULL)
4126 {
4127 LocalSchemaInfo *local_schema = tmp->data;
4128
4129 if (strcmp (local_schema->locale, "C") != 0 &&
4130 local_schema->short_desc != NULL &&
4131 local_schema->long_desc != NULL)
4132 {
4133 g_hash_table_replace (non_c_desc_locales,
4134 (char *) local_schema->locale,
4135 GINT_TO_POINTER (TRUE));
4136 }
4137
4138 tmp = tmp->next;
4139 }
4140 }
4141
4142 static LocalSchemaInfo *
get_local_schema_info(MarkupEntry * entry,const char * locale)4143 get_local_schema_info (MarkupEntry *entry,
4144 const char *locale)
4145 {
4146 GSList *tmp;
4147
4148 tmp = entry->local_schemas;
4149 while (tmp != NULL)
4150 {
4151 LocalSchemaInfo *local_schema = tmp->data;
4152
4153 if (strcmp (local_schema->locale, locale) == 0)
4154 {
4155 return local_schema;
4156 }
4157
4158 tmp = tmp->next;
4159 }
4160
4161 return NULL;
4162 }
4163
4164 static gboolean
write_entry(MarkupEntry * entry,FILE * f,int indent,gboolean save_as_subtree,const char * locale,GHashTable * other_locales)4165 write_entry (MarkupEntry *entry,
4166 FILE *f,
4167 int indent,
4168 gboolean save_as_subtree,
4169 const char *locale,
4170 GHashTable *other_locales)
4171 {
4172 LocalSchemaInfo *local_schema_info;
4173 gboolean retval;
4174
4175 retval = FALSE;
4176 local_schema_info = NULL;
4177
4178 if (save_as_subtree)
4179 {
4180 if (locale == NULL)
4181 {
4182 g_assert (other_locales != NULL);
4183 get_non_c_desc_locales (entry, other_locales);
4184 }
4185 else
4186 {
4187 if ((local_schema_info = get_local_schema_info (entry, locale)) == NULL)
4188 return TRUE;
4189 }
4190 }
4191
4192 g_assert (entry->name != NULL);
4193
4194 if (fprintf (f, "%s<entry name=\"%s\"", make_whitespace (indent), entry->name) < 0)
4195 goto out;
4196
4197 if (local_schema_info == NULL)
4198 {
4199 if (fprintf (f, " mtime=\"%lu\"", (unsigned long) entry->mod_time) < 0)
4200 goto out;
4201
4202 if (entry->schema_name)
4203 {
4204 if (fprintf (f, " schema=\"%s\"", entry->schema_name) < 0)
4205 goto out;
4206 }
4207
4208 if (entry->mod_user)
4209 {
4210 if (fprintf (f, " muser=\"%s\"", entry->mod_user) < 0)
4211 goto out;
4212 }
4213
4214 if (entry->value != NULL)
4215 {
4216 if (!write_value_element (entry->value,
4217 "entry",
4218 f,
4219 indent,
4220 entry->local_schemas,
4221 save_as_subtree))
4222 goto out;
4223 }
4224 else
4225 {
4226 if (fputs ("/>\n", f) < 0)
4227 goto out;
4228 }
4229 }
4230 else
4231 {
4232 if (fputs (">\n", f) < 0)
4233 goto out;
4234
4235 if (!write_local_schema_info (local_schema_info,
4236 f,
4237 indent + INDENT_SPACES,
4238 TRUE,
4239 TRUE))
4240 goto out;
4241
4242 if (fprintf (f, "%s</entry>\n", make_whitespace (indent)) < 0)
4243 goto out;
4244 }
4245
4246 retval = TRUE;
4247
4248 out:
4249
4250 return retval;
4251 }
4252
4253 static gboolean
write_dir(MarkupDir * dir,FILE * f,int indent,gboolean save_as_subtree,const char * locale,GHashTable * other_locales)4254 write_dir (MarkupDir *dir,
4255 FILE *f,
4256 int indent,
4257 gboolean save_as_subtree,
4258 const char *locale,
4259 GHashTable *other_locales)
4260 {
4261 GSList *tmp;
4262 gboolean retval = FALSE;
4263
4264 dir->not_in_filesystem = TRUE;
4265
4266 if (save_as_subtree && locale != NULL && dir->is_dir_empty)
4267 return TRUE;
4268
4269 g_assert (dir->name != NULL);
4270
4271 if (fprintf (f, "%s<dir name=\"%s\">\n",
4272 make_whitespace (indent), dir->name) < 0)
4273 goto out;
4274
4275 tmp = dir->entries;
4276 while (tmp != NULL)
4277 {
4278 MarkupEntry *entry = tmp->data;
4279
4280 if (!write_entry (entry,
4281 f,
4282 indent + INDENT_SPACES,
4283 save_as_subtree,
4284 locale,
4285 other_locales))
4286 goto out;
4287
4288 tmp = tmp->next;
4289 }
4290
4291 tmp = dir->subdirs;
4292 while (tmp != NULL)
4293 {
4294 MarkupDir *subdir = tmp->data;
4295
4296 if (!write_dir (subdir,
4297 f,
4298 indent + INDENT_SPACES,
4299 save_as_subtree,
4300 locale,
4301 other_locales))
4302 goto out;
4303
4304 tmp = tmp->next;
4305 }
4306
4307 if (fprintf (f, "%s</dir>\n", make_whitespace (indent)) < 0)
4308 return FALSE;
4309
4310 retval = TRUE;
4311
4312 out:
4313
4314 return retval;
4315 }
4316
4317 static gboolean
init_is_dir_empty_flags(MarkupDir * dir,const char * locale)4318 init_is_dir_empty_flags (MarkupDir *dir,
4319 const char *locale)
4320 {
4321 GSList *tmp;
4322
4323 dir->is_dir_empty = TRUE;
4324
4325 tmp = dir->entries;
4326 while (tmp != NULL)
4327 {
4328 MarkupEntry *entry = tmp->data;
4329
4330 if (get_local_schema_info (entry, locale) != NULL)
4331 {
4332 dir->is_dir_empty = FALSE;
4333 break;
4334 }
4335
4336 tmp = tmp->next;
4337 }
4338
4339 tmp = dir->subdirs;
4340 while (tmp != NULL)
4341 {
4342 MarkupDir *subdir = tmp->data;
4343
4344 if (!init_is_dir_empty_flags (subdir, locale))
4345 dir->is_dir_empty = FALSE;
4346
4347 tmp = tmp->next;
4348 }
4349
4350 return dir->is_dir_empty;
4351 }
4352
4353 static void
save_tree_with_locale(MarkupDir * dir,gboolean save_as_subtree,const char * locale,GHashTable * other_locales,guint file_mode,GError ** err)4354 save_tree_with_locale (MarkupDir *dir,
4355 gboolean save_as_subtree,
4356 const char *locale,
4357 GHashTable *other_locales,
4358 guint file_mode,
4359 GError **err)
4360 {
4361 /* We save to a secondary file then copy over, to handle
4362 * out-of-disk-space robustly
4363 */
4364 FILE *f;
4365 int new_fd;
4366 char *filename;
4367 char *new_filename;
4368 #ifdef G_OS_WIN32
4369 char *tmp_filename;
4370 gboolean target_renamed;
4371 #endif
4372 char *err_str;
4373 gboolean write_failed;
4374 GSList *tmp;
4375 struct stat st;
4376
4377 write_failed = FALSE;
4378 err_str = NULL;
4379 new_fd = -1;
4380 f = NULL;
4381
4382 filename = markup_dir_build_file_path (dir, save_as_subtree, locale);
4383
4384 new_filename = g_strconcat (filename, ".new", NULL);
4385 #ifdef G_OS_WIN32
4386 tmp_filename = g_strconcat (filename, ".tmp", NULL);
4387 #endif
4388 new_fd = g_open (new_filename, O_WRONLY | O_CREAT, file_mode);
4389 if (new_fd < 0)
4390 {
4391 err_str = g_strdup_printf (_("Failed to open \"%s\": %s\n"),
4392 new_filename, g_strerror (errno));
4393 goto out;
4394 }
4395
4396 /* Leave the file empty to avoid parsing it later
4397 * if there are no entries in it.
4398 */
4399 if (dir->entries == NULL && (!save_as_subtree || dir->subdirs == NULL))
4400 {
4401 fsync (new_fd);
4402 close (new_fd);
4403 new_fd = -1;
4404 goto done_writing;
4405 }
4406
4407 f = fdopen (new_fd, "w");
4408 if (f == NULL)
4409 {
4410 err_str = g_strdup_printf (_("Failed to open \"%s\": %s\n"),
4411 new_filename, g_strerror (errno));
4412 goto out;
4413 }
4414
4415 new_fd = -1; /* owned by the FILE* now */
4416
4417 write_failed = FALSE;
4418
4419 if (fputs ("<?xml version=\"1.0\"?>\n", f) < 0)
4420 {
4421 write_failed = TRUE;
4422 goto done_writing;
4423 }
4424
4425 if (fputs ("<gconf>\n", f) < 0)
4426 {
4427 write_failed = TRUE;
4428 goto done_writing;
4429 }
4430
4431
4432 tmp = dir->entries;
4433 while (tmp != NULL)
4434 {
4435 MarkupEntry *entry = tmp->data;
4436
4437 if (!write_entry (entry,
4438 f,
4439 INDENT_SPACES,
4440 save_as_subtree,
4441 locale,
4442 other_locales))
4443 {
4444 write_failed = TRUE;
4445 goto done_writing;
4446 }
4447
4448 tmp = tmp->next;
4449 }
4450
4451 if (save_as_subtree)
4452 {
4453 if (locale != NULL)
4454 init_is_dir_empty_flags (dir, locale);
4455
4456 tmp = dir->subdirs;
4457 while (tmp != NULL)
4458 {
4459 MarkupDir *dir = tmp->data;
4460
4461 if (!write_dir (dir,
4462 f,
4463 INDENT_SPACES,
4464 save_as_subtree,
4465 locale,
4466 other_locales))
4467 {
4468 write_failed = TRUE;
4469 goto done_writing;
4470 }
4471
4472 tmp = tmp->next;
4473 }
4474 }
4475
4476 if (fputs ("</gconf>\n", f) < 0)
4477 {
4478 write_failed = TRUE;
4479 goto done_writing;
4480 }
4481
4482 if (fflush (f) != 0 || fsync (fileno (f)) < 0)
4483 {
4484 gconf_log (GCL_WARNING,
4485 _("Could not flush file '%s' to disk: %s"),
4486 new_filename, g_strerror (errno));
4487 }
4488
4489 if (fclose (f) < 0)
4490 {
4491 f = NULL; /* f is still freed even if fclose fails according to the
4492 * linux man page
4493 */
4494 write_failed = TRUE;
4495 goto done_writing;
4496 }
4497
4498 f = NULL;
4499
4500 done_writing:
4501
4502 if (write_failed)
4503 {
4504 err_str = g_strdup_printf (_("Error writing file \"%s\": %s"),
4505 new_filename, g_strerror (errno));
4506 goto out;
4507 }
4508
4509 #ifdef G_OS_WIN32
4510 g_remove (tmp_filename);
4511 target_renamed = (g_rename (filename, tmp_filename) == 0);
4512 #endif
4513
4514 #ifndef G_OS_WIN32
4515 if (g_stat (filename, &st) == 0) {
4516 /* Restore permissions. There is not much error checking we can do
4517 * here. The final data is saved anyways. Note the order:
4518 * mode, uid+gid, gid, uid, mode.
4519 */
4520 chmod (new_filename, st.st_mode);
4521 if (chown (new_filename, st.st_uid, st.st_gid) < 0)
4522 {
4523 /* We cannot set both. Maybe we can set one. */
4524 chown (new_filename, -1, st.st_gid);
4525 chown (new_filename, st.st_uid, -1);
4526 }
4527 chmod (new_filename, st.st_mode);
4528 }
4529 #endif
4530
4531 if (g_rename (new_filename, filename) < 0)
4532 {
4533 err_str = g_strdup_printf (_("Failed to move temporary file \"%s\" to final location \"%s\": %s"),
4534 new_filename, filename, g_strerror (errno));
4535 #ifdef G_OS_WIN32
4536 if (target_renamed)
4537 g_rename (tmp_filename, filename);
4538 #endif
4539 goto out;
4540 }
4541
4542 #ifdef G_OS_WIN32
4543 if (target_renamed)
4544 g_remove (tmp_filename);
4545 #endif
4546
4547 out:
4548 #ifdef G_OS_WIN32
4549 g_free (tmp_filename);
4550 #endif
4551 g_free (new_filename);
4552 g_free (filename);
4553
4554 if (err_str)
4555 {
4556 if (err)
4557 *err = g_error_new_literal (GCONF_ERROR,
4558 GCONF_ERROR_FAILED,
4559 err_str);
4560
4561 g_free (err_str);
4562 }
4563
4564 if (new_fd >= 0)
4565 close (new_fd);
4566
4567 if (f != NULL)
4568 fclose (f);
4569 }
4570
4571 typedef struct
4572 {
4573 MarkupDir *dir;
4574 guint file_mode;
4575 GError *first_error;
4576 } OtherLocalesForeachData;
4577
4578 static void
other_locales_foreach(const char * locale,gpointer dummy,OtherLocalesForeachData * data)4579 other_locales_foreach (const char *locale,
4580 gpointer dummy,
4581 OtherLocalesForeachData *data)
4582 {
4583 GError *error;
4584
4585 error = NULL;
4586 save_tree_with_locale (data->dir,
4587 TRUE,
4588 locale,
4589 NULL,
4590 data->file_mode,
4591 &error);
4592 if (error != NULL)
4593 {
4594 if (data->first_error != NULL)
4595 data->first_error = error;
4596 else
4597 g_error_free (error);
4598 }
4599 }
4600
4601 static void
save_tree(MarkupDir * dir,gboolean save_as_subtree,guint file_mode,GError ** err)4602 save_tree (MarkupDir *dir,
4603 gboolean save_as_subtree,
4604 guint file_mode,
4605 GError **err)
4606 {
4607 if (!save_as_subtree)
4608 {
4609 save_tree_with_locale (dir, FALSE, NULL, NULL, file_mode, err);
4610 }
4611 else
4612 {
4613 OtherLocalesForeachData other_locales_foreach_data;
4614 GHashTable *other_locales;
4615
4616 /* First save %gconf-tree.xml with all values and C locale
4617 * schema descriptions; then save schema descriptions for
4618 * all other locales in %gconf-tree-$(locale).xml
4619 */
4620
4621 other_locales = g_hash_table_new (g_str_hash, g_str_equal);
4622
4623 save_tree_with_locale (dir,
4624 TRUE,
4625 NULL,
4626 other_locales,
4627 file_mode,
4628 err);
4629
4630 other_locales_foreach_data.dir = dir;
4631 other_locales_foreach_data.file_mode = file_mode;
4632 other_locales_foreach_data.first_error = NULL;
4633
4634 g_hash_table_foreach (other_locales,
4635 (GHFunc) other_locales_foreach,
4636 &other_locales_foreach_data);
4637
4638 if (other_locales_foreach_data.first_error != NULL)
4639 {
4640 if (err != NULL && *err == NULL)
4641 *err = other_locales_foreach_data.first_error;
4642 else
4643 g_error_free (other_locales_foreach_data.first_error);
4644 }
4645
4646 g_hash_table_destroy (other_locales);
4647 }
4648 }
4649
4650 /*
4651 * Local schema
4652 */
4653
4654 static LocalSchemaInfo*
local_schema_info_new(void)4655 local_schema_info_new (void)
4656 {
4657 LocalSchemaInfo *info;
4658
4659 info = g_new0 (LocalSchemaInfo, 1);
4660
4661 return info;
4662 }
4663
4664 static void
local_schema_info_free(LocalSchemaInfo * info)4665 local_schema_info_free (LocalSchemaInfo *info)
4666 {
4667 g_free (info->locale);
4668 g_free (info->short_desc);
4669 g_free (info->long_desc);
4670 if (info->default_value)
4671 gconf_value_free (info->default_value);
4672 g_free (info);
4673 }
4674