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