* Experimental text syntax parser/loader

* Added new text syntax parsing and loading from CLI
  * Unified text output functions to `xml2txt` and moved to clixon_text_syntax.[ch]
    * The following are removed: `cli_xml2txt` and `xml2txt_cb`
  * Text output format changed:
    * Namespace/modulename added to top-level
  * See [Support performant load_config_file(...) for TEXT format](https://github.com/clicon/clixon/issues/324)
This commit is contained in:
Olof hagsand 2022-05-19 12:56:44 +02:00
parent 43a57dad79
commit 2ece0b8f51
29 changed files with 1140 additions and 238 deletions

View file

@ -38,6 +38,14 @@
## 5.8.0
Planned: July 2022
### New features
* Text syntax parser/loader
* Added new text syntax parsing and loading from CLI
* Text output format changed:
* Namespace/modulename added to top-level
* See [Support performant load_config_file(...) for TEXT format](https://github.com/clicon/clixon/issues/324)
### C/CLI-API changes on existing features
Developers may need to change their code
@ -52,6 +60,7 @@ Developers may need to change their code
* Replace `xml2json_cb(...)` with `xml2json_file(..., 0)`
* `clicon_xml2cbuf()`: Added `skiptop` parameter: `clicon_xml2cbuf(..., int skiptop)`
* `xml2cli()`: Added `skiptop` parameter: `xml2cli(..., int skiptop)`
* Merged `cli_xml2txt()` and `xml2txt_cb()` with `xml2txt()`
## 5.7.0
17 May 2022

View file

@ -125,28 +125,6 @@ cvec_append(cvec *cvv0,
return cvv2;
}
/*! x is element and has exactly one child which in turn has none
* @see child_type in clixon_json.c
*/
static int
tleaf(cxobj *x)
{
cxobj *xc;
if (xml_type(x) != CX_ELMNT)
return 0;
if (xml_child_nr_notype(x, CX_ATTR) != 1)
return 0;
/* From here exactly one noattr child, get it */
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
break;
if (xc == NULL)
return -1; /* n/a */
return (xml_child_nr_notype(xc, CX_ATTR) == 0);
}
/*! Print an XML tree structure from an auto-cli env to stdout and encode chars "<>&"
*
* @param[in] xn clicon xml tree
@ -256,64 +234,6 @@ cli_xml2file(cxobj *xn,
return retval;
}
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
* @param[in] xn XML object to print
* @param[in] fn Callback to make print function
* @param[in] level print 4 spaces per level in front of each line
*/
int
cli_xml2txt(cxobj *xn,
clicon_output_cb *fn,
int level)
{
cxobj *xc = NULL;
int children=0;
int retval = -1;
int exist = 0;
if (xn == NULL || fn == NULL){
clicon_err(OE_XML, EINVAL, "xn or fn is NULL");
goto done;
}
if (yang_extension_value(xml_spec(xn), "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
goto done;
if (exist)
goto ok;
xc = NULL; /* count children (elements and bodies, not attributes) */
while ((xc = xml_child_each(xn, xc, -1)) != NULL)
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
children++;
if (!children){ /* If no children print line */
switch (xml_type(xn)){
case CX_BODY:
(*fn)(stdout, "%s;\n", xml_value(xn));
break;
case CX_ELMNT:
(*fn)(stdout, "%*s%s;\n", 4*level, "", xml_name(xn));
break;
default:
break;
}
goto ok;
}
(*fn)(stdout, "%*s", 4*level, "");
(*fn)(stdout, "%s ", xml_name(xn));
if (!tleaf(xn))
(*fn)(stdout, "{\n");
xc = NULL;
while ((xc = xml_child_each(xn, xc, -1)) != NULL){
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
if (cli_xml2txt(xc, fn, level+1) < 0)
break;
}
if (!tleaf(xn))
(*fn)(stdout, "%*s}\n", 4*level, "");
ok:
retval = 0;
done:
return retval;
}
/*! Enter a CLI edit mode
* @param[in] h CLICON handle
* @param[in] cvv Vector of variables from CLIgen command-line
@ -657,10 +577,10 @@ cli_auto_show(clicon_handle h,
break;
case FORMAT_TEXT:
if (isroot)
cli_xml2txt(xp, cligen_output, 0); /* tree-formed text */
xml2txt(xp, cligen_output, stdout, 0); /* tree-formed text */
else
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL)
cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */
xml2txt(xc, cligen_output, stdout, 0); /* tree-formed text */
break;
case FORMAT_CLI:
if (xml2cli(h, stdout, xp, prefix, cligen_output, !isroot) < 0)

View file

@ -698,7 +698,7 @@ compare_xmls(cxobj *xc1,
xc = NULL;
if (astext)
while ((xc = xml_child_each(xc1, xc, -1)) != NULL)
xml2txt_cb(f, xc, cligen_output);
xml2txt(xc, cligen_output, f, 0);
else
while ((xc = xml_child_each(xc1, xc, -1)) != NULL)
clicon_xml2file_cb(f, xc, 0, 1, cligen_output);
@ -715,7 +715,7 @@ compare_xmls(cxobj *xc1,
xc = NULL;
if (astext)
while ((xc = xml_child_each(xc2, xc, -1)) != NULL)
xml2txt_cb(f, xc, cligen_output);
xml2txt(xc, cligen_output, f, 0);
else
while ((xc = xml_child_each(xc2, xc, -1)) != NULL)
clicon_xml2file_cb(f, xc, 0, 1, cligen_output);
@ -882,6 +882,14 @@ load_config_file(clicon_handle h,
goto done;
}
break;
case FORMAT_TEXT:
if ((ret = clixon_text_syntax_parse_file(fp, YB_NONE, yspec, &xt, &xerr)) < 0)
goto done;
if (ret == 0){
clixon_netconf_error(xerr, "Loading", filename);
goto done;
}
break;
case FORMAT_CLI:
{
char *mode = cli_syntax_mode(h);
@ -1034,7 +1042,7 @@ save_config_file(clicon_handle h,
goto done;
break;
case FORMAT_TEXT:
if (xml2txt(f, xt, 0) < 0)
if (xml2txt(xt, fprintf, f, 0) < 0)
goto done;
break;
case FORMAT_CLI:
@ -1165,7 +1173,7 @@ cli_notification_cb(int s,
goto done;
break;
case FORMAT_TEXT:
if (xml2txt_cb(stdout, x, cligen_output) < 0)
if (xml2txt(x, cligen_output, stdout, 0) < 0)
goto done;
break;
default:

View file

@ -505,7 +505,7 @@ cli_show_config1(clicon_handle h,
case FORMAT_TEXT:
xc = NULL; /* Dont print xt itself */
while ((xc = xml_child_each(xt, xc, -1)) != NULL)
cli_xml2txt(xc, cligen_output, 0); /* tree-formed text */
xml2txt(xc, cligen_output, stdout, 0); /* tree-formed text */
break;
case FORMAT_CLI:
if (xml2cli(h, stdout, xt, prefix, cligen_output, 1) < 0)
@ -789,7 +789,7 @@ cli_show_generated(clicon_handle h,
cli_xml2file(xp_helper, 0, 1, fprintf);
break;
case FORMAT_TEXT:
cli_xml2txt(xp_helper, cligen_output, 0); /* tree-formed text */
xml2txt(xp_helper, cligen_output, stdout, 0); /* tree-formed text */
break;
default: /* see cli_show_config() */
break;
@ -986,7 +986,7 @@ cli_pagination(clicon_handle h,
xml2json_file(stdout, xc, 1, cligen_output, 0);
break;
case FORMAT_TEXT:
xml2txt_cb(stdout, xc, cligen_output); /* tree-formed text */
xml2txt(xc, cligen_output, stdout, 0); /* tree-formed text */
break;
case FORMAT_CLI:
/* hardcoded to compress and list-keyword = nokey */

View file

@ -136,7 +136,6 @@ int cli_help(clicon_handle h, cvec *vars, cvec *argv);
int expand_dbvar(void *h, char *name, cvec *cvv, cvec *argv,
cvec *commands, cvec *helptexts);
int cli_xml2file (cxobj *xn, int level, int prettyprint, clicon_output_cb *fn);
int cli_xml2txt(cxobj *xn, clicon_output_cb *fn, int level);
int xml2cli(clicon_handle h, FILE *f, cxobj *xn, char *prepend, clicon_output_cb *fn, int skiptop);
/* cli_show.c: CLIgen new vector arg callbacks */

View file

@ -122,7 +122,7 @@ example_client_rpc(clicon_handle h,
fprintf(stdout,"\n");
/* pretty-print:
xml2txt_cb(stdout, xml_child_i(xret, 0), cligen_output);
xml2txt(xml_child_i(xret, 0), cligen_output, stdout, 0);
*/
retval = 0;
done:

View file

@ -111,11 +111,13 @@ load("Load configuration from XML file") <filename:string>("Filename (local file
cli("Replace candidate with file containing CLI commands"), load_config_file("","filename", "replace", "cli");
xml("Replace candidate with file containing XML"), load_config_file("","filename", "replace", "xml");
json("Replace candidate with file containing JSON"), load_config_file("","filename", "replace", "json");
text("Replace candidate with file containing TEXT"), load_config_file("","filename", "replace", "text");
}
merge("Merge file with existent candidate"), load_config_file("filename", "merge");{
cli("Merge candidate with file containing CLI commands"), load_config_file("","filename", "merge", "cli");
xml("Merge candidate with file containing XML"), load_config_file("","filename", "merge", "xml");
json("Merge candidate with file containing JSON"), load_config_file("","filename", "merge", "json");
text("Merge candidate with file containing TEXT"), load_config_file("","filename", "merge", "text");
}
}
example("This is a comment") <var:int32>("Just a random number"), mycallback("myarg");

View file

@ -103,6 +103,7 @@ extern "C" {
#include <clixon/clixon_xpath_optimize.h>
#include <clixon/clixon_xpath_yang.h>
#include <clixon/clixon_json.h>
#include <clixon/clixon_text_syntax.h>
#include <clixon/clixon_nacm.h>
#include <clixon/clixon_xml_changelog.h>
#include <clixon/clixon_xml_nsctx.h>

View file

@ -0,0 +1,47 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* TEXT / curly-brace syntax parsing and translations
*/
#ifndef _CLIXON_TEXT_SYNTAX_H
#define _CLIXON_TEXT_SYNTAX_H
/*
* Prototypes
*/
int xml2txt(cxobj *xn, clicon_output_cb *fn, FILE *f, int level);
int clixon_text_syntax_parse_string(char *str, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);
int clixon_text_syntax_parse_file(FILE *fp, yang_bind yb, yang_stmt *yspec, cxobj **xt, cxobj **xerr);
#endif /* _CLIXON_TEXT_SYNTAX_H */

View file

@ -225,6 +225,7 @@ cxobj *xml_child_each(cxobj *xparent, cxobj *xprev, enum cxobj_type type);
int xml_child_insert_pos(cxobj *x, cxobj *xc, int i);
int xml_childvec_set(cxobj *x, int len);
cxobj **xml_childvec_get(cxobj *x);
int clixon_child_xvec_append(cxobj *x, clixon_xvec *xv);
cxobj *xml_new(char *name, cxobj *xn_parent, enum cxobj_type type);
cxobj *xml_new_body(char *name, cxobj *parent, char *val);
yang_stmt *xml_spec(cxobj *x);

View file

@ -50,8 +50,6 @@ typedef enum yang_class yang_class;
* Prototypes
*/
int isxmlns(cxobj *x);
int xml2txt_cb(FILE *f, cxobj *x, clicon_output_cb *fn);
int xml2txt(FILE *f, cxobj *x, int level);
int xmlns_assign(cxobj *x);
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);

View file

@ -41,7 +41,7 @@
/*
* Types
*/
typedef struct clixon_xml_vec clixon_xvec; /* struct defined in clicon_xvec.c */
typedef struct clixon_xml_vec clixon_xvec; /* struct defined in clicon_xml_vec.c */
/*
* Prototypes
@ -51,7 +51,7 @@ clixon_xvec *clixon_xvec_dup(clixon_xvec *xv0);
int clixon_xvec_free(clixon_xvec *xv);
int clixon_xvec_len(clixon_xvec *xv);
cxobj *clixon_xvec_i(clixon_xvec *xv, int i);
int clixon_xvec_extract(clixon_xvec *xv, cxobj ***xvec, int *xlen);
int clixon_xvec_extract(clixon_xvec *xv, cxobj ***xvcec, int *xlen, int *xmax);
int clixon_xvec_append(clixon_xvec *xv, cxobj *x);
int clixon_xvec_prepend(clixon_xvec *xv, cxobj *x);
int clixon_xvec_insert_pos(clixon_xvec *xv, cxobj *x, int i);

View file

@ -85,14 +85,15 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
clixon_xpath_optimize.c clixon_xpath_yang.c \
clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c clixon_client.c clixon_netns.c \
clixon_dispatcher.c
clixon_dispatcher.c clixon_text_syntax.c
YACCOBJS = lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
lex.clixon_json_parse.o clixon_json_parse.tab.o \
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o \
lex.clixon_api_path_parse.o clixon_api_path_parse.tab.o \
lex.clixon_instance_id_parse.o clixon_instance_id_parse.tab.o
lex.clixon_instance_id_parse.o clixon_instance_id_parse.tab.o \
lex.clixon_text_syntax_parse.o clixon_text_syntax_parse.tab.o
# Generated src
GENSRC = build.c
@ -123,6 +124,7 @@ clean:
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
rm -f clixon_api_path_parse.tab.[ch] clixon_api_path_parse.[co]
rm -f clixon_instance_id_parse.tab.[ch] clixon_instance_id_parse.[co]
rm -f clixon_text_syntax_parse.tab.[ch] clixon_text_syntax_parse.[co]
rm -f lex.clixon_xml_parse.c
rm -f lex.clixon_yang_parse.c
rm -f lex.clixon_json_parse.c
@ -221,6 +223,19 @@ clixon_instance_id_parse.tab.c: clixon_instance_id_parse.tab.h
lex.clixon_instance_id_parse.o : lex.clixon_instance_id_parse.c clixon_instance_id_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
# text syntax parser
lex.clixon_text_syntax_parse.c : clixon_text_syntax_parse.l clixon_text_syntax_parse.tab.h
$(LEX) -Pclixon_text_syntax_parse clixon_text_syntax_parse.l # -d is debug
clixon_text_syntax_parse.tab.h: clixon_text_syntax_parse.y
$(YACC) -l -d -b clixon_text_syntax_parse -p clixon_text_syntax_parse clixon_text_syntax_parse.y # -t is debug
# extra rule to avoid parallell yaccs
clixon_text_syntax_parse.tab.c: clixon_text_syntax_parse.tab.h
lex.clixon_text_syntax_parse.o : lex.clixon_text_syntax_parse.c clixon_text_syntax_parse.tab.h
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
distclean: clean
rm -f Makefile *~ .depend

View file

@ -87,7 +87,7 @@
/* Size of json read buffer when reading from file*/
#define BUFLEN 1024
/* Name of xml top object created by xml parse functions */
/* Name of xml top object created by parse functions */
#define JSON_TOP_SYMBOL "top"
enum array_element_type{
@ -1545,6 +1545,7 @@ clixon_json_parse_string(char *str,
*
* @param[in] fp File descriptor to the JSON file (ASCII string)
* @param[in] rfc7951 Do sanity checks according to RFC 7951 JSON Encoding of Data Modeled with YANG
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Yang specification, or NULL
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
* @param[out] xerr Reason for invalid returned as netconf err msg

View file

@ -1652,7 +1652,7 @@ clixon_xml_find_api_path(cxobj *xt,
if (ret == 0)
goto fail;
/* Convert to api xvec format */
if (clixon_xvec_extract(xv, xvec, xlen) < 0)
if (clixon_xvec_extract(xv, xvec, xlen, NULL) < 0)
goto done;
retval = 1;
done:
@ -1745,7 +1745,7 @@ clixon_xml_find_instance_id(cxobj *xt,
if (ret == 0)
goto fail;
/* Convert to api xvec format */
if (xv && clixon_xvec_extract(xv, xvec, xlen) < 0)
if (xv && clixon_xvec_extract(xv, xvec, xlen, NULL) < 0)
goto done;
retval = 1;
done:

View file

@ -0,0 +1,421 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* TEXT / curly-brace syntax parsing and translations
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_options.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_io.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_bind.h"
#include "clixon_text_syntax.h"
#include "clixon_text_syntax_parse.h"
/* Size of json read buffer when reading from file*/
#define BUFLEN 1024
/* Name of xml top object created by parse functions */
#define TOP_SYMBOL "top"
/*! x is element and has eactly one child which in turn has none
* @see child_type in clixon_json.c
*/
static int
tleaf(cxobj *x)
{
cxobj *xc;
if (xml_type(x) != CX_ELMNT)
return 0;
if (xml_child_nr_notype(x, CX_ATTR) != 1)
return 0;
/* From here exactly one noattr child, get it */
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
break;
if (xc == NULL)
return -1; /* n/a */
return (xml_child_nr_notype(xc, CX_ATTR) == 0);
}
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
* @param[in] xn XML object to print
* @param[in] fn Callback to make print function
* @param[in] f File to print to
* @param[in] level print 4 spaces per level in front of each line
* @see xml2txt XXX why are these different?
*/
int
xml2txt(cxobj *xn,
clicon_output_cb *fn,
FILE *f,
int level)
{
cxobj *xc = NULL;
int children=0;
int retval = -1;
int exist = 0;
yang_stmt *yn;
yang_stmt *yp;
yang_stmt *ymod;
yang_stmt *ypmod;
char *prefix = NULL;
if (xn == NULL || fn == NULL){
clicon_err(OE_XML, EINVAL, "xn or fn is NULL");
goto done;
}
if ((yn = xml_spec(xn)) != NULL){
if (yang_extension_value(yn, "hide-show", CLIXON_AUTOCLI_NS, &exist, NULL) < 0)
goto done;
if (exist)
goto ok;
/* Find out prefix if needed: topmost or new module a la API-PATH */
if (ys_real_module(yn, &ymod) < 0)
goto done;
if ((yp = yang_parent_get(yn)) != NULL &&
yp != ymod){
if (ys_real_module(yp, &ypmod) < 0)
goto done;
if (ypmod != ymod)
prefix = yang_argument_get(ymod);
}
else
prefix = yang_argument_get(ymod);
}
xc = NULL; /* count children (elements and bodies, not attributes) */
while ((xc = xml_child_each(xn, xc, -1)) != NULL)
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
children++;
if (!children){ /* If no children print line */
switch (xml_type(xn)){
case CX_BODY:
(*fn)(f, "%s;\n", xml_value(xn));
break;
case CX_ELMNT:
(*fn)(f, "%*s%s;\n", 4*level, "", xml_name(xn));
break;
default:
break;
}
goto ok;
}
(*fn)(f, "%*s", 4*level, "");
if (prefix)
(*fn)(f, "%s:", prefix);
(*fn)(f, "%s ", xml_name(xn));
if (!tleaf(xn))
(*fn)(f, "{\n");
xc = NULL;
while ((xc = xml_child_each(xn, xc, -1)) != NULL){
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
if (xml2txt(xc, fn, f, level+1) < 0)
break;
}
if (!tleaf(xn))
(*fn)(f, "%*s}\n", 4*level, "");
ok:
retval = 0;
done:
return retval;
}
/*! Parse a string containing text syntax and return an XML tree
*
* @param[in] str Input string containing JSON
* @param[in] rfc7951 Do sanity checks according to RFC 7951 JSON Encoding of Data Modeled with YANG
* @param[in] yb How to bind yang to XML top-level when parsing (if rfc7951)
* @param[in] yspec Yang specification (if rfc 7951)
* @param[out] xt XML top of tree typically w/o children on entry (but created)
* @param[out] xerr Reason for invalid returned as netconf err msg
*
* @see _xml_parse for XML variant
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec)
* @retval -1 Error with clicon_err called
*/
static int
_text_syntax_parse(char *str,
yang_bind yb,
yang_stmt *yspec,
cxobj *xt,
cxobj **xerr)
{
int retval = -1;
clixon_text_syntax_yacc ts = {0,};
int ret;
cxobj *x;
cbuf *cberr = NULL;
int failed = 0; /* yang assignment */
clicon_debug(1, "%s %d %s", __FUNCTION__, yb, str);
ts.ts_parse_string = str;
ts.ts_linenum = 1;
ts.ts_xtop = xt;
ts.ts_yspec = yspec;
if (clixon_text_syntax_parsel_init(&ts) < 0)
goto done;
if (clixon_text_syntax_parseparse(&ts) != 0) { /* yacc returns 1 on error */
clicon_log(LOG_NOTICE, "TEXT SYNTAX error: line %d", ts.ts_linenum);
if (clicon_errno == 0)
clicon_err(OE_JSON, 0, "TEXT SYNTAX parser error with no error code (should not happen)");
goto done;
}
x = NULL;
while ((x = xml_child_each(ts.ts_xtop, x, CX_ELMNT)) != NULL) {
/* Populate, ie associate xml nodes with yang specs
*/
switch (yb){
case YB_NONE:
break;
case YB_PARENT:
/* xt:n Has spec
* x: <a> <-- populate from parent
*/
if ((ret = xml_bind_yang0(x, YB_PARENT, NULL, xerr)) < 0)
goto done;
if (ret == 0)
failed++;
break;
case YB_MODULE_NEXT:
if ((ret = xml_bind_yang(x, YB_MODULE, yspec, xerr)) < 0)
goto done;
if (ret == 0)
failed++;
break;
case YB_MODULE:
/* xt:<top> nospec
* x: <a> <-- populate from modules
*/
if ((ret = xml_bind_yang0(x, YB_MODULE, yspec, xerr)) < 0)
goto done;
if (ret == 0)
failed++;
break;
case YB_RPC:
if ((ret = xml_bind_yang_rpc(x, yspec, xerr)) < 0)
goto done;
if (ret == 0){ /* Add message-id */
if (*xerr && clixon_xml_attr_copy(x, *xerr, "message-id") < 0)
goto done;
failed++;
}
break;
} /* switch */
}
if (failed)
goto fail;
/* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
not bound */
if (yb != YB_NONE)
if (xml_sort_recurse(xt) < 0)
goto done;
retval = 1;
done:
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
if (cberr)
cbuf_free(cberr);
clixon_text_syntax_parsel_exit(&ts);
return retval;
fail: /* invalid */
retval = 0;
goto done;
}
/*! Parse string containing TEXT syntax and return an XML tree
*
* @param[in] str String containing TEXT syntax
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Yang specification, mandatory to make module->xmlns translation
* @param[in,out] xt Top object, if not exists, on success it is created with name 'top'
* @param[out] xerr Reason for invalid returned as netconf err msg
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec) w xerr set
* @retval -1 Error with clicon_err called
*
* @code
* cxobj *x = NULL;
* if (clixon_text_syntax_parse_string(str, YB_MODULE, yspec, &x, &xerr) < 0)
* err;
* xml_free(x);
* @endcode
* @note you need to free the xml parse tree after use, using xml_free()
* @see clixon_text_syntax_parse_file From a file
*/
int
clixon_text_syntax_parse_string(char *str,
yang_bind yb,
yang_stmt *yspec,
cxobj **xt,
cxobj **xerr)
{
clicon_debug(1, "%s", __FUNCTION__);
if (xt==NULL){
clicon_err(OE_XML, EINVAL, "xt is NULL");
return -1;
}
if (*xt == NULL){
if ((*xt = xml_new("top", NULL, CX_ELMNT)) == NULL)
return -1;
}
return _text_syntax_parse(str, yb, yspec, *xt, xerr);
}
/*! Read a TEXT syntax definition from file and parse it into a parse-tree.
*
* File will be parsed as follows:
* (1) parsed according to TEXT syntax; # Only this check if yspec is NULL
* (2) sanity checked wrt yang
* (4) an xml parse tree will be returned
*
* @param[in] fp File descriptor to the TEXT syntax file
* @param[in] yb How to bind yang to XML top-level when parsing
* @param[in] yspec Yang specification, or NULL
* @param[in,out] xt Pointer to (XML) parse tree. If empty, create.
* @param[out] xerr Reason for invalid returned as netconf err msg
*
* @code
* cxobj *xt = NULL;
* if (clixon_text_syntax_parse_file(stdin, YB_MODULE, yspec, &xt) < 0)
* err;
* xml_free(xt);
* @endcode
* @note you need to free the xml parse tree after use, using xml_free()
* @note, If xt empty, a top-level symbol will be added so that <tree../> will be: <top><tree.../></tree></top>
* @note May block on file I/O
*
* @retval 1 OK and valid
* @retval 0 Invalid (only if yang spec) w xerr set
* @retval -1 Error with clicon_err called
*
* @see clixon_text_syntax_parse_string
*/
int
clixon_text_syntax_parse_file(FILE *fp,
yang_bind yb,
yang_stmt *yspec,
cxobj **xt,
cxobj **xerr)
{
int retval = -1;
int ret;
char *textbuf = NULL;
int textbuflen = BUFLEN; /* start size */
int oldtextbuflen;
char *ptr;
char ch;
int len = 0;
if (xt==NULL){
clicon_err(OE_XML, EINVAL, "xt is NULL");
return -1;
}
if ((textbuf = malloc(textbuflen)) == NULL){
clicon_err(OE_XML, errno, "malloc");
goto done;
}
memset(textbuf, 0, textbuflen);
ptr = textbuf;
while (1){
if ((ret = fread(&ch, 1, 1, fp)) < 0){
clicon_err(OE_XML, errno, "read");
break;
}
if (ret != 0)
textbuf[len++] = ch;
if (ret == 0){
if (*xt == NULL)
if ((*xt = xml_new(TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
goto done;
if (len){
if ((ret = _text_syntax_parse(ptr, yb, yspec, *xt, xerr)) < 0)
goto done;
if (ret == 0)
goto fail;
}
break;
}
if (len >= textbuflen-1){ /* Space: one for the null character */
oldtextbuflen = textbuflen;
textbuflen *= 2;
if ((textbuf = realloc(textbuf, textbuflen)) == NULL){
clicon_err(OE_XML, errno, "realloc");
goto done;
}
memset(textbuf+oldtextbuflen, 0, textbuflen-oldtextbuflen);
ptr = textbuf;
}
}
retval = 1;
done:
if (retval < 0 && *xt){
free(*xt);
*xt = NULL;
}
if (textbuf)
free(textbuf);
return retval;
fail:
retval = 0;
goto done;
}

View file

@ -0,0 +1,69 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* TEXT / curly-brace syntax parsing and translations
*/
#ifndef _CLIXON_TEXT_SYNTAX_PARSE_H_
#define _CLIXON_TEXT_SYNTAX_PARSE_H_
/*
* Types
*/
/*! XML parser yacc handler struct */
struct clixon_text_syntax_parse_yacc {
char *ts_parse_string; /* original (copy of) parse string */
int ts_linenum; /* Number of \n in parsed buffer */
void *ts_lexbuf; /* internal parse buffer from lex */
cxobj *ts_xtop; /* Vector of created top-level nodes (to know which are created) */
int ts_xlen; /* Length of ts_xvec */
int ts_lex_state; /* lex return state */
yang_stmt *ts_yspec; /* Yang spec */
};
typedef struct clixon_text_syntax_parse_yacc clixon_text_syntax_yacc;
/*
* Variables
*/
extern char *clixon_text_syntax_parsetext;
/*
* Prototypes
*/
int clixon_text_syntax_parsel_init(clixon_text_syntax_yacc *ya);
int clixon_text_syntax_parsel_exit(clixon_text_syntax_yacc *ya);
int clixon_text_syntax_parsel_linenr(void);
int clixon_text_syntax_parselex(void *);
int clixon_text_syntax_parseparse(void *);
#endif /* _CLIXON_TEXT_SYNTAX_PARSE_H_ */

View file

@ -0,0 +1,124 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* TEXT / curly-brace syntax parsing and translations
*/
%{
#include "clixon_config.h"
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "clixon_text_syntax_parse.tab.h" /* generated file */
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_text_syntax_parse.h"
/* Redefine main lex function so that you can send arguments to it: _ts is added to arg list */
#define YY_DECL int clixon_text_syntax_parselex(void *_ts)
/* Dont use input function (use user-buffer) */
#define YY_NO_INPUT
/* typecast macro */
#define _TS ((clixon_text_syntax_yacc *)_ts)
#undef clixon_xml_parsewrap
int clixon_text_syntax_parsewrap(void)
{
return 1;
}
/*
*/
%}
%s COMMENT
%%
<INITIAL>[ \t]*
<INITIAL>\n { _TS->ts_linenum++; }
<INITIAL>\r
<INITIAL><<EOF>> { return MY_EOF; }
<INITIAL># { _TS->ts_lex_state =INITIAL; BEGIN(COMMENT); }
<INITIAL>\{ { return *yytext; }
<INITIAL>\} { return *yytext; }
<INITIAL>\[ { return *yytext; }
<INITIAL>\] { return *yytext; }
<INITIAL>\; { return *yytext; }
<INITIAL>\: { return *yytext; }
<INITIAL>[^\n\r \t\[\]\{\}\;\:]+ {
clixon_text_syntax_parselval.string = strdup(yytext);
return TOKEN; }
<INITIAL>. { return -1; }
<COMMENT>\n { _TS->ts_linenum++; BEGIN(_TS->ts_lex_state);}
<COMMENT><<EOF>> { return MY_EOF; }
<COMMENT>[^\n]+
%%
/*! Initialize XML scanner.
*/
int
clixon_text_syntax_parsel_init(clixon_text_syntax_yacc *ts)
{
BEGIN(INITIAL);
ts->ts_lexbuf = yy_scan_string (ts->ts_parse_string);
if (0)
yyunput(0, ""); /* XXX: just to use unput to avoid warning */
return 0;
}
/*! Exit xml scanner */
int
clixon_text_syntax_parsel_exit(clixon_text_syntax_yacc *ts)
{
yy_delete_buffer(ts->ts_lexbuf);
clixon_text_syntax_parselex_destroy(); /* modern */
return 0;
}

View file

@ -0,0 +1,195 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* TEXT / curly-brace syntax parsing and translations
*/
%union {
char *string;
void *stack;
}
%token MY_EOF
%token <string> TOKEN
%type <stack> stmts
%type <stack> stmt
%type <stack> id
%type <string> value
%start top
%lex-param {void *_ts} /* Add this argument to parse() and lex() function */
%parse-param {void *_ts}
%{
/* typecast macro */
#define _TS ((clixon_text_syntax_yacc *)_ts)
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <stdlib.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_string.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_vec.h"
#include "clixon_data.h"
#include "clixon_text_syntax_parse.h"
/* Enable for debugging, steals some cycles otherwise */
#if 0
#define _PARSE_DEBUG(s) clicon_debug(1,(s))
#else
#define _PARSE_DEBUG(s)
#endif
void
clixon_text_syntax_parseerror(void *arg,
char *s)
{
clixon_text_syntax_yacc *ts = (clixon_text_syntax_yacc *)arg;
clicon_err(OE_XML, XMLPARSE_ERRNO, "text_syntax_parse: line %d: %s: at or before: %s",
ts->ts_linenum,
s,
clixon_text_syntax_parsetext);
return;
}
static int
text_add_value(cxobj *xn,
char *value)
{
int retval = -1;
cxobj *xb = NULL;
if ((xb = xml_new("body", xn, CX_BODY)) == NULL)
goto done;
if (xml_value_set(xb, value) < 0){
xml_free(xb);
goto done;
}
retval = 0;
done:
return retval;
}
static cxobj*
text_create_node(clixon_text_syntax_yacc *ts,
char *module,
char *name)
{
cxobj *xn;
yang_stmt *ymod;
char *ns;
if ((xn = xml_new(name, NULL, CX_ELMNT)) == NULL)
goto done;
if (module && ts->ts_yspec){
/* Silently ignore if module name not found */
if ((ymod = yang_find(ts->ts_yspec, Y_MODULE, NULL)) != NULL){
if ((ns = yang_find_mynamespace(ymod)) == NULL){
clicon_err(OE_YANG, 0, "No namespace");
goto done;
}
/* Set default namespace */
if (xmlns_set(xn, NULL, ns) < 0)
goto done;
}
}
done:
return xn;
}
%}
%%
top : stmt MY_EOF { _PARSE_DEBUG("top->stmt");
if (xml_addsub(_TS->ts_xtop, $1) < 0) YYERROR;
YYACCEPT; }
;
stmts : stmts stmt { _PARSE_DEBUG("stmts->stmts stmt");
if (clixon_xvec_append($1, $2) < 0) YYERROR;
$$ = $1;
}
| { _PARSE_DEBUG("stmts->stmt");
if (($$ = clixon_xvec_new()) == NULL) YYERROR;
}
;
stmt : id value ';' { _PARSE_DEBUG("stmt-> id value ;");
if (text_add_value($1, $2) < 0) YYERROR;
$$ = $1;
}
| id '{' stmts '}' { _PARSE_DEBUG("stmt-> id { stmts }");
if (clixon_child_xvec_append($1, $3) < 0) YYERROR;
clixon_xvec_free($3);
$$ = $1;
}
| id '[' values ']' { _PARSE_DEBUG("stmt-> id [ values ]"); }
;
id : TOKEN { _PARSE_DEBUG("id->TOKEN");
if (($$ = text_create_node(_TS, NULL, $1)) == NULL) YYERROR;;
}
| TOKEN ':' TOKEN { _PARSE_DEBUG("id->TOKEN : TOKEN");
if (($$ = text_create_node(_TS, $1, $3)) == NULL) YYERROR;;
}
;
values : values TOKEN { _PARSE_DEBUG("values->values TOKEN"); }
| { _PARSE_DEBUG("values->"); }
;
value : TOKEN { _PARSE_DEBUG("value->TOKEN"); $$ = $1; }
;
%%

View file

@ -936,7 +936,6 @@ xml_child_each(cxobj *xparent,
return xn;
}
/*! Extend child vector with one and insert xml node there
* @note does not do anything with child, you may need to set its parent, etc
* @see xml_child_insert_pos
@ -1038,6 +1037,33 @@ xml_childvec_get(cxobj *x)
return x->x_childvec;
}
/*! Given an XML object and a vector of children xvec, append the children to the object
*
* @param[in] x XML node
* @param[in] xv Clixon xml vector
* @retval 0 OK
* @retval -1 Error
* @note xv is not same type as x_childvec
*/
int
clixon_child_xvec_append(cxobj *xn,
clixon_xvec *xv)
{
int retval = -1;
cxobj *xc;
int i;
for (i=0; i<clixon_xvec_len(xv); i++){
xc = clixon_xvec_i(xv, i);
if (xml_addsub(xn, xc) < 0)
// if (xml_child_append(xn, xc) < 0)
goto done;
}
retval = 0;
done:
return retval;
}
/*! Create new xml node given a name and parent. Free with xml_free().
*
* @param[in] name Name of XML node
@ -1968,29 +1994,6 @@ xml_dup(cxobj *x0)
}
#if 1 /* XXX At some point migrate this code to the clixon_xml_vec.[ch] API */
/*! Copy XML vector from vec0 to vec1
* @param[in] vec0 Source XML tree vector
* @param[in] len0 Length of source XML tree vector
* @param[out] vec1 Destination XML tree vector
* @param[out] len1 Length of destination XML tree vector
*/
int
cxvec_dup(cxobj **vec0,
int len0,
cxobj ***vec1,
int *len1)
{
int retval = -1;
*len1 = len0;
if ((*vec1 = calloc(len0, sizeof(cxobj*))) == NULL)
goto done;
memcpy(*vec1, vec0, len0*sizeof(cxobj*));
retval = 0;
done:
return retval;
}
/*! Append a new xml tree to an existing xml vector last in the list
* @param[in] x XML tree (append this to vector)
* @param[in,out] vec XML tree vector
@ -2066,6 +2069,7 @@ cxvec_prepend(cxobj *x,
}
#endif
/*! Apply a function call recursively on all xml node children recursively
* Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
* each object found. The function is called with the xml node and an

View file

@ -107,110 +107,6 @@ isxmlns(cxobj *x)
return 0;
}
/*! x is element and has eactly one child which in turn has none
* @see child_type in clixon_json.c
*/
static int
tleaf(cxobj *x)
{
cxobj *xc;
if (xml_type(x) != CX_ELMNT)
return 0;
if (xml_child_nr_notype(x, CX_ATTR) != 1)
return 0;
/* From here exactly one noattr child, get it */
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) != CX_ATTR)
break;
if (xc == NULL)
return -1; /* n/a */
return (xml_child_nr_notype(xc, CX_ATTR) == 0);
}
/*! Translate XML to a "pseudo-code" textual format using a callback - internal function
* @param[in] f File to print to
* @param[in] x XML object to print
* @param[in] fn Callback to make print function
* @param[in] level print 4 spaces per level in front of each line
*/
static int
xml2txt_recurse(FILE *f,
cxobj *x,
clicon_output_cb *fn,
int level)
{
cxobj *xc = NULL;
int children=0;
int retval = -1;
if (f == NULL || x == NULL || fn == NULL){
clicon_err(OE_XML, EINVAL, "f, x or fn is NULL");
goto done;
}
xc = NULL; /* count children (elements and bodies, not attributes) */
while ((xc = xml_child_each(x, xc, -1)) != NULL)
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
children++;
if (!children){ /* If no children print line */
switch (xml_type(x)){
case CX_BODY:
(*fn)(f, "%s;\n", xml_value(x));
break;
case CX_ELMNT:
(*fn)(f, "%*s%s;\n", 4*level, "", xml_name(x));
break;
default:
break;
}
goto ok;
}
(*fn)(f, "%*s", 4*level, "");
(*fn)(f, "%s ", xml_name(x));
if (!tleaf(x))
(*fn)(f, "{\n");
xc = NULL;
while ((xc = xml_child_each(x, xc, -1)) != NULL){
if (xml_type(xc) == CX_ELMNT || xml_type(xc) == CX_BODY)
if (xml2txt_recurse(f, xc, fn, level+1) < 0)
break;
}
if (!tleaf(x))
(*fn)(f, "%*s}\n", 4*level, "");
ok:
retval = 0;
done:
return retval;
}
/*! Translate XML to a "pseudo-code" textual format using a callback
* @param[in] f File to print to
* @param[in] x XML object to print
* @param[in] fn Callback to make print function
*/
int
xml2txt_cb(FILE *f,
cxobj *x,
clicon_output_cb *fn)
{
return xml2txt_recurse(f, x, fn, 0);
}
/*! Translate XML to a "pseudo-code" textual format using stdio file
* @param[in] f File to print to
* @param[in] x XML object to print
* @param[in] level print 4 spaces per level in front of each line
* @see xml2txt_cb
*/
int
xml2txt(FILE *f,
cxobj *x,
int level)
{
return xml2txt_recurse(f, x, fprintf, level);
}
/*! Translate a single xml node to a cligen variable vector. Note not recursive
* @param[in] xt XML tree containing one top node
* @param[in] ys Yang spec containing type specification of top-node of xt

View file

@ -58,6 +58,9 @@ struct clixon_xml_parse_yacc {
};
typedef struct clixon_xml_parse_yacc clixon_xml_yacc;
/*
* Variables
*/
extern char *clixon_xml_parsetext;
/*

View file

@ -203,13 +203,16 @@ clixon_xvec_i(clixon_xvec *xv,
* Used in glue code between clixon_xvec code and cxobj **, size_t code, may go AWAY?
* @param[in] xv XML tree vector
* @param[out] xvec XML object vector
* @param[out] xlen Number of elements
* @param[out] xmax Length of allocated vector
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_xvec_extract(clixon_xvec *xv,
cxobj ***xvec,
int *xlen)
int *xlen,
int *xmax)
{
int retval = -1;
@ -219,6 +222,8 @@ clixon_xvec_extract(clixon_xvec *xv,
}
*xvec = xv->xv_vec;
*xlen = xv->xv_len;
if (xmax)
*xmax = xv->xv_max;
if (xv->xv_vec != NULL){
xv->xv_len = 0;
xv->xv_max = 0;

View file

@ -342,7 +342,7 @@ xpath_optimize_check(xpath_tree *xs,
if ((ret = xpath_list_optimize_fn(xs, xv, xvec)) < 0)
return -1;
if (ret == 1){
if (clixon_xvec_extract(xvec, xvec0, xlen0) < 0)
if (clixon_xvec_extract(xvec, xvec0, xlen0, NULL) < 0)
return -1;
clixon_xvec_free(xvec);
_optimize_hits++;

View file

@ -133,8 +133,8 @@ ret=$($clixon_cli -1 -f $cfg show config)
if [ $? -ne 0 ]; then
err 0 $r
fi
if [ "$ret" != "hello world;" ]; then
err "$ret" "hello world;"
if [ "$ret" != "clixon-hello:hello world;" ]; then
err "$ret" "clixon-hello:hello world;"
fi
new "netconf edit-config"

View file

@ -85,6 +85,7 @@ LIBDEPS += $(top_srcdir)/lib/src/$(CLIXON_LIB)
APPSRC = clixon_util_xml.c
APPSRC += clixon_util_xml_mod.c
APPSRC += clixon_util_json.c
APPSRC += clixon_util_text_syntax.c
APPSRC += clixon_util_yang.c
APPSRC += clixon_util_xpath.c
APPSRC += clixon_util_path.c
@ -152,6 +153,9 @@ clixon_util_validate: clixon_util_validate.c $(LIBDEPS)
clixon_util_dispatcher: clixon_util_dispatcher.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ -l clixon_backend -o $@ $(LIBS)
clixon_util_text_syntax: clixon_util_text_syntax.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ $(LIBS) -o $@
ifdef with_restconf
clixon_util_stream: clixon_util_stream.c $(LIBDEPS)
$(CC) $(INCLUDES) $(CPPFLAGS) @CFLAGS@ $(LDFLAGS) $^ $(LIBS) -lcurl -o $@

View file

@ -32,8 +32,7 @@
***** END LICENSE BLOCK *****
* JSON support functions.
* JSON syntax is according to:
* JSON utility command
* http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
*/

View file

@ -0,0 +1,169 @@
/*
*
***** BEGIN LICENSE BLOCK *****
Copyright (C) 2022 Olof Hagsand and Rubicon Communications, LLC (Netgate)
This file is part of CLIXON.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Alternatively, the contents of this file may be used under the terms of
the GNU General Public License Version 3 or later (the "GPL"),
in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the Apache License version 2 or the GPL.
***** END LICENSE BLOCK *****
* Text syntax utility command
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <stdint.h>
#include <syslog.h>
#include <signal.h>
/* cligen */
#include <cligen/cligen.h>
/* clixon */
#include "clixon/clixon.h"
/* Command line options passed to getopt(3) */
#define UTIL_TEXT_SYNTAX_OPTS "hD:f:tl:y:"
/*
* Text syntax parse and pretty print test program
* Example compile:
*/
static int
usage(char *argv0)
{
fprintf(stderr, "usage:%s [options] JSON as input on stdin\n"
"where options are\n"
"\t-h \t\tHelp\n"
"\t-D <level> \tDebug\n"
"\t-f <file>\tTEXT syntax input file (overrides stdin)\n"
"\t-t \t\tOutput as TEXT syntax (default is as XML)\n"
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr, std(o)ut (stderr is default)\n"
"\t-y <filename> \tyang filename to parse (must be stand-alone)\n" ,
argv0);
exit(0);
}
int
main(int argc,
char **argv)
{
int retval = -1;
cxobj *xt = NULL;
cxobj *xc;
cbuf *cb = cbuf_new();
int c;
int logdst = CLICON_LOG_STDERR;
int text_syntax_output = 0;
char *yang_filename = NULL;
yang_stmt *yspec = NULL;
cxobj *xerr = NULL; /* malloced must be freed */
int ret;
int dbg = 0;
char *input_filename = NULL;
FILE *fp = stdin; /* base file, stdin */
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, UTIL_TEXT_SYNTAX_OPTS)) != -1)
switch (c) {
case 'h':
usage(argv[0]);
break;
case 'D':
if (sscanf(optarg, "%d", &dbg) != 1)
usage(argv[0]);
break;
case 'f':
input_filename = optarg;
break;
case 't':
text_syntax_output++;
break;
case 'l': /* Log destination: s|e|o|f */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(argv[0]);
break;
case 'y':
yang_filename = optarg;
break;
default:
usage(argv[0]);
break;
}
clicon_log_init(__FILE__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clicon_debug_init(dbg, NULL);
if (yang_filename){
if ((yspec = yspec_new()) == NULL)
goto done;
if (yang_parse_filename(yang_filename, yspec) == NULL){
fprintf(stderr, "yang parse error %s\n", clicon_err_reason);
return -1;
}
}
if (input_filename){
if ((fp = fopen(input_filename, "r")) == NULL){
clicon_err(OE_YANG, errno, "open(%s)", input_filename);
goto done;
}
}
if ((ret = clixon_text_syntax_parse_file(fp, yspec?YB_MODULE:YB_NONE, yspec, &xt, &xerr)) < 0)
goto done;
if (ret == 0){
xml_print(stderr, xerr);
goto done;
}
xc = NULL;
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if (text_syntax_output)
xml2txt(xc, fprintf, stdout, 0);
else{
clicon_xml2cbuf(cb, xc, 0, 1, -1); /* print xml */
fprintf(stdout, "%s", cbuf_get(cb));
}
}
fflush(stdout);
retval = 0;
done:
if (yspec)
ys_free(yspec);
if (xt)
xml_free(xt);
if (cb)
cbuf_free(cb);
return retval;
}

View file

@ -67,7 +67,7 @@
#include "clixon/clixon.h"
/* Command line options passed to getopt(3) */
#define UTIL_XML_OPTS "hD:f:Jjl:pvoy:Y:t:T:u"
#define UTIL_XML_OPTS "hD:f:JjXl:pvoy:Y:t:T:u"
static int
validate_tree(clicon_handle h,
@ -118,6 +118,7 @@ usage(char *argv0)
"\t-f <file>\tXML input file (overrides stdin)\n"
"\t-J \t\tInput as JSON\n"
"\t-j \t\tOutput as JSON\n"
"\t-X \t\tOutput as TEXT \n"
"\t-l <s|e|o> \tLog on (s)yslog, std(e)rr, std(o)ut (stderr is default)\n"
"\t-o \t\tOutput the file\n"
"\t-v \t\tValidate the result in terms of Yang model (requires -y)\n"
@ -144,6 +145,7 @@ main(int argc,
int logdst = CLICON_LOG_STDERR;
int jsonin = 0;
int jsonout = 0;
int textout = 0;
char *input_filename = NULL;
char *top_input_filename = NULL;
char *yang_file_dir = NULL;
@ -196,6 +198,9 @@ main(int argc,
case 'j':
jsonout++;
break;
case 'X':
textout++;
break;
case 'l': /* Log destination: s|e|o|f */
if ((logdst = clicon_log_opt(optarg[0])) < 0)
usage(argv[0]);
@ -334,9 +339,16 @@ main(int argc,
if (validate_tree(h, xt, yspec) < 0)
goto done;
}
/* 4. Output data (xml/json) */
/* 4. Output data (xml/json/text) */
if (output){
if (jsonout)
if (textout){
xc = NULL;
while ((xc = xml_child_each(xt, xc, -1)) != NULL){
if (xml2txt(xc, fprintf, stdout, 0) < 0)
goto done;
}
}
else if (jsonout)
xml2json_cbuf(cb, xt, pretty, 1); /* print json */
else
clicon_xml2cbuf(cb, xt, 0, pretty, -1, 1); /* print xml */