/src/neomutt/config/string.c
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file |
3 | | * Type representing a string |
4 | | * |
5 | | * @authors |
6 | | * Copyright (C) 2017-2025 Richard Russon <rich@flatcap.org> |
7 | | * Copyright (C) 2020 Jakub Jindra <jakub.jindra@socialbakers.com> |
8 | | * Copyright (C) 2020 Pietro Cerutti <gahr@gahr.ch> |
9 | | * |
10 | | * @copyright |
11 | | * This program is free software: you can redistribute it and/or modify it under |
12 | | * the terms of the GNU General Public License as published by the Free Software |
13 | | * Foundation, either version 2 of the License, or (at your option) any later |
14 | | * version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
19 | | * details. |
20 | | * |
21 | | * You should have received a copy of the GNU General Public License along with |
22 | | * this program. If not, see <http://www.gnu.org/licenses/>. |
23 | | */ |
24 | | |
25 | | /** |
26 | | * @page config_string Type: String |
27 | | * |
28 | | * Config type representing a string. |
29 | | * |
30 | | * - Backed by `char *` |
31 | | * - Empty string is stored as `NULL` |
32 | | * - Validator is passed `char *`, which may be `NULL` |
33 | | * - Data is freed when `ConfigSet` is freed |
34 | | * - Implementation: #CstString |
35 | | */ |
36 | | |
37 | | #include "config.h" |
38 | | #include <stdbool.h> |
39 | | #include <stddef.h> |
40 | | #include <stdint.h> |
41 | | #include "mutt/lib.h" |
42 | | #include "set.h" |
43 | | #include "types.h" |
44 | | |
45 | | /** |
46 | | * string_destroy - Destroy a String - Implements ConfigSetType::destroy() - @ingroup cfg_type_destroy |
47 | | */ |
48 | | static void string_destroy(void *var, const struct ConfigDef *cdef) |
49 | 1.00M | { |
50 | 1.00M | const char **str = (const char **) var; |
51 | 1.00M | if (!*str) |
52 | 721k | return; |
53 | | |
54 | 279k | FREE(var); |
55 | 279k | } |
56 | | |
57 | | /** |
58 | | * string_string_set - Set a String by string - Implements ConfigSetType::string_set() - @ingroup cfg_type_string_set |
59 | | */ |
60 | | static int string_string_set(void *var, struct ConfigDef *cdef, |
61 | | const char *value, struct Buffer *err) |
62 | 0 | { |
63 | | /* Store empty strings as NULL */ |
64 | 0 | if (value && (value[0] == '\0')) |
65 | 0 | value = NULL; |
66 | |
|
67 | 0 | if (!value && (cdef->type & D_NOT_EMPTY)) |
68 | 0 | { |
69 | 0 | buf_printf(err, _("Option %s may not be empty"), cdef->name); |
70 | 0 | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
71 | 0 | } |
72 | | |
73 | 0 | int rc = CSR_SUCCESS; |
74 | |
|
75 | 0 | if (var) |
76 | 0 | { |
77 | 0 | if (mutt_str_equal(value, (*(char **) var))) |
78 | 0 | return CSR_SUCCESS | CSR_SUC_NO_CHANGE; |
79 | | |
80 | 0 | if (startup_only(cdef, err)) |
81 | 0 | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
82 | | |
83 | 0 | if (cdef->validator) |
84 | 0 | { |
85 | 0 | rc = cdef->validator(cdef, (intptr_t) value, err); |
86 | |
|
87 | 0 | if (CSR_RESULT(rc) != CSR_SUCCESS) |
88 | 0 | return rc | CSR_INV_VALIDATOR; |
89 | 0 | } |
90 | | |
91 | 0 | string_destroy(var, cdef); |
92 | |
|
93 | 0 | const char *str = mutt_str_dup(value); |
94 | 0 | if (!str) |
95 | 0 | rc |= CSR_SUC_EMPTY; |
96 | |
|
97 | 0 | *(const char **) var = str; |
98 | 0 | } |
99 | 0 | else |
100 | 0 | { |
101 | 0 | if (cdef->type & D_INTERNAL_INITIAL_SET) |
102 | 0 | FREE(&cdef->initial); |
103 | |
|
104 | 0 | cdef->type |= D_INTERNAL_INITIAL_SET; |
105 | 0 | cdef->initial = (intptr_t) mutt_str_dup(value); |
106 | 0 | } |
107 | | |
108 | 0 | return rc; |
109 | 0 | } |
110 | | |
111 | | /** |
112 | | * string_string_get - Get a String as a string - Implements ConfigSetType::string_get() - @ingroup cfg_type_string_get |
113 | | */ |
114 | | static int string_string_get(void *var, const struct ConfigDef *cdef, struct Buffer *result) |
115 | 0 | { |
116 | 0 | const char *str = NULL; |
117 | |
|
118 | 0 | if (var) |
119 | 0 | str = *(const char **) var; |
120 | 0 | else |
121 | 0 | str = (char *) cdef->initial; |
122 | |
|
123 | 0 | if (!str) |
124 | 0 | return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty string */ |
125 | | |
126 | 0 | buf_addstr(result, str); |
127 | 0 | return CSR_SUCCESS; |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * string_native_set - Set a String config item by string - Implements ConfigSetType::native_set() - @ingroup cfg_type_native_set |
132 | | */ |
133 | | static int string_native_set(void *var, const struct ConfigDef *cdef, |
134 | | intptr_t value, struct Buffer *err) |
135 | 0 | { |
136 | 0 | const char *str = (const char *) value; |
137 | | |
138 | | /* Store empty strings as NULL */ |
139 | 0 | if (str && (str[0] == '\0')) |
140 | 0 | value = 0; |
141 | |
|
142 | 0 | if ((value == 0) && (cdef->type & D_NOT_EMPTY)) |
143 | 0 | { |
144 | 0 | buf_printf(err, _("Option %s may not be empty"), cdef->name); |
145 | 0 | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
146 | 0 | } |
147 | | |
148 | 0 | if (mutt_str_equal((const char *) value, (*(char **) var))) |
149 | 0 | return CSR_SUCCESS | CSR_SUC_NO_CHANGE; |
150 | | |
151 | 0 | int rc; |
152 | |
|
153 | 0 | if (startup_only(cdef, err)) |
154 | 0 | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
155 | | |
156 | 0 | if (cdef->validator) |
157 | 0 | { |
158 | 0 | rc = cdef->validator(cdef, value, err); |
159 | |
|
160 | 0 | if (CSR_RESULT(rc) != CSR_SUCCESS) |
161 | 0 | return rc | CSR_INV_VALIDATOR; |
162 | 0 | } |
163 | | |
164 | 0 | string_destroy(var, cdef); |
165 | |
|
166 | 0 | str = mutt_str_dup(str); |
167 | 0 | rc = CSR_SUCCESS; |
168 | 0 | if (!str) |
169 | 0 | rc |= CSR_SUC_EMPTY; |
170 | |
|
171 | 0 | *(const char **) var = str; |
172 | 0 | return rc; |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * string_native_get - Get a string from a String config item - Implements ConfigSetType::native_get() - @ingroup cfg_type_native_get |
177 | | */ |
178 | | static intptr_t string_native_get(void *var, const struct ConfigDef *cdef, struct Buffer *err) |
179 | 2 | { |
180 | 2 | const char *str = *(const char **) var; |
181 | | |
182 | 2 | return (intptr_t) str; |
183 | 2 | } |
184 | | |
185 | | /** |
186 | | * string_string_plus_equals - Add to a String by string - Implements ConfigSetType::string_plus_equals() - @ingroup cfg_type_string_plus_equals |
187 | | */ |
188 | | static int string_string_plus_equals(void *var, const struct ConfigDef *cdef, |
189 | | const char *value, struct Buffer *err) |
190 | 0 | { |
191 | | /* Skip if the value is missing or empty string*/ |
192 | 0 | if (!value || (value[0] == '\0')) |
193 | 0 | return CSR_SUCCESS | CSR_SUC_NO_CHANGE; |
194 | | |
195 | 0 | if (value && startup_only(cdef, err)) |
196 | 0 | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
197 | | |
198 | 0 | int rc = CSR_SUCCESS; |
199 | |
|
200 | 0 | char *str = NULL; |
201 | 0 | const char **var_str = (const char **) var; |
202 | |
|
203 | 0 | if (*var_str) |
204 | 0 | mutt_str_asprintf(&str, "%s%s", *var_str, value); |
205 | 0 | else |
206 | 0 | str = mutt_str_dup(value); |
207 | |
|
208 | 0 | if (cdef->validator) |
209 | 0 | { |
210 | 0 | rc = cdef->validator(cdef, (intptr_t) str, err); |
211 | |
|
212 | 0 | if (CSR_RESULT(rc) != CSR_SUCCESS) |
213 | 0 | { |
214 | 0 | FREE(&str); |
215 | 0 | return rc | CSR_INV_VALIDATOR; |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | 0 | string_destroy(var, cdef); |
220 | 0 | *var_str = str; |
221 | |
|
222 | 0 | return rc; |
223 | 0 | } |
224 | | |
225 | | /** |
226 | | * string_has_been_set - Is the config value different to its initial value? - Implements ConfigSetType::has_been_set() - @ingroup cfg_type_has_been_set |
227 | | */ |
228 | | static bool string_has_been_set(void *var, const struct ConfigDef *cdef) |
229 | 0 | { |
230 | 0 | const char *initial = (const char *) cdef->initial; |
231 | 0 | const char *value = *(const char **) var; |
232 | |
|
233 | 0 | return !mutt_str_equal(initial, value); |
234 | 0 | } |
235 | | |
236 | | /** |
237 | | * string_reset - Reset a String to its initial value - Implements ConfigSetType::reset() - @ingroup cfg_type_reset |
238 | | */ |
239 | | static int string_reset(void *var, const struct ConfigDef *cdef, struct Buffer *err) |
240 | 721k | { |
241 | 721k | int rc = CSR_SUCCESS; |
242 | | |
243 | 721k | const char *str = mutt_str_dup((const char *) cdef->initial); |
244 | 721k | if (!str) |
245 | 423k | rc |= CSR_SUC_EMPTY; |
246 | | |
247 | 721k | if (mutt_str_equal(str, (*(char **) var))) |
248 | 423k | { |
249 | 423k | FREE(&str); |
250 | 423k | return rc | CSR_SUC_NO_CHANGE; |
251 | 423k | } |
252 | | |
253 | 298k | if (startup_only(cdef, err)) |
254 | 19.2k | { |
255 | 19.2k | FREE(&str); |
256 | 19.2k | return CSR_ERR_INVALID | CSR_INV_VALIDATOR; |
257 | 19.2k | } |
258 | | |
259 | 279k | if (cdef->validator) |
260 | 0 | { |
261 | 0 | rc = cdef->validator(cdef, cdef->initial, err); |
262 | |
|
263 | 0 | if (CSR_RESULT(rc) != CSR_SUCCESS) |
264 | 0 | { |
265 | 0 | FREE(&str); |
266 | 0 | return rc | CSR_INV_VALIDATOR; |
267 | 0 | } |
268 | 0 | } |
269 | | |
270 | 279k | string_destroy(var, cdef); |
271 | | |
272 | 279k | if (!str) |
273 | 0 | rc |= CSR_SUC_EMPTY; |
274 | | |
275 | 279k | *(const char **) var = str; |
276 | 279k | return rc; |
277 | 279k | } |
278 | | |
279 | | /** |
280 | | * CstString - Config type representing a string |
281 | | */ |
282 | | const struct ConfigSetType CstString = { |
283 | | DT_STRING, |
284 | | "string", |
285 | | string_string_set, |
286 | | string_string_get, |
287 | | string_native_set, |
288 | | string_native_get, |
289 | | string_string_plus_equals, |
290 | | NULL, // string_minus_equals |
291 | | string_has_been_set, |
292 | | string_reset, |
293 | | string_destroy, |
294 | | }; |