type_dup(struct lys_module *mod, struct lys_node *parent, struct lys_type *new, struct lys_type *old,
         LY_DATA_TYPE base, int in_grp, int shallow, struct unres_schema *unres)
{
    int i;
    unsigned int u;

    switch (base) {
    case LY_TYPE_BINARY:
        if (old->info.binary.length) {
            new->info.binary.length = lys_restr_dup(mod, old->info.binary.length, 1, shallow, unres);
        }
        break;

    case LY_TYPE_BITS:
        new->info.bits.count = old->info.bits.count;
        if (new->info.bits.count) {
            new->info.bits.bit = calloc(new->info.bits.count, sizeof *new->info.bits.bit);
            LY_CHECK_ERR_RETURN(!new->info.bits.bit, LOGMEM(mod->ctx), -1);

            for (u = 0; u < new->info.bits.count; u++) {
                new->info.bits.bit[u].name = lydict_insert(mod->ctx, old->info.bits.bit[u].name, 0);
                new->info.bits.bit[u].dsc = lydict_insert(mod->ctx, old->info.bits.bit[u].dsc, 0);
                new->info.bits.bit[u].ref = lydict_insert(mod->ctx, old->info.bits.bit[u].ref, 0);
                new->info.bits.bit[u].flags = old->info.bits.bit[u].flags;
                new->info.bits.bit[u].pos = old->info.bits.bit[u].pos;
                new->info.bits.bit[u].ext_size = old->info.bits.bit[u].ext_size;
                if (lys_ext_dup(mod->ctx, mod, old->info.bits.bit[u].ext, old->info.bits.bit[u].ext_size,
                                &new->info.bits.bit[u], LYEXT_PAR_TYPE_BIT,
                                &new->info.bits.bit[u].ext, shallow, unres)) {
                    return -1;
                }
            }
        }
        break;

    case LY_TYPE_DEC64:
        new->info.dec64.dig = old->info.dec64.dig;
        new->info.dec64.div = old->info.dec64.div;
        if (old->info.dec64.range) {
            new->info.dec64.range = lys_restr_dup(mod, old->info.dec64.range, 1, shallow, unres);
        }
        break;

    case LY_TYPE_ENUM:
        new->info.enums.count = old->info.enums.count;
        if (new->info.enums.count) {
            new->info.enums.enm = calloc(new->info.enums.count, sizeof *new->info.enums.enm);
            LY_CHECK_ERR_RETURN(!new->info.enums.enm, LOGMEM(mod->ctx), -1);

            for (u = 0; u < new->info.enums.count; u++) {
                new->info.enums.enm[u].name = lydict_insert(mod->ctx, old->info.enums.enm[u].name, 0);
                new->info.enums.enm[u].dsc = lydict_insert(mod->ctx, old->info.enums.enm[u].dsc, 0);
                new->info.enums.enm[u].ref = lydict_insert(mod->ctx, old->info.enums.enm[u].ref, 0);
                new->info.enums.enm[u].flags = old->info.enums.enm[u].flags;
                new->info.enums.enm[u].value = old->info.enums.enm[u].value;
                new->info.enums.enm[u].ext_size = old->info.enums.enm[u].ext_size;
                if (lys_ext_dup(mod->ctx, mod, old->info.enums.enm[u].ext, old->info.enums.enm[u].ext_size,
                                &new->info.enums.enm[u], LYEXT_PAR_TYPE_ENUM,
                                &new->info.enums.enm[u].ext, shallow, unres)) {
                    return -1;
                }
            }
        }
        break;

    case LY_TYPE_IDENT:
        new->info.ident.count = old->info.ident.count;
        if (old->info.ident.count) {
            new->info.ident.ref = malloc(old->info.ident.count * sizeof *new->info.ident.ref);
            LY_CHECK_ERR_RETURN(!new->info.ident.ref, LOGMEM(mod->ctx), -1);
            memcpy(new->info.ident.ref, old->info.ident.ref, old->info.ident.count * sizeof *new->info.ident.ref);
        } else {
            /* there can be several unresolved base identities, duplicate them all */
            i = -1;
            do {
                i = unres_schema_find(unres, i, old, UNRES_TYPE_IDENTREF);
                if (i != -1) {
                    if (unres_schema_add_str(mod, unres, new, UNRES_TYPE_IDENTREF, unres->str_snode[i]) == -1) {
                        return -1;
                    }
                }
                --i;
            } while (i > -1);
        }
        break;

    case LY_TYPE_INST:
        new->info.inst.req = old->info.inst.req;
        break;

    case LY_TYPE_INT8:
    case LY_TYPE_INT16:
    case LY_TYPE_INT32:
    case LY_TYPE_INT64:
    case LY_TYPE_UINT8:
    case LY_TYPE_UINT16:
    case LY_TYPE_UINT32:
    case LY_TYPE_UINT64:
        if (old->info.num.range) {
            new->info.num.range = lys_restr_dup(mod, old->info.num.range, 1, shallow, unres);
        }
        break;

    case LY_TYPE_LEAFREF:
        if (old->info.lref.path) {
            new->info.lref.path = lydict_insert(mod->ctx, old->info.lref.path, 0);
            new->info.lref.req = old->info.lref.req;
            if (!in_grp && unres_schema_add_node(mod, unres, new, UNRES_TYPE_LEAFREF, parent) == -1) {
                return -1;
            }
        }
        break;

    case LY_TYPE_STRING:
        if (old->info.str.length) {
            new->info.str.length = lys_restr_dup(mod, old->info.str.length, 1, shallow, unres);
        }
        if (old->info.str.pat_count) {
            new->info.str.patterns = lys_restr_dup(mod, old->info.str.patterns, old->info.str.pat_count, shallow, unres);
            new->info.str.pat_count = old->info.str.pat_count;
#ifdef LY_ENABLED_CACHE
            if (!in_grp) {
                new->info.str.patterns_pcre = malloc(new->info.str.pat_count * 2 * sizeof *new->info.str.patterns_pcre);
                LY_CHECK_ERR_RETURN(!new->info.str.patterns_pcre, LOGMEM(mod->ctx), -1);
                for (u = 0; u < new->info.str.pat_count; u++) {
                    if (lyp_precompile_pattern(mod->ctx, &new->info.str.patterns[u].expr[1],
                                              (pcre**)&new->info.str.patterns_pcre[2 * u],
                                              (pcre_extra**)&new->info.str.patterns_pcre[2 * u + 1])) {
                        free(new->info.str.patterns_pcre);
                        new->info.str.patterns_pcre = NULL;
                        return -1;
                    }
                }
            }
#endif
        }
        break;

    case LY_TYPE_UNION:
        new->info.uni.has_ptr_type = old->info.uni.has_ptr_type;
        new->info.uni.count = old->info.uni.count;
        if (new->info.uni.count) {
            new->info.uni.types = calloc(new->info.uni.count, sizeof *new->info.uni.types);
            LY_CHECK_ERR_RETURN(!new->info.uni.types, LOGMEM(mod->ctx), -1);

            for (u = 0; u < new->info.uni.count; u++) {
                if (lys_type_dup(mod, parent, &(new->info.uni.types[u]), &(old->info.uni.types[u]), in_grp,
                        shallow, unres)) {
                    return -1;
                }
            }
        }
        break;

    default:
        /* nothing to do for LY_TYPE_BOOL, LY_TYPE_EMPTY */
        break;
    }

    return EXIT_SUCCESS;
}