*** /dev/null Tue Mar 11 06:01:36 2025
--- - Tue Mar 11 06:01:37 2025
***************
*** 0 ****
--- 1,409 ----
+ /*
+ ** OSSP cfg - Configuration Parsing
+ ** Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+ ** 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 <stdio.h>
+ #include <ctype.h>
+
+ #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;
+ }
+
|