view sidutil.c @ 313:b3d46806787d

Move a number of more or less generic helper functions into a separate sidutil.[ch] module.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 11 Jan 2020 18:30:10 +0200
parents
children b0c844b39516
line wrap: on
line source

/*
 * SIDLib common utility functions
 * Programmed and designed by Matti 'ccr' Hämäläinen <ccr@tnsp.org>
 * (C) Copyright 2014-2020 Tecnic Software productions (TNSP)
 */
#include "sidutil.h"
#include "th_file.h"
#include "th_string.h"
#include "th_datastruct.h"


void sidutil_print_license(void)
{
    printf("%s - %s\n%s\n", th_prog_name, th_prog_desc, th_prog_author);
    printf(
    "\n"
    "Redistribution and use in source and binary forms, with or without\n"
    "modification, are permitted provided that the following conditions\n"
    "are met:\n"
    "\n"
    " 1. Redistributions of source code must retain the above copyright\n"
    "    notice, this list of conditions and the following disclaimer.\n"
    "\n"
    " 2. Redistributions in binary form must reproduce the above copyright\n"
    "    notice, this list of conditions and the following disclaimer in\n"
    "    the documentation and/or other materials provided with the\n"
    "    distribution.\n"
    "\n"
    " 3. The name of the author may not be used to endorse or promote\n"
    "    products derived from this software without specific prior written\n"
    "    permission.\n"
    "\n"
    "THIS SOFTWARE IS PROVIDED BY THE AUTHOR \"AS IS\" AND ANY EXPRESS OR\n"
    "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
    "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
    "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\n"
    "INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n"
    "(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\n"
    "SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
    "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
    "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
    "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
    "POSSIBILITY OF SUCH DAMAGE.\n"
    );
}


const char *sidutil_strip_hvsc_path(const char *hvscPath, const char *filename)
{
    if (hvscPath != NULL)
    {
        const char *hvsc = hvscPath, *fptr = filename;

        // Compare until end of string(s)
        for (; *hvsc != 0 && *fptr != 0 && *hvsc == *fptr; hvsc++, fptr++);

        // Full match?
        if (*hvsc == 0)
            return fptr - 1;
    }

    return filename;
}


char *sidutil_check_hvsc_file(const char *hvscPath, const char *filebase, const char *fext)
{
    th_stat_data sdata;
    char *npath = th_strdup_printf("%s%c%s%c%s%s",
        hvscPath, TH_DIR_SEPARATOR_CHR,
        SET_HVSC_DOCUMENTS, TH_DIR_SEPARATOR_CHR,
        filebase, fext != NULL ? fext : "");

    if (npath != NULL &&
        th_stat_path(npath, &sdata) &&
        (sdata.flags & TH_IS_READABLE) &&
        (sdata.flags & TH_IS_DIR) == 0)
        return npath;

    th_free(npath);
    return NULL;
}


#ifndef HAVE_ICONV

static const uint8_t sidutil_lang_iso88591_to_cp850[16*6] = {
0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee,
0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8,
0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8,
0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1,
0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98,
};

static const uint8_t sidutil_lang_iso88591_to_cp437[16*6] = {
0xff, 0xad, 0x9b, 0x9c, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0xa6, 0xae, 0xaa, 0x00, 0x00, 0x00,
0xf8, 0xf1, 0xfd, 0x00, 0x00, 0xe6, 0x00, 0xfa, 0x00, 0x00, 0xa7, 0xaf, 0xac, 0xab, 0x00, 0xa8,
0x00, 0x00, 0x00, 0x00, 0x8e, 0x8f, 0x92, 0x80, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x00, 0xe1,
0x85, 0xa0, 0x83, 0x00, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b,
0x00, 0xa4, 0x95, 0xa2, 0x93, 0x00, 0x94, 0xf6, 0x00, 0x97, 0xa3, 0x96, 0x81, 0x00, 0x00, 0x98,
};


static char *sidutil_chconv_internal(SIDUtilChConvCtx *ctx, const char *src)
{
    // Fallback conversion of ISO-8859-1 to X
    const uint8_t *srcPtr = (const uint8_t *) src;
    const uint8_t *convTable;
    size_t outSize, outLen;
    char *outBuf, outByte;

    if (src == NULL)
        return NULL;

    outSize = strlen(src) + 1;
    if ((outBuf = th_malloc(outSize)) == NULL)
        return NULL;

    for (outLen = 0; *srcPtr; srcPtr++)
    {
        switch (ctx->outLangID)
        {
            case TH_LANG_UTF8:
                // Not 100% correct really, but close enough
                if (*srcPtr < 0x80)
                {
                    if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
                        goto err;
                }
                else
                if (*srcPtr < 0xBF)
                {
                    if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC2) ||
                        !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
                        goto err;
                }
                else
                {
                    if (!th_strbuf_putch(&outBuf, &outSize, &outLen, 0xC3) ||
                        !th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr - 0x40))
                        goto err;
                }
                break;

            case TH_LANG_ISO88591:
                if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
                    goto err;
                break;

            case TH_LANG_CP850:
            case TH_LANG_CP437:
                // Not 100% correct either, but close enough
                convTable = (ctx->outLangID == TH_LANG_CP850) ?
                    sidutil_lang_iso88591_to_cp850 : sidutil_lang_iso88591_to_cp437;

                if (*srcPtr < 0x7f)
                    outByte = *srcPtr;
                else
                if (*srcPtr >= 0xA0)
                    outByte = convTable[*srcPtr - 0xA0];
                else
                    outByte = '?';

                if (!th_strbuf_putch(&outBuf, &outSize, &outLen, outByte))
                    goto err;
                break;
        }
    }

    if (!th_strbuf_putch(&outBuf, &outSize, &outLen, *srcPtr))
        goto err;

    return outBuf;

err:
    th_free(outBuf);
    return NULL;
}

#endif


// NOTICE! Only call this function IF ctx->enabled == TRUE
char * sidutil_chconv_convert(SIDUtilChConvCtx *ctx, const char *src)
{
#ifdef HAVE_ICONV
    size_t srcLeft = strlen(src) + 1;
    size_t outLeft = srcLeft * 2;
    char *srcPtr = (char *) src;
    char *outBuf, *outPtr;

    if ((outBuf = outPtr = th_malloc(outLeft + 1)) == NULL)
        return NULL;

    while (srcLeft > 0)
    {
        size_t ret = iconv(ctx->iconvCtx, &srcPtr, &srcLeft, &outPtr, &outLeft);
        if (ret == (size_t) -1)
            break;
    }

    return (char *) outBuf;
#else
    return sidutil_chconv_internal(ctx, src);
#endif
}


int sidutil_chconv_init(SIDUtilChConvCtx *ctx, const char *outLang)
{
    if (ctx == NULL)
        return THERR_NULLPTR;

    memset(ctx, 0, sizeof(*ctx));

    if (outLang != NULL)
    {
        // Get the character encoding part (e.g. "UTF-8" etc.) and
        // strip out and lowercase everything (e.g. "utf8")
        size_t i;
        char *ptr;

        if ((ctx->outLang = th_strdup(outLang)) == NULL)
            return THERR_MALLOC;

        if ((ptr = strchr(ctx->outLang, '.')) == NULL)
            ptr = ctx->outLang;
        else
            ptr++;

        for (i = 0; *ptr; ptr++)
        {
            if (*ptr != '-')
                ctx->outLang[i++] = th_tolower(*ptr);
        }
        ctx->outLang[i] = 0;

#ifdef HAVE_ICONV
        // Initialize iconv, check if we have language/charset
        ctx->iconvCtx = iconv_open(ctx->outLang, "iso88591");
        ctx->enabled = (ctx->iconvCtx != (iconv_t) -1);
#else
        // Check if we can use our fallback converter
        if (strcmp(ctx->outLang, "utf8") == 0)
            ctx->outLangID = TH_LANG_UTF8;
        else
        if (strcmp(ctx->outLang, "iso88591") == 0 ||
            strcmp(ctx->outLang, "cp819") == 0 ||
            strcmp(ctx->outLang, "latin1") == 0 ||
            strcmp(ctx->outLang, "cp28591") == 0)
            ctx->outLangID = TH_LANG_ISO88591;
        else
        if (strcmp(ctx->outLang, "cp850") == 0)
            ctx->outLangID = TH_LANG_CP850;
        else
        if (strcmp(ctx->outLang, "cp437") == 0)
            ctx->outLangID = TH_LANG_CP437;
        else
            ctx->outLangID = TH_LANG_ISO88591;

        ctx->enabled = ctx->outLangID != TH_LANG_ISO88591;
#endif
    }

    return THERR_OK;
}


void sidutil_chconv_close(SIDUtilChConvCtx *ctx)
{
#ifdef HAVE_ICONV
    if (ctx->iconvCtx != (iconv_t) -1)
        iconv_close(ctx->iconvCtx);
#else
#endif

    th_free(ctx->outLang);
}