/*
 * Conversion functions for libjpiconv
 *
 * SPDX-FileType: SOURCE
 * SPDX-FileCopyrightText: Michael Bäuerle
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <stddef.h>
#include <string.h>

#include "libjpiconv-0/iconv.h"  /* Always include main header file first */
#include "libjpiconv-0/iconv_errno.h"
#include "libjpiconv-0/iconv_name.h"
#include "libjpiconv-0/iconv_rfc1468.h"


/* Check macros must not depend on C execution character set and/or locale */
#define JPIC0_I_PRINTABLE_ASCII(c)  ((0x20U < c) && (0x7FU > c))
#define JPIC0_I_LOWERCASE_ASCII(c)  ((0x60U < c) && (0x7BU > c))


/* ========================================================================== */
/*
 * Search encoding definition 'def' for encoding 'enc'.
 *
 * Returns a pointer to the mapping table or NULL (encoding not supported).
 */
static const jpic0_i_iso2022_jp *jpic0_i_check_def(const char *enc,
                                                   const jpic0_i_name_def *def)
{
    size_t i = 0;

    while (NULL != def[i].name)
    {
        if (!strcmp(enc, def[i].name))
            return def[i].table;
        ++i;
    }

    return NULL;
}


/* ========================================================================== */
/*
 * Select encoding definition and search for encoding 'enc'.
 *
 * Returns a pointer to the mapping table or NULL (encoding not supported).
 */
static const jpic0_i_iso2022_jp *jpic0_i_check(const char *enc)
{
    const jpic0_i_iso2022_jp *ret = NULL;

    ret = jpic0_i_check_def(enc, jpic0_i_name_src);
    if (NULL != ret)
        return ret;

#if JPIC0_I_NAME_ENABLE_ALIASES
    ret = jpic0_i_check_def(enc, jpic0_i_name_src_alias);
    if (NULL != ret)
        return ret;
#endif  /* JPIC0_I_NAME_ENABLE_ALIASES */

#if JPIC0_I_NAME_ENABLE_NONSTANDARD
    ret = jpic0_i_check_def(enc, jpic0_i_name_src_nonstd);
    if (NULL != ret)
        return ret;
#endif  /* JPIC0_I_NAME_ENABLE_NONSTANDARD */

    return ret;
}


/* ========================================================================== */
/*
 * Check string for printable US-ASCII and convert it to uppercase
 *
 * The parameter 'str' must point to a string with NUL-termination.
 *
 * Returns 1 for success and 0 for error.
 */
static int jpic0_i_to_uppercase(char *str)
{
    size_t i = 0;

    do
    {
        unsigned char c = str[i];

        if (0 == c)
            break;

        if (!JPIC0_I_PRINTABLE_ASCII(c))
            return 0;
        if (JPIC0_I_LOWERCASE_ASCII(c))
            str[i] = c - 0x20U;
    }
    while (JPIC0_I_NAME_MAX > ++i);

    return 1;
}


/* ========================================================================== */
/*
 * Check whether conversion from an encoding to Unicode is supported.
 *
 * The parameter 'enc' is the name of the encoding (treated case-insensitive).
 *
 * Returns a pointer to the mapping table or NULL (not supported).
 */
static const jpic0_i_iso2022_jp *jpic0_i_check_source_encoding(const char *enc)
{
    char buf[JPIC0_I_NAME_MAX + 2U];  /* +2 to silence warning from GCC 14 */

    /* Copy name and ensure that it is NUL-terminated (not too long) */
    (void)strncpy(buf, enc, JPIC0_I_NAME_MAX + 1U);
    if (0 != buf[JPIC0_I_NAME_MAX])
        return NULL;

    /* Check name for printable US-ASCII and convert it to uppercase */
    if (!jpic0_i_to_uppercase(buf))
        return NULL;

    /* Check whether encoding is supported */
    return jpic0_i_check(buf);
}


/* ========================================================================== */
/*
 * Check target encoding.
 *
 * The parameter 'enc' is the name of the encoding (treated case-insensitive).
 *
 * Returns 1 for "UTF-8" (supported) or 0 otherwise (not supported).
 */
static unsigned char jpic0_i_check_target_encoding(const char *enc)
{
    char buf[JPIC0_I_NAME_MAX + 2U];  /* +2 to silence warning from GCC 14 */

    /* Copy name and ensure that it is NUL-terminated (not too long) */
    (void)strncpy(buf, enc, JPIC0_I_NAME_MAX + 1U);
    if (0 != buf[JPIC0_I_NAME_MAX])
        return 0;

    /* Check name for printable US-ASCII and convert it to uppercase */
    if (!jpic0_i_to_uppercase(buf))
        return 0;

    /* Check whether encoding is supported */
    if (!strncmp("UTF-8", buf, JPIC0_I_NAME_MAX + 1U))
        return 1;

    return 0;
}


/* ========================================================================== */
size_t jpic0_iconvstr(const char *tocode, const char *fromcode,
                      char *inarray, size_t *inlen,
                      char *outarray, size_t *outlen,
                      int flag)
{
    size_t                    ret = 0;
    const unsigned char       tgt = jpic0_i_check_target_encoding(tocode);
    const jpic0_i_iso2022_jp *src = jpic0_i_check_source_encoding(fromcode);

    if ((0 == tgt) || (NULL == src))
    {
        /* Requested conversion is not supported */
        errno = JPIC0_I_EBADF;
        return (size_t)-1;
    }

    if (jpic0_i_rfc1468(inarray, inlen, outarray, outlen, flag, &ret))
        return (size_t)-1;

    return ret;
}
