1 /**
2 * @file
3 * A collection of config items
4 *
5 * @authors
6 * Copyright (C) 2017-2018 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_set Config Set
25 *
26 * A collection of config items.
27 */
28
29 #include "config.h"
30 #include <limits.h>
31 #include <stdio.h>
32 #include "mutt/lib.h"
33 #include "set.h"
34 #include "inheritance.h"
35 #include "types.h"
36
37 struct ConfigSetType RegisteredTypes[18] = {
38 { 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
39 };
40
41 /**
42 * cs_hashelem_free - Callback function for the Hash Table - Implements ::hash_hdata_free_t - @ingroup hash_hdata_free_api
43 * @param type Object type, e.g. #DT_STRING
44 * @param obj Object to destroy
45 * @param data ConfigSet associated with the object
46 */
cs_hashelem_free(int type,void * obj,intptr_t data)47 static void cs_hashelem_free(int type, void *obj, intptr_t data)
48 {
49 if (data == 0)
50 return; /* LCOV_EXCL_LINE */
51
52 struct ConfigSet *cs = (struct ConfigSet *) data;
53
54 const struct ConfigSetType *cst = NULL;
55
56 if (type & DT_INHERITED)
57 {
58 struct Inheritance *i = obj;
59
60 struct HashElem *he_base = cs_get_base(i->parent);
61 struct ConfigDef *cdef = he_base->data;
62
63 if (!cdef)
64 return; // LCOV_EXCL_LINE
65
66 cst = cs_get_type_def(cs, he_base->type);
67 if (cst && cst->destroy)
68 cst->destroy(cs, (void **) &i->var, cdef);
69
70 FREE(&i->name);
71 FREE(&i);
72 }
73 else
74 {
75 struct ConfigDef *cdef = obj;
76
77 cst = cs_get_type_def(cs, type);
78 if (cst && cst->destroy)
79 cst->destroy(cs, &cdef->var, cdef);
80
81 /* If we allocated the initial value, clean it up */
82 if (cdef->type & DT_INITIAL_SET)
83 FREE(&cdef->initial);
84 }
85 }
86
87 /**
88 * create_synonym - Create an alternative name for a config item
89 * @param cs Config items
90 * @param cdef Variable definition
91 * @param err Buffer for error messages
92 * @retval ptr New HashElem representing the config item synonym
93 */
create_synonym(const struct ConfigSet * cs,struct ConfigDef * cdef,struct Buffer * err)94 static struct HashElem *create_synonym(const struct ConfigSet *cs,
95 struct ConfigDef *cdef, struct Buffer *err)
96 {
97 if (!cs || !cdef)
98 return NULL; /* LCOV_EXCL_LINE */
99
100 const char *name = (const char *) cdef->initial;
101 struct HashElem *parent = cs_get_elem(cs, name);
102 if (!parent)
103 {
104 mutt_buffer_printf(err, _("No such variable: %s"), name);
105 return NULL;
106 }
107
108 struct HashElem *child =
109 mutt_hash_typed_insert(cs->hash, cdef->name, cdef->type, (void *) cdef);
110 if (!child)
111 return NULL; /* LCOV_EXCL_LINE */
112
113 cdef->var = (intptr_t) parent;
114 return child;
115 }
116
117 /**
118 * reg_one_var - Register one config item
119 * @param cs Config items
120 * @param cdef Variable definition
121 * @param err Buffer for error messages
122 * @retval ptr New HashElem representing the config item
123 */
reg_one_var(const struct ConfigSet * cs,struct ConfigDef * cdef,struct Buffer * err)124 static struct HashElem *reg_one_var(const struct ConfigSet *cs,
125 struct ConfigDef *cdef, struct Buffer *err)
126 {
127 if (!cs || !cdef)
128 return NULL; /* LCOV_EXCL_LINE */
129
130 if (DTYPE(cdef->type) == DT_SYNONYM)
131 return create_synonym(cs, cdef, err);
132
133 const struct ConfigSetType *cst = cs_get_type_def(cs, cdef->type);
134 if (!cst)
135 {
136 mutt_buffer_printf(err, _("Variable '%s' has an invalid type %d"),
137 cdef->name, cdef->type);
138 return NULL;
139 }
140
141 struct HashElem *he = mutt_hash_typed_insert(cs->hash, cdef->name, cdef->type, cdef);
142 if (!he)
143 return NULL; /* LCOV_EXCL_LINE */
144
145 if (cst && cst->reset)
146 cst->reset(cs, &cdef->var, cdef, err);
147
148 return he;
149 }
150
151 /**
152 * cs_new - Create a new Config Set
153 * @param size Number of expected config items
154 * @retval ptr New ConfigSet object
155 */
cs_new(size_t size)156 struct ConfigSet *cs_new(size_t size)
157 {
158 struct ConfigSet *cs = mutt_mem_calloc(1, sizeof(*cs));
159
160 cs->hash = mutt_hash_new(size, MUTT_HASH_NO_FLAGS);
161 mutt_hash_set_destructor(cs->hash, cs_hashelem_free, (intptr_t) cs);
162
163 return cs;
164 }
165
166 /**
167 * cs_free - Free a Config Set
168 * @param[out] ptr Config items
169 */
cs_free(struct ConfigSet ** ptr)170 void cs_free(struct ConfigSet **ptr)
171 {
172 if (!ptr || !*ptr)
173 return;
174
175 struct ConfigSet *cs = *ptr;
176
177 mutt_hash_free(&cs->hash);
178 FREE(ptr);
179 }
180
181 /**
182 * cs_get_base - Find the root Config Item
183 * @param he Config Item to examine
184 * @retval ptr Root Config Item
185 *
186 * Given an inherited HashElem, find the HashElem representing the original
187 * Config Item.
188 */
cs_get_base(struct HashElem * he)189 struct HashElem *cs_get_base(struct HashElem *he)
190 {
191 if (!(he->type & DT_INHERITED))
192 return he;
193
194 struct Inheritance *i = he->data;
195 return cs_get_base(i->parent);
196 }
197
198 /**
199 * cs_get_elem - Get the HashElem representing a config item
200 * @param cs Config items
201 * @param name Name of config item
202 * @retval ptr HashElem representing the config item
203 */
cs_get_elem(const struct ConfigSet * cs,const char * name)204 struct HashElem *cs_get_elem(const struct ConfigSet *cs, const char *name)
205 {
206 if (!cs || !name)
207 return NULL;
208
209 struct HashElem *he = mutt_hash_find_elem(cs->hash, name);
210 if (!he)
211 return NULL;
212
213 if (DTYPE(he->type) != DT_SYNONYM)
214 return he;
215
216 const struct ConfigDef *cdef = he->data;
217
218 return (struct HashElem *) cdef->var;
219 }
220
221 /**
222 * cs_get_type_def - Get the definition for a type
223 * @param cs Config items
224 * @param type Type to lookup, e.g. #DT_NUMBER
225 * @retval ptr ConfigSetType representing the type
226 */
cs_get_type_def(const struct ConfigSet * cs,unsigned int type)227 const struct ConfigSetType *cs_get_type_def(const struct ConfigSet *cs, unsigned int type)
228 {
229 if (!cs)
230 return NULL;
231
232 type = DTYPE(type);
233 if ((type < 1) || (type >= mutt_array_size(cs->types)))
234 return NULL;
235
236 if (!cs->types[type].name)
237 return NULL;
238
239 return &cs->types[type];
240 }
241
242 /**
243 * cs_register_type - Register a type of config item
244 * @param cs Config items
245 * @param cst Structure defining the type
246 * @retval true Type was registered successfully
247 */
cs_register_type(struct ConfigSet * cs,const struct ConfigSetType * cst)248 bool cs_register_type(struct ConfigSet *cs, const struct ConfigSetType *cst)
249 {
250 if (!cs || !cst)
251 return false;
252
253 if (!cst->name || !cst->string_set || !cst->string_get || !cst->reset ||
254 !cst->native_set || !cst->native_get)
255 {
256 return false;
257 }
258
259 if (cst->type >= mutt_array_size(cs->types))
260 return false;
261
262 if (cs->types[cst->type].name)
263 return false; /* already registered */
264
265 cs->types[cst->type] = *cst;
266 return true;
267 }
268
269 /**
270 * cs_register_variables - Register a set of config items
271 * @param cs Config items
272 * @param vars Variable definition
273 * @param flags Flags, e.g. #DT_DEPRECATED
274 * @retval true All variables were registered successfully
275 */
cs_register_variables(const struct ConfigSet * cs,struct ConfigDef vars[],uint32_t flags)276 bool cs_register_variables(const struct ConfigSet *cs, struct ConfigDef vars[], uint32_t flags)
277 {
278 if (!cs || !vars)
279 return false;
280
281 struct Buffer err = mutt_buffer_make(0);
282
283 bool rc = true;
284
285 for (size_t i = 0; vars[i].name; i++)
286 {
287 vars[i].type |= flags;
288 if (!reg_one_var(cs, &vars[i], &err))
289 {
290 mutt_debug(LL_DEBUG1, "%s\n", mutt_buffer_string(&err));
291 rc = false;
292 }
293 }
294
295 mutt_buffer_dealloc(&err);
296 return rc;
297 }
298
299 /**
300 * cs_inherit_variable - Create in inherited config item
301 * @param cs Config items
302 * @param parent HashElem of parent config item
303 * @param name Name of account-specific config item
304 * @retval ptr New HashElem representing the inherited config item
305 */
cs_inherit_variable(const struct ConfigSet * cs,struct HashElem * parent,const char * name)306 struct HashElem *cs_inherit_variable(const struct ConfigSet *cs,
307 struct HashElem *parent, const char *name)
308 {
309 if (!cs || !parent)
310 return NULL;
311
312 struct Inheritance *i = mutt_mem_calloc(1, sizeof(*i));
313 i->parent = parent;
314 i->name = mutt_str_dup(name);
315
316 struct HashElem *he = mutt_hash_typed_insert(cs->hash, i->name, DT_INHERITED, i);
317 if (!he)
318 {
319 FREE(&i->name);
320 FREE(&i);
321 }
322
323 return he;
324 }
325
326 /**
327 * cs_uninherit_variable - Remove an inherited config item
328 * @param cs Config items
329 * @param name Name of config item to remove
330 */
cs_uninherit_variable(const struct ConfigSet * cs,const char * name)331 void cs_uninherit_variable(const struct ConfigSet *cs, const char *name)
332 {
333 if (!cs || !name)
334 return;
335
336 mutt_hash_delete(cs->hash, name, NULL);
337 }
338
339 /**
340 * cs_he_reset - Reset a config item to its initial value
341 * @param cs Config items
342 * @param he HashElem representing config item
343 * @param err Buffer for error messages
344 * @retval num Result, e.g. #CSR_SUCCESS
345 */
cs_he_reset(const struct ConfigSet * cs,struct HashElem * he,struct Buffer * err)346 int cs_he_reset(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
347 {
348 if (!cs || !he)
349 return CSR_ERR_CODE;
350
351 /* An inherited var that's already pointing to its parent.
352 * Return 'success', but don't send a notification. */
353 if ((he->type & DT_INHERITED) && (DTYPE(he->type) == 0))
354 return CSR_SUCCESS;
355
356 int rc = CSR_SUCCESS;
357
358 if (he->type & DT_INHERITED)
359 {
360 struct Inheritance *i = he->data;
361 struct HashElem *he_base = cs_get_base(he);
362 struct ConfigDef *cdef = he_base->data;
363 if (!cdef)
364 return CSR_ERR_CODE; // LCOV_EXCL_LINE
365
366 const struct ConfigSetType *cst = cs_get_type_def(cs, he_base->type);
367 if (cst && cst->destroy)
368 cst->destroy(cs, (void **) &i->var, cdef);
369
370 he->type = DT_INHERITED;
371 }
372 else
373 {
374 struct ConfigDef *cdef = he->data;
375 if (!cdef)
376 return CSR_ERR_CODE; // LCOV_EXCL_LINE
377
378 const struct ConfigSetType *cst = cs_get_type_def(cs, he->type);
379 if (cst)
380 rc = cst->reset(cs, &cdef->var, cdef, err);
381 }
382
383 return rc;
384 }
385
386 /**
387 * cs_str_reset - Reset a config item to its initial value
388 * @param cs Config items
389 * @param name Name of config item
390 * @param err Buffer for error messages
391 * @retval num Result, e.g. #CSR_SUCCESS
392 */
cs_str_reset(const struct ConfigSet * cs,const char * name,struct Buffer * err)393 int cs_str_reset(const struct ConfigSet *cs, const char *name, struct Buffer *err)
394 {
395 if (!cs || !name)
396 return CSR_ERR_CODE;
397
398 struct HashElem *he = cs_get_elem(cs, name);
399 if (!he)
400 {
401 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
402 return CSR_ERR_UNKNOWN;
403 }
404
405 return cs_he_reset(cs, he, err);
406 }
407
408 /**
409 * cs_he_initial_set - Set the initial value of a config item
410 * @param cs Config items
411 * @param he HashElem representing config item
412 * @param value Value to set
413 * @param err Buffer for error messages
414 * @retval num Result, e.g. #CSR_SUCCESS
415 */
cs_he_initial_set(const struct ConfigSet * cs,struct HashElem * he,const char * value,struct Buffer * err)416 int cs_he_initial_set(const struct ConfigSet *cs, struct HashElem *he,
417 const char *value, struct Buffer *err)
418 {
419 if (!cs || !he)
420 return CSR_ERR_CODE;
421
422 struct ConfigDef *cdef = NULL;
423
424 if (he->type & DT_INHERITED)
425 {
426 struct HashElem *he_base = cs_get_base(he);
427 cdef = he_base->data;
428 mutt_debug(LL_DEBUG1, "Variable '%s' is inherited type\n", cdef->name);
429 return CSR_ERR_CODE;
430 }
431
432 cdef = he->data;
433 if (!cdef)
434 return CSR_ERR_CODE; // LCOV_EXCL_LINE
435
436 const struct ConfigSetType *cst = cs_get_type_def(cs, he->type);
437 if (!cst)
438 {
439 mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
440 return CSR_ERR_CODE;
441 }
442
443 int rc = cst->string_set(cs, NULL, cdef, value, err);
444 if (CSR_RESULT(rc) != CSR_SUCCESS)
445 return rc;
446
447 return CSR_SUCCESS;
448 }
449
450 /**
451 * cs_str_initial_set - Set the initial value of a config item
452 * @param cs Config items
453 * @param name Name of config item
454 * @param value Value to set
455 * @param err Buffer for error messages
456 * @retval num Result, e.g. #CSR_SUCCESS
457 */
cs_str_initial_set(const struct ConfigSet * cs,const char * name,const char * value,struct Buffer * err)458 int cs_str_initial_set(const struct ConfigSet *cs, const char *name,
459 const char *value, struct Buffer *err)
460 {
461 if (!cs || !name)
462 return CSR_ERR_CODE;
463
464 struct HashElem *he = cs_get_elem(cs, name);
465 if (!he)
466 {
467 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
468 return CSR_ERR_UNKNOWN;
469 }
470
471 return cs_he_initial_set(cs, he, value, err);
472 }
473
474 /**
475 * cs_he_initial_get - Get the initial, or parent, value of a config item
476 * @param cs Config items
477 * @param he HashElem representing config item
478 * @param result Buffer for results or error messages
479 * @retval num Result, e.g. #CSR_SUCCESS
480 *
481 * If a config item is inherited from another, then this will get the parent's
482 * value. Otherwise, it will get the config item's initial value.
483 */
cs_he_initial_get(const struct ConfigSet * cs,struct HashElem * he,struct Buffer * result)484 int cs_he_initial_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
485 {
486 if (!cs || !he || !result)
487 return CSR_ERR_CODE;
488
489 const struct ConfigDef *cdef = NULL;
490 const struct ConfigSetType *cst = NULL;
491
492 if (he->type & DT_INHERITED)
493 {
494 struct HashElem *he_base = cs_get_base(he);
495 cdef = he_base->data;
496 cst = cs_get_type_def(cs, he_base->type);
497 }
498 else
499 {
500 cdef = he->data;
501 cst = cs_get_type_def(cs, he->type);
502 }
503
504 if (!cst)
505 return CSR_ERR_CODE; // LCOV_EXCL_LINE
506
507 return cst->string_get(cs, NULL, cdef, result);
508 }
509
510 /**
511 * cs_str_initial_get - Get the initial, or parent, value of a config item
512 * @param cs Config items
513 * @param name Name of config item
514 * @param result Buffer for results or error messages
515 * @retval num Result, e.g. #CSR_SUCCESS
516 *
517 * If a config item is inherited from another, then this will get the parent's
518 * value. Otherwise, it will get the config item's initial value.
519 */
cs_str_initial_get(const struct ConfigSet * cs,const char * name,struct Buffer * result)520 int cs_str_initial_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
521 {
522 if (!cs || !name)
523 return CSR_ERR_CODE;
524
525 struct HashElem *he = cs_get_elem(cs, name);
526 if (!he)
527 {
528 mutt_buffer_printf(result, _("Unknown variable '%s'"), name);
529 return CSR_ERR_UNKNOWN;
530 }
531
532 return cs_he_initial_get(cs, he, result);
533 }
534
535 /**
536 * cs_he_string_set - Set a config item by string
537 * @param cs Config items
538 * @param he HashElem representing config item
539 * @param value Value to set
540 * @param err Buffer for error messages
541 * @retval num Result, e.g. #CSR_SUCCESS
542 */
cs_he_string_set(const struct ConfigSet * cs,struct HashElem * he,const char * value,struct Buffer * err)543 int cs_he_string_set(const struct ConfigSet *cs, struct HashElem *he,
544 const char *value, struct Buffer *err)
545 {
546 if (!cs || !he)
547 return CSR_ERR_CODE;
548
549 struct ConfigDef *cdef = NULL;
550 const struct ConfigSetType *cst = NULL;
551 void *var = NULL;
552
553 if (he->type & DT_INHERITED)
554 {
555 struct Inheritance *i = he->data;
556 struct HashElem *he_base = cs_get_base(he);
557 cdef = he_base->data;
558 cst = cs_get_type_def(cs, he_base->type);
559 var = &i->var;
560 }
561 else
562 {
563 cdef = he->data;
564 cst = cs_get_type_def(cs, he->type);
565 var = &cdef->var;
566 }
567
568 if (!cdef)
569 return CSR_ERR_CODE; // LCOV_EXCL_LINE
570
571 if (!cst)
572 {
573 mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
574 return CSR_ERR_CODE;
575 }
576
577 int rc = cst->string_set(cs, var, cdef, value, err);
578 if (CSR_RESULT(rc) != CSR_SUCCESS)
579 return rc;
580
581 if (he->type & DT_INHERITED)
582 he->type = cdef->type | DT_INHERITED;
583
584 return rc;
585 }
586
587 /**
588 * cs_str_string_set - Set a config item by string
589 * @param cs Config items
590 * @param name Name of config item
591 * @param value Value to set
592 * @param err Buffer for error messages
593 * @retval num Result, e.g. #CSR_SUCCESS
594 */
cs_str_string_set(const struct ConfigSet * cs,const char * name,const char * value,struct Buffer * err)595 int cs_str_string_set(const struct ConfigSet *cs, const char *name,
596 const char *value, struct Buffer *err)
597 {
598 if (!cs || !name)
599 return CSR_ERR_CODE;
600
601 struct HashElem *he = cs_get_elem(cs, name);
602 if (!he)
603 {
604 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
605 return CSR_ERR_UNKNOWN;
606 }
607
608 return cs_he_string_set(cs, he, value, err);
609 }
610
611 /**
612 * cs_he_string_get - Get a config item as a string
613 * @param cs Config items
614 * @param he HashElem representing config item
615 * @param result Buffer for results or error messages
616 * @retval num Result, e.g. #CSR_SUCCESS
617 */
cs_he_string_get(const struct ConfigSet * cs,struct HashElem * he,struct Buffer * result)618 int cs_he_string_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *result)
619 {
620 if (!cs || !he || !result)
621 return CSR_ERR_CODE;
622
623 struct ConfigDef *cdef = NULL;
624 const struct ConfigSetType *cst = NULL;
625 void *var = NULL;
626
627 if (he->type & DT_INHERITED)
628 {
629 struct Inheritance *i = he->data;
630
631 // inherited, value not set
632 if (DTYPE(he->type) == 0)
633 return cs_he_string_get(cs, i->parent, result);
634
635 // inherited, value set
636 struct HashElem *he_base = cs_get_base(he);
637 cdef = he_base->data;
638 cst = cs_get_type_def(cs, he_base->type);
639 var = &i->var;
640 }
641 else
642 {
643 // not inherited
644 cdef = he->data;
645 cst = cs_get_type_def(cs, he->type);
646 var = &cdef->var;
647 }
648
649 if (!cdef || !cst)
650 return CSR_ERR_CODE; // LCOV_EXCL_LINE
651
652 return cst->string_get(cs, var, cdef, result);
653 }
654
655 /**
656 * cs_str_string_get - Get a config item as a string
657 * @param cs Config items
658 * @param name Name of config item
659 * @param result Buffer for results or error messages
660 * @retval num Result, e.g. #CSR_SUCCESS
661 */
cs_str_string_get(const struct ConfigSet * cs,const char * name,struct Buffer * result)662 int cs_str_string_get(const struct ConfigSet *cs, const char *name, struct Buffer *result)
663 {
664 if (!cs || !name)
665 return CSR_ERR_CODE;
666
667 struct HashElem *he = cs_get_elem(cs, name);
668 if (!he)
669 {
670 mutt_buffer_printf(result, _("Unknown variable '%s'"), name);
671 return CSR_ERR_UNKNOWN;
672 }
673
674 return cs_he_string_get(cs, he, result);
675 }
676
677 /**
678 * cs_he_native_set - Natively set the value of a HashElem config item
679 * @param cs Config items
680 * @param he HashElem representing config item
681 * @param value Native pointer/value to set
682 * @param err Buffer for error messages
683 * @retval num Result, e.g. #CSR_SUCCESS
684 */
cs_he_native_set(const struct ConfigSet * cs,struct HashElem * he,intptr_t value,struct Buffer * err)685 int cs_he_native_set(const struct ConfigSet *cs, struct HashElem *he,
686 intptr_t value, struct Buffer *err)
687 {
688 if (!cs || !he)
689 return CSR_ERR_CODE;
690
691 struct ConfigDef *cdef = NULL;
692 const struct ConfigSetType *cst = NULL;
693 void *var = NULL;
694
695 if (he->type & DT_INHERITED)
696 {
697 struct Inheritance *i = he->data;
698 struct HashElem *he_base = cs_get_base(he);
699 cdef = he_base->data;
700 cst = cs_get_type_def(cs, he_base->type);
701 var = &i->var;
702 }
703 else
704 {
705 cdef = he->data;
706 cst = cs_get_type_def(cs, he->type);
707 var = &cdef->var;
708 }
709
710 if (!cst)
711 {
712 mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
713 return CSR_ERR_CODE;
714 }
715
716 if (!var || !cdef)
717 return CSR_ERR_CODE; // LCOV_EXCL_LINE
718
719 int rc = cst->native_set(cs, var, cdef, value, err);
720 if (CSR_RESULT(rc) != CSR_SUCCESS)
721 return rc;
722
723 if (he->type & DT_INHERITED)
724 he->type = cdef->type | DT_INHERITED;
725
726 return rc;
727 }
728
729 /**
730 * cs_str_native_set - Natively set the value of a string config item
731 * @param cs Config items
732 * @param name Name of config item
733 * @param value Native pointer/value to set
734 * @param err Buffer for error messages
735 * @retval num Result, e.g. #CSR_SUCCESS
736 */
cs_str_native_set(const struct ConfigSet * cs,const char * name,intptr_t value,struct Buffer * err)737 int cs_str_native_set(const struct ConfigSet *cs, const char *name,
738 intptr_t value, struct Buffer *err)
739 {
740 if (!cs || !name)
741 return CSR_ERR_CODE;
742
743 struct HashElem *he = cs_get_elem(cs, name);
744 if (!he)
745 {
746 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
747 return CSR_ERR_UNKNOWN;
748 }
749
750 struct ConfigDef *cdef = NULL;
751 const struct ConfigSetType *cst = NULL;
752 void *var = NULL;
753
754 if (he->type & DT_INHERITED)
755 {
756 struct Inheritance *i = he->data;
757 struct HashElem *he_base = cs_get_base(he);
758 cdef = he_base->data;
759 cst = cs_get_type_def(cs, he_base->type);
760 var = &i->var;
761 }
762 else
763 {
764 cdef = he->data;
765 cst = cs_get_type_def(cs, he->type);
766 var = &cdef->var;
767 }
768
769 if (!cst || !var || !cdef)
770 return CSR_ERR_CODE; /* LCOV_EXCL_LINE */
771
772 int rc = cst->native_set(cs, var, cdef, value, err);
773 if (CSR_RESULT(rc) != CSR_SUCCESS)
774 return rc;
775
776 if (he->type & DT_INHERITED)
777 he->type = cdef->type | DT_INHERITED;
778
779 return rc;
780 }
781
782 /**
783 * cs_he_native_get - Natively get the value of a HashElem config item
784 * @param cs Config items
785 * @param he HashElem representing config item
786 * @param err Buffer for results or error messages
787 * @retval intptr_t Native pointer/value
788 * @retval INT_MIN Error
789 */
cs_he_native_get(const struct ConfigSet * cs,struct HashElem * he,struct Buffer * err)790 intptr_t cs_he_native_get(const struct ConfigSet *cs, struct HashElem *he, struct Buffer *err)
791 {
792 if (!cs || !he)
793 return INT_MIN;
794
795 struct ConfigDef *cdef = NULL;
796 const struct ConfigSetType *cst = NULL;
797 void *var = NULL;
798
799 if (he->type & DT_INHERITED)
800 {
801 struct Inheritance *i = he->data;
802
803 // inherited, value not set
804 if (DTYPE(he->type) == 0)
805 return cs_he_native_get(cs, i->parent, err);
806
807 // inherited, value set
808 struct HashElem *he_base = cs_get_base(he);
809 cdef = he_base->data;
810 cst = cs_get_type_def(cs, he_base->type);
811 var = &i->var;
812 }
813 else
814 {
815 // not inherited
816 cdef = he->data;
817 cst = cs_get_type_def(cs, he->type);
818 var = &cdef->var;
819 }
820
821 if (!var || !cdef)
822 return INT_MIN; // LCOV_EXCL_LINE
823
824 if (!cst)
825 {
826 mutt_buffer_printf(err, _("Variable '%s' has an invalid type %d"), cdef->name, he->type);
827 return INT_MIN;
828 }
829
830 return cst->native_get(cs, var, cdef, err);
831 }
832
833 /**
834 * cs_str_native_get - Natively get the value of a string config item
835 * @param cs Config items
836 * @param name Name of config item
837 * @param err Buffer for error messages
838 * @retval intptr_t Native pointer/value
839 * @retval INT_MIN Error
840 */
cs_str_native_get(const struct ConfigSet * cs,const char * name,struct Buffer * err)841 intptr_t cs_str_native_get(const struct ConfigSet *cs, const char *name, struct Buffer *err)
842 {
843 if (!cs || !name)
844 return INT_MIN;
845
846 struct HashElem *he = cs_get_elem(cs, name);
847 return cs_he_native_get(cs, he, err);
848 }
849
850 /**
851 * cs_he_string_plus_equals - Add to a config item by string
852 * @param cs Config items
853 * @param he HashElem representing config item
854 * @param value Value to set
855 * @param err Buffer for error messages
856 * @retval num Result, e.g. #CSR_SUCCESS
857 */
cs_he_string_plus_equals(const struct ConfigSet * cs,struct HashElem * he,const char * value,struct Buffer * err)858 int cs_he_string_plus_equals(const struct ConfigSet *cs, struct HashElem *he,
859 const char *value, struct Buffer *err)
860 {
861 if (!cs || !he)
862 return CSR_ERR_CODE;
863
864 struct ConfigDef *cdef = NULL;
865 const struct ConfigSetType *cst = NULL;
866 void *var = NULL;
867
868 if (he->type & DT_INHERITED)
869 {
870 struct Inheritance *i = he->data;
871 struct HashElem *he_base = cs_get_base(he);
872 cdef = he_base->data;
873 cst = cs_get_type_def(cs, he_base->type);
874 var = &i->var;
875 }
876 else
877 {
878 cdef = he->data;
879 cst = cs_get_type_def(cs, he->type);
880 var = &cdef->var;
881 }
882
883 if (!var || !cdef)
884 return INT_MIN; // LCOV_EXCL_LINE
885
886 if (!cst)
887 {
888 mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
889 return CSR_ERR_CODE;
890 }
891
892 if (!cst->string_plus_equals)
893 {
894 // L10N: e.g. Type 'boolean' doesn't support operation '+='
895 mutt_buffer_printf(err, _("Type '%s' doesn't support operation '%s'"), cst->name, "+=");
896 return CSR_ERR_INVALID | CSV_INV_NOT_IMPL;
897 }
898
899 int rc = cst->string_plus_equals(cs, var, cdef, value, err);
900 if (CSR_RESULT(rc) != CSR_SUCCESS)
901 return rc;
902
903 if (he->type & DT_INHERITED)
904 he->type = cdef->type | DT_INHERITED;
905
906 return rc;
907 }
908
909 /**
910 * cs_str_string_plus_equals - Add to a config item by string
911 * @param cs Config items
912 * @param name Name of config item
913 * @param value Value to set
914 * @param err Buffer for error messages
915 * @retval num Result, e.g. #CSR_SUCCESS
916 */
cs_str_string_plus_equals(const struct ConfigSet * cs,const char * name,const char * value,struct Buffer * err)917 int cs_str_string_plus_equals(const struct ConfigSet *cs, const char *name,
918 const char *value, struct Buffer *err)
919 {
920 if (!cs || !name)
921 return CSR_ERR_CODE;
922
923 struct HashElem *he = cs_get_elem(cs, name);
924 if (!he)
925 {
926 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
927 return CSR_ERR_UNKNOWN;
928 }
929
930 return cs_he_string_plus_equals(cs, he, value, err);
931 }
932
933 /**
934 * cs_he_string_minus_equals - Remove from a config item by string
935 * @param cs Config items
936 * @param he HashElem representing config item
937 * @param value Value to set
938 * @param err Buffer for error messages
939 * @retval num Result, e.g. #CSR_SUCCESS
940 */
cs_he_string_minus_equals(const struct ConfigSet * cs,struct HashElem * he,const char * value,struct Buffer * err)941 int cs_he_string_minus_equals(const struct ConfigSet *cs, struct HashElem *he,
942 const char *value, struct Buffer *err)
943 {
944 if (!cs || !he)
945 return CSR_ERR_CODE;
946
947 struct ConfigDef *cdef = NULL;
948 const struct ConfigSetType *cst = NULL;
949 void *var = NULL;
950
951 if (he->type & DT_INHERITED)
952 {
953 struct Inheritance *i = he->data;
954 struct HashElem *he_base = cs_get_base(he);
955 cdef = he_base->data;
956 cst = cs_get_type_def(cs, he_base->type);
957 var = &i->var;
958 }
959 else
960 {
961 cdef = he->data;
962 cst = cs_get_type_def(cs, he->type);
963 var = &cdef->var;
964 }
965
966 if (!var || !cdef)
967 return INT_MIN; // LCOV_EXCL_LINE
968
969 if (!cst)
970 {
971 mutt_debug(LL_DEBUG1, "Variable '%s' has an invalid type %d\n", cdef->name, he->type);
972 return CSR_ERR_CODE;
973 }
974
975 if (!cst->string_minus_equals)
976 {
977 // L10N: e.g. Type 'boolean' doesn't support operation '+='
978 mutt_buffer_printf(err, _("Type '%s' doesn't support operation '%s'"), cst->name, "-=");
979 return CSR_ERR_INVALID | CSV_INV_NOT_IMPL;
980 }
981
982 int rc = cst->string_minus_equals(cs, var, cdef, value, err);
983 if (CSR_RESULT(rc) != CSR_SUCCESS)
984 return rc;
985
986 if (he->type & DT_INHERITED)
987 he->type = cdef->type | DT_INHERITED;
988
989 return rc;
990 }
991
992 /**
993 * cs_str_string_minus_equals - Remove from a config item by string
994 * @param cs Config items
995 * @param name Name of config item
996 * @param value Value to set
997 * @param err Buffer for error messages
998 * @retval num Result, e.g. #CSR_SUCCESS
999 */
cs_str_string_minus_equals(const struct ConfigSet * cs,const char * name,const char * value,struct Buffer * err)1000 int cs_str_string_minus_equals(const struct ConfigSet *cs, const char *name,
1001 const char *value, struct Buffer *err)
1002 {
1003 if (!cs || !name)
1004 return CSR_ERR_CODE;
1005
1006 struct HashElem *he = cs_get_elem(cs, name);
1007 if (!he)
1008 {
1009 mutt_buffer_printf(err, _("Unknown variable '%s'"), name);
1010 return CSR_ERR_UNKNOWN;
1011 }
1012
1013 return cs_he_string_minus_equals(cs, he, value, err);
1014 }
1015