/* grecs - Gray's Extensible Configuration System
Copyright (C) 2007-2011 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 "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 "sock-addr";
case grecs_type_section:
return "section";
}
return "UNKNOWN?";
}
static void
format_level(unsigned level, FILE *stream)
{
while (level--)
fprintf(stream, " ");
}
void
grecs_format_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_format_simple_statement(struct grecs_keyword *kwp, unsigned level,
FILE *stream)
{
const char *argstr;
if (kwp->docstring)
grecs_format_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 (strchr (argstr, ':'))
fprintf(stream, "%s <%s>;\n", kwp->ident, gettext(argstr));
else {
fprintf(stream, "%s <%s: ", kwp->ident, gettext(argstr));
if (GRECS_IS_LIST(kwp->type))
fprintf(stream, "list of %s",
gettext(grecs_data_type_string(
GRECS_TYPE(kwp->type))));
else
fprintf(stream, "%s",
gettext(grecs_data_type_string(
GRECS_TYPE(kwp->type))));
fprintf(stream, ">;\n");
}
}
void
grecs_format_block_statement(struct grecs_keyword *kwp, unsigned level,
FILE *stream)
{
if (kwp->docstring)
grecs_format_docstring(kwp->docstring, level, stream);
format_level(level, stream);
fprintf(stream, "%s", kwp->ident);
if (kwp->argname)
fprintf(stream, " <%s>", gettext(kwp->argname));
fprintf(stream, " {\n");
grecs_format_statement_array(kwp->kwd, 0, level + 1, stream);
format_level(level, stream);
fprintf(stream, "}\n");
}
void
grecs_format_statement_array(struct grecs_keyword *kwp,
unsigned n,
unsigned level,
FILE *stream)
{
for (; kwp->ident; kwp++, n++) {
if (n)
fputc('\n', stream);
if (kwp->type == grecs_type_section)
grecs_format_block_statement(kwp, level, stream);
else
grecs_format_simple_statement(kwp, level, stream);
}
}
void
grecs_format_locus(grecs_locus_t *locus, FILE *fp)
{
if (locus)
fprintf(fp, "%s:%d", locus->file, locus->line);
}
void
grecs_format_node_path(struct grecs_node *node, int flags, FILE *fp)
{
int delim = flags & 0xff;
if (node->up)
grecs_format_node_path(node->up, flags, fp);
if (node->type == grecs_node_root)
return;
fputc(delim ? delim : '.', fp);
fprintf(fp, "%s", node->ident);
if (node->type == grecs_node_block &&
!GRECS_VALUE_EMPTY_P(node->v.value)) {
fputc('=', fp);
grecs_format_value(node->v.value, flags|GRECS_NODE_FLAG_QUOTE,
fp);
}
}
void
grecs_format_value(struct grecs_value *val, int flags, FILE *fp)
{
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;
fprintf(fp, "\"%s\"", cbuf);
grecs_free(cbuf);
} else
fprintf(fp, "%s", val->v.string);
break;
case GRECS_TYPE_LIST:
fputc('(', fp);
for (ep = val->v.list->head; ep; ep = ep->next) {
grecs_format_value(ep->data, flags, fp);
if (ep->next) {
fputc(',', fp);
fputc(' ', fp);
}
}
fputc(')', fp);
break;
case GRECS_TYPE_ARRAY:
for (i = 0; i < val->v.arg.c; i++) {
if (i)
fputc(' ', fp);
grecs_format_value(val->v.arg.v[i], flags, fp);
}
}
}
void
grecs_txtacc_format_value(struct grecs_value *val, int flags,
struct grecs_txtacc *acc)
{
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);
grecs_txtacc_grow_char(acc, '"');
grecs_txtacc_grow(acc, cbuf, clen);
grecs_txtacc_grow_char(acc, '"');
grecs_free(cbuf);
} else
grecs_txtacc_grow(acc, val->v.string,
strlen(val->v.string));
break;
case GRECS_TYPE_LIST:
grecs_txtacc_grow_char(acc, '(');
for (ep = val->v.list->head; ep; ep = ep->next) {
grecs_txtacc_format_value(ep->data, flags, acc);
if (ep->next) {
grecs_txtacc_grow_char(acc, ',');
grecs_txtacc_grow_char(acc, ' ');
}
}
grecs_txtacc_grow_char(acc, ')');
break;
case GRECS_TYPE_ARRAY:
for (i = 0; i < val->v.arg.c; i++) {
if (i)
grecs_txtacc_grow_char(acc, ' ');
grecs_txtacc_format_value(val->v.arg.v[i], flags, acc);
}
}
}
void
grecs_format_node(struct grecs_node *node, int flags, FILE *fp)
{
const char *delim_str = NULL;
if (!flags)
flags = GRECS_NODE_FLAG_DEFAULT;
switch (node->type) {
case grecs_node_root:
case grecs_node_block:
if (!(flags & GRECS_NODE_FLAG_NODESCEND)) {
for (node = node->down; node; node = node->next) {
grecs_format_node(node, flags, fp);
if (node->next)
fputc('\n', fp);
}
break;
}
case grecs_node_stmt:
if (flags & GRECS_NODE_FLAG_LOCUS) {
grecs_format_locus(&node->locus, fp);
delim_str = ": ";
}
if (flags & GRECS_NODE_FLAG_PATH) {
if (delim_str)
fprintf(fp, "%s", delim_str);
grecs_format_node_path(node, flags, fp);
delim_str = ": ";
}
if (flags & GRECS_NODE_FLAG_VALUE) {
if (delim_str)
fprintf(fp, "%s", delim_str);
grecs_format_value(node->v.value, flags, fp);
}
}
}