/*
 *
  ***** BEGIN LICENSE BLOCK *****
 
  Copyright (C) 2009-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 *****

 * Yang functions
 * @see https://tools.ietf.org/html/rfc6020 YANG 1.0
 * @see https://tools.ietf.org/html/rfc7950 YANG 1.1
 */

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

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <regex.h>
#include <dirent.h>
#include <sys/types.h>
#include <fcntl.h>
#include <syslog.h>
#include <assert.h>
#include <libgen.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <netinet/in.h>

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

/* clixon */
#include "clixon_string.h"
#include "clixon_map.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_file.h"
#include "clixon_yang.h"
#include "clixon_hash.h"
#include "clixon_xml.h"
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_debug.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_yang_module.h"
#include "clixon_plugin.h"
#include "clixon_data.h"
#include "clixon_options.h"
#include "clixon_yang_parse.h"
#include "clixon_yang_sub_parse.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_yang_cardinality.h"
#include "clixon_yang_type.h"
#include "clixon_yang_schema_mount.h"
#include "clixon_yang_internal.h" /* internal included by this file only, not API */

#ifdef XML_EXPLICIT_INDEX
static int yang_search_index_extension(clixon_handle h, yang_stmt *yext, yang_stmt *ys);
#endif

/*
 * Local variables
 */
/* Mapping between yang keyword string <--> clixon constants 
 * Here is also the place where doc on some types store variables (cv)
 */
static const map_str2int ykmap[] = {
    {"action",           Y_ACTION},
    {"anydata",          Y_ANYDATA},
    {"anyxml",           Y_ANYXML},
    {"argument",         Y_ARGUMENT},
    {"augment",          Y_AUGMENT},
    {"base",             Y_BASE},
    {"belongs-to",       Y_BELONGS_TO},
    {"bit",              Y_BIT},
    {"case",             Y_CASE},
    {"choice",           Y_CHOICE},
    {"config",           Y_CONFIG},  /* cv: boolean config flag */
    {"contact",          Y_CONTACT},
    {"container",        Y_CONTAINER},
    {"default",          Y_DEFAULT},
    {"description",      Y_DESCRIPTION},
    {"deviate",          Y_DEVIATE},
    {"deviation",        Y_DEVIATION},
    {"enum",             Y_ENUM},
    {"error-app-tag",    Y_ERROR_APP_TAG},
    {"error_message",    Y_ERROR_MESSAGE},
    {"extension",        Y_EXTENSION},
    {"feature",          Y_FEATURE},        /* cv: feature as boolean */
    {"fraction-digits",  Y_FRACTION_DIGITS}, /* cv: fraction-digits as uint8 */
    {"grouping",         Y_GROUPING},
    {"identity",         Y_IDENTITY},
    {"if-feature",       Y_IF_FEATURE},
    {"import",           Y_IMPORT},
    {"include",          Y_INCLUDE},
    {"input",            Y_INPUT},
    {"key",              Y_KEY},
    {"leaf",             Y_LEAF},           /* cv: store default value (if any)*/
    {"leaf-list",        Y_LEAF_LIST},      /* cv: store default value (if any)*/
    {"length",           Y_LENGTH},
    {"list",             Y_LIST},
    {"mandatory",        Y_MANDATORY},      /* cv: store mandatory boolean */
    {"max-elements",     Y_MAX_ELEMENTS},
    {"min-elements",     Y_MIN_ELEMENTS},
    {"modifier",         Y_MODIFIER},
    {"module",           Y_MODULE},
    {"must",             Y_MUST},
    {"namespace",        Y_NAMESPACE},
    {"notification",     Y_NOTIFICATION},
    {"ordered-by",       Y_ORDERED_BY},
    {"organization",     Y_ORGANIZATION},
    {"output",           Y_OUTPUT},
    {"path",             Y_PATH},
    {"pattern",          Y_PATTERN},
    {"position",         Y_POSITION},
    {"prefix",           Y_PREFIX},
    {"presence",         Y_PRESENCE},
    {"range",            Y_RANGE},
    {"reference",        Y_REFERENCE},
    {"refine",           Y_REFINE},
    {"require-instance", Y_REQUIRE_INSTANCE},
    {"revision",         Y_REVISION},          /* cv: YYYY-MM-DD as uint32 */
    {"revision-date",    Y_REVISION_DATE},     /* cv: YYYY-MM-DD as uint32 */
    {"rpc",              Y_RPC},
    {"status",           Y_STATUS},
    {"submodule",        Y_SUBMODULE},
    {"type",             Y_TYPE},
    {"typedef",          Y_TYPEDEF},
    {"unique",           Y_UNIQUE},
    {"units",            Y_UNITS},
    {"unknown",          Y_UNKNOWN},  /* cv: store extra string */
    {"uses",             Y_USES},
    {"value",            Y_VALUE},
    {"when",             Y_WHEN},
    {"yang-version",     Y_YANG_VERSION},
    {"yin-element",      Y_YIN_ELEMENT},
    {"yang-specification", Y_SPEC}, /* XXX: NOTE NOT YANG STATEMENT, reserved
                                       for top level spec */
    {NULL,               -1}
};

/* XXX: Global variables, should really be on handle but not available in some cases */
static map_ptr2ptr *_yang_when_map = NULL;
static map_ptr2ptr *_yang_mymodule_map = NULL;

/* Forward static */
static int yang_type_cache_free(yang_type_cache *ycache);

/* Access functions
 */

/*! Get Number of children yang statements
 *
 * @param[in] ys  Yang statement node
 */
int
yang_len_get(yang_stmt *ys)
{
    return ys->ys_len;
}

/*! Get Specific Yang statement child
 *
 * @param[in] ys  Yang statement node
 * @param[in] i   Return this child
 */
yang_stmt *
yang_child_i(yang_stmt *ys,
             int        i)
{
    return ys->ys_stmt[i];
}

/*! Get yang statement parent
 *
 * @param[in] ys  Yang statement node
 */
yang_stmt *
yang_parent_get(yang_stmt *ys)
{
    return ys->ys_parent;
}

/*! Get yang statement keyword
 *
 * @param[in] ys  Yang statement node
 */
enum rfc_6020
yang_keyword_get(yang_stmt *ys)
{
    return ys->ys_keyword;
}

/*! Get yang statement context-dependent argument
 *
 * @param[in] ys  Yang statement node
 */
char*
yang_argument_get(yang_stmt *ys)
{
    return ys->ys_argument;
}

/*! Set yang argument, not not copied
 *
 * @param[in] ys   Yang statement node
 * @param[in] arg  Argument, note must be malloced
 * Typically only done at parsing / initiation
 * @see yang_argument_dup
 */
int
yang_argument_set(yang_stmt *ys,
                  char      *arg)
{
    ys->ys_argument = arg; /* not strdup/copied */
    return 0;
}

/*! Set yang argument, copied
 *
 * @param[in] ys   Yang statement node
 * @param[in] arg  Argument, is copied
 */
int
yang_argument_dup(yang_stmt *ys,
                  char      *arg)
{
    char *dup;

    if ((dup = strdup(arg)) == NULL){
        clixon_err(OE_UNIX, errno, "strdup");
        return -1;
    }
    ys->ys_argument = dup; /* not strdup/copied */
    return 0;
}

/*! Get original back-pointer if node is grouping or augmented
 *
 * Defined only if this statement was created as part of a uses/grouping or augment expansion
 * @param[in] ys    Yang statement node
 * @retval    y0    Original yang statement node
 * @retval    NULL  Not part of grouping
 */
yang_stmt *
yang_orig_get(yang_stmt *ys)
{
    return ys->ys_orig;
}

/*! Set original back-pointer if any
 *
 * Will recursively evaluate to set to original node in case of chain of groupings
 * @param[in] ys    Yang statement node
 * @param[in] y0    Original yang statement node
 * @retval    NULL  Not part of grouping
 */
int
yang_orig_set(yang_stmt *ys,
              yang_stmt *y0)
{
    if (y0->ys_orig){
        yang_orig_set(ys, y0->ys_orig);
    }
    else
        ys->ys_orig = y0;
    return 0;
}

/*! Get yang statement CLIgen variable
 *
 * See comment under ys_cv for how this is used
 * @param[in] ys  Yang statement node
 */
cg_var*
yang_cv_get(yang_stmt *ys)
{
    return ys->ys_cv;
}

/*! Set yang statement CLIgen variable
 *
 * @param[in] ys  Yang statement node
 * @param[in] cv  cligen variable
 * @note: Frees on replace, not if cv is NULL. This is for some ys_cp/ys_dup cases, which means 
 *        you need to free it explicitly to set it to NULL proper.
 */
int
yang_cv_set(yang_stmt *ys,
            cg_var    *cv)
{
    if (cv != NULL && ys->ys_cv != NULL)
        cv_free(ys->ys_cv);
    ys->ys_cv = cv;
    return 0;
}

/*! Get yang statement CLIgen variable vector
 *
 * See comment under ys_cvec for how this is used
 * @param[in] ys  Yang statement node
 * @note  To add entries, use yang_cvec_add(), since this function may return NULL
 */
cvec*
yang_cvec_get(yang_stmt *ys)
{
    return ys->ys_cvec;
}

/*! Set yang statement CLIgen variable vector
 *
 * @param[in] ys   Yang statement node
 * @param[in] cvec CLIgen vector
 * @retval    0    OK
 * @retval   -1    Error
 */
int
yang_cvec_set(yang_stmt *ys,
              cvec      *cvv)
{
    if (ys->ys_cvec)
        cvec_free(ys->ys_cvec);
    ys->ys_cvec = cvv;
    return 0;
}

/*! Add new value to yang cvec, create if not exist
 *
 * @param[in] ys    Yang statement
 * @param[in] type  Append a new cv to the vector with this type
 * @param[in] name  Name of variable
 * @retval    cv    The new cligen variable
 * @retval    NULL  Error
 */
cg_var *
yang_cvec_add(yang_stmt    *ys,
              enum cv_type type,
              char        *name)
{
    cg_var *cv;
    cvec   *cvv;

    if ((cvv = yang_cvec_get(ys)) == NULL){
        if ((cvv = cvec_new(0)) == NULL){
            clixon_err(OE_YANG, errno, "cvec_new");
            return NULL;
        }
        yang_cvec_set(ys, cvv);
    }
    if ((cv = cvec_add(cvv, type)) == NULL){
        clixon_err(OE_YANG, errno, "cvec_add");
        return NULL;
    }
    if (cv_name_set(cv, name) == NULL){
        clixon_err(OE_YANG, errno, "cv_name_set(%s)", name);
        return NULL;
    }
    return cv;
}

/*! Get yang stmt flags, used for internal algorithms
 *
 * @param[in]  ys     Yang statement
 * @param[in]  flag   Flags value(s) to get, see YANG_FLAG_*
 * @retval     value  Flags value masked by "flag" parameter, see YANG_FLAG_*
 */
uint16_t
yang_flag_get(yang_stmt *ys,
              uint16_t   flag)
{
    return ys->ys_flags&flag;
}

/*! Set yang stmt flags, used for internal algorithms
 *
 * @param[in]  ys     Yang statement
 * @param[in]  flag   Flag value(s) to set, see YANG_FLAG_*
 */
int
yang_flag_set(yang_stmt *ys,
              uint16_t   flag)
{
    ys->ys_flags |= flag;
    return 0;
}

/*! Reset yang stmt flags, used for internal algorithms
 *
 * @param[in]  ys     Yang statement
 * @param[in]  flag   Flag value(s) to reset, see YANG_FLAG_*
 */
int
yang_flag_reset(yang_stmt *ys,
                uint16_t   flag)
{
    ys->ys_flags &= ~flag;
    return 0;
}

/*! Get Yang when statement from external map
 *
 * @param[in]  h     Clixon handle (may be NULL)
 * @param[in]  ys    Yang statement
 * @retval     ywhen Yang when statement
 * @retval     NULL  No yang when
 * @note  h may be NULL since may not be available by caller
 */
yang_stmt *
yang_when_get(clixon_handle h,
              yang_stmt    *ys)
{
    map_ptr2ptr *mp = _yang_when_map;

    if (mp == NULL){
        clixon_log(h, LOG_WARNING, "when_map not defined, yang_init() not called?");
        return NULL;
    }
    else {
        if (yang_flag_get(ys, YANG_FLAG_WHEN) != 0x0 && mp != NULL)
            return clixon_ptr2ptr(mp, ys);
    }
    return NULL;
}

/*! Set Yang when statement to external map
 *
 * @param[in]  h     Clixon handle
 * @param[in]  ys    Yang statement
 * @param[int] ywhen Yang when statement
 * @retval     0     OK
 * @retval    -1     Error
 */
int
yang_when_set(clixon_handle h,
              yang_stmt    *ys,
              yang_stmt    *ywhen)
{
    int          retval = -1;
    map_ptr2ptr *mp = _yang_when_map;

    if (mp == NULL){
        clixon_log(h, LOG_WARNING, "when_map not defined, yang_init() not called?");
        goto done;
    }
    else {
        if (clixon_ptr2ptr(mp, ys) != NULL) {
            clixon_err(OE_YANG, 0, "when pointer already set");
            goto done;
        }
        if (clixon_ptr2ptr_add(&_yang_when_map, ys, ywhen) < 0)
            goto done;
        yang_flag_set(ys, YANG_FLAG_WHEN);
    }
    retval = 0;
 done:
    return retval;
}

/*! Get xpath and namespace context for "when"-associated augment
 *
 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
 * Inserts new yang nodes at <path> with this special "when" struct (not yang node)
 * @param[in]  ys     Yang statement
 * @param[out] xpath
 * @param[out] nsc
 */
int
yang_when_xpath_get(yang_stmt *ys,
                    char      **xpath,
                    cvec      **nsc)
{
    int        retval = -1;
    yang_stmt *ywhen;
    cvec      *nsc0 = NULL;

    if ((ywhen = yang_when_get(NULL, ys)) != NULL) {
        if (xml_nsctx_yang(ywhen, nsc) < 0)
            goto done;
        if (xpath)
            *xpath = yang_argument_get(ywhen);
    }
    retval = 0;
 done:
    if (nsc0)
        cvec_free(nsc0);
    return retval;
}

/*! Get canonical xpath and namespace context for "when"-associated augment
 *
 * Ie, for yang structures like: augment <path> { when <xpath>; ... }
 * Inserts new yang nodes at <path> with this special "when" struct (not yang node)
 * @param[in]  ys     Yang statement
 * @param[out] xpath  Free after use
 * @param[out] nsc    Free with cvec_free
 */
int
yang_when_canonical_xpath_get(yang_stmt *ys,
                              char     **xpath,
                              cvec     **nsc)
{
    int        retval = -1;
    yang_stmt *ywhen;
    char      *xpath0;
    cvec      *nsc0 = NULL;

    if ((ywhen = yang_when_get(NULL, ys)) != NULL) {
        if (xml_nsctx_yang(ywhen, &nsc0) < 0)
            goto done;
        xpath0 = yang_argument_get(ywhen);
        if (xpath0 && nsc0){
            if (xpath2canonical1(xpath0, nsc0, ys_spec(ys), 1, xpath, nsc, NULL) < 0)
                goto done;
        }
    }
    retval = 0;
 done:
    if (nsc0)
        cvec_free(nsc0);
    return retval;
}

/*! Get yang filename for error/debug purpose (only modules)
 *
 * @param[in]  ys       Yang statement
 * @retval     filename
 * @note there maye not always be a "filename" in case the yang is read from memory
 */
const char *
yang_filename_get(yang_stmt *ys)
{
    return ys->ys_filename;
}

/*! Set yang filename for error/debug purpose
 *
 * @param[in]  ys       Yang statement
 * @param[in]  filename
 * @retval     0        OK
 * @retval    -1        Error
 * @note there maye not always be a "filename" in case the yang is read from memory
 */
int
yang_filename_set(yang_stmt  *ys,
                  const char *filename)
{
    if ((ys->ys_filename = strdup(filename)) == NULL){
        clixon_err(OE_UNIX, errno, "strdup");
        return -1;
    }
    return 0;
}

/*! Get line number of yang filename for error/debug purpose
 *
 * @param[in]  ys       Yang statement
 * @retval     linenum
 */
uint32_t
yang_linenum_get(yang_stmt *ys)
{
#ifdef YANG_SPEC_LINENR
    return ys->ys_linenum;
#else
    return 0;
#endif
}

/*! Set line number of yang filename for error/debug purpose
 *
 * @param[in]  ys       Yang statement
 * @param[in]  linenum
 */
int
yang_linenum_set(yang_stmt *ys,
                 uint32_t   linenum)
{
#ifdef YANG_SPEC_LINENR
    ys->ys_linenum = linenum;
#endif
    return 0;
}

/*! Get type cache
 *
 * @param[in]  ys       Yang statement
 * @retval     ycache
 */
void *
yang_typecache_get(yang_stmt *ys)
{
    return ys->ys_typecache;
}

/*! Set type cache
 *
 * @param[in]  ys      Yang statement
 * @param[in]  ycache  Type cache
 * @retval     0       OK
 */
int
yang_typecache_set(yang_stmt *ys,
                   void      *ycache)
{
    ys->ys_typecache = ycache;
    return 0;
}

/*! Get mymodule
 *
 * Shortcut to "my" module. Used by augmented and unknown nodes
 * @param[in]  ys    YANG statement
 * @retval     ymod  YANG module
 */
yang_stmt*
yang_mymodule_get(yang_stmt *ys)
{
    map_ptr2ptr *mp = _yang_mymodule_map;

    if (mp == NULL){
        clixon_log(NULL, LOG_WARNING, "mymodule_map not defined, yang_init() not called?");
        return NULL;
    }
    else if (yang_flag_get(ys, YANG_FLAG_MYMODULE) == 0x0)
        return NULL;
    else
        return clixon_ptr2ptr(mp, ys);
}

/*! Set mymodule
 *
 * Shortcut to "my" module. Used by augmented and unknown nodes
 * @param[in]  ys    YANG statement
 * @param[in]  ymod  YANG module
 * @retval     0     OK
 * @retval    -1     Error
 */
int
yang_mymodule_set(yang_stmt *ys,
                  yang_stmt *ym)
{
    int          retval = -1;
    map_ptr2ptr *mp = _yang_mymodule_map;

    if (mp == NULL){
        clixon_log(NULL, LOG_WARNING, "mymodule_map not defined, yang_init() not called?");
        goto done;
    }
    else {
        if (clixon_ptr2ptr(mp, ys) == NULL) {
            if (clixon_ptr2ptr_add(&_yang_mymodule_map, ys, ym) < 0)
                goto done;
        }
    }
    yang_flag_set(ys, YANG_FLAG_MYMODULE);
    retval = 0;
 done:
    return retval;
}

/* End access functions */

/* Stats */
static uint64_t _stats_yang_nr = 0;

/*! Get global statistics about YANG statements: created - freed
 *
 * @param[out]  nr  Number of existing YANG objects (created - freed)
 */
int
yang_stats_global(uint64_t *nr)
{
    if (nr)
        *nr = _stats_yang_nr;
    return 0;
}

/*! Return the alloced memory of a single YANG obj
 *
 * @param[in]   y    YANG object
 * @param[out]  szp  Size of this YANG obj
 * @retval      0    OK
 * (baseline: )
 */
static int
yang_stats_one(yang_stmt *ys,
               size_t    *szp)
{
    size_t           sz = 0;
    yang_type_cache *yc;

    sz += sizeof(struct yang_stmt);
    sz += ys->ys_len*sizeof(struct yang_stmt*);
    if (ys->ys_argument)
        sz += strlen(ys->ys_argument) + 1;
    if (ys->ys_cvec)
        sz += cvec_size(ys->ys_cvec);
    switch (ys->ys_keyword) {
    case Y_TYPE:
        if ((yc = yang_typecache_get(ys)) != NULL){
             sz += sizeof(struct yang_type_cache);
             if (yc->yc_cvv)
                 sz += cvec_size(yc->yc_cvv);
             if (yc->yc_patterns)
                 sz += cvec_size(yc->yc_patterns);
             if (yc->yc_regexps)
                 sz += cvec_size(yc->yc_regexps);
         }
         break;
    case Y_MODULE:
    case Y_SUBMODULE:
        if (ys->ys_filename)
            sz += strlen(ys->ys_filename) + 1;
        break;
    default:
        break;
    }
    if (szp)
        *szp = sz;
    return 0;
}

/*! Return statistics of an YANG-stmt tree recursively
 *
 * @param[in]   yt   YANG object
 * @param[in]   keyw YANG keyword, or 0 for all
 * @param[out]  nrp  Number of YANG objects recursively
 * @param[out]  szp  Size of this YANG stmt recursively
 * @retval      0    OK
 * @retval     -1    Error
 */
int
yang_stats(yang_stmt    *yt,
           enum rfc_6020 keyw,
           uint64_t     *nrp,
           size_t       *szp)
{
    int        retval = -1;
    size_t     sz = 0;
    yang_stmt *ys;
    int        inext;

    if (yt == NULL){
        clixon_err(OE_XML, EINVAL, "yang spec is NULL");
        goto done;
    }
    if (keyw == 0 || yang_keyword_get(yt) == keyw){
        *nrp += 1;
        yang_stats_one(yt, &sz);
        if (szp)
            *szp += sz;
    }
    inext = 0;
    while ((ys = yn_iter(yt, &inext)) != NULL) {
        sz = 0;
        yang_stats(ys, keyw, nrp, &sz);
        if (szp)
            *szp += sz;
    }
    retval = 0;
 done:
    return retval;
}

/* stats end */

/*! Create new yang specification, addd as child to top-level yang_mounts
 *
 * @retval  yspec    Free with ys_free()
 * @retval  NULL     Error
 */
yang_stmt *
yspec_new(clixon_handle h,
          char         *name)
{
    yang_stmt *ymounts;
    yang_stmt *yspec;

    if ((ymounts = clixon_yang_mounts_get(h)) == NULL){
        clixon_err(OE_YANG, 0, "Yang-mounts not created");
        goto done;
    }
    if ((yspec = ys_new(Y_SPEC)) == NULL)
        goto done;
    if (yang_argument_dup(yspec, name) < 0)
        goto done;
    if (yn_insert(ymounts, yspec) < 0)
        goto done;
    return yspec;
 done:
    return NULL;
}

/*! Create or add a shared yspec
 *
 * @param[in]     h      Clixon handle
 * @param[in]     name   Typically an xpath
 * @param[in]     yspec0 Input NULL if no previous shared exist, otherwise a shared yspec but new name
 * @retval        yspec1 New or (previously shared)
 * @retval        NULL   Error
 */
yang_stmt *
yspec_new_shared(clixon_handle h,
                 char         *name,
                 yang_stmt    *yspec0)
{
    yang_stmt *yspec1 = NULL;

    if (yspec0 != NULL){ /* shared */
        assert(clicon_option_bool(h, "CLICON_YANG_SCHEMA_MOUNT_SHARE") != 0);
        yspec1 = yspec0;
    }
    else {
        if ((yspec1 = yspec_new(h, name)) == NULL)
            goto done;
        yang_flag_set(yspec1, YANG_FLAG_SPEC_MOUNT);
        clixon_debug(CLIXON_DBG_YANG, "new yang-spec: %p", yspec1);
    }
    if (yang_cvec_add(yspec1, CGV_STRING, name) < 0){
        yspec1 = NULL;
        goto done;
    }
 done:
    return yspec1;
}

/*! Create new yang node/statement given size
 *
 * Size parameter for variable size, eg extended YANG struct
 * @param[in] keyw  Yang stmt keyword
 * @param[in] sz    Size of created struct
 * @retval    ys    New yang-stmt. Free with ys_free()
 * @retval    NULL  Error
 */
static yang_stmt *
ys_new_sz(enum rfc_6020 keyw,
          size_t        sz)
{
    yang_stmt *ys;

    if ((ys = malloc(sz)) == NULL){
        clixon_err(OE_YANG, errno, "malloc");
        return NULL;
    }
    memset(ys, 0, sz);
    ys->ys_keyword = keyw;
    _stats_yang_nr++;
    return ys;
}

/*! Create new yang node/statement
 *
 * @param[in] keyw  Yang stmt keyword
 * @retval    ys    New yang-stmt. Free with ys_free()
 * @retval    NULL  Error
 * @note UNKNOWN nodes are always extended
 */
yang_stmt *
ys_new(enum rfc_6020 keyw)
{
    return ys_new_sz(keyw, sizeof(struct yang_stmt));
}

/*! Free a single yang statement, dont remove children, called after children freed
 * 
 * @param[in]  ys   Yang node to remove 
 * @param[in]  self Free own node including child vector
 * @retval     0    OK
 * @retval    -1    Error
 * @see ys_free  Also free children
 */
int
ys_free1(yang_stmt *ys,
         int        self)
{
    rpc_callback_t *rc;
    cg_var         *cv;

    if ((cv = ys->ys_cv) != NULL){ // To not trigger asserts
        ys->ys_cv = NULL;
        cv_free(cv);
    }
    if (ys->ys_argument){
        free(ys->ys_argument);
        ys->ys_argument = NULL;
    }
    if (ys->ys_stmt)
        free(ys->ys_stmt);
    switch (ys->ys_keyword) {     /* type-specifi union fields */
    case Y_ACTION:
        while((rc = ys->ys_action_cb) != NULL) {
            DELQ(rc, ys->ys_action_cb, rpc_callback_t *);
            if (rc->rc_namespace)
                free(rc->rc_namespace);
            if (rc->rc_name)
                free(rc->rc_name);
            free(rc);
        }
        break;
    case Y_TYPE:
        if (ys->ys_typecache){
            yang_type_cache_free(ys->ys_typecache);
            ys->ys_typecache = NULL;
        }
        break;
    case Y_MODULE:
    case Y_SUBMODULE:
        if (ys->ys_filename)
            free(ys->ys_filename);
        break;
    default:
        break;
    }
    if (self){
        free(ys);
        _stats_yang_nr--;
    }
    return 0;
}

/*! Remove child i from parent yp (dont free) 
 *
 * @param[in]  yp   Parent node
 * @param[in]  i    Order of child to remove
 * @retval     yc   returned orphaned yang node
 * @retval     NULL No such node, nothing done
 * @see ys_free Deallocate yang node
 * @see ys_prune_self Prune child itself
 * @note Do not call this in a loop of yang children (unless you know what you are doing)
 */
yang_stmt *
ys_prune(yang_stmt *yp,
         int        i)
{
    size_t     size;
    yang_stmt *yc = NULL;

    if (i >= yp->ys_len)
        goto done;
    yc = yp->ys_stmt[i];
    if (i < yp->ys_len - 1){
        size = (yp->ys_len - i - 1)*sizeof(struct yang_stmt *);
        memmove(&yp->ys_stmt[i],
                &yp->ys_stmt[i+1],
                size);
    }
    yp->ys_len--;
    yp->ys_stmt[yp->ys_len] = NULL;
 done:
    return yc;
}

/*! Remove yang node from parent (dont free)
 *
 * @param[in]  ys   Yang node to remove
 * @retval     0    OK
 * @retval    -1    Error
 * @see ys_prune  if parent and position is know
 * @see ys_free Deallocate yang node
 * @note Do not call this in a loop of yang children (unless you know what you are doing)
 */
int
ys_prune_self(yang_stmt *ys)
{
    int        retval = -1;
    yang_stmt *yp;
    yang_stmt *yc;
    int        i;
    int        inext;

    if ((yp = yang_parent_get(ys)) != NULL){
        i = 0;
        inext = 0;
        /* Find order of ys in child-list */
        while ((yc = yn_iter(yp, &inext)) != NULL) {
            if (ys == yc)
                break;
            i++;
        }
        if (yc != NULL){
            assert(yc == ys);
            ys_prune(yp, i);
        }
    }
    retval = 0;
    // done:
    return retval;
}

/*! Free all yang children tree recursively 
 *
 * @param[in]  ys   Yang node to its children recursively
 */
static int
ys_freechildren(yang_stmt *ys)
{
    yang_stmt *yc;
    int        i;

    for (i=0; i<ys->ys_len; i++){
        if ((yc = ys->ys_stmt[i]) != NULL)
            ys_free(yc);
    }
    ys->ys_len = 0;
    if (ys->ys_stmt){
        free(ys->ys_stmt);
        ys->ys_stmt = NULL;
    }
    return 0;
}

/*! Free a yang statement tree recursively 
 *
 * @param[in]  ys   Yang node to remove and all its children recursively
 * @note does not remove yang node from tree
 * @see ys_prune  Remove from parent
 * @note special case of keeping track of reference counts if yang-spec
 */
int
ys_free(yang_stmt *ys)
{
    ys_freechildren(ys);
    ys_free1(ys, 1);
    return 0;
}

/*! Allocate larger yang statement vector adding empty field last 
 */
static int
yn_realloc(yang_stmt *yn)
{
    yn->ys_len++;

    if ((yn->ys_stmt = realloc(yn->ys_stmt, (yn->ys_len)*sizeof(yang_stmt *))) == 0){
        clixon_err(OE_YANG, errno, "realloc");
        return -1;
    }
    yn->ys_stmt[yn->ys_len - 1] = NULL; /* init field */
    return 0;
}

#ifdef YANG_ORIG_PTR_SKIP
/*! Return 1 if yang stmt should be skipped in derived trees
 *
 * @param[in] keyword YANG keyword
 * @retval    1       Yes, use orig-ptr if exists to fetch original object
 * @retval    0       No, use existing object
 * Comments includes for:
 * - nodes that could not be skipped and the failed test
 * - nodes that have children in turn
 * - nodes that can be refined
 * @note RFC 7950 Sec 7.13.2.  The "refine" Statement can change the following nodes
 * - default values
 * - description
 * - reference
 * - config
 * - mandatory
 * - presence
 * - min/max-elements
 * - if-feature
 */
static int
uses_orig_ptr(enum rfc_6020 keyword)
{
    return
        //        keyword == Y_CONFIG // NO (test_openconfig.sh) + refine
        // keyword == Y_DEFAULT // NO (test_augment.sh) + refine
        keyword == Y_DESCRIPTION // XXX refine
        || keyword == Y_ENUM // children
        || keyword == Y_ERROR_APP_TAG
        || keyword == Y_ERROR_MESSAGE
        || keyword == Y_FRACTION_DIGITS
        // || keyword ==  Y_KEY // NO
        || keyword == Y_LENGTH // children
        || keyword == Y_MANDATORY // XXX refine
        || keyword == Y_MAX_ELEMENTS // XXX refine
        || keyword == Y_MIN_ELEMENTS // XXX refine
        || keyword == Y_MODIFIER
        || keyword == Y_ORDERED_BY
        || keyword == Y_PATH
        || keyword == Y_PATTERN // children
        || keyword == Y_POSITION
        || keyword == Y_PREFIX
        || keyword == Y_PRESENCE // XXX refine
        || keyword == Y_RANGE // children
        || keyword == Y_REQUIRE_INSTANCE
        || keyword == Y_STATUS
        || keyword == Y_UNIQUE
        || keyword == Y_UNITS
        //  || keyword == Y_UNKNOWN // NO (test_snmp_ifmib.sh)
        || keyword == Y_VALUE
        || keyword == Y_YIN_ELEMENT
        || keyword == Y_WHEN // children
        ;
}
#endif /* YANG_ORIG_PTR_SKIP */

/*! Copy single yang statement no children
 *
 * @param[in] ynew  New empty (but created) yang statement (to)
 * @param[in] yold  Old existing yang statement (from)
 * @retval    0     OK
 * @retval   -1     Error
 * @code
 * yang_stmt *new = ys_new(Y_LEAF);
 * if (ys_cp_one(new, old) < 0)
 *    err;
 * @endcode
 * @see ys_cp  for recursive copy
 */
int
ys_cp_one(yang_stmt *ynew,
          yang_stmt *yold)
{
    int     retval = -1;
    cvec   *cvv;
    cg_var *cvn;
    cg_var *cvo;
    size_t  sz;

    sz = sizeof(*yold);
    memcpy(ynew, yold, sz);
    yang_flag_reset(ynew, YANG_FLAG_WHEN); /* Dont inherit WHENs */
    ynew->ys_parent = NULL;
    if (yold->ys_stmt)
        if ((ynew->ys_stmt = calloc(yold->ys_len, sizeof(yang_stmt *))) == NULL){
            clixon_err(OE_YANG, errno, "calloc");
            goto done;
        }
    if (yold->ys_argument)
        if ((ynew->ys_argument = strdup(yold->ys_argument)) == NULL){
            clixon_err(OE_YANG, errno, "strdup");
            goto done;
        }
    if ((cvo = yold->ys_cv) != NULL){ // Note direct access to avoid wrappings
        yang_cv_set(ynew, NULL);
        if ((cvn = cv_dup(cvo)) == NULL){
            clixon_err(OE_YANG, errno, "cv_dup");
            goto done;
        }
        yang_cv_set(ynew, cvn);
    }
    if ((cvv = yold->ys_cvec) != NULL){ // Note direct access to avoid wrappings
        ynew->ys_cvec = NULL; // Note direct access to avoid cvec_free
        if ((cvv = cvec_dup(cvv)) == NULL){
            clixon_err(OE_YANG, errno, "cvec_dup");
            goto done;
        }
        yang_cvec_set(ynew, cvv);
    }
    switch (yold->ys_keyword) {     /* type-specifi union fields */
    case Y_TYPE:
        if (yang_typecache_get(yold)) /* Dont copy type cache, use only original */
            yang_typecache_set(ynew, NULL);
        break;
    default:
        break;
    }
    if (yang_flag_get(yold, YANG_FLAG_MYMODULE) != 0x0)
        yang_mymodule_set(ynew, yang_mymodule_get(yold));
    retval = 0;
 done:
    return retval;
}

/*! Copy yang statement recursively from old to new
 *
 * @param[in] ynew  New empty (but created) yang statement (to)
 * @param[in] yold  Old existing yang statement (from)
 * @retval    0     OK
 * @retval   -1     Error
 * @code
 * yang_stmt *new = ys_new(Y_LEAF);
 * if (ys_cp(new, old) < 0)
 *    err;
 * @endcode
 * @see ys_replace
 */
int
ys_cp(yang_stmt *ynew,
      yang_stmt *yold)
{
    int        retval = -1;
    int        i;
    int        j;
    yang_stmt *ycn; /* new child */
    yang_stmt *yco; /* old child */

    if (ys_cp_one(ynew, yold) < 0)
        goto done;
    for (i=0,j=0; i<yold->ys_len; i++){
        yco = yold->ys_stmt[i];
#ifdef YANG_ORIG_PTR_SKIP
        if (uses_orig_ptr(yang_keyword_get(yco))) {
            ynew->ys_len--;
            continue;
        }
#endif
        if ((ycn = ys_dup(yco)) == NULL)
            goto done;
         ynew->ys_stmt[j++] = ycn;
        ycn->ys_parent = ynew;
    }
    retval = 0;
 done:
    return retval;
}

/*! Create a new yang node and copy the contents recursively from the original.  *
 *
 * @param[in] old  Old existing yang statement (from)
 * @retval    NULL Error
 * @retval    nw   New created yang statement
 * @retval    0     OK
 * @retval   -1     Error
 * This may involve duplicating strings, etc.
 * The new yang tree needs to be freed by ys_free().
 * The parent of new is NULL, it needs to be explicityl inserted somewhere
 */
yang_stmt *
ys_dup(yang_stmt *old)
{
    yang_stmt *nw;

    if ((nw = ys_new(old->ys_keyword)) == NULL)
        return NULL;
    if (ys_cp(nw, old) < 0){
        ys_free(nw);
        return NULL;
    }
    return nw;
}

/*! Replace yold with ynew (insert ynew at the exact place of yold). Keep yold pointer as-is.
 *
 * @param[in] yorig  Existing yang statement
 * @param[in] yfrom  New empty (but created) yang statement
 * @retval    0      OK
 * @retval   -1      Error
 * @code
 * if (ys_replace(new, old) < 0)
 *    err;
 * @endcode
 * @see ys_cp
 * @note yfrom is left in its original state
 */
int
ys_replace(yang_stmt *yorig,
           yang_stmt *yfrom)
{
    int        retval = -1;
    yang_stmt *yp; /* parent */

    yp = yang_parent_get(yorig);
    /* Remove old yangs all children */
    ys_freechildren(yorig);
    ys_free1(yorig, 0); /* Remove all in yold except the actual object */
    if (ys_cp(yorig, yfrom) < 0)
        goto done;
    yorig->ys_parent = yp;
    retval = 0;
 done:
    return retval;
}

/*! Append yang statement as child of a parent yang_statement, last in list
 *
 * @param[in] ys_parent  Add child to this parent
 * @param[in] ys_child   Add this child
 * @retval    0          OK
 * @retval   -1          Error
 * Also add parent to child as up-pointer
 * @see ys_prune
 */
int
yn_insert(yang_stmt *ys_parent,
          yang_stmt *ys_child)
{
    int pos = ys_parent->ys_len;

    if (yn_realloc(ys_parent) < 0)
        return -1;
    ys_parent->ys_stmt[pos] = ys_child;
    ys_child->ys_parent = ys_parent;
    return 0;
}

/*! Variant of yn_insert where parent is not set
 */
int
yn_insert1(yang_stmt *ys_parent,
           yang_stmt *ys_child)
{
    int pos = ys_parent->ys_len;

    if (yn_realloc(ys_parent) < 0)
        return -1;
    ys_parent->ys_stmt[pos] = ys_child;
    return 0;
}

/*! Iterate through all yang statements from a yang node using an integer iterator
 *
 * @param[in] yparent   yang statement whose children are iterated
 * @param[in,out] inext Iterator, or 0 on init
 * @code
 *   yang_stmt *yprev;
 *   int inext = 0;
 *   while ((ynext = yn_iter(yparent, &inext)) != NULL) {
 *     ...ynext...
 *   }
 * @endcode
 */
yang_stmt *
yn_iter(yang_stmt *yparent,
        int       *inext)
{
    yang_stmt *yc = NULL;

    if (yparent == NULL || *inext < 0 || (*inext)>=yparent->ys_len)
        return NULL;
    for (; (*inext)<yparent->ys_len;){
        if ((yc = yparent->ys_stmt[*inext]) == NULL){
            (*inext)++;
            continue;
        }
        (*inext)++;
        break; /* this is next object after previous */
    }
    return yc;
}

/*! Find first child yang_stmt with matching keyword and argument
 *
 * Find child given keyword and argument.
 * Special case: look in imported INPUTs as well (for (sub)modules.
 * This could need an optimized lookup, especially the INPUT case.
 * Most common use for the special case, ie in openconfig, is grouping and identity
 * @param[in]  yn         Yang node, current context node.
 * @param[in]  keyword    if 0 match any keyword. Actual type: enum rfc_6020
 * @param[in]  argument   String compare w argument. if NULL, match any.
 * @retval     ys         Yang statement, if any
 * @see yang_find_datanode
 */
yang_stmt *
yang_find(yang_stmt  *yn,
          int         keyword,
          const char *argument)
{
    yang_stmt *ys = NULL;
    int        i;
    yang_stmt *yret = NULL;
    yang_stmt *yretsub = NULL;
    char      *name;
    yang_stmt *yspec;
    yang_stmt *ym;
#ifdef YANG_ORIG_PTR_SKIP
    yang_stmt *yorig;

    if ((yorig = yang_orig_get(yn)) != NULL) {
        if (uses_orig_ptr(keyword))
            return yang_find(yorig, keyword, argument);
    }
#endif
    for (i=0; i<yn->ys_len; i++){
        ys = yn->ys_stmt[i];
        if (keyword == 0 || ys->ys_keyword == keyword){
            if (argument == NULL ||
                (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)){
                yret = ys;
                break;
            }
        }
        /* Special case: if not match and yang node is module or submodule, extend
         * search to include submodules 
         */
        if (yretsub == NULL &&
            yang_keyword_get(ys) == Y_INCLUDE &&
            keyword != Y_NAMESPACE &&
            (yang_keyword_get(yn) == Y_MODULE ||
             yang_keyword_get(yn) == Y_SUBMODULE)){
            yspec = ys_spec(yn);
            name = yang_argument_get(ys);
            if ((ym = yang_find_module_by_name(yspec, name)) != NULL) {
                yretsub = yang_find(ym, keyword, argument);
            }
        }
    }
    return yret?yret:yretsub;
}

/*! Find child data node with matching argument (container, leaf, list, leaf-list)
 *
 * @param[in]  yn         Yang node, current context node.
 * @param[in]  argument   Argument that child should match with
 * @retval     ymatch     Matching child
 * @retval     NULL       No match or error
 *
 * @see yang_find   Looks for any node
 * @note May deviate from RFC since it explores choice/case not just return it.
 * XXX: differentiate between not found and error
 */
yang_stmt *
yang_find_datanode(yang_stmt *yn,
                   char      *argument)
{
    yang_stmt *ys = NULL;
    yang_stmt *yc = NULL;
    yang_stmt *yspec;
    yang_stmt *ysmatch = NULL;
    char      *name;
    int        inext;
    int        inext2;

    inext = 0;
    while ((ys = yn_iter(yn, &inext)) != NULL){
        if (yang_keyword_get(ys) == Y_CHOICE){ /* Look for its children */
            inext2 = 0;
            while ((yc = yn_iter(ys, &inext2)) != NULL){
                if (yang_keyword_get(yc) == Y_CASE) /* Look for its children */
                    ysmatch = yang_find_datanode(yc, argument);
                else
                    if (yang_datanode(yc)){
                        if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
                            ysmatch = yc;
                    }
                if (ysmatch)
                    goto done; // maybe break?
            }
        } /* Y_CHOICE */
        else if (yang_keyword_get(ys) == Y_INPUT ||
                 yang_keyword_get(ys) == Y_OUTPUT){ /* Look for its children */
            if ((ysmatch = yang_find_datanode(ys, argument)) != NULL)
                break;
        }
        else if (yang_datanode(ys)){
            if (argument == NULL)
                ysmatch = ys;
            else
                if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
                    ysmatch = ys;
            if (ysmatch)
                goto done; // maybe break?
        }
    }
    /* Special case: if not match and yang node is module or submodule, extend
     * search to include submodules */
    if (ysmatch == NULL &&
        (yang_keyword_get(yn) == Y_MODULE ||
         yang_keyword_get(yn) == Y_SUBMODULE)){
        yspec = ys_spec(yn);
        inext = 0;
        while ((ys = yn_iter(yn, &inext)) != NULL){
            if (yang_keyword_get(ys) == Y_INCLUDE){
                name = yang_argument_get(ys);
                yc = yang_find_module_by_name(yspec, name);
                if ((ysmatch = yang_find_datanode(yc, argument)) != NULL)
                    break;
            }
        }
    }
 done:
    return ysmatch;
}

/*! Find child schema node with matching argument (container, leaf, etc)
 *
 * @param[in]  yn         Yang node, current context node.
 * @param[in]  argument   if NULL, match any(first) argument.
 * @note XXX unify code with yang_find_datanode?
 * @see yang_find_datanode
 * @see yang_abs_schema_nodeid  Top level function
 */
yang_stmt *
yang_find_schemanode(yang_stmt *yn,
                     char      *argument)
{
    yang_stmt *ys = NULL;
    yang_stmt *yc = NULL;
    yang_stmt *yspec;
    yang_stmt *ysmatch = NULL;
    char      *name;
    int        i, j;

    for (i=0; i<yn->ys_len; i++){
        ys = yn->ys_stmt[i];
        if (yang_keyword_get(ys) == Y_CHOICE){
            /* First check choice itself */
            if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0){
                ysmatch = ys;
                goto match;
            }
            /* Then look for its children (case) */
            for (j=0; j<ys->ys_len; j++){
                yc = ys->ys_stmt[j];
                if (yang_keyword_get(yc) == Y_CASE) /* Look for its children */
                    ysmatch = yang_find_schemanode(yc, argument);
                else
                    if (yang_schemanode(yc)){
                        if (argument == NULL)
                            ysmatch = yc;
                        else
                            if (yc->ys_argument && strcmp(argument, yc->ys_argument) == 0)
                                ysmatch = yc;
                    }
                if (ysmatch)
                    goto match;
            }
        } /* Y_CHOICE */
        else
            if (yang_schemanode(ys)){
                if (strcmp(argument, "input") == 0 && yang_keyword_get(ys) == Y_INPUT)
                    ysmatch = ys;
                else if (strcmp(argument, "output") == 0 && yang_keyword_get(ys) == Y_OUTPUT)
                    ysmatch = ys;
                else if (argument == NULL)
                    ysmatch = ys;
                else
                    if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0)
                        ysmatch = ys;
                if (ysmatch)
                    goto match;
            }
    }
    /* Special case: if not match and yang node is module or submodule, extend
     * search to include submodules */
    if (ysmatch == NULL &&
        (yang_keyword_get(yn) == Y_MODULE ||
         yang_keyword_get(yn) == Y_SUBMODULE)){
        yspec = ys_spec(yn);
        for (i=0; i<yn->ys_len; i++){
            ys = yn->ys_stmt[i];
            if (yang_keyword_get(ys) == Y_INCLUDE){
                name = yang_argument_get(ys);
                yc = yang_find_module_by_name(yspec, name);
                if ((ysmatch = yang_find_schemanode(yc, argument)) != NULL)
                    break;
            }
        }
    }
 match:
    return ysmatch;
}

/*! Given a yang statement, or module itself, find the prefix associated to this module
 *
 * @param[in]  ys        Yang statement in module tree (or module itself)
 * @retval     prefix    OK: Prefix as char* pointer into yang tree
 * @retval     NULL      No prefix found. This is an error
 * @code
 *   char *myprefix;
 *   myprefix = yang_find_myprefix(ys);
 * @endcode
 */
char *
yang_find_myprefix(yang_stmt *ys)
{
    yang_stmt *ymod = NULL; /* My module */
    yang_stmt *yprefix;
    char      *prefix = NULL;

    /* Not good enough with submodule, must be actual module */
    if (ys_real_module(ys, &ymod) < 0)
        goto done;
    if (ymod == NULL){
        clixon_err(OE_YANG, ENOENT, "Internal error: no module");
        goto done;
    }
    if ((yprefix = yang_find(ymod, Y_PREFIX, NULL)) == NULL){
        clixon_err(OE_YANG, ENOENT, "No prefix found for module %s", yang_argument_get(ymod));
        goto done;
    }
    prefix = yang_argument_get(yprefix);
 done:
    return prefix;
}

/*! Given a yang statement, find the namespace URI associated to this module
 *
 * @param[in]  ys        Yang statement in module tree (or module itself)
 * @retval     ns        Namspace URI as char* pointer into yang tree
 * @retval     NULL      Error: No namespace found. This is an error
 * @code
 * char *myns = yang_find_mynamespace(ys);
 * @endcode
 * @see yang_find_module_by_namespace
 */
char *
yang_find_mynamespace(yang_stmt *ys)
{
    yang_stmt *ymod = NULL; /* My module */
    yang_stmt *ynamespace;
    char      *ns = NULL;

    if (ys_real_module(ys, &ymod) < 0)
        goto done;
    if ((ynamespace = yang_find(ymod, Y_NAMESPACE, NULL)) == NULL){
        clixon_err(OE_YANG, ENOENT, "No namespace found for module %s", yang_argument_get(ymod));
        goto done;
    }
    ns = yang_argument_get(ynamespace);
 done:
    return ns;
}

/*! Given a yang statement and namespace, find local prefix valid in module
 *
 * This is useful if you want to make a "reverse" lookup, you know the
 * (global) namespace of a module, but you do not know the local prefix
 * used to access it in XML.
 * @param[in]  ys        Yang statement in module tree (or module itself)
 * @param[in]  ns        Namespace URI as char* pointer into yang tree
 * @param[out] prefix    Local prefix to access module with (direct pointer)
 * @retval     1         Found
 * @retval     0         Not found
 * @retval    -1         Error
 * @note  prefix NULL is not returned, if own module, then return its prefix
 * @code
 *    char *prefix = NULL;
 *    if ((found = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix)) < 0)
 *      err;
 *    if (found)
 *      // use prefix
 * @endcode
 * @see yang_find_module_by_namespace
 */
int
yang_find_prefix_by_namespace(yang_stmt *ys,
                              char      *ns,
                              char     **prefix)
{
    int        retval = -1;
    yang_stmt *my_ymod; /* My module */
    char      *myns;   /* My ns */
    yang_stmt *yspec;
    yang_stmt *ymod;
    char      *modname = NULL;
    yang_stmt *yimport;
    yang_stmt *yprefix;
    int        inext;

    clixon_debug(CLIXON_DBG_YANG | CLIXON_DBG_DETAIL, "namespace %s", ns);
    if (prefix == NULL){
        clixon_err(OE_YANG, EINVAL, "prefix is NULL");
        goto done;
    }
    /* First check if namespace is my own module */
    myns = yang_find_mynamespace(ys);
    if (strcmp(myns, ns) == 0){
        *prefix = yang_find_myprefix(ys); /* or NULL? */
        goto found;
    }
    /* Next, find namespaces in imported modules */
    yspec = ys_spec(ys);
    if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL)
        goto notfound;
    modname = yang_argument_get(ymod);
    my_ymod = ys_module(ys);
    /* Loop through import statements to find a match with ymod */
    inext = 0;
    while ((yimport = yn_iter(my_ymod, &inext)) != NULL) {
        if (yang_keyword_get(yimport) == Y_IMPORT &&
            strcmp(modname, yang_argument_get(yimport)) == 0){ /* match */
            yprefix = yang_find(yimport, Y_PREFIX, NULL);
            *prefix = yang_argument_get(yprefix);
            goto found;
        }
    }
 notfound:
    retval = 0; /* not found */
 done:
    return retval;
 found:
    assert(*prefix);
    retval = 1;
    goto done;
}

/*! Given a yang statement and local prefix valid in module, find namespace
 *
 * @param[in]  ys        Yang statement in module tree (or module itself)
 * @param[in]  prefix    Local prefix to access module with (direct pointer)
 * @param[out] ns        Namespace URI as char* pointer into yang tree
 * @retval     1         Found
 * @retval     0         Not found
 * @retval    -1         Error
 * @note  prefix NULL is not returned, if own module, then return its prefix
 * @code
 *    char *ns = NULL;
 *    if ((found = yang_find_namespace_by_prefix(ys, "ex", &ns)) < 0)
 *      err;
 *    if (found)
 *      // use ns *    
 * @endcode
 * @see yang_find_module_by_prefix
 */
int
yang_find_namespace_by_prefix(yang_stmt *ys,
                              char      *prefix,
                              char     **ns)
{
    int        retval = -1;
    yang_stmt *ym;

    if (ns == NULL){
        clixon_err(OE_YANG, EINVAL, "ns is NULL");
        goto done;
    }
    if ((ym = yang_find_module_by_prefix(ys, prefix)) == NULL)
        goto notfound;
    if ((*ns = yang_find_mynamespace(ym)) == NULL)
        goto notfound;
    retval = 1; /* found */
 done:
    return retval;
 notfound:
    retval = 0;
    goto done;
}

/*! Return topmost yang root node directly under module/submodule
 *
 * @param[in] ys    Yang statement
 * @retval    ytop  Topmost yang node (can be ys itself)
 * @retval    NULL  ys is spec, module, NULL etc with no reasonable rootnode
 */
yang_stmt *
yang_myroot(yang_stmt *ys)
{
    yang_stmt    *yp;
    enum rfc_6020 kw;

    kw = yang_keyword_get(ys);
    if (ys==NULL || kw==Y_SPEC || kw == Y_MODULE || kw == Y_SUBMODULE)
        return NULL;
    yp = yang_parent_get(ys);
    while((yp = yang_parent_get(ys)) != NULL) {
        kw = yang_keyword_get(yp);
        if (kw == Y_MODULE || kw == Y_SUBMODULE)
            return ys;
        ys = yp;
    }
    return NULL;
}

/*! Given a YANG node, return closest yang case and choice, if any
 *
 * @param[in]  yc      Yang node
 * @param[out] ycase   case parent of yc
 * @param[out] ychoice choice parent of yc (or grandparent)
 * @retval     1       yc has case or choice parent (or both) as returned in ycase/ychoice
 * @retval     0       yc has no parent or parent is neither case or choice
 */
int
yang_choice_case_get(yang_stmt  *yc,
                     yang_stmt **ycase,
                     yang_stmt **ychoice)
{
    yang_stmt *yp;

    if ((yp = yang_parent_get(yc)) == NULL)
        return 0;
    if (yang_keyword_get(yp) == Y_CASE){
        if (ycase)
            *ycase = yp;
        *ychoice = yang_parent_get(yp);
        return 1;
    }
    else if (yang_keyword_get(yp) == Y_CHOICE){
        if (ycase)
            *ycase = NULL;
        *ychoice = yp;
        return 1;
    }
    return 0;
}

/*! If a given yang stmt has a choice/case as parent, return the choice statement
 *
 *
 * @see yang_choice_case_get
 */
yang_stmt *
yang_choice(yang_stmt *y)
{
    yang_stmt *yp = NULL;

    yang_choice_case_get(y, NULL, &yp);
    return yp;
}

/*! Find matching y in yp:s children, "yang order" of y when y is choice
 *
 * @param[in]  yp     Choice node
 * @param[in]  y      Yang datanode to find
 * @param[out] index  Index of y in yp:s list of children
 * @retval     1      found
 * @retval     0      not found (must be datanode)
 * @see order1 the main function
 * There are two distinct cases, either (1) the choice has case statements, or
 * (2) it uses shortcut mode without case statements.
 * In (1) we need to count how many sub-statements and keep a max
 * In (2) we increment with only 1.
 */
static int
yang_order1_choice(yang_stmt *yp,
                   yang_stmt *y,
                   int       *index)
{
    yang_stmt  *ys;
    yang_stmt  *yc;
    int         i;
    int         j;
    int         max=0;
    int         index0;

    index0 = *index;
    for (i=0; i<yp->ys_len; i++){ /* Loop through choice */
        ys = yp->ys_stmt[i];
        if (ys->ys_keyword == Y_CASE){ /* Loop through case */
            *index = index0;
            for (j=0; j<ys->ys_len; j++){
                yc = ys->ys_stmt[j];
                if (yc->ys_keyword == Y_CHOICE){
                    if (yang_order1_choice(yc, y, index) == 1) /* If one of the choices is "y" */
                        return 1;
                }
                else {
                    if (yang_datanode(yc) && yc == y){
                        //                      *index = index0 + j;
                        return 1;
                    }
                    (*index)++;
                }
            }
            if (*index-index0 > max)
                max = *index-index0;
        }
        else {
            max = 1;   /* Shortcut, no case */
            if (yang_datanode(ys) && ys == y)
                return 1;
        }
    }
    *index += max;
    return 0;
}

/*! Find matching y in yp:s children, return "yang order" of y or -1 if not found
 *
 * @param[in]  yp     Parent
 * @param[in]  y      Yang datanode to find
 * @param[out] index  Index of y in yp:s list of children
 * @retval     0      OK: found
 * @retval    -1      Error: not found (must be datanode)
 */
static int
yang_order1(yang_stmt *yp,
            yang_stmt *y,
            int       *index)
{
    int         retval = -1;
    yang_stmt  *ys;
    int         i;

    for (i=0; i<yp->ys_len; i++){
        ys = yp->ys_stmt[i];
        if (ys->ys_keyword == Y_CHOICE){
            if (yang_order1_choice(ys, y, index) == 1) /* If one of the choices is "y" */
                break;
        }
        else {
            if (!yang_datanode(ys) &&
                yang_keyword_get(ys) != Y_ACTION) /* action is special case */
                continue;
            if (ys == y)
                break;
            (*index)++;
        }
    }
    if (i < yp->ys_len) /* break -> found */
        retval = 0;
    else
        assert(0); // XXX
    return retval;
}

/*! Return order of yang statement y in parents child vector
 *
 * @param[in]  y      Find position of this data-node
 * @param[out] index  Index of y in yp:s list of children
 * @retval   >=0      Order of child with specified argument
 * @retval    -1      No spec, y is NULL, which applies to eg attributes and are placed first
 * @retval    -2      Error: Not found
 * @note special handling if y is child of (sub)module
 */
int
yang_order(yang_stmt *y)
{
    int         retval = -2;
    yang_stmt  *yp;
    yang_stmt  *ypp;
    yang_stmt  *ym;
    int         i;
    int         j=0;
    int         tot = 0;

    if (y == NULL){
        retval = -1;
        goto done;
    }
    /* Some special handling if yp is choice (or case)
     * if so, the real parent (from an xml point of view) is the parents
     * parent.
     */
    yp = yang_parent_get(y);
    while (yang_keyword_get(yp) == Y_CASE || yang_keyword_get(yp) == Y_CHOICE)
        yp = yp->ys_parent;

    /* XML nodes with yang specs that are children of modules are special - 
     * In clixon, they are seen as an "implicit" container where the XML can come from different
     * modules. The order must therefore be global among yang top-symbols to be unique.
     * Example: <x xmlns="foo"/><y xmlns="bar"/>
     * The order of x and y cannot be compared within a single yang module since they belong to different
     */
    if (yang_keyword_get(yp) == Y_MODULE || yang_keyword_get(yp) == Y_SUBMODULE){
        ypp = yang_parent_get(yp); /* yang spec */
        for (i=0; i<ypp->ys_len; i++){ /* iterate through other modules */
            ym = ypp->ys_stmt[i];
            if (yp == ym)
                break;
            tot += ym->ys_len;
        }
    }
    if (yang_order1(yp, y, &j) < 0){
        clixon_err(OE_YANG, 0, "YANG node %s not ordered: not found", yang_argument_get(y));
        goto done;
    }
    retval = tot + j;
 done:
    return retval;
}

/*! Map from YANG keywords ints to strings
 *
 * @param[in] int  Integer representation of YANG keywords
 * @retval    str  String representation of YANG keywords 
 */
char *
yang_key2str(int keyword)
{
    return (char*)clicon_int2str(ykmap, keyword);
}

/*! Map from yang keyword strings to ints
 *
 * @param[in] str  String representation of YANG keywords
 * @retval    int  Integer representation of YANG keywords
 */
int
yang_str2key(char *str)
{
    return clicon_str2int(ykmap, str);
}

/*! Find top data node among all modules by namespace in xml tree
 *
 * @param[in]  yspec    Yang specification
 * @param[in]  xt       XML node
 * @param[out] ymod     Yang module (NULL if not found)
 * @retval     0        OK
 * @retval    -1        Error
 * @note works for xml namespaces (xmlns / xmlns:ns)
 * Note that xt xml symbol may belong to submodule of ymod
 */
int
ys_module_by_xml(yang_stmt  *yspec,
                 cxobj      *xt,
                 yang_stmt **ymodp)
{
    int        retval = -1;
    yang_stmt *ym = NULL; /* module */
    char      *prefix = NULL;
    char      *ns = NULL; /* namespace URI */

    if (ymodp)
        *ymodp = NULL;
    prefix = xml_prefix(xt);
    if (xml2ns(xt, prefix, &ns) < 0) /* prefix may be NULL */
        goto done;
    /* No namespace found, give up */
    if (ns == NULL)
        goto ok;
    /* We got the namespace, now get the module */
    ym = yang_find_module_by_namespace(yspec, ns);
    /* Set result param */
    if (ymodp && ym)
        *ymodp = ym;
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Find the top module or sub-module given a statement from within a yang tree
 *
 * Ultimate top is yang spec, dont return that
 * The routine recursively finds ancestors.
 * @param[in] ys    Any yang statement in a yang tree
 * @retval    ymod  The top module or sub-module 
 * @see ys_spec
 * @see ys_real_module find the submodule's belongs-to module
 * @note For an augmented node, the original module is returned
 */
yang_stmt *
ys_module(yang_stmt *ys)
{
    yang_stmt *yn;

    if (ys==NULL || ys->ys_keyword==Y_SPEC)
        return NULL;
    if (ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE)
        return ys;
    while (ys != NULL &&
           ys->ys_keyword != Y_MODULE &&
           ys->ys_keyword != Y_SUBMODULE){
        if (yang_mymodule_get(ys)){ /* shortcut due to augment */
            ys = yang_mymodule_get(ys);
            break;
        }
        yn = ys->ys_parent;
        /* Some extra stuff to ensure ys is a stmt */
        if (yn && yn->ys_keyword == Y_SPEC)
            yn = NULL;
        ys = (yang_stmt*)yn;
    }
    /* Here it is either NULL or is a typedef-kind yang-stmt */
    return ys;
}

/*! Find real top module given a statement in a yang tree
 *
 * With "real" top module means that if sub-module is the top-node,
 * the module that the sub-module belongs-to is found recursively
 * @param[in]  ys    Any yang statement in a yang tree
 * @param[out] ymod  The top module or sub-module 
 * @retval     0     OK, returned module in ymod
 * @retval    -1     YANG validation error: all yang statements should have a "real" module
 * @see ys_module
 * @note For an augmented node, the original module is returned
 */
int
ys_real_module(yang_stmt  *ys,
               yang_stmt **ymod)
{
    int        retval = -1;
    yang_stmt *ym = NULL;
    yang_stmt *ysubm;
    yang_stmt *yb;
    char      *name;
    yang_stmt *yspec;

    if (ymod == NULL){
        clixon_err(OE_YANG, EINVAL, "ymod is NULL");
        goto done;
    }
    if ((ym = ys_module(ys)) != NULL){
        yspec = ys_spec(ym);
        while (ym && yang_keyword_get(ym) == Y_SUBMODULE){
            if ((yb = yang_find(ym, Y_BELONGS_TO, NULL)) == NULL){
                clixon_err(OE_YANG, ENOENT, "No belongs-to statement of submodule %s", yang_argument_get(ym)); /* shouldnt happen */
                goto done;
            }
            if ((name = yang_argument_get(yb)) == NULL){
                clixon_err(OE_YANG, ENOENT, "Belongs-to statement of submodule %s has no argument", yang_argument_get(ym)); /* shouldnt happen */
                goto done;
            }
            if ((ysubm = yang_find_module_by_name(yspec, name)) == NULL){
                clixon_err(OE_YANG, ENOENT, "submodule %s references non-existent module %s in its belongs-to statement",
                           yang_argument_get(ym), name);
                goto done;
            }
            ym = ysubm;
        }
    }
    *ymod = ym;
    retval = 0;
 done:
    return retval;
}

/*! Find top of tree, the yang specification from within the tree
 *
 * @param[in] ys    Any yang statement in a yang tree
 * @retval    yspec The top yang specification
 * @see  ys_module
 * @see  yang_augment_node where shortcut is set for augment
 * @see  yang_myroot for first node under (sub)module
 */
yang_stmt *
ys_spec(yang_stmt *ys)
{
    yang_stmt *yn;

    while (ys != NULL && ys->ys_keyword != Y_SPEC){
        yn = ys->ys_parent;
        ys = (yang_stmt*)yn;
    }
    /* Here it is either NULL or is a typedef-kind yang-stmt */
    return ys;
}

/*! Find top of tree, the yang specification from within the tree
 *
 * @param[in] ys    Any yang statement in a yang tree
 * @retval    yspec The top yang specification
 * @see  ys_module
 * @see  yang_augment_node where shortcut is set for augment
 * @see  yang_myroot for first node under (sub)module
 */
yang_stmt *
ys_mounts(yang_stmt *ys)
{
    yang_stmt *yn;

    while (ys != NULL && ys->ys_keyword != Y_MOUNTS){
        yn = ys->ys_parent;
        ys = (yang_stmt*)yn;
    }
    /* Here it is either NULL or is a typedef-kind yang-stmt */
    return ys;
}

/*! String is quoted if it contains space or tab, needs double ''
 */
static int inline
quotedstring(char *s)
{
    int len = strlen(s);
    int i;

    for (i=0; i<len; i++)
        if (isblank(s[i]))
            break;
    return i < len;
}

/*! Print yang specification to file
 *
 * @param[in]  f     File to print to.
 * @param[in]  yn    Yang node to print
 * @param[in]  fn    Callback to make print function
 * @retval     0         OK
 * @retval    -1         Error
 * @see yang_print_cbuf
 */
int
yang_print_cb(FILE             *f,
              yang_stmt        *yn,
              clicon_output_cb *fn)
{
    int        retval = -1;
    cbuf      *cb = NULL;

    if ((cb = cbuf_new()) == NULL){
        clixon_err(OE_YANG, errno, "cbuf_new");
        goto done;
    }
    if (yang_print_cbuf(cb, yn, 0, 1) < 0)
        goto done;
    (*fn)(f, "%s", cbuf_get(cb));
    if (cb)
        cbuf_free(cb);
    retval = 0;
 done:
    return retval;
}

/*! Print yang specification to file
 *
 * @param[in]  f         File to print to.
 * @param[in]  yn        Yang node to print
 * @see yang_print_cbuf
 */
int
yang_print(FILE      *f,
           yang_stmt *yn)
{
    return yang_print_cb(f, yn, fprintf);
}

/*! Print yang top-level modules only
 *
 * @param[in]  f         File to print to.
 * @param[in]  yspec     Yang spec to print
 * @see yang_print_cbuf
 */
int
yang_spec_print(FILE      *f,
                yang_stmt *yspec)
{
    yang_stmt *ym;
    yang_stmt *yrev;
    int        inext;

    if (yspec == NULL || yang_keyword_get(yspec) != Y_SPEC){
        clixon_err(OE_YANG, EINVAL, "yspec is not of type Y_SPEC");
        return -1;
    }
    inext = 0;
    while ((ym = yn_iter(yspec, &inext)) != NULL) {
        fprintf(f, "%s", yang_key2str(ym->ys_keyword));
        fprintf(f, " %s", yang_argument_get(ym));
        if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL){
            fprintf(f, "@%s", yang_argument_get(yrev));
        }
        fprintf(f, ".yang");
        fprintf(f, "\n");
    }
    return 0;
}

/*! Print yang top-level specs
 *
 * @param[in]  f         File to print to.
 * @param[in]  ymounts        Yang mounts to print
 */
int
yang_mounts_print(FILE      *f,
                  yang_stmt *ymounts)
{
    yang_stmt *yspec;
    int        inext;
    cg_var    *cv;
    cvec      *cvv;

    if (ymounts == NULL || yang_keyword_get(ymounts) != Y_MOUNTS){
        clixon_err(OE_YANG, EINVAL, "yspec is not of type Y_MOUNTS");
        return -1;
    }
    inext = 0;
    while ((yspec = yn_iter(ymounts, &inext)) != NULL) {
        fprintf(f, " %s\n", yang_argument_get(yspec));
        if ((cv = yang_cv_get(yspec)) != NULL){
            cv_print(f, yang_cv_get(yspec));
            fprintf(f, "\n");
        }
        if ((cvv = yang_cvec_get(yspec)) != NULL){
            cvec_print(f, cvv);
            fprintf(f, "\n");
        }
    }
    return 0;
}

/* Log/debug info about top-level (sub)modules no recursion
 * @param[in]  yspec     Yang spec
 * @param[in]  dbglevel  Debug level
 */
int
yang_spec_dump(yang_stmt *yspec,
               int        dbglevel)
{
    int        retval = -1;
    yang_stmt *ym;
    yang_stmt *yrev;
    cbuf      *cb = NULL;
    int        inext;

    if ((cb = cbuf_new()) ==NULL){
        clixon_err(OE_YANG, errno, "cbuf_new");
        goto done;
    }
    inext = 0;
    while ((ym = yn_iter(yspec, &inext)) != NULL) {
        cprintf(cb, "%s", yang_key2str(ym->ys_keyword));
        cprintf(cb, " %s", yang_argument_get(ym));
        if ((yrev = yang_find(ym, Y_REVISION, NULL)) != NULL){
            cprintf(cb, "@%u", cv_uint32_get(yang_cv_get(yrev)));
        }
        cprintf(cb, ".yang");
        clixon_debug(dbglevel, "%s", cbuf_get(cb));
        cbuf_reset(cb);
    }
    retval = 0;
 done:
    if (cb)
        cbuf_free(cb);
    return retval;
}

/*! Print yang specification to cligen buf
 *
 * @param[in]  cb        Cligen buffer. This is where the pretty print is.
 * @param[in]  yn        Yang node to print
 * @param[in]  marginal  Tab indentation, mainly for recursion.
 * @param[in]  pretty    Pretty-print output
 * @retval     0         OK
 * @retval    -1         Error
 * @code
 *  cbuf *cb = cbuf_new();
 *  yang_print_cbuf(cb, yn, 0, 1);
 *  // output is in cbuf_buf(cb);
 *  cbuf_free(cb);
 * @endcode
 */
int
yang_print_cbuf(cbuf      *cb,
                yang_stmt *yn,
                int        marginal,
                int        pretty)
{
    int           retval = -1;
    yang_stmt    *ys;
    enum rfc_6020 keyw;
    char         *arg;
    int           inext;

    if (yn == NULL || cb == NULL){
        clixon_err(OE_YANG, EINVAL, "cb or yn is NULL");
        goto done;
    }
    keyw = yang_keyword_get(yn);
    if (keyw == Y_UNKNOWN){ /* dont print unknown - proxy for extension*/
        if (pretty)
            cprintf(cb, "%*s", marginal-1, "");
    }
    else{
        if (pretty)
            cprintf(cb, "%*s%s", marginal, "", yang_key2str(keyw));
        else
            cprintf(cb, "%s", yang_key2str(keyw));
    }
    arg = yang_argument_get(yn);
    if (arg){
        if (quotedstring(arg))
            cprintf(cb, " \"%s\"", arg);
        else
            cprintf(cb, " %s", arg);
    }
    if (yang_len_get(yn)){
        cprintf(cb, " {");
        if (pretty)
            cprintf(cb, "\n");
        inext = 0;
        while ((ys = yn_iter(yn, &inext)) != NULL) {
            if (yang_print_cbuf(cb, ys, marginal + PRETTYPRINT_INDENT, pretty) < 0)
                goto done;
        }
        if (pretty)
            cprintf(cb, "%*s%s\n", marginal, "", "}");
        else
            cprintf(cb, "}");
    }
    else{
        cprintf(cb, ";");
        if (pretty)
            cprintf(cb, "\n");
    }
    retval = 0;
 done:
    return retval;
}

/*! Yang deviation/deviate 
 *
 * Identify deviation target node, and go through all its deviate statements.
 * Features/if-feature must have run before
 * @param[in] ys    The yang deviation to populate.
 * @param[in] h     Clixon handle
 * @retval    0     OK
 * @retval   -1     Error
 * @see RFC 7950 5.6.3 and 7.20.3
 */
int
yang_deviation(yang_stmt *ys,
               void      *arg)

{
    int           retval = -1;
    char         *nodeid;
    yang_stmt    *ytarget = NULL;
    yang_stmt    *yd;
    yang_stmt    *yc;
    yang_stmt    *yc1;
    yang_stmt    *ytc;
    char         *devop;
    clixon_handle h = (clixon_handle)arg;
    enum rfc_6020 kw;
    int           min;
    int           max;
    int           inext0;
    int           inext;

    if (yang_keyword_get(ys) != Y_DEVIATION)
        goto ok;
    /* Absolute schema node identifier identifying target node */
    if ((nodeid = yang_argument_get(ys)) == NULL){
        clixon_err(OE_YANG, EINVAL, "No argument to deviation");
        goto done;
    }
    /* Get target node */
    if (yang_abs_schema_nodeid(ys, nodeid, &ytarget) < 0)
        goto done;
    if (ytarget == NULL){
        clixon_log(h, LOG_WARNING, "deviation %s: target not found", nodeid);
        goto ok;
        /* The RFC does not explicitly say the target node MUST exist 
           clixon_err(OE_YANG, 0, "schemanode sanity check of %s", nodeid);
           goto done;
        */
    }
    /* Go through deviates of deviation */
    inext0 = 0;
    while ((yd = yn_iter(ys, &inext0)) != NULL) {
        /* description / if-feature / reference */
        if (yang_keyword_get(yd) != Y_DEVIATE)
            continue;
        devop = yang_argument_get(yd);
        if (strcmp(devop, "not-supported") == 0){
            if (ys_prune_self(ytarget) < 0)
                goto done;
            if (ys_free(ytarget) < 0)
                goto done;
            goto ok; /* Target node removed, no other deviates possible */
        }
        else if (strcmp(devop, "add") == 0){
            inext = 0;
            while ((yc = yn_iter(yd, &inext)) != NULL) {
                /* If a property can only appear once, the property MUST NOT exist in the target node. */
                kw = yang_keyword_get(yc);
                if (yang_find(ytarget, kw, NULL) != NULL){
                    if (yang_cardinality_interval(h,
                                                  yang_keyword_get(ytarget),
                                                  kw,
                                                  &min,
                                                  &max) < 0)
                        goto done;
                    if (max == 1){
                        clixon_err(OE_YANG, 0, "deviation %s: \"%s %s\" added but node already exist in target %s",
                                   nodeid,
                                   yang_key2str(kw), yang_argument_get(yc),
                                   yang_argument_get(ytarget));
                        goto done;
                    }
                }
                /* Make a copy of deviate child and insert. */
                if ((yc1 = ys_dup(yc)) == NULL)
                    goto done;
                /* Special case: resolve types in temporary old deviation context */
                if (yn_insert(yd, yc1) < 0)
                    goto done;
                if (yang_apply(yc1, Y_TYPE, ys_resolve_type, -1, h) < 0)
                    goto done;
                if (ys_prune_self(yc1) < 0)
                    goto done;
                /* Mark all as refined */
                if (yang_apply(yc1, -1, (yang_applyfn_t*)yang_flag_set, -1, (void*)YANG_FLAG_REFINE) < 0)
                    goto done;
                if (yn_insert(ytarget, yc1) < 0)
                    goto done;
            }
        }
        else if (strcmp(devop, "replace") == 0){
            inext = 0;
            while ((yc = yn_iter(yd, &inext)) != NULL) {
                /* The properties to replace MUST exist in the target node.*/
                kw = yang_keyword_get(yc);
                ytc = yang_find(ytarget, kw, NULL);
                switch (kw){
                case Y_CONFIG: /* special case: implicit default is config true */
                    break;
                default:
                    if (ytc == NULL){
                        clixon_err(OE_YANG, 0, "deviation %s: \"%s %s\" replaced but node does not exist in target %s",
                                   nodeid,
                                   yang_key2str(kw), yang_argument_get(yc),
                                   yang_argument_get(ytarget));
                        goto done;
                    }
                    break;
                }
                if (ytc){
                    /* Remove old */
                    if (ys_prune_self(ytc) < 0)
                        goto done;
                    if (ys_free(ytc) < 0)
                        goto done;
                }
                /* Make a copy of deviate child and insert. */
                if ((yc1 = ys_dup(yc)) == NULL)
                    goto done;
                /* Special case: resolve types in temporary old deviation context */
                if (yn_insert(yd, yc1) < 0)
                    goto done;
                if (yang_apply(yc1, Y_TYPE, ys_resolve_type, -1, h) < 0)
                    goto done;
                if (ys_prune_self(yc1) < 0)
                    goto done;
                /* Mark all as refined */
                if (yang_apply(yc1, -1, (yang_applyfn_t*)yang_flag_set, -1, (void*)YANG_FLAG_REFINE) < 0)
                    goto done;
                if (yn_insert(ytarget, yc1) < 0)
                    goto done;
            }
        }
        else if (strcmp(devop, "delete") == 0){
            inext = 0;
            while ((yc = yn_iter(yd, &inext)) != NULL) {
                /* The substatement's keyword MUST match a corresponding keyword in the target node, and the
                 * argument's string MUST be equal to the corresponding keyword's argument string in the 
                 * target node. */
                kw = yang_keyword_get(yc);
                if ((ytc = yang_find(ytarget, kw, NULL)) == NULL){
                    clixon_err(OE_YANG, 0, "deviation %s: \"%s %s\" replaced but node does not exist in target %s",
                               nodeid,
                               yang_key2str(kw), yang_argument_get(yc),
                               yang_argument_get(ytarget));
                    goto done;
                }
                if (ys_prune_self(ytc) < 0)
                    goto done;
                if (ys_free(ytc) < 0)
                    goto done;
            }
        }
        else{ /* Shouldnt happen, lex/yacc takes it */
            clixon_err(OE_YANG, EINVAL, "%s: invalid deviate operator", devop);
            goto done;
        }
    }
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Populate yang leafs after parsing. Create cv and fill it in.
 *
 * Populate leaf in 2nd round of yang parsing, now that context is complete:
 * 1. Find type specification and set cv type accordingly 
 * 2. Create the CV using cvtype and name it 
 * 3. Check if default value. Here we parse the string in the default-stmt and add it to leafs cv.
 * 4. Check if leaf is part of list, if key exists mark leaf as key/unique 
 * XXX: extend type search
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement to populate.
 * @retval    0    OK
 * @retval   -1    Error with clixon_err called
 */
static int
ys_populate_leaf(clixon_handle h,
                 yang_stmt    *ys)
{
    int             retval = -1;
    cg_var         *cv = NULL;
    yang_stmt      *yparent;
    yang_stmt      *ydef;
    enum cv_type    cvtype = CGV_ERR;
    int             cvret;
    int             ret;
    char           *reason = NULL;
    yang_stmt      *yrestype = NULL;  /* resolved type */
    char           *restype;  /* resolved type */
    char           *origtype=NULL;   /* original type */
    uint8_t         fraction_digits = 0;
    int             options = 0x0;
    yang_stmt      *ytypedef; /* where type is define */

    yparent = ys->ys_parent;     /* Find parent: list/container */
    /* 1. Find type specification and set cv type accordingly */
    if (yang_type_get(ys, &origtype, &yrestype, &options, NULL, NULL, NULL, &fraction_digits) < 0)
        goto done;
    restype = yrestype?yang_argument_get(yrestype):NULL;
    if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
        goto done;
    /* 2. Create the CV using cvtype and name it */
    if ((cv = cv_new(cvtype)) == NULL){
        clixon_err(OE_YANG, errno, "cv_new");
        goto done;
    }
    if (options & YANG_OPTIONS_FRACTION_DIGITS && cvtype == CGV_DEC64) /* XXX: Seems misplaced? / too specific */
        cv_dec64_n_set(cv, fraction_digits);

    if (cv_name_set(cv, yang_argument_get(ys)) == NULL){
        clixon_err(OE_YANG, errno, "cv_name_set");
        goto done;
    }
    /* get parent of where type is defined, can be original object */
    ytypedef = yrestype?yang_parent_get(yrestype):ys;

    /* 3. Check if default value. Here we parse the string in the default-stmt
     * and add it to the leafs cv.
     * 3a) First check local default
     */
    if ((ydef = yang_find(ys, Y_DEFAULT, NULL)) != NULL){
        if ((cvret = cv_parse1(yang_argument_get(ydef), cv, &reason)) < 0){ /* error */
            clixon_err(OE_YANG, errno, "parsing cv");
            goto done;
        }
        if (cvret == 0){ /* parsing failed */
            clixon_err(OE_YANG, errno, "Parsing CV: %s", reason);
            free(reason);
            goto done;
        }
    }
    /* 2. then check typedef default */
    else if (ytypedef != ys &&
             (ydef = yang_find(ytypedef, Y_DEFAULT, NULL)) != NULL) {
        if ((cvret = cv_parse1(yang_argument_get(ydef), cv, &reason)) < 0){ /* error */
            clixon_err(OE_YANG, errno, "parsing cv");
            goto done;
        }
        if (cvret == 0){ /* parsing failed */
            clixon_err(OE_YANG, errno, "Parsing CV: %s", reason);
            free(reason);
            goto done;
        }
    }
    else{
        /* 3b. If not default value, indicate empty cv. */
        cv_flag_set(cv, V_UNSET); /* no value (no default) */
    }
    /* 4. Check if leaf is part of list, if key exists mark leaf as key/unique */
    if (yparent && yparent->ys_keyword == Y_LIST){
        if ((ret = yang_key_match(yparent, yang_argument_get(ys), NULL)) < 0)
            goto done;
    }
    yang_cv_set(ys, cv);
    retval = 0;
  done:
    if (origtype)
        free(origtype);
    if (cv && retval < 0)
        cv_free(cv);
    return retval;
}

/*! Populate list yang statement
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (type) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 */
static int
ys_populate_list(clixon_handle h,
                 yang_stmt    *ys)
{
    yang_stmt *ykey;
    cvec      *cvv;

    if ((ykey = yang_find(ys, Y_KEY, NULL)) == NULL)
        return 0;
    if ((cvv = yang_arg2cvec(ykey, " ")) == NULL)
        return -1;
    yang_cvec_set(ys, cvv);
    return 0;
}

/*! Set range or length boundary for built-in yang types
 *
 * Help functions to range and length statements 
 * @retval     0       OK
 * @retval    -1       Error
 */
static int
bound_add(yang_stmt   *ys,
          enum cv_type cvtype,
          char        *name,
          char        *val,
          uint8_t      fraction_digits)
{
    int     retval = -1;
    cg_var *cv;
    char   *reason = NULL;
    int     ret = 1;

    if (ys == NULL){
        clixon_err(OE_YANG, EINVAL, "ys is NULL");
        goto done;
    }
    if ((cv = yang_cvec_add(ys, cvtype, name)) == NULL)
        goto done;
    if (cvtype == CGV_DEC64)
        cv_dec64_n_set(cv, fraction_digits);
    if (strcmp(val, "min") == 0)
        cv_min_set(cv);
    else if (strcmp(val, "max") == 0)
        cv_max_set(cv);
    else if ((ret = cv_parse1(val, cv, &reason)) < 0){
        clixon_err(OE_YANG, errno, "cv_parse1");
        goto done;
    }
    if (ret == 0){ /* parsing failed */
        clixon_err(OE_YANG, errno, "range statement %s: %s", val, reason);
        free(reason);
        goto done;
    }
    retval = 0;
 done:
    return retval;
}

/*! Common range length parsing of "x .. y | z..w " statements
 *
 * @retval     0       OK
 * @retval    -1       Error
 */
static int
range_parse(yang_stmt   *ys,
            enum cv_type cvtype,
            uint8_t      fraction_digits)
{
    int    retval = -1;
    char **vec = NULL;
    int    nvec;
    int    i;
    char  *v;
    char  *v2;

    if ((vec = clicon_strsep(yang_argument_get(ys), "|", &nvec)) == NULL)
        goto done;
    for (i=0; i<nvec; i++){
        v = vec[i];
        if ((v2 = strstr(v, "..")) != NULL){
            *v2 = '\0';
            v2 += 2;
            v2 = clixon_trim(v2);           /* trim blanks */
        }
        v = clixon_trim(v);     /* trim blanks */
        if (bound_add(ys, cvtype, "range_min", v, fraction_digits) < 0)
            goto done;
        if (v2)
            if (bound_add(ys, cvtype, "range_max", v2, fraction_digits) < 0)
                goto done;
    }
    retval = 0;
  done:
    if (vec)
        free(vec);
    return retval;
}

/*! Populate string built-in range statement
 *
 * Create cvec variables "range_min" and "range_max". Assume parent is type.
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (range) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 * Actually: bound[..bound] (| bound[..bound])*  
 *   where bound is integer, decimal or keywords 'min' or 'max. 
 * RFC 7950 9.2.4:
 *  A range consists of an explicit value, or a lower-inclusive bound,
 *  two consecutive dots "..", and an upper-inclusive bound.  Multiple
 *  values or ranges can be given, separated by "|".  If multiple values
 *  or ranges are given, they all MUST be disjoint and MUST be in
 *  ascending order
 */
static int
ys_populate_range(clixon_handle h,
                  yang_stmt    *ys)
{
    int             retval = -1;
    yang_stmt      *yparent;         /* type */
    char           *origtype = NULL; /* orig type */
    yang_stmt      *yrestype;        /* resolved type */
    char           *restype;         /* resolved type */
    int             options = 0x0;
    uint8_t         fraction_digits;
    enum cv_type    cvtype = CGV_ERR;

    yparent = ys->ys_parent;     /* Find parent: type */
    if (yparent->ys_keyword != Y_TYPE){
        clixon_err(OE_YANG, 0, "parent should be type");
        goto done;
    }
    if (yang_type_resolve(ys, ys, (yang_stmt*)yparent, &yrestype,
                          &options, NULL, NULL, NULL, &fraction_digits) < 0)
        goto done;
    if (yrestype == NULL){
        clixon_err(OE_YANG, 0, "result-type should not be NULL");
        goto done;
    }
    restype = yrestype?yang_argument_get(yrestype):NULL;
    if (nodeid_split(yang_argument_get(yparent), NULL, &origtype) < 0)
         goto done;
    /* This handles non-resolved also */
    if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
        goto done;
    if (!cv_isint(cvtype) && cvtype != CGV_DEC64){
        clixon_err(OE_YANG, 0, "The range substatement only applies to int types, not to type: %s", origtype);
        goto done;
    }
    if (range_parse(ys, cvtype, fraction_digits) < 0)
        goto done;
    retval = 0;
  done:
    if (origtype)
        free(origtype);
    return retval;
}

/*! Populate integer built-in length statement
 *
 * Create cvec variables "range_min" and "range_max". Assume parent is type.
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (length) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 *
 * Actually: len[..len] (| len[..len])*  
 *   len is unsigned integer or keywords 'min' or 'max'
 *
 * From RFC 7950 Sec 9.4.4:
 *  A length range consists of an explicit value, or a lower bound, two
 *  consecutive dots "..", and an upper bound.  Multiple values or ranges
 *  can be given, separated by "|".  Length-restricting values MUST NOT
 *  be negative.  If multiple values or ranges are given, they all MUST
 *  be disjoint and MUST be in ascending order.
 */
static int
ys_populate_length(clixon_handle h,
                   yang_stmt    *ys)
{
    int             retval = -1;
    yang_stmt      *yparent;        /* type */
    enum cv_type    cvtype = CGV_ERR;

    yparent = ys->ys_parent;     /* Find parent: type */
    if (yparent->ys_keyword != Y_TYPE){
        clixon_err(OE_YANG, 0, "parent should be type");
        goto done;
    }
    cvtype = CGV_UINT64;
    if (range_parse(ys, cvtype, 0) < 0)
        goto done;
    retval = 0;
  done:
    return retval;
}

/*! Assign enum values
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (type) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 */
static int
ys_populate_type_enum(clixon_handle h,
                      yang_stmt    *ys)
{
    int retval = -1;

    yang_stmt *yenum = NULL;
    yang_stmt *yval;
    char      *valstr;
    int        v;
    int        i = 0;
    cg_var    *cv = NULL;
    int        inext;

    inext = 0;
    while ((yenum = yn_iter(ys, &inext)) != NULL) {
        if ((cv = cv_new(CGV_INT32)) == NULL){
            clixon_err(OE_YANG, errno, "cv_new");
            goto done;
        }
        if ((yval = yang_find(yenum, Y_VALUE, NULL)) != NULL){
            valstr = yang_argument_get(yval);
            if (parse_int32(valstr, &v, NULL) < 0)
                goto done;
            if (v > i)
                i = v;
        }
        cv_int32_set(cv, i++);
        yang_cv_set(yenum, cv);
    }
    retval = 0;
 done:
    return retval;
}

/*! Sanity check yang type statement
 *
 * XXX: Replace with generic parent/child type-check
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (type) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 */
static int
ys_populate_type(clixon_handle h,
                 yang_stmt    *ys)

{
    int             retval = -1;
    yang_stmt      *ybase;

    if (strcmp(yang_argument_get(ys), "decimal64") == 0){
        if (yang_find(ys, Y_FRACTION_DIGITS, NULL) == NULL){
            clixon_err(OE_YANG, 0, "decimal64 type requires fraction-digits sub-statement");
            goto done;
        }
    }
    else if (strcmp(yang_argument_get(ys), "identityref") == 0){
        if ((ybase = yang_find(ys, Y_BASE, NULL)) == NULL){
            clixon_err(OE_YANG, 0, "identityref type requires base sub-statement");
            goto done;
        }
        if ((yang_find_identity(ys, yang_argument_get(ybase))) == NULL){
            clixon_err(OE_YANG, 0, "Identity %s not found (base type of %s)",
                       yang_argument_get(ybase), yang_argument_get(ys));
            goto done;
        }
    }
    else if (strcmp(yang_argument_get(ys), "enumeration") == 0){
        if (ys_populate_type_enum(h, ys) < 0)
            goto done;
    }
    retval = 0;
  done:
    return retval;
}

/*! Sanity check yang identity statement recursively and create derived id list
 *
 * Find base identities if any and add this identity to derived identity list.
 * Do this recursively
 * The derived identity list is a list of <module>:<id> pairs. Prefixes cannot
 * be used since they are local in scope.
 * @param[in] h     Clixon handle
 * @param[in] ys    The yang identity to populate.
 * @param[in] idref If set contains the derived identifier(NULL on top call)
 * @retval    0     OK
 * @retval   -1     Error
 * @see validate_identityref  which in runtime validates actual values
 */
static int
ys_populate_identity(clixon_handle h,
                     yang_stmt    *ys,
                     char         *idref)
{
    int             retval = -1;
    yang_stmt      *yc = NULL;
    yang_stmt      *ybaseid;
    char           *baseid;
    char           *prefix = NULL;
    char           *id = NULL;
    cbuf           *cb = NULL;
    yang_stmt      *ymod;
    cvec           *idrefvec; /* Derived identityref list: (module:id)**/
    int             inext;

    /* Top-call (no recursion) create idref
     * The idref is (here) in "canonical form": <module>:<id>
     */
    if (idref == NULL){
        /* Create derived identity through prefix:id if not recursively called*/
        if ((cb = cbuf_new()) == NULL){
            clixon_err(OE_UNIX, errno, "cbuf_new");
            goto done;
        }
        if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
            goto done;
        if ((ymod = ys_module(ys)) == NULL){
            clixon_err(OE_YANG, ENOENT, "No module found");
            goto done;
        }
        cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
        idref = cbuf_get(cb);
    }
    /* Iterate through all base statements and check the base identity exists 
     * AND populate the base identity recursively
     */
    inext = 0;
    while ((yc = yn_iter(ys, &inext)) != NULL) {
        if (yc->ys_keyword != Y_BASE)
            continue;
        baseid = yang_argument_get(yc); /* on the form: prefix:id */
        if (((ybaseid = yang_find_identity(ys, baseid))) == NULL){
            clixon_err(OE_YANG, ENOENT, "No such identity: %s", baseid);
            goto done;
        }
        //          continue; /* root identity */
        /* Check if derived id is already in base identifier 
         * note that cvec is always created in ys_new()
         */
        idrefvec = yang_cvec_get(ybaseid);
        if (cvec_find(idrefvec, idref) != NULL)
            continue;
        /* Add derived id to ybaseid */
        if (yang_cvec_add(ybaseid, CGV_STRING, idref) == NULL){
            clixon_err(OE_UNIX, errno, "cv_new");
            goto done;
        }
        /* Transitive to the root */
        if (ys_populate_identity(h, ybaseid, idref) < 0)
            goto done;
    }
    retval = 0;
  done:
    if (prefix)
        free(prefix);
    if (id)
        free(id);
    if (cb)
        cbuf_free(cb);
    return retval;
}

/*! Return 1 if feature is enabled, 0 if not using the populated yang tree
 *
 * @param[in] yspec   yang specification
 * @param[in] module  Name of module
 * @param[in] feature Name of feature
 * @retval    1       Found and set
 * @retval    0       Not found or not set
 * XXX: should the in-param be h, ymod, or yspec?
 */
int
if_feature(yang_stmt    *yspec,
           char         *module,
           char         *feature)
{
    yang_stmt *ym; /* module */
    yang_stmt *yf; /* feature */
    cg_var    *cv;

    if ((ym = yang_find_module_by_name(yspec, module)) == NULL)
        return 0;
    if ((yf = yang_find(ym, Y_FEATURE, feature)) == NULL)
        return 0;
    if ((cv = yang_cv_get(yf)) == NULL)
        return 0;
    return cv_bool_get(cv);
}

/*! Populate yang feature statement - set cv to 1 if enabled
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   Feature yang statement to populate.
 * @retval    0    OK
 * @retval   -1    Error
 * Bootstrapping: A feature is enabled if found in clixon-config
 */
int
ys_populate_feature(clixon_handle h,
                    yang_stmt    *ys)
{
    int        retval = -1;
    cxobj     *x;
    yang_stmt *ymod;
    int        found = 0;
    cg_var    *cv;
    char      *module;
    char      *feature;
    cxobj     *xc;
    char      *m;
    char      *f;

    /* Get clicon config file in xml form.
     * Bootstrapping: A feature is enabled if found in clixon-config
     */
    if ((x = clicon_conf_xml(h)) == NULL)
        goto ok;
    if ((ymod = ys_module(ys)) == NULL){
        clixon_err(OE_YANG, 0, "module not found");
        goto done;
    }
    module = yang_argument_get(ymod);
    feature = yang_argument_get(ys);
    xc = NULL;
    while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL && found == 0) {
        m = NULL;
        f = NULL;
        if (strcmp(xml_name(xc), "CLICON_FEATURE") != 0)
            continue;
        /* CLICON_FEATURE is on the form <module>:<feature>.
         * Split on colon to get module(m) and feature(f) respectively */
        if (nodeid_split(xml_body(xc), &m, &f) < 0)
            goto done;
        if (m && f &&
            (strcmp(m,"*")==0 ||
             strcmp(m, module)==0) &&
            (strcmp(f,"*")==0 ||
             strcmp(f, feature)==0))
            found = 1;
        if (m) free(m);
        if (f) free(f);
    }
    if ((cv = cv_new(CGV_BOOL)) == NULL){
        clixon_err(OE_YANG, errno, "cv_new");
        goto done;
    }
    cv_name_set(cv, feature);
    cv_bool_set(cv, found);
    if (found)
        clixon_debug(CLIXON_DBG_YANG, "%s:%s", module, feature);
    yang_cv_set(ys, cv);
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Populate the unique statement with a cvec
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (unique) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 */
static int
ys_populate_unique(clixon_handle h,
                   yang_stmt    *ys)
{
    cvec *cvv;

    if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
        return -1;
    yang_cvec_set(ys, cvv);
    return 0;
}

/*! Populate unknown node with extension
 *
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (unknown) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 * RFC 7950 Sec 7.19:
 * If no "argument" statement is present, the keyword expects no argument when
 * it is used.
 * A pointer to the unknwon node is stored in the cvec of the extension:
 *                     1              n
 *   yang-extension ext1 (cvec) --->  unknown of ext1
 *          (yext)               (ys)
 *
 * @note this breaks if yangs are freed
 * Note there is some complexity in different yang modules with unknown-statements.
 * y0) The location of the extension definition. E.g. extension autocli-op
 * y1) The location of the unknown-statement (ys). This is for example: cl:autocli-op hide.
 *     Lookup of "cl" is lexically scoped in this context (to find (y0)).
 * y2) The unknown statement may be used by uses/grouping of (y1). But "cl" is declared 
 *     in y1 context. This is fixed by setting ys_mymodule to y1 which is then used in
 *     lookups such as in yang_extension_value().
 * @see yang_extension_value  Called on expanded YANG, eg in context of (y2)
 */
static int
ys_populate_unknown(clixon_handle h,
                    yang_stmt    *ys)
{
    int        retval = -1;
    yang_stmt *ymod;
    yang_stmt *yext;  /* extension */
    char      *prefix = NULL;
    char      *id = NULL;
    char      *argument; /* This is the unknown optional argument */
    cg_var    *cv;

    /* Find extension, if found, store it as unknown, if not,
       break for error */
    if (nodeid_split(yang_argument_get(ys), &prefix, &id) < 0)
        goto done;
    if ((ymod = yang_find_module_by_prefix(ys, prefix)) == NULL){
        clixon_err(OE_YANG, ENOENT, "Extension \"%s:%s\", module not found", prefix, id);
        goto done;
    }
    /* To find right binding eg after grouping/uses */
    yang_mymodule_set(ys, ys_module(ys));
    if ((yext = yang_find(ymod, Y_EXTENSION, id)) == NULL){
        clixon_err(OE_YANG, ENOENT, "Extension \"%s:%s\" not found", prefix, id);
        goto done;
    }
    /* Optional argument (only if "argument") - save it in ys_cv */
    if ((cv = yang_cv_get(ys)) != NULL &&
        (argument = cv_string_get(cv)) != NULL){
        if (yang_find(yext, Y_ARGUMENT, NULL) == NULL &&
            argument != NULL){
            clixon_err(OE_YANG, 0, "No argument specified in extension %s, but argument %s present when used", yang_argument_get(ys), argument);
            goto done;
        }
    }
#ifdef XML_EXPLICIT_INDEX
    /* Add explicit index extension */
    if ((retval = yang_search_index_extension(h, yext, ys)) < 0) {
        clixon_debug(CLIXON_DBG_YANG, "plugin_extension() failed");
        return -1;
    }
#endif
    /* Make extension callbacks that may alter yang structure 
     * Note: this may be a "layering" violation: assuming plugins are loaded
     * at yang parse time
     */
    if (clixon_plugin_extension_all(h, yext, ys) < 0)
        goto done;
    /* Add unknown to vector in extension */
    if ((cv = yang_cvec_add(yext, CGV_VOID, yang_argument_get(ys))) == NULL)
        goto done;
    cv_void_set(cv, ys);
    retval = 0;
 done:
    if (prefix)
        free(prefix);
    if (id)
        free(id);
    return retval;
}

/*! Populate modules / submodules
 *
 * Check RFC 7950: 7.1.4: All prefixes, including the prefix for the module itself, 
 * MUST be unique within the module or submodule.
 * @param[in] h    Clixon handle
 * @param[in] ys   The yang statement (module/submodule) to populate.
 * @retval    0    OK
 * @retval   -1    Error
 */
static int
ys_populate_module_submodule(clixon_handle h,
                             yang_stmt    *ym)
{
    int        retval = -1;
    yang_stmt *yp;
    yang_stmt *yi;
    yang_stmt *yi2; /* remaining */
    char      *p0 = NULL;
    char      *pi;
    char      *pi2; /* remaining */
    int        inext;
    int        inext2;

    /* Modules but not submodules have prefixes */
    if ((yp = yang_find(ym, Y_PREFIX, NULL)) != NULL)
        p0 = yang_argument_get(yp);
    inext = 0;
    while ((yi = yn_iter(ym, &inext)) != NULL) {
        if (yang_keyword_get(yi) != Y_IMPORT)
            continue;
        yp = yang_find(yi, Y_PREFIX, NULL);
        pi = yang_argument_get(yp);
        if (p0 && strcmp(p0, pi) == 0){         /* Check top-level */
            clixon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
                       pi, yang_argument_get(ym));
            goto done;
        }
        /* Check rest of imports */
        inext2 = inext;
        while ((yi2 = yn_iter(ym, &inext2)) != NULL) {
            if (yang_keyword_get(yi2) != Y_IMPORT)
                continue;
            yp = yang_find(yi2, Y_PREFIX, NULL);
            pi2 = yang_argument_get(yp);
            if (strcmp(pi2, pi) == 0){
                clixon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
                           pi, yang_argument_get(ym));
                goto done;
            }
        }
    }
    retval = 0;
 done:
    return retval;
}

/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
 *
 * @param[in]  ys  Yang statement
 * @param[in]  arg Argument - in effect Clixon handle
 * @retval     0   OK
 * @retval    -1   Error
 * Preferably run this command using yang_apply
 * Done in 2nd pass after complete parsing to be sure to have a complete
 * parse-tree

 * After this pass, cv:s are set for LEAFs and LEAF-LISTs
 * @see ys_parse_sub   for first pass and what can be assumed
 * @see ys_populate2   for after grouping expand and augment
 * (there may be more functions (all?) that may be moved to ys_populate2)
 */
int
ys_populate(yang_stmt    *ys,
            void         *arg)
{
    int           retval = -1;
    clixon_handle h = (clixon_handle)arg;

    switch(ys->ys_keyword){
    case Y_IDENTITY:
        if (ys_populate_identity(h, ys, NULL) < 0)
            goto done;
        break;
    case Y_LENGTH:
        if (ys_populate_length(h, ys) < 0)
            goto done;
        break;
    case Y_LIST:
        if (ys_populate_list(h, ys) < 0)
            goto done;
        break;
    case Y_MODULE:
    case Y_SUBMODULE:
        if (ys_populate_module_submodule(h, ys) < 0)
            goto done;
        break;
    case Y_RANGE:
        if (ys_populate_range(h, ys) < 0)
            goto done;
        break;
    case Y_TYPE:
        if (ys_populate_type(h, ys) < 0)
            goto done;
        break;
    case Y_UNIQUE:
        if (ys_populate_unique(h, ys) < 0)
            goto done;
        break;
    case Y_UNKNOWN:
        if (ys_populate_unknown(h, ys) < 0)
            goto done;
        break;
    default:
        break;
    }
    retval = 0;
  done:
    return retval;
}

/*! Run after grouping expand and augment
 *
 * Run in yang_apply but also other places
 * @param[in]  yn   yang node
 * @param[in]  arg  Argument
 * @retval     n    OK, abort traversal and return to caller with "n"
 * @retval     0    OK, continue with next
 * @retval    -1    Error, abort
 * @see ys_populate   run before grouping expand and augment
 */
int
ys_populate2(yang_stmt    *ys,
             void         *arg)
{
    int           retval = -1;
    clixon_handle h = (clixon_handle)arg;
    cg_var       *cv;
    int           ret;

    switch(ys->ys_keyword){
    case Y_AUGMENT:
    case Y_GROUPING:
        retval = 2; /* Skip sub-tree */
        goto done;
        break;
    case Y_LEAF:
    case Y_LEAF_LIST:
        if (ys_populate_leaf(h, ys) < 0)
            goto done;
        break;
    case Y_MANDATORY: /* call yang_mandatory() to check if set */
    case Y_CONFIG:
    case Y_REQUIRE_INSTANCE:
        if (ys_parse(ys, CGV_BOOL) == NULL)
            goto done;
        if (ys->ys_keyword == Y_CONFIG){
            if ((cv = yang_cv_get(ys)) != NULL && !cv_bool_get(cv))
                yang_flag_set(yang_parent_get(ys), YANG_FLAG_STATE_LOCAL);
        }
        break;
    default:
        break;
    }
    /* RFC 8528 Yang schema mount  flag for optimization */
    if ((ret = yang_schema_mount_point0(ys)) < 0)
        goto done;
    if (ret == 1)
        yang_flag_set(ys, YANG_FLAG_MTPOINT_POTENTIAL);
    retval = 0;
  done:
    return retval;
}

/*! Find feature and if-feature nodes, check features and remove disabled nodes
 *
 * @param[in] h   Clixon handle
 * @param[in] yt  Yang statement
 * @retval    1   OK
 * @retval    0   Feature not enabled: remove yt
 * @retval   -1   Error
 * @note On return 0 the over-lying function need to remove yt from its parent
 * @note cannot use yang_apply here since child-list is modified (destructive) 
 * @note if-features is parsed in full context here, previous restricted pass in ys_parse_sub
 */
int
yang_features(clixon_handle h,
              yang_stmt    *yt)
{
    int        retval = -1;
    int        i;
    int        j;
    yang_stmt *ys = NULL;
    yang_stmt *ymod;
    const char *mainfile = NULL;
    int        ret;

    i = 0;
    while (i<yt->ys_len){
        ys = yt->ys_stmt[i];
        if (ys->ys_keyword == Y_IF_FEATURE){
            /* Parse the if-feature-expr string using yang sub-parser */
            if ((ymod = ys_module(ys)) != NULL)
                mainfile = yang_filename_get(ymod);
            ret = 0;
            if (yang_subparse(yang_argument_get(ys), ys, YA_IF_FEATURE, mainfile, 1, &ret, h) < 0)
                goto done;
            clixon_debug(CLIXON_DBG_YANG | CLIXON_DBG_DETAIL, "%s %d", yang_argument_get(ys), ret);
            if (ret == 0)
                goto disabled;
        }
        else if (ys->ys_keyword == Y_FEATURE){
                if (ys_populate_feature(h, ys) < 0)
                    goto done;
        }
        else
            switch (yang_features(h, ys)){
                case -1: /* error */
                    goto done;
                    break;
                case 0: /* disabled: remove ys */
                    /* Change datanodes YANG to ANYDATA, other nodes are removed
                     */
                    if (yang_datanode(ys) && yang_config_ancestor(ys)){
                        ys->ys_keyword = Y_ANYDATA;
                        ys_freechildren(ys);
                        ys->ys_len = 0;
                        yang_flag_set(ys, YANG_FLAG_DISABLED);
                        break;
                    }
                    for (j=i+1; j<yt->ys_len; j++)
                        yt->ys_stmt[j-1] = yt->ys_stmt[j];
                    yt->ys_len--;
                    yt->ys_stmt[yt->ys_len] = NULL;
                    ys_free(ys);
                    continue; /* Don't increment i */
                    break;
                default: /* ok */
                    break;
                }
        i++;
    }
    retval = 1;
 done:
    return retval;
 disabled:
    retval = 0;  /* feature not enabled */
    goto done;
}

/*! Apply a function call recursively on all yang-stmt s recursively
 *
 * Recursively traverse all yang-nodes in a parse-tree and apply fn(arg) for 
 * each object found. The function is called with the yang-stmt 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]  yn   yang node
 * @param[in]  key  yang keyword to use as filter or -1 for all
 * @param[in]  fn   Callback
 * @param[in]  depth Depth argument: where to start. If <=0 call the calling node yn, if 1 start with its children, etc
 * @param[in]  arg  Argument
 * @retval     2    OK, yn was skipped
 * @retval     1    OK, aborted at first encounter of first match
 * @retval     0    OK, all nodes traversed
 * @retval    -1    Error, aborted at first error encounter
 * @code
 * int ys_fn(yang_stmt *ys, void *arg)
 * {
 *   return 0;
 * }
 * yang_apply(ys, Y_TYPE, ys_fn, 1, NULL); // Call all yn:s children recursively
 * @endcode
 * @note do not delete or move around any children during this function
 */
int
yang_apply(yang_stmt     *yn,
           enum rfc_6020  keyword,
           yang_applyfn_t fn,
           int            depth,
           void          *arg)
{
    int        retval = -1;
    yang_stmt *ys = NULL;
    int        i;
    int        ret;

    if (depth <= 0){
        if ((int)keyword == -1 || keyword == yn->ys_keyword){
            if ((ret = fn(yn, arg)) < 0)
                goto done;
            if (ret > 0){
                retval = ret;
                goto done;
            }
        }
    }
    for (i=0; i<yn->ys_len; i++){
        ys = yn->ys_stmt[i];
        if ((ret = yang_apply(ys, keyword, fn, depth-1, arg)) < 0)
            goto done;
        if (ret == 2)
            continue;
        else if (ret > 0){
            retval = ret;
            goto done;
        }
    }
    retval = 0;
  done:
    return retval;
}

/*! Check if a node is a yang "data node"
 *
 * @param[in]  ys  Yang statement node
 * @retval     0   Yang node is NOT a data node
 * @retval   !=0   Yang node IS a data noed
 * @see RFC7950 Sec 3:
 *   o  data node: A node in the schema tree that can be instantiated in a
 *      data tree.  One of container, leaf, leaf-list, list, anydata, and
 *      anyxml.
 */
int
yang_datanode(yang_stmt *ys)
{
    enum rfc_6020 keyw;

    keyw = yang_keyword_get(ys);
    return (keyw == Y_CONTAINER ||
            keyw == Y_LEAF ||
            keyw == Y_LIST ||
            keyw == Y_LEAF_LIST ||
            keyw == Y_ANYXML ||
            keyw == Y_ANYDATA);
}

/*! All the work for schema_nodeid functions both absolute and descendant
 *
 * @param[in]  yn    Yang node. For absolute schemanodeids this should be a module, otherwise any yang
 * @param[in]  yspec Yang spec (Cornsercase if yn is pruned)
 * @param[in]  cvv   Schema-node path encoded as a name/value pair list.
 * @param[in]  nsc   Namespace context from yang for the prefixes (names) of cvv
 * @param[out] yres  Result yang statement node, or NULL if not found
 * @retval     0     OK
 * @retval    -1     Error, with clixon_err called
 *   A schema node identifier consists of a path of identifiers, separated by slashes ("/").
 *   References to identifiers defined in external modules MUST be
 *   qualified with appropriate prefixes, and references to identifiers
 *   defined in the current module and its submodules MAY use a prefix.
 * prefixes are implemented by cvv names, and id:s by cvv strings.
 * A namespace context of the original module is nsc as prefix context.
 *
 * @see RFC7950 Sec 6.5
 */
static int
schema_nodeid_iterate(yang_stmt    *yn,
                      yang_stmt    *yspec,
                      cvec         *nodeid_cvv,
                      cvec         *nsc,
                      yang_stmt   **yres)
{
    int              retval = -1;
    yang_stmt       *ymod;
    char            *prefix; /* node-identifier     = [prefix ":"] identifier */
    char            *id;
    yang_stmt       *ys;
    yang_stmt       *yp;
    cg_var          *cv;
    char            *ns;

    yp = yn;
    /* Iterate over node identifiers /prefix:id/... */
    cv = NULL;
    while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
        prefix = cv_name_get(cv);
        id = cv_string_get(cv);
        /* Top level is repeated from abs case, but here this is done to match with 
         * matching module below
         * Get namespace */
        if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
            clixon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier in module %s",
                       prefix,
                       yang_argument_get(ys_module(yn)));
            goto done;
        }
        /* Get yang module */
        if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
            clixon_err(OE_YANG, EFAULT, "No module for namespace: %s", ns);
            goto done;
        }
        ys = yang_find_schemanode(yp, id);
        /* Special case: if rpc/action, an empty input/output may need to be created, it is optional but may 
         * still be referenced.
         * XXX: maybe input/output should always be created when rpc/action is created?
         */
        if (ys == NULL &&
            (yang_keyword_get(yp) == Y_RPC || yang_keyword_get(yp) == Y_ACTION) &&
            (strcmp(id, "input") == 0 || strcmp(id, "output") == 0)){
            enum rfc_6020 kw;
            kw = clicon_str2int(ykmap, id);
            /* Add ys as id to yp */
            if ((ys = ys_new(kw)) == NULL)
                goto done;
            if (yn_insert(yp, ys) < 0) /* Insert into hierarchy */
                goto done;
        }
        if (ys == NULL){
            clixon_debug(CLIXON_DBG_YANG, "%s not found, last id found:%s",
                         id, yang_argument_get(yp));
            goto ok;
        }
        yp = ys; /* ys is matched */
        ys = NULL;
    } /* while cv */
    assert(yp && yang_schemanode((yang_stmt*)yp));
    *yres = (yang_stmt*)yp;
 ok:
    retval = 0;
 done:
    return retval;
}

/*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec  
 *
 * @param[in]  yn            Original yang stmt (where call is made)
 * @param[in]  schema_nodeid Absolute schema-node-id, ie /a/b
 * @param[out] yres          Result yang statement node, or NULL if not found
 * @retval     0             OK , with result in yres
 * @retval    -1             Error, with clixon_err called
 * Assume schema nodeid:s have prefixes, (actually the first).
 * @see RFC7950 6.5
 * o  schema node: A node in the schema tree.  One of action, container,
 *    leaf, leaf-list, list, choice, case, rpc, input, output,
 *    notification, anydata, and anyxml.
 * Used in yang: deviation, top-level augment
 * @see yang_desc_schema_nodeid
 */
int
yang_abs_schema_nodeid(yang_stmt  *yn,
                       char       *schema_nodeid,
                       yang_stmt **yres)
{
    int           retval = -1;
    cvec         *nodeid_cvv = NULL;
    cvec         *nsc = NULL;
    cg_var       *cv;
    char         *prefix;
    char         *ns;
    yang_stmt    *yspec;
    yang_stmt    *ymod;
    char         *str;

    if ((yspec = ys_spec(yn)) == NULL){
        clixon_err(OE_YANG, EINVAL, "No yang spec");
        goto done;
    }
    *yres = NULL;
    /* check absolute schema_nodeid */
    if (schema_nodeid[0] != '/'){
        clixon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
        goto done;
    }
    /* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
     */
    if (uri_str2cvec(schema_nodeid, '/', ':', 1, &nodeid_cvv) < 0)
        goto done;
    if (cvec_len(nodeid_cvv) == 0)
        goto ok;
    /* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
    cv = NULL;
    while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
        if (cv_type_get(cv) != CGV_STRING)
        cv_type_set(cv, CGV_STRING);
        if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
            if (cv_string_set(cv, cv_name_get(cv)) < 0){
                clixon_err(OE_UNIX, errno, "cv_string_set");
                goto done;
            }
            cv_name_set(cv, NULL);
        }
    }
    /* Make a namespace context from yang for the prefixes (names) of nodeid_cvv */
    if (yang_keyword_get(yn) == Y_SPEC){
        if (xml_nsctx_yangspec(yn, &nsc) < 0)
            goto done;
    }
    else if (xml_nsctx_yang(yn, &nsc) < 0)
            goto done;
    /* Since this is an _absolute_ schema nodeid start from top 
     * Get namespace */
    cv = cvec_i(nodeid_cvv, 0);
    prefix = cv_name_get(cv);
    if ((ns = xml_nsctx_get(nsc, prefix)) == NULL){
        clixon_err(OE_YANG, EFAULT, "No namespace for prefix: %s in schema node identifier: %s",
                   prefix, schema_nodeid);
        goto done;
    }
    /* Get yang module */
    if ((ymod = yang_find_module_by_namespace(yspec, ns)) == NULL){
        clixon_err(OE_YANG, EFAULT, "No module for namespace: %s in schema node identifier: %s",
                   ns, schema_nodeid);
        goto done;
    }
    /* Iterate through cvv to find schemanode using ymod as starting point (since it is absolute) */
    if (schema_nodeid_iterate(ymod, yspec, nodeid_cvv, nsc, yres) < 0)
        goto done;
 ok:
    retval = 0;
 done:
    if (nodeid_cvv)
        cvec_free(nodeid_cvv);
    if (nsc)
        cvec_free(nsc);
    return retval;
}

/*! Given a descendant schema-nodeid (eg a/b/c) find matching yang spec  
 *
 * @param[in]  yn            Yang node (must be part of yspec tree, cannot be "dangling")
 * @param[in]  schema_nodeid Descendant schema-node-id, ie a/b
 * @param[in]  keyword       A schemode of this type, or -1 if any
 * @param[in]  ns            Namespace context
 * @param[out] yres          First yang node matching schema nodeid
 * @retval     0             OK
 * @retval    -1             Error, with clixon_err called
 * Used in yang: unique, refine, uses augment
 * @see yang_abs_schema_nodeid
 */
int
yang_desc_schema_nodeid(yang_stmt    *yn,
                        char         *schema_nodeid,
                        yang_stmt   **yres)
{
    int           retval = -1;
    cvec         *nodeid_cvv = NULL;
    cg_var       *cv;
    char         *str;
    yang_stmt    *yspec;
    cvec         *nsc = NULL;

    if (schema_nodeid == NULL || strlen(schema_nodeid) == 0){
        clixon_err(OE_YANG, EINVAL, "nodeid is empty");
        goto done;
    }
    if ((yspec = ys_spec(yn)) == NULL){
        clixon_err(OE_YANG, EINVAL, "No yang spec");
        goto done;
    }
    *yres = NULL;
    /* check absolute schema_nodeid */
    if (schema_nodeid[0] == '/'){
        clixon_err(OE_YANG, EINVAL, "descendant schema nodeid should not start with /");
        goto done;
    }
    /* Split nodeid on the form /p0:i0/p1:i1 to a cvec with [name:p0 value:i0][...]
     */
    if (uri_str2cvec(schema_nodeid, '/', ':', 1, &nodeid_cvv) < 0)
        goto done;
    if (cvec_len(nodeid_cvv) == 0)
        goto ok;
    /* If p0 is NULL an entry will be: [i0] which needs to be transformed to [NULL:i0] */
    cv = NULL;
    while ((cv = cvec_each(nodeid_cvv, cv)) != NULL){
        if (cv_type_get(cv) != CGV_STRING)
            cv_type_set(cv, CGV_STRING);
        if ((str = cv_string_get(cv)) == NULL || !strlen(str)){
            if (cv_string_set(cv, cv_name_get(cv)) < 0){
                clixon_err(OE_UNIX, errno, "cv_string_set");
                goto done;
            }
            cv_name_set(cv, NULL);
        }
    }
    /* Make a namespace context from yang for the prefixes (names) of nodeid_cvv 
     * Requires yn exist in hierarchy
     */
    if (xml_nsctx_yang(yn, &nsc) < 0)
        goto done;
    /* Iterate through cvv to find schemanode using yn as relative starting point */
    if (schema_nodeid_iterate(yn, yspec, nodeid_cvv, nsc, yres) < 0)
        goto done;
 ok:
    retval = 0;
 done:
    if (nsc)
        cvec_free(nsc);
    if (nodeid_cvv)
        cvec_free(nodeid_cvv);
    return retval;
}

/*! Return config state of this node
 *
 * @param[in] ys  Yang statement
 * @retval    1   If node has not config sub-statement or it is true
 * @retval    0   If node has a config sub-statement and it is false
 * @see yang_config_ancestor  which also takes ancestors into account, which you should normally do.
 */
int
yang_config(yang_stmt *ys)
{
    yang_stmt *ym;

    if ((ym = yang_find(ys, Y_CONFIG, NULL)) != NULL){
        if (yang_cv_get(ym) == NULL) /* shouldnt happen */
            return 1;
        return cv_bool_get(yang_cv_get(ym));
    }
    return 1;
}

/*! Return config state of this node taking parents/ancestors into account
 *
 * config statement is default true. 
 * @param[in] ys  Yang statement
 * @retval    1   Neither node nor any of its ancestors has config false
 * @retval    0   Node or one of its ancestor has config false or is RPC or notification
 */
int
yang_config_ancestor(yang_stmt *ys)
{
    yang_stmt    *yp;
    enum rfc_6020 keyw;

    yp = ys;
    do {
        if (yang_flag_get(yp, YANG_FLAG_STATE_LOCAL) != 0)
            return 0;
        else {
            keyw = yang_keyword_get(yp);
            if (keyw == Y_INPUT || keyw == Y_OUTPUT || keyw == Y_NOTIFICATION){
                return 0;
            }
        }
    } while((yp = yang_parent_get(yp)) != NULL);
    return 1;
}

/*! Given a yang node, translate the argument string to a cv vector
 *
 * @param[in]  ys         Yang statement 
 * @param[in]  delimiter  Delimiter character (eg ' ' or ',')
 * @retval     cvec  Vector of strings. Free with cvec_free()
 * @retval     NULL  Error
 * @code
 *    cvec   *cvv;
 *    cg_var *cv = NULL;
 *    if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
 *       goto err;
 *    while ((cv = cvec_each(cvv, cv)) != NULL) 
 *         ...cv_string_get(cv);
 *    cvec_free(cvv);
 * @endcode
 * @note must free return value after use w cvec_free
 */
cvec *
yang_arg2cvec(yang_stmt *ys,
              char      *delim)
{
    char  **vec = NULL;
    int     i;
    int     nvec;
    cvec   *cvv = NULL;
    cg_var *cv;

    if ((vec = clicon_strsep(yang_argument_get(ys), " ", &nvec)) == NULL)
        goto done;
    if ((cvv = cvec_new(nvec)) == NULL){
        clixon_err(OE_YANG, errno, "cvec_new");
        goto done;
    }
    for (i = 0; i < nvec; i++) {
        cv = cvec_i(cvv, i);
        cv_type_set(cv, CGV_STRING);
        if ((cv_string_set(cv, vec[i])) == NULL){
            clixon_err(OE_YANG, errno, "cv_string_set");
            cvv = NULL;
            goto done;
        }
    }
 done:
    if (vec)
        free(vec);
    return cvv;
}

/*! Check if yang node yn has key-stmt as child which matches name
 *
 * The function looks at the LIST argument string (not actual children)
 * @param[in]  yn      Yang list node with sub-statements (look for a key child)
 * @param[in]  name    Check if this name (eg "b") is a key in the yang key statement
 * @param[out] lastkey If 1 this is the last key in a multi-key list
 * @retval     1       Yes match
 * @retval     0       No match
 * @retval    -1       Error
 */
int
yang_key_match(yang_stmt *yn,
               char      *name,
               int       *lastkey)
{
    int        retval = -1;
    yang_stmt *ys = NULL;
    int        i;
    int        j;
    cvec      *cvv = NULL;
    cg_var    *cv;

    for (i=0; i<yn->ys_len; i++){
        ys = yn->ys_stmt[i];
        if (ys->ys_keyword == Y_KEY){
            if ((cvv = yang_arg2cvec(ys, " ")) == NULL)
                goto done;
            j = 0;
            cv = NULL;
            while ((cv = cvec_each(cvv, cv)) != NULL) {
                j++;
                if (strcmp(name, cv_string_get(cv)) == 0){
                    if (j == cvec_len(cvv) && lastkey)
                        *lastkey = 1;
                    retval = 1; /* match */
                    goto done;
                }
            }
            cvec_free(cvv);
            cvv = NULL;
        }
    }
    retval = 0;
 done:
    if (cvv)
        cvec_free(cvv);
    return retval;
}

/*! Set type cache for yang type
 *
 * @param[in] rxmode  Which regexp engine to use, see enum regexp_mode
 * @retval    0       OK
 * @retval   -1       Error
 */
int
yang_type_cache_set2(yang_stmt *ys,
                     yang_stmt *resolved,
                     int        options,
                     cvec      *cvv,
                     cvec      *patterns,
                     uint8_t    fraction,
                     int        rxmode,
                     cvec      *regexps)
{
    int              retval = -1;
    yang_type_cache *ycache;

    if (yang_typecache_get(ys) != NULL){
        clixon_err(OE_YANG, EEXIST, "yang type cache");
        goto done;
    }
    if ((ycache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
        clixon_err(OE_UNIX, errno, "malloc");
        goto done;
    }
    yang_typecache_set(ys, ycache);
    memset(ycache, 0, sizeof(*ycache));
    ycache->yc_resolved  = resolved;
    ycache->yc_options  = options;
    if (cvv){
        if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
            clixon_err(OE_UNIX, errno, "cvec_dup");
            goto done;
        }
    }
    if (patterns && (ycache->yc_patterns  = cvec_dup(patterns)) == NULL){
        clixon_err(OE_UNIX, errno, "cvec_dup");
        goto done;
    }
    ycache->yc_fraction  = fraction;
    if (regexps != NULL) {
        ycache->yc_rxmode = rxmode;
        if ((ycache->yc_regexps  = cvec_dup(regexps)) == NULL){
            clixon_err(OE_UNIX, errno, "cvec_dup");
            goto done;
        }
    }
    retval = 0;
 done:
    return retval;
}

/*! Get individual fields (direct/destructively) from yang type cache. 
 *
 * @param[out] patterns Initialized cvec of regexp patterns strings
 * @param[out] rxmode   Which regexp engine to use, see enum regexp_mode
 * @retval     1        OK
 * @retval     0        No cache
 * @retval    -1        Error
 */
int
yang_type_cache_get2(yang_stmt  *ytype,
                     yang_stmt **resolved,
                     int        *options,
                     cvec      **cvv,
                     cvec       *patterns,
                     cvec       *regexps,
                     uint8_t    *fraction)
{
    int              retval = -1;
    cg_var          *cv = NULL;
    yang_type_cache *ycache;

    ycache = yang_typecache_get(ytype);
    if (ycache == NULL){ /* No cache return 0 */
        retval = 0;
        goto done;
    }
    if (resolved)
        *resolved = ycache->yc_resolved;
    if (options)
        *options  = ycache->yc_options;
    if (cvv)
        *cvv    = ycache->yc_cvv;
    if (patterns){
        cv = NULL;
        while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
            cvec_append_var(patterns, cv);
    }
    if (regexps){
        cv = NULL;
        while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
            cvec_append_var(regexps, cv);
    }
    if (fraction)
        *fraction = ycache->yc_fraction;
    retval = 1; /* cache exists and is returned OK */
 done:
    return retval;
}

/*! Free yang type cache
 */
static int
yang_type_cache_free(yang_type_cache *ycache)
{
    cg_var *cv;
    void   *p;

    if (ycache->yc_cvv)
        cvec_free(ycache->yc_cvv);
    if (ycache->yc_patterns)
        cvec_free(ycache->yc_patterns);
    if (ycache->yc_regexps){
        cv = NULL;
        while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL){
            /* need to store mode since clixon_handle is not available */
            switch (ycache->yc_rxmode){
            case REGEXP_POSIX:
                cligen_regex_posix_free(cv_void_get(cv));
                if ((p = cv_void_get(cv)) != NULL){
                    free(p);
                    cv_void_set(cv, NULL);
                }
                break;
            case REGEXP_LIBXML2:
                cligen_regex_libxml2_free(cv_void_get(cv));
                /* Note, already freed in libxml2 case */
                if ((p = cv_void_get(cv)) != NULL){
                    cv_void_set(cv, NULL);
                }
                break;
            default:
                break;
            }

        }
        cvec_free(ycache->yc_regexps);
    }
    free(ycache);
    return 0;
}

/*! Add a simple anydata-node 
 *
 * One usecase is CLICON_YANG_UNKNOWN_ANYDATA when unknown data is treated as anydata
 * @param[in]  yp    Yang parent statement
 * @param[in]  name  Node name, will be copied
 * @retval     ys    OK
 * @retval     NULL  Error
 * @see ysp_add
 */
yang_stmt *
yang_anydata_add(yang_stmt *yp,
                 char      *name)
{
    yang_stmt *ys = NULL;

    if ((ys = ys_new(Y_ANYDATA)) == NULL)
        goto done;
    if (yang_argument_dup(ys, name) < 0)
        goto done;
    if (yp && yn_insert(yp, ys) < 0){ /* Insert into hierarchy */
        ys = NULL;
        goto done;
    }
 done:
    return ys;
}

/*! Find extension argument and return if extension exists and its argument value
 *
 * @param[in]  ys     Yang statement where unknown statement may occur referencing to extension
 * @param[in]  name   Name of the extension 
 * @param[in]  ns     The namespace of the module where the extension is defined
 * @param[out] exist  The extension exists.
 * @param[out] value  Extension value if any
 * @retval     0      OK: Look in exist and value for return value
 * @retval    -1      Error
 * This is for extensions with an argument
 * @code
 *     char *value = NULL;
 *     int  exist = 0;
 *     if (yang_extension_value(ys, "mymode", "urn:example:lib", &exist, &value) < 0)
 *        err;
 *     if (value != NULL){
 *        // use extension argument
 *     }
 * @endcode
 * @see ys_populate_unknown  Called when parsing YANG
 */
int
yang_extension_value(yang_stmt *ys,
                     char      *name,
                     char      *ns,
                     int       *exist,
                     char     **value)
{
    int        retval = -1;
    yang_stmt *yext;
    yang_stmt *ymod;
    cg_var    *cv;
    char      *prefix = NULL;
    cbuf      *cb = NULL;
    int        ret;
    int        inext;

    if (ys == NULL){
        clixon_err(OE_YANG, EINVAL, "ys is NULL");
        goto done;
    }
    if (exist)
        *exist = 0;
    if ((cb = cbuf_new()) == NULL){
        clixon_err(OE_UNIX, errno, "cbuf_new");
        goto done;
    }
    inext = 0; /* This loop gets complicated in the case the extension is augmented */
    while ((yext = yn_iter(ys, &inext)) != NULL) {
        if (yang_keyword_get(yext) != Y_UNKNOWN)
            continue;
        if ((ymod = ys_module(yext)) == NULL)
            continue;
        if ((ret = yang_find_prefix_by_namespace(ymod, ns, &prefix)) < 0)
            goto done;
        if (ret == 0) /* not found (this may happen in augment and maybe should be treated otherwise) */
            continue;
        cbuf_reset(cb);
        cprintf(cb, "%s:%s", prefix, name);
        if (strcmp(yang_argument_get(yext), cbuf_get(cb)) != 0)
            continue;
        break;
    }
    if (yext != NULL){ /* Found */
        if (exist)
            *exist = 1;
        if (value &&
            (cv = yang_cv_get(yext)) != NULL)
            *value = cv_string_get(cv);
    }
    retval = 0;
 done:
    if (cb)
        cbuf_free(cb);
    return retval;
}

#if 0 /* NOTE this may need to reinserted with new implementation */
/* Sort substatements with when:s last */
static int
yang_sort_subelements_fn(const void* arg1,
                         const void* arg2)
{
    yang_stmt *ys1 = *(yang_stmt **)arg1;
    yang_stmt *ys2 = *(yang_stmt **)arg2;
    int        w1; /* ys1 has when substatement */
    int        w2;

    w1 = yang_find(ys1, Y_WHEN, NULL) != NULL;
    w2 = yang_find(ys2, Y_WHEN, NULL) != NULL;
    if (w1 == w2)
        return ys1->_ys_vector_i - ys2->_ys_vector_i;
    else if (w1)
        return 1;
    else if (w2)
        return -1;
    else return ys1->_ys_vector_i - ys2->_ys_vector_i;
}
#endif

/*! Experimental code for sorting YANG children
 *
 * RFC 7950 7.5.7 and 7.8.5 says that containers and list sub elements are encoded in any order.
 * with some exceptions, eg rpc/action input/output or list key elements
 *
 * Clixon by default encodes them in yang order. But this function can change that.
 * For example, openconfig-network-instances.yang requires subelements with "when" statements last.
 */
int
yang_sort_subelements(yang_stmt *ys)
{
#if 1
    return 0;
#else
    int        retval = -1;
    int        inext;

    if ((yang_keyword_get(ys) == Y_CONTAINER ||
         yang_keyword_get(ys) == Y_LIST)){
        /* This enumerates _ys_vector_i in ys->ys_stmt vector */
        inext = 0;
        while (yn_iter(ys, &inext) != NULL) ;
        //        qsort(ys->ys_stmt, ys->ys_len, sizeof(ys), yang_sort_subelements_fn);
    }
    retval = 0;
    // done:
    return retval;
#endif
}

#ifdef XML_EXPLICIT_INDEX
/*! Mark element as search_index in list
 *
 * @retval     0   OK
 * @retval    -1   Error
 */
int
yang_list_index_add(yang_stmt *ys)
{
    int        retval = -1;
    yang_stmt *yp;

    if ((yp = yang_parent_get(ys)) == NULL ||
        yang_keyword_get(yp) != Y_LIST){
        clixon_log(NULL, LOG_WARNING, "search_index should in a list");
        goto ok;
    }
    yang_flag_set(ys, YANG_FLAG_INDEX);
 ok:
    retval = 0;
   // done:
    return retval;
}

/*! Callback for yang clixon search_index extension
 * 
 * @param[in] h    Clixon handle
 * @param[in] yext Yang node of extension 
 * @param[in] ys   Yang node of (unknown) statement belonging to extension
 * @retval    0    OK (warnings may appear)
 * @retval   -1    Error
 */
int
yang_search_index_extension(clixon_handle h,
                            yang_stmt    *yext,
                            yang_stmt    *ys)
{
    int        retval = -1;
    char      *extname;
    char      *modname;
    yang_stmt *ymod;
    yang_stmt *yp;

    ymod = ys_module(yext);
    modname = yang_argument_get(ymod);
    extname = yang_argument_get(yext);
    if (strcmp(modname, "clixon-config") != 0 || strcmp(extname, "search_index") != 0)
        goto ok;
    clixon_debug(CLIXON_DBG_YANG, "Enabled extension:%s:%s", modname, extname);
    yp = yang_parent_get(ys);
    if (yang_list_index_add(yp) < 0)
        goto done;
 ok:
    retval = 0;
 done:
    return retval;
}

#endif /* XML_EXPLICIT_INDEX */

/*! Check if yang node has a single child of specific type
 *
 * Mainly used for condition for CLI compression
 * The yang should be:
 * 1) If container it should be non-presence
 * 2) parent of a (single) specified type
 * 3) no other data node children
 * @param[in] ys      Yang node
 * @param[in] subkeyw Expected keyword of single child (typically Y_LIST)
 * @retval    1       Yes, node has single child of specified type
 * @retval    0       No, node does not have single child of specified type
 * @see https://github.com/openconfig/ygot/blob/master/docs/design.md#openconfig-path-compression 2nd clause
 */
int
yang_single_child_type(yang_stmt    *ys,
                       enum rfc_6020 subkeyw)

{
    yang_stmt    *yc;
    int           i;
    int           inext;
    enum rfc_6020 keyw;

    /* Match parent */
    /* If container, check it is Non-presence */
    if (yang_keyword_get(ys) == Y_CONTAINER){
        if (yang_find(ys, Y_PRESENCE, NULL) != NULL)
            return 0;
    }
    /* Ensure a single list child and no other data nodes */
    i = 0; /* Number of list nodes */
    inext = 0;
    while ((yc = yn_iter(ys, &inext)) != NULL) {
        keyw = yang_keyword_get(yc);
        /* case/choice could hide anything so disqualify those */
        if (keyw == Y_CASE || keyw == Y_CHOICE)
            break;
        if (!yang_datanode(yc)) /* Allowed, check next */
            continue;
        if (keyw != subkeyw) /* Another datanode than subkeyw */
            break;
        if (i++>0) /* More than one list (or could this work?) */
            break;
    }
    if (yc != NULL) /* break from loop */
        return 0;
    if (i != 1) /* List found */
        return 0;
    return 1; /* Passed all tests: yes you can hide this keyword */
}

/*! Get action callback list
 *
 * XXX rc shouldnt really be void* but the type definitions in .h file got complicated
 */
void *
yang_action_cb_get(yang_stmt *ys)
{
    assert(ys->ys_keyword == Y_ACTION);
    return ys->ys_action_cb;
}

/*! Add an action callback to YANG node
 *
 * XXX rc shouldnt really be void* but the type definitions in .h file got complicated
 */
int
yang_action_cb_add(yang_stmt *ys,
                   void      *arg)
{
    rpc_callback_t *rc = (rpc_callback_t *)arg;

    assert(ys->ys_keyword == Y_ACTION);
    if (rc == NULL){
        clixon_err(OE_YANG, EINVAL, "arg is NULL");
        return -1;
    }
    ADDQ(rc, ys->ys_action_cb);
    return 0;
}

/*! Init yang code
 *
 * Add two external tables for YANGs
 * @param[in]  h  Clixon handle
 * @retval     0  OK
 * @retval    -1  Error
 */
int
yang_init(clixon_handle h)
{
    int          retval = -1;
    map_ptr2ptr *mp;
    yang_stmt   *ymounts;

    if ((mp = calloc(1, sizeof(*mp))) == NULL){
        clixon_err(OE_UNIX, errno, "calloc");
        goto done;
    }
    _yang_when_map = mp;
    if ((mp = calloc(1, sizeof(*mp))) == NULL){
        clixon_err(OE_UNIX, errno, "calloc");
        goto done;
    }
    _yang_mymodule_map = mp;
    if (yang_cardinality_init(h) < 0)
        goto done;
    if ((ymounts = ys_new(Y_MOUNTS)) == NULL)
        goto done;
    if (clixon_yang_mounts_set(h, ymounts) < 0)
        goto done;
    retval = 0;
 done:
    return retval;
}

/*! Exit yang code
 *
 * @param[in]  h  Clixon handle
 */
int
yang_exit(clixon_handle h)
{
    yang_stmt *ymounts;

    if (_yang_when_map != NULL) {
        free(_yang_when_map);
        _yang_when_map = NULL;
    }
    if (_yang_mymodule_map != NULL) {
        free(_yang_mymodule_map);
        _yang_mymodule_map = NULL;
    }
    if ((ymounts = clixon_yang_mounts_get(h)) != NULL){
        ys_free(ymounts);
    }
    clixon_yang_mounts_set(h, NULL);
    return 0;
}
