1 /* cfg_format.c -- convert configuration parse tree to human-readable format.
2 Copyright (C) 2007-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; either version 3, or (at
7 your option) any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <mailutils/alloc.h>
23 #include <mailutils/stream.h>
24 #include <mailutils/error.h>
25 #include <mailutils/cfg.h>
26 #include <mailutils/wordsplit.h>
27 #include <mailutils/nls.h>
28 #include <mailutils/iterator.h>
29 #include <ctype.h>
30
31 struct tree_print
32 {
33 int flags;
34 unsigned level;
35 mu_stream_t stream;
36 char *buf;
37 size_t bufsize;
38 };
39
40 static void
format_level(mu_stream_t stream,int level)41 format_level (mu_stream_t stream, int level)
42 {
43 while (level--)
44 mu_stream_write (stream, " ", 2, NULL);
45 }
46
47 static void
format_string_value(struct tree_print * tp,const char * str)48 format_string_value (struct tree_print *tp, const char *str)
49 {
50 size_t size;
51 int quote;
52 char *p;
53
54 size = mu_wordsplit_c_quoted_length (str, 1, "e);
55 if (quote)
56 size += 2;
57 size++;
58 if (size > tp->bufsize)
59 {
60 p = mu_realloc (tp->buf, size);
61 tp->bufsize = size;
62 tp->buf = p;
63 }
64
65 p = tp->buf;
66 if (quote)
67 {
68 tp->buf[0] = '"';
69 tp->buf[size-2] = '"';
70 p++;
71 }
72 tp->buf[size-1] = 0;
73 mu_wordsplit_c_quote_copy (p, str, 1);
74 mu_stream_write (tp->stream, tp->buf, size - 1, NULL);
75 }
76
77 static void format_value (struct tree_print *tp, mu_config_value_t *val);
78
79 static void
format_list_value(struct tree_print * tp,mu_config_value_t * val)80 format_list_value (struct tree_print *tp, mu_config_value_t *val)
81 {
82 int i;
83 mu_iterator_t itr;
84 mu_stream_write (tp->stream, "(", 1, NULL);
85 mu_list_get_iterator (val->v.list, &itr);
86
87 for (mu_iterator_first (itr), i = 0;
88 !mu_iterator_is_done (itr); mu_iterator_next (itr), i++)
89 {
90 mu_config_value_t *p;
91 mu_iterator_current (itr, (void**)&p);
92 if (i)
93 mu_stream_write (tp->stream, ", ", 2, NULL);
94 format_value (tp, p);
95 }
96 mu_iterator_destroy (&itr);
97 mu_stream_write (tp->stream, ")", 1, NULL);
98 }
99
100 static void
format_array_value(struct tree_print * tp,mu_config_value_t * val)101 format_array_value (struct tree_print *tp, mu_config_value_t *val)
102 {
103 int i;
104
105 for (i = 0; i < val->v.arg.c; i++)
106 {
107 if (i)
108 mu_stream_write (tp->stream, " ", 1, NULL);
109 format_value (tp, &val->v.arg.v[i]);
110 }
111 }
112
113 static void
format_value(struct tree_print * tp,mu_config_value_t * val)114 format_value (struct tree_print *tp, mu_config_value_t *val)
115 {
116 switch (val->type)
117 {
118 case MU_CFG_STRING:
119 format_string_value (tp, val->v.string);
120 break;
121
122 case MU_CFG_LIST:
123 format_list_value (tp, val);
124 break;
125
126 case MU_CFG_ARRAY:
127 format_array_value (tp, val);
128 }
129 }
130
131 static void
format_path(struct tree_print * tp,const mu_cfg_node_t * node,int delim)132 format_path (struct tree_print *tp, const mu_cfg_node_t *node, int delim)
133 {
134 char c;
135
136 if (node->parent)
137 format_path (tp, node->parent, MU_CFG_PATH_DELIM);
138
139 mu_stream_write (tp->stream, node->tag, strlen (node->tag), NULL);
140 if (node->type == mu_cfg_node_statement && node->label)
141 {
142 mu_stream_write (tp->stream, "=\"", 2, NULL);
143 format_value (tp, node->label);
144 mu_stream_write (tp->stream, "\"", 1, NULL);
145 }
146 c = delim;
147 mu_stream_write (tp->stream, &c, 1, NULL);
148 }
149
150 static int
format_node(const mu_cfg_node_t * node,void * data)151 format_node (const mu_cfg_node_t *node, void *data)
152 {
153 struct tree_print *tp = data;
154
155 if ((tp->flags & MU_CF_FMT_LOCUS) && node->locus.beg.mu_file)
156 mu_stream_printf (tp->stream, "# %lu \"%s\"\n",
157 (unsigned long) node->locus.beg.mu_line,
158 node->locus.beg.mu_file);
159 format_level (tp->stream, tp->level);
160 switch (node->type)
161 {
162 case mu_cfg_node_undefined:
163 mu_stream_printf (tp->stream, "%s",
164 _("ERROR: undefined statement"));
165 break;
166
167 case mu_cfg_node_statement:
168 if (tp->flags & MU_CF_FMT_PARAM_PATH)
169 return MU_CFG_ITER_OK;
170 else
171 {
172 mu_stream_write (tp->stream, node->tag, strlen (node->tag), NULL);
173 if (node->label)
174 {
175 mu_stream_write (tp->stream, " ", 1, NULL);
176 format_value (tp, node->label);
177 }
178 mu_stream_write (tp->stream, " {", 2, NULL);
179 tp->level++;
180 }
181 break;
182
183 case mu_cfg_node_param:
184 if (tp->flags & MU_CF_FMT_VALUE_ONLY)
185 format_value (tp, node->label);
186 else if (tp->flags & MU_CF_FMT_PARAM_PATH)
187 {
188 format_path (tp, node, ':');
189 mu_stream_write (tp->stream, " ", 1, NULL);
190 format_value (tp, node->label);
191 }
192 else
193 {
194 mu_stream_write (tp->stream, node->tag, strlen (node->tag), NULL);
195 if (node->label)
196 {
197 mu_stream_write (tp->stream, " ", 1, NULL);
198 format_value (tp, node->label);
199 mu_stream_write (tp->stream, ";", 1, NULL);
200 }
201 }
202 break;
203 }
204 mu_stream_write (tp->stream, "\n", 1, NULL);
205 return MU_CFG_ITER_OK;
206 }
207
208 static int
format_node_end(const mu_cfg_node_t * node,void * data)209 format_node_end (const mu_cfg_node_t *node, void *data)
210 {
211 struct tree_print *tp = data;
212 if (!(tp->flags & MU_CF_FMT_PARAM_PATH))
213 {
214 tp->level--;
215 format_level (tp->stream, tp->level);
216 mu_stream_write (tp->stream, "};\n", 3, NULL);
217 }
218 return MU_CFG_ITER_OK;
219 }
220
221 void
mu_cfg_format_parse_tree(mu_stream_t stream,mu_cfg_tree_t * tree,int flags)222 mu_cfg_format_parse_tree (mu_stream_t stream, mu_cfg_tree_t *tree, int flags)
223 {
224 struct mu_cfg_iter_closure clos;
225 struct tree_print t;
226
227 t.flags = flags;
228 t.level = 0;
229 t.stream = stream;
230 t.buf = NULL;
231 t.bufsize = 0;
232 clos.beg = format_node;
233 clos.end = format_node_end;
234 clos.data = &t;
235 mu_cfg_preorder (tree->nodes, &clos);
236 free (t.buf);
237 }
238
239 void
mu_cfg_format_node(mu_stream_t stream,const mu_cfg_node_t * node,int flags)240 mu_cfg_format_node (mu_stream_t stream, const mu_cfg_node_t *node, int flags)
241 {
242 struct tree_print t;
243
244 if (node->type == mu_cfg_node_statement)
245 flags &= ~MU_CF_FMT_VALUE_ONLY;
246 t.flags = flags;
247 t.level = 0;
248 t.stream = stream;
249 t.buf = NULL;
250 t.bufsize = 0;
251 format_node (node, &t);
252 if (node->type == mu_cfg_node_statement)
253 {
254 struct mu_cfg_iter_closure clos;
255 clos.beg = format_node;
256 clos.end = format_node_end;
257 clos.data = &t;
258 mu_cfg_preorder (node->nodes, &clos);
259 format_node_end (node, &t);
260 }
261 }
262
263
264
265 const char *
mu_c_type_string(int type)266 mu_c_type_string (int type)
267 {
268 switch (type)
269 {
270 case mu_c_string:
271 return N_("string");
272 case mu_c_short:
273 case mu_c_ushort:
274 case mu_c_int:
275 case mu_c_uint:
276 case mu_c_long:
277 case mu_c_ulong:
278 case mu_c_size:
279 case mu_c_off:
280 case mu_c_incr:
281 return N_("number");
282 case mu_c_time:
283 return N_("time");
284 case mu_c_bool:
285 return N_("boolean");
286 case mu_c_ipv4:
287 return N_("ipv4");
288 case mu_c_cidr:
289 return N_("cidr");
290 case mu_c_host:
291 return N_("host");
292 case mu_cfg_section:
293 return N_("section");
294 case mu_cfg_callback:
295 return N_("callback");
296 default:
297 break;
298 }
299 return N_("unknown");
300 }
301
302 void
mu_cfg_format_docstring(mu_stream_t stream,const char * docstring,int level)303 mu_cfg_format_docstring (mu_stream_t stream, const char *docstring, int level)
304 {
305 size_t len = strlen (docstring);
306 int width = 78 - level * 2;
307
308 if (width < 0)
309 {
310 width = 78;
311 level = 0;
312 }
313
314 while (len)
315 {
316 size_t seglen;
317 const char *p;
318
319 for (seglen = 0, p = docstring; p < docstring + width && *p; p++)
320 {
321 if (*p == '\n')
322 {
323 seglen = p - docstring;
324 break;
325 }
326 if (isspace (*p))
327 seglen = p - docstring;
328 }
329 if (seglen == 0 || *p == 0)
330 seglen = p - docstring;
331
332 format_level (stream, level);
333 mu_stream_write (stream, "# ", 2, NULL);
334 mu_stream_write (stream, docstring, seglen, NULL);
335 mu_stream_write (stream, "\n", 1, NULL);
336 len -= seglen;
337 docstring += seglen;
338 if (*docstring == '\n')
339 {
340 docstring++;
341 len--;
342 }
343 else
344 while (*docstring && isspace (*docstring))
345 {
346 docstring++;
347 len--;
348 }
349 }
350 }
351
352 static void
format_param(mu_stream_t stream,struct mu_cfg_param * param,int level)353 format_param (mu_stream_t stream, struct mu_cfg_param *param, int level)
354 {
355 if (param->docstring)
356 mu_cfg_format_docstring (stream, gettext (param->docstring), level);
357 format_level (stream, level);
358 if (param->argname && strchr (param->argname, ':'))
359 mu_stream_printf (stream, "%s <%s>;\n",
360 param->ident,
361 gettext (param->argname));
362 else if (MU_CFG_IS_LIST (param->type))
363 mu_stream_printf (stream, "%s <%s: list of %s>;\n",
364 param->ident,
365 gettext (param->argname ?
366 param->argname : N_("arg")),
367 gettext (mu_c_type_string (MU_CFG_TYPE (param->type))));
368 else
369 mu_stream_printf (stream, "%s <%s: %s>;\n",
370 param->ident,
371 gettext (param->argname ?
372 param->argname : N_("arg")),
373 gettext (mu_c_type_string (param->type)));
374 }
375
376 static void format_container (mu_stream_t stream, struct mu_cfg_cont *cont,
377 int level);
378
379 static int
_f_helper(void * item,void * data)380 _f_helper (void *item, void *data)
381 {
382 struct tree_print *tp = data;
383 struct mu_cfg_cont *cont = item;
384 format_container (tp->stream, cont, tp->level);
385 return 0;
386 }
387
388 static void
format_section(mu_stream_t stream,struct mu_cfg_section * sect,int level)389 format_section (mu_stream_t stream, struct mu_cfg_section *sect, int level)
390 {
391 struct tree_print c;
392 if (sect->docstring)
393 mu_cfg_format_docstring (stream, gettext (sect->docstring), level);
394 format_level (stream, level);
395 if (sect->ident)
396 {
397 mu_stream_write (stream, sect->ident, strlen (sect->ident), NULL);
398 if (sect->label)
399 {
400 mu_stream_write (stream, " ", 1, NULL);
401 mu_stream_write (stream, sect->label, strlen (sect->label), NULL);
402 }
403 mu_stream_write (stream, " {\n", 3, NULL);
404 c.stream = stream;
405 c.level = level + 1;
406 mu_list_foreach (sect->children, _f_helper, &c);
407 format_level (stream, level);
408 mu_stream_write (stream, "};\n\n", 4, NULL);
409 }
410 else
411 {
412 c.stream = stream;
413 c.level = level;
414 mu_list_foreach (sect->children, _f_helper, &c);
415 }
416 }
417
418 static void
format_container(mu_stream_t stream,struct mu_cfg_cont * cont,int level)419 format_container (mu_stream_t stream, struct mu_cfg_cont *cont, int level)
420 {
421 switch (cont->type)
422 {
423 case mu_cfg_cont_section:
424 format_section (stream, &cont->v.section, level);
425 break;
426
427 case mu_cfg_cont_param:
428 format_param (stream, &cont->v.param, level);
429 break;
430 }
431 }
432
433 void
mu_cfg_format_container(mu_stream_t stream,struct mu_cfg_cont * cont)434 mu_cfg_format_container (mu_stream_t stream, struct mu_cfg_cont *cont)
435 {
436 format_container (stream, cont, 0);
437 }
438