diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sidutil.c	Sat Jan 11 18:30:10 2020 +0200
@@ -0,0 +1,280 @@
+/*
+ * 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);
+}