1 /**
2 * @file
3 * Subset of config items
4 *
5 * @authors
6 * Copyright (C) 2019 Richard Russon <rich@flatcap.org>
7 *
8 * @copyright
9 * This program is free software: you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 2 of the License, or (at your option) any later
12 * version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /**
24 * @page config_subset Subset of config items
25 *
26 * Subset of config items
27 */
28
29 #include "config.h"
30 #include <limits.h>
31 #include <stdint.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include "mutt/lib.h"
35 #include "subset.h"
36 #include "set.h"
37
38 struct Notify;
39
40 /**
41 * ConfigEventNames - Names for logging
42 */
43 static const struct Mapping ConfigEventNames[] = {
44 // clang-format off
45 { "NT_CONFIG_SET", NT_CONFIG_SET },
46 { "NT_CONFIG_RESET", NT_CONFIG_RESET },
47 { NULL, 0 },
48 // clang-format on
49 };
50
51 /**
52 * elem_list_sort - Sort two HashElem pointers to config
53 * @param a First HashElem
54 * @param b Second HashElem
55 * @retval -1 a precedes b
56 * @retval 0 a and b are identical
57 * @retval 1 b precedes a
58 */
elem_list_sort(const void * a,const void * b)59 int elem_list_sort(const void *a, const void *b)
60 {
61 if (!a || !b)
62 return 0;
63
64 const struct HashElem *hea = *(struct HashElem const *const *) a;
65 const struct HashElem *heb = *(struct HashElem const *const *) b;
66
67 return mutt_istr_cmp(hea->key.strkey, heb->key.strkey);
68 }
69
70 /**
71 * get_elem_list - Create a sorted list of all config items
72 * @param cs ConfigSet to read
73 * @retval ptr Null-terminated array of HashElem
74 */
get_elem_list(struct ConfigSet * cs)75 struct HashElem **get_elem_list(struct ConfigSet *cs)
76 {
77 if (!cs)
78 return NULL;
79
80 struct HashElem **list = mutt_mem_calloc(1024, sizeof(struct HashElem *));
81 size_t index = 0;
82
83 struct HashWalkState walk = { 0 };
84 struct HashElem *he = NULL;
85
86 while ((he = mutt_hash_walk(cs->hash, &walk)))
87 {
88 list[index++] = he;
89 if (index == 1022)
90 break; /* LCOV_EXCL_LINE */
91 }
92
93 qsort(list, index, sizeof(struct HashElem *), elem_list_sort);
94
95 return list;
96 }
97
98 /**
99 * cs_subset_free - Free a Config Subset
100 * @param ptr Subset to free
101 *
102 * @note Config items matching this Subset will be freed
103 */
cs_subset_free(struct ConfigSubset ** ptr)104 void cs_subset_free(struct ConfigSubset **ptr)
105 {
106 if (!ptr || !*ptr)
107 return;
108
109 struct ConfigSubset *sub = *ptr;
110
111 if (sub->cs && sub->name)
112 {
113 char scope[256];
114 snprintf(scope, sizeof(scope), "%s:", sub->name);
115
116 // We don't know if any config items have been set,
117 // so search for anything with a matching scope.
118 struct HashElem **list = get_elem_list(sub->cs);
119 for (size_t i = 0; list[i]; i++)
120 {
121 const char *item = list[i]->key.strkey;
122 if (mutt_str_startswith(item, scope) != 0)
123 {
124 cs_uninherit_variable(sub->cs, item);
125 }
126 }
127 FREE(&list);
128 }
129
130 notify_free(&sub->notify);
131 FREE(&sub->name);
132 FREE(ptr);
133 }
134
135 /**
136 * cs_subset_new - Create a new Config Subset
137 * @param name Name for this Subset
138 * @param sub_parent Parent Subset
139 * @param not_parent Parent Notification
140 * @retval ptr New Subset
141 *
142 * @note The name will be combined with the parents' names
143 */
cs_subset_new(const char * name,struct ConfigSubset * sub_parent,struct Notify * not_parent)144 struct ConfigSubset *cs_subset_new(const char *name, struct ConfigSubset *sub_parent,
145 struct Notify *not_parent)
146 {
147 struct ConfigSubset *sub = mutt_mem_calloc(1, sizeof(*sub));
148
149 if (sub_parent)
150 {
151 sub->parent = sub_parent;
152 sub->cs = sub_parent->cs;
153 }
154
155 if (name)
156 {
157 char scope[256];
158
159 if (sub_parent && sub_parent->name)
160 snprintf(scope, sizeof(scope), "%s:%s", sub_parent->name, name);
161 else
162 mutt_str_copy(scope, name, sizeof(scope));
163
164 sub->name = mutt_str_dup(scope);
165 }
166
167 sub->notify = notify_new();
168 notify_set_parent(sub->notify, not_parent);
169
170 return sub;
171 }
172
173 /**
174 * cs_subset_lookup - Find an inherited config item
175 * @param sub Subset to search
176 * @param name Name of Config item to find
177 * @retval ptr HashElem of the config item
178 */
cs_subset_lookup(const struct ConfigSubset * sub,const char * name)179 struct HashElem *cs_subset_lookup(const struct ConfigSubset *sub, const char *name)
180 {
181 if (!sub || !name)
182 return NULL;
183
184 char scope[256];
185 if (sub->name)
186 snprintf(scope, sizeof(scope), "%s:%s", sub->name, name);
187 else
188 mutt_str_copy(scope, name, sizeof(scope));
189
190 return cs_get_elem(sub->cs, scope);
191 }
192
193 /**
194 * cs_subset_create_inheritance - Create a Subset config item (inherited)
195 * @param sub Config Subset
196 * @param name Name of config item
197 * @retval ptr HashElem of the config item
198 * @retval NULL Error
199 */
cs_subset_create_inheritance(const struct ConfigSubset * sub,const char * name)200 struct HashElem *cs_subset_create_inheritance(const struct ConfigSubset *sub, const char *name)
201 {
202 if (!sub)
203 return NULL;
204
205 struct HashElem *he = cs_subset_lookup(sub, name);
206 if (he)
207 return he;
208
209 if (sub->parent)
210 {
211 // Create parent before creating name
212 he = cs_subset_create_inheritance(sub->parent, name);
213 }
214
215 if (!he)
216 return NULL;
217
218 char scope[256];
219 snprintf(scope, sizeof(scope), "%s:%s", sub->name, name);
220 return cs_inherit_variable(sub->cs, he, scope);
221 }
222
223 /**
224 * cs_subset_notify_observers - Notify all observers of an event
225 * @param sub Config Subset
226 * @param he HashElem representing config item
227 * @param ev Type of event
228 */
cs_subset_notify_observers(const struct ConfigSubset * sub,struct HashElem * he,enum NotifyConfig ev)229 void cs_subset_notify_observers(const struct ConfigSubset *sub,
230 struct HashElem *he, enum NotifyConfig ev)
231 {
232 if (!sub || !he)
233 return;
234
235 struct HashElem *he_base = cs_get_base(he);
236 struct EventConfig ev_c = { sub, he_base->key.strkey, he };
237 mutt_debug(LL_NOTIFY, "%s: %s\n",
238 NONULL(mutt_map_get_name(ev, ConfigEventNames)), he_base->key.strkey);
239 notify_send(sub->notify, NT_CONFIG, ev, &ev_c);
240 }
241
242 /**
243 * cs_subset_he_native_get - Natively get the value of a HashElem config item
244 * @param sub Config Subset
245 * @param he HashElem representing config item
246 * @param err Buffer for error messages
247 * @retval intptr_t Native pointer/value
248 * @retval INT_MIN Error
249 */
cs_subset_he_native_get(const struct ConfigSubset * sub,struct HashElem * he,struct Buffer * err)250 intptr_t cs_subset_he_native_get(const struct ConfigSubset *sub,
251 struct HashElem *he, struct Buffer *err)
252 {
253 if (!sub)
254 return INT_MIN;
255
256 return cs_he_native_get(sub->cs, he, err);
257 }
258
259 /**
260 * cs_subset_str_native_get - Natively get the value of a string config item
261 * @param sub Config Subset
262 * @param name Name of config item
263 * @param err Buffer for error messages
264 * @retval intptr_t Native pointer/value
265 * @retval INT_MIN Error
266 */
cs_subset_str_native_get(const struct ConfigSubset * sub,const char * name,struct Buffer * err)267 intptr_t cs_subset_str_native_get(const struct ConfigSubset *sub,
268 const char *name, struct Buffer *err)
269 {
270 struct HashElem *he = cs_subset_create_inheritance(sub, name);
271
272 return cs_subset_he_native_get(sub, he, err);
273 }
274
275 /**
276 * cs_subset_he_native_set - Natively set the value of a HashElem config item
277 * @param sub Config Subset
278 * @param he HashElem representing config item
279 * @param value Native pointer/value to set
280 * @param err Buffer for error messages
281 * @retval num Result, e.g. #CSR_SUCCESS
282 */
cs_subset_he_native_set(const struct ConfigSubset * sub,struct HashElem * he,intptr_t value,struct Buffer * err)283 int cs_subset_he_native_set(const struct ConfigSubset *sub, struct HashElem *he,
284 intptr_t value, struct Buffer *err)
285 {
286 if (!sub)
287 return CSR_ERR_CODE;
288
289 int rc = cs_he_native_set(sub->cs, he, value, err);
290
291 if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
292 cs_subset_notify_observers(sub, he, NT_CONFIG_SET);
293
294 return rc;
295 }
296
297 /**
298 * cs_subset_str_native_set - Natively set the value of a string config item
299 * @param sub Config Subset
300 * @param name Name of config item
301 * @param value Native pointer/value to set
302 * @param err Buffer for error messages
303 * @retval num Result, e.g. #CSR_SUCCESS
304 */
cs_subset_str_native_set(const struct ConfigSubset * sub,const char * name,intptr_t value,struct Buffer * err)305 int cs_subset_str_native_set(const struct ConfigSubset *sub, const char *name,
306 intptr_t value, struct Buffer *err)
307 {
308 struct HashElem *he = cs_subset_create_inheritance(sub, name);
309
310 return cs_subset_he_native_set(sub, he, value, err);
311 }
312
313 /**
314 * cs_subset_he_reset - Reset a config item to its initial value
315 * @param sub Config Subset
316 * @param he HashElem representing config item
317 * @param err Buffer for error messages
318 * @retval num Result, e.g. #CSR_SUCCESS
319 */
cs_subset_he_reset(const struct ConfigSubset * sub,struct HashElem * he,struct Buffer * err)320 int cs_subset_he_reset(const struct ConfigSubset *sub, struct HashElem *he, struct Buffer *err)
321 {
322 if (!sub)
323 return CSR_ERR_CODE;
324
325 int rc = cs_he_reset(sub->cs, he, err);
326
327 if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
328 cs_subset_notify_observers(sub, he, NT_CONFIG_RESET);
329
330 return rc;
331 }
332
333 /**
334 * cs_subset_str_reset - Reset a config item to its initial value
335 * @param sub Config Subset
336 * @param name Name of config item
337 * @param err Buffer for error messages
338 * @retval num Result, e.g. #CSR_SUCCESS
339 */
cs_subset_str_reset(const struct ConfigSubset * sub,const char * name,struct Buffer * err)340 int cs_subset_str_reset(const struct ConfigSubset *sub, const char *name, struct Buffer *err)
341 {
342 struct HashElem *he = cs_subset_create_inheritance(sub, name);
343
344 return cs_subset_he_reset(sub, he, err);
345 }
346
347 /**
348 * cs_subset_he_string_get - Get a config item as a string
349 * @param sub Config Subset
350 * @param he HashElem representing config item
351 * @param result Buffer for results or error messages
352 * @retval num Result, e.g. #CSR_SUCCESS
353 */
cs_subset_he_string_get(const struct ConfigSubset * sub,struct HashElem * he,struct Buffer * result)354 int cs_subset_he_string_get(const struct ConfigSubset *sub, struct HashElem *he,
355 struct Buffer *result)
356 {
357 if (!sub)
358 return CSR_ERR_CODE;
359
360 return cs_he_string_get(sub->cs, he, result);
361 }
362
363 /**
364 * cs_subset_str_string_get - Get a config item as a string
365 * @param sub Config Subset
366 * @param name Name of config item
367 * @param result Buffer for results or error messages
368 * @retval num Result, e.g. #CSR_SUCCESS
369 */
cs_subset_str_string_get(const struct ConfigSubset * sub,const char * name,struct Buffer * result)370 int cs_subset_str_string_get(const struct ConfigSubset *sub, const char *name,
371 struct Buffer *result)
372 {
373 struct HashElem *he = cs_subset_create_inheritance(sub, name);
374
375 return cs_subset_he_string_get(sub, he, result);
376 }
377
378 /**
379 * cs_subset_he_string_set - Set a config item by string
380 * @param sub Config Subset
381 * @param he HashElem representing config item
382 * @param value Value to set
383 * @param err Buffer for error messages
384 * @retval num Result, e.g. #CSR_SUCCESS
385 */
cs_subset_he_string_set(const struct ConfigSubset * sub,struct HashElem * he,const char * value,struct Buffer * err)386 int cs_subset_he_string_set(const struct ConfigSubset *sub, struct HashElem *he,
387 const char *value, struct Buffer *err)
388 {
389 if (!sub)
390 return CSR_ERR_CODE;
391
392 int rc = cs_he_string_set(sub->cs, he, value, err);
393
394 if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
395 cs_subset_notify_observers(sub, he, NT_CONFIG_SET);
396
397 return rc;
398 }
399
400 /**
401 * cs_subset_str_string_set - Set a config item by string
402 * @param sub Config Subset
403 * @param name Name of config item
404 * @param value Value to set
405 * @param err Buffer for error messages
406 * @retval num Result, e.g. #CSR_SUCCESS
407 */
cs_subset_str_string_set(const struct ConfigSubset * sub,const char * name,const char * value,struct Buffer * err)408 int cs_subset_str_string_set(const struct ConfigSubset *sub, const char *name,
409 const char *value, struct Buffer *err)
410 {
411 struct HashElem *he = cs_subset_create_inheritance(sub, name);
412
413 return cs_subset_he_string_set(sub, he, value, err);
414 }
415
416 /**
417 * cs_subset_he_string_plus_equals - Add to a config item by string
418 * @param sub Config Subset
419 * @param he HashElem representing config item
420 * @param value Value to set
421 * @param err Buffer for error messages
422 * @retval num Result, e.g. #CSR_SUCCESS
423 */
cs_subset_he_string_plus_equals(const struct ConfigSubset * sub,struct HashElem * he,const char * value,struct Buffer * err)424 int cs_subset_he_string_plus_equals(const struct ConfigSubset *sub, struct HashElem *he,
425 const char *value, struct Buffer *err)
426 {
427 if (!sub)
428 return CSR_ERR_CODE;
429
430 int rc = cs_he_string_plus_equals(sub->cs, he, value, err);
431
432 if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
433 cs_subset_notify_observers(sub, he, NT_CONFIG_SET);
434
435 return rc;
436 }
437
438 /**
439 * cs_subset_str_string_plus_equals - Add to a config item by string
440 * @param sub Config Subset
441 * @param name Name of config item
442 * @param value Value to set
443 * @param err Buffer for error messages
444 * @retval num Result, e.g. #CSR_SUCCESS
445 */
cs_subset_str_string_plus_equals(const struct ConfigSubset * sub,const char * name,const char * value,struct Buffer * err)446 int cs_subset_str_string_plus_equals(const struct ConfigSubset *sub, const char *name,
447 const char *value, struct Buffer *err)
448 {
449 struct HashElem *he = cs_subset_create_inheritance(sub, name);
450
451 return cs_subset_he_string_plus_equals(sub, he, value, err);
452 }
453
454 /**
455 * cs_subset_he_string_minus_equals - Remove from a config item by string
456 * @param sub Config Subset
457 * @param he HashElem representing config item
458 * @param value Value to set
459 * @param err Buffer for error messages
460 * @retval num Result, e.g. #CSR_SUCCESS
461 */
cs_subset_he_string_minus_equals(const struct ConfigSubset * sub,struct HashElem * he,const char * value,struct Buffer * err)462 int cs_subset_he_string_minus_equals(const struct ConfigSubset *sub, struct HashElem *he,
463 const char *value, struct Buffer *err)
464 {
465 if (!sub)
466 return CSR_ERR_CODE;
467
468 int rc = cs_he_string_minus_equals(sub->cs, he, value, err);
469
470 if ((CSR_RESULT(rc) == CSR_SUCCESS) && !(rc & CSR_SUC_NO_CHANGE))
471 cs_subset_notify_observers(sub, he, NT_CONFIG_SET);
472
473 return rc;
474 }
475
476 /**
477 * cs_subset_str_string_minus_equals - Remove from a config item by string
478 * @param sub Config Subset
479 * @param name Name of config item
480 * @param value Value to set
481 * @param err Buffer for error messages
482 * @retval num Result, e.g. #CSR_SUCCESS
483 */
cs_subset_str_string_minus_equals(const struct ConfigSubset * sub,const char * name,const char * value,struct Buffer * err)484 int cs_subset_str_string_minus_equals(const struct ConfigSubset *sub, const char *name,
485 const char *value, struct Buffer *err)
486 {
487 struct HashElem *he = cs_subset_create_inheritance(sub, name);
488
489 return cs_subset_he_string_minus_equals(sub, he, value, err);
490 }
491