Index: ossp-pkg/cfg/ChangeLog RCS File: /v/ossp/cvs/ossp-pkg/cfg/ChangeLog,v rcsdiff -q -kk '-r1.23' '-r1.24' -u '/v/ossp/cvs/ossp-pkg/cfg/ChangeLog,v' 2>/dev/null --- ChangeLog 2004/11/28 12:58:25 1.23 +++ ChangeLog 2004/11/28 13:49:11 1.24 @@ -10,6 +10,9 @@ Changes between 0.9.5 and 0.9.6 (27-Nov-2004 to xx-Dec-2004): + *) Escape non-printable characters of input extracts in error messages. + [Ralf S. Engelschall ] + *) Cleanup and extend buffer handling sub-library (cfg_buf.[ch]) [Ralf S. Engelschall ] Index: ossp-pkg/cfg/cfg_syn.c RCS File: /v/ossp/cvs/ossp-pkg/cfg/cfg_syn.c,v co -q -kk -p'1.21' '/v/ossp/cvs/ossp-pkg/cfg/cfg_syn.c,v' | diff -u /dev/null - -L'ossp-pkg/cfg/cfg_syn.c' 2>/dev/null --- ossp-pkg/cfg/cfg_syn.c +++ - 2024-05-18 18:00:59.627588976 +0200 @@ -0,0 +1,409 @@ +/* +** OSSP cfg - Configuration Parsing +** Copyright (c) 2002-2004 Ralf S. Engelschall +** Copyright (c) 2002-2004 The OSSP Project (http://www.ossp.org/) +** Copyright (c) 2002-2004 Cable & Wireless (http://www.cw.com/) +** +** This file is part of OSSP cfg, a configuration parsing +** library which can be found at http://www.ossp.org/pkg/lib/cfg/. +** +** Permission to use, copy, modify, and distribute this software for +** any purpose with or without fee is hereby granted, provided that +** the above copyright notice and this permission notice appear in all +** copies. +** +** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED +** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR +** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +** SUCH DAMAGE. +** +** cfg_syn.c: syntax parsing and formatting +*/ + +#include +#include + +#include "cfg.h" +#include "cfg_global.h" +#include "cfg_grid.h" +#include "cfg_node.h" +#include "cfg_fmt.h" +#include "cfg_syn.h" +#include "cfg_buf.h" + +/* prototypes for Flex-generated scanner */ +extern int cfg_syn_lex_init(void *); +extern int cfg_syn_lex_destroy(void *); +extern void cfg_syn_set_extra(void *, void *); + +/* prototypes for Bison-generated parser */ +extern int cfg_syn_parse(void *); + +/* build a node tree according to a textual specification */ +cfg_rc_t cfg_syn_import( + cfg_t *cfg, + cfg_node_t **node, + const char *in_ptr, + size_t in_len, + char *err_buf, + size_t err_len) +{ + cfg_syn_ctx_t ctx; + void *yyscan; + + /* argument sanity checking */ + if (node == NULL || in_ptr == NULL) + return CFG_ERR_ARG; + + /* initialize scanner */ + cfg_syn_lex_init(&yyscan); + cfg_syn_set_extra(&ctx, yyscan); + + /* establish our own context which is passed + through the parser and scanner */ + ctx.inputptr = in_ptr; + ctx.inputbuf = in_ptr; + ctx.inputlen = in_len; + ctx.cfg = cfg; + ctx.node = NULL; + ctx.rv = CFG_OK; + ctx.err_buf = err_buf; + ctx.err_len = err_len; + ctx.yyscan = yyscan; + + /* start the parser loop */ + if (cfg_syn_parse(&ctx)) + ctx.rv = (ctx.rv == CFG_OK ? CFG_ERR_INT : ctx.rv); + + /* destroy scanner */ + cfg_syn_lex_destroy(yyscan); + + /* provide root/top-level node as result */ + *node = ctx.node; + + return ctx.rv; +} + +/* helper function for copying out input with escaping of characters */ +static size_t cfg_syn_error_cpyout(char *out_ptr, const char *in_ptr, size_t in_len) +{ + size_t out_len; + char c; + + out_len = 0; + while (in_len-- > 0) { + c = *in_ptr++; + switch (c) { + case '\n': *out_ptr++ = '\\'; *out_ptr++ = 'n'; out_len += 2; break; + case '\r': *out_ptr++ = '\\'; *out_ptr++ = 'r'; out_len += 2; break; + case '\t': *out_ptr++ = '\\'; *out_ptr++ = 't'; out_len += 2; break; + case '\b': *out_ptr++ = '\\'; *out_ptr++ = 'b'; out_len += 2; break; + case '\f': *out_ptr++ = '\\'; *out_ptr++ = 'f'; out_len += 2; break; + default: { + if (!isprint((int)c)) { + *out_ptr++ = '\\'; + *out_ptr++ = '?'; + out_len += 2; + } + else { + *out_ptr++ = c; + out_len++; + } + } + } + } + return out_len; +} + +/* remember a parsing error (used internally) */ +void cfg_syn_error(cfg_syn_ctx_t *ctx, cfg_rc_t rv, YYLTYPE *loc, const char *fmt, ...) +{ + va_list ap; + const char *cpF, *cpL; + const char *cpP, *cpE; + int line, column; + char *cpBuf; + char *cp; + size_t n; + + /* remember error code */ + ctx->rv = rv; + + /* short circuit processing if no error buffer exists */ + if (ctx->err_buf == NULL || ctx->err_len <= 0) + return; + + /* determine first and last positions of token */ + cpF = ctx->inputbuf+loc->first; + if (cpF < ctx->inputbuf) + cpF = ctx->inputbuf; + if (cpF > ctx->inputbuf+ctx->inputlen) + cpF = ctx->inputbuf+ctx->inputlen; + cpL = ctx->inputbuf+loc->last; + if (cpL < ctx->inputbuf) + cpL = ctx->inputbuf; + if (cpL > ctx->inputbuf+ctx->inputlen) + cpL = ctx->inputbuf+ctx->inputlen; + + /* determine epilog and prolog of token */ + cpP = cpF-4; + if (cpP < ctx->inputbuf) + cpP = ctx->inputbuf; + cpE = cpL+4; + if (cpE > ctx->inputbuf+ctx->inputlen) + cpE = ctx->inputbuf+ctx->inputlen; + + /* calculate line and column of token */ + line = 1; + column = 1; + for (cp = (char *)ctx->inputbuf; cp < (ctx->inputbuf+ctx->inputlen) && cp != cpF; cp++) { + column++; + if (*cp == '\n') { + column = 1; + line++; + } + } + + /* extract token context with mark token borders */ + if ((cpBuf = malloc((size_t)(((cpE-cpP)*2)+2+1))) == NULL) + return; + cp = cpBuf; + cp += cfg_syn_error_cpyout(cp, cpP, cpF-cpP); + *cp++ = '<'; + cp += cfg_syn_error_cpyout(cp, cpF, cpL-cpF); + *cp++ = '>'; + cp += cfg_syn_error_cpyout(cp, cpL, cpE-cpL); + *cp++ = '\0'; + + /* remember parsing error: part I (context part) */ + cfg_fmt_sprintf(ctx->err_buf, ctx->err_len, + "line %d, column %d: `%s'", + line, column, cpBuf); + free(cpBuf); + + /* remember parsing error: part II (parsing part) */ + n = strlen(ctx->err_buf); + va_start(ap, fmt); + cfg_fmt_vsprintf(ctx->err_buf+n, sizeof(ctx->err_len)-n, fmt, ap); + va_end(ap); + + return; +} + +/* internal cfg_syn_export structure */ +typedef struct { + cfg_t *cfg; + cfg_buf_t *buf; + int indent; +} export_t; + +/* internal cfg_syn_export helper function: sprintf(3) style output formatting */ +static void export_format(export_t *ctx, char *fmt, ...) +{ + int i; + char *cp; + char *cp2; + va_list ap; + char *str; + + va_start(ap, fmt); + if ((str = cfg_fmt_vasprintf(fmt, ap)) == NULL) + return; + cp = str; + while ((cp2 = strchr(cp, '\n')) != NULL) { + cfg_buf_format(ctx->buf, "%.*s", cp2-cp+1, cp); + for (i = 0; i < ctx->indent; i++) + cfg_buf_format(ctx->buf, " "); + cp = cp2+1; + } + if (cp[0] != '\0') + cfg_buf_format(ctx->buf, "%s", cp); + free(str); + va_end(ap); + return; +} + +/* internal cfg_syn_export helper function: indentation handling */ +static void export_indent(export_t *ctx, signed int n) +{ + if (n > 0) { + while (n > 0) { + n--; + ctx->indent++; + cfg_buf_format(ctx->buf, " "); + } + } + else { + while (n < 0 && ctx->indent > 0) { + n++; + ctx->indent--; + cfg_buf_resize(ctx->buf, -4); + } + } +} + +/* internal cfg_syn_export helper function: token output formatting */ +static void export_token(export_t *ctx, const char *token) +{ + const char *cp; + char *out; + int plain; + char c; + + plain = 1; + for (cp = token; *cp != '\0'; cp++) { + if ( !isprint((int)(*cp)) + || strchr(" \n\r\t\b\f;{}\\\"'", (int)(*cp)) != NULL) { + plain = 0; + break; + } + } + if (plain) + export_format(ctx, "%s", token); + else { + export_format(ctx, "\""); + cp = token; + while ((c = *cp++) != '\0') { + out = NULL; + switch (c) { + case '\n': out = "\\n"; break; + case '\r': out = "\\r"; break; + case '\t': out = "\\t"; break; + case '\b': out = "\\b"; break; + case '\f': out = "\\f"; break; + case '\\': out = "\\\\"; break; + case '"' : out = "\\\""; break; + } + if (out != NULL) + export_format(ctx, "%s", out); + else { + if (isprint((int)c)) + export_format(ctx, "%c", c); + else + export_format(ctx, "\\x%02x", c); + } + } + export_format(ctx, "\""); + } + return; +} + +/* internal cfg_syn_export helper function: recursive node processing */ +static void export_node(export_t *ctx, cfg_node_t *node) +{ + cfg_node_type_t type; + cfg_node_t *node2; + cfg_rc_t rc; + char *token; + + if ((rc = cfg_node_get(ctx->cfg, node, CFG_NODE_ATTR_TYPE, &type)) != CFG_OK) + return; + if (type == CFG_NODE_TYPE_SEQ) { + /* node is a sequence */ + export_format(ctx, "{\n"); + export_indent(ctx, 1); + cfg_node_get(ctx->cfg, node, CFG_NODE_ATTR_CHILD1, &node2); + while (node2 != NULL) { + export_node(ctx, node2); /* recursion */ + cfg_node_get(ctx->cfg, node2, CFG_NODE_ATTR_RBROTH, &node2); + } + export_indent(ctx, -1); + export_format(ctx, "}"); + } + else if (type == CFG_NODE_TYPE_DIR) { + /* node is a directive */ + cfg_node_get(ctx->cfg, node, CFG_NODE_ATTR_CHILD1, &node2); + while (node2 != NULL) { + export_node(ctx, node2); /* recursion */ + cfg_node_get(ctx->cfg, node2, CFG_NODE_ATTR_RBROTH, &node2); + if (node2 != NULL) + export_format(ctx, " "); + } + cfg_node_get(ctx->cfg, node, CFG_NODE_ATTR_RBROTH, &node2); + if (node2 != NULL) + export_format(ctx, ";"); + export_format(ctx, "\n"); + } + else if (type == CFG_NODE_TYPE_ARG) { + /* node is a token */ + cfg_node_get(ctx->cfg, node, CFG_NODE_ATTR_TOKEN, &token); + if (token != NULL) + export_token(ctx, token); + else + export_format(ctx, ""); + } + return; +} + +/* export configuration node tree into textual OSSP cfg syntax */ +cfg_rc_t cfg_syn_export( + cfg_t *cfg, + cfg_node_t *node, + char **output) +{ + cfg_buf_t *buf; + cfg_rc_t rc; + export_t ctx; + cfg_node_t *root; + + if (node == NULL || output == NULL) + return CFG_ERR_ARG; + if ((rc = cfg_buf_create(&buf)) != CFG_OK) + return rc; + + ctx.cfg = cfg; + ctx.buf = buf; + ctx.indent = 0; + + if ((rc = cfg_node_root(cfg, NULL, &root)) != CFG_OK) + return rc; + if (node == root) { + /* if we dump the whole configuration, treat the first SEQ node special + because of the implicit braces around it. So expand it manually instead + of just calling once export_node(&ctx, node); */ + cfg_node_get(cfg, node, CFG_NODE_ATTR_CHILD1, &node); + while (node != NULL) { + export_node(&ctx, node); + cfg_node_get(cfg, node, CFG_NODE_ATTR_RBROTH, &node); + } + } + else { + export_node(&ctx, node); + } + + cfg_buf_content(buf, output, NULL, NULL); + cfg_buf_destroy(buf); + + return CFG_OK; +} + +/* internal cfg_syn_destroy helper function: recursive node destruction */ +static cfg_rc_t cfg_syn_destroy_node(cfg_t *cfg, cfg_node_t *node) +{ + if (node == NULL) + return CFG_ERR_ARG; + if (node->child1 != NULL) + cfg_syn_destroy_node(cfg, node->child1); /* recursion */ + if (node->rbroth != NULL) + cfg_syn_destroy_node(cfg, node->rbroth); /* recursion */ + cfg_node_destroy(cfg, node); + return CFG_OK; +} + +/* destroy syntax node tree */ +cfg_rc_t cfg_syn_destroy(cfg_t *cfg, cfg_node_t *node) +{ + if (node == NULL) + return CFG_ERR_ARG; + cfg_syn_destroy_node(cfg, node); + return CFG_OK; +} +