/*
 * SPDX-FileCopyrightText: (c) 2003, 2004 Lev Walkin <vlm@lionet.info>. All rights reserved.
 * SPDX-License-Identifier: BSD-1-Clause
 */

// clang-format off
#include "asn_internal.h"
#include "constraints.h"
// clang-format on

int asn_generic_no_constraint(asn_TYPE_descriptor_t *type_descriptor,
    const void *struct_ptr,
    asn_app_constraint_failed_f *cb, void *key)
{
    (void)type_descriptor; /* Unused argument */
    (void)struct_ptr;      /* Unused argument */
    (void)cb;              /* Unused argument */
    (void)key;             /* Unused argument */

    /* Nothing to check */
    return 0;
}

int asn_generic_unknown_constraint(asn_TYPE_descriptor_t *type_descriptor,
    const void *struct_ptr,
    asn_app_constraint_failed_f *cb, void *key)
{
    (void)type_descriptor; /* Unused argument */
    (void)struct_ptr;      /* Unused argument */
    (void)cb;              /* Unused argument */
    (void)key;             /* Unused argument */

    /* Unknown how to check */
    return 0;
}

struct errbufDesc
{
    asn_TYPE_descriptor_t *failed_type;
    const void *failed_struct_ptr;
    char *errbuf;
    size_t errlen;
};

static void asn_i_ctfailcb(void *key, asn_TYPE_descriptor_t *td,
    const void *sptr, const char *fmt, ...)
{
    struct errbufDesc *arg = key;
    va_list ap;
    ssize_t vlen;
    ssize_t maxlen;

    arg->failed_type = td;
    arg->failed_struct_ptr = sptr;

    maxlen = arg->errlen;
    if (maxlen <= 0)
        {
            return;
        }

    va_start(ap, fmt);
    vlen = vsnprintf(arg->errbuf, maxlen, fmt, ap);
    va_end(ap);
    if (vlen >= maxlen)
        {
            arg->errbuf[maxlen - 1] = '\0'; /* Ensuring libc correctness */
            arg->errlen = maxlen - 1;       /* Not counting termination */
            return;
        }
    else if (vlen >= 0)
        {
            arg->errbuf[vlen] = '\0'; /* Ensuring libc correctness */
            arg->errlen = vlen;       /* Not counting termination */
        }
    else
        {
            /*
             * The libc on this system is broken.
             */
            vlen = sizeof("<broken vsnprintf>") - 1;
            maxlen--;
            arg->errlen = vlen < maxlen ? vlen : maxlen;
            memcpy(arg->errbuf, "<broken vsnprintf>", arg->errlen);
            arg->errbuf[arg->errlen] = 0;
        }
}

int asn_check_constraints(asn_TYPE_descriptor_t *type_descriptor,
    const void *struct_ptr, char *errbuf, size_t *errlen)
{
    struct errbufDesc arg;
    int ret;

    arg.failed_type = 0;
    arg.failed_struct_ptr = 0;
    arg.errbuf = errbuf;
    arg.errlen = errlen ? *errlen : 0;

    ret = type_descriptor->check_constraints(type_descriptor, struct_ptr,
        asn_i_ctfailcb, &arg);
    if (ret == -1 && errlen)
        {
            *errlen = arg.errlen;
        }

    return ret;
}
