1 /* grecs - Gray's Extensible Configuration System
2 Copyright (C) 2007-2016 Sergey Poznyakoff
3
4 Grecs is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by the
6 Free Software Foundation; either version 3 of the License, or (at your
7 option) any later version.
8
9 Grecs is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <grecs.h>
21 #include <wordsplit.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <errno.h>
27
28 const char *
grecs_data_type_string(enum grecs_data_type type)29 grecs_data_type_string(enum grecs_data_type type)
30 {
31 switch (type) {
32 case grecs_type_void:
33 return N_("void");
34
35 case grecs_type_string:
36 return N_("string");
37
38 case grecs_type_short:
39 case grecs_type_ushort:
40 case grecs_type_int:
41 case grecs_type_uint:
42 case grecs_type_long:
43 case grecs_type_ulong:
44 case grecs_type_size:
45 /*FIXME case grecs_type_off:*/
46 return N_("number");
47
48 case grecs_type_time:
49 return N_("time");
50
51 case grecs_type_bool:
52 return N_("boolean");
53
54 case grecs_type_ipv4:
55 return N_("IPv4");
56
57 case grecs_type_cidr:
58 return N_("CIDR");
59
60 case grecs_type_host:
61 return N_("hostname");
62
63 case grecs_type_sockaddr:
64 return N_("sockaddr");
65
66 case grecs_type_section:
67 return N_("section");
68
69 case grecs_type_null:
70 return N_("null");
71 }
72 return "UNKNOWN?";
73 }
74
75 static void
format_level(unsigned level,FILE * stream)76 format_level(unsigned level, FILE *stream)
77 {
78 while (level--)
79 fprintf(stream, " ");
80 }
81
82 void
grecs_print_docstring(const char * docstring,unsigned level,FILE * stream)83 grecs_print_docstring(const char *docstring, unsigned level, FILE *stream)
84 {
85 size_t len = strlen(docstring);
86 int width = 78 - level * 2;
87
88 if (width < 0) {
89 width = 78;
90 level = 0;
91 }
92
93 while (len) {
94 size_t seglen;
95 const char *p;
96
97 for (seglen = 0, p = docstring; p < docstring + width && *p;
98 p++) {
99 if (*p == '\n') {
100 seglen = p - docstring;
101 break;
102 }
103 if (isspace(*p))
104 seglen = p - docstring;
105 }
106 if (seglen == 0 || *p == 0)
107 seglen = p - docstring;
108
109 format_level(level, stream);
110 fprintf(stream, "# ");
111 fwrite(docstring, seglen, 1, stream);
112 fputc('\n', stream);
113 len -= seglen;
114 docstring += seglen;
115 if (*docstring == '\n') {
116 docstring++;
117 len--;
118 } else
119 while (*docstring && isspace(*docstring)) {
120 docstring++;
121 len--;
122 }
123 }
124 }
125
126 void
grecs_print_simple_statement(struct grecs_keyword * kwp,unsigned level,FILE * stream)127 grecs_print_simple_statement(struct grecs_keyword *kwp, unsigned level,
128 FILE *stream)
129 {
130 const char *argstr;
131
132 if (kwp->flags & GRECS_INAC)
133 grecs_print_docstring(N_("Disabled;"), level, stream);
134 if (kwp->docstring)
135 grecs_print_docstring(kwp->docstring, level, stream);
136 format_level(level, stream);
137
138 if (kwp->argname)
139 argstr = kwp->argname;
140 else
141 argstr = N_("arg");
142
143 if (strchr("<[", argstr[0]))
144 fprintf(stream, "%s %s;\n", kwp->ident, gettext(argstr));
145 else if (argstr[0] == '\'')
146 fprintf(stream, "%s %s;\n", kwp->ident, argstr + 1);
147 else if (strchr (argstr, ':'))
148 fprintf(stream, "%s <%s>;\n", kwp->ident, gettext(argstr));
149 else {
150 fprintf(stream, "%s <%s: ", kwp->ident, gettext(argstr));
151 if (kwp->flags & GRECS_LIST)
152 fprintf(stream, "list of %s",
153 gettext(grecs_data_type_string(kwp->type)));
154 else
155 fprintf(stream, "%s",
156 gettext(grecs_data_type_string(kwp->type)));
157 fprintf(stream, ">;\n");
158 }
159 }
160
161 void
grecs_print_block_statement(struct grecs_keyword * kwp,unsigned level,FILE * stream)162 grecs_print_block_statement(struct grecs_keyword *kwp, unsigned level,
163 FILE *stream)
164 {
165 if (kwp->docstring)
166 grecs_print_docstring(kwp->docstring, level, stream);
167 format_level(level, stream);
168 fprintf(stream, "%s", kwp->ident);
169 if (kwp->argname) {
170 if (kwp->argname[0] == '\'')
171 fprintf(stream, " %s", kwp->argname + 1);
172 else
173 fprintf(stream, " <%s>", gettext(kwp->argname));
174 }
175 fprintf(stream, " {\n");
176 grecs_print_statement_array(kwp->kwd, 0, level + 1, stream);
177 format_level(level, stream);
178 fprintf(stream, "}\n");
179 }
180
181 void
grecs_print_statement_array(struct grecs_keyword * kwp,unsigned n,unsigned level,FILE * stream)182 grecs_print_statement_array(struct grecs_keyword *kwp,
183 unsigned n,
184 unsigned level,
185 FILE *stream)
186 {
187 if (!kwp) {
188 return;
189 }
190 for (; kwp->ident; kwp++, n++) {
191 if (kwp->flags & GRECS_HIDDEN)
192 continue;
193 if (n)
194 fputc('\n', stream);
195 if (kwp->type == grecs_type_section)
196 grecs_print_block_statement(kwp, level, stream);
197 else
198 grecs_print_simple_statement(kwp, level, stream);
199 }
200 }
201
202
203 void
grecs_format_locus(grecs_locus_t * locus,struct grecs_format_closure * clos)204 grecs_format_locus(grecs_locus_t *locus, struct grecs_format_closure *clos)
205 {
206 if (locus) {
207 char *str = NULL;
208 size_t size = 0;
209
210 if (locus->beg.col == 0)
211 grecs_asprintf(&str, &size, "%s:%u",
212 locus->beg.file,
213 locus->beg.line);
214 else if (strcmp(locus->beg.file, locus->end.file))
215 grecs_asprintf(&str, &size, "%s:%u.%u-%s:%u.%u",
216 locus->beg.file,
217 locus->beg.line, locus->beg.col,
218 locus->end.file,
219 locus->end.line, locus->end.col);
220 else if (locus->beg.line != locus->end.line)
221 grecs_asprintf(&str, &size, "%s:%u.%u-%u.%u",
222 locus->beg.file,
223 locus->beg.line, locus->beg.col,
224 locus->end.line, locus->end.col);
225 else if (locus->beg.col != locus->end.col)
226 grecs_asprintf(&str, &size, "%s:%u.%u-%u",
227 locus->beg.file,
228 locus->beg.line, locus->beg.col,
229 locus->end.col);
230 else
231 grecs_asprintf(&str, &size, "%s:%u.%u",
232 locus->beg.file,
233 locus->beg.line,
234 locus->beg.col);
235
236 clos->fmtfun(str, clos->data);
237 free(str);
238 }
239 }
240
241 void
grecs_format_node_path(struct grecs_node * node,int flags,struct grecs_format_closure * clos)242 grecs_format_node_path(struct grecs_node *node, int flags,
243 struct grecs_format_closure *clos)
244 {
245 char delim[2] = ".";
246
247 if (!node) {
248 clos->fmtfun("NULL", clos->data);
249 return;
250 }
251
252 if (node->up)
253 grecs_format_node_path(node->up, flags, clos);
254 if (node->type == grecs_node_root)
255 return;
256 if (flags & _GRECS_NODE_MASK_DELIM)
257 delim[0] = flags & _GRECS_NODE_MASK_DELIM;
258 clos->fmtfun(delim, clos->data);
259 clos->fmtfun(node->ident, clos->data);
260 if (node->type == grecs_node_block &&
261 !GRECS_VALUE_EMPTY_P(node->v.value)) {
262 clos->fmtfun("=", clos->data);
263 grecs_format_value(node->v.value, flags|GRECS_NODE_FLAG_QUOTE,
264 clos);
265 }
266 }
267
268 void
grecs_format_value(struct grecs_value * val,int flags,struct grecs_format_closure * clos)269 grecs_format_value(struct grecs_value *val, int flags,
270 struct grecs_format_closure *clos)
271 {
272 int i;
273 struct grecs_list_entry *ep;
274 size_t clen;
275 int need_quote;
276
277 if (!val)
278 return;
279 switch (val->type) {
280 case GRECS_TYPE_STRING:
281 clen = wordsplit_c_quoted_length(val->v.string,
282 flags & GRECS_NODE_FLAG_QUOTE_HEX,
283 &need_quote);
284 if (flags & GRECS_NODE_FLAG_QUOTE)
285 need_quote = 1;
286 else if (flags & GRECS_NODE_FLAG_NOQUOTE)
287 need_quote = 0;
288 if (need_quote) {
289 char *cbuf = grecs_malloc(clen + 1);
290 wordsplit_c_quote_copy(cbuf, val->v.string,
291 flags & GRECS_NODE_FLAG_QUOTE_HEX);
292 cbuf[clen] = 0;
293 clos->fmtfun("\"", clos->data);
294 clos->fmtfun(cbuf, clos->data);
295 clos->fmtfun("\"", clos->data);
296 grecs_free(cbuf);
297 } else
298 clos->fmtfun(val->v.string, clos->data);
299 break;
300
301 case GRECS_TYPE_LIST:
302 clos->fmtfun("(", clos->data);
303 for (ep = val->v.list->head; ep; ep = ep->next) {
304 grecs_format_value(ep->data, flags, clos);
305 if (ep->next)
306 clos->fmtfun(", ", clos->data);
307 }
308 clos->fmtfun(")", clos->data);
309 break;
310
311 case GRECS_TYPE_ARRAY:
312 for (i = 0; i < val->v.arg.c; i++) {
313 if (i)
314 clos->fmtfun(" ", clos->data);
315 grecs_format_value(val->v.arg.v[i], flags, clos);
316 }
317 }
318 }
319
320 int
grecs_format_node(struct grecs_node * node,int flags,struct grecs_format_closure * clos)321 grecs_format_node(struct grecs_node *node, int flags,
322 struct grecs_format_closure *clos)
323 {
324 const char *delim_str = NULL;
325
326 if (!(flags & _GRECS_NODE_MASK_OUTPUT)) {
327 errno = EINVAL;
328 return 1;
329 }
330
331 if (!node) {
332 clos->fmtfun("NULL", clos->data);
333 return 0;
334 }
335
336 switch (node->type) {
337 case grecs_node_root:
338 case grecs_node_block:
339 if (flags & GRECS_NODE_FLAG_DESCEND) {
340 for (node = node->down; node; node = node->next) {
341 grecs_format_node(node, flags, clos);
342 if (node->next)
343 clos->fmtfun("\n", clos->data);
344 }
345 break;
346 }
347
348 case grecs_node_stmt:
349 if (flags & GRECS_NODE_FLAG_LOCUS) {
350 grecs_locus_t *locus;
351
352 if (flags & GRECS_NODE_FLAG_PATH) {
353 if (flags & GRECS_NODE_FLAG_VALUE)
354 locus = &node->locus;
355 else
356 locus = &node->idloc;
357 } else if (flags & GRECS_NODE_FLAG_VALUE)
358 locus = &node->v.value->locus;
359 else
360 locus = &node->locus;
361 grecs_format_locus(locus, clos);
362 delim_str = ": ";
363 }
364 if (flags & GRECS_NODE_FLAG_PATH) {
365 if (delim_str)
366 clos->fmtfun(delim_str, clos->data);
367 grecs_format_node_path(node, flags, clos);
368 delim_str = ": ";
369 }
370 if (flags & GRECS_NODE_FLAG_VALUE) {
371 if (delim_str)
372 clos->fmtfun(delim_str, clos->data);
373 grecs_format_value(node->v.value, flags, clos);
374 }
375 }
376 return 0;
377 }
378
379
380 static int
txtacc_fmt(const char * str,void * data)381 txtacc_fmt(const char *str, void *data)
382 {
383 struct grecs_txtacc *acc = data;
384 grecs_txtacc_grow(acc, str, strlen(str));
385 return 0;
386 }
387
388 void
grecs_txtacc_format_value(struct grecs_value * val,int flags,struct grecs_txtacc * acc)389 grecs_txtacc_format_value(struct grecs_value *val, int flags,
390 struct grecs_txtacc *acc)
391 {
392 struct grecs_format_closure clos = { txtacc_fmt, acc };
393 grecs_format_value(val, flags, &clos);
394 }
395
396
397 static int
file_fmt(const char * str,void * data)398 file_fmt(const char *str, void *data)
399 {
400 fputs(str, (FILE*)data);
401 return 0;
402 }
403
404 void
grecs_print_locus(grecs_locus_t * locus,FILE * fp)405 grecs_print_locus(grecs_locus_t *locus, FILE *fp)
406 {
407 struct grecs_format_closure clos = { file_fmt, fp };
408 grecs_format_locus(locus, &clos);
409 }
410
411 void
grecs_print_node_path(struct grecs_node * node,int flag,FILE * fp)412 grecs_print_node_path(struct grecs_node *node, int flag, FILE *fp)
413 {
414 struct grecs_format_closure clos = { file_fmt, fp };
415 grecs_format_node_path(node, flag, &clos);
416 }
417
418 void
grecs_print_value(struct grecs_value * val,int flags,FILE * fp)419 grecs_print_value(struct grecs_value *val, int flags, FILE *fp)
420 {
421 struct grecs_format_closure clos = { file_fmt, fp };
422 grecs_format_value(val, flags, &clos);
423 }
424
425 int
grecs_print_node(struct grecs_node * node,int flags,FILE * fp)426 grecs_print_node(struct grecs_node *node, int flags, FILE *fp)
427 {
428 struct grecs_format_closure clos = { file_fmt, fp };
429 return grecs_format_node(node, flags, &clos);
430 }
431