2 * Copyright © 2010 Codethink Limited
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the licence, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 * Author: Ryan Lortie <desrt@desrt.ca>
20 unowned Gvdb
.Item
get_parent (Gvdb
.HashTable table
, string name
) {
21 unowned Gvdb
.Item parent
;
25 for (int i
= 1; name
[i
] != '\0'; i
++) {
26 if (name
[i
- 1] == '/') {
33 var parent_name
= name
.substring (0, end
);
34 parent
= table
.lookup (parent_name
);
37 parent
= table
.insert (parent_name
);
38 parent
.set_parent (get_parent (table
, parent_name
));
44 SList
<string>?
list_directory (string dirname
, Posix
.mode_t mode
) throws GLib
.Error
{
45 var list
= new SList
<string> ();
46 var dir
= Dir
.open (dirname
);
49 while ((name
= dir
.read_name ()) != null) {
50 if (name
.has_prefix (".")) {
54 var filename
= Path
.build_filename (dirname
, name
);
57 // only files of the requested type
58 if (Posix
.stat (filename
, out buf
) < 0 || (buf
.st_mode
& Posix
.S_IFMT
) != mode
) {
62 list
.prepend (filename
);
68 Gvdb
.HashTable?
read_locks_directory (string dirname
) throws GLib
.Error
{
72 files
= list_directory (dirname
, Posix
.S_IFREG
);
73 } catch (FileError
.NOENT e
) {
74 /* If locks directory is missing, there are just no locks... */
78 var table
= new Gvdb
.HashTable ();
80 foreach (var filename
in files
) {
82 FileUtils
.get_contents (filename
, out contents
, null);
84 foreach (var line
in contents
.split ("\n")) {
85 if (line
.has_prefix ("/")) {
86 table
.insert_string (line
, "");
94 Gvdb
.HashTable
read_directory (string dirname
) throws GLib
.Error
{
95 var table
= new Gvdb
.HashTable ();
98 var files
= list_directory (dirname
, Posix
.S_IFREG
);
102 foreach (var filename
in files
) {
103 var kf
= new
KeyFile ();
106 kf
.load_from_file (filename
, KeyFileFlags
.NONE
);
107 } catch (GLib
.Error e
) {
108 e
.message
= "warning: Failed to read keyfile '%s': %s".printf (filename
, e
.message
);
113 foreach (var group
in kf
.get_groups ()) {
114 if (group
.has_prefix ("/") || group
.has_suffix ("/") || "//" in group
) {
115 stderr
.printf ("%s: ignoring invalid group name: %s\n", filename
, group
);
119 foreach (var key
in kf
.get_keys (group
)) {
121 throw new KeyFileError
.INVALID_VALUE ("%s: [%s]: invalid key name: %s",
122 filename
, group
, key
);
125 var path
= "/" + group
+ "/" + key
;
127 if (table
.lookup (path
) != null) {
128 /* We process the files in reverse alphabetical order. If the key is already set then
129 * it must have been set from a file with higher precedence so we should ignore this
135 var text
= kf
.get_value (group
, key
);
138 var value
= Variant
.parse (null, text
);
139 unowned Gvdb
.Item item
= table
.insert (path
);
140 item
.set_parent (get_parent (table
, path
));
141 item
.set_value (value
);
142 } catch (VariantParseError e
) {
143 e
.message
= "%s: [%s]: %s: invalid value: %s (%s)".printf (filename
, group
, key
, text
, e
.message
);
148 } catch (KeyFileError e
) {
149 /* This should never happen... */
150 warning ("unexpected keyfile error: %s. Please file a bug.", e
.message
);
151 assert_not_reached ();
155 var locks
= read_locks_directory (dirname
+ "/locks");
158 unowned Gvdb
.Item item
= table
.insert (".locks");
159 item
.set_hash_table (locks
);
165 void maybe_update_from_directory (string dirname
) throws GLib
.Error
{
168 if (Posix
.stat (dirname
, out dir_buf
) == 0 && Posix
.S_ISDIR (dir_buf
.st_mode
)) {
169 Posix
.Stat lockdir_buf
;
172 var filename
= dirname
.substring (0, dirname
.length
- 2);
174 if (Posix
.stat (dirname
+ "/locks", out lockdir_buf
) == 0 && lockdir_buf
.st_mtime
> dir_buf
.st_mtime
) {
175 // if the lock directory has been updated more recently then consider its timestamp instead
176 dir_buf
.st_mtime
= lockdir_buf
.st_mtime
;
179 if (Posix
.stat (filename
, out file_buf
) == 0 && file_buf
.st_mtime
> dir_buf
.st_mtime
) {
183 var table
= read_directory (dirname
);
185 var fd
= Posix
.open (filename
, Posix
.O_WRONLY
);
187 if (fd
< 0 && errno
!= Posix
.ENOENT
) {
188 var saved_error
= errno
;
189 printerr ("warning: Failed to open '%s' for replacement: %s\n", filename
, strerror (saved_error
));
193 // We expect that fd < 0 here if ENOENT (ie: the db merely didn't exist yet)
196 table
.write_contents (filename
);
199 Posix
.write (fd
, "\0\0\0\0\0\0\0\0", 8);
202 printerr ("warning: %s\n", e
.message
);
211 var system_bus
= Bus
.get_sync (BusType
.SYSTEM
);
212 system_bus
.emit_signal (null, "/ca/desrt/dconf/Writer/" + Path
.get_basename (filename
),
213 "ca.desrt.dconf.Writer", "WritabilityNotify", new
Variant ("(s)", "/"));
214 system_bus
.flush_sync ();
216 /* if we can't, ... don't. */
221 void update_all (string dirname
) throws GLib
.Error
{
222 foreach (var name
in list_directory (dirname
, Posix
.S_IFDIR
)) {
223 if (name
.has_suffix (".d")) {
225 maybe_update_from_directory (name
);
227 printerr ("unable to compile %s: %s\n", name
, e
.message
);
233 void dconf_compile (string[] args
) throws GLib
.Error
{
234 if (args
[2] == null || args
[3] == null || args
[4] != null) {
235 throw new OptionError
.FAILED ("must give output file and .d dir");
239 // We always write the result of "dconf compile" as little endian
240 // so that it can be installed in /usr/share
241 var table
= read_directory (args
[3]);
242 var should_byteswap
= (BYTE_ORDER
== ByteOrder
.BIG_ENDIAN
);
243 table
.write_contents (args
[2], should_byteswap
);
245 printerr ("%s\n", e
.message
);
250 void dconf_update (string[] args
) throws GLib
.Error
{
251 update_all ("/etc/dconf/db");
254 // vim:noet ts=4 sw=4