view sidutil.c @ 318:f3ba2ba894b1

Rename few HVSC related #defines to have SIDUTIL_ prefix.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 11 Jan 2020 19:11:34 +0200
parents b0c844b39516
children 1e7ffcaeb8ad
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"


char * sidutil_escape_string(const char *str, const char *escchars)
{
    size_t len, size;
    char *buf;

    if (str == NULL)
        return NULL;

    if (escchars == NULL)
        return th_strdup(str);

    size = strlen(str) + 1;
    if ((buf = th_malloc(size)) == NULL)
        return NULL;

    for (len = 0; *str; str++)
    {
        if (strchr(escchars, *str) != NULL || *str == '\\')
        {
            if (!th_strbuf_putch(&buf, &size, &len, '\\'))
                goto err;
        }

        if (!th_strbuf_putch(&buf, &size, &len, *str))
            goto err;
    }

    if (!th_strbuf_putch(&buf, &size, &len, 0))
        goto err;

    return buf;

err:
    th_free(buf);
    return NULL;
}


void sidutil_print_string_escaped(FILE *outFile, const char *str)
{
    while (*str)
    {
        if (*str == '\\')
        switch (*(++str))
        {
            case 'n': fputc('\n', outFile); break;
            case 'r': fputc('\r', outFile); break;
            case 't': fputc('\r', outFile); break;
            case '\\': fputc('\\', outFile); break;
            default: fputc(*str, outFile); break;
        }
        else
            fputc(*str, outFile);

        str++;
    }
}


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,
        SIDUTIL_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);
}