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 (cxobj) support 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" /* xml_bind_yang */
70 #include "clixon_yang_module.h"
71 #include "clixon_xml_map.h" /* xml_bind_yang */
72 #include "clixon_xml_vec.h"
73 #include "clixon_xml_sort.h"
74 #include "clixon_xml_io.h"
75 #include "clixon_xml_parse.h"
76 #include "clixon_xml_nsctx.h"
77 
78 /*
79  * Constants
80  */
81 /* How many XML children to start with if any. Then add quadratic until threshold when
82  * add lineraly
83  * Heurestics: if child is body only single child is expected, but element children may
84  * have siblings
85  */
86 #define XML_CHILDVEC_SIZE_START 1
87 #define XML_CHILDVEC_SIZE_START_ELMNT 16
88 #define XML_CHILDVEC_SIZE_THRESHOLD 65536
89 
90 /* Intention of these macros is to guard against access of type-specific fields
91  * As debug they can contain an assert.
92  */
93 #define is_element(x) (xml_type(x)==CX_ELMNT)
94 #define is_bodyattr(x) (xml_type(x)==CX_BODY || xml_type(x)==CX_ATTR)
95 
96 /*
97  * Types
98  */
99 
100 #ifdef XML_EXPLICIT_INDEX
101 static int xml_search_index_free(cxobj *x);
102 
103 /* A search index pair consisting of a name of an (index) variable and a vector of xml children
104  * the variable should be a potential child of the XML node
105  * The vector should have the same elements as the regular XML childvec, but in different order
106  *
107  *                        +-----+-----+-----+
108  * search index vector i: |  b  |  c  |  a  |
109  *                        +-----+-----+-----+
110  *
111  *                    index: "i"
112  *               +-----+-----+-----+
113  * x_childvec:   |  a  |  b  |  c  |
114  *               +-----+-----+-----+
115  *                  |     |     |
116  *                  v     v     v
117  *                +---+ +---+ +---+
118  * value of "i"   | 5 | | 0 | | 2 |
119  *                +---+ +---+ +---+
120 
121  */
122 struct search_index{
123     qelem_t      si_q;    /* Queue header */
124     char        *si_name; /* Name of index variable (must be (potential) child of xml node at hand */
125     clixon_xvec *si_xvec; /* Sorted vector of xml object pointers (should be of YANG type LIST) */
126 };
127 #endif
128 
129 /*! xml tree node, with name, type, parent, children, etc
130  * Note that this is a private type not visible from externally, use
131  * access functions.
132  * A word on ordering of x_children:
133  * If there is no yang specification, xml children are ordered as they are entered.
134  * If there is a yang specification (and the appropriate functions are called) the
135  * xml children are ordered as follows:
136  * 1) After yang specification order.
137  * 2) list and leaf-list are sorted alphabetically unless ordered-by user.
138  * Example:
139  * container c{
140  *  leaf a;
141  *  leaf-list x;
142  * }
143  * then regardless in which order the xml is entered, it will be sorted as follows:
144  * <c>
145  *   <a/>
146  *   <x>a</<x>
147  *   <x>b</<x>
148  * </c>
149  * From https://www.w3.org/TR/2009/REC-xml-names-20091208
150  * Definitions:
151  * - XML namespace: is identified by a URI reference [RFC3986]; element and
152  *   attribute names may be placed in an XML namespace using the mechanisms
153  *   described in this specification.
154  * - Expanded name: is a pair consisting of a namespace name and a local name.
155  * - Namespace name: For a name N in a namespace identified by a URI I, the
156  *   "namespace name" is I.
157  *   For a name N that is not in a namespace, the "namespace name" has no value.
158  * - Local name: In either case the "local name" is N (also "prefix")
159  * It is this combination of the universally managed URI namespace with the
160  * vocabulary's local names that is effective in avoiding name clashes.
161  * @see struct xmlbody    For XML body and attributes
162  */
163 struct xml{
164     enum cxobj_type   x_type;       /* type of node: element, attribute, body */
165     char             *x_name;       /* name of node */
166     char             *x_prefix;     /* namespace localname N, called prefix */
167     uint16_t          x_flags;      /* Flags according to XML_FLAG_* */
168     struct xml       *x_up;         /* parent node in hierarchy if any */
169     int              _x_vector_i;   /* internal use: xml_child_each */
170     int              _x_i;          /* internal use for sorting:
171 				       see xml_enumerate and xml_cmp */
172     /*----- next is body/attribute only */
173     cbuf             *x_value_cb;  /* attribute and body nodes have values (XXX: this consumes
174 				       memory) cv? */
175     /*----- up to here is common to all next is element only */
176     struct xml      **x_childvec;   /* vector of children nodes (XXX: use clixon_vec ) */
177     int               x_childvec_len;/* Number of children */
178     int               x_childvec_max;/* Length of allocated vector */
179 
180 
181     cvec             *x_ns_cache;   /* Cached vector of namespaces (set by bind-yang) */
182     yang_stmt        *x_spec;       /* Pointer to specification, eg yang,
183 				       by reference, dont free */
184     cg_var           *x_cv;         /* Cached value as cligen variable (set by xml_cmp) */
185 #ifdef XML_EXPLICIT_INDEX
186     struct search_index *x_search_index; /* explicit search index vectors */
187 #endif
188 };
189 
190 /* Variant of struct xml for use by non-elements to save space
191  * @see struct xml  For XML elements
192  */
193 struct xmlbody{
194     enum cxobj_type   xb_type;       /* type of node: element, attribute, body */
195     char             *xb_name;       /* name of node */
196     char             *xb_prefix;     /* namespace localname N, called prefix */
197     uint16_t          xb_flags;      /* Flags according to XML_FLAG_* */
198     struct xml       *xb_up;         /* parent node in hierarchy if any */
199     int              _xb_vector_i;   /* internal use: xml_child_each */
200     int              _xb_i;          /* internal use for sorting:
201 				       see xml_enumerate and xml_cmp */
202     cbuf             *xb_value_cb;  /* attribute and body nodes have values */
203 };
204 
205 /*
206  * Variables
207  */
208 
209 /* Mapping between xml type <--> string */
210 static const map_str2int xsmap[] = {
211     {"error",         CX_ERROR},
212     {"element",       CX_ELMNT},
213     {"attr",          CX_ATTR},
214     {"body",          CX_BODY},
215     {NULL,           -1}
216 };
217 
218 /*! Translate from xml type in enum form to string keyword
219  * @param[in] type  Xml type
220  * @retval    str   String keyword
221  */
222 char *
xml_type2str(enum cxobj_type type)223 xml_type2str(enum cxobj_type type)
224 {
225     return (char*)clicon_int2str(xsmap, type);
226 }
227 
228 /* Stats */
229 uint64_t _stats_nr = 0;
230 
231 /*! Get global statistics about XML objects
232  */
233 int
xml_stats_global(uint64_t * nr)234 xml_stats_global(uint64_t *nr)
235 {
236     if (nr)
237 	*nr = _stats_nr;
238     return 0;
239 }
240 
241 
242 /*! Return the alloced memory of a single XML obj
243  * @param[in]   x    XML object
244  * @param[out]  szp  Size of this XML obj
245  * @retval      0    OK
246  * (baseline: 96 bytes per object on x86-64)
247  */
248 static int
xml_stats_one(cxobj * x,size_t * szp)249 xml_stats_one(cxobj    *x,
250 	      size_t   *szp)
251 {
252     size_t sz = 0;
253 
254     if (x->x_name)
255 	sz += strlen(x->x_name) + 1;
256     if (x->x_prefix)
257 	sz += strlen(x->x_prefix) + 1;
258     switch (xml_type(x)){
259     case CX_ELMNT:
260 	sz += sizeof(struct xml);
261 	sz += x->x_childvec_max*sizeof(struct xml*);
262 	if (x->x_ns_cache)
263 	    sz += cvec_size(x->x_ns_cache);
264 	if (x->x_cv)
265 	    sz += cv_size(x->x_cv);
266 #ifdef XML_EXPLICIT_INDEX
267 	if (x->x_search_index){
268 	    /* XXX: only one */
269 	    sz += sizeof(struct search_index);
270 	    if (x->x_search_index->si_name)
271 		sz += strlen(x->x_search_index->si_name)+1;
272 	    if (x->x_search_index->si_xvec)
273 		sz += clixon_xvec_len(x->x_search_index->si_xvec)*sizeof(struct cxobj*);
274 	}
275 #endif
276 	break;
277     case CX_BODY:
278     case CX_ATTR:
279 	sz += sizeof(struct xmlbody);
280 	if (x->x_value_cb)
281 	    sz += cbuf_buflen(x->x_value_cb);
282 	break;
283     default:
284 	break;
285     }
286     if (szp)
287 	*szp = sz;
288     clicon_debug(1, "%s %zu", __FUNCTION__, sz);
289     return 0;
290 }
291 
292 #if 0
293 /*! Print memory stats of a single object
294  */
295 static int
296 xml_print_stats_one(FILE   *f,
297 		    cxobj  *x)
298 {
299     size_t sz = 0;
300 
301     xml_stats_one(x, &sz);
302     fprintf(f, "%s:\n", xml_name(x));
303     fprintf(f, "  sum: \t\t%u\n", (unsigned int)sz);
304     if (xml_type(x) == CX_ELMNT)
305 	fprintf(f, "  base struct: \t%u\n", (unsigned int)sizeof(struct xml));
306     else
307 	fprintf(f, "  base struct: \t%u\n", (unsigned int)sizeof(struct xmlbody));
308     if (x->x_name)
309 	fprintf(f, "  name: \t%u\n", (unsigned int)strlen(x->x_name) + 1);
310     if (x->x_prefix)
311 	fprintf(f, "  prefix: \t%u\n", (unsigned int)strlen(x->x_prefix) + 1);
312     if (xml_type(x) == CX_ELMNT){
313 	if (x->x_childvec_max)
314 	    fprintf(f, "  childvec: \t%u\n", (unsigned int)(x->x_childvec_max*sizeof(struct xml*)));
315 	if (x->x_ns_cache)
316 	    fprintf(f, "  ns-cache: \t%u\n", (unsigned int)cvec_size(x->x_ns_cache));
317 	if (x->x_cv)
318 	    fprintf(f, "  value-cv: \t%u\n", (unsigned int)cv_size(x->x_cv));
319 	if (x->x_search_index)
320 	    fprintf(f, "  search-index: \t%u\n",
321 		    (unsigned int)(strlen(x->x_search_index->si_name) + 1 + clixon_xvec_len(x->x_search_index->si_xvec)*sizeof(struct cxobj*)));
322     }
323     else{
324 	if (x->x_value_cb)
325 	    fprintf(f, "  value-cb: \t%u\n", cbuf_buflen(x->x_value_cb));
326     }
327     return 0;
328 }
329 #endif
330 
331 /*! Return statistics of an XML tree recursively
332  * @param[in]   xt   XML object
333  * @param[out]  szp  Size of this XML obj recursively
334  * @retval      0    OK
335  * @retval     -1    Error
336  */
337 int
xml_stats(cxobj * xt,uint64_t * nrp,size_t * szp)338 xml_stats(cxobj    *xt,
339 	  uint64_t *nrp,
340 	  size_t   *szp)
341 {
342     int    retval = -1;
343     size_t sz = 0;
344     cxobj *xc;
345 
346     if (xt == NULL){
347 	clicon_err(OE_XML, EINVAL, "xml node is NULL");
348 	goto done;
349     }
350     //    xml_print_stats_one(stderr, xt);
351     *nrp += 1;
352     xml_stats_one(xt, &sz);
353     if (szp)
354 	*szp += sz;
355     xc = NULL;
356     while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
357 	sz=0;
358 	xml_stats(xc, nrp, &sz);
359 	if (szp)
360 	    *szp += sz;
361     }
362     clicon_debug(1, "%s %zu", __FUNCTION__, *szp);
363     retval = 0;
364  done:
365     return retval;
366 }
367 
368 /*
369  * Access functions
370  */
371 /*! Get name of xnode
372  * @param[in]  xn    xml node
373  * @retval     name of xml node
374  */
375 char*
xml_name(cxobj * xn)376 xml_name(cxobj *xn)
377 {
378     return xn->x_name;
379 }
380 
381 /*! Set name of xnode, name is copied
382  * @param[in]  xn    xml node
383  * @param[in]  name  new name, null-terminated string, copied by function
384  * @retval     -1    on error with clicon-err set
385  * @retval     0     OK
386  */
387 int
xml_name_set(cxobj * xn,char * name)388 xml_name_set(cxobj *xn,
389 	     char  *name)
390 {
391     if (xn->x_name){
392 	free(xn->x_name);
393 	xn->x_name = NULL;
394     }
395     if (name){
396 	if ((xn->x_name = strdup(name)) == NULL){
397 	    clicon_err(OE_XML, errno, "strdup");
398 	    return -1;
399 	}
400     }
401     return 0;
402 }
403 
404 /*! Get prefix of xnode
405  * @param[in]  xn     xml node
406  * @retval     prefix of xml node
407  */
408 char*
xml_prefix(cxobj * xn)409 xml_prefix(cxobj *xn)
410 {
411     return xn->x_prefix;
412 }
413 
414 /*! Set prefix of xnode, prefix is copied
415  * @param[in]  xn      XML node
416  * @param[in]  prefix  New prefix, null-terminated string, copied by function
417  * @retval     -1      Error with clicon-err set
418  * @retval     0       OK
419  */
420 int
xml_prefix_set(cxobj * xn,char * prefix)421 xml_prefix_set(cxobj *xn,
422 	       char  *prefix)
423 {
424     if (xn->x_prefix){
425 	free(xn->x_prefix);
426 	xn->x_prefix = NULL;
427     }
428     if (prefix){
429 	if ((xn->x_prefix = strdup(prefix)) == NULL){
430 	    clicon_err(OE_XML, errno, "strdup");
431 	    return -1;
432 	}
433     }
434     return 0;
435 }
436 
437 /*! Get cached namespace (given prefix)
438  * @param[in] x      XML node
439  * @param[in] prefix Namespace prefix, or NULL for default
440  * @retval    ns     Cached namespace
441  * @retval    NULL   No namespace found (not cached or not found)
442  * @note may want to distinguish between not set cache and no namespace?
443  */
444 char*
nscache_get(cxobj * x,char * prefix)445 nscache_get(cxobj *x,
446 	    char  *prefix)
447 {
448     if (!is_element(x))
449 	return NULL;
450     if (x->x_ns_cache != NULL)
451 	return xml_nsctx_get(x->x_ns_cache, prefix);
452     return NULL;
453 }
454 
455 /*! Get cached prefix (given namespace)
456  * @param[in]  x         XML node
457  * @param[in]  namespace
458  * @param[out] prefix
459  * @retval     0      No prefix found
460  * @retval     1      Prefix found
461  */
462 int
nscache_get_prefix(cxobj * x,char * namespace,char ** prefix)463 nscache_get_prefix(cxobj *x,
464 		   char  *namespace,
465 		   char **prefix)
466 {
467     if (!is_element(x))
468 	return 0;
469     if (x->x_ns_cache != NULL)
470 	return xml_nsctx_get_prefix(x->x_ns_cache, namespace, prefix);
471     return 0;
472 }
473 
474 /*! Dump whole namespace context cache of one xml node
475  * @param[in]  x    XML node
476  * @retval     nsc  Whole namespace context of x
477  * @retval     NULL Empty nsc
478  * @see nscache_get  For a single prefix
479  */
480 cvec *
nscache_get_all(cxobj * x)481 nscache_get_all(cxobj *x)
482 {
483     if (!is_element(x))
484 	return NULL;
485     return x->x_ns_cache;
486 }
487 
488 /*! Set cached namespace for specific namespace. Replace if necessary
489  * @param[in] x         XML node
490  * @param[in] prefix    Namespace prefix, or NULL for default
491  * @param[in] namespace Cached namespace to set (assume non-null?)
492  * @retval    0         OK
493  * @retval   -1         Error
494  * @see nscache_replace  to replace the whole context
495  */
496 int
nscache_set(cxobj * x,char * prefix,char * namespace)497 nscache_set(cxobj *x,
498 	    char  *prefix,
499 	    char  *namespace)
500 {
501     int     retval = -1;
502 
503     if (!is_element(x))
504 	return 0;
505     if (x->x_ns_cache == NULL){
506 	if ((x->x_ns_cache = xml_nsctx_init(prefix, namespace)) == NULL)
507 	    goto done;
508     }
509     else
510 	return xml_nsctx_add(x->x_ns_cache, prefix, namespace);
511     retval = 0;
512  done:
513     return retval;
514 }
515 
516 /*! Set complete cached namespace context
517  * @param[in] x      XML node
518  * @param[in] nsc    Namespace context (note consumed, dont free)
519  * @retval    0      OK
520  * @retval   -1      Error
521  * @see nscache_set  set a single cache line
522  */
523 int
nscache_replace(cxobj * x,cvec * nsc)524 nscache_replace(cxobj *x,
525 		cvec  *nsc)
526 {
527     int     retval = -1;
528 
529     if (!is_element(x))
530 	return 0;
531     if (x->x_ns_cache != NULL){
532 	xml_nsctx_free(x->x_ns_cache);
533 	x->x_ns_cache = NULL;
534     }
535     x->x_ns_cache = nsc;
536     retval = 0;
537     // done:
538     return retval;
539 }
540 
541 /*! Clear cached namespace context
542  * Clear the whole namespace context, not just single cache lines
543  * @param[in] x         XML node
544  * @retval    0         OK
545  * @see nscache_set  For setting specific namespace cache lines
546  * @see xml_addsub
547  */
548 int
nscache_clear(cxobj * x)549 nscache_clear(cxobj *x)
550 {
551 
552     if (!is_element(x))
553 	return 0;
554     if (x->x_ns_cache != NULL){
555 	xml_nsctx_free(x->x_ns_cache);
556 	x->x_ns_cache = NULL;
557     }
558     return 0;
559 }
560 
561 /*! Get parent of xnode
562  * @param[in]  xn    xml node
563  * @retval     parent xml node
564  */
565 cxobj*
xml_parent(cxobj * xn)566 xml_parent(cxobj *xn)
567 {
568     return xn->x_up;
569 }
570 
571 /*! Set parent of xnode, parent is copied.
572  * @param[in]  xn      xml node
573  * @param[in]  parent  pointer to new parent xml node
574  * @retval     0       OK
575  */
576 int
xml_parent_set(cxobj * xn,cxobj * parent)577 xml_parent_set(cxobj *xn,
578 	       cxobj *parent)
579 {
580     xn->x_up = parent;
581     return 0;
582 }
583 
584 /*! Get xml node flags, used for internal algorithms
585  * @param[in]  xn    xml node
586  * @retval     flag  Flag value(s), see XML_FLAG_MARK et al
587  */
588 uint16_t
xml_flag(cxobj * xn,uint16_t flag)589 xml_flag(cxobj   *xn,
590 	 uint16_t flag)
591 {
592     return xn->x_flags&flag;
593 }
594 
595 /*! Set xml node flags, used for internal algorithms
596  * @param[in]  xn      xml node
597  * @param[in]  flag    Flag values to set, see XML_FLAG_MARK et al
598  */
599 int
xml_flag_set(cxobj * xn,uint16_t flag)600 xml_flag_set(cxobj   *xn,
601 	     uint16_t flag)
602 {
603     xn->x_flags |= flag;
604     return 0;
605 }
606 
607 /*! Reset xml node flags, used for internal algorithms
608  * @param[in]  xn      xml node
609  * @param[in]  flag    Flag value(s) to reset, see XML_FLAG_*
610  */
611 int
xml_flag_reset(cxobj * xn,uint16_t flag)612 xml_flag_reset(cxobj   *xn,
613 	       uint16_t flag)
614 {
615     xn->x_flags &= ~flag;
616     return 0;
617 }
618 
619 /*! Get value of xnode
620  * @param[in]  xn    xml node
621  * @retval     value of xml node
622  */
623 char*
xml_value(cxobj * xn)624 xml_value(cxobj *xn)
625 {
626     if (!is_bodyattr(xn))
627 	return NULL;
628     return xn->x_value_cb?cbuf_get(xn->x_value_cb):NULL;
629 }
630 
631 /*! Set value of xml node, value is copied
632  * @param[in]  xn    xml node
633  * @param[in]  val   new value, null-terminated string, copied by function
634  * @retval     -1    on error with clicon-err set
635  * @retval     0     OK
636  */
637 int
xml_value_set(cxobj * xn,char * val)638 xml_value_set(cxobj *xn,
639 	      char  *val)
640 {
641     int    retval = -1;
642     size_t sz;
643 
644     if (!is_bodyattr(xn))
645 	return 0;
646     if (val == NULL){
647 	clicon_err(OE_XML, EINVAL, "value is NULL");
648 	goto done;
649     }
650     sz = strlen(val)+1;
651     if (xn->x_value_cb == NULL){
652 	if ((xn->x_value_cb = cbuf_new_alloc(sz)) == NULL){
653 	    clicon_err(OE_XML, errno, "cbuf_new");
654 	    goto done;
655 	}
656     }
657     else
658 	cbuf_reset(xn->x_value_cb);
659     cbuf_append_str(xn->x_value_cb, val);
660     retval = 0;
661  done:
662     return retval;
663 }
664 
665 /*! Append value of xnode, value is copied
666  * @param[in]  xn    xml node
667  * @param[in]  val   appended value, null-terminated string, copied by function
668  * @retval     NULL  on error with clicon-err set, or if value is set to NULL
669  * @retval     new value
670  */
671 int
xml_value_append(cxobj * xn,char * val)672 xml_value_append(cxobj *xn,
673 		 char  *val)
674 {
675     int    retval = -1;
676     size_t sz;
677 
678     if (!is_bodyattr(xn))
679 	return 0;
680     if (val == NULL){
681 	clicon_err(OE_XML, EINVAL, "value is NULL");
682 	goto done;
683     }
684     sz = strlen(val)+1;
685     if (xn->x_value_cb == NULL){
686 	if ((xn->x_value_cb = cbuf_new_alloc(sz)) == NULL){
687 	    clicon_err(OE_XML, errno, "cbuf_new");
688 	    goto done;
689 	}
690     }
691     if (cbuf_append_str(xn->x_value_cb, val) < 0){
692 	clicon_err(OE_XML, errno, "cprintf");
693 	goto done;
694     }
695     retval = 0;
696  done:
697     return retval;
698 }
699 
700 /*! Get type of xnode
701  * @param[in]  xn    xml node
702  * @retval     type of xml node
703  */
704 enum cxobj_type
xml_type(cxobj * xn)705 xml_type(cxobj *xn)
706 {
707     return xn->x_type;
708 }
709 
710 /*! Set type of xnode
711  * @param[in]  xn    xml node
712  * @param[in]  type  new type
713  * @retval     type  old type
714  */
715 static enum cxobj_type
xml_type_set(cxobj * xn,enum cxobj_type type)716 xml_type_set(cxobj          *xn,
717 	     enum cxobj_type type)
718 {
719     enum cxobj_type old = xn->x_type;
720 
721     xn->x_type = type;
722     return old;
723 }
724 
725 /*! Get number of children
726  * @param[in]  xn    xml node
727  * @retval     number of children in XML tree
728  * @see xml_child_nr_type
729  * @see xml_child_nr_notype
730  */
731 int
xml_child_nr(cxobj * xn)732 xml_child_nr(cxobj *xn)
733 {
734     if (!is_element(xn))
735 	return 0;
736     return xn->x_childvec_len;
737 }
738 
739 /*! Get number of children of EXCEPT specific type
740  * @param[in]  xn    xml node
741  * @param[in]  type  XML type or -1 for all
742  * @retval     number of typed children in XML tree (except type)
743  * @see xml_child_nr
744  * @see xml_child_nr_type
745  */
746 int
xml_child_nr_notype(cxobj * xn,enum cxobj_type type)747 xml_child_nr_notype(cxobj          *xn,
748 		    enum cxobj_type type)
749 {
750     cxobj *x = NULL;
751     int    nr = 0;
752 
753     if (!is_element(xn))
754 	return 0;
755     while ((x = xml_child_each(xn, x, -1)) != NULL) {
756 	if (xml_type(x) != type)
757 	    nr++;
758     }
759     return nr;
760 }
761 
762 /*! Get number of children of specific type
763  * @param[in]  xn    xml node
764  * @param[in]  type  XML type or -1 for all
765  * @retval     number of typed children in XML tree
766  * @see xml_child_nr
767  * @see xml_child_nr_notype
768  */
769 int
xml_child_nr_type(cxobj * xn,enum cxobj_type type)770 xml_child_nr_type(cxobj          *xn,
771 		  enum cxobj_type type)
772 {
773     cxobj *x = NULL;
774     int    len = 0;
775 
776     if (!is_element(xn))
777 	return 0;
778     while ((x = xml_child_each(xn, x, type)) != NULL)
779 	len++;
780     return len;
781 }
782 
783 /*! Get a specific child
784  * @param[in]  xn    xml node
785  * @param[in]  i     the number of the child, eg order in children vector
786  * @retval     xml   The child xml node
787  * @retval     NULL  if no such child, or empty child
788  * @see xml_child_i_type
789  * @see xml_child_order
790  */
791 cxobj *
xml_child_i(cxobj * xn,int i)792 xml_child_i(cxobj *xn,
793 	    int    i)
794 {
795     if (!is_element(xn))
796 	return NULL;
797     if (i < xn->x_childvec_len)
798 	return xn->x_childvec[i];
799     return NULL;
800 }
801 
802 /*! Get a specific child of a specific type
803  * @param[in]  xn    xml node
804  * @param[in]  i     the number of the child of specific type
805  * @param[in]  type  Child type
806  * @retval     child in XML tree, or NULL if no such child, or empty child
807  * @see xml_child_i
808  */
809 cxobj *
xml_child_i_type(cxobj * xn,int i,enum cxobj_type type)810 xml_child_i_type(cxobj          *xn,
811 		 int             i,
812 		 enum cxobj_type type)
813 {
814     cxobj *x = NULL;
815     int    it = 0;
816 
817     if (!is_element(xn))
818 	return NULL;
819     while ((x = xml_child_each(xn, x, type)) != NULL) {
820 	if (x->x_type == type && (i == it++))
821 	    return x;
822     }
823     return NULL;
824 }
825 
826 /*! Set specific child
827  * @param[in]  xn    xml node
828  * @param[in]  i     the number of the child, eg order in children vector
829  * @param[in]  xc    The child to set at position i
830  * @retval     0     OK
831  */
832 cxobj *
xml_child_i_set(cxobj * xt,int i,cxobj * xc)833 xml_child_i_set(cxobj *xt,
834 		int    i,
835 		cxobj *xc)
836 {
837     if (!is_element(xt))
838 	return NULL;
839     if (i < xt->x_childvec_len)
840 	xt->x_childvec[i] = xc;
841     return 0;
842 }
843 
844 /*! Get the order of child
845  * @param[in]  xp    xml parent node
846  * @param[in]  xc    the xml child to look for
847  * @retval     xml   The child xml node
848  * @retval     i     The order of the child
849  * @retval     -1    if no such child, or empty child
850  * @see xml_child_i
851  */
852 int
xml_child_order(cxobj * xp,cxobj * xc)853 xml_child_order(cxobj *xp,
854 		cxobj *xc)
855 {
856     cxobj *x = NULL;
857     int    i = 0;
858 
859     if (!is_element(xp))
860 	return -1;
861     while ((x = xml_child_each(xp, x, -1)) != NULL) {
862 	if (x == xc)
863 	    return i;
864 	i++;
865     }
866     return -1;
867 }
868 
869 /*! Iterator over xml children objects
870  *
871  * @param[in] xparent xml tree node whose children should be iterated
872  * @param[in] xprev   previous child, or NULL on init
873  * @param[in] type    matching type or -1 for any
874  * @code
875  *   cxobj *x = NULL;
876  *   while ((x = xml_child_each(x_top, x, -1)) != NULL) {
877  *     ...
878  *   }
879  * @endcode
880  * @note uses _x_vector_i as a shared resource: you cannot mix loops over same parent
881  * Further, never manipulate the child-list during operation or using the
882  * same object recursively, the function uses an internal field to remember the
883  * index used. It works as long as the same object is not iterated concurrently.
884  * If you need to delete a node you can do somethhing like:
885  * @code
886  *   cxobj *xprev = NULL;
887  *   cxobj *x = NULL;
888  *   while ((x = xml_child_each(x_top, x, -1)) != NULL) {
889  *      if (something){
890  *	    if (xml_purge(x) < 0)
891  *		goto done;
892  *	    x = xprev;
893  *	    continue;
894  *      }
895  *      xprev = x;
896  *   }
897  * @endcode
898 #ifdef XML_EXPLICIT_INDEX
899  * @see xml_child_index_each
900 #endif XML_EXPLICIT_INDEX
901  */
902 cxobj *
xml_child_each(cxobj * xparent,cxobj * xprev,enum cxobj_type type)903 xml_child_each(cxobj           *xparent,
904 	       cxobj           *xprev,
905 	       enum cxobj_type  type)
906 {
907     int    i;
908     cxobj *xn = NULL;
909 
910     if (xparent == NULL)
911 	return NULL;
912     if (!is_element(xparent))
913 	return NULL;
914     for (i=xprev?xprev->_x_vector_i+1:0; i<xparent->x_childvec_len; i++){
915 	xn = xparent->x_childvec[i];
916 	if (xn == NULL)
917 	    continue;
918 	if (type != CX_ERROR && xml_type(xn) != type)
919 	    continue;
920 	break; /* this is next object after previous */
921     }
922     if (i < xparent->x_childvec_len) /* found */
923 	xn->_x_vector_i = i;
924     else
925 	xn = NULL;
926     return xn;
927 }
928 
929 
930 /*! Extend child vector with one and insert xml node there
931  * @note does not do anything with child, you may need to set its parent, etc
932  * @see xml_child_insert_pos
933  * XXX could insert hint if we know this is a yang list and not a leaf to increase start.
934  */
935 static int
xml_child_append(cxobj * xp,cxobj * xc)936 xml_child_append(cxobj *xp,
937 		 cxobj *xc)
938 {
939     size_t start;
940 
941     if (!is_element(xp))
942 	return 0;
943     start = XML_CHILDVEC_SIZE_START;
944     /* Heurestics: if child is body only single child is expected, but element children may
945      * have siblings
946      */
947     if (xml_type(xc) == CX_ELMNT)
948 	start = XML_CHILDVEC_SIZE_START_ELMNT;
949     xp->x_childvec_len++;
950     if (xp->x_childvec_len > xp->x_childvec_max){
951 	if (xp->x_childvec_len < XML_CHILDVEC_SIZE_THRESHOLD)
952 	    xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:start;
953 	else
954 	    xp->x_childvec_max += XML_CHILDVEC_SIZE_THRESHOLD;
955 	xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_max*sizeof(cxobj*));
956 	if (xp->x_childvec == NULL){
957 	    clicon_err(OE_XML, errno, "realloc");
958 	    return -1;
959 	}
960     }
961     xp->x_childvec[xp->x_childvec_len-1] = xc;
962     return 0;
963 }
964 
965 /*! Insert child xc at position i under parent xp
966  *
967  * @see xml_child_append
968  * @note does not do anything with child, you may need to set its parent, etc
969  */
970 int
xml_child_insert_pos(cxobj * xp,cxobj * xc,int i)971 xml_child_insert_pos(cxobj *xp,
972 		     cxobj *xc,
973 		     int    i)
974 {
975     size_t size;
976 
977     if (!is_element(xp))
978 	return 0;
979     xp->x_childvec_len++;
980     if (xp->x_childvec_len > xp->x_childvec_max){
981 	if (xp->x_childvec_len < XML_CHILDVEC_SIZE_THRESHOLD)
982 	    xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:XML_CHILDVEC_SIZE_START;
983 	else
984 	    xp->x_childvec_max += XML_CHILDVEC_SIZE_THRESHOLD;
985 	xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_max*sizeof(cxobj*));
986 	if (xp->x_childvec == NULL){
987 	    clicon_err(OE_XML, errno, "realloc");
988 	    return -1;
989 	}
990     }
991     size = (xml_child_nr(xp) - i - 1)*sizeof(cxobj *);
992     memmove(&xp->x_childvec[i+1], &xp->x_childvec[i], size);
993     xp->x_childvec[i] = xc;
994     return 0;
995 }
996 
997 /*! Set a childvec to a specific size, fill with children after
998  * @code
999  *   xml_childvec_set(x, 2);
1000  *   xml_child_i_set(x, 0, xc0)
1001  *   xml_child_i_set(x, 1, xc1);
1002  * @endcode
1003  */
1004 int
xml_childvec_set(cxobj * x,int len)1005 xml_childvec_set(cxobj *x,
1006 		 int    len)
1007 {
1008     if (!is_element(x))
1009 	return 0;
1010     x->x_childvec_len = len;
1011     x->x_childvec_max = len;
1012     if (x->x_childvec)
1013 	free(x->x_childvec);
1014     if ((x->x_childvec = calloc(len, sizeof(cxobj*))) == NULL){
1015 	clicon_err(OE_XML, errno, "calloc");
1016 	return -1;
1017     }
1018     return 0;
1019 }
1020 
1021 /*! Get the children of an XML node as an XML vector
1022  */
1023 cxobj **
xml_childvec_get(cxobj * x)1024 xml_childvec_get(cxobj *x)
1025 {
1026     if (!is_element(x))
1027 	return NULL;
1028     return x->x_childvec;
1029 }
1030 
1031 /*! Create new xml node given a name and parent. Free with xml_free().
1032  *
1033  * @param[in]  name      Name of XML node
1034  * @param[in]  xp        The parent where the new xml node will be appended
1035  * @param[in]  type      XML type
1036  * @retval     xml       Created xml object if successful. Free with xml_free()
1037  * @retval     NULL      Error and clicon_err() called
1038  * @code
1039  *   cxobj *x;
1040  *   if ((x = xml_new(name, xparent, CX_ELMNT)) == NULL)
1041  *     err;
1042  *   ...
1043  *   xml_free(x);
1044  * @endcode
1045  * @note Differentiates between body/attribute vs element to reduce mem allocation
1046  * @see xml_sort_insert
1047  */
1048 cxobj *
xml_new(char * name,cxobj * xp,enum cxobj_type type)1049 xml_new(char           *name,
1050 	cxobj          *xp,
1051 	enum cxobj_type type)
1052 {
1053     struct xml *x = NULL;
1054     size_t      sz;
1055 
1056     switch (type){
1057     case CX_ELMNT:
1058 	sz = sizeof(struct xml);
1059 	break;
1060     case CX_ATTR:
1061     case CX_BODY:
1062 	sz = sizeof(struct xmlbody);
1063 	break;
1064     default:
1065 	clicon_err(OE_XML, EINVAL, "Invalid type: %d", type);
1066 	return NULL;
1067 	break;
1068     }
1069     if ((x = malloc(sz)) == NULL){
1070 	clicon_err(OE_XML, errno, "malloc");
1071 	return NULL;
1072     }
1073     memset(x, 0, sz);
1074     xml_type_set(x, type);
1075     if (name && (xml_name_set(x, name)) < 0)
1076 	return NULL;
1077     if (xp){
1078 	xml_parent_set(x, xp);
1079 	if (xml_child_append(xp, x) < 0)
1080 	    return NULL;
1081 	x->_x_i = xml_child_nr(xp)-1;
1082     }
1083     _stats_nr++;
1084     return x;
1085 }
1086 
1087 /*! Create a new XML node and set it's body to a value
1088  *
1089  * @param[in]   name    The name of the new node
1090  * @param[in]   parent  The parent to put the new node under
1091  * @param[in]   val     The value to set in the body
1092  *
1093  * Creates a new node, sets it as a child of the parent, if one was passed in.
1094  * Creates a child of the node, sets the child's type to CX_BODY, and sets
1095  * the value of the body/child.
1096  * Thanks mgsmith@netgate.com
1097  */
1098 cxobj *
xml_new_body(char * name,cxobj * parent,char * val)1099 xml_new_body(char  *name,
1100 	     cxobj *parent,
1101 	     char  *val)
1102 {
1103     cxobj *new_node = NULL;
1104     cxobj *body_node;
1105 
1106     if (!name || !parent || !val) {
1107 	return NULL;
1108     }
1109     if ((new_node = xml_new(name, parent, CX_ELMNT)) == NULL) {
1110 	return NULL;
1111     }
1112     if ((body_node = xml_new("body", new_node, CX_BODY)) == NULL ||
1113 	xml_value_set(body_node, val) < 0) {
1114 	xml_free(new_node);
1115 	new_node = NULL;
1116 	body_node = NULL;
1117     } else {
1118 	xml_type_set(body_node, CX_BODY);
1119     }
1120     return new_node;
1121 }
1122 
1123 
1124 /*! Return yang spec of node.
1125  * Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.
1126  */
1127 yang_stmt *
xml_spec(cxobj * x)1128 xml_spec(cxobj *x)
1129 {
1130     if (!is_element(x))
1131 	return NULL;
1132     return x->x_spec;
1133 }
1134 
1135 int
xml_spec_set(cxobj * x,yang_stmt * spec)1136 xml_spec_set(cxobj     *x,
1137 	     yang_stmt *spec)
1138 {
1139     if (!is_element(x))
1140 	return 0;
1141     x->x_spec = spec;
1142     return 0;
1143 }
1144 
1145 /*! Return (cached)  cligen variable value of xml node
1146  * @param[in]  x    XML node (body and leaf/leaf-list)
1147  * @retval     cv   CLIgen variable containing value of x body
1148  * @retval     NULL
1149  * Only applicable if x is body and has yang-spec and is leaf or leaf-list
1150  * Only accessed by xml_cv_cache as part of sorting in xml_cmp
1151  * @see xml_cv_cache
1152  */
1153 cg_var *
xml_cv(cxobj * x)1154 xml_cv(cxobj *x)
1155 {
1156     if (!is_element(x))
1157 	return NULL;
1158     return x->x_cv;
1159 }
1160 
1161 /*! Set (cached) cligen variable value of xml node
1162  * @param[in]  x   XML node (body and leaf/leaf-list)
1163  * @param[in]  cv  CLIgen variable containing value of x body
1164  * @retval     0   OK
1165  * Only applicable if x is body and has yang-spec and is leaf or leaf-list
1166  * Only accessed by xml_cv_cache as part of sorting in xml_cmp
1167  * @see xml_cv_cache
1168  */
1169 int
xml_cv_set(cxobj * x,cg_var * cv)1170 xml_cv_set(cxobj  *x,
1171 	   cg_var *cv)
1172 {
1173     if (!is_element(x))
1174 	return 0;
1175     if (x->x_cv)
1176 	cv_free(x->x_cv);
1177     x->x_cv = cv;
1178     return 0;
1179 }
1180 
1181 /*! Find an XML node matching name among a parent's children.
1182  *
1183  * Get first XML node directly under x_up in the xml hierarchy with
1184  * name "name".
1185  *
1186  * @param[in]  x_up   Base XML object
1187  * @param[in]  name   Node name
1188  *
1189  * @retval xmlobj     if found.
1190  * @retval NULL       if no such node found.
1191  * There are several issues with this function:
1192  * @note (1) Ignores prefix which means namespaces are ignored
1193  * @note (2) Does not differentiate between element,attributes and body. You usually want elements.
1194  * @note (3) Linear scalability and relies on strcmp, does not use search/key indexes
1195  * @note (4) Only returns first match, eg a list/leaf-list may have several children with same name
1196  * @see xml_find_type  A more generic function fixes (1) and (2) above
1197  */
1198 cxobj *
xml_find(cxobj * xp,char * name)1199 xml_find(cxobj *xp,
1200 	 char  *name)
1201 {
1202     cxobj *x = NULL;
1203 
1204     if (!is_element(xp))
1205 	return NULL;
1206     while ((x = xml_child_each(xp, x, -1)) != NULL)
1207 	if (strcmp(name, xml_name(x)) == 0)
1208 	    break; /* x is set */
1209     return x;
1210 }
1211 
1212 /*! Append xc as child to xp. Remove xc from previous parent.
1213  * @param[in] xp  Parent xml node. If NULL just remove from old parent.
1214  * @param[in] xc  Child xml node to insert under xp
1215  * @retval    0   OK
1216  * @retval    -1  Error
1217  * @see xml_wrap
1218  * @see xml_insert
1219  * @note xc is not sorted correctly, need to call xml_sort on parent
1220  * @see xml_insert which is a higher layer function including yang and sorting
1221  */
1222 int
xml_addsub(cxobj * xp,cxobj * xc)1223 xml_addsub(cxobj *xp,
1224 	   cxobj *xc)
1225 {
1226     int    retval = -1;
1227     cxobj *oldp;
1228     int    i;
1229     char  *pns = NULL; /* parent namespace */
1230     char  *cns = NULL; /* child namespace */
1231     cxobj *xa;
1232 
1233     if ((oldp = xml_parent(xc)) != NULL){
1234 	/* Find child order i in old parent*/
1235 	for (i=0; i<xml_child_nr(oldp); i++)
1236 	    if (xml_child_i(oldp, i) == xc)
1237 		break;
1238 	/* Remove xc from old parent */
1239 	if (i < xml_child_nr(oldp))
1240 	    xml_child_rm(oldp, i);
1241     }
1242     /* Add xc to new parent */
1243     if (xp){
1244 	if (xml_child_append(xp, xc) < 0)
1245 	    goto done;
1246 	/* Set new parent in child */
1247 	xml_parent_set(xc, xp);
1248 	/* Ensure default namespace is not duplicated
1249 	 * here only remove duplicate default namespace, there may be more */
1250 	/* 1. Get parent default namespace */
1251 	if (xml2ns(xp, NULL, &pns) < 0)
1252 	    goto done;
1253 	/* 2. Get child default namespace */
1254 	if (pns &&
1255 	    xml_type(xc) == CX_ELMNT &&
1256 	    (xa = xml_find_type(xc, NULL, "xmlns", CX_ATTR)) != NULL &&
1257 	    (cns = xml_value(xa)) != NULL){
1258 	    /* 3. check if same, if so remove child's */
1259 	    if (strcmp(pns, cns) == 0)
1260 		xml_purge(xa);
1261 	}
1262 	/* clear namespace context cache of child */
1263 	nscache_clear(xc);
1264 #ifdef XML_EXPLICIT_INDEX
1265     if (xml_search_index_p(xc))
1266 	    xml_search_child_insert(xp, xc);
1267 #endif
1268     }
1269     retval = 0;
1270  done:
1271     return retval;
1272 }
1273 
1274 /*! Wrap a new node between a parent xml node (xp) and all its children
1275  *  Before:  xp --> xc*
1276  *  After:   xp --> xw --> xc*
1277  * @param[in] xp  Parent xml node
1278  * @param[in] tag Name of new xml child
1279  * @retval    xw  Return the new child (xw)
1280  * @see xml_addsub
1281  * @see xml_wrap  (wrap s single node)
1282  */
1283 cxobj *
xml_wrap_all(cxobj * xp,char * tag)1284 xml_wrap_all(cxobj *xp,
1285 	     char  *tag)
1286 {
1287     cxobj *xw; /* new wrap node */
1288 
1289     if (!is_element(xp))
1290 	return NULL;
1291     if ((xw = xml_new(tag, NULL, CX_ELMNT)) == NULL)
1292 	goto done;
1293     while (xp->x_childvec_len)
1294 	if (xml_addsub(xw, xml_child_i(xp, 0)) < 0)
1295 	    goto done;
1296     if (xml_addsub(xp, xw) < 0)
1297 	goto done;
1298   done:
1299     return xw;
1300 }
1301 
1302 /*! Wrap a new element above a single xml node (xc) with new tag
1303  *  Before:  xp --> xc # specific child (xp can be NULL)
1304  *  After:   xp --> xt(tag) --> xc
1305  * @param[in] xp   Parent xml node
1306  * @param[in] tag  Name of new xml child
1307  * @retval    NULL Error
1308  * @retval    xc   Return the new child (xc)
1309  * @see xml_addsub (give the parent)
1310  * @see xml_wrap_all  (wrap all children of a node, not just one)
1311  */
1312 cxobj *
xml_wrap(cxobj * xc,char * tag)1313 xml_wrap(cxobj *xc,
1314 	 char  *tag)
1315 {
1316     cxobj *xw; /* new wrap node */
1317     cxobj *xp; /* parent */
1318 
1319     xp = xml_parent(xc);
1320     if ((xw = xml_new(tag, xp, CX_ELMNT)) == NULL)
1321 	goto done;
1322     if (xml_addsub(xw, xc) < 0)
1323 	goto done;
1324   done:
1325     return xw;
1326 }
1327 
1328 /*! Remove and free an xml node child from xml parent
1329  * @param[in]   xc          xml child node (to be removed and freed)
1330  * @retval      0           OK
1331  * @retval      -1
1332  * @note you cannot remove xchild in the loop (unless yoy keep track of xprev)
1333  * @note Linear complexity - use xml_child_rm if possible
1334  * @see xml_free      Free, dont remove from parent
1335  * @see xml_child_rm  Remove if child order is known (does not free)
1336  * Differs from xml_free it is removed from parent.
1337  */
1338 int
xml_purge(cxobj * xc)1339 xml_purge(cxobj *xc)
1340 {
1341     int       retval = -1;
1342     int       i;
1343     cxobj    *xp;
1344 
1345     if ((xp = xml_parent(xc)) != NULL){
1346 	/* Find child order i in parent*/
1347 	for (i=0; i<xml_child_nr(xp); i++)
1348 	    if (xml_child_i(xp, i) == xc)
1349 		break;
1350 	/* Remove xc from parent */
1351 	if (i < xml_child_nr(xp))
1352 	    if (xml_child_rm(xp, i) < 0)
1353 		goto done;
1354     }
1355     xml_free(xc);
1356     retval = 0;
1357  done:
1358     return retval;
1359 }
1360 
1361 /*! Remove child xml node from parent xml node. No free and child is root
1362  * @param[in]   xp     xml parent node
1363  * @param[in]   i      Number of xml child node (to remove)
1364  * @retval      0      OK
1365  * @retval      -1
1366  * @note you should not remove xchild in loop (unless yoy keep track of xprev)
1367  *
1368  * @see xml_rootchild
1369  * @see xml_rm     Remove the node itself from parent
1370  */
1371 int
xml_child_rm(cxobj * xp,int i)1372 xml_child_rm(cxobj *xp,
1373 	     int    i)
1374 {
1375     int    retval = -1;
1376     cxobj *xc = NULL;
1377 
1378     if (!is_element(xp))
1379 	return 0;
1380     if ((xc = xml_child_i(xp, i)) == NULL){
1381 	clicon_err(OE_XML, 0, "Child not found");
1382 	goto done;
1383     }
1384     xml_parent_set(xc, NULL);
1385     xp->x_childvec[i] = NULL;
1386     xp->x_childvec_len--;
1387     if (i<xp->x_childvec_len)
1388 	memmove(&xp->x_childvec[i], &xp->x_childvec[i+1], (xp->x_childvec_len-i)*sizeof(cxobj*));
1389 #ifdef XML_EXPLICIT_INDEX
1390     if (xml_type(xc) == CX_ELMNT){
1391 	if (xml_search_index_p(xc))
1392 	    xml_search_child_rm(xp, xc);
1393 
1394     }
1395 #endif
1396     retval = 0;
1397  done:
1398     return retval;
1399 }
1400 
1401 /*! Remove this xml node from parent xml node. No freeing and node is new root
1402  * @param[in]   xc     xml child node to be removed
1403  * @retval      0      OK
1404  * @retval      -1
1405  * @note you should not remove xchild in loop (unless yoy keep track of xprev)
1406  *
1407  * @see xml_child_rm  Remove a child of a node
1408  */
1409 int
xml_rm(cxobj * xc)1410 xml_rm(cxobj *xc)
1411 {
1412     int    retval = -1;
1413     cxobj *xp;
1414     cxobj *x;
1415     int    i;
1416 
1417     if ((xp = xml_parent(xc)) == NULL)
1418 	goto ok;
1419     /* Find child in parent XXX: search? */
1420     x = NULL; i = 0;
1421     while ((x = xml_child_each(xp, x, -1)) != NULL) {
1422 	if (x == xc)
1423 	    break;
1424 	i++;
1425     }
1426     if (x != NULL)
1427 	if (xml_child_rm(xp, i) < 0)
1428 	    goto done;
1429  ok:
1430     retval = 0;
1431  done:
1432     return retval;
1433 }
1434 
1435 /*! Remove all children of specific type
1436  * @param[in] x    XML node
1437  * @param[in] type Remove all children of xn of this type
1438  * @retval    0    OK
1439  * @retval   -1    Error
1440  */
1441 int
xml_rm_children(cxobj * xp,enum cxobj_type type)1442 xml_rm_children(cxobj          *xp,
1443 		enum cxobj_type type)
1444 {
1445     int    retval = -1;
1446     cxobj *xc;
1447     int    i;
1448 
1449     if (!is_element(xp))
1450 	return 0;
1451     for (i=0; i<xml_child_nr(xp);){
1452 	xc = xml_child_i(xp, i);
1453 	if (xml_type(xc) != type){
1454 	    i++;
1455 	    continue;
1456 	}
1457 	if (xml_child_rm(xp, i) < 0)
1458 	    goto done;
1459 	xml_free(xc);
1460     }
1461     retval = 0;
1462  done:
1463     return retval;
1464 }
1465 
1466 /*! Remove top XML object and all children except a single child
1467  * Given a root xml node, and the i:th child, remove the child from its parent
1468  * and return it, remove the parent and all other children. (unwrap)
1469  * Before: xp-->[..xc..]
1470  * After: xc
1471  * @param[in]  xp   xml parent node. Will be deleted
1472  * @param[in]  i    Child nr in parent child vector
1473  * @param[out] xcp  xml child node. New root
1474  * @retval     0    OK
1475  * @retval    -1    Error
1476  * @code
1477  *   cxobj *xt = NULL;
1478  *   if (clixon_xml_parse_string("<a>2</a>", YB_NONE, NULL, &xt, NULL) < 0)
1479  *      err;
1480  *  # Here xt will be: <top><a>2</a></top>
1481  *   if (xml_rootchild(xt, 0, &xt) < 0)
1482  *      err;
1483  *  # Here xt will be: <a>2</a>
1484  * @endcode
1485  * @see xml_child_rm
1486  * @see xml_rootchild_node  where xc is explicitly given
1487  */
1488 int
xml_rootchild(cxobj * xp,int i,cxobj ** xcp)1489 xml_rootchild(cxobj  *xp,
1490 	      int     i,
1491 	      cxobj **xcp)
1492 {
1493     int    retval = -1;
1494     cxobj *xc;
1495 
1496     if (!is_element(xp))
1497 	return 0;
1498     if (xml_parent(xp) != NULL){
1499 	clicon_err(OE_XML, 0, "Parent is not root");
1500 	goto done;
1501     }
1502     if ((xc = xml_child_i(xp, i)) == NULL){
1503 	clicon_err(OE_XML, 0, "Child not found");
1504 	goto done;
1505     }
1506     if (xml_child_rm(xp, i) < 0)
1507 	goto done;
1508     if (xml_free(xp) < 0)
1509 	goto done;
1510     *xcp = xc;
1511     retval = 0;
1512  done:
1513     return retval;
1514 }
1515 
1516 /*! Remove top XML object and all children except a single (given) child
1517  * Given a root xml node, remove the child from its parent
1518  * , remove the parent and all other children. (unwrap)
1519  * Before: xp-->[..xc..]
1520  * After: xc
1521  * @param[in]  xp   xml parent node. Must be root. Will be deleted
1522  * @param[in]  xc   xml child node. Must be a child of xp
1523  * @retval     0    OK
1524  * @retval    -1    Error
1525  * @see xml_rootchild  where an index is used to find xc
1526  */
1527 int
xml_rootchild_node(cxobj * xp,cxobj * xc)1528 xml_rootchild_node(cxobj  *xp,
1529 		   cxobj  *xc)
1530 {
1531     int    retval = -1;
1532     cxobj *x;
1533     int    i;
1534 
1535     if (!is_element(xp))
1536 	return 0;
1537     if (xml_parent(xp) != NULL){
1538 	clicon_err(OE_XML, 0, "Parent is not root");
1539 	goto done;
1540     }
1541     x = NULL; i = 0;
1542     while ((x = xml_child_each(xp, x, -1)) != NULL) {
1543 	if (x == xc)
1544 	    break;
1545 	i++;
1546     }
1547     if (xml_child_rm(xp, i) < 0)
1548 	goto done;
1549     if (xml_free(xp) < 0)
1550 	goto done;
1551     retval = 0;
1552  done:
1553     return retval;
1554 }
1555 
1556 /*! Help function to sorting: enumerate all children according to present order
1557  * This is so that the child itself know its present order in a list.
1558  * When sorting by "ordered by user", the order should remain in its present
1559  * state.
1560  * A child can always compute its order functionally but it computes
1561  * more cycles,..
1562  * @param[in]  xp  Enumerate its children
1563  * @retval     0   OK
1564  * @see xml_sort
1565  * @see xml_enumerate_get  Call to the child to get the number
1566  */
1567 int
xml_enumerate_children(cxobj * xp)1568 xml_enumerate_children(cxobj *xp)
1569 {
1570     cxobj *x = NULL;
1571     int    i = 0;
1572 
1573     if (!is_element(xp))
1574 	return 0;
1575     while ((x = xml_child_each(xp, x, -1)) != NULL)
1576 	x->_x_i = i++;
1577     return 0;
1578 }
1579 
1580 /*! Reset enumeration as done by xml_enumerate_children
1581  */
1582 int
xml_enumerate_reset(cxobj * xp)1583 xml_enumerate_reset(cxobj *xp)
1584 {
1585     cxobj *x = NULL;
1586 
1587     if (!is_element(xp))
1588 	return 0;
1589     while ((x = xml_child_each(xp, x, -1)) != NULL)
1590 	x->_x_i = 0;
1591     return 0;
1592 }
1593 
1594 /*! Get the enumeration of a single child set by enumeration of parent
1595  * @see xml_children_enumerate
1596  * @note that it has to be called right after xml_children_enumerate. If not,
1597  * there are many cases where this info is stale.
1598  * @param[in]  x   A child whose parent has enumerated its children
1599  * @retval     n   Enumeration
1600  * @see xml_enumerate_children   Call to the parent to compute the nr
1601  */
1602 int
xml_enumerate_get(cxobj * x)1603 xml_enumerate_get(cxobj *x)
1604 {
1605     return x->_x_i;
1606 }
1607 
1608 /*! Get the first sub-node which is an XML body.
1609  * @param[in]   xn          xml tree node
1610  * @retval  The returned body as a pointer to the name string
1611  * @retval  NULL if no such node or no body in found node
1612  * Note, make a copy of the return value to use it properly
1613  * @see xml_find_body
1614  * Explaining picture:
1615  *       xt  --> xb (x_type=CX_BODY)
1616  *               return xb.x_value
1617  */
1618 char *
xml_body(cxobj * xn)1619 xml_body(cxobj *xn)
1620 {
1621     cxobj *xb = NULL;
1622 
1623     if (!is_element(xn))
1624 	return NULL;
1625     while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL)
1626 	return xml_value(xb);
1627     return NULL;
1628 }
1629 
1630 /*! Get (first) body of xml node, note could be many
1631  * @param[in]   xt          xml tree node
1632  * Explaining picture:
1633  *       xt  --> xb (x_type=CX_BODY)
1634  *               return xb
1635  */
1636 cxobj *
xml_body_get(cxobj * xt)1637 xml_body_get(cxobj *xt)
1638 {
1639     cxobj *xb = NULL;
1640 
1641     if (!is_element(xt))
1642 	return NULL;
1643     while ((xb = xml_child_each(xt, xb, CX_BODY)) != NULL)
1644 	return xb;
1645     return NULL;
1646 }
1647 
1648 /*! Find and return the value of an xml child of specific type given prefix and name
1649  *
1650  * The value can be of an attribute only
1651  * @param[in]   xt          xml tree node
1652  * @param[in]   prefix      Prefix (namespace local name) or NULL
1653  * @param[in]   name        name of xml tree node (eg attr name or "body")
1654  * @retval      val         Pointer to the name string
1655  * @retval      NULL        No such node or no value in node
1656  * @code
1657  * char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
1658  * @endcode
1659  * @note, make a copy of the return value to use it properly
1660  * @see xml_find_type return the xml object
1661  * @see xml_find_value where a body can be found as well
1662  */
1663 char *
xml_find_type_value(cxobj * xt,const char * prefix,const char * name,enum cxobj_type type)1664 xml_find_type_value(cxobj           *xt,
1665 		    const char      *prefix,
1666 		    const char      *name,
1667 		    enum cxobj_type  type)
1668 {
1669     cxobj *x;
1670 
1671     if (!is_element(xt))
1672 	return NULL;
1673     if ((x = xml_find_type(xt, prefix, name, type)) != NULL)
1674 	return xml_value(x);
1675     return NULL;
1676 }
1677 
1678 /*! Find and return the xml child of specific type given prefix and name
1679  *
1680  * The value can be of an attribute only
1681  * @param[in]   xt          xml tree node
1682  * @param[in]   prefix      Prefix (namespace local name) or NULL
1683  * @param[in]   name        name of xml tree node (eg attr name or "body")
1684  * @param[in]   type        Matching type or -1 for any
1685  * @retval      val         Pointer to the name string
1686  * @retval      NULL        No such node or no value in node
1687  * @code
1688  * cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
1689  * @endcode
1690  * @see xml_find  which finds any child given name
1691  * @see xml_find_value where a body can be found as well
1692  */
1693 cxobj *
xml_find_type(cxobj * xt,const char * prefix,const char * name,enum cxobj_type type)1694 xml_find_type(cxobj           *xt,
1695 	      const char      *prefix,
1696 	      const char      *name,
1697 	      enum cxobj_type  type)
1698 {
1699     cxobj *x = NULL;
1700     int    pmatch; /* prefix match */
1701     char  *xprefix;     /* xprefix */
1702 
1703     if (!is_element(xt))
1704 	return NULL;
1705     while ((x = xml_child_each(xt, x, type)) != NULL) {
1706 	if (prefix){
1707 	    xprefix = xml_prefix(x);
1708 	    pmatch = xprefix ? strcmp(prefix,xprefix)==0 : 0;
1709 	}
1710 	else
1711 	    pmatch = 1;
1712 	if (pmatch && strcmp(name, xml_name(x)) == 0)
1713 	    return x;
1714     }
1715     return NULL;
1716 }
1717 
1718 /*! Find and return the value of a sub xml node
1719  *
1720  * The value can be of an attribute or body.
1721  * @param[in]   xt          xml tree node
1722  * @param[in]   name        name of xml tree nod (eg attr name or "body")
1723  * @retval      val         Pointer to the name string
1724  * @retval      NULL        No such node or no value in node
1725  *
1726  * Note, make a copy of the return value to use it properly
1727  * @see xml_find_body
1728  * Explaining picture:
1729  *       xt  --> x
1730  *               x_name=name
1731  *               return x_value
1732  */
1733 char *
xml_find_value(cxobj * xt,const char * name)1734 xml_find_value(cxobj      *xt,
1735 	       const char *name)
1736 {
1737     cxobj *x = NULL;
1738 
1739     if (!is_element(xt))
1740 	return NULL;
1741     while ((x = xml_child_each(xt, x, -1)) != NULL)
1742 	if (strcmp(name, xml_name(x)) == 0)
1743 	    return xml_value(x);
1744     return NULL;
1745 }
1746 
1747 /*! Find and return a body (string) of a sub xml node
1748  * @param[in]   xn          xml tree node
1749  * @param[in]   name        name of xml tree node
1750  * @retval  The returned body as a pointer to the name string
1751  * @retval  NULL if no such node or no body in found node
1752  * @note, make a copy of the return value to use it properly
1753  * @see xml_find_value
1754  * Explaining picture:
1755  *       xt  --> x          --> bx (x_type=CX_BODY)
1756  *               x_name=name    return x_value
1757  */
1758 char *
xml_find_body(cxobj * xt,const char * name)1759 xml_find_body(cxobj      *xt,
1760 	      const char *name)
1761 {
1762     cxobj *x=NULL;
1763 
1764     if (!is_element(xt))
1765 	return NULL;
1766     while ((x = xml_child_each(xt, x, -1)) != NULL)
1767 	if (strcmp(name, xml_name(x)) == 0)
1768 	    return xml_body(x);
1769     return NULL;
1770 }
1771 
1772 /*! Find xml object with matching name and value.
1773  *
1774  * This can be useful if x is a leaf-list with many subs with same name,
1775  * but you need to pick the object with a specific value
1776  * @param[in]  xt   XML tree
1777  * @param[in]  name Name of child (there can be many with same name)
1778  * @param[in]  val  Value. Must be equal to body of child.
1779  * @retval     x    Child with matching name and body
1780  *
1781  * Explaining picture:
1782  *       xt  --> x          --> bx (x_type=CX_BODY)
1783  *               x_name=name    x_value=val
1784  *               return x
1785  */
1786 cxobj *
xml_find_body_obj(cxobj * xt,const char * name,char * val)1787 xml_find_body_obj(cxobj      *xt,
1788 		  const char *name,
1789 		  char       *val)
1790 {
1791     cxobj *x = NULL;
1792     char  *bstr;
1793 
1794     if (!is_element(xt))
1795 	return NULL;
1796     while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
1797 	if (strcmp(name, xml_name(x)))
1798 	    continue;
1799 	if ((bstr = xml_body(x)) == NULL)
1800 	    continue;
1801 	if (strcmp(bstr, val) == 0)
1802 	    break; /* x is returned */
1803     }
1804     return x;
1805 }
1806 
1807 /*! Free an xl sub-tree recursively, but do not remove it from parent
1808  * @param[in]  x  the xml tree to be freed.
1809  * @see xml_purge where x is also removed from parent
1810  */
1811 int
xml_free(cxobj * x)1812 xml_free(cxobj *x)
1813 {
1814     int    i;
1815     cxobj *xc;
1816 
1817     if (x->x_name)
1818 	free(x->x_name);
1819     if (x->x_prefix)
1820 	free(x->x_prefix);
1821     switch (xml_type(x)){
1822     case CX_ELMNT:
1823 	for (i=0; i<x->x_childvec_len; i++){
1824 	    if ((xc = x->x_childvec[i]) != NULL){
1825 		xml_free(xc);
1826 		x->x_childvec[i] = NULL;
1827 	    }
1828 	}
1829 	if (x->x_childvec)
1830 	    free(x->x_childvec);
1831 	if (x->x_cv)
1832 	    cv_free(x->x_cv);
1833 	if (x->x_ns_cache)
1834 	    xml_nsctx_free(x->x_ns_cache);
1835 #ifdef XML_EXPLICIT_INDEX
1836 	xml_search_index_free(x);
1837 #endif
1838 	break;
1839     case CX_BODY:
1840     case CX_ATTR:
1841 	if (x->x_value_cb)
1842 	    cbuf_free(x->x_value_cb);
1843 	break;
1844     default:
1845 	break;
1846     }
1847     free(x);
1848     _stats_nr--;
1849     return 0;
1850 }
1851 
1852 /*! Copy single xml node from x0 to x1 without copying children
1853  * @param[in]  x0  Source XML tree
1854  * @param[in]  x1  Destination XML tree (must exist)
1855  * @retval     0   OK
1856  * @retval    -1   Error
1857  */
1858 int
xml_copy_one(cxobj * x0,cxobj * x1)1859 xml_copy_one(cxobj *x0,
1860 	     cxobj *x1)
1861 {
1862     int   retval = -1;
1863     char *s;
1864 
1865     xml_type_set(x1, xml_type(x0));
1866     if ((s = xml_name(x0))) /* malloced string */
1867 	if ((xml_name_set(x1, s)) < 0)
1868 	    goto done;
1869     if ((s = xml_prefix(x0))) /* malloced string */
1870 	if ((xml_prefix_set(x1, s)) < 0)
1871 	    goto done;
1872     switch (xml_type(x0)){
1873     case CX_ELMNT:
1874 	xml_spec_set(x1, xml_spec(x0));
1875 	break;
1876     case CX_BODY:
1877     case CX_ATTR:
1878 	if ((s = xml_value(x0))){ /* malloced string */
1879 	    if (xml_value_set(x1, s) < 0)
1880 		goto done;
1881 
1882 	}
1883 	break;
1884     default:
1885 	break;
1886     }
1887     xml_flag_set(x1, xml_flag(x0, XML_FLAG_DEFAULT)); /* Maybe more flags */
1888     retval = 0;
1889  done:
1890     return retval;
1891 }
1892 
1893 /*! Copy xml tree x0 to other existing tree x1
1894  *
1895  * x1 should be a created placeholder. If x1 is non-empty,
1896  * the copied tree is appended to the existing tree.
1897  * @param[in]  x0  Source XML tree
1898  * @param[in]  x1  Destination XML tree (must exist)
1899  * @retval     0   OK
1900  * @retval    -1   Error
1901  * @code
1902  *   x1 = xml_new("new", xparent, xml_type(x0));
1903  *   if (xml_copy(x0, x1) < 0)
1904  *      err;
1905  * @endcode
1906  * @see xml_dup
1907  */
1908 int
xml_copy(cxobj * x0,cxobj * x1)1909 xml_copy(cxobj *x0,
1910 	 cxobj *x1)
1911 {
1912     int    retval = -1;
1913     cxobj *x;
1914     cxobj *xcopy;
1915 
1916     if (xml_copy_one(x0, x1) <0)
1917 	goto done;
1918     x = NULL;
1919     while ((x = xml_child_each(x0, x, -1)) != NULL) {
1920 	if ((xcopy = xml_new(xml_name(x), x1, xml_type(x))) == NULL)
1921 	    goto done;
1922 	if (xml_copy(x, xcopy) < 0) /* recursion */
1923 	    goto done;
1924     }
1925     retval = 0;
1926   done:
1927     return retval;
1928 }
1929 
1930 
1931 
1932 /*! Create and return a copy of xml tree.
1933  *
1934  * @code
1935  *   cxobj *x1;
1936  *   x1 = xml_dup(x0);
1937  * @endcode
1938  * Note, returned tree should be freed as: xml_free(x1)
1939  * @see xml_cp
1940  */
1941 cxobj *
xml_dup(cxobj * x0)1942 xml_dup(cxobj *x0)
1943 {
1944     cxobj *x1;
1945 
1946     if ((x1 = xml_new("new", NULL, xml_type(x0))) == NULL)
1947 	return NULL;
1948     if (xml_copy(x0, x1) < 0)
1949 	return NULL;
1950     return x1;
1951 }
1952 
1953 #if 1 /* XXX At some point migrate this code to the clixon_xml_vec.[ch] API */
1954 /*! Copy XML vector from vec0 to vec1
1955  * @param[in]  vec0    Source XML tree vector
1956  * @param[in]  len0    Length of source XML tree vector
1957  * @param[out] vec1    Destination XML tree vector
1958  * @param[out] len1    Length of destination XML tree vector
1959  */
1960 int
cxvec_dup(cxobj ** vec0,int len0,cxobj *** vec1,int * len1)1961 cxvec_dup(cxobj  **vec0,
1962 	  int      len0,
1963 	  cxobj ***vec1,
1964 	  int     *len1)
1965 {
1966     int retval = -1;
1967 
1968     *len1 = len0;
1969     if ((*vec1 = calloc(len0, sizeof(cxobj*))) == NULL)
1970 	goto done;
1971     memcpy(*vec1, vec0, len0*sizeof(cxobj*));
1972     retval = 0;
1973  done:
1974     return retval;
1975 }
1976 
1977 /*! Append a new xml tree to an existing xml vector last in the list
1978  * @param[in]      x      XML tree (append this to vector)
1979  * @param[in,out]  vec    XML tree vector
1980  * @param[in,out]  len    Length of XML tree vector
1981  * @retval         0      OK
1982  * @retval        -1      Error
1983  * @code
1984  *  cxobj  **xvec = NULL;
1985  *  int      xlen = 0;
1986  *  cxobj   *x;
1987  *
1988  *  if (cxvec_append(x, &xvec, &xlen) < 0)
1989  *     err;
1990  *  if (xvec)
1991  *     free(xvec);
1992  * @endcode
1993  * @see cxvec_prepend
1994  * @see clixon_cxvec_append  which is its own encapsulated xml vector datatype
1995  */
1996 int
cxvec_append(cxobj * x,cxobj *** vec,int * len)1997 cxvec_append(cxobj   *x,
1998 	     cxobj ***vec,
1999 	     int     *len)
2000 {
2001     int retval = -1;
2002 
2003     if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
2004 	clicon_err(OE_XML, errno, "realloc");
2005 	goto done;
2006     }
2007     (*vec)[(*len)++] = x;
2008     retval = 0;
2009  done:
2010     return retval;
2011 }
2012 
2013 /*! Prepend a new xml tree to an existing xml vector first in the list
2014  * @param[in]      x      XML tree (append this to vector)
2015  * @param[in,out]  vec    XML tree vector
2016  * @param[in,out]  len    Length of XML tree vector
2017  * @retval         0      OK
2018  * @retval        -1      Error
2019  * @code
2020  *  cxobj  **xvec = NULL;
2021  *  size_t   xlen = 0;
2022  *  cxobj   *x;
2023  *
2024  *  if (cxvec_append(x, &xvec, &xlen) < 0)
2025  *     err;
2026  *  if (xvec)
2027  *     free(xvec);
2028  * @endcode
2029  * @see cxvec_prepend
2030  * @see clixon_cxvec_prepend  which is its own encapsulated xml vector datatype
2031  */
2032 int
cxvec_prepend(cxobj * x,cxobj *** vec,int * len)2033 cxvec_prepend(cxobj   *x,
2034 	     cxobj ***vec,
2035 	     int     *len)
2036 {
2037     int retval = -1;
2038 
2039     if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
2040 	clicon_err(OE_XML, errno, "realloc");
2041 	goto done;
2042     }
2043     memmove(&(*vec)[1], &(*vec)[0], sizeof(cxobj *) * (*len));
2044     (*vec)[0] = x;
2045     (*len)++;
2046     retval = 0;
2047  done:
2048     return retval;
2049 }
2050 #endif
2051 
2052 /*! Apply a function call recursively on all xml node children recursively
2053  * Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
2054  * each object found. The function is called with the xml node and an
2055  * argument as args.
2056  * The tree is traversed depth-first, which at least guarantees that a parent is
2057  * traversed before a child.
2058  * @param[in]  xn   XML node
2059  * @param[in]  type Matching type or -1 for any
2060  * @param[in]  fn   Callback
2061  * @param[in]  arg  Argument
2062  * @retval    -1    Error, aborted at first error encounter
2063  * @retval     0    OK, all nodes traversed (subparts may have been skipped)
2064  * @retval     1    OK, aborted on first fn returned 1
2065  *
2066  * @code
2067  * int x_fn(cxobj *x, void *arg)
2068  * {
2069  *   return 0;
2070  * }
2071  * xml_apply(xn, CX_ELMNT, x_fn, NULL);
2072  * @endcode
2073  * @note do not delete or move around any children during this function
2074  * @note return value > 0 aborts the traversal
2075  * @see xml_apply0 including top object
2076  * @see xml_apply_ancestor for marking all parents recursively
2077  */
2078 int
xml_apply(cxobj * xn,enum cxobj_type type,xml_applyfn_t fn,void * arg)2079 xml_apply(cxobj          *xn,
2080 	  enum cxobj_type type,
2081 	  xml_applyfn_t   fn,
2082 	  void           *arg)
2083 {
2084     int        retval = -1;
2085     cxobj     *x;
2086     int        ret;
2087 
2088     if (!is_element(xn))
2089 	return 0;
2090     x = NULL;
2091     while ((x = xml_child_each(xn, x, type)) != NULL) {
2092 	if ((ret = fn(x, arg)) < 0)
2093 	    goto done;
2094 	if (ret == 2)
2095 	    continue; /* Abort this node, dont recurse */
2096 	else if (ret == 1){
2097 	    retval = 1;
2098 	    goto done;
2099 	}
2100 	if ((ret = xml_apply(x, type, fn, arg)) < 0)
2101 	    goto done;
2102 	if (ret == 1){
2103 	    retval = 1;
2104 	    goto done;
2105 	}
2106     }
2107     retval = 0;
2108   done:
2109     return retval;
2110 }
2111 
2112 /*! Apply a function call on top object and all xml node children recursively
2113  * @param[in]  xn   XML node
2114  * @param[in]  type Matching type or -1 for any
2115  * @param[in]  fn   Callback
2116  * @param[in]  arg  Argument
2117  * @retval    -1    Error, aborted at first error encounter
2118  * @retval     0    OK, all nodes traversed (subparts may have been skipped)
2119  * @retval     1    OK, aborted on first fn returned 1
2120  * @see xml_apply not including top object
2121  */
2122 int
xml_apply0(cxobj * xn,enum cxobj_type type,xml_applyfn_t fn,void * arg)2123 xml_apply0(cxobj          *xn,
2124 	  enum cxobj_type type,
2125 	  xml_applyfn_t   fn,
2126 	  void           *arg)
2127 {
2128     int        retval = -1;
2129     int        ret;
2130 
2131     if ((ret = fn(xn, arg)) < 0) /* -1, 0, 1, 2 */
2132 	goto done;
2133     if (ret == 1)
2134 	retval = 1;
2135     else  if (ret > 1)
2136 	retval = 0;
2137     else /* 0 */
2138 	retval = xml_apply(xn, type, fn, arg);
2139   done:
2140     return retval;
2141 }
2142 
2143 /*! Apply a function call recursively on all ancestors
2144  * Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for
2145  * each object found. The function is called with the xml node and an
2146  * argument as args.
2147  * @param[in]  xn   XML node
2148  * @param[in]  fn   Callback
2149  * @param[in]  arg  Argument
2150  * @retval    -1    Error, aborted at first error encounter
2151  * @retval     0    OK, all nodes traversed
2152  * @retval     n    OK, aborted at first encounter of first match
2153  * @code
2154  * int x_fn(cxobj *x, void *arg)
2155  * {
2156  *   return 0;
2157  * }
2158  * xml_apply_ancestor(xn, x_fn, NULL);
2159  * @endcode
2160  * @see xml_apply
2161  * @note do not delete or move around any children during this function
2162  * @note It does not apply fn to the root node,..
2163  */
2164 int
xml_apply_ancestor(cxobj * xn,xml_applyfn_t fn,void * arg)2165 xml_apply_ancestor(cxobj          *xn,
2166 		   xml_applyfn_t   fn,
2167 		   void           *arg)
2168 {
2169     int        retval = -1;
2170     cxobj     *xp = NULL;
2171     int        ret;
2172 
2173     while ((xp = xml_parent(xn)) != NULL) {
2174 	if (fn(xp, arg) < 0)
2175 	    goto done;
2176 	if ((ret = xml_apply_ancestor(xp, fn, arg)) < 0)
2177 	    goto done;
2178 	if (ret > 0){
2179 	    retval = ret;
2180 	    goto done;
2181 	}
2182 	xn = xp;
2183     }
2184     retval = 0;
2185   done:
2186     return retval;
2187 }
2188 
2189 /*! Is xpp ancestor of x?
2190  * @param[in]   x       XML node
2191  * @param[in]   xpp     Potential ancestor of x in XML tree
2192  * @retval      0       No, xpp is not ancestor of x
2193  * @retval      1       Yes, xpp is ancestor of x
2194  */
2195 int
xml_isancestor(cxobj * x,cxobj * xpp)2196 xml_isancestor(cxobj *x,
2197 	       cxobj *xpp)
2198 {
2199     cxobj     *xp = NULL;
2200     cxobj     *xn = NULL;
2201 
2202     xn = x;
2203     while ((xp = xml_parent(xn)) != NULL) {
2204 	if (xp == xpp)
2205 	    return 1;
2206 	xn = xp;
2207     }
2208     return 0;
2209 }
2210 
2211 /*! Get ultimate root ancestor of an xml-node: top-level node without parent
2212  * @param[in]   xn      XML node
2213  * @retval      xr      XML root node (can be xn)
2214  */
2215 cxobj *
xml_root(cxobj * xn)2216 xml_root(cxobj *xn)
2217 {
2218     cxobj     *xp = NULL;
2219     cxobj     *x = NULL;
2220 
2221     x = xn;
2222     while ((xp = xml_parent(x)) != NULL)
2223 	x = xp;
2224     return x;
2225 }
2226 
2227 /*! Map xml operation from string to enumeration
2228  * @param[in]   opstr  String, eg "merge"
2229  * @param[out]  op     Enumeration, eg OP_MERGE
2230  * @code
2231  *   enum operation_type op;
2232  *   xml_operation("replace", &op)
2233  * @endcode
2234  */
2235 int
xml_operation(char * opstr,enum operation_type * op)2236 xml_operation(char                *opstr,
2237 	      enum operation_type *op)
2238 {
2239     if (strcmp("merge", opstr) == 0)
2240 	*op = OP_MERGE;
2241     else if (strcmp("replace", opstr) == 0)
2242 	*op = OP_REPLACE;
2243     else if (strcmp("create", opstr) == 0)
2244 	*op = OP_CREATE;
2245     else if (strcmp("delete", opstr) == 0)
2246 	*op = OP_DELETE;
2247     else if (strcmp("remove", opstr) == 0)
2248 	*op = OP_REMOVE;
2249     else if (strcmp("none", opstr) == 0)
2250 	*op = OP_NONE;
2251     else{
2252 	clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
2253 	return -1;
2254     }
2255     return 0;
2256 }
2257 
2258 /*! Map xml operation from enumeration to string
2259  * @param[in]   op   enumeration operation, eg OP_MERGE,...
2260  * @retval      str  String, eg "merge". Static string, no free necessary
2261  * @code
2262  *   enum operation_type op;
2263  *   xml_operation("replace", &op)
2264  * @endcode
2265  */
2266 char *
xml_operation2str(enum operation_type op)2267 xml_operation2str(enum operation_type op)
2268 {
2269     switch (op){
2270     case OP_MERGE:
2271 	return "merge";
2272 	break;
2273     case OP_REPLACE:
2274 	return "replace";
2275 	break;
2276     case OP_CREATE:
2277 	return "create";
2278 	break;
2279     case OP_DELETE:
2280 	return "delete";
2281 	break;
2282     case OP_REMOVE:
2283 	return "remove";
2284 	break;
2285     default:
2286 	return "none";
2287     }
2288 }
2289 /*! Map xml insert attribute from string to enumeration
2290  * @param[in]   instr String, eg "first"
2291  * @param[out]  ins   Enumeration, eg INS_FIRST
2292  * @code
2293  *   enum insert_type ins;
2294  *   xml_operation("last", &ins)
2295  * @endcode
2296  */
2297 int
xml_attr_insert2val(char * instr,enum insert_type * ins)2298 xml_attr_insert2val(char             *instr,
2299 		    enum insert_type *ins)
2300 {
2301     if (strcmp("first", instr) == 0)
2302 	*ins = INS_FIRST;
2303     else if (strcmp("last", instr) == 0)
2304 	*ins = INS_LAST;
2305     else if (strcmp("before", instr) == 0)
2306 	*ins = INS_BEFORE;
2307     else if (strcmp("after", instr) == 0)
2308 	*ins = INS_AFTER;
2309     else{
2310 	clicon_err(OE_XML, 0, "Bad-attribute operation: %s", instr);
2311 	return -1;
2312     }
2313     return 0;
2314 }
2315 
2316 /*! Specialization of clicon_debug with xml tree
2317  * @param[in]  level    log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG.
2318  * @param[in]  x        XML tree that is logged without prettyprint
2319  * @param[in]  format   Message to print as argv.
2320 */
2321 int
clicon_log_xml(int level,cxobj * x,const char * format,...)2322 clicon_log_xml(int         level,
2323 	       cxobj      *x,
2324 	       const char *format, ...)
2325 {
2326     va_list args;
2327     int     len;
2328     char   *msg = NULL;
2329     cbuf   *cb = NULL;
2330     int     retval = -1;
2331 
2332     /* Print xml as cbuf */
2333     if ((cb = cbuf_new()) == NULL){
2334 	clicon_err(OE_XML, errno, "cbuf_new");
2335 	goto done;
2336     }
2337     if (clicon_xml2cbuf(cb, x, 0, 0, -1) < 0)
2338 	goto done;
2339 
2340     /* first round: compute length of debug message */
2341     va_start(args, format);
2342     len = vsnprintf(NULL, 0, format, args);
2343     va_end(args);
2344 
2345     /* allocate a message string exactly fitting the message length */
2346     if ((msg = malloc(len+1)) == NULL){
2347 	fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
2348 	goto done;
2349     }
2350 
2351     /* second round: compute write message from format and args */
2352     va_start(args, format);
2353     if (vsnprintf(msg, len+1, format, args) < 0){
2354 	va_end(args);
2355 	fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
2356 	goto done;
2357     }
2358     va_end(args);
2359 
2360     /* Actually log it */
2361     clicon_log(level, "%s: %s", msg, cbuf_get(cb));
2362 
2363     retval = 0;
2364   done:
2365     if (cb)
2366 	cbuf_free(cb);
2367     if (msg)
2368 	free(msg);
2369     return retval;
2370 }
2371 
2372 #ifdef XML_EXPLICIT_INDEX
2373 /*
2374  *
2375  */
2376 
2377 /*! Is this XML object a search index, ie it is registered as a yang clixon cc:search_index
2378  * Is this xml node a search index and does it have a parent that is a list and a grandparent
2379  * where a search-vector can be placed
2380  * @param[in] x  XML object
2381  * @retval    1  Yes
2382  * @retval    0  No
2383  */
2384 int
xml_search_index_p(cxobj * x)2385 xml_search_index_p(cxobj *x)
2386 {
2387     yang_stmt *y;
2388     cxobj     *xp;
2389 
2390     /* The index variable has a yang spec */
2391     if ((y = xml_spec(x)) == NULL)
2392 	return 0;
2393     /* The index variable is a registered search index */
2394     if (yang_flag_get(y, YANG_FLAG_INDEX) == 0)
2395 	return 0;
2396     /* The index variable has a parent which has a LIST yang spec  */
2397     if ((xp = xml_parent(x)) == NULL)
2398 	return 0;
2399     if ((y = xml_spec(xp)) == NULL)
2400 	return 0;
2401     if (yang_keyword_get(y) != Y_LIST)
2402 	return 0;
2403     /* The index variable has a grand-parent */
2404     if (xml_parent(xp) == NULL)
2405 	return 0;
2406     return 1;
2407 }
2408 
2409 
2410 /*! Free all search vector pairs of this XML node
2411  * @param[in]  x    XML object
2412  * @retval     0    OK
2413  * @retval    -1    Error
2414  */
2415 static int
xml_search_index_free(cxobj * x)2416 xml_search_index_free(cxobj *x)
2417 {
2418     struct search_index *si;
2419 
2420     while ((si = x->x_search_index) != NULL) {
2421 	DELQ(si, x->x_search_index, struct search_index *);
2422 	if (si->si_name)
2423 	    free(si->si_name);
2424 	if (si->si_xvec)
2425 	    clixon_xvec_free(si->si_xvec);
2426 	free(si);
2427     }
2428     return 0;
2429 }
2430 
2431 /*! Add single search vector pair to this XML node
2432  * @param[in]  x     XML object
2433  * @param[in]  name  Name of index variable
2434  * @retval     0     OK
2435  * @retval    -1     Error
2436  */
2437 static struct search_index *
xml_search_index_add(cxobj * x,char * name)2438 xml_search_index_add(cxobj *x,
2439 		     char  *name)
2440 {
2441     struct search_index *si = NULL;
2442 
2443     if ((si = malloc(sizeof(struct search_index))) == NULL){
2444 	clicon_err(OE_XML, errno, "malloc");
2445 	goto done;
2446     }
2447     memset(si, 0, sizeof(struct search_index));
2448     if ((si->si_name = strdup(name)) == NULL){
2449 	clicon_err(OE_XML, errno, "strdup");
2450 	free(si);
2451 	si = NULL;
2452 	goto done;
2453     }
2454     if ((si->si_xvec = clixon_xvec_new()) == NULL){
2455 	free(si->si_name);
2456 	free(si);
2457 	si = NULL;
2458 	goto done;
2459     }
2460     ADDQ(si, x->x_search_index);
2461  done:
2462     return si;
2463 }
2464 
2465 /*! Add single search vector pair to this XML node
2466  * @param[in]  x     XML object
2467  * @param[in]  name  Name of index variable
2468  * @retval     0     OK
2469  * @retval    -1     Error
2470  */
2471 static struct search_index *
xml_search_index_get(cxobj * x,char * name)2472 xml_search_index_get(cxobj *x,
2473 		     char  *name)
2474 {
2475     struct search_index *si = NULL;
2476 
2477     if ((si = x->x_search_index) != NULL) {
2478 	do {
2479 	    if (strcmp(si->si_name, name) == 0){
2480 		goto done;
2481 		break;
2482 	    }
2483 	    si = NEXTQ(struct search_index *, si);
2484 	} while (si && si != x->x_search_index);
2485     }
2486  done:
2487     return si;
2488 }
2489 
2490 /*--------------------------------------------------*/
2491 
2492 /*! Get sorted index vector for list for variable "name"
2493  * @param[in]  xp    XML parent object
2494  * @param[in]  name  Name of index variable
2495  * @param[out] xvec  XML object search vector
2496  * @retval     0     OK
2497  */
2498 int
xml_search_vector_get(cxobj * xp,char * name,clixon_xvec ** xvec)2499 xml_search_vector_get(cxobj        *xp,
2500 		      char         *name,
2501 		      clixon_xvec **xvec)
2502 {
2503     struct search_index *si;
2504 
2505     *xvec = NULL;
2506     if ((si = xp->x_search_index) != NULL) {
2507 	do {
2508 	    if (strcmp(si->si_name, name) == 0){
2509 		*xvec = si->si_xvec;
2510 		break;
2511 	    }
2512 	    si = NEXTQ(struct search_index *, si);
2513 	} while (si && si != xp->x_search_index);
2514     }
2515     return 0;
2516 }
2517 
2518 /*! Insert a new cxobj into search index vector for list for variable "name"
2519  * @param[in] xp XML parent object (the list element)
2520  * @param[in] xi XML index object (that should be added)
2521  */
2522 int
xml_search_child_insert(cxobj * xp,cxobj * xi)2523 xml_search_child_insert(cxobj *xp,
2524 			cxobj *xi)
2525 {
2526     int                  retval = -1;
2527     char                *indexvar;
2528     struct search_index *si;
2529     cxobj               *xpp;
2530     int                  i;
2531     int                  len;
2532 
2533     indexvar = xml_name(xi);
2534     if ((xpp = xml_parent(xp)) == NULL)
2535 	goto ok;
2536     /* Find base vector in grandparent */
2537     if ((si = xml_search_index_get(xpp, indexvar)) == NULL){
2538 	/* If not found add base vector in grand-parent */
2539 	if ((si = xml_search_index_add(xpp, indexvar)) == NULL)
2540 	    goto done;
2541     }
2542     /* Find element position using binary search and then remove */
2543     len = clixon_xvec_len(si->si_xvec);
2544     if ((i = xml_search_indexvar_binary_pos(xp, indexvar, si->si_xvec, 0, len, len, NULL)) < 0)
2545 	goto done;
2546     assert(clixon_xvec_i(si->si_xvec, i) != xp);
2547     if (clixon_xvec_insert_pos(si->si_xvec, xp, i) < 0)
2548 	goto done;
2549  ok:
2550     retval = 0;
2551  done:
2552     return retval;
2553 }
2554 
2555 /*! Remove a single cxobj from search vector
2556  * @param[in] xp  XML parent object (the list element)
2557  * @param[in] xi  XML index object (that should be added)
2558  */
2559 int
xml_search_child_rm(cxobj * xp,cxobj * xi)2560 xml_search_child_rm(cxobj *xp,
2561 		    cxobj *xi)
2562 {
2563     int                 retval = -1;
2564     cxobj              *xpp;
2565     char               *indexvar;
2566     int                 i;
2567     int                 len;
2568     struct search_index *si;
2569     int                  eq = 0;
2570 
2571     indexvar = xml_name(xi);
2572     if ((xpp = xml_parent(xp)) == NULL)
2573 	goto ok;
2574     /* Find base vector in grandparent */
2575     if ((si = xml_search_index_get(xpp, indexvar)) == NULL)
2576 	goto ok;
2577 
2578     /* Find element using binary search and then remove */
2579     len = clixon_xvec_len(si->si_xvec);
2580     if ((i = xml_search_indexvar_binary_pos(xp, indexvar, si->si_xvec, 0, len, len, &eq)) < 0)
2581 	goto done;
2582 	//    if (clixon_xvec_i(si->si_xvec, i) == xp)
2583     if (eq)
2584 	if (clixon_xvec_rm_pos(si->si_xvec, i) < 0)
2585 	    goto done;
2586  ok:
2587     retval = 0;
2588  done:
2589     return retval;
2590 }
2591 
2592 /*! Iterator over xml children objects using (explicit) index variable
2593  *
2594  * @param[in] xparent xml tree node whose children should be iterated
2595  * @param[in] name    Name of index variable
2596  * @param[in] xprev   previous child, or NULL on init
2597  * @param[in] type    matching type or -1 for any
2598  * @code
2599  *   cxobj *x = NULL;
2600  *   while ((x = xml_child_index_each(x_top, "i", x, -1)) != NULL) {
2601  *     ...
2602  *   }
2603  * @endcode
2604  * @see xml_child_each  for looping over structural children.
2605  * @note uses _x_vector_i as a shared resource: you cannot mix loops over same parent
2606  * Further, never manipulate the child-list during operation or using the
2607  * same object recursively, the function uses an internal field to remember the
2608  * index used. It works as long as the same object is not iterated concurrently.
2609  * If you need to delete a node you can do somethjing like:
2610  */
2611 cxobj *
xml_child_index_each(cxobj * xparent,char * name,cxobj * xprev,enum cxobj_type type)2612 xml_child_index_each(cxobj           *xparent,
2613 		     char            *name,
2614 		     cxobj           *xprev,
2615 		     enum cxobj_type  type)
2616 {
2617     cxobj        *xn = NULL;
2618     clixon_xvec  *xv = NULL;
2619     int           i;
2620 
2621     if (xparent == NULL)
2622 	return NULL;
2623     if (!is_element(xparent))
2624 	return NULL;
2625     if (xml_search_vector_get(xparent, name, &xv) < 0)
2626 	return NULL;
2627     if (xv == NULL)
2628 	return NULL;
2629     for (i=xprev?xprev->_x_vector_i+1:0; i<clixon_xvec_len(xv); i++){
2630 	if ((xn = clixon_xvec_i(xv, i)) == NULL)
2631 	    continue;
2632 	if (type != CX_ERROR && xml_type(xn) != type)
2633 	    continue;
2634 	break; /* this is next object after previous */
2635     }
2636     if (i < clixon_xvec_len(xv)) /* found */
2637 	xn->_x_vector_i = i;
2638     else
2639 	xn = NULL;
2640     return xn;
2641 }
2642 
2643 #endif /* XML_EXPLICIT_INDEX */
2644