root/src/add-ons/kernel/file_systems/fat/bsd/libkern/iconv.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
 *
 * Copyright (c) 2000-2001 Boris Popov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


// Modified to support the Haiku FAT driver.

#include "sys/param.h"
#include "sys/systm.h"
#include "sys/cdefs.h"
#include "sys/malloc.h"
#include "sys/iconv.h"


int printf(const char* format, ...);


/*! Create an instance of a converter.

*/
int
fat_iconv_open(const char* to, const char* from, void** handle)
{
#ifdef USER
        iconv_t descriptor = iconv_open(to, from);
        if (descriptor == (iconv_t)(-1))
                return errno;
        *handle = descriptor;

        return 0;
#else
        return ENOTSUP;
#endif // !USER
}


int
fat_iconv_close(void* handle)
{
#ifdef USER
        iconv_t* descriptor = (iconv_t*)handle;
        int error = 0;

        error = iconv_close(descriptor);

        return error;
#else
        return ENOTSUP;
#endif // !USER
}


int
iconv_conv(void* handle, const char** inbuf, size_t* inbytesleft, char** outbuf,
        size_t* outbytesleft)
{
#ifdef USER
        while (iconv(handle, __DECONST(char**, inbuf), inbytesleft, outbuf, outbytesleft) != (size_t)-1
                && *inbytesleft > 0 && strlen(*inbuf) > 0)
                ;

        return 0;
#else
        return ENOTSUP;
#endif // !USER
}


int
iconv_convchr(void* handle, const char** inbuf, size_t* inbytesleft, char** outbuf,
        size_t* outbytesleft)
{
#ifdef USER
        int status = iconv(handle, __DECONST(char**, inbuf), inbytesleft, outbuf, outbytesleft);

        return status;
#else
        return ENOTSUP;
#endif // !USER
}


/*! Only those casetype values used by the FAT driver are implemented.

*/
int
iconv_convchr_case(void* handle, const char** inbuf, size_t* inbytesleft, char** outbuf,
        size_t* outbytesleft, int casetype)
{
#ifdef USER
        size_t status = 0;
        int inbufAdvanced = 0;

        if (casetype == KICONV_FROM_LOWER || casetype == KICONV_FROM_UPPER) {
                char finalInbuf[4];
                char* finalInbufPtr = finalInbuf;
                int (*tocase)(int ch) = (casetype == KICONV_FROM_LOWER) ? tolower : toupper;

                // The characters must be set to the indicated case before conversion.
                // Since iconv only takes one char at a time, just set 1 (possibly multibyte)
                // character, not the whole inbuf.
                int i;
                for (i = 0; i < 4; i++)
                        finalInbuf[i] = tocase((*inbuf)[i]);

                status = iconv(handle, &finalInbufPtr, inbytesleft, outbuf, outbytesleft);
                if (status == (size_t)(-1))
                        printf("FAT iconv_convchr_case:  %s\n", strerror(errno));

                // iconv has incremented finalInbufPtr instead of inbuf; update inbuf manually
                inbufAdvanced = finalInbufPtr - finalInbuf;
                *inbuf += inbufAdvanced;

                return status;
        }
        if (casetype == KICONV_LOWER) {
                status = iconv(handle, __DECONST(char**, inbuf), inbytesleft, outbuf, outbytesleft);
                // Outbuf will be advanced one slot by iconv each time it's called.
                // Just deal with the one char that is pointed to right now.
                **outbuf = tolower(**outbuf);
                return status;
        }

        printf("FAT iconv_convchr_case: unrecognized casetype\n");
        return -1;
#else
        return ENOTSUP;
#endif // !USER
}