1 /*
2  *
3   ***** BEGIN LICENSE BLOCK *****
4 
5   Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
6   Copyright (C) 2017-2019 Olof Hagsand
7   Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
8 
9   This file is part of CLIXON.
10 
11   Licensed under the Apache License, Version 2.0 (the "License");
12   you may not use this file except in compliance with the License.
13   You may obtain a copy of the License at
14 
15     http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22 
23   Alternatively, the contents of this file may be used under the terms of
24   the GNU General Public License Version 3 or later (the "GPL"),
25   in which case the provisions of the GPL are applicable instead
26   of those above. If you wish to allow use of your version of this file only
27   under the terms of the GPL, and not to allow others to
28   use your version of this file under the terms of Apache License version 2,
29   indicate your decision by deleting the provisions above and replace them with
30   the  notice and other provisions required by the GPL. If you do not delete
31   the provisions above, a recipient may use your version of this file under
32   the terms of any one of the Apache License version 2 or the GPL.
33 
34   ***** END LICENSE BLOCK *****
35 
36  * Clixon XML object parse and print functions
37  * @see https://www.w3.org/TR/2008/REC-xml-20081126
38  *      https://www.w3.org/TR/2009/REC-xml-names-20091208
39  * Canonical XML version (just for info)
40  *      https://www.w3.org/TR/xml-c14n
41  */
42 
43 #ifdef HAVE_CONFIG_H
44 #include "clixon_config.h" /* generated by config & autoconf */
45 #endif
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <stdint.h>
50 #include <inttypes.h>
51 #include <unistd.h>
52 #include <errno.h>
53 #include <string.h>
54 #include <limits.h>
55 #include <assert.h>
56 
57 /* cligen */
58 #include <cligen/cligen.h>
59 
60 /* clixon */
61 #include "clixon_err.h"
62 #include "clixon_string.h"
63 #include "clixon_queue.h"
64 #include "clixon_hash.h"
65 #include "clixon_handle.h"
66 #include "clixon_log.h"
67 #include "clixon_yang.h"
68 #include "clixon_xml.h"
69 #include "clixon_options.h"
70 #include "clixon_yang_module.h"
71 #include "clixon_xml_bind.h"
72 #include "clixon_xml_vec.h"
73 #include "clixon_xml_sort.h"
74 #include "clixon_xml_nsctx.h"
75 #include "clixon_xml_parse.h"
76 #include "clixon_xml_io.h"
77 
78 /*
79  * Constants
80  */
81 /* Size of xml read buffer */
82 #define BUFLEN 1024
83 /* Indentation for xml pretty-print. Consider option? */
84 #define XML_INDENT 3
85 /* Name of xml top object created by xml parse functions */
86 #define XML_TOP_SYMBOL "top"
87 
88 
89 /*------------------------------------------------------------------------
90  * XML printing functions. Output a parse tree to file, string cligen buf
91  *------------------------------------------------------------------------*/
92 
93 /*! Print an XML tree structure to an output stream and encode chars "<>&"
94  *
95  * @param[in]   f           UNIX output stream
96  * @param[in]   xn          clicon xml tree
97  * @param[in]   level       how many spaces to insert before each line
98  * @param[in]   prettyprint insert \n and spaces tomake the xml more readable.
99  * @param[in]   fn          Callback to make print function
100  * @see clicon_xml2cbuf
101  * One can use clicon_xml2cbuf to get common code, but using fprintf is
102  * much faster than using cbuf and then printing that,...
103  */
104 int
xml2file_recurse(FILE * f,cxobj * x,int level,int prettyprint,clicon_output_cb * fn)105 xml2file_recurse(FILE             *f,
106 		 cxobj            *x,
107 		 int               level,
108 		 int               prettyprint,
109 		 clicon_output_cb *fn)
110 {
111     int    retval = -1;
112     char  *name;
113     char  *namespace;
114     cxobj *xc;
115     int    hasbody;
116     int    haselement;
117     char  *val;
118     char  *encstr = NULL; /* xml encoded string */
119 
120     if (x == NULL)
121 	goto ok;
122     name = xml_name(x);
123     namespace = xml_prefix(x);
124     switch(xml_type(x)){
125     case CX_BODY:
126 	if ((val = xml_value(x)) == NULL) /* incomplete tree */
127 	    break;
128 	if (xml_chardata_encode(&encstr, "%s", val) < 0)
129 	    goto done;
130 	(*fn)(f, "%s", encstr);
131 	break;
132     case CX_ATTR:
133 	(*fn)(f, " ");
134 	if (namespace)
135 	    (*fn)(f, "%s:", namespace);
136 	(*fn)(f, "%s=\"%s\"", name, xml_value(x));
137 	break;
138     case CX_ELMNT:
139 	(*fn)(f, "%*s<", prettyprint?(level*XML_INDENT):0, "");
140 	if (namespace)
141 	    (*fn)(f, "%s:", namespace);
142 	(*fn)(f, "%s", name);
143 	hasbody = 0;
144 	haselement = 0;
145 	xc = NULL;
146 	/* print attributes only */
147 	while ((xc = xml_child_each(x, xc, -1)) != NULL) {
148 	    switch (xml_type(xc)){
149 	    case CX_ATTR:
150 		if (xml2file_recurse(f, xc, level+1, prettyprint, fn) <0)
151 		    goto done;
152 		break;
153 	    case CX_BODY:
154 		hasbody=1;
155 		break;
156 	    case CX_ELMNT:
157 		haselement=1;
158 		break;
159 	    default:
160 		break;
161 	    }
162 	}
163 	/* Check for special case <a/> instead of <a></a>:
164 	 * Ie, no CX_BODY or CX_ELMNT child.
165 	 */
166 	if (hasbody==0 && haselement==0)
167 	    (*fn)(f, "/>");
168 	else{
169 	    (*fn)(f, ">");
170 	    if (prettyprint && hasbody == 0)
171 		    (*fn)(f, "\n");
172 	    xc = NULL;
173 	    while ((xc = xml_child_each(x, xc, -1)) != NULL) {
174 		if (xml_type(xc) != CX_ATTR)
175 		    if (xml2file_recurse(f, xc, level+1, prettyprint, fn) <0)
176 			goto done;
177 	    }
178 	    if (prettyprint && hasbody==0)
179 		(*fn)(f, "%*s", level*XML_INDENT, "");
180 	    (*fn)(f, "</");
181 	    if (namespace)
182 		(*fn)(f, "%s:", namespace);
183 	    (*fn)(f, "%s>", name);
184 	}
185 	if (prettyprint)
186 	    (*fn)(f, "\n");
187 	break;
188     default:
189 	break;
190     }/* switch */
191  ok:
192     retval = 0;
193  done:
194     if (encstr)
195 	free(encstr);
196     return retval;
197 }
198 
199 /*! Print an XML tree structure to an output stream and encode chars "<>&"
200  *
201  * @param[in]   f           UNIX output stream
202  * @param[in]   xn          clicon xml tree
203  * @param[in]   level       how many spaces to insert before each line
204  * @param[in]   prettyprint insert \n and spaces tomake the xml more readable.
205  * @see clicon_xml2cbuf print to a cbuf string
206  * @see clicon_xml2cbuf_cb print using a callback
207  */
208 int
clicon_xml2file(FILE * f,cxobj * x,int level,int prettyprint)209 clicon_xml2file(FILE  *f,
210 		cxobj *x,
211 		int    level,
212 		int    prettyprint)
213 {
214     return xml2file_recurse(f, x, level, prettyprint, fprintf);
215 }
216 
217 /*! Print an XML tree structure to an output stream and encode chars "<>&"
218  *
219  * @param[in]   f           UNIX output stream
220  * @param[in]   xn          clicon xml tree
221  * @param[in]   level       how many spaces to insert before each line
222  * @param[in]   prettyprint insert \n and spaces tomake the xml more readable.
223  * @see clicon_xml2cbuf
224  */
225 int
clicon_xml2file_cb(FILE * f,cxobj * x,int level,int prettyprint,clicon_output_cb * fn)226 clicon_xml2file_cb(FILE             *f,
227 		   cxobj            *x,
228 		   int               level,
229 		   int               prettyprint,
230 		   clicon_output_cb *fn)
231 {
232     return xml2file_recurse(f, x, level, prettyprint, fn);
233 }
234 
235 /*! Print an XML tree structure to an output stream
236  *
237  * Uses clicon_xml2file internally
238  *
239  * @param[in]   f           UNIX output stream
240  * @param[in]   xn          clicon xml tree
241  * @see clicon_xml2cbuf
242  * @see clicon_xml2cbuf_cb print using a callback
243  */
244 int
xml_print(FILE * f,cxobj * x)245 xml_print(FILE  *f,
246 	  cxobj *x)
247 {
248     return xml2file_recurse(f, x, 0, 1, fprintf);
249 }
250 
251 /*! Print an XML tree structure to a cligen buffer and encode chars "<>&"
252  *
253  * @param[in,out] cb          Cligen buffer to write to
254  * @param[in]     xn          Clicon xml tree
255  * @param[in]     level       Indentation level for prettyprint
256  * @param[in]     prettyprint insert \n and spaces tomake the xml more readable.
257  * @param[in]     depth       Limit levels of child resources: -1 is all, 0 is none, 1 is node itself
258  *
259  * @code
260  * cbuf *cb;
261  * cb = cbuf_new();
262  * if (clicon_xml2cbuf(cb, xn, 0, 1, -1) < 0)
263  *   goto err;
264  * fprintf(stderr, "%s", cbuf_get(cb));
265  * cbuf_free(cb);
266  * @endcode
267  * @see  clicon_xml2file
268  */
269 int
clicon_xml2cbuf(cbuf * cb,cxobj * x,int level,int prettyprint,int32_t depth)270 clicon_xml2cbuf(cbuf   *cb,
271 		cxobj  *x,
272 		int     level,
273 		int     prettyprint,
274 		int32_t depth)
275 {
276     int    retval = -1;
277     cxobj *xc;
278     char  *name;
279     int    hasbody;
280     int    haselement;
281     char  *namespace;
282     char  *val;
283 
284     if (depth == 0)
285 	goto ok;
286     name = xml_name(x);
287     namespace = xml_prefix(x);
288     switch(xml_type(x)){
289     case CX_BODY:
290 	if ((val = xml_value(x)) == NULL) /* incomplete tree */
291 	    break;
292 	if (xml_chardata_cbuf_append(cb, val) < 0)
293 	    goto done;
294 	break;
295     case CX_ATTR:
296 	cbuf_append_str(cb, " ");
297 	if (namespace){
298 	    cbuf_append_str(cb, namespace);
299 	    cbuf_append_str(cb, ":");
300 	}
301 	cprintf(cb, "%s=\"%s\"", name, xml_value(x));
302 	break;
303     case CX_ELMNT:
304 	if (prettyprint)
305 	    cprintf(cb, "%*s<", level*XML_INDENT, "");
306 	else
307 	    cbuf_append_str(cb, "<");
308 	if (namespace){
309 	    cbuf_append_str(cb, namespace);
310 	    cbuf_append_str(cb, ":");
311 	}
312 	cbuf_append_str(cb, name);
313 	hasbody = 0;
314 	haselement = 0;
315 	xc = NULL;
316 	/* print attributes only */
317 	while ((xc = xml_child_each(x, xc, -1)) != NULL)
318 	    switch (xml_type(xc)){
319 	    case CX_ATTR:
320 		if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, -1) < 0)
321 		    goto done;
322 		break;
323 	    case CX_BODY:
324 		hasbody=1;
325 		break;
326 	    case CX_ELMNT:
327 		haselement=1;
328 		break;
329 	    default:
330 		break;
331 	    }
332 	/* Check for special case <a/> instead of <a></a> */
333 	if (hasbody==0 && haselement==0)
334 	    cbuf_append_str(cb, "/>");
335 	else{
336 	    cbuf_append_str(cb, ">");
337 	    if (prettyprint && hasbody == 0)
338 		cbuf_append_str(cb, "\n");
339 	    xc = NULL;
340 	    while ((xc = xml_child_each(x, xc, -1)) != NULL)
341 		if (xml_type(xc) != CX_ATTR)
342 		    if (clicon_xml2cbuf(cb, xc, level+1, prettyprint, depth-1) < 0)
343 			goto done;
344 	    if (prettyprint && hasbody == 0)
345 		cprintf(cb, "%*s", level*XML_INDENT, "");
346 	    cbuf_append_str(cb, "</");
347 	    if (namespace){
348 		cbuf_append_str(cb, namespace);
349 		cbuf_append_str(cb, ":");
350 	    }
351 	    cbuf_append_str(cb, name);
352 	    cbuf_append_str(cb, ">");
353 	}
354 	if (prettyprint)
355 	    cbuf_append_str(cb, "\n");
356 	break;
357     default:
358 	break;
359     }/* switch */
360  ok:
361     retval = 0;
362  done:
363     return retval;
364 }
365 
366 /*! Return an xml tree as a pretty-printed malloced string.
367  * @param[in]  x    XML tree
368  * @retval     str  Malloced pretty-printed string (should be free:d after use)
369  * @retval     NULL Error
370  */
371 char *
clicon_xml2str(cxobj * x)372 clicon_xml2str(cxobj  *x)
373 {
374     cbuf  *cb;
375     char  *str;
376 
377     if ((cb = cbuf_new()) == NULL){
378        clicon_err(OE_XML, errno, "cbuf_new");
379        return NULL;
380     }
381     if (clicon_xml2cbuf(cb, x, 0, 1, -1) < 0)
382        return NULL;
383     if ((str = strdup(cbuf_get(cb))) == NULL){
384        clicon_err(OE_XML, errno, "strdup");
385        return NULL;
386     }
387     return str;
388 }
389 
390 /*! Print actual xml tree datastructures (not xml), mainly for debugging
391  * @param[in,out] cb          Cligen buffer to write to
392  * @param[in]     xn          Clicon xml tree
393  * @param[in]     level       Indentation level
394  */
395 int
xmltree2cbuf(cbuf * cb,cxobj * x,int level)396 xmltree2cbuf(cbuf  *cb,
397 	     cxobj *x,
398 	     int    level)
399 {
400     cxobj *xc;
401     int    i;
402 
403     for (i=0; i<level*XML_INDENT; i++)
404 	cprintf(cb, " ");
405     if (xml_type(x) != CX_BODY)
406 	cprintf(cb, "%s", xml_type2str(xml_type(x)));
407     if (xml_prefix(x)==NULL)
408 	cprintf(cb, " %s", xml_name(x));
409     else
410 	cprintf(cb, " %s:%s", xml_prefix(x), xml_name(x));
411     if (xml_value(x))
412 	cprintf(cb, " value:\"%s\"", xml_value(x));
413     if (xml_flag(x, 0xff))
414 	cprintf(cb, " flags:0x%x", xml_flag(x, 0xff));
415     if (xml_child_nr(x))
416 	cprintf(cb, " {");
417     cprintf(cb, "\n");
418     xc = NULL;
419     while ((xc = xml_child_each(x, xc, -1)) != NULL)
420 	xmltree2cbuf(cb, xc, level+1);
421     if (xml_child_nr(x)){
422 	for (i=0; i<level*XML_INDENT; i++)
423 	    cprintf(cb, " ");
424 	cprintf(cb, "}\n");
425     }
426     return 0;
427 }
428 
429 /*--------------------------------------------------------------------
430  * XML parsing functions. Create XML parse tree from string and file.
431  *--------------------------------------------------------------------*/
432 /*! Common internal xml parsing function string to parse-tree
433  *
434  * Given a string containing XML, parse into existing XML tree and return
435  * @param[in]     str   Pointer to string containing XML definition.
436  * @param[in]     yb    How to bind yang to XML top-level when parsing
437  * @param[in]     yspec Yang specification (only if bind is TOP or CONFIG)
438  * @param[in,out] xtop  Top of XML parse tree. Assume created. Holds new tree.
439  * @param[out]    xerr  Reason for failure (yang assignment not made)
440  * @retval        1     Parse OK and all yang assignment made
441  * @retval        0     Parse OK but yang assigment not made (or only partial) and xerr set
442  * @retval       -1     Error with clicon_err called. Includes parse error
443  * @see clixon_xml_parse_file
444  * @see clixon_xml_parse_string
445  * @see _json_parse
446  * @note special case is empty XML where the parser is not invoked.
447  */
448 static int
_xml_parse(const char * str,yang_bind yb,yang_stmt * yspec,cxobj * xt,cxobj ** xerr)449 _xml_parse(const char *str,
450 	   yang_bind   yb,
451 	   yang_stmt  *yspec,
452 	   cxobj      *xt,
453 	   cxobj     **xerr)
454 {
455     int             retval = -1;
456     clixon_xml_yacc xy = {0,};
457     cxobj          *x;
458     int             ret;
459     int             failed = 0; /* yang assignment */
460     int             i;
461 
462     clicon_debug(2, "%s", __FUNCTION__);
463     if (strlen(str) == 0)
464 	return 1; /* OK */
465     if (xt == NULL){
466 	clicon_err(OE_XML, errno, "Unexpected NULL XML");
467 	return -1;
468     }
469     if ((xy.xy_parse_string = strdup(str)) == NULL){
470 	clicon_err(OE_XML, errno, "strdup");
471 	return -1;
472     }
473     xy.xy_xtop = xt;
474     xy.xy_xparent = xt;
475     xy.xy_yspec = yspec;
476     if (clixon_xml_parsel_init(&xy) < 0)
477 	goto done;
478     if (clixon_xml_parseparse(&xy) != 0)  /* yacc returns 1 on error */
479 	goto done;
480     /* Purge all top-level body objects */
481     x = NULL;
482     while ((x = xml_find_type(xt, NULL, "body", CX_BODY)) != NULL)
483 	xml_purge(x);
484     /* Traverse new objects */
485     for (i = 0; i < xy.xy_xlen; i++) {
486 	x = xy.xy_xvec[i];
487 	/* Verify namespaces after parsing */
488 	if (xml2ns_recurse(x) < 0)
489 	    goto done;
490 	/* Populate, ie associate xml nodes with yang specs
491 	 */
492 	switch (yb){
493 	case YB_NONE:
494 	    break;
495 	case YB_PARENT:
496 	    /* xt:n         Has spec
497 	     * x:   <a> <-- populate from parent
498 	     */
499 	    if ((ret = xml_bind_yang0(x, YB_PARENT, NULL, xerr)) < 0)
500 		goto done;
501 	    if (ret == 0)
502 		failed++;
503 	    break;
504 
505 	case YB_MODULE:
506 	    /* xt:<top>     nospec
507 	     * x:   <a> <-- populate from modules
508 	     */
509 #ifdef XMLDB_CONFIG_HACK
510 	    if (strcmp(xml_name(x),"config") == 0 ||
511 		strcmp(xml_name(x),"data") == 0){
512 		/* xt:<top>         nospec
513 		 * x:   <config>
514 		 *         <a>  <-- populate from modules
515 		 */
516 		if ((ret = xml_bind_yang(x, YB_MODULE, yspec, xerr)) < 0)
517 		    goto done;
518 	    }
519 	    else
520 #endif
521 	    if ((ret = xml_bind_yang0(x, YB_MODULE, yspec, xerr)) < 0)
522 		goto done;
523 	    if (ret == 0)
524 		failed++;
525 	    break;
526 	case YB_RPC:
527 	    if ((ret = xml_bind_yang_rpc(x, yspec, xerr)) < 0)
528 		goto done;
529 	    if (ret == 0)
530 		failed++;
531 	    break;
532 	} /* switch */
533     }
534     if (failed)
535 	goto fail;
536     /* Sort the complete tree after parsing. Sorting is not really meaningful if Yang
537        not bound */
538     if (yb != YB_NONE)
539 	if (xml_sort_recurse(xt) < 0)
540 	    goto done;
541     retval = 1;
542   done:
543     clixon_xml_parsel_exit(&xy);
544     if (xy.xy_parse_string != NULL)
545 	free(xy.xy_parse_string);
546     if (xy.xy_xvec)
547 	free(xy.xy_xvec);
548     return retval;
549  fail: /* invalid */
550     retval = 0;
551     goto done;
552 }
553 
554 /*! FSM to detect substring
555  */
556 static inline int
FSM(char * tag,char ch,int state)557 FSM(char *tag,
558     char  ch,
559     int   state)
560 {
561     if (tag[state] == ch)
562 	return state+1;
563     else
564 	return 0;
565 }
566 
567 /*! Read an XML definition from file and parse it into a parse-tree, advanced API
568  *
569  * @param[in]     fd    A file descriptor containing the XML file (as ASCII characters)
570  * @param[in]     yb    How to bind yang to XML top-level when parsing
571  * @param[in]     yspec Yang specification (only if bind is TOP or CONFIG)
572  * @param[in]     endtag  Read until encounter "endtag" in the stream, or NULL
573  * @param[in,out] xt    Pointer to XML parse tree. If empty, create.
574  * @retval        1     Parse OK and all yang assignment made
575  * @retval        0     Parse OK but yang assigment not made (or only partial) and xerr set
576  * @retval       -1     Error with clicon_err called. Includes parse error
577  *
578  * @code
579  *  cxobj *xt = NULL;
580  *  cxobj *xerr = NULL;
581  *  int    fd;
582  *  fd = open(filename, O_RDONLY);
583  *  if ((ret = clixon_xml_parse_file(fd, YB_MODULE, yspec, "</config>", &xt, &xerr)) < 0)
584  *    err;
585  *  xml_free(xt);
586  * @endcode
587  * @see clixon_xml_parse_string
588  * @see clixon_json_parse_file
589  * @note, If xt empty, a top-level symbol will be added so that <tree../> will be:  <top><tree.../></tree></top>
590  * @note May block on file I/O
591  */
592 int
clixon_xml_parse_file(int fd,yang_bind yb,yang_stmt * yspec,char * endtag,cxobj ** xt,cxobj ** xerr)593 clixon_xml_parse_file(int        fd,
594 		      yang_bind  yb,
595 		      yang_stmt *yspec,
596 		      char      *endtag,
597 		      cxobj    **xt,
598 		      cxobj    **xerr)
599 {
600     int   retval = -1;
601     int   ret;
602     int   len = 0;
603     char  ch;
604     char *xmlbuf = NULL;
605     char *ptr;
606     int   xmlbuflen = BUFLEN; /* start size */
607     int   endtaglen = 0;
608     int   state = 0;
609     int   oldxmlbuflen;
610     int   failed = 0;
611 
612     if (xt==NULL){
613 	clicon_err(OE_XML, EINVAL, "xt is NULL");
614 	return -1;
615     }
616     if (yb == YB_MODULE && yspec == NULL){
617 	clicon_err(OE_XML, EINVAL, "yspec is required if yb == YB_MODULE");
618 	return -1;
619     }
620     if (endtag != NULL)
621 	endtaglen = strlen(endtag);
622     if ((xmlbuf = malloc(xmlbuflen)) == NULL){
623 	clicon_err(OE_XML, errno, "malloc");
624 	goto done;
625     }
626     memset(xmlbuf, 0, xmlbuflen);
627     ptr = xmlbuf;
628     while (1){
629 	if ((ret = read(fd, &ch, 1)) < 0){
630 	    clicon_err(OE_XML, errno, "read: [pid:%d]",
631 		    (int)getpid());
632 	    break;
633 	}
634 	if (ret != 0){
635 	    if (endtag)
636 		state = FSM(endtag, ch, state);
637 	    xmlbuf[len++] = ch;
638 	}
639 	if (ret == 0 ||
640 	    (endtag && (state == endtaglen))){
641 	    state = 0;
642 	    if (*xt == NULL)
643 		if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
644 		    goto done;
645 	    if ((ret = _xml_parse(ptr, yb, yspec, *xt, xerr)) < 0)
646 		goto done;
647 	    if (ret == 0)
648 		failed++;
649 	    break;
650 	}
651 	if (len >= xmlbuflen-1){ /* Space: one for the null character */
652 	    oldxmlbuflen = xmlbuflen;
653 	    xmlbuflen *= 2;
654 	    if ((xmlbuf = realloc(xmlbuf, xmlbuflen)) == NULL){
655 		clicon_err(OE_XML, errno, "realloc");
656 		goto done;
657 	    }
658 	    memset(xmlbuf+oldxmlbuflen, 0, xmlbuflen-oldxmlbuflen);
659 	    ptr = xmlbuf;
660 	}
661     } /* while */
662     retval = (failed==0) ? 1 : 0;
663  done:
664     if (retval < 0 && *xt){
665 	free(*xt);
666 	*xt = NULL;
667     }
668     if (xmlbuf)
669 	free(xmlbuf);
670     return retval;
671 }
672 
673 /*! Read an XML definition from string and parse it into a parse-tree, advanced API
674  *
675  * @param[in]     str   String containing XML definition.
676  * @param[in]     yb    How to bind yang to XML top-level when parsing
677  * @param[in]     yspec Yang specification, or NULL
678  * @param[in,out] xt    Pointer to XML parse tree. If empty will be created.
679  * @param[out]    xerr  Reason for failure (yang assignment not made) if retval = 0
680  * @retval        1     Parse OK and all yang assignment made
681  * @retval        0     Parse OK but yang assigment not made (or only partial)
682  * @retval       -1     Error with clicon_err called. Includes parse error
683  *
684  * @code
685  *  cxobj *xt = NULL;
686  *  cxobj *xerr = NULL;
687  *  if (clixon_xml_parse_string(str, YB_MODULE, yspec, &xt, &xerr) < 0)
688  *    err;
689  *  if (xml_rootchild(xt, 0, &xt) < 0) # If you want to remove TOP
690  *    err;
691  * @endcode
692  * @see clixon_xml_parse_file
693  * @see clixon_xml_parse_va
694  * @note You need to free the xml parse tree after use, using xml_free()
695  * @note If empty on entry, a new TOP xml will be created named "top"
696  */
697 int
clixon_xml_parse_string(const char * str,yang_bind yb,yang_stmt * yspec,cxobj ** xt,cxobj ** xerr)698 clixon_xml_parse_string(const char *str,
699 			yang_bind   yb,
700 			yang_stmt  *yspec,
701 			cxobj     **xt,
702 			cxobj     **xerr)
703 {
704     if (xt==NULL){
705 	clicon_err(OE_XML, EINVAL, "xt is NULL");
706 	return -1;
707     }
708     if (yb == YB_MODULE && yspec == NULL){
709 	clicon_err(OE_XML, EINVAL, "yspec is required if yb == YB_MODULE");
710 	return -1;
711     }
712     if (*xt == NULL){
713 	if ((*xt = xml_new(XML_TOP_SYMBOL, NULL, CX_ELMNT)) == NULL)
714 	    return -1;
715     }
716     return _xml_parse(str, yb, yspec, *xt, xerr);
717 }
718 
719 /*! Read XML from var-arg list and parse it into xml tree
720  *
721  * Utility function using stdarg instead of static string.
722 
723  * @param[in]     yb     How to bind yang to XML top-level when parsing
724  * @param[in]     yspec  Yang specification, or NULL
725  * @param[in,out] xtop   Top of XML parse tree. If it is NULL, top element
726  *                       called 'top' will be created. Call xml_free() after use
727  * @param[out]    xerr   Reason for failure (yang assignment not made)
728  * @param[in]     format Format string for stdarg according to printf(3)
729  * @retval        1      Parse OK and all yang assignment made
730  * @retval        0      Parse OK but yang assigment not made (or only partial)
731  * @retval       -1      Error with clicon_err called. Includes parse error
732  *
733  * @code
734  *  cxobj *xt = NULL;
735  *  if (clixon_xml_parse_va(YB_NONE, NULL, &xt, NULL, "<xml>%d</xml>", 22) < 0)
736  *    err;
737  *  xml_free(xt);
738  * @endcode
739  * @see clixon_xml_parse_string
740  * @see clixon_xml_parse_file
741  * @note If vararg list is empty, consider using clixon_xml_parse_string()
742  */
743 int
clixon_xml_parse_va(yang_bind yb,yang_stmt * yspec,cxobj ** xtop,cxobj ** xerr,const char * format,...)744 clixon_xml_parse_va(yang_bind   yb,
745 		    yang_stmt  *yspec,
746 		    cxobj     **xtop,
747 		    cxobj     **xerr,
748 		    const char *format, ...)
749 {
750     int     retval = -1;
751     va_list args;
752     char   *str = NULL;
753     int     len;
754 
755     va_start(args, format);
756     len = vsnprintf(NULL, 0, format, args) + 1;
757     va_end(args);
758     if ((str = malloc(len)) == NULL){
759 	clicon_err(OE_UNIX, errno, "malloc");
760 	goto done;
761     }
762     memset(str, 0, len);
763     va_start(args, format);
764     len = vsnprintf(str, len, format, args) + 1;
765     va_end(args);
766     retval = clixon_xml_parse_string(str, yb, yspec, xtop, xerr);
767  done:
768     if (str)
769 	free(str);
770     return retval;
771 }
772 
773