/*
 *
  ***** BEGIN LICENSE BLOCK *****
 
  Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
  Copyright (C) 2017-2019 Olof Hagsand
  Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgate)

  This file is part of CLIXON.

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

  Alternatively, the contents of this file may be used under the terms of
  the GNU General Public License Version 3 or later (the "GPL"),
  in which case the provisions of the GPL are applicable instead
  of those above. If you wish to allow use of your version of this file only
  under the terms of the GPL, and not to allow others to
  use your version of this file under the terms of Apache License version 2, 
  indicate your decision by deleting the provisions above and replace them with
  the  notice and other provisions required by the GPL. If you do not delete
  the provisions above, a recipient may use your version of this file under
  the terms of any one of the Apache License version 2 or the GPL.

  ***** END LICENSE BLOCK *****

 * Clixon XML object (cxobj) support functions.
 * @see https://www.w3.org/TR/2008/REC-xml-20081126
 *      https://www.w3.org/TR/2009/REC-xml-names-20091208
 * Canonical XML version (just for info)
 *      https://www.w3.org/TR/xml-c14n
 */

#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <assert.h>

/* cligen */
#include <cligen/cligen.h>

/* clixon */
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h" /* xml_bind_yang */
#include "clixon_yang_module.h"
#include "clixon_xml_map.h" /* xml_bind_yang */
#include "clixon_xml_vec.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_io.h"
#include "clixon_xml_parse.h"
#include "clixon_xml_nsctx.h"

/*
 * Constants
 */
/* How many XML children to start with if any. Then add quadratic until threshold when
 * add lineraly 
 * Heurestics: if child is body only single child is expected, but element children may
 * have siblings
 */
#define XML_CHILDVEC_SIZE_START 1        
#define XML_CHILDVEC_SIZE_START_ELMNT 16 
#define XML_CHILDVEC_SIZE_THRESHOLD 65536

/* Intention of these macros is to guard against access of type-specific fields 
 * As debug they can contain an assert.
 */
#define is_element(x) (xml_type(x)==CX_ELMNT)
#define is_bodyattr(x) (xml_type(x)==CX_BODY || xml_type(x)==CX_ATTR)

/*
 * Types
 */

#ifdef XML_EXPLICIT_INDEX
static int xml_search_index_free(cxobj *x);

/* A search index pair consisting of a name of an (index) variable and a vector of xml children
 * the variable should be a potential child of the XML node
 * The vector should have the same elements as the regular XML childvec, but in different order
 *
 *                        +-----+-----+-----+
 * search index vector i: |  b  |  c  |  a  |
 *                        +-----+-----+-----+
 *
 *                    index: "i"
 *               +-----+-----+-----+
 * x_childvec:   |  a  |  b  |  c  |
 *               +-----+-----+-----+
 *                  |     |     |
 *                  v     v     v
 *                +---+ +---+ +---+
 * value of "i"   | 5 | | 0 | | 2 |
 *                +---+ +---+ +---+

 */
struct search_index{
    qelem_t      si_q;    /* Queue header */
    char        *si_name; /* Name of index variable (must be (potential) child of xml node at hand */
    clixon_xvec *si_xvec; /* Sorted vector of xml object pointers (should be of YANG type LIST) */
};
#endif

/*! xml tree node, with name, type, parent, children, etc 
 * Note that this is a private type not visible from externally, use
 * access functions.
 * A word on ordering of x_children:
 * If there is no yang specification, xml children are ordered as they are entered.
 * If there is a yang specification (and the appropriate functions are called) the
 * xml children are ordered as follows:
 * 1) After yang specification order.
 * 2) list and leaf-list are sorted alphabetically unless ordered-by user.
 * Example:
 * container c{
 *  leaf a;
 *  leaf-list x;
 * }
 * then regardless in which order the xml is entered, it will be sorted as follows:
 * <c>
 *   <a/>
 *   <x>a</<x>
 *   <x>b</<x>
 * </c>
 * From https://www.w3.org/TR/2009/REC-xml-names-20091208
 * Definitions:
 * - XML namespace: is identified by a URI reference [RFC3986]; element and 
 *   attribute names may be placed in an XML namespace using the mechanisms 
 *   described in this specification.
 * - Expanded name: is a pair consisting of a namespace name and a local name.
 * - Namespace name: For a name N in a namespace identified by a URI I, the 
 *   "namespace name" is I.
 *   For a name N that is not in a namespace, the "namespace name" has no value.
 * - Local name: In either case the "local name" is N (also "prefix")
 * It is this combination of the universally managed URI namespace with the 
 * vocabulary's local names that is effective in avoiding name clashes.
 * @see struct xmlbody    For XML body and attributes
 */
struct xml{
    enum cxobj_type   x_type;       /* type of node: element, attribute, body */
    char             *x_name;       /* name of node */
    char             *x_prefix;     /* namespace localname N, called prefix */
    uint16_t          x_flags;      /* Flags according to XML_FLAG_* */
    struct xml       *x_up;         /* parent node in hierarchy if any */
#ifdef XML_PARENT_CANDIDATE
    struct xml       *x_up_candidate; /* Candidate parent node for special cases (when+xpath) */
#endif
    int              _x_vector_i;   /* internal use: xml_child_each */
    int              _x_i;          /* internal use for stable sorting: 
                                       see xml_enumerate and xml_cmp */
    /*----- next is body/attribute only */
    cbuf             *x_value_cb;  /* attribute and body nodes have values (XXX: this consumes 
                                       memory) cv? */
    /*----- up to here is common to all next is element only */
    struct xml      **x_childvec;   /* vector of children nodes (XXX: use clixon_vec ) */
    int               x_childvec_len;/* Number of children */
    int               x_childvec_max;/* Length of allocated vector */

    cvec             *x_ns_cache;   /* Cached vector of namespaces (set by bind-yang) */
    yang_stmt        *x_spec;       /* Pointer to specification, eg yang, 
                                       by reference, dont free */
    cg_var           *x_cv;         /* Cached value as cligen variable (set by xml_cmp) */
    cvec             *x_creators;   /* Support clixon-lib creator annotation */
#ifdef XML_EXPLICIT_INDEX
    struct search_index *x_search_index; /* explicit search index vectors */
#endif
};

/* Variant of struct xml for use by non-elements to save space
 * @see struct xml  For XML elements
 */
struct xmlbody{
    enum cxobj_type   xb_type;       /* type of node: element, attribute, body */
    char             *xb_name;       /* name of node */
    char             *xb_prefix;     /* namespace localname N, called prefix */
    uint16_t          xb_flags;      /* Flags according to XML_FLAG_* */
    struct xml       *xb_up;         /* parent node in hierarchy if any */
#ifdef XML_PARENT_CANDIDATE
    struct xml       *xb_up_candidate; /* Candidate parent node for special cases (when+xpath) */
#endif
    int              _xb_vector_i;   /* internal use: xml_child_each */
    int              _xb_i;          /* internal use for sorting: 
                                       see xml_enumerate and xml_cmp */
    cbuf             *xb_value_cb;  /* attribute and body nodes have values */
};

/*
 * Variables
 */

/* Mapping between xml type <--> string */
static const map_str2int xsmap[] = {
    {"error",         CX_ERROR}, 
    {"element",       CX_ELMNT}, 
    {"attr",          CX_ATTR}, 
    {"body",          CX_BODY},
    {NULL,           -1}
};

/*! Translate from xml type in enum form to string keyword
 * @param[in] type  Xml type
 * @retval    str   String keyword
 */
char *
xml_type2str(enum cxobj_type type)
{
    return (char*)clicon_int2str(xsmap, type);
}

/* Stats (too low-level to hang it on handle) */
static uint64_t _stats_xml_nr = 0;

/*! Get global statistics about XML objects
 *
 * @param[out]  nr  Number of existing XML objects (created - freed)
 */
int
xml_stats_global(uint64_t *nr)
{
    if (nr)
        *nr = _stats_xml_nr;
    return 0;
}

/*! Return the alloced memory of a single XML obj 
 * @param[in]   x    XML object
 * @param[out]  szp  Size of this XML obj
 * @retval      0    OK
 * (baseline: 96 bytes per object on x86-64)
 */
static int
xml_stats_one(cxobj    *x,
              size_t   *szp)
{
    size_t sz = 0;

    if (x->x_name)
        sz += strlen(x->x_name) + 1;
    if (x->x_prefix)
        sz += strlen(x->x_prefix) + 1;
    switch (xml_type(x)){
    case CX_ELMNT:
        sz += sizeof(struct xml);
        sz += x->x_childvec_max*sizeof(struct xml*);
        if (x->x_ns_cache)
            sz += cvec_size(x->x_ns_cache);
        if (x->x_cv)
            sz += cv_size(x->x_cv);
#ifdef XML_EXPLICIT_INDEX
        if (x->x_search_index){
            /* XXX: only one */
            sz += sizeof(struct search_index);
            if (x->x_search_index->si_name)
                sz += strlen(x->x_search_index->si_name)+1;
            if (x->x_search_index->si_xvec)
                sz += clixon_xvec_len(x->x_search_index->si_xvec)*sizeof(struct cxobj*);
        }
#endif
        break;
    case CX_BODY:
    case CX_ATTR:
        sz += sizeof(struct xmlbody);
        if (x->x_value_cb)
            sz += cbuf_buflen(x->x_value_cb);
        break;
    default:
        break;
    }
    if (szp)
        *szp = sz;
    return 0;
}

/*! Return statistics of an XML tree recursively
 * @param[in]   xt   XML object
 * @param[out]  nrp  Number of XML obj recursively
 * @param[out]  szp  Size of this XML obj recursively
 * @retval      0    OK
 * @retval     -1    Error
 */
int
xml_stats(cxobj    *xt,
          uint64_t *nrp,
          size_t   *szp)
{
    int    retval = -1;
    size_t sz = 0;
    cxobj *xc;

    if (xt == NULL){
        clicon_err(OE_XML, EINVAL, "xml node is NULL");
        goto done;
    }
    *nrp += 1;
    xml_stats_one(xt, &sz);
    if (szp)
        *szp += sz;
    xc = NULL;
    while ((xc = xml_child_each(xt, xc, -1)) != NULL) {
        sz=0;
        xml_stats(xc, nrp, &sz);
        if (szp)
            *szp += sz;
    }
    retval = 0;
 done:
    return retval;
}

/*
 * Access functions
 */
/*! Get name of xnode
 * @param[in]  xn    xml node
 * @retval     name of xml node
 */
char*
xml_name(cxobj *xn)
{
    if (xn == NULL) {
        return NULL;
    }
    return xn->x_name;
}

/*! Set name of xnode, name is copied
 * @param[in]  xn    xml node
 * @param[in]  name  new name, null-terminated string, copied by function
 * @retval     0     OK
 * @retval     -1    on error with clicon-err set
 */
int
xml_name_set(cxobj *xn, 
             char  *name)
{
    if (xn->x_name){
        free(xn->x_name);
        xn->x_name = NULL;
    }
    if (name){
        if ((xn->x_name = strdup(name)) == NULL){
            clicon_err(OE_XML, errno, "strdup");
            return -1;
        }
    }
    return 0;
}

/*! Get prefix of xnode
 * @param[in]  xn     xml node
 * @retval     prefix of xml node
 */
char*
xml_prefix(cxobj *xn)
{
    return xn->x_prefix;
}

/*! Set prefix of xnode, prefix is copied
 * @param[in]  xn      XML node
 * @param[in]  prefix  New prefix, null-terminated string, copied by function
 * @retval     0       OK
 * @retval     -1      Error with clicon-err set
 */
int
xml_prefix_set(cxobj *xn, 
               char  *prefix)
{
    if (xn->x_prefix){
        free(xn->x_prefix);
        xn->x_prefix = NULL;
    }
    if (prefix){
        if ((xn->x_prefix = strdup(prefix)) == NULL){
            clicon_err(OE_XML, errno, "strdup");
            return -1;
        }
    }
    return 0;
}

/*! Get cached namespace (given prefix)
 * @param[in] x      XML node
 * @param[in] prefix Namespace prefix, or NULL for default
 * @retval    ns     Cached namespace
 * @retval    NULL   No namespace found (not cached or not found)
 * @note may want to distinguish between not set cache and no namespace?
 */
char*
nscache_get(cxobj *x,
            char  *prefix)
{
    if (!is_element(x))
        return NULL;
    if (x->x_ns_cache != NULL)
        return xml_nsctx_get(x->x_ns_cache, prefix);
    return NULL;
}

/*! Get cached prefix (given namespace) 
 * @param[in]  x         XML node
 * @param[in]  namespace
 * @param[out] prefix
 * @retval     1      Prefix found
 * @retval     0      No prefix found
 */
int
nscache_get_prefix(cxobj *x,
                   char  *namespace,
                   char **prefix)
{
    if (!is_element(x))
        return 0;
    if (x->x_ns_cache != NULL)
        return xml_nsctx_get_prefix(x->x_ns_cache, namespace, prefix);
    return 0;
}

/*! Dump whole namespace context cache of one xml node 
 * @param[in]  x    XML node
 * @retval     nsc  Whole namespace context of x
 * @retval     NULL Empty nsc
 * @see nscache_get  For a single prefix
 */
cvec *
nscache_get_all(cxobj *x)
{
    if (!is_element(x))
        return NULL;
    return x->x_ns_cache;
}

/*! Set cached namespace for specific namespace. Replace if necessary
 * @param[in] x         XML node
 * @param[in] prefix    Namespace prefix, or NULL for default
 * @param[in] namespace Cached namespace to set (assume non-null?)
 * @retval    0         OK
 * @retval   -1         Error
 * @see nscache_replace  to replace the whole context
 */
int
nscache_set(cxobj *x,
            char  *prefix,
            char  *namespace)
{
    int     retval = -1;

    if (!is_element(x))
        return 0;
    if (x->x_ns_cache == NULL){
        if ((x->x_ns_cache = xml_nsctx_init(prefix, namespace)) == NULL)
            goto done;
    }
    else 
        return xml_nsctx_add(x->x_ns_cache, prefix, namespace);
    retval = 0;
 done:
    return retval;
}

/*! Set complete cached namespace context
 * @param[in] x      XML node
 * @param[in] nsc    Namespace context (note consumed, dont free)
 * @retval    0      OK
 * @retval   -1      Error
 * @see nscache_set  set a single cache line
 */
int
nscache_replace(cxobj *x,
                cvec  *nsc)
{
    int     retval = -1;

    if (!is_element(x))
        return 0;
    if (x->x_ns_cache != NULL){
        xml_nsctx_free(x->x_ns_cache);
        x->x_ns_cache = NULL;
    }
    x->x_ns_cache = nsc;
    retval = 0;
    // done:
    return retval;
}

/*! Clear cached namespace context
 * Clear the whole namespace context, not just single cache lines
 * @param[in] x         XML node
 * @retval    0         OK
 * @see nscache_set  For setting specific namespace cache lines
 * @see xml_addsub
 */
int
nscache_clear(cxobj *x)
{

    if (!is_element(x))
        return 0;
    if (x->x_ns_cache != NULL){
        xml_nsctx_free(x->x_ns_cache);
        x->x_ns_cache = NULL;
    }
    return 0;
}

/*! Get parent of xnode
 * @param[in]  xn    xml node
 * @retval     parent xml node
 */
cxobj*
xml_parent(cxobj *xn)
{
    if (xn == NULL) {
        return NULL;
    }
    return xn->x_up;
}

/*! Set parent of xml node.
 * @param[in]  xn      xml node
 * @param[in]  parent  pointer to new parent xml node
 * @retval     0       OK
 * @see xml_child_rm  remove child from parent
 */
int
xml_parent_set(cxobj *xn, 
               cxobj *parent)
{
    xn->x_up = parent;
    return 0;
}

#ifdef XML_PARENT_CANDIDATE
/*! Get candidate parent of xnode
 * @param[in]  xn    xml node
 * @retval     parent xml node
 */
cxobj*
xml_parent_candidate(cxobj *xn)
{
    if (xn == NULL) {
        return NULL;
    }
    return xn->x_up_candidate;
}

/*! Set candidate parent of xml node
 * @param[in]  xn      xml node
 * @param[in]  parent  pointer to candidate parent xml node
 * @retval     0       OK
 */
int
xml_parent_candidate_set(cxobj *xn, 
                         cxobj *parent)
{
    xn->x_up_candidate = parent;
    return 0;
}
#endif /* XML_PARENT_CANDIDATE */

/*! Get xml node flags, used for internal algorithms
 *
 * @param[in]  xn    xml node
 * @retval     flag  Flag value(s), see XML_FLAG_MARK et al
 */
uint16_t
xml_flag(cxobj   *xn, 
         uint16_t flag)
{
    return xn->x_flags&flag;
}

/*! Set xml node flags, used for internal algorithms
 *
 * @param[in]  xn      xml node
 * @param[in]  flag    Flag values to set, see XML_FLAG_MARK et al
 */
int
xml_flag_set(cxobj   *xn, 
             uint16_t flag)
{
    xn->x_flags |= flag;
    return 0;
}

/*! Reset xml node flags, used for internal algorithms
 *
 * @param[in]  xn      xml node
 * @param[in]  flag    Flag value(s) to reset, see XML_FLAG_*
 */
int
xml_flag_reset(cxobj   *xn, 
               uint16_t flag)
{
    xn->x_flags &= ~flag;
    return 0;
}

/*! Add a creator tag
 *
 * @param[in]  xn    XML tree
 * @param[in]  name  Name of creator tag
 * @retval     0     OK
 * @retval    -1     Error
 */
int
xml_creator_add(cxobj *xn,
                char  *name)
{
    int     retval = -1;
    cg_var *cv;
    
    if (!is_element(xn))
        return 0;
    if (xn->x_creators == NULL){
        if ((xn->x_creators = cvec_new(0)) == NULL){
            clicon_err(OE_XML, errno, "cvec_new");
            goto done;
        }
    }
    if ((cv = cvec_find(xn->x_creators, name)) == NULL) 
        cvec_add_string(xn->x_creators, name, NULL);
    retval = 0;
 done:
    return retval;
}

/*! Remove a creator tag
 *
 * @param[in]  xn    XML tree
 * @param[in]  name  Name of creator tag
 * @retval     0     OK
 * @retval    -1     Error
 */
int
xml_creator_rm(cxobj *xn,
               char  *name)
{
    cg_var *cv;
    
    if (!is_element(xn))
        return 0;
    if ((cv = cvec_find(xn->x_creators, name)) == NULL)
        return 0;
    return cvec_del(xn->x_creators, cv);
}

/*! Find a creator string
 *
 * @param[in]  xn    XML tree
 * @param[in]  name  Name of creator tag
 * @retval     1     Yes, xn is tagged with creator tag "name"
 * @retval     0     No, xn is not tagged with creator tag "name"
 */
int
xml_creator_find(cxobj *xn,
                 char  *name)
{
    if (!is_element(xn))
        return 0;
    if (xn->x_creators != NULL &&
        cvec_find(xn->x_creators, name) != NULL)
        return 1;
    return 0;
}

/*! Get number of creator strings
 *
 * @param[in]  xn    XML tree
 * @retval     len   Number of creator tags assigned to xn
 * @retval     0     No creator tags or non-applicable
 */
size_t
xml_creator_len(cxobj *xn)
{
    if (!is_element(xn))
        return 0;
    if (xn->x_creators)
        return cvec_len(xn->x_creators);
    else
        return 0;
}

/*! Copy creator info from x0 to x1
 *
 * @param[in]  x0  Source XML node
 * @param[in]  x1  Destination XML node
 * @retval     0   OK
 * @retval    -1   Error
 */
int
xml_creator_copy(cxobj *x0, 
                 cxobj *x1)
{
    int retval = -1;
    
    if (x0->x_creators)
        if ((x1->x_creators = cvec_dup(x0->x_creators)) == NULL){
            clicon_err(OE_UNIX, errno, "cvec_dup");
            goto done;
        }
    retval = 0;
 done:
    return retval;
}

/*! Print XML and creator tags where they exists, apply help function
 *
 * @param[in]  x    XML tree
 * @param[in]  arg  FIle
 */
static int
creator_print_fn(cxobj *x,
                 void  *arg)
{
    FILE   *f = (FILE *)arg;
    cg_var *cv;
 
    if (x->x_creators == NULL)
        return 0;
    cv = NULL;
    while ((cv = cvec_each(x->x_creators, cv)) != NULL){
        fprintf(f, "%s ", cv_name_get(cv));
    }
    fprintf(f, ":\n");
    clixon_xml2file(f, x, 3, 1, NULL, cligen_output, 0, 0);
    return 2; /* Locally abort this subtree, continue with others */
}

/*! Print XML and creator tags where they exists, for debugging
 *
 * @param[in]  xn    XML tree
 * @retval     see xml_apply
 */
int
xml_creator_print(FILE  *f,
                  cxobj *xn)
{
    return xml_apply0(xn, CX_ELMNT, creator_print_fn, f);
}

/*! Get value of xnode
 * @param[in]  xn    xml node
 * @retval     value of xml node
 */
char*
xml_value(cxobj *xn)
{
    if (!is_bodyattr(xn))
        return NULL;
    return xn->x_value_cb?cbuf_get(xn->x_value_cb):NULL;
}

/*! Set value of xml node, value is copied
 * @param[in]  xn    xml node
 * @param[in]  val   new value, null-terminated string, copied by function
 * @retval     0     OK
 * @retval     -1    on error with clicon-err set
 */
int
xml_value_set(cxobj *xn, 
              char  *val)
{
    int    retval = -1;
    size_t sz;

    if (!is_bodyattr(xn))
        return 0;
    if (val == NULL){
        clicon_err(OE_XML, EINVAL, "value is NULL");
        goto done;
    }
    sz = strlen(val)+1;
    if (xn->x_value_cb == NULL){
        if ((xn->x_value_cb = cbuf_new_alloc(sz)) == NULL){
            clicon_err(OE_XML, errno, "cbuf_new");
            goto done;
        }
    }
    else
        cbuf_reset(xn->x_value_cb);
    cbuf_append_str(xn->x_value_cb, val);
    retval = 0;
 done:
    return retval;
}

/*! Append value of xnode, value is copied
 * @param[in]  xn    xml node
 * @param[in]  val   appended value, null-terminated string, copied by function
 * @retval     new value
 * @retval     NULL  on error with clicon-err set, or if value is set to NULL
 */
int
xml_value_append(cxobj *xn, 
                 char  *val)
{
    int    retval = -1;
    size_t sz;

    if (!is_bodyattr(xn))
        return 0;
    if (val == NULL){
        clicon_err(OE_XML, EINVAL, "value is NULL");
        goto done;
    }
    sz = strlen(val)+1;
    if (xn->x_value_cb == NULL){
        if ((xn->x_value_cb = cbuf_new_alloc(sz)) == NULL){
            clicon_err(OE_XML, errno, "cbuf_new");
            goto done;
        }
    }
    if (cbuf_append_str(xn->x_value_cb, val) < 0){
        clicon_err(OE_XML, errno, "cprintf");
        goto done;
    }
    retval = 0;
 done:
    return retval;
}

/*! Get type of xnode
 * @param[in]  xn    xml node
 * @retval     type of xml node
 */
enum cxobj_type
xml_type(cxobj *xn)
{
    if (xn == NULL) {
        return CX_ERROR;
    }
    return xn->x_type;
}

/*! Set type of xnode
 * @param[in]  xn    xml node
 * @param[in]  type  new type
 * @retval     type  old type
 */
static enum cxobj_type 
xml_type_set(cxobj          *xn, 
             enum cxobj_type type)
{
    enum cxobj_type old = xn->x_type;

    xn->x_type = type;
    return old;
}

/*! Get number of children
 * @param[in]  xn    xml node
 * @retval     number of children in XML tree
 * @see xml_child_nr_type
 * @see xml_child_nr_notype
 */
int   
xml_child_nr(cxobj *xn)
{
    if (xn == NULL) {
        return 0;
    }
    if (!is_element(xn))
        return 0;
    return xn->x_childvec_len;
}

/*! Get number of children of EXCEPT specific type
 * @param[in]  xn    xml node
 * @param[in]  type  XML type or -1 for all
 * @retval     number of typed children in XML tree (except type)
 * @see xml_child_nr
 * @see xml_child_nr_type
 */
int   
xml_child_nr_notype(cxobj          *xn, 
                    enum cxobj_type type)
{
    cxobj *x = NULL;
    int    nr = 0;

    if (!is_element(xn))
        return 0;
    while ((x = xml_child_each(xn, x, -1)) != NULL) {
        if (xml_type(x) != type)
            nr++;
    }
    return nr;
}

/*! Get number of children of specific type
 * @param[in]  xn    xml node
 * @param[in]  type  XML type or -1 for all
 * @retval     number of typed children in XML tree
 * @see xml_child_nr
 * @see xml_child_nr_notype
 */
int   
xml_child_nr_type(cxobj          *xn, 
                  enum cxobj_type type)
{
    cxobj *x = NULL;
    int    len = 0;

    if (!is_element(xn))
        return 0;
    while ((x = xml_child_each(xn, x, type)) != NULL) 
        len++;
    return len;
}

/*! Get a specific child
 * @param[in]  xn    xml node
 * @param[in]  i     the number of the child, eg order in children vector
 * @retval     xml   The child xml node
 * @retval     NULL  if no such child, or empty child
 * @see xml_child_i_type
 * @see xml_child_order
 */
cxobj *
xml_child_i(cxobj *xn, 
            int    i)
{
    if (xn == NULL || i < 0) {
        return NULL;
    }
    if (!is_element(xn))
        return NULL;
    if (i < xn->x_childvec_len)
        return xn->x_childvec[i];
    return NULL;
}

/*! Get a specific child of a specific type
 * @param[in]  xn    xml node
 * @param[in]  i     the number of the child of specific type
 * @param[in]  type  Child type
 * @retval     child in XML tree, or NULL if no such child, or empty child
 * @see xml_child_i
 */
cxobj *
xml_child_i_type(cxobj          *xn, 
                 int             i,
                 enum cxobj_type type)
{
    cxobj *x = NULL;
    int    it = 0;
    
    if (!is_element(xn))
        return NULL;
    while ((x = xml_child_each(xn, x, type)) != NULL) {
        if (x->x_type == type && (i == it++))
            return x;
    }
    return NULL;
}

/*! Set specific child
 * @param[in]  xn    xml node
 * @param[in]  i     the number of the child, eg order in children vector
 * @param[in]  xc    The child to set at position i
 * @retval     0     OK
 */
cxobj *
xml_child_i_set(cxobj *xt, 
                int    i, 
                cxobj *xc)
{
    if (!is_element(xt))
        return NULL;
    if (i < xt->x_childvec_len)
        xt->x_childvec[i] = xc;
    return 0;
}

/*! Get the order of child
 * @param[in]  xp    xml parent node
 * @param[in]  xc    the xml child to look for
 * @retval     i     The order of the child
 * @retval     -1    if no such child, or empty child
 * @see xml_child_i
 */
int
xml_child_order(cxobj *xp, 
                cxobj *xc)
{
    cxobj *x = NULL;
    int    i = 0;

    if (!is_element(xp))
        return -1;
    while ((x = xml_child_each(xp, x, -1)) != NULL) {
        if (x == xc)
            return i;
        i++;
    }
    return -1;
}

/*! Iterator over xml children objects
 *
 * @param[in] xparent xml tree node whose children should be iterated
 * @param[in] xprev   previous child, or NULL on init
 * @param[in] type    matching type or -1 for any
 * @retval    xn      Next XML node
 * @retval    NULL    End of list
 * @code
 *   cxobj *x = NULL;
 *   while ((x = xml_child_each(x_top, x, -1)) != NULL) {
 *     ...
 *   }
 * @endcode
 * @note uses _x_vector_i as a shared resource: you cannot mix loops over same parent
 * Further, never manipulate the child-list during operation or using the
 * same object recursively, the function uses an internal field to remember the
 * index used. It works as long as the same object is not iterated concurrently. 
 * If you need to delete a node you can do somethhing like:
 * @code
 *   cxobj *xprev = NULL;
 *   cxobj *x = NULL;
 *   while ((x = xml_child_each(x_top, x, -1)) != NULL) {
 *      if (something){
 *          if (xml_purge(x) < 0)
 *              goto done;
 *          x = xprev;
 *          continue;  
 *      }
 *      xprev = x;
 *   }
 * @endcode
 * @see xml_child_index_each
 * @see xml_child_each_attr  hardcoded for sorted list and attributes
 */
cxobj *
xml_child_each(cxobj           *xparent, 
               cxobj           *xprev, 
               enum cxobj_type  type)
{
    int    i;
    cxobj *xn = NULL; 

    if (xparent == NULL)
        return NULL;
    if (!is_element(xparent))
        return NULL;
    for (i=xprev?xprev->_x_vector_i+1:0; i<xparent->x_childvec_len; i++){
        xn = xparent->x_childvec[i];
        if (xn == NULL)
            continue;
        if (type != CX_ERROR && xml_type(xn) != type)
            continue;
        break; /* this is next object after previous */
    }
    if (i < xparent->x_childvec_len) /* found */
        xn->_x_vector_i = i;
    else
        xn = NULL;
    return xn;
}

/*! Same as xml_child_each but hard-coded for attributes
 *
 * Assumes attributes are first in list, which they are if they are sorted, but there are
 * situations where the children have not (yet) been sorted, in which case you need to use the 
 * original function.
 * @param[in] xparent xml tree node whose children should be iterated
 * @param[in] xprev   previous child, or NULL on init
 * @retval    xn      Next XML node
 * @retval    NULL    End of list
 * @see xml_child_each
 */
cxobj *
xml_child_each_attr(cxobj           *xparent, 
                    cxobj           *xprev)
{
    int             i;
    cxobj          *xn = NULL; 

    if (xparent == NULL)
        return NULL;
    if (!is_element(xparent))
        return NULL;
    for (i=xprev?xprev->_x_vector_i+1:0; i<xparent->x_childvec_len; i++){
        xn = xparent->x_childvec[i];
        if (xn == NULL)
            continue;
        if (xml_type(xn) != CX_ATTR){
            return NULL;
        }
        break; /* this is next object after previous */
    }
    if (i < xparent->x_childvec_len) /* found */
        xn->_x_vector_i = i;
    else
        xn = NULL;
    return xn;
}

/*! Extend child vector with one and insert xml node there
 * @note does not do anything with child, you may need to set its parent, etc
 * @see xml_child_insert_pos
 * XXX could insert hint if we know this is a yang list and not a leaf to increase start.
 */
static int
xml_child_append(cxobj *xp,
                 cxobj *xc)
{
    size_t start;

    if (!is_element(xp))
        return 0;
    start = XML_CHILDVEC_SIZE_START;
    /* Heurestics: if child is body only single child is expected, but element children may
     * have siblings
     */
    if (xml_type(xc) == CX_ELMNT)
        start = XML_CHILDVEC_SIZE_START_ELMNT;
    xp->x_childvec_len++;
    if (xp->x_childvec_len > xp->x_childvec_max){
        if (xp->x_childvec_len < XML_CHILDVEC_SIZE_THRESHOLD)
            xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:start;
        else
            xp->x_childvec_max += XML_CHILDVEC_SIZE_THRESHOLD;
        xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_max*sizeof(cxobj*));
        if (xp->x_childvec == NULL){
            clicon_err(OE_XML, errno, "realloc");
            return -1;
        }
    }
    xp->x_childvec[xp->x_childvec_len-1] = xc;
    return 0;
}

/*! Insert child xc at position i under parent xp
 * 
 * @see xml_child_append
 * @note does not do anything with child, you may need to set its parent, etc
 */
int
xml_child_insert_pos(cxobj *xp,
                     cxobj *xc,
                     int    i)
{
    size_t size;
   
    if (!is_element(xp))
        return 0;
    xp->x_childvec_len++;
    if (xp->x_childvec_len > xp->x_childvec_max){
        if (xp->x_childvec_len < XML_CHILDVEC_SIZE_THRESHOLD)
            xp->x_childvec_max = xp->x_childvec_max?2*xp->x_childvec_max:XML_CHILDVEC_SIZE_START;
        else
            xp->x_childvec_max += XML_CHILDVEC_SIZE_THRESHOLD;
        xp->x_childvec = realloc(xp->x_childvec, xp->x_childvec_max*sizeof(cxobj*));
        if (xp->x_childvec == NULL){
            clicon_err(OE_XML, errno, "realloc");
            return -1;
        }
    }
    size = (xml_child_nr(xp) - i - 1)*sizeof(cxobj *);
    memmove(&xp->x_childvec[i+1], &xp->x_childvec[i], size);
    xp->x_childvec[i] = xc;
    return 0;
}

/*! Set a childvec to a specific size, fill with children after
 * @code
 *   xml_childvec_set(x, 2);
 *   xml_child_i_set(x, 0, xc0)
 *   xml_child_i_set(x, 1, xc1);
 * @endcode
 */
int
xml_childvec_set(cxobj *x, 
                 int    len)
{
    if (!is_element(x))
        return 0;
    x->x_childvec_len = len;
    x->x_childvec_max = len;
    if (x->x_childvec)
        free(x->x_childvec);
    if ((x->x_childvec = calloc(len, sizeof(cxobj*))) == NULL){
        clicon_err(OE_XML, errno, "calloc");
        return -1;
    }
    return 0;
}

/*! Get the children of an XML node as an XML vector
 */
cxobj **
xml_childvec_get(cxobj *x)
{
    if (!is_element(x))
        return NULL;
    return x->x_childvec;
}

/*! Given an XML object and a vector of children xvec, append the children to the object
 *
 * @param[in]  x   XML node
 * @param[in]  xv  Clixon xml vector
 * @retval     0   OK
 * @retval     -1  Error
 * @note xv is not same type as x_childvec
 */
int
clixon_child_xvec_append(cxobj       *xn,
                         clixon_xvec *xv)
{
    int    retval = -1;
    cxobj *xc;
    int    i;

    for (i=0; i<clixon_xvec_len(xv); i++){
        xc = clixon_xvec_i(xv, i);
        if (xml_addsub(xn, xc) < 0)
            goto done;
    }
    retval = 0;
 done:
    return retval;
}

/*! Create new xml node given a name and parent. Free with xml_free().
 *
 * @param[in]  name      Name of XML node
 * @param[in]  xp        The parent where the new xml node will be appended
 * @param[in]  type      XML type
 * @retval     xml       Created xml object if successful. Free with xml_free()
 * @retval     NULL      Error and clicon_err() called
 * @code
 *   cxobj *x;
 *   if ((x = xml_new(name, xparent, CX_ELMNT)) == NULL)
 *     err;
 *   ...
 *   xml_free(x);
 * @endcode
 * @note Differentiates between body/attribute vs element to reduce mem allocation
 * @see xml_sort_insert
 */
cxobj *
xml_new(char           *name, 
        cxobj          *xp,
        enum cxobj_type type)
{
    struct xml *x = NULL;
    size_t      sz;
    
    switch (type){
    case CX_ELMNT:
        sz = sizeof(struct xml);
        break;
    case CX_ATTR:
    case CX_BODY:
        sz = sizeof(struct xmlbody);
        break;
    default:
        clicon_err(OE_XML, EINVAL, "Invalid type: %d", type);
        return NULL;
        break;
    }
    if ((x = malloc(sz)) == NULL){
        clicon_err(OE_XML, errno, "malloc");
        return NULL;
    }
    memset(x, 0, sz);
    xml_type_set(x, type);
    if (name && (xml_name_set(x, name)) < 0)
        return NULL;
    if (xp){
        xml_parent_set(x, xp);
        if (xml_child_append(xp, x) < 0) 
            return NULL;
        x->_x_i = xml_child_nr(xp)-1;
    }
    _stats_xml_nr++;
    return x;
}

/*! Create a new XML node and set it's body to a value
 *
 * @param[in]   name    The name of the new node
 * @param[in]   parent  The parent to put the new node under
 * @param[in]   val     The value to set in the body                  
 *
 * Creates a new node, sets it as a child of the parent, if one was passed in.
 * Creates a child of the node, sets the child's type to CX_BODY, and sets
 * the value of the body/child.
 * Thanks mgsmith@netgate.com
 */
cxobj *
xml_new_body(char  *name,
             cxobj *parent,
             char  *val)
{
    cxobj *new_node = NULL;
    cxobj *body_node;

    if (!name || !parent || !val) {
        return NULL;
    }
    if ((new_node = xml_new(name, parent, CX_ELMNT)) == NULL) {
        return NULL;
    }
    if ((body_node = xml_new("body", new_node, CX_BODY)) == NULL ||
        xml_value_set(body_node, val) < 0) {
        xml_free(new_node);
        new_node = NULL;
        body_node = NULL;
    } else {
        xml_type_set(body_node, CX_BODY);
    }
    return new_node;
}


/*! Return yang spec of node. 
 * Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.
 */
yang_stmt *
xml_spec(cxobj *x)
{
    if (!is_element(x))
        return NULL;
    return x->x_spec;
}

int
xml_spec_set(cxobj     *x, 
             yang_stmt *spec)
{
    if (!is_element(x))
        return 0;
    x->x_spec = spec;
    return 0;
}

/*! Return (cached)  cligen variable value of xml node
 * @param[in]  x    XML node (body and leaf/leaf-list)
 * @retval     cv   CLIgen variable containing value of x body
 * @retval     NULL
 * Only applicable if x is body and has yang-spec and is leaf or leaf-list
 * Only accessed by xml_cv_cache as part of sorting in xml_cmp
 * @see xml_cv_cache
 */
cg_var *
xml_cv(cxobj *x)
{
    if (!is_element(x))
        return NULL;
    return x->x_cv;
}

/*! Set (cached) cligen variable value of xml node
 * @param[in]  x   XML node (body and leaf/leaf-list)
 * @param[in]  cv  CLIgen variable containing value of x body
 * @retval     0   OK
 * Only applicable if x is body and has yang-spec and is leaf or leaf-list
 * Only accessed by xml_cv_cache as part of sorting in xml_cmp
 * @see xml_cv_cache
 */
int
xml_cv_set(cxobj  *x, 
           cg_var *cv)
{
    if (!is_element(x))
        return 0;
    if (x->x_cv)
        cv_free(x->x_cv);
    x->x_cv = cv;
    return 0;
}

/*! Find an XML node matching name among a parent's children.
 *
 * Get first XML node directly under x_up in the xml hierarchy with
 * name "name".
 *
 * @param[in]  xp   Base XML object
 * @param[in]  name   Node name
 *
 * @retval xmlobj     if found.
 * @retval NULL       if no such node found.
 * There are several issues with this function:
 * @note (1) Ignores prefix which means namespaces are ignored
 * @note (2) Does not differentiate between element,attributes and body. You usually want elements.
 * @note (3) Linear scalability and relies on strcmp, does not use search/key indexes
 * @note (4) Only returns first match, eg a list/leaf-list may have several children with same name
 * @see xml_find_type  A more generic function fixes (1) and (2) above
 */
cxobj *
xml_find(cxobj *xp, 
         char  *name)
{
    cxobj *x = NULL;

    if (xp == NULL || name == NULL) {
        return NULL;
    }
    if (!is_element(xp))
        return NULL;
    while ((x = xml_child_each(xp, x, -1)) != NULL) 
        if (strcmp(name, xml_name(x)) == 0)
            break; /* x is set */
    return x;
}

/*! Append xc as child to xp. Remove xc from previous parent.
 * @param[in] xp  Parent xml node. If NULL just remove from old parent.
 * @param[in] xc  Child xml node to insert under xp
 * @retval    0   OK
 * @retval    -1  Error
 * @see xml_wrap
 * @see xml_insert
 * @note xc is not sorted correctly, need to call xml_sort on parent
 * @see xml_insert which is a higher layer function including yang and sorting
 */
int
xml_addsub(cxobj *xp, 
           cxobj *xc)
{
    int    retval = -1;
    cxobj *oldp;
    int    i;
    char  *pns = NULL; /* parent namespace */
    char  *cns = NULL; /* child namespace */
    cxobj *xa;

    if ((oldp = xml_parent(xc)) != NULL){
        /* Find child order i in old parent*/
        for (i=0; i<xml_child_nr(oldp); i++)
            if (xml_child_i(oldp, i) == xc)
                break;
        /* Remove xc from old parent */
        if (i < xml_child_nr(oldp))
            xml_child_rm(oldp, i);
    }
    /* Add xc to new parent */
    if (xp){
        if (xml_child_append(xp, xc) < 0)
            goto done;
        /* Set new parent in child */
        xml_parent_set(xc, xp);
        /* Ensure default namespace is not duplicated 
         * here only remove duplicate default namespace, there may be more */
        /* 1. Get parent default namespace */
        if (xml2ns(xp, NULL, &pns) < 0)
            goto done;
        /* 2. Get child default namespace */
        if (pns &&
            xml_type(xc) == CX_ELMNT &&
            (xa = xml_find_type(xc, NULL, "xmlns", CX_ATTR)) != NULL &&
            (cns = xml_value(xa)) != NULL){
            /* 3. check if same, if so remove child's */
            if (strcmp(pns, cns) == 0)
                xml_purge(xa); 
        }
        /* clear namespace context cache of child */
        nscache_clear(xc);
#ifdef XML_EXPLICIT_INDEX
    if (xml_search_index_p(xc))
            xml_search_child_insert(xp, xc);
#endif
    }
    retval = 0;
 done:
    return retval;
}

/*! Wrap a new node between a parent xml node (xp) and all its children
 *  Before:  xp --> xc*
 *  After:   xp --> xw --> xc*
 * @param[in] xp  Parent xml node
 * @param[in] tag Name of new xml child
 * @retval    xw  Return the new child (xw)
 * @see xml_addsub
 * @see xml_wrap  (wrap s single node)
 */
cxobj *
xml_wrap_all(cxobj *xp, 
             char  *tag)
{
    cxobj *xw; /* new wrap node */

    if (!is_element(xp))
        return NULL;
    if ((xw = xml_new(tag, NULL, CX_ELMNT)) == NULL)
        goto done;
    while (xp->x_childvec_len)
        if (xml_addsub(xw, xml_child_i(xp, 0)) < 0)
            goto done;
    if (xml_addsub(xp, xw) < 0)
        goto done;
  done:
    return xw;
}

/*! Wrap a new element above a single xml node (xc) with new tag 
 *  Before:  xp --> xc # specific child (xp can be NULL)
 *  After:   xp --> xt(tag) --> xc
 * @param[in] xp   Parent xml node
 * @param[in] tag  Name of new xml child
 * @retval    xc   Return the new child (xc)
 * @retval    NULL Error
 * @see xml_addsub (give the parent)
 * @see xml_wrap_all  (wrap all children of a node, not just one)
 */
cxobj *
xml_wrap(cxobj *xc, 
         char  *tag)
{
    cxobj *xw; /* new wrap node */
    cxobj *xp; /* parent */

    xp = xml_parent(xc);
    if ((xw = xml_new(tag, xp, CX_ELMNT)) == NULL)
        goto done;
    if (xml_addsub(xw, xc) < 0)
        goto done;
  done:
    return xw;
}

/*! Remove and free an xml node child from xml parent
 * @param[in]   xc          xml child node (to be removed and freed)
 * @retval      0           OK
 * @retval      -1
 * @note you cannot remove xchild in the loop (unless you keep track of xprev)
 * @note Linear complexity - use xml_child_rm if possible
 * @see xml_free      Free, dont remove from parent
 * @see xml_child_rm  Remove if child order is known (does not free)
 * Differs from xml_free it is removed from parent.
 */
int
xml_purge(cxobj *xc)
{
    int       retval = -1;
    int       i;
    cxobj    *xp;

    if ((xp = xml_parent(xc)) != NULL){
        /* Find child order i in parent*/
        for (i=0; i<xml_child_nr(xp); i++)
            if (xml_child_i(xp, i) == xc)
                break;
        /* Remove xc from parent */
        if (i < xml_child_nr(xp))
            if (xml_child_rm(xp, i) < 0)
                goto done;
    }
    xml_free(xc);           
    retval = 0;
 done:
    return retval; 
}

/*! Remove child xml node from parent xml node. No free and child is root
 * @param[in]   xp     xml parent node
 * @param[in]   i      Number of xml child node (to remove)
 * @retval      0      OK
 * @retval      -1
 * @note you should not remove xchild in loop (unless you keep track of xprev)
 * @see xml_rootchild
 * @see xml_rm     Remove the node itself from parent
 */
int
xml_child_rm(cxobj *xp, 
             int    i)
{
    int    retval = -1;
    cxobj *xc = NULL;

    if (!is_element(xp))
        return 0;
    if ((xc = xml_child_i(xp, i)) == NULL){
        clicon_err(OE_XML, 0, "Child not found");
        goto done;
    }
    xml_parent_set(xc, NULL);
    xp->x_childvec[i] = NULL;
    xp->x_childvec_len--;
    if (i<xp->x_childvec_len)
        memmove(&xp->x_childvec[i], &xp->x_childvec[i+1], (xp->x_childvec_len-i)*sizeof(cxobj*));
#ifdef XML_EXPLICIT_INDEX
    if (xml_type(xc) == CX_ELMNT){
        if (xml_search_index_p(xc))
            xml_search_child_rm(xp, xc);

    }
#endif
    retval = 0;
 done:
    return retval;
}

/*! Remove this xml node from parent xml node. No freeing and node is new root
 * @param[in]   xc     xml child node to be removed
 * @retval      0      OK
 * @retval      -1     Error
 * @note you should not remove xchild in loop (unless you keep track of xprev)
 * @see xml_child_rm  Remove a child of a node
 */
int
xml_rm(cxobj *xc)
{
    int    retval = -1;
    cxobj *xp;
    cxobj *x;
    int    i;

    if ((xp = xml_parent(xc)) == NULL)
        goto ok;
    /* Find child in parent XXX: search? */
    x = NULL; i = 0;
    while ((x = xml_child_each(xp, x, -1)) != NULL) {
        if (x == xc)
            break;
        i++;
    }
    if (x != NULL)
        if (xml_child_rm(xp, i) < 0)
            goto done;
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Remove all children of specific type
 * @param[in] x    XML node
 * @param[in] type Remove all children of xn of this type
 * @retval    0    OK
 * @retval   -1    Error
 */
int
xml_rm_children(cxobj          *xp,
                enum cxobj_type type)
{
    int    retval = -1;
    cxobj *xc;
    int    i;

    if (!is_element(xp))
        return 0;
    for (i=0; i<xml_child_nr(xp);){
        xc = xml_child_i(xp, i);
        if (xml_type(xc) != type){
            i++;
            continue;
        }
        if (xml_child_rm(xp, i) < 0)
            goto done;
        xml_free(xc);
    }
    retval = 0;
 done:
    return retval;
}


/*! Remove top XML object and all children except a single child
 * Given a root xml node, and the i:th child, remove the child from its parent
 * and return it, remove the parent and all other children. (unwrap)
 * Before: xp-->[..xc..]
 * After: xc
 * @param[in]  xp   xml parent node. Will be deleted
 * @param[in]  i    Child nr in parent child vector
 * @param[out] xcp  xml child node. New root
 * @retval     0    OK
 * @retval    -1    Error
 * @code
 *   cxobj *xt = NULL; 
 *   if (clixon_xml_parse_string("<a>2</a>", YB_NONE, NULL, &xt, NULL) < 0)
 *      err;
 *  # Here xt will be: <top><a>2</a></top>
 *   if (xml_rootchild(xt, 0, &xt) < 0)
 *      err;
 *  # Here xt will be: <a>2</a>
 * @endcode
 * @see xml_child_rm
 * @see xml_rootchild_node  where xc is explicitly given
 */
int
xml_rootchild(cxobj  *xp, 
              int     i,
              cxobj **xcp)
{
    int    retval = -1;
    cxobj *xc;

    if (!is_element(xp))
        return 0;
    if (xml_parent(xp) != NULL){
        clicon_err(OE_XML, 0, "Parent is not root");
        goto done;
    }
    if ((xc = xml_child_i(xp, i)) == NULL){
        clicon_err(OE_XML, ENOENT, "Child %d of parent %s not found", i, xml_name(xp));
        goto done;
    }
    if (xml_child_rm(xp, i) < 0)
        goto done;
    if (xml_free(xp) < 0)
        goto done;
    *xcp = xc;
    retval = 0;
 done:
    return retval;
}

/*! Remove top XML object and all children except a single (given) child
 * Given a root xml node, remove the child from its parent
 * , remove the parent and all other children. (unwrap)
 * Before: xp-->[..xc..]
 * After: xc
 * @param[in]  xp   xml parent node. Must be root. Will be deleted
 * @param[in]  xc   xml child node. Must be a child of xp
 * @retval     0    OK
 * @retval    -1    Error
 * @see xml_rootchild  where an index is used to find xc
 */
int
xml_rootchild_node(cxobj  *xp, 
                   cxobj  *xc)
{
    int    retval = -1;
    cxobj *x;
    int    i;

    if (!is_element(xp))
        return 0;
    if (xml_parent(xp) != NULL){
        clicon_err(OE_XML, 0, "Parent is not root");
        goto done;
    }
    x = NULL; i = 0;
    while ((x = xml_child_each(xp, x, -1)) != NULL) {
        if (x == xc)
            break;
        i++;
    }
    if (xml_child_rm(xp, i) < 0)
        goto done;
    if (xml_free(xp) < 0)
        goto done;
    retval = 0;
 done:
    return retval;
}

/*! Help function to sorting: enumerate all children according to present order
 * This is so that the child itself know its present order in a list.
 * When sorting by "ordered by user", the order should remain in its present
 * state.
 * A child can always compute its order functionally but it computes
 * more cycles,..
 * @param[in]  xp  Enumerate its children
 * @retval     0   OK
 * @see xml_sort
 * @see xml_enumerate_get  Call to the child to get the number
 */
int
xml_enumerate_children(cxobj *xp)
{
    cxobj *x = NULL;
    int    i = 0;

    if (!is_element(xp))
        return 0;
    while ((x = xml_child_each(xp, x, -1)) != NULL)
        x->_x_i = i++;
    return 0;
}

/*! Reset enumeration as done by xml_enumerate_children
 */
int
xml_enumerate_reset(cxobj *xp)
{
    cxobj *x = NULL;
 
    if (!is_element(xp))
        return 0;
    while ((x = xml_child_each(xp, x, -1)) != NULL)
        x->_x_i = 0;
    return 0;
}

/*! Get the enumeration of a single child set by enumeration of parent
 * @see xml_children_enumerate
 * @note that it has to be called right after xml_children_enumerate. If not,
 * there are many cases where this info is stale. 
 * @param[in]  x   A child whose parent has enumerated its children
 * @retval     n   Enumeration
 * @see xml_enumerate_children   Call to the parent to compute the nr
 */
int
xml_enumerate_get(cxobj *x)
{
    return x->_x_i;
}

/*! Get the first sub-node which is an XML body.
 * @param[in]   xn          xml tree node
 * @retval  The returned body as a pointer to the name string
 * @retval  NULL if no such node or no body in found node
 * Note, make a copy of the return value to use it properly
 * @see xml_find_body
 * Explaining picture:
 *       xt  --> xb (x_type=CX_BODY)
 *               return xb.x_value
 */
char *
xml_body(cxobj *xn)
{
    cxobj *xb = NULL;

    if (!is_element(xn))
        return NULL;
    while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL) 
        return xml_value(xb);
    return NULL;
}

/*! Get (first) body of xml node, note could be many 
 * @param[in]   xt          xml tree node
 * Explaining picture:
 *       xt  --> xb (x_type=CX_BODY)
 *               return xb
 */
cxobj *
xml_body_get(cxobj *xt)
{
    cxobj *xb = NULL;

    if (!is_element(xt))
        return NULL;
    while ((xb = xml_child_each(xt, xb, CX_BODY)) != NULL) 
        return xb;
    return NULL;
}

/*! Find and return the value of an xml child of specific type given prefix and name
 *
 * The value can be of an attribute only
 * @param[in]   xt          xml tree node
 * @param[in]   prefix      Prefix (namespace local name) or NULL
 * @param[in]   name        name of xml tree node (eg attr name or "body")
 * @retval      val         Pointer to the name string
 * @retval      NULL        No such node or no value in node
 * @code
 * char *str = xml_find_type_value(x, "prefix", "name", CX_ATTR);
 * @endcode
 * @note, make a copy of the return value to use it properly
 * @see xml_find_type return the xml object
 * @see xml_find_value where a body can be found as well
 */
char *
xml_find_type_value(cxobj           *xt, 
                    const char      *prefix,
                    const char      *name,
                    enum cxobj_type  type)
{
    cxobj *x;

    if (!is_element(xt))
        return NULL;
    if ((x = xml_find_type(xt, prefix, name, type)) != NULL)
        return xml_value(x);
    return NULL;
}

/*! Find and return the xml child of specific type given prefix and name
 *
 * The value can be of an attribute only
 * @param[in]   xt          xml tree node
 * @param[in]   prefix      Prefix (namespace local name) or NULL (any prefix)
 * @param[in]   name        name of xml tree node (eg attr name or "body")
 * @param[in]   type        Matching type or -1 for any
 * @retval      val         Pointer to the name string
 * @retval      NULL        No such node or no value in node
 * @code
 * cxobj *x = xml_find_type(x, "prefix", "name", CX_ATTR);
 * @endcode
 * @see xml_find  which finds any child given name (and no prefix)
 * @see xml_find_value where a body can be found as well
 */
cxobj *
xml_find_type(cxobj           *xt, 
              const char      *prefix,
              const char      *name,
              enum cxobj_type  type)
{
    cxobj *x = NULL;
    int    pmatch;  /* prefix match */
    char  *xprefix; /* xprefix */
    
    if (!is_element(xt))
        return NULL;
    while ((x = xml_child_each(xt, x, type)) != NULL) {
        if (prefix){
            xprefix = xml_prefix(x);
            pmatch = xprefix ? strcmp(prefix,xprefix)==0 : 0;
        }
        else
            pmatch = 1;
        if (pmatch && (name==NULL || strcmp(name, xml_name(x)) == 0))
            return x;
    }
    return NULL;
}

/*! Find and return the value of a sub xml node
 *
 * The value can be of an attribute or body.
 * @param[in]   xt          xml tree node
 * @param[in]   name        name of xml tree nod (eg attr name or "body")
 * @retval      val         Pointer to the name string
 * @retval      NULL        No such node or no value in node
 *
 * Note, make a copy of the return value to use it properly
 * @see xml_find_body
 * Explaining picture:
 *       xt  --> x         
 *               x_name=name
 *               return x_value
 */
char *
xml_find_value(cxobj      *xt, 
               const char *name)
{
    cxobj *x = NULL;
    
    if (!is_element(xt))
        return NULL;
    while ((x = xml_child_each(xt, x, -1)) != NULL) 
        if (strcmp(name, xml_name(x)) == 0)
            return xml_value(x);
    return NULL;
}

/*! Find and return a body (string) of a sub xml node
 * @param[in]   xn          xml tree node
 * @param[in]   name        name of xml tree node
 * @retval  The returned body as a pointer to the name string
 * @retval  NULL if no such node or no body in found node
 * @note, make a copy of the return value to use it properly
 * @see xml_find_value
 * Explaining picture:
 *       xt  --> x          --> bx (x_type=CX_BODY)
 *               x_name=name    return x_value
 */
char *
xml_find_body(cxobj      *xt, 
              const char *name)
{
    cxobj *x=NULL;

    if (!is_element(xt))
        return NULL;
    while ((x = xml_child_each(xt, x, -1)) != NULL) 
        if (strcmp(name, xml_name(x)) == 0)
            return xml_body(x);
    return NULL;
}

/*! Find xml object with matching name and value.
 *
 * This can be useful if x is a leaf-list with many subs with same name,
 * but you need to pick the object with a specific value
 * @param[in]  xt   XML tree
 * @param[in]  name Name of child (there can be many with same name)
 * @param[in]  val  Value. Must be equal to body of child.
 * @retval     x    Child with matching name and body
 *               
 * Explaining picture:
 *       xt  --> x          --> bx (x_type=CX_BODY)
 *               x_name=name    x_value=val
 *               return x
 */
cxobj *
xml_find_body_obj(cxobj      *xt, 
                  const char *name,
                  char       *val)
{
    cxobj *x = NULL;
    char  *bstr;

    if (!is_element(xt))
        return NULL;
    while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
        if (strcmp(name, xml_name(x)))
            continue;
        if ((bstr = xml_body(x)) == NULL)
            continue;
        if (strcmp(bstr, val) == 0)
            break; /* x is returned */
    }
    return x;
}

/*! Free an xl sub-tree recursively, but do not remove it from parent
 * @param[in]  x  the xml tree to be freed.
 * @see xml_purge where x is also removed from parent
 */
int
xml_free(cxobj *x)
{
    int    i;
    cxobj *xc;

    if (x == NULL){
        return 0;
    }
    if (x->x_name)
        free(x->x_name);
    if (x->x_prefix)
        free(x->x_prefix);
    switch (xml_type(x)){
    case CX_ELMNT:
        for (i=0; i<x->x_childvec_len; i++){
            if ((xc = x->x_childvec[i]) != NULL){
                xml_free(xc);
                x->x_childvec[i] = NULL;
            }
        }
        if (x->x_childvec)
            free(x->x_childvec);
        if (x->x_cv)
            cv_free(x->x_cv);
        if (x->x_ns_cache)
            xml_nsctx_free(x->x_ns_cache);
#ifdef XML_EXPLICIT_INDEX
        xml_search_index_free(x);
#endif
        if (x->x_creators)
            cvec_free(x->x_creators);
        break;
    case CX_BODY:
    case CX_ATTR:
        if (x->x_value_cb)
            cbuf_free(x->x_value_cb);  
        break;
    default:
        break;
    }
    free(x);
    _stats_xml_nr--;
    return 0;
}

/*! Copy single xml node from x0 to x1 without copying children
 * @param[in]  x0  Source XML tree
 * @param[in]  x1  Destination XML tree (must exist)
 * @retval     0   OK
 * @retval    -1   Error
 */
int
xml_copy_one(cxobj *x0, 
             cxobj *x1)
{
    int   retval = -1;
    char *s;
    
    if (x0 == NULL || x1 == NULL){
        clicon_err(OE_XML, EINVAL, "x0 or x1 is NULL");
        goto done;
    }
    xml_type_set(x1, xml_type(x0));
    if ((s = xml_name(x0))) /* malloced string */
        if ((xml_name_set(x1, s)) < 0)
            goto done;
    if ((s = xml_prefix(x0))) /* malloced string */
        if ((xml_prefix_set(x1, s)) < 0)
            goto done;
    switch (xml_type(x0)){
    case CX_ELMNT:
        xml_spec_set(x1, xml_spec(x0));
        if (xml_creator_copy(x0, x1) < 0)
            goto done;
        break;
    case CX_BODY:
    case CX_ATTR:
        if ((s = xml_value(x0))){ /* malloced string */
            if (xml_value_set(x1, s) < 0)
                goto done;

        }
        break;
    default:
        break;
    }
    xml_flag_set(x1, xml_flag(x0, XML_FLAG_DEFAULT | XML_FLAG_TOP)); /* Maybe more flags */
    retval = 0;
 done:
    return retval;
}

/*! Copy xml tree x0 to other existing tree x1
 *
 * x1 should be a created placeholder. If x1 is non-empty,
 * the copied tree is appended to the existing tree.
 * @param[in]  x0  Source XML tree
 * @param[in]  x1  Destination XML tree (must exist)
 * @retval     0   OK
 * @retval    -1   Error
 * @code
 *   x1 = xml_new("new", xparent, xml_type(x0));
 *   if (xml_copy(x0, x1) < 0)
 *      err;
 * @endcode
 * @see xml_dup
 */
int 
xml_copy(cxobj *x0, 
         cxobj *x1)
{
    int    retval = -1;
    cxobj *x;
    cxobj *xcopy;

    if (xml_copy_one(x0, x1) <0)
        goto done;
    x = NULL;
    while ((x = xml_child_each(x0, x, -1)) != NULL) {
        if ((xcopy = xml_new(xml_name(x), x1, xml_type(x))) == NULL)
            goto done;
        if (xml_copy(x, xcopy) < 0) /* recursion */
            goto done;
    }
    retval = 0;
  done:
    return retval;
}

/*! Create and return a copy of xml tree.
 *
 * @code
 *   cxobj *x1;
 *   x1 = xml_dup(x0);
 * @endcode
 * Note, returned tree should be freed as: xml_free(x1)
 * @see xml_cp
 */
cxobj *
xml_dup(cxobj *x0)
{
    cxobj *x1;

    if ((x1 = xml_new("new", NULL, xml_type(x0))) == NULL)
        return NULL;
    if (xml_copy(x0, x1) < 0)
        return NULL;
    return x1;
}

#if 1 /* XXX At some point migrate this code to the clixon_xml_vec.[ch] API */
/*! Append a new xml tree to an existing xml vector last in the list
 * @param[in]      x      XML tree (append this to vector)
 * @param[in,out]  vec    XML tree vector
 * @param[in,out]  len    Length of XML tree vector
 * @retval         0      OK
 * @retval        -1      Error
 * @code
 *  cxobj  **xvec = NULL;
 *  int      xlen = 0;
 *  cxobj   *x; 
 *
 *  if (cxvec_append(x, &xvec, &xlen) < 0) 
 *     err;
 *  if (xvec)
 *     free(xvec);
 * @endcode
 * @see cxvec_prepend
 * @see clixon_xvec_append  which is its own encapsulated xml vector datatype
 */
int
cxvec_append(cxobj   *x, 
             cxobj ***vec, 
             int     *len)
{
    int retval = -1;

    if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
        clicon_err(OE_XML, errno, "realloc");
        goto done;
    }
    (*vec)[(*len)++] = x;
    retval = 0;
 done:
    return retval;
}

/*! Prepend a new xml tree to an existing xml vector first in the list
 * @param[in]      x      XML tree (append this to vector)
 * @param[in,out]  vec    XML tree vector
 * @param[in,out]  len    Length of XML tree vector
 * @retval         0      OK
 * @retval        -1      Error
 * @code
 *  cxobj  **xvec = NULL;
 *  size_t   xlen = 0;
 *  cxobj   *x; 
 *
 *  if (cxvec_append(x, &xvec, &xlen) < 0) 
 *     err;
 *  if (xvec)
 *     free(xvec);
 * @endcode
 * @see cxvec_append
 * @see clixon_xvec_prepend  which is its own encapsulated xml vector datatype
 */
int
cxvec_prepend(cxobj   *x, 
             cxobj ***vec, 
             int     *len)
{
    int retval = -1;

    if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
        clicon_err(OE_XML, errno, "realloc");
        goto done;
    }
    memmove(&(*vec)[1], &(*vec)[0], sizeof(cxobj *) * (*len));
    (*vec)[0] = x;
    (*len)++;
    retval = 0;
 done:
    return retval;
}
#endif


/*! Apply a function call recursively on all xml node children recursively
 *
 * Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for 
 * each object found. The function is called with the xml node and an 
 * argument as args.
 * The tree is traversed depth-first, which at least guarantees that a parent is
 * traversed before a child.
 * @param[in]  xn   XML node
 * @param[in]  type Matching type or -1 for any
 * @param[in]  fn   Callback
 * @param[in]  arg  Argument
 * @retval     1    OK, aborted on first fn returned 1
 * @retval     0    OK, all nodes traversed (subparts may have been skipped)
 * @retval    -1    Error, aborted at first error encounter
 *
 * @code
 * int x_fn(cxobj *x, void *arg)
 * {
 *   return 0;
 * }
 * xml_apply(xn, CX_ELMNT, x_fn, NULL);
 * @endcode
 * @note do not delete or move around any children during this function
 * @note return value > 0 aborts the traversal
 * @see xml_apply0 including top object
 * @see xml_apply_ancestor for marking all parents recursively
 */
int
xml_apply(cxobj          *xn, 
          enum cxobj_type type, 
          xml_applyfn_t   fn, 
          void           *arg)
{
    int        retval = -1;
    cxobj     *x;
    int        ret;

    if (!is_element(xn))
        return 0;
    x = NULL;
    while ((x = xml_child_each(xn, x, type)) != NULL) {
        if ((ret = fn(x, arg)) < 0)
            goto done;
        if (ret == 2)
            continue; /* Abort this node, dont recurse */
        else if (ret == 1){
            retval = 1;
            goto done;
        }
        if ((ret = xml_apply(x, type, fn, arg)) < 0)
            goto done;
        if (ret == 1){
            retval = 1;
            goto done;
        }
    }
    retval = 0;
  done:
    return retval;   
}

/*! Apply a function call on top object and all xml node children recursively 
 * @param[in]  xn   XML node
 * @param[in]  type Matching type or -1 for any
 * @param[in]  fn   Callback
 * @param[in]  arg  Argument
 * @retval     1    OK, aborted on first fn returned 1
 * @retval     0    OK, all nodes traversed (subparts may have been skipped)
 * @retval    -1    Error, aborted at first error encounter
 * @see xml_apply not including top object
 */
int
xml_apply0(cxobj          *xn, 
          enum cxobj_type type, 
          xml_applyfn_t   fn, 
          void           *arg)
{
    int        retval = -1;
    int        ret;

    if ((ret = fn(xn, arg)) < 0) /* -1, 0, 1, 2 */
        goto done;
    if (ret == 1)
        retval = 1;
    else  if (ret > 1)
        retval = 0;
    else /* 0 */
        retval = xml_apply(xn, type, fn, arg);
  done:
    return retval;   
}

/*! Apply a function call recursively on all ancestors
 * Recursively traverse upwards to all ancestor nodes in a parse-tree and apply fn(arg) for 
 * each object found. The function is called with the xml node and an 
 * argument as args.
 * @param[in]  xn   XML node
 * @param[in]  fn   Callback
 * @param[in]  arg  Argument
 * @retval     n    OK, aborted at first encounter of first match
 * @retval     0    OK, all nodes traversed
 * @retval    -1    Error, aborted at first error encounter
 * @code
 * int x_fn(cxobj *x, void *arg)
 * {
 *   return 0;
 * }
 * xml_apply_ancestor(xn, x_fn, NULL);
 * @endcode
 * @see xml_apply
 * @note do not delete or move around any children during this function
 * @note It does not apply fn to the root node,..
 */
int
xml_apply_ancestor(cxobj          *xn, 
                   xml_applyfn_t   fn, 
                   void           *arg)
{
    int        retval = -1;
    cxobj     *xp = NULL;
    int        ret;

    while ((xp = xml_parent(xn)) != NULL) {
        if ((ret = fn(xp, arg)) < 0)
            goto done;
        if (ret > 0){
            retval = ret;
            goto done;
        }
        xn = xp;
    }
    retval = 0;
  done:
    return retval;   
}

/*! Is xpp ancestor of x?
 * @param[in]   x       XML node
 * @param[in]   xpp     Potential ancestor of x in XML tree
 * @retval      1       Yes, xpp is ancestor of x
 * @retval      0       No, xpp is not ancestor of x
 */
int
xml_isancestor(cxobj *x,
               cxobj *xpp)
{
    cxobj     *xp = NULL;
    cxobj     *xn = NULL;

    xn = x;
    while ((xp = xml_parent(xn)) != NULL) {
        if (xp == xpp)
            return 1;
        xn = xp;
    }
    return 0;
}

/*! Get ultimate root ancestor of an xml-node: top-level node without parent
 * @param[in]   xn      XML node
 * @retval      xr      XML root node (can be xn)
 */
cxobj *
xml_root(cxobj *xn)
{
    cxobj     *xp = NULL;
    cxobj     *x = NULL;

    x = xn;
    while ((xp = xml_parent(x)) != NULL) 
        x = xp;
    return x;
}

/*! Map xml operation from string to enumeration
 * @param[in]   opstr  String, eg "merge"
 * @param[out]  op     Enumeration, eg OP_MERGE
 * @code
 *   enum operation_type op;
 *   xml_operation("replace", &op)
 * @endcode
 */
int
xml_operation(char                *opstr, 
              enum operation_type *op)
{
    if (strcmp("merge", opstr) == 0)
        *op = OP_MERGE;
    else if (strcmp("replace", opstr) == 0)
        *op = OP_REPLACE;
    else if (strcmp("create", opstr) == 0)
        *op = OP_CREATE;
    else if (strcmp("delete", opstr) == 0)
        *op = OP_DELETE;
    else if (strcmp("remove", opstr) == 0)
        *op = OP_REMOVE;
    else if (strcmp("none", opstr) == 0)
        *op = OP_NONE;
    else{
        clicon_err(OE_XML, 0, "Bad-attribute operation: %s", opstr);
        return -1;
    }
    return 0;
}

/*! Map xml operation from enumeration to string
 * @param[in]   op   enumeration operation, eg OP_MERGE,...
 * @retval      str  String, eg "merge". Static string, no free necessary
 * @code
 *   enum operation_type op;
 *   xml_operation("replace", &op)
 * @endcode
 */
char *
xml_operation2str(enum operation_type op)
{
    switch (op){
    case OP_MERGE:
        return "merge";
        break;
    case OP_REPLACE:
        return "replace";
        break;
    case OP_CREATE:
        return "create";
        break;
    case OP_DELETE:
        return "delete";
        break;
    case OP_REMOVE:
        return "remove";
        break;
    default:
        return "none";
    }
}

/*! Map xml insert attribute from string to enumeration
 * @param[in]   instr String, eg "first"
 * @param[out]  ins   Enumeration, eg INS_FIRST
 * @code
 *   enum insert_type ins;
 *   xml_operation("last", &ins)
 * @endcode
 */
int
xml_attr_insert2val(char             *instr, 
                    enum insert_type *ins)
{
    if (strcmp("first", instr) == 0)
        *ins = INS_FIRST;
    else if (strcmp("last", instr) == 0)
        *ins = INS_LAST;
    else if (strcmp("before", instr) == 0)
        *ins = INS_BEFORE;
    else if (strcmp("after", instr) == 0)
        *ins = INS_AFTER;
    else{
        clicon_err(OE_XML, 0, "Bad-attribute operation: %s", instr);
        return -1;
    }
    return 0;
}

/*! Add attribute and value to an xml element as well as namespace mapping
 *
 * @param[in]  xn        XML element
 * @param[in]  name      Attribute name
 * @param[in]  value     Attribute value
 * @param[in]  prefix    Attribute prefix, or NULL
 * @param[in]  namespace Attribute prefix, if NULL do not add namespace mapping
 */
int
xml_add_attr(cxobj *xn,
             char  *name,
             char  *value,
             char  *prefix,
             char  *namespace)
{
    int    retval = -1;
    cxobj *xa;
    char  *ns = NULL;

    if ((xa = xml_new(name, xn, CX_ATTR)) == NULL)
        goto done;
    if (prefix && xml_prefix_set(xa, prefix) < 0) /* clixon lib */
        goto done;
    if (xml_value_set(xa, value) < 0)
        goto done;
    if (namespace){
        if (xml2ns(xn, prefix, &ns) < 0)
            goto done;
        if (ns == NULL && xmlns_set(xn, prefix, namespace) < 0) 
            goto done;
    }
    retval = 0;
 done:
    return retval;
}

/*! Specialization of clicon_log with xml tree 
 *
 * @param[in]  dbglevel 
 * @param[in]  level    log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. 
 * @param[in]  x        XML tree that is logged without prettyprint
 * @param[in]  format   Message to print as argv.
 * @see clicon_debug_xml  which uses debug setting instead of direct syslog
 */
int
clicon_log_xml(int         level, 
               cxobj      *x,
               const char *format, ...)
{
    va_list args;
    size_t  len;
    char   *msg = NULL;
    cbuf   *cb = NULL;
    int     retval = -1;
    size_t  trunc;

    /* Print xml as cbuf */
    if ((cb = cbuf_new()) == NULL){
        clicon_err(OE_XML, errno, "cbuf_new");
        goto done;
    }
    if (clixon_xml2cbuf(cb, x, 0, 0, NULL, -1, 0) < 0)
        goto done;
    /* first round: compute length of debug message */
    va_start(args, format);
    len = vsnprintf(NULL, 0, format, args);
    va_end(args);
    
    /* Truncate long debug strings */
    if ((trunc = clicon_log_string_limit_get()) && trunc < len)
        len = trunc;

    /* allocate a message string exactly fitting the message length */
    if ((msg = malloc(len+1)) == NULL){
        fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
        goto done;
    }

    /* second round: compute write message from format and args */
    va_start(args, format);
    if (vsnprintf(msg, len+1, format, args) < 0){
        va_end(args);
        fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
        goto done;
    }
    va_end(args);

    /* Actually log it */
    clicon_log(level, "%s: %s", msg, cbuf_get(cb));

    retval = 0;
  done:
    if (cb)
        cbuf_free(cb);
    if (msg)
        free(msg);
    return retval;
}

/*! Specialization of clicon_debug with xml tree 
 *
 * @param[in]  dbglevel Mask of CLIXON_DBG_DEFAULT and other masks
 * @param[in]  x        XML tree that is logged without prettyprint
 * @param[in]  format   Message to print as argv.
 * @see clicon_log_xml  For syslog
 * @see clicon_debug    base function and see CLIXON_DBG_* flags
*/
int
clicon_debug_xml(int         dbglevel, 
                 cxobj      *x,
                 const char *format, ...)
{
    va_list args;
    size_t  len;
    char   *msg = NULL;
    cbuf   *cb = NULL;
    int     retval = -1;
    size_t  trunc;

    /* Mask debug level with global dbg variable */
    if ((dbglevel & clicon_debug_get()) == 0) 
        return 0;
    /* Print xml as cbuf */
    if ((cb = cbuf_new()) == NULL){
        clicon_err(OE_XML, errno, "cbuf_new");
        goto done;
    }
    if (clixon_xml2cbuf(cb, x, 0, 0, NULL, -1, 0) < 0)
        goto done;
    /* first round: compute length of debug message */
    va_start(args, format);
    len = vsnprintf(NULL, 0, format, args);
    va_end(args);
    
    /* Truncate long debug strings */
    if ((trunc = clicon_log_string_limit_get()) && trunc < len)
        len = trunc;

    /* allocate a message string exactly fitting the message length */
    if ((msg = malloc(len+1)) == NULL){
        fprintf(stderr, "malloc: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
        goto done;
    }

    /* second round: compute write message from format and args */
    va_start(args, format);
    if (vsnprintf(msg, len+1, format, args) < 0){
        va_end(args);
        fprintf(stderr, "vsnprintf: %s\n", strerror(errno)); /* dont use clicon_err here due to recursion */
        goto done;
    }
    va_end(args);

    /* Actually log it */
    clicon_debug(dbglevel, "%s: %s", msg, cbuf_get(cb));

    retval = 0;
  done:
    if (cb)
        cbuf_free(cb);
    if (msg)
        free(msg);
    return retval;
}

#ifdef XML_EXPLICIT_INDEX
/*
 *
 */

/*! Is this XML object a search index, ie it is registered as a yang clixon cc:search_index
 * Is this xml node a search index and does it have a parent that is a list and a grandparent 
 * where a search-vector can be placed
 * @param[in] x  XML object
 * @retval    1  Yes
 * @retval    0  No
 */
int
xml_search_index_p(cxobj *x)
{
    yang_stmt *y;
    cxobj     *xp;
    
    /* The index variable has a yang spec */
    if ((y = xml_spec(x)) == NULL)
        return 0;
    /* The index variable is a registered search index */
    if (yang_flag_get(y, YANG_FLAG_INDEX) == 0)
        return 0;
    /* The index variable has a parent which has a LIST yang spec  */
    if ((xp = xml_parent(x)) == NULL)
        return 0;
    if ((y = xml_spec(xp)) == NULL)
        return 0;
    if (yang_keyword_get(y) != Y_LIST)
        return 0;
    /* The index variable has a grand-parent */
    if (xml_parent(xp) == NULL)
        return 0;
    return 1;
}

/*! Free all search vector pairs of this XML node
 * @param[in]  x    XML object
 * @retval     0    OK
 * @retval    -1    Error
 */
static int
xml_search_index_free(cxobj *x)
{
    struct search_index *si;

    while ((si = x->x_search_index) != NULL) {
        DELQ(si, x->x_search_index, struct search_index *);
        if (si->si_name)
            free(si->si_name);
        if (si->si_xvec)
            clixon_xvec_free(si->si_xvec);
        free(si);
    }
    return 0;
}

/*! Add single search vector pair to this XML node
 * @param[in]  x     XML object
 * @param[in]  name  Name of index variable
 * @retval     0     OK
 * @retval    -1     Error
 */
static struct search_index *
xml_search_index_add(cxobj *x,
                     char  *name)
{
    struct search_index *si = NULL;

    if ((si = malloc(sizeof(struct search_index))) == NULL){
        clicon_err(OE_XML, errno, "malloc");
        goto done;
    }
    memset(si, 0, sizeof(struct search_index));
    if ((si->si_name = strdup(name)) == NULL){
        clicon_err(OE_XML, errno, "strdup");
        free(si);
        si = NULL;
        goto done;
    }
    if ((si->si_xvec = clixon_xvec_new()) == NULL){
        free(si->si_name);
        free(si);
        si = NULL;
        goto done;
    }
    ADDQ(si, x->x_search_index);
 done:
    return si;
}

/*! Add single search vector pair to this XML node
 * @param[in]  x     XML object
 * @param[in]  name  Name of index variable
 * @retval     0     OK
 * @retval    -1     Error
 */
static struct search_index *
xml_search_index_get(cxobj *x,
                     char  *name)
{
    struct search_index *si = NULL;

    if ((si = x->x_search_index) != NULL) {
        do {
            if (strcmp(si->si_name, name) == 0){
                goto done;
                break;
            }
            si = NEXTQ(struct search_index *, si);
        } while (si && si != x->x_search_index);
    }
 done:
    return si;
}

/*--------------------------------------------------*/

/*! Get sorted index vector for list for variable "name"
 * @param[in]  xp    XML parent object
 * @param[in]  name  Name of index variable
 * @param[out] xvec  XML object search vector
 * @retval     0     OK
 */
int
xml_search_vector_get(cxobj        *xp,
                      char         *name,
                      clixon_xvec **xvec)
{
    struct search_index *si;

    *xvec = NULL;
    if ((si = xp->x_search_index) != NULL) {
        do {
            if (strcmp(si->si_name, name) == 0){
                *xvec = si->si_xvec;
                break;
            }
            si = NEXTQ(struct search_index *, si);
        } while (si && si != xp->x_search_index);
    }
    return 0;
}

/*! Insert a new cxobj into search index vector for list for variable "name"
 * @param[in] xp XML parent object (the list element)
 * @param[in] xi XML index object (that should be added)
 */
int
xml_search_child_insert(cxobj *xp,
                        cxobj *xi)
{
    int                  retval = -1;
    char                *indexvar;
    struct search_index *si;
    cxobj               *xpp;
    int                  i;
    int                  len;
    
    indexvar = xml_name(xi);
    if ((xpp = xml_parent(xp)) == NULL)
        goto ok;
    /* Find base vector in grandparent */
    if ((si = xml_search_index_get(xpp, indexvar)) == NULL){
        /* If not found add base vector in grand-parent */            
        if ((si = xml_search_index_add(xpp, indexvar)) == NULL)
            goto done;
    }
    /* Find element position using binary search and then remove */
    len = clixon_xvec_len(si->si_xvec);
    if ((i = xml_search_indexvar_binary_pos(xp, indexvar, si->si_xvec, 0, len, len, NULL)) < 0)
        goto done;
    assert(clixon_xvec_i(si->si_xvec, i) != xp);
    if (clixon_xvec_insert_pos(si->si_xvec, xp, i) < 0)
        goto done;
 ok:
    retval = 0;
 done:    
    return retval;
}

/*! Remove a single cxobj from search vector 
 * @param[in] xp  XML parent object (the list element)
 * @param[in] xi  XML index object (that should be added)
 */
int
xml_search_child_rm(cxobj *xp,
                    cxobj *xi)
{
    int                 retval = -1;
    cxobj              *xpp;
    char               *indexvar;
    int                 i;
    int                 len;
    struct search_index *si;
    int                  eq = 0;
    
    indexvar = xml_name(xi);
    if ((xpp = xml_parent(xp)) == NULL)
        goto ok;
    /* Find base vector in grandparent */
    if ((si = xml_search_index_get(xpp, indexvar)) == NULL)
        goto ok;
    
    /* Find element using binary search and then remove */
    len = clixon_xvec_len(si->si_xvec);
    if ((i = xml_search_indexvar_binary_pos(xp, indexvar, si->si_xvec, 0, len, len, &eq)) < 0)
        goto done;
        //    if (clixon_xvec_i(si->si_xvec, i) == xp)
    if (eq)
        if (clixon_xvec_rm_pos(si->si_xvec, i) < 0)
            goto done;          
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Iterator over xml children objects using (explicit) index variable
 *
 * @param[in] xparent xml tree node whose children should be iterated
 * @param[in] name    Name of index variable
 * @param[in] xprev   previous child, or NULL on init
 * @param[in] type    matching type or -1 for any
 * @code
 *   cxobj *x = NULL;
 *   while ((x = xml_child_index_each(x_top, "i", x, -1)) != NULL) {
 *     ...
 *   }
 * @endcode
 * @see xml_child_each  for looping over structural children.
 * @note uses _x_vector_i as a shared resource: you cannot mix loops over same parent
 * Further, never manipulate the child-list during operation or using the
 * same object recursively, the function uses an internal field to remember the
 * index used. It works as long as the same object is not iterated concurrently. 
 * If you need to delete a node you can do somethjing like:
 */
cxobj *
xml_child_index_each(cxobj           *xparent, 
                     char            *name,
                     cxobj           *xprev, 
                     enum cxobj_type  type)
{
    cxobj        *xn = NULL; 
    clixon_xvec  *xv = NULL;
    int           i;
    
    if (xparent == NULL)
        return NULL;
    if (!is_element(xparent))
        return NULL;
    if (xml_search_vector_get(xparent, name, &xv) < 0)
        return NULL;
    if (xv == NULL)
        return NULL;
    for (i=xprev?xprev->_x_vector_i+1:0; i<clixon_xvec_len(xv); i++){
        if ((xn = clixon_xvec_i(xv, i)) == NULL)
            continue;
        if (type != CX_ERROR && xml_type(xn) != type)
            continue;
        break; /* this is next object after previous */
    }
    if (i < clixon_xvec_len(xv)) /* found */
        xn->_x_vector_i = i;
    else
        xn = NULL;
    return xn;
}

#endif /* XML_EXPLICIT_INDEX */
