Line | Branch | Exec | Source |
---|---|---|---|
1 | /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- | ||
2 | * | ||
3 | * Copyright © 2013 Collabora Ltd. | ||
4 | * Copyright © 2016 Kinvolk GmbH | ||
5 | * Copyright © 2017, 2018 Endless Mobile, Inc. | ||
6 | * | ||
7 | * SPDX-License-Identifier: LGPL-2.1-or-later | ||
8 | * | ||
9 | * This library is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU Lesser General Public | ||
11 | * License as published by the Free Software Foundation; either | ||
12 | * version 2.1 of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * This library is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * Lesser General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU Lesser General Public | ||
20 | * License along with this library; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
22 | * | ||
23 | * Authors: | ||
24 | * - Vivek Dasmohapatra <vivek@etla.org> | ||
25 | * - Krzesimir Nowak <krzesimir@kinvolk.io> | ||
26 | * - Philip Withnall <withnall@endlessm.com> | ||
27 | */ | ||
28 | |||
29 | #include <gio/gio.h> | ||
30 | #include <glib.h> | ||
31 | #include <glib-object.h> | ||
32 | #include <libgsystemservice/config-file.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <string.h> | ||
35 | |||
36 | /** | ||
37 | * SECTION:config-file | ||
38 | * @short_description: Hierarchical configuration files | ||
39 | * @stability: Stable | ||
40 | * @include: libgsystemservice/config-file.h | ||
41 | * | ||
42 | * This represents a configuration file, loaded from one or more layered | ||
43 | * configuration files following the same schema. For each schema, there must | ||
44 | * always be one canonical copy of the configuration file compiled into the | ||
45 | * program as a #GResource; ultimately, default values are loaded from this. | ||
46 | * It is advised that a copy of this default configuration file is also | ||
47 | * installed in a read-only location on the system, so users can inspect and | ||
48 | * copy from the default configuration. | ||
49 | * | ||
50 | * When queried for keys, an #GssConfigFile instance will return the value from | ||
51 | * the first configuration file in its hierarchy which contains that key. | ||
52 | * If an administrator wishes to override a value from a lower configuration | ||
53 | * file, they must do so explicitly in a higher one. | ||
54 | * | ||
55 | * When listing groups, an #GssConfigFile will return the deduplicated union | ||
56 | * of all the groups in all of its hierarchy of configuration files. When | ||
57 | * overriding a group of keys, the entire group must be copied from one | ||
58 | * configuration file to a higher one; otherwise queries for some keys will fall | ||
59 | * back to the lower configuration file. | ||
60 | * | ||
61 | * Since: 0.1.0 | ||
62 | */ | ||
63 | |||
64 | /** | ||
65 | * GssConfigFile: | ||
66 | * | ||
67 | * Implementation of a configuration file hierarchy. | ||
68 | * | ||
69 | * Since: 0.1.0 | ||
70 | */ | ||
71 | struct _GssConfigFile | ||
72 | { | ||
73 | GObject parent_instance; | ||
74 | |||
75 | gchar **paths; /* (array length=n_paths); final element is always the default path */ | ||
76 | gsize n_paths; | ||
77 | GPtrArray *key_files; /* (element-type GKeyFile); same indexing as paths */ | ||
78 | |||
79 | GResource *default_resource; | ||
80 | gchar *default_path; | ||
81 | GKeyFile *default_key_file; | ||
82 | }; | ||
83 | |||
84 |
6/7✓ Branch 0 taken 1 times.
✓ Branch 1 taken 50 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 50 times.
|
106 | G_DEFINE_TYPE (GssConfigFile, gss_config_file, G_TYPE_OBJECT) |
85 | |||
86 | typedef enum | ||
87 | { | ||
88 | PROP_PATHS = 1, | ||
89 | PROP_DEFAULT_RESOURCE, | ||
90 | PROP_DEFAULT_PATH, | ||
91 | } GssConfigFileProperty; | ||
92 | |||
93 | static GParamSpec *props[PROP_DEFAULT_PATH + 1] = { NULL, }; | ||
94 | |||
95 | static void | ||
96 | 7 | gss_config_file_init (GssConfigFile *self) | |
97 | { | ||
98 | 7 | self->key_files = g_ptr_array_new_with_free_func ((GDestroyNotify) g_key_file_unref); | |
99 | 7 | } | |
100 | |||
101 | static void | ||
102 | ✗ | gss_config_file_get_property (GObject *object, | |
103 | guint property_id, | ||
104 | GValue *value, | ||
105 | GParamSpec *spec) | ||
106 | { | ||
107 | ✗ | GssConfigFile *self = GSS_CONFIG_FILE (object); | |
108 | |||
109 | ✗ | switch ((GssConfigFileProperty) property_id) | |
110 | { | ||
111 | ✗ | case PROP_PATHS: | |
112 | ✗ | g_value_set_boxed (value, self->paths); | |
113 | ✗ | break; | |
114 | |||
115 | ✗ | case PROP_DEFAULT_RESOURCE: | |
116 | ✗ | g_value_set_boxed (value, self->default_resource); | |
117 | ✗ | break; | |
118 | |||
119 | ✗ | case PROP_DEFAULT_PATH: | |
120 | ✗ | g_value_set_string (value, self->default_path); | |
121 | ✗ | break; | |
122 | |||
123 | ✗ | default: | |
124 | ✗ | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); | |
125 | ✗ | break; | |
126 | } | ||
127 | ✗ | } | |
128 | |||
129 | static void | ||
130 | 21 | gss_config_file_set_property (GObject *object, | |
131 | guint property_id, | ||
132 | const GValue *value, | ||
133 | GParamSpec *spec) | ||
134 | { | ||
135 | 21 | GssConfigFile *self = GSS_CONFIG_FILE (object); | |
136 | |||
137 |
3/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 7 times.
✗ Branch 3 not taken.
|
21 | switch ((GssConfigFileProperty) property_id) |
138 | { | ||
139 | 7 | case PROP_PATHS: | |
140 | /* Construct-only; must be non-empty */ | ||
141 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->paths == NULL); |
142 | 7 | self->paths = g_value_dup_boxed (value); | |
143 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->paths != NULL); |
144 | 7 | self->n_paths = g_strv_length (self->paths); | |
145 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->n_paths > 0); |
146 | 7 | break; | |
147 | |||
148 | 7 | case PROP_DEFAULT_RESOURCE: | |
149 | /* Construct-only. Must be non-%NULL. */ | ||
150 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_resource == NULL); |
151 | 7 | self->default_resource = g_value_dup_boxed (value); | |
152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_resource != NULL); |
153 | 7 | break; | |
154 | |||
155 | 7 | case PROP_DEFAULT_PATH: | |
156 | /* Construct only; must be non-%NULL. */ | ||
157 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_path == NULL); |
158 | 7 | self->default_path = g_value_dup_string (value); | |
159 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_path != NULL); |
160 | 7 | break; | |
161 | |||
162 | ✗ | default: | |
163 | ✗ | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, spec); | |
164 | ✗ | break; | |
165 | } | ||
166 | 21 | } | |
167 | |||
168 | static void | ||
169 | 7 | gss_config_file_constructed (GObject *object) | |
170 | { | ||
171 | 7 | GssConfigFile *self = GSS_CONFIG_FILE (object); | |
172 | 7 | g_autoptr(GBytes) bytes = NULL; | |
173 | 7 | g_autoptr(GError) error = NULL; | |
174 | |||
175 | /* Chain up. */ | ||
176 | 7 | G_OBJECT_CLASS (gss_config_file_parent_class)->constructed (object); | |
177 | |||
178 | /* Load the default config file from the given resource. It’s a fatal error | ||
179 | * if this fails. We load this in the constructor to ensure we fail early, | ||
180 | * rather than conditionally on accessing something from the config file. */ | ||
181 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_resource != NULL); |
182 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert (self->default_path != NULL); |
183 | |||
184 | 7 | bytes = g_resource_lookup_data (self->default_resource, self->default_path, | |
185 | G_RESOURCE_LOOKUP_FLAGS_NONE, &error); | ||
186 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert_no_error (error); |
187 | |||
188 | 7 | self->default_key_file = g_key_file_new (); | |
189 | 7 | g_key_file_load_from_bytes (self->default_key_file, bytes, | |
190 | G_KEY_FILE_NONE, &error); | ||
191 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_assert_no_error (error); |
192 | 7 | } | |
193 | |||
194 | static void | ||
195 | 7 | gss_config_file_finalize (GObject *object) | |
196 | { | ||
197 | 7 | GssConfigFile *self = GSS_CONFIG_FILE (object); | |
198 | |||
199 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | g_clear_pointer (&self->paths, g_strfreev); |
200 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | g_clear_pointer (&self->key_files, g_ptr_array_unref); |
201 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | g_clear_pointer (&self->default_resource, g_resource_unref); |
202 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | g_clear_pointer (&self->default_path, g_free); |
203 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | g_clear_pointer (&self->default_key_file, g_key_file_unref); |
204 | |||
205 | 7 | G_OBJECT_CLASS (gss_config_file_parent_class)->finalize (object); | |
206 | 7 | } | |
207 | |||
208 | static void | ||
209 | 1 | gss_config_file_class_init (GssConfigFileClass *klass) | |
210 | { | ||
211 | 1 | GObjectClass *object_class = G_OBJECT_CLASS (klass); | |
212 | |||
213 | 1 | object_class->constructed = gss_config_file_constructed; | |
214 | 1 | object_class->finalize = gss_config_file_finalize; | |
215 | 1 | object_class->get_property = gss_config_file_get_property; | |
216 | 1 | object_class->set_property = gss_config_file_set_property; | |
217 | |||
218 | /** | ||
219 | * GssConfigFile:paths: | ||
220 | * | ||
221 | * Ordered collection of paths of configuration files to load. This must | ||
222 | * always contain at least one element; the final element in the collection is | ||
223 | * treated as the default configuration file. | ||
224 | * | ||
225 | * Since: 0.1.0 | ||
226 | */ | ||
227 | 1 | props[PROP_PATHS] = g_param_spec_boxed ("paths", | |
228 | "Paths", | ||
229 | "Ordered collection of paths of " | ||
230 | "configuration files to load.", | ||
231 | G_TYPE_STRV, | ||
232 | G_PARAM_READWRITE | | ||
233 | G_PARAM_CONSTRUCT_ONLY | | ||
234 | G_PARAM_STATIC_STRINGS); | ||
235 | |||
236 | /** | ||
237 | * GssConfigFile:default-resource: | ||
238 | * | ||
239 | * #GResource containing the default configuration file. | ||
240 | * | ||
241 | * Since: 0.1.0 | ||
242 | */ | ||
243 | 1 | props[PROP_DEFAULT_RESOURCE] = g_param_spec_boxed ("default-resource", | |
244 | "Default Resource", | ||
245 | "GResource containing the " | ||
246 | "default configuration file.", | ||
247 | G_TYPE_RESOURCE, | ||
248 | G_PARAM_READWRITE | | ||
249 | G_PARAM_CONSTRUCT_ONLY | | ||
250 | G_PARAM_STATIC_STRINGS); | ||
251 | |||
252 | /** | ||
253 | * GssConfigFile:default-path: | ||
254 | * | ||
255 | * Path to the default configuration file in #GssConfigFile:default-resource. | ||
256 | * | ||
257 | * Since: 0.1.0 | ||
258 | */ | ||
259 | 1 | props[PROP_DEFAULT_PATH] = g_param_spec_string ("default-path", | |
260 | "Default Path", | ||
261 | "Path to the default " | ||
262 | "configuration file in " | ||
263 | "#GssConfigFile:default-resource.", | ||
264 | NULL, | ||
265 | G_PARAM_READWRITE | | ||
266 | G_PARAM_CONSTRUCT_ONLY | | ||
267 | G_PARAM_STATIC_STRINGS); | ||
268 | |||
269 | 1 | g_object_class_install_properties (object_class, | |
270 | G_N_ELEMENTS (props), | ||
271 | props); | ||
272 | 1 | } | |
273 | |||
274 | /** | ||
275 | * gss_config_file_new: | ||
276 | * @key_file_paths: (array zero-terminated=1): %NULL-terminated ordered | ||
277 | * collection of paths of configuration files to load; must be non-empty | ||
278 | * @default_resource: (transfer none): #GResource containing the default | ||
279 | * configuration | ||
280 | * @default_path: path to the default configuration file in @default_resource | ||
281 | * | ||
282 | * Create a new #GssConfigFile representing the configuration loaded from the | ||
283 | * given collection of @key_file_paths, which must all follow the same schema. | ||
284 | * @key_file_paths must contain at least one element; its final element is | ||
285 | * treated as the default configuration file containing all default values. | ||
286 | * | ||
287 | * This function does no file I/O. | ||
288 | * | ||
289 | * Returns: (transfer full): a newly allocated #GssConfigFile | ||
290 | * Since: 0.1.0 | ||
291 | */ | ||
292 | GssConfigFile * | ||
293 | 7 | gss_config_file_new (const gchar * const *key_file_paths, | |
294 | GResource *default_resource, | ||
295 | const gchar *default_path) | ||
296 | { | ||
297 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_return_val_if_fail (key_file_paths != NULL, NULL); |
298 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_return_val_if_fail (key_file_paths[0] != NULL, NULL); |
299 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_return_val_if_fail (default_resource != NULL, NULL); |
300 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | g_return_val_if_fail (default_path != NULL, NULL); |
301 | |||
302 | 7 | return g_object_new (GSS_TYPE_CONFIG_FILE, | |
303 | "paths", key_file_paths, | ||
304 | "default-resource", default_resource, | ||
305 | "default-path", default_path, | ||
306 | NULL); | ||
307 | } | ||
308 | |||
309 | static gboolean | ||
310 | 17 | gss_config_file_ensure_loaded (GssConfigFile *self, | |
311 | gsize idx, | ||
312 | GKeyFile **key_file_out, | ||
313 | GError **error) | ||
314 | { | ||
315 | 17 | g_autoptr(GError) local_error = NULL; | |
316 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
|
17 | const gchar *path = (idx < self->n_paths) ? self->paths[idx] : self->default_path; |
317 | 17 | gboolean is_default = (idx == self->n_paths); | |
318 | 17 | GKeyFile *key_file = NULL; | |
319 | |||
320 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | g_return_val_if_fail (idx <= self->n_paths, FALSE); |
321 | |||
322 | /* Handle the default key file as a special case, with an index just off the | ||
323 | * end of the array. */ | ||
324 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
|
17 | if (is_default) |
325 | { | ||
326 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (key_file_out != NULL) |
327 | 2 | *key_file_out = self->default_key_file; | |
328 | 2 | return TRUE; | |
329 | } | ||
330 | |||
331 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
|
15 | if (idx < self->key_files->len) |
332 | 2 | key_file = g_ptr_array_index (self->key_files, idx); | |
333 | |||
334 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 2 times.
|
15 | if (key_file == NULL) |
335 | { | ||
336 | 13 | key_file = g_key_file_new (); | |
337 | 13 | g_ptr_array_insert (self->key_files, (gint) idx, key_file); | |
338 | 13 | g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &local_error); | |
339 | } | ||
340 | |||
341 |
2/2✓ Branch 2 taken 5 times.
✓ Branch 3 taken 10 times.
|
15 | if (g_error_matches (local_error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) |
342 | { | ||
343 | /* File doesn’t exist. Don’t propagate the error. */ | ||
344 | 5 | g_debug ("Configuration file ‘%s’ not found.", path); | |
345 | /* fall through */ | ||
346 | } | ||
347 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
|
10 | else if (local_error != NULL) |
348 | { | ||
349 | 1 | g_propagate_error (error, g_steal_pointer (&local_error)); | |
350 | 1 | return FALSE; | |
351 | } | ||
352 | |||
353 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (key_file_out != NULL) |
354 | 14 | *key_file_out = key_file; | |
355 | |||
356 | 14 | return TRUE; | |
357 | } | ||
358 | |||
359 | static gboolean | ||
360 | 8 | gss_config_file_get_file_for_key (GssConfigFile *self, | |
361 | const gchar *group_name, | ||
362 | const gchar *key_name, | ||
363 | GKeyFile **key_file_out, | ||
364 | const gchar **path_out, | ||
365 | GError **error) | ||
366 | { | ||
367 | 8 | GKeyFile *key_file = NULL; | |
368 | 8 | const gchar *path = NULL; | |
369 | gsize i; | ||
370 | |||
371 | /* Deliberately iterate on (i == self->n_paths) — it’s a special case for | ||
372 | * gss_config_file_ensure_loaded() which loads the default config file. */ | ||
373 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | for (i = 0; i <= self->n_paths; i++) |
374 | { | ||
375 |
2/2✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
|
14 | path = (i < self->n_paths) ? self->paths[i] : self->default_path; |
376 | |||
377 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13 times.
|
14 | if (!gss_config_file_ensure_loaded (self, i, &key_file, error)) |
378 | 1 | return FALSE; | |
379 | |||
380 | /* Try and find the key in this file. */ | ||
381 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 6 times.
|
13 | if (g_key_file_has_key (key_file, group_name, key_name, NULL)) |
382 | 7 | break; | |
383 | } | ||
384 | |||
385 | /* Not found? */ | ||
386 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
|
7 | if (i > self->n_paths) |
387 | { | ||
388 | ✗ | key_file = NULL; | |
389 | ✗ | path = NULL; | |
390 | } | ||
391 | |||
392 |
1/2✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
|
7 | if (key_file_out != NULL) |
393 | 7 | *key_file_out = key_file; | |
394 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
|
7 | if (path_out != NULL) |
395 | 5 | *path_out = path; | |
396 | |||
397 | 7 | return TRUE; | |
398 | } | ||
399 | |||
400 | /** | ||
401 | * gss_config_file_get_uint: | ||
402 | * @self: an #GssConfigFile | ||
403 | * @group_name: name of the configuration group | ||
404 | * @key_name: name of the configuration key | ||
405 | * @min_value: minimum valid value (inclusive) | ||
406 | * @max_value: maximum valid value (inclusive) | ||
407 | * @error: return location for a #GError, or %NULL | ||
408 | * | ||
409 | * Load an unsigned integer value from the configuration, and validate that it | ||
410 | * lies in [@min_value, @max_value]. The given key must exist in the default | ||
411 | * configuration file, if not in any others. It will be loaded from the first | ||
412 | * configuration file which contains it. | ||
413 | * | ||
414 | * If the loaded value does not validate, %G_KEY_FILE_ERROR_INVALID_VALUE is | ||
415 | * returned. | ||
416 | * | ||
417 | * Returns: the loaded unsigned integer | ||
418 | * Since: 0.1.0 | ||
419 | */ | ||
420 | guint | ||
421 | 6 | gss_config_file_get_uint (GssConfigFile *self, | |
422 | const gchar *group_name, | ||
423 | const gchar *key_name, | ||
424 | guint min_value, | ||
425 | guint max_value, | ||
426 | GError **error) | ||
427 | { | ||
428 | 6 | g_autoptr(GError) local_error = NULL; | |
429 | GKeyFile *key_file; | ||
430 | guint64 val; | ||
431 | const gchar *path; | ||
432 | |||
433 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
6 | g_return_val_if_fail (GSS_IS_CONFIG_FILE (self), 0); |
434 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | g_return_val_if_fail (group_name != NULL, 0); |
435 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | g_return_val_if_fail (key_name != NULL, 0); |
436 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | g_return_val_if_fail (min_value <= max_value, 0); |
437 |
2/4✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
|
6 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
438 | |||
439 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
|
6 | if (!gss_config_file_get_file_for_key (self, group_name, key_name, &key_file, &path, error)) |
440 | 1 | return 0; | |
441 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | g_assert (key_file != NULL); |
442 | |||
443 | 5 | val = g_key_file_get_uint64 (key_file, group_name, key_name, &local_error); | |
444 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
|
5 | if (local_error != NULL) |
445 | { | ||
446 | ✗ | g_propagate_error (error, g_steal_pointer (&local_error)); | |
447 | ✗ | return 0; | |
448 | } | ||
449 | |||
450 |
2/4✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 5 times.
|
5 | if (val < min_value || val > max_value) |
451 | { | ||
452 | ✗ | g_set_error (error, G_KEY_FILE_ERROR, G_KEY_FILE_ERROR_INVALID_VALUE, | |
453 | "Integer value %" G_GUINT64_FORMAT " for key ‘%s/%s’ in " | ||
454 | "configuration file ‘%s’ outside valid range [%u, %u].", | ||
455 | val, group_name, key_name, path, min_value, max_value); | ||
456 | ✗ | return 0; | |
457 | } | ||
458 | |||
459 | 5 | return (guint) val; | |
460 | } | ||
461 | |||
462 | /** | ||
463 | * gss_config_file_get_boolean: | ||
464 | * @self: an #GssConfigFile | ||
465 | * @group_name: name of the configuration group | ||
466 | * @key_name: name of the configuration key | ||
467 | * @error: return location for a #GError, or %NULL | ||
468 | * | ||
469 | * Load a boolean value from the configuration. The given key must exist in the | ||
470 | * default configuration file, if not in any others. It will be loaded from the | ||
471 | * first configuration file which contains it. | ||
472 | * | ||
473 | * Returns: the loaded boolean | ||
474 | * Since: 0.1.0 | ||
475 | */ | ||
476 | gboolean | ||
477 | 2 | gss_config_file_get_boolean (GssConfigFile *self, | |
478 | const gchar *group_name, | ||
479 | const gchar *key_name, | ||
480 | GError **error) | ||
481 | { | ||
482 | GKeyFile *key_file; | ||
483 | |||
484 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | g_return_val_if_fail (GSS_IS_CONFIG_FILE (self), FALSE); |
485 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | g_return_val_if_fail (group_name != NULL, FALSE); |
486 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | g_return_val_if_fail (key_name != NULL, FALSE); |
487 |
2/4✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
|
2 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
488 | |||
489 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (!gss_config_file_get_file_for_key (self, group_name, key_name, &key_file, NULL, error)) |
490 | ✗ | return FALSE; | |
491 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
|
2 | g_assert (key_file != NULL); |
492 | |||
493 | 2 | return g_key_file_get_boolean (key_file, group_name, key_name, error); | |
494 | } | ||
495 | |||
496 | /** | ||
497 | * gss_config_file_get_string: | ||
498 | * @self: an #GssConfigFile | ||
499 | * @group_name: name of the configuration group | ||
500 | * @key_name: name of the configuration key | ||
501 | * @error: return location for a #GError, or %NULL | ||
502 | * | ||
503 | * Load a string value from the configuration. The given key must exist in the | ||
504 | * default configuration file, if not in any others. It will be loaded from the | ||
505 | * first configuration file which contains it. | ||
506 | * | ||
507 | * Returns: (transfer full): the loaded string, which is guaranteed to be | ||
508 | * non-%NULL but may be empty | ||
509 | * Since: 0.1.0 | ||
510 | */ | ||
511 | gchar * | ||
512 | ✗ | gss_config_file_get_string (GssConfigFile *self, | |
513 | const gchar *group_name, | ||
514 | const gchar *key_name, | ||
515 | GError **error) | ||
516 | { | ||
517 | GKeyFile *key_file; | ||
518 | |||
519 | ✗ | g_return_val_if_fail (GSS_IS_CONFIG_FILE (self), NULL); | |
520 | ✗ | g_return_val_if_fail (group_name != NULL, NULL); | |
521 | ✗ | g_return_val_if_fail (key_name != NULL, NULL); | |
522 | ✗ | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
523 | |||
524 | ✗ | if (!gss_config_file_get_file_for_key (self, group_name, key_name, &key_file, NULL, error)) | |
525 | ✗ | return NULL; | |
526 | ✗ | g_assert (key_file != NULL); | |
527 | |||
528 | ✗ | return g_key_file_get_string (key_file, group_name, key_name, error); | |
529 | } | ||
530 | |||
531 | /** | ||
532 | * gss_config_file_get_strv: | ||
533 | * @self: an #GssConfigFile | ||
534 | * @group_name: name of the configuration group | ||
535 | * @key_name: name of the configuration key | ||
536 | * @n_elements_out: (out caller-allocates) (optional): return location for the | ||
537 | * number of elements in the string array (not including the terminating | ||
538 | * %NULL), or %NULL | ||
539 | * @error: return location for a #GError, or %NULL | ||
540 | * | ||
541 | * Load a string array value from the configuration. The given key must exist | ||
542 | * in the default configuration file, if not in any others. It will be loaded | ||
543 | * from the first configuration file which contains it. | ||
544 | * | ||
545 | * Returns: (transfer full) (array zero-terminated=1 length=n_elements_out): | ||
546 | * the loaded string array, which is guaranteed to be non-%NULL but may be | ||
547 | * empty | ||
548 | * Since: 0.1.0 | ||
549 | */ | ||
550 | gchar ** | ||
551 | ✗ | gss_config_file_get_strv (GssConfigFile *self, | |
552 | const gchar *group_name, | ||
553 | const gchar *key_name, | ||
554 | gsize *n_elements_out, | ||
555 | GError **error) | ||
556 | { | ||
557 | GKeyFile *key_file; | ||
558 | |||
559 | ✗ | g_return_val_if_fail (GSS_IS_CONFIG_FILE (self), NULL); | |
560 | ✗ | g_return_val_if_fail (group_name != NULL, NULL); | |
561 | ✗ | g_return_val_if_fail (key_name != NULL, NULL); | |
562 | ✗ | g_return_val_if_fail (error == NULL || *error == NULL, NULL); | |
563 | |||
564 | ✗ | if (!gss_config_file_get_file_for_key (self, group_name, key_name, &key_file, NULL, error)) | |
565 | ✗ | return NULL; | |
566 | ✗ | g_assert (key_file != NULL); | |
567 | |||
568 | ✗ | return g_key_file_get_string_list (key_file, group_name, key_name, n_elements_out, error); | |
569 | } | ||
570 | |||
571 | static int | ||
572 | 4 | strcmp_p (const void *p1, | |
573 | const void *p2) | ||
574 | { | ||
575 | /* qsort() passes us pointer to the (gchar*)s. */ | ||
576 | 4 | return strcmp (*((char * const *) p1), *((char * const *) p2)); | |
577 | } | ||
578 | |||
579 | /** | ||
580 | * gss_config_file_get_groups: | ||
581 | * @self: an #GssConfigFile | ||
582 | * @n_groups_out: (out caller-allocates) (optional): return location for the | ||
583 | * number of groups returned (not including the terminating %NULL), or %NULL | ||
584 | * @error: return location for a #GError, or %NULL | ||
585 | * | ||
586 | * List the groups from all the configuration files, eliminating duplicates. | ||
587 | * Empty groups are included in the list. The list is sorted lexicographically. | ||
588 | * | ||
589 | * Returns: (transfer full) (array zero-terminated=1 length=n_groups_out): | ||
590 | * the groups in the configuration files, which is guaranteed to be non-%NULL | ||
591 | * but may be empty | ||
592 | * Since: 0.1.0 | ||
593 | */ | ||
594 | gchar ** | ||
595 | 1 | gss_config_file_get_groups (GssConfigFile *self, | |
596 | gsize *n_groups_out, | ||
597 | GError **error) | ||
598 | { | ||
599 | gsize i; | ||
600 | 1 | g_autoptr(GHashTable) groups = NULL; | |
601 | 1 | g_auto(GStrv) groups_array = NULL; | |
602 | gsize groups_array_len; | ||
603 | |||
604 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | g_return_val_if_fail (GSS_IS_CONFIG_FILE (self), NULL); |
605 |
2/4✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
1 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
606 | |||
607 | 1 | groups = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); | |
608 | |||
609 | /* Deliberately iterate on (i == self->n_paths) — it’s a special case for | ||
610 | * gss_config_file_ensure_loaded() which loads the default config file. */ | ||
611 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
|
4 | for (i = 0; i <= self->n_paths; i++) |
612 | { | ||
613 | GKeyFile *key_file; | ||
614 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
3 | g_auto(GStrv) file_groups = NULL; |
615 | gsize j; | ||
616 | |||
617 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
|
3 | if (!gss_config_file_ensure_loaded (self, i, &key_file, error)) |
618 | ✗ | return NULL; | |
619 | |||
620 | /* Get and deduplicate the groups for this file. */ | ||
621 | 3 | file_groups = g_key_file_get_groups (key_file, NULL); | |
622 | |||
623 |
3/4✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
|
9 | for (j = 0; file_groups != NULL && file_groups[j] != NULL; j++) |
624 | 6 | g_hash_table_add (groups, g_steal_pointer (&file_groups[j])); | |
625 | } | ||
626 | |||
627 | /* Convert to an array, sort and NULL terminate. */ | ||
628 | 1 | groups_array = (gchar **) g_hash_table_get_keys_as_array (groups, NULL); | |
629 | 1 | groups_array_len = g_hash_table_size (groups); | |
630 | 1 | g_hash_table_steal_all (groups); | |
631 | |||
632 | 1 | qsort (groups_array, groups_array_len, sizeof (*groups_array), strcmp_p); | |
633 | 1 | groups_array = g_realloc_n (g_steal_pointer (&groups_array), | |
634 | groups_array_len + 1, sizeof (*groups_array)); | ||
635 | 1 | groups_array[groups_array_len] = NULL; | |
636 | |||
637 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (n_groups_out != NULL) |
638 | 1 | *n_groups_out = groups_array_len; | |
639 | |||
640 | 1 | return g_steal_pointer (&groups_array); | |
641 | } | ||
642 |