Line data Source code
1 : /**
2 : * @file
3 : * Type representing a path
4 : *
5 : * @authors
6 : * Copyright (C) 2020 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_path Type: Path
25 : *
26 : * Config type representing a path.
27 : *
28 : * - Backed by `char *`
29 : * - Empty path is stored as `NULL`
30 : * - Validator is passed `char *`, which may be `NULL`
31 : * - Data is freed when `ConfigSet` is freed
32 : */
33 :
34 : #include "config.h"
35 : #include <stddef.h>
36 : #include <limits.h>
37 : #include <stdbool.h>
38 : #include <stdint.h>
39 : #include "mutt/lib.h"
40 : #include "set.h"
41 : #include "types.h"
42 :
43 : extern char *HomeDir;
44 :
45 : /**
46 : * path_tidy - Tidy a path for storage
47 : * @param path Path to be tidied
48 : * @param is_dir Is the path a directory?
49 : * @retval ptr Tidy path
50 : *
51 : * Expand `~` and remove junk like `/./`
52 : *
53 : * @note The caller must free the returned string
54 : */
55 128 : static char *path_tidy(const char *path, bool is_dir)
56 : {
57 128 : if (!path || (*path == '\0'))
58 31 : return NULL;
59 :
60 97 : char buf[PATH_MAX] = { 0 };
61 97 : mutt_str_copy(buf, path, sizeof(buf));
62 :
63 97 : mutt_path_tilde(buf, sizeof(buf), HomeDir);
64 97 : mutt_path_tidy(buf, is_dir);
65 :
66 97 : return mutt_str_dup(buf);
67 : }
68 :
69 : /**
70 : * path_destroy - Destroy a Path - Implements ConfigSetType::destroy()
71 : */
72 172 : static void path_destroy(const struct ConfigSet *cs, void *var, const struct ConfigDef *cdef)
73 : {
74 172 : const char **str = (const char **) var;
75 172 : if (!*str)
76 79 : return;
77 :
78 93 : FREE(var);
79 : }
80 :
81 : /**
82 : * path_string_set - Set a Path by string - Implements ConfigSetType::string_set()
83 : */
84 61 : static int path_string_set(const struct ConfigSet *cs, void *var, struct ConfigDef *cdef,
85 : const char *value, struct Buffer *err)
86 : {
87 : /* Store empty paths as NULL */
88 61 : if (value && (value[0] == '\0'))
89 6 : value = NULL;
90 :
91 61 : if (!value && (cdef->type & DT_NOT_EMPTY))
92 : {
93 2 : mutt_buffer_printf(err, _("Option %s may not be empty"), cdef->name);
94 2 : return CSR_ERR_INVALID | CSR_INV_VALIDATOR;
95 : }
96 :
97 59 : int rc = CSR_SUCCESS;
98 :
99 59 : if (var)
100 : {
101 53 : if (mutt_str_equal(value, (*(char **) var)))
102 10 : return CSR_SUCCESS | CSR_SUC_NO_CHANGE;
103 :
104 43 : if (cdef->validator)
105 : {
106 8 : rc = cdef->validator(cs, cdef, (intptr_t) value, err);
107 :
108 8 : if (CSR_RESULT(rc) != CSR_SUCCESS)
109 2 : return rc | CSR_INV_VALIDATOR;
110 : }
111 :
112 41 : path_destroy(cs, var, cdef);
113 :
114 41 : char *str = path_tidy(value, cdef->type & DT_PATH_DIR);
115 41 : if (!str)
116 6 : rc |= CSR_SUC_EMPTY;
117 :
118 41 : *(char **) var = str;
119 : }
120 : else
121 : {
122 6 : if (cdef->type & DT_INITIAL_SET)
123 4 : FREE(&cdef->initial);
124 :
125 6 : cdef->type |= DT_INITIAL_SET;
126 6 : cdef->initial = IP mutt_str_dup(value);
127 : }
128 :
129 47 : return rc;
130 : }
131 :
132 : /**
133 : * path_string_get - Get a Path as a string - Implements ConfigSetType::string_get()
134 : */
135 72 : static int path_string_get(const struct ConfigSet *cs, void *var,
136 : const struct ConfigDef *cdef, struct Buffer *result)
137 : {
138 72 : const char *str = NULL;
139 :
140 72 : if (var)
141 62 : str = *(const char **) var;
142 : else
143 10 : str = (char *) cdef->initial;
144 :
145 72 : if (!str)
146 14 : return CSR_SUCCESS | CSR_SUC_EMPTY; /* empty path */
147 :
148 58 : mutt_buffer_addstr(result, str);
149 58 : return CSR_SUCCESS;
150 : }
151 :
152 : /**
153 : * path_native_set - Set a Path config item by string - Implements ConfigSetType::native_set()
154 : */
155 28 : static int path_native_set(const struct ConfigSet *cs, void *var,
156 : const struct ConfigDef *cdef, intptr_t value, struct Buffer *err)
157 : {
158 28 : char *str = (char *) value;
159 :
160 : /* Store empty paths as NULL */
161 28 : if (str && (str[0] == '\0'))
162 6 : value = 0;
163 :
164 28 : if ((value == 0) && (cdef->type & DT_NOT_EMPTY))
165 : {
166 2 : mutt_buffer_printf(err, _("Option %s may not be empty"), cdef->name);
167 2 : return CSR_ERR_INVALID | CSR_INV_VALIDATOR;
168 : }
169 :
170 26 : if (mutt_str_equal((const char *) value, (*(char **) var)))
171 8 : return CSR_SUCCESS | CSR_SUC_NO_CHANGE;
172 :
173 : int rc;
174 :
175 18 : if (cdef->validator)
176 : {
177 6 : rc = cdef->validator(cs, cdef, value, err);
178 :
179 6 : if (CSR_RESULT(rc) != CSR_SUCCESS)
180 2 : return rc | CSR_INV_VALIDATOR;
181 : }
182 :
183 16 : path_destroy(cs, var, cdef);
184 :
185 16 : str = path_tidy(str, cdef->type & DT_PATH_DIR);
186 16 : rc = CSR_SUCCESS;
187 16 : if (!str)
188 4 : rc |= CSR_SUC_EMPTY;
189 :
190 16 : *(const char **) var = str;
191 16 : return rc;
192 : }
193 :
194 : /**
195 : * path_native_get - Get a string from a Path config item - Implements ConfigSetType::native_get()
196 : */
197 20 : static intptr_t path_native_get(const struct ConfigSet *cs, void *var,
198 : const struct ConfigDef *cdef, struct Buffer *err)
199 : {
200 20 : const char *str = *(const char **) var;
201 :
202 20 : return (intptr_t) str;
203 : }
204 :
205 : /**
206 : * path_reset - Reset a Path to its initial value - Implements ConfigSetType::reset()
207 : */
208 71 : static int path_reset(const struct ConfigSet *cs, void *var,
209 : const struct ConfigDef *cdef, struct Buffer *err)
210 : {
211 71 : int rc = CSR_SUCCESS;
212 :
213 71 : const char *str = path_tidy((const char *) cdef->initial, cdef->type & DT_PATH_DIR);
214 71 : if (!str)
215 21 : rc |= CSR_SUC_EMPTY;
216 :
217 71 : if (mutt_str_equal(str, (*(char **) var)))
218 : {
219 21 : FREE(&str);
220 21 : return rc | CSR_SUC_NO_CHANGE;
221 : }
222 :
223 50 : if (cdef->validator)
224 : {
225 10 : rc = cdef->validator(cs, cdef, cdef->initial, err);
226 :
227 10 : if (CSR_RESULT(rc) != CSR_SUCCESS)
228 : {
229 2 : FREE(&str);
230 2 : return rc | CSR_INV_VALIDATOR;
231 : }
232 : }
233 :
234 48 : path_destroy(cs, var, cdef);
235 :
236 48 : if (!str)
237 2 : rc |= CSR_SUC_EMPTY;
238 :
239 48 : *(const char **) var = str;
240 48 : return rc;
241 : }
242 :
243 : /**
244 : * cst_path - Config type representing a path
245 : */
246 : const struct ConfigSetType cst_path = {
247 : DT_PATH,
248 : "path",
249 : path_string_set,
250 : path_string_get,
251 : path_native_set,
252 : path_native_get,
253 : NULL, // string_plus_equals
254 : NULL, // string_minus_equals
255 : path_reset,
256 : path_destroy,
257 : };
|