GCC Code Coverage Report


Directory: ./
File: libgsystemservice/config-file.c
Date: 2024-04-09 14:29:48
Exec Total Coverage
Lines: 148 193 76.7%
Functions: 15 18 83.3%
Branches: 85 169 50.3%

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