view sidutil.c @ 367:f73270cabde2

Bump copyright years.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 02 Jan 2021 11:37:13 +0200
parents a5131cd64110
children 078a25cd67d8
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-2021 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,
};


#define SI_PUTCH(xch) th_strbuf_putch(&outBuf, &outSize, &outLen, (xch))


//
// Convert _ONLY_ FROM ISO-8859-1 encoding to few other encodings
//
static char *sidutil_chconv_internal(SIDUtilChConvCtx *ctx, const char *src)
{
    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 (!SI_PUTCH(*srcPtr))
                        goto err;
                }
                else
                if (*srcPtr < 0xBF)
                {
                    if (!SI_PUTCH(0xC2) ||
                        !SI_PUTCH(*srcPtr))
                        goto err;
                }
                else
                {
                    if (!SI_PUTCH(0xC3) ||
                        !SI_PUTCH(*srcPtr - 0x40))
                        goto err;
                }
                break;

            case TH_LANG_ISO88591:
                if (!SI_PUTCH(*srcPtr))
                    goto err;
                break;

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

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

                if (!SI_PUTCH(outByte))
                    goto err;
                break;
        }
    }

    if (!SI_PUTCH(*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, "cp858") == 0)
            ctx->outLangID = TH_LANG_CP858;
        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->enabled)
        iconv_close(ctx->iconvCtx);
#else
#endif

    th_free(ctx->outLang);
}


void sidutil_ioctx_error(th_ioctx *fh, const int err, const char *msg)
{
    (void) err;
    THERR("[%s:%" PRIu_SIZE_T "] %s\n", fh->filename, fh->line, msg);
}


int sidutil_read_sldb_file(const char *filename, SIDLibSLDB **pdbh)
{
    th_ioctx *infh = NULL;
    int res;

    // Initialize SLDB
    if ((res = th_io_fopen(&infh, &th_stdio_io_ops, filename, "r")) != THERR_OK)
    {
        THERR("Could not open SLDB '%s': %s\n",
            filename, th_error_str(res));
        goto out;
    }

    th_io_set_handlers(infh, sidutil_ioctx_error, NULL);

    if ((res = sidlib_sldb_new(pdbh)) != THERR_OK)
    {
        THERR("Could not allocate SLDB database structure: %s\n",
            th_error_str(res));
        goto out;
    }

    if ((res = sidlib_sldb_read(infh, *pdbh)) != THERR_OK)
    {
        THERR("Error parsing SLDB: %s\n",
            th_error_str(res));
        goto out;
    }

    if ((res = sidlib_sldb_build_index(*pdbh)) != THERR_OK)
    {
        THERR("Error building SLDB index: %s\n",
            th_error_str(res));
        goto out;
    }

out:
    th_io_close(infh);
    return res;
}


int sidutil_read_stildb_file(const char *filename, SIDLibSTILDB **pdbh, SIDLibChConvCtx *chconv)
{
    th_ioctx *infh = NULL;
    int res;

    // Initialize STILDB
    if ((res = th_io_fopen(&infh, &th_stdio_io_ops, filename, "r")) != THERR_OK)
    {
        THERR("Could not open STIL database '%s': %s\n",
            filename, th_error_str(res));
        goto out;
    }

    th_io_set_handlers(infh, sidutil_ioctx_error, NULL);

    if ((res = sidlib_stildb_new(pdbh)) != THERR_OK)
    {
        THERR("Could not allocate STIL database structure: %s\n",
            th_error_str(res));
        goto out;
    }

    if ((res = sidlib_stildb_read(infh, *pdbh, chconv)) != THERR_OK)
    {
        THERR("Error parsing STIL: %s\n",
            th_error_str(res));
        goto out;
    }

    if ((res = sidlib_stildb_build_index(*pdbh)) != THERR_OK)
    {
        THERR("Error building STIL index: %s\n",
            th_error_str(res));
        goto out;
    }

out:
    th_io_close(infh);
    return res;
}