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", &ltype,
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