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