json parser
This commit is contained in:
parent
20087932c5
commit
887d43428b
15 changed files with 830 additions and 209 deletions
|
|
@ -190,7 +190,7 @@ plugin_load (clicon_handle h,
|
|||
|
||||
initfun = dlsym(handle, PLUGIN_INIT);
|
||||
if ((error = (char*)dlerror()) != NULL) {
|
||||
clicon_err(OE_UNIX, 0, "dlsym: %s", error);
|
||||
clicon_err(OE_UNIX, 0, "dlsym: %s: %s", file, error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
Clixon yang routing example
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
0. Compile and run
|
||||
------------------
|
||||
cd example
|
||||
make && sudo make install
|
||||
clixon_cli -f /usr/local/etc/routing.conf
|
||||
|
||||
1. Setting data example using netconf
|
||||
-------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@
|
|||
#include <clixon/clixon_xml_map.h>
|
||||
#include <clixon/clixon_xml_db.h>
|
||||
#include <clixon/clixon_xsl.h>
|
||||
#include <clixon/clixon_json.h>
|
||||
#include <clixon/clixon_plugin.h>
|
||||
#include <clixon/clixon_plugin.h>
|
||||
|
||||
|
|
|
|||
34
lib/clixon/clixon_json.h
Normal file
34
lib/clixon/clixon_json.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
* JSON support functions.
|
||||
*/
|
||||
#ifndef _CLIXON_JSON_H
|
||||
#define _CLIXON_JSON_H
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int json_parse_str(char *str, cxobj **xt);
|
||||
|
||||
int xml2json_cbuf(cbuf *cb, cxobj *x, int level);
|
||||
int xml2json(FILE *f, cxobj *x, int level);
|
||||
|
||||
#endif /* _CLIXON_JSON_H */
|
||||
|
|
@ -108,7 +108,9 @@ int xml_print(FILE *f, cxobj *xn);
|
|||
int clicon_xml2file(FILE *f, cxobj *xn, int level, int prettyprint);
|
||||
int clicon_xml2cbuf(cbuf *xf, cxobj *xn, int level, int prettyprint);
|
||||
int clicon_xml_parse_file(int fd, cxobj **xml_top, char *endtag);
|
||||
int clicon_xml_parse_string(char **str, cxobj **xml_top);
|
||||
/* XXX obsolete */
|
||||
#define clicon_xml_parse_string(str, x) clicon_xml_parse_str((*str), x)
|
||||
int clicon_xml_parse_str(char *str, cxobj **xml_top);
|
||||
|
||||
int xml_copy(cxobj *x0, cxobj *x1);
|
||||
cxobj *xml_dup(cxobj *x0);
|
||||
|
|
|
|||
|
|
@ -41,8 +41,6 @@ enum {
|
|||
*/
|
||||
int xml2txt(FILE *f, cxobj *x, int level);
|
||||
int xml2cli(FILE *f, cxobj *x, char *prepend, enum genmodel_type gt, const char *label);
|
||||
int xml2json_cbuf(cbuf *cb, cxobj *x, int level);
|
||||
int xml2json(FILE *f, cxobj *x, int level);
|
||||
int xml_yang_validate(cxobj *xt, yang_stmt *ys) ;
|
||||
int xml2cvec(cxobj *xt, yang_stmt *ys, cvec **cvv0);
|
||||
int cvec2xml_1(cvec *cvv, char *toptag, cxobj *xp, cxobj **xt0);
|
||||
|
|
|
|||
|
|
@ -52,14 +52,15 @@ SRC = clixon_sig.c clixon_qdb.c clixon_log.c clixon_err.c clixon_event.c \
|
|||
clixon_chunk.c clixon_proc.c \
|
||||
clixon_string.c clixon_handle.c \
|
||||
clixon_xml.c clixon_xml_map.c clixon_file.c \
|
||||
clixon_json.c \
|
||||
clixon_yang.c clixon_yang_type.c \
|
||||
clixon_hash.c clixon_options.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_encode.c clixon_proto_client.c \
|
||||
clixon_xsl.c clixon_sha1.c clixon_xml_db.c clixon_xml_db_rpc.c
|
||||
|
||||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o
|
||||
# Logically, the below 4 should be in YACCOBJS?
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
lex.clixon_json_parse.o clixon_json_parse.tab.o
|
||||
|
||||
|
||||
# Generated src
|
||||
|
|
@ -81,11 +82,10 @@ clean:
|
|||
rm -f $(OBJS) $(MYLIB) $(MYLIBLINK) $(GENOBJS) $(GENSRC) *.core
|
||||
rm -f clixon_xml_parse.tab.[ch] clixon_xml_parse.yy.[co]
|
||||
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
|
||||
rm -f lex.clixon_yang_parse.c
|
||||
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
|
||||
rm -f lex.clixon_xml_parse.c
|
||||
# disabled when USE_DBSPEC_PT is disabled in clixon_config.h.in
|
||||
# rm -f clixon_dbspec.tab.[ch] clixon_dbspec.[co]
|
||||
# rm -f lex.clixon_dbspec.c
|
||||
rm -f lex.clixon_yang_parse.c
|
||||
rm -f lex.clixon_json_parse.c
|
||||
|
||||
#############################################################################
|
||||
# Implicit rules for lex and yacc.
|
||||
|
|
@ -111,7 +111,7 @@ clixon_xml_parse.tab.c clixon_xml_parse.tab.h: clixon_xml_parse.y
|
|||
lex.clixon_xml_parse.o : lex.clixon_xml_parse.c clixon_xml_parse.tab.h # special rule to for make clean to work
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
# clixon_yang parser
|
||||
# yang parser
|
||||
lex.clixon_yang_parse.c : clixon_yang_parse.l clixon_yang_parse.tab.h
|
||||
$(LEX) -Pclixon_yang_parse clixon_yang_parse.l # -d is debug
|
||||
|
||||
|
|
@ -123,6 +123,18 @@ clixon_yang_parse.tab.c clixon_yang_parse.tab.h: clixon_yang_parse.y
|
|||
lex.clixon_yang_parse.o : lex.clixon_yang_parse.c clixon_yang_parse.tab.h
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
# json parser
|
||||
lex.clixon_json_parse.c : clixon_json_parse.l clixon_json_parse.tab.h
|
||||
$(LEX) -Pclixon_json_parse clixon_json_parse.l # -d is debug
|
||||
|
||||
clixon_json_parse.tab.c clixon_json_parse.tab.h: clixon_json_parse.y
|
||||
$(YACC) -l -d -p clixon_json_parse clixon_json_parse.y # -t is debug
|
||||
mv y.tab.c clixon_json_parse.tab.c
|
||||
mv y.tab.h clixon_json_parse.tab.h
|
||||
|
||||
lex.clixon_json_parse.o : lex.clixon_json_parse.c clixon_json_parse.tab.h
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
|
||||
|
|
|
|||
304
lib/src/clixon_json.c
Normal file
304
lib/src/clixon_json.c
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
* For unit testing compile with -_MAIN:
|
||||
*
|
||||
* JSON support functions.
|
||||
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <fnmatch.h>
|
||||
#include <stdint.h>
|
||||
#include <syslog.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_xml.h"
|
||||
|
||||
#include "clixon_json.h"
|
||||
#include "clixon_json_parse.h"
|
||||
|
||||
|
||||
/*! x is element and has eactly one child which in turn has none
|
||||
* Clone from clixon_xml_map.c
|
||||
*/
|
||||
static int
|
||||
tleaf(cxobj *x)
|
||||
{
|
||||
cxobj *c;
|
||||
|
||||
if (xml_type(x) != CX_ELMNT)
|
||||
return 0;
|
||||
if (xml_child_nr(x) != 1)
|
||||
return 0;
|
||||
c = xml_child_i(x, 0);
|
||||
return (xml_child_nr(c) == 0);
|
||||
}
|
||||
|
||||
|
||||
enum list_element_type{
|
||||
LIST_NO,
|
||||
LIST_FIRST,
|
||||
LIST_MIDDLE,
|
||||
LIST_LAST
|
||||
};
|
||||
static enum list_element_type
|
||||
list_eval(cxobj *x)
|
||||
{
|
||||
enum list_element_type list = LIST_NO;
|
||||
cxobj *xp;
|
||||
cxobj *xprev=NULL;
|
||||
cxobj *xnext=NULL;
|
||||
int i;
|
||||
int eqprev=0;
|
||||
int eqnext=0;
|
||||
|
||||
assert(xml_type(x)==CX_ELMNT);
|
||||
if ((xp = xml_parent(x)) == NULL)
|
||||
goto done;
|
||||
for (i=0; i<xml_child_nr(xp); i++)
|
||||
if (x == xml_child_i(xp, i))
|
||||
break;
|
||||
assert(i<xml_child_nr(xp));
|
||||
if (i < xml_child_nr(xp)-1){
|
||||
xnext = xml_child_i(xp, i+1);
|
||||
if (xml_type(xnext)==CX_ELMNT &&
|
||||
strcmp(xml_name(x),xml_name(xnext))==0)
|
||||
eqnext++;
|
||||
}
|
||||
if (i){
|
||||
xprev = xml_child_i(xp, i-1);
|
||||
if (xml_type(xprev)==CX_ELMNT &&
|
||||
strcmp(xml_name(x),xml_name(xprev))==0)
|
||||
eqprev++;
|
||||
}
|
||||
if (eqprev && eqnext)
|
||||
list = LIST_MIDDLE;
|
||||
else if (eqprev)
|
||||
list = LIST_LAST;
|
||||
else if (eqnext)
|
||||
list = LIST_FIRST;
|
||||
else
|
||||
list = LIST_NO;
|
||||
done:
|
||||
return list;
|
||||
}
|
||||
|
||||
/*!
|
||||
* List only if adjacent,
|
||||
* ie <a>1</a><a>2</a><b>3</b> -> {"a":[1,2],"b":3}
|
||||
* ie <a>1</a><b>3</b><a>2</a> -> {"a":1,"b":3,"a":2}
|
||||
*/
|
||||
static int
|
||||
xml2json1_cbuf(cbuf *cb,
|
||||
cxobj *x)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xc;
|
||||
enum list_element_type list;
|
||||
|
||||
switch(xml_type(x)){
|
||||
case CX_BODY:
|
||||
fprintf(stderr, "%s body %s\n", __FUNCTION__, xml_value(x));
|
||||
if (xml_value(x))
|
||||
cprintf(cb, "\"%s\"", xml_value(x));
|
||||
else
|
||||
cprintf(cb, "null");
|
||||
break;
|
||||
case CX_ELMNT:
|
||||
list = list_eval(x);
|
||||
fprintf(stderr, "%s element %s\n", __FUNCTION__, xml_name(x));
|
||||
switch (list){
|
||||
case LIST_NO:
|
||||
cprintf(cb, "\"%s\":", xml_name(x));
|
||||
if (!tleaf(x))
|
||||
cprintf(cb, "{");
|
||||
break;
|
||||
case LIST_FIRST:
|
||||
cprintf(cb, "\"%s\":[", xml_name(x));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (i=0; i<xml_child_nr(x); i++){
|
||||
xc = xml_child_i(x, i);
|
||||
if (xml2json1_cbuf(cb, xc) < 0)
|
||||
goto done;
|
||||
if (i<xml_child_nr(x)-1)
|
||||
cprintf(cb, ",");
|
||||
}
|
||||
switch (list){
|
||||
case LIST_NO:
|
||||
if (!tleaf(x))
|
||||
cprintf(cb, "}");
|
||||
break;
|
||||
case LIST_LAST:
|
||||
cprintf(cb, "]");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Translate an XML tree to JSON in a CLIgen buffer
|
||||
*
|
||||
* @param[in,out] cb Cligen buffer to write to
|
||||
* @param[in] x XML tree to translate from
|
||||
* @param[in] level Indentation level
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @code
|
||||
* cbuf *cb;
|
||||
* cb = cbuf_new();
|
||||
* if (xml2json_cbuf(cb, xn, 0, 1) < 0)
|
||||
* goto err;
|
||||
* cbuf_free(cb);
|
||||
* @endcode
|
||||
* See also xml2json
|
||||
*/
|
||||
int
|
||||
xml2json_cbuf(cbuf *cb,
|
||||
cxobj *x,
|
||||
int level)
|
||||
{
|
||||
int retval = 1;
|
||||
|
||||
cprintf(cb, "{");
|
||||
if (xml2json1_cbuf(cb, x) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "}\n");
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from xml tree to JSON and print to file
|
||||
* @param[in] f File to print to
|
||||
* @param[in] x XML tree to translate from
|
||||
* @param[in] level Indentation level
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @code
|
||||
* if (xml2json(stderr, xn, 0) < 0)
|
||||
* goto err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml2json(FILE *f,
|
||||
cxobj *x,
|
||||
int level)
|
||||
{
|
||||
int retval = 1;
|
||||
cbuf *cb;
|
||||
|
||||
if ((cb = cbuf_new()) ==NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml2json_cbuf(cb, x, level) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse a string containing JSON and return an XML tree
|
||||
* @param[in] str Input string containing JSON
|
||||
* @param[in] name Log string, typically filename
|
||||
* @param[out] xt XML top of tree typically w/o children on entry (but created)
|
||||
*/
|
||||
static int
|
||||
json_parse(char *str,
|
||||
const char *name,
|
||||
cxobj *xt)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_json_yacc_arg jy = {0,};
|
||||
|
||||
jy.jy_parse_string = str;
|
||||
jy.jy_name = name;
|
||||
jy.jy_linenum = 1;
|
||||
jy.jy_current = xt;
|
||||
|
||||
if (json_scan_init(&jy) < 0)
|
||||
goto done;
|
||||
if (json_parse_init(&jy) < 0)
|
||||
goto done;
|
||||
if (clixon_json_parseparse(&jy) != 0) { /* yacc returns 1 on error */
|
||||
clicon_log(LOG_NOTICE, "JSON error: %s on line %d", name, jy.jy_linenum);
|
||||
if (clicon_errno == 0)
|
||||
clicon_err(OE_XML, 0, "JSON parser error with no error code (should not happen)");
|
||||
goto done;
|
||||
}
|
||||
if (jy.jy_current)
|
||||
xml_print(stdout, jy.jy_current);
|
||||
done:
|
||||
json_parse_exit(&jy);
|
||||
json_scan_exit(&jy);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Parse string containing JSON and return an XML tree
|
||||
*
|
||||
* @param[in] str String containing JSON
|
||||
* @param[out] xt On success a top of XML parse tree is created with name 'top'
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error with clicon_err called
|
||||
*
|
||||
* @code
|
||||
* cxobj *cx = NULL;
|
||||
* if (json_parse_str(str, &cx) < 0)
|
||||
* err;
|
||||
* xml_free(cx);
|
||||
* @endcode
|
||||
* @note you need to free the xml parse tree after use, using xml_free()
|
||||
*/
|
||||
int
|
||||
json_parse_str(char *str,
|
||||
cxobj **xt)
|
||||
{
|
||||
if ((*xt = xml_new("top", NULL)) == NULL)
|
||||
return -1;
|
||||
return json_parse(str, "", *xt);
|
||||
}
|
||||
|
||||
55
lib/src/clixon_json_parse.h
Normal file
55
lib/src/clixon_json_parse.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
#ifndef _CLIXON_JSON_PARSE_H_
|
||||
#define _CLIXON_JSON_PARSE_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
|
||||
struct clicon_json_yacc_arg{ /* XXX: mostly unrelevant */
|
||||
const char *jy_name; /* Name of syntax (for error string) */
|
||||
int jy_linenum; /* Number of \n in parsed buffer */
|
||||
char *jy_parse_string; /* original (copy of) parse string */
|
||||
void *jy_lexbuf; /* internal parse buffer from lex */
|
||||
cxobj *jy_current;
|
||||
};
|
||||
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
extern char *clixon_json_parsetext;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int json_scan_init(struct clicon_json_yacc_arg *jy);
|
||||
int json_scan_exit(struct clicon_json_yacc_arg *jy);
|
||||
|
||||
int json_parse_init(struct clicon_json_yacc_arg *jy);
|
||||
int json_parse_exit(struct clicon_json_yacc_arg *jy);
|
||||
|
||||
int clixon_json_parselex(void *);
|
||||
int clixon_json_parseparse(void *);
|
||||
void clixon_json_parseerror(void *, char*);
|
||||
|
||||
#endif /* _CLIXON_JSON_PARSE_H_ */
|
||||
128
lib/src/clixon_json_parse.l
Normal file
128
lib/src/clixon_json_parse.l
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include "clixon_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "clixon_json_parse.tab.h" /* generated */
|
||||
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_json_parse.h"
|
||||
|
||||
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
|
||||
#define YY_DECL int clixon_json_parselex(void *_yy)
|
||||
|
||||
/* Dont use input function (use user-buffer) */
|
||||
#define YY_NO_INPUT
|
||||
|
||||
/* typecast macro */
|
||||
#define _JY ((struct clicon_json_yacc_arg *)_yy)
|
||||
|
||||
#define MAXBUF 4*4*64*1024
|
||||
|
||||
#undef clixon_json_parsewrap
|
||||
int
|
||||
clixon_json_parsewrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
%}
|
||||
|
||||
%x START
|
||||
%s STRING
|
||||
%s ESCAPE
|
||||
|
||||
%%
|
||||
<START>[ \t]
|
||||
<START>\n { _JY->jy_linenum++; }
|
||||
<START><<EOF>> { return J_EOF; }
|
||||
<START>\{ { return *yytext; }
|
||||
<START>\} { return *yytext; }
|
||||
<START>\[ { return *yytext; }
|
||||
<START>\] { return *yytext; }
|
||||
<START>\: { return *yytext; }
|
||||
<START>\, { return *yytext; }
|
||||
<START>\" { BEGIN(STRING); return J_DQ; }
|
||||
<START>null { return J_NULL; }
|
||||
<START>false { return J_FALSE; }
|
||||
<START>true { return J_TRUE; }
|
||||
<START>[-+]?[0-9]+ { clixon_json_parselval.string = strdup(yytext);
|
||||
return J_NUMBER;}
|
||||
<START>. { return -1; }
|
||||
<STRING>\" { BEGIN(START); return J_DQ; }
|
||||
<STRING>\\ { BEGIN(ESCAPE); }
|
||||
<STRING>\n { _JY->jy_linenum++;
|
||||
clixon_json_parselval.string = strdup(yytext);
|
||||
return J_CHAR;}
|
||||
<STRING>. { clixon_json_parselval.string = strdup(yytext);
|
||||
return J_CHAR;}
|
||||
<ESCAPE>. { BEGIN(STRING);
|
||||
clixon_json_parselval.string = strdup(yytext);
|
||||
return J_CHAR; }
|
||||
|
||||
%%
|
||||
|
||||
|
||||
/*! Initialize scanner.
|
||||
*/
|
||||
int
|
||||
json_scan_init(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
BEGIN(START);
|
||||
jy->jy_lexbuf = yy_scan_string (jy->jy_parse_string);
|
||||
#if 1 /* XXX: just to use unput to avoid warning */
|
||||
if (0)
|
||||
yyunput(0, "");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free buffers
|
||||
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
|
||||
*/
|
||||
int
|
||||
json_scan_exit(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
yy_delete_buffer(jy->jy_lexbuf);
|
||||
#if defined(YY_FLEX_SUBMINOR_VERSION) && YY_FLEX_SUBMINOR_VERSION >= 9
|
||||
clixon_json_parselex_destroy(); /* modern */
|
||||
#else
|
||||
yy_init = 1; /* This does not quite free all buffers */
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
264
lib/src/clixon_json_parse.y
Normal file
264
lib/src/clixon_json_parse.y
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
*
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
CLIXON is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
CLIXON is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with CLIXON; see the file LICENSE. If not, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
* JSON Parser
|
||||
* From http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
||||
|
||||
Structural tokens:
|
||||
[ left square bracket
|
||||
{ left curly bracket
|
||||
] right square bracket
|
||||
} right curly bracket
|
||||
: colon
|
||||
, comma
|
||||
|
||||
Literal name tokens:
|
||||
true
|
||||
false
|
||||
null
|
||||
|
||||
A JSON value is an object, array, number, string, true, false, or null
|
||||
|
||||
value ::= object |
|
||||
array |
|
||||
number |
|
||||
string |
|
||||
'true' |
|
||||
'false' |
|
||||
'null' ;
|
||||
|
||||
object ::= '{' [objlist] '}';
|
||||
objlist ::= pair [',' objlist];
|
||||
pair ::= string ':' value;
|
||||
|
||||
array ::= '[' [vallist] ']';
|
||||
vallist ::= value [',' vallist];
|
||||
|
||||
XML translation:
|
||||
<a>34</a> <--> { "a": "34" }
|
||||
Easiest if top-object is single xml-tree <--> single object
|
||||
JSON lists are more difficult to translate since they inbtroduce a top
|
||||
object.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
%start json
|
||||
|
||||
%union {
|
||||
int intval;
|
||||
char *string;
|
||||
}
|
||||
|
||||
%token <string> J_FALSE
|
||||
%token <string> J_TRUE
|
||||
%token <string> J_NULL
|
||||
%token <string> J_EOF
|
||||
%token <string> J_DQ
|
||||
%token <string> J_CHAR
|
||||
%token <string> J_NUMBER
|
||||
|
||||
%type <string> string
|
||||
%type <string> ustring
|
||||
%type <string> number
|
||||
|
||||
%lex-param {void *_jy} /* Add this argument to parse() and lex() function */
|
||||
%parse-param {void *_jy}
|
||||
|
||||
%{
|
||||
/* Here starts user C-code */
|
||||
|
||||
/* typecast macro */
|
||||
#define _JY ((struct clicon_json_yacc_arg *)_jy)
|
||||
|
||||
#define _YYERROR(msg) {clicon_debug(2, "YYERROR %s '%s' %d", (msg), clixon_json_parsetext, _JY->jy_linenum); YYERROR;}
|
||||
|
||||
/* add _yy to error paramaters */
|
||||
#define YY_(msgid) msgid
|
||||
|
||||
#include "clixon_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <cligen/cligen.h>
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
#include "clixon_json_parse.h"
|
||||
|
||||
extern int clixon_json_parseget_lineno (void);
|
||||
|
||||
/*
|
||||
also called from yacc generated code *
|
||||
*/
|
||||
|
||||
void
|
||||
clixon_json_parseerror(void *_jy, char *s)
|
||||
{
|
||||
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
|
||||
_JY->jy_name,
|
||||
_JY->jy_linenum ,
|
||||
s,
|
||||
clixon_json_parsetext);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
json_parse_init(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
// clicon_debug_init(2, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
json_parse_exit(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
json_current_new(struct clicon_json_yacc_arg *jy,
|
||||
char *name)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xn;
|
||||
|
||||
clicon_debug(2, "%s", __FUNCTION__);
|
||||
if ((xn = xml_new(name, jy->jy_current)) == NULL)
|
||||
goto done;
|
||||
jy->jy_current = xn;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
json_current_pop(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
if (jy->jy_current)
|
||||
jy->jy_current = xml_parent(jy->jy_current);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
json_current_clone(struct clicon_json_yacc_arg *jy)
|
||||
{
|
||||
cxobj *xn;
|
||||
|
||||
assert(xn = jy->jy_current);
|
||||
json_current_pop(jy);
|
||||
if (jy->jy_current)
|
||||
json_current_new(jy, xml_name(xn));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
json_current_body(struct clicon_json_yacc_arg *jy,
|
||||
char *value)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xn;
|
||||
|
||||
clicon_debug(2, "%s", __FUNCTION__);
|
||||
if ((xn = xml_new("body", jy->jy_current)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xn, CX_BODY);
|
||||
if (value && xml_value_append(xn, value)==NULL)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/* top: json -> value is also possible */
|
||||
json : object J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
|
||||
;
|
||||
|
||||
value : J_TRUE { json_current_body(_JY, "true");}
|
||||
| J_FALSE { json_current_body(_JY, "false");}
|
||||
| J_NULL { json_current_body(_JY, NULL);}
|
||||
| object
|
||||
| array
|
||||
| number { json_current_body(_JY, $1); free($1);}
|
||||
| string { json_current_body(_JY, $1); free($1);}
|
||||
|
||||
;
|
||||
|
||||
object : '{' '}'
|
||||
| '{' objlist '}'
|
||||
;
|
||||
|
||||
objlist : pair
|
||||
| objlist ',' pair
|
||||
;
|
||||
|
||||
pair : string { json_current_new(_JY, $1);free($1);} ':'
|
||||
value { json_current_pop(_JY);}
|
||||
;
|
||||
|
||||
array : '[' ']'
|
||||
| '[' valuelist ']'
|
||||
;
|
||||
|
||||
valuelist : value
|
||||
| valuelist { json_current_clone(_JY);} ',' value
|
||||
;
|
||||
|
||||
/* quoted string */
|
||||
string : J_DQ ustring J_DQ { $$=$2; }
|
||||
;
|
||||
|
||||
/* unquoted string */
|
||||
ustring : ustring J_CHAR
|
||||
{
|
||||
int len = strlen($1);
|
||||
$$ = realloc($1, len+strlen($2) + 1);
|
||||
sprintf($$+len, "%s", $2);
|
||||
free($2);
|
||||
}
|
||||
| J_CHAR
|
||||
{$$=$1;}
|
||||
;
|
||||
|
||||
number : J_NUMBER { $$ = $1; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
|
@ -681,8 +681,6 @@ xml_rm(cxobj *xc)
|
|||
* @param[in] xp xml parent node. Will be deleted
|
||||
* @param[in] i Child nr in parent child vector
|
||||
* @param[out] xcp xml child node. New root
|
||||
* @code
|
||||
* @endcode
|
||||
* @see xml_child_rm
|
||||
*/
|
||||
int
|
||||
|
|
@ -920,13 +918,13 @@ clicon_xml2cbuf(cbuf *cb,
|
|||
* @see clicon_xml_parse_file clicon_xml_parse_string
|
||||
*/
|
||||
static int
|
||||
xml_parse(char **str,
|
||||
xml_parse(char *str,
|
||||
cxobj *x_up)
|
||||
{
|
||||
int retval = -1;
|
||||
struct xml_parse_yacc_arg ya = {0,};
|
||||
|
||||
if ((ya.ya_parse_string = strdup(*str)) == NULL){
|
||||
if ((ya.ya_parse_string = strdup(str)) == NULL){
|
||||
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -972,7 +970,7 @@ FSM(char *tag,
|
|||
* clicon_xml_parse_file(0, &xt, "</clicon>");
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* * @see clicon_xml_parse_string
|
||||
* * @see clicon_xml_parse_str
|
||||
* Note, you need to free the xml parse tree after use, using xml_free()
|
||||
* Note, xt will add a top-level symbol called "top" meaning that <tree../> will look as:
|
||||
* <top><tree.../></tree>
|
||||
|
|
@ -1019,7 +1017,7 @@ clicon_xml_parse_file(int fd,
|
|||
state = 0;
|
||||
if ((*cx = xml_new("top", NULL)) == NULL)
|
||||
break;
|
||||
if (xml_parse(&ptr, *cx) < 0)
|
||||
if (xml_parse(ptr, *cx) < 0)
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
|
|
@ -1041,21 +1039,16 @@ clicon_xml_parse_file(int fd,
|
|||
|
||||
/*! Read an XML definition from string and parse it into a parse-tree.
|
||||
*
|
||||
* @param[in] str Pointer to string containing XML definition. NOTE: destructively
|
||||
* modified. This means if str is malloced, you need to make a copy
|
||||
* of str before use and free that.
|
||||
* @param[out] xml_top Top of XML parse tree. Will add extra top element called 'top'.
|
||||
* @param[in] str Pointer to string containing XML definition.
|
||||
* @param[out] xml_top Top of XML parse tree. Will add extra top element called 'top'.
|
||||
* you must free it after use, using xml_free()
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error with clicon_err called
|
||||
*
|
||||
* @code
|
||||
* cxobj *cx = NULL;
|
||||
* str = strdup(...);
|
||||
* str0 = str;
|
||||
* if (clicon_xml_parse_string(&str0, &cx) < 0)
|
||||
* if (clicon_xml_parse_str(str, &cx) < 0)
|
||||
* err;
|
||||
* free(str0);
|
||||
* xml_free(cx);
|
||||
* @endcode
|
||||
* @see clicon_xml_parse_file
|
||||
|
|
@ -1063,8 +1056,8 @@ clicon_xml_parse_file(int fd,
|
|||
* Update: with yacc parser I dont think it changes,....
|
||||
*/
|
||||
int
|
||||
clicon_xml_parse_string(char **str,
|
||||
cxobj **cxtop)
|
||||
clicon_xml_parse_str(char *str,
|
||||
cxobj **cxtop)
|
||||
{
|
||||
if ((*cxtop = xml_new("top", NULL)) == NULL)
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ xmldb_get_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (vector){
|
||||
i=0;
|
||||
|
|
@ -319,7 +319,7 @@ xmldb_copy_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
|
|
@ -361,7 +361,7 @@ xmldb_lock_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
|
|
@ -403,7 +403,7 @@ xmldb_unlock_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 0;
|
||||
|
|
@ -443,7 +443,7 @@ xmldb_islocked_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//unlocked"))
|
||||
retval = 0;
|
||||
|
|
@ -486,7 +486,7 @@ xmldb_exists_rpc(clicon_handle h,
|
|||
cbuf_len(cb)+1,
|
||||
rb, &retlen) < 0)
|
||||
goto done;
|
||||
if (clicon_xml_parse_string(&rb, &xt) < 0)
|
||||
if (clicon_xml_parse_str(rb, &xt) < 0)
|
||||
goto done;
|
||||
if (xpath_first(xt, "//ok"))
|
||||
retval = 1;
|
||||
|
|
|
|||
|
|
@ -262,164 +262,6 @@ xml2cli(FILE *f,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Internal function to translate from xml tree to JSON
|
||||
* @param[in,out] cb Cligen buffer to write to
|
||||
* @param[in] x XML tree to translate from
|
||||
* @param[in] level Indentation level
|
||||
* @param[in] eq
|
||||
* @param[in] comma
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* XXX ugly code could be cleaned up
|
||||
*/
|
||||
static int
|
||||
xml2json1_cbuf(cbuf *cb,
|
||||
cxobj *x,
|
||||
int level,
|
||||
int eq,
|
||||
int comma)
|
||||
{
|
||||
cxobj *xe = NULL;
|
||||
cxobj *x1;
|
||||
int retval = -1;
|
||||
int level1 = level+1;
|
||||
int level2 = level+2;
|
||||
int i;
|
||||
int n;
|
||||
int eq1;
|
||||
|
||||
switch(xml_type(x)){
|
||||
case CX_BODY:
|
||||
cprintf(cb, "\"%s\"", xml_value(x));
|
||||
break;
|
||||
case CX_ELMNT:
|
||||
if (eq == 2)
|
||||
cprintf(cb, "%*s", 2*level2, "");
|
||||
else{
|
||||
cprintf(cb, "%*s", 2*level1, "");
|
||||
cprintf(cb, "\"%s\": ", xml_name(x));
|
||||
}
|
||||
if (xml_body(x)!=NULL){
|
||||
if (eq==1){
|
||||
cprintf(cb, "[\n");
|
||||
cprintf(cb, "%*s", 2*level2, "");
|
||||
}
|
||||
}
|
||||
else {
|
||||
cprintf(cb, "{\n");
|
||||
}
|
||||
xe = NULL;
|
||||
n = xml_child_nr(x);
|
||||
eq1 = 0;
|
||||
for (i=0; i<n; i++){
|
||||
xe = xml_child_i(x, i);
|
||||
if (xml_body(xe)!=NULL){
|
||||
if ((x1 = xml_child_i(x, i+1)) != NULL){
|
||||
if (xml_body(x1) && strcmp(xml_name(xe), xml_name(x1)) == 0){
|
||||
if (!eq1)
|
||||
eq1 = 1;
|
||||
}
|
||||
else
|
||||
if (eq1)
|
||||
eq1 = 2; /* last */
|
||||
}
|
||||
}
|
||||
if (xml2json1_cbuf(cb, xe, level1, eq1, (i+1<n)) < 0)
|
||||
goto done;
|
||||
if (xml_body(xe)!=NULL){
|
||||
if (eq1 == 2){
|
||||
cprintf(cb, "\n");
|
||||
cprintf(cb, "%*s", 2*level2, "");
|
||||
cprintf(cb, "]");
|
||||
}
|
||||
if (i+1<n)
|
||||
cprintf(cb, ",");
|
||||
cprintf(cb, "\n");
|
||||
}
|
||||
if (eq1==2)
|
||||
eq1 = 0;
|
||||
}
|
||||
if (tleaf(x)){
|
||||
}
|
||||
else{
|
||||
cprintf(cb, "%*s}", 2*level1, "");
|
||||
if (comma)
|
||||
cprintf(cb, ",");
|
||||
cprintf(cb, "\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// cprintf(cb, "%*s", 2*level, "");
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate an XML tree to JSON in a CLIgen buffer
|
||||
*
|
||||
* @param[in,out] cb Cligen buffer to write to
|
||||
* @param[in] x XML tree to translate from
|
||||
* @param[in] level Indentation level
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @code
|
||||
* cbuf *cb;
|
||||
* cb = cbuf_new();
|
||||
* if (xml2json_cbuf(cb, xn, 0, 1) < 0)
|
||||
* goto err;
|
||||
* cbuf_free(cb);
|
||||
* @endcode
|
||||
* See also xml2json
|
||||
*/
|
||||
int
|
||||
xml2json_cbuf(cbuf *cb,
|
||||
cxobj *x,
|
||||
int level)
|
||||
{
|
||||
int retval = 1;
|
||||
|
||||
cprintf(cb, "{\n");
|
||||
if (xml2json1_cbuf(cb, x, level, 0, 0) < 0)
|
||||
goto done;
|
||||
cprintf(cb, "}\n");
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate from xml tree to JSON and print to file
|
||||
* @param[in] f File to print to
|
||||
* @param[in] x XML tree to translate from
|
||||
* @param[in] level Indentation level
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*
|
||||
* @code
|
||||
* if (xml2json(stderr, xn, 0) < 0)
|
||||
* goto err;
|
||||
* @endcode
|
||||
*/
|
||||
int
|
||||
xml2json(FILE *f,
|
||||
cxobj *x,
|
||||
int level)
|
||||
{
|
||||
int retval = 1;
|
||||
cbuf *cb;
|
||||
|
||||
if ((cb = cbuf_new()) ==NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (xml2json_cbuf(cb, x, level) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Validate a single XML node with yang specification
|
||||
* - If no value and mandatory flag set in spec, report error.
|
||||
|
|
|
|||
|
|
@ -63,24 +63,6 @@ clixon_yang_parsewrap(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
/* like strdup but strip \:s */
|
||||
static char *
|
||||
stripdup(char *s0)
|
||||
{
|
||||
char *s1;
|
||||
char *s;
|
||||
|
||||
if ((s1 = strdup(s0)) == NULL){
|
||||
fprintf(stderr, "%s: strdup: %s\n", __FUNCTION__, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
while ((s = index(s1, '\\')) != NULL)
|
||||
memmove(s, s+1, strlen(s));
|
||||
return s1;
|
||||
}
|
||||
#endif /* notused */
|
||||
|
||||
/*
|
||||
statement = keyword [argument] (";" / "{" *statement "}")
|
||||
The argument is a string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue