/* grecs - Gray's Extensible Configuration System Copyright (C) 2007-2015 Sergey Poznyakoff Grecs is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Grecs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Grecs. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include "wordsplit.h" const char * grecs_data_type_string(enum grecs_data_type type) { switch (type) { case grecs_type_void: return "void"; case grecs_type_string: return "string"; case grecs_type_short: case grecs_type_ushort: case grecs_type_int: case grecs_type_uint: case grecs_type_long: case grecs_type_ulong: case grecs_type_size: /*FIXME case grecs_type_off:*/ return "number"; case grecs_type_time: return "time"; case grecs_type_bool: return "boolean"; case grecs_type_ipv4: return "IPv4"; case grecs_type_cidr: return "CIDR"; case grecs_type_host: return "hostname"; case grecs_type_sockaddr: return "sockaddr"; case grecs_type_section: return "section"; case grecs_type_null: return "null"; } return "UNKNOWN?"; } static void format_level(unsigned level, FILE *stream) { while (level--) fprintf(stream, " "); } void grecs_print_docstring(const char *docstring, unsigned level, FILE *stream) { size_t len = strlen(docstring); int width = 78 - level * 2; if (width < 0) { width = 78; level = 0; } while (len) { size_t seglen; const char *p; for (seglen = 0, p = docstring; p < docstring + width && *p; p++) { if (*p == '\n') { seglen = p - docstring; break; } if (isspace(*p)) seglen = p - docstring; } if (seglen == 0 || *p == 0) seglen = p - docstring; format_level(level, stream); fprintf(stream, "# "); fwrite(docstring, seglen, 1, stream); fputc('\n', stream); len -= seglen; docstring += seglen; if (*docstring == '\n') { docstring++; len--; } else while (*docstring && isspace(*docstring)) { docstring++; len--; } } } void grecs_print_simple_statement(struct grecs_keyword *kwp, unsigned level, FILE *stream) { const char *argstr; if (kwp->flags & GRECS_INAC) grecs_print_docstring(N_("Disabled;"), level, stream); if (kwp->docstring) grecs_print_docstring(kwp->docstring, level, stream); format_level(level, stream); if (kwp->argname) argstr = kwp->argname; else argstr = N_("arg"); if (strchr("<[", argstr[0])) fprintf(stream, "%s %s;\n", kwp->ident, gettext(argstr)); else if (argstr[0] == '\'') fprintf(stream, "%s %s;\n", kwp->ident, argstr + 1); else if (strchr (argstr, ':')) fprintf(stream, "%s <%s>;\n", kwp->ident, gettext(argstr)); else { fprintf(stream, "%s <%s: ", kwp->ident, gettext(argstr)); if (kwp->flags & GRECS_LIST) fprintf(stream, "list of %s", gettext(grecs_data_type_string(kwp->type))); else fprintf(stream, "%s", gettext(grecs_data_type_string(kwp->type))); fprintf(stream, ">;\n"); } } void grecs_print_block_statement(struct grecs_keyword *kwp, unsigned level, FILE *stream) { if (kwp->docstring) grecs_print_docstring(kwp->docstring, level, stream); format_level(level, stream); fprintf(stream, "%s", kwp->ident); if (kwp->argname) { if (kwp->argname[0] == '\'') fprintf(stream, " %s", kwp->argname + 1); else fprintf(stream, " <%s>", gettext(kwp->argname)); } fprintf(stream, " {\n"); grecs_print_statement_array(kwp->kwd, 0, level + 1, stream); format_level(level, stream); fprintf(stream, "}\n"); } void grecs_print_statement_array(struct grecs_keyword *kwp, unsigned n, unsigned level, FILE *stream) { if (!kwp) { return; } for (; kwp->ident; kwp++, n++) { if (kwp->flags & GRECS_HIDDEN) continue; if (n) fputc('\n', stream); if (kwp->type == grecs_type_section) grecs_print_block_statement(kwp, level, stream); else grecs_print_simple_statement(kwp, level, stream); } } void grecs_format_locus(grecs_locus_t *locus, struct grecs_format_closure *clos) { if (locus) { char *str = NULL; size_t size = 0; if (locus->beg.col == 0) grecs_asprintf(&str, &size, "%s:%u", locus->beg.file, locus->beg.line); else if (strcmp(locus->beg.file, locus->end.file)) grecs_asprintf(&str, &size, "%s:%u.%u-%s:%u.%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.file, locus->end.line, locus->end.col); else if (locus->beg.line != locus->end.line) grecs_asprintf(&str, &size, "%s:%u.%u-%u.%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.line, locus->end.col); else if (locus->beg.col != locus->end.col) grecs_asprintf(&str, &size, "%s:%u.%u-%u", locus->beg.file, locus->beg.line, locus->beg.col, locus->end.col); else grecs_asprintf(&str, &size, "%s:%u.%u", locus->beg.file, locus->beg.line, locus->beg.col); clos->fmtfun(str, clos->data); free(str); } } void grecs_format_node_path(struct grecs_node *node, int flags, struct grecs_format_closure *clos) { char delim[2] = "."; if (!node) { clos->fmtfun("NULL", clos->data); return; } if (node->up) grecs_format_node_path(node->up, flags, clos); if (node->type == grecs_node_root) return; if (flags & _GRECS_NODE_MASK_DELIM) delim[0] = flags & _GRECS_NODE_MASK_DELIM; clos->fmtfun(delim, clos->data); clos->fmtfun(node->ident, clos->data); if (node->type == grecs_node_block && !GRECS_VALUE_EMPTY_P(node->v.value)) { clos->fmtfun("=", clos->data); grecs_format_value(node->v.value, flags|GRECS_NODE_FLAG_QUOTE, clos); } } void grecs_format_value(struct grecs_value *val, int flags, struct grecs_format_closure *clos) { int i; struct grecs_list_entry *ep; size_t clen; int need_quote; if (!val) return; switch (val->type) { case GRECS_TYPE_STRING: clen = wordsplit_c_quoted_length(val->v.string, flags & GRECS_NODE_FLAG_QUOTE_HEX, &need_quote); if (flags & GRECS_NODE_FLAG_QUOTE) need_quote = 1; else if (flags & GRECS_NODE_FLAG_NOQUOTE) need_quote = 0; if (need_quote) { char *cbuf = grecs_malloc(clen + 1); wordsplit_c_quote_copy(cbuf, val->v.string, flags & GRECS_NODE_FLAG_QUOTE_HEX); cbuf[clen] = 0; clos->fmtfun("\"", clos->data); clos->fmtfun(cbuf, clos->data); clos->fmtfun("\"", clos->data); grecs_free(cbuf); } else clos->fmtfun(val->v.string, clos->data); break; case GRECS_TYPE_LIST: clos->fmtfun("(", clos->data); for (ep = val->v.list->head; ep; ep = ep->next) { grecs_format_value(ep->data, flags, clos); if (ep->next) clos->fmtfun(", ", clos->data); } clos->fmtfun(")", clos->data); break; case GRECS_TYPE_ARRAY: for (i = 0; i < val->v.arg.c; i++) { if (i) clos->fmtfun(" ", clos->data); grecs_format_value(val->v.arg.v[i], flags, clos); } } } int grecs_format_node(struct grecs_node *node, int flags, struct grecs_format_closure *clos) { const char *delim_str = NULL; if (!(flags & _GRECS_NODE_MASK_OUTPUT)) { errno = EINVAL; return 1; } if (!node) { clos->fmtfun("NULL", clos->data); return 0; } switch (node->type) { case grecs_node_root: case grecs_node_block: if (flags & GRECS_NODE_FLAG_DESCEND) { for (node = node->down; node; node = node->next) { grecs_format_node(node, flags, clos); if (node->next) clos->fmtfun("\n", clos->data); } break; } case grecs_node_stmt: if (flags & GRECS_NODE_FLAG_LOCUS) { grecs_locus_t *locus; if (flags & GRECS_NODE_FLAG_PATH) { if (flags & GRECS_NODE_FLAG_VALUE) locus = &node->locus; else locus = &node->idloc; } else if (flags & GRECS_NODE_FLAG_VALUE) locus = &node->v.value->locus; else locus = &node->locus; grecs_format_locus(locus, clos); delim_str = ": "; } if (flags & GRECS_NODE_FLAG_PATH) { if (delim_str) clos->fmtfun(delim_str, clos->data); grecs_format_node_path(node, flags, clos); delim_str = ": "; } if (flags & GRECS_NODE_FLAG_VALUE) { if (delim_str) clos->fmtfun(delim_str, clos->data); grecs_format_value(node->v.value, flags, clos); } } return 0; } static int txtacc_fmt(const char *str, void *data) { struct grecs_txtacc *acc = data; grecs_txtacc_grow(acc, str, strlen(str)); return 0; } void grecs_txtacc_format_value(struct grecs_value *val, int flags, struct grecs_txtacc *acc) { struct grecs_format_closure clos = { txtacc_fmt, acc }; grecs_format_value(val, flags, &clos); } static int file_fmt(const char *str, void *data) { fputs(str, (FILE*)data); return 0; } void grecs_print_locus(grecs_locus_t *locus, FILE *fp) { struct grecs_format_closure clos = { file_fmt, fp }; grecs_format_locus(locus, &clos); } void grecs_print_node_path(struct grecs_node *node, int flag, FILE *fp) { struct grecs_format_closure clos = { file_fmt, fp }; grecs_format_node_path(node, flag, &clos); } void grecs_print_value(struct grecs_value *val, int flags, FILE *fp) { struct grecs_format_closure clos = { file_fmt, fp }; grecs_format_value(val, flags, &clos); } int grecs_print_node(struct grecs_node *node, int flags, FILE *fp) { struct grecs_format_closure clos = { file_fmt, fp }; return grecs_format_node(node, flags, &clos); }