* 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:
parent
43a57dad79
commit
2ece0b8f51
29 changed files with 1140 additions and 238 deletions
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -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,7 +60,8 @@ 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
47
lib/clixon/clixon_text_syntax.h
Normal file
47
lib/clixon/clixon_text_syntax.h
Normal 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 */
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
421
lib/src/clixon_text_syntax.c
Normal file
421
lib/src/clixon_text_syntax.c
Normal 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;
|
||||
}
|
||||
69
lib/src/clixon_text_syntax_parse.h
Normal file
69
lib/src/clixon_text_syntax_parse.h
Normal 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_ */
|
||||
124
lib/src/clixon_text_syntax_parse.l
Normal file
124
lib/src/clixon_text_syntax_parse.l
Normal 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;
|
||||
}
|
||||
195
lib/src/clixon_text_syntax_parse.y
Normal file
195
lib/src/clixon_text_syntax_parse.y
Normal 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; }
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ struct clixon_xml_parse_yacc {
|
|||
};
|
||||
typedef struct clixon_xml_parse_yacc clixon_xml_yacc;
|
||||
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
extern char *clixon_xml_parsetext;
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 $@
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
||||
|
|
|
|||
169
util/clixon_util_text_syntax.c
Normal file
169
util/clixon_util_text_syntax.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue