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